diff --git a/.github/.cSpellWords.txt b/.github/.cSpellWords.txt
index 526fa1220..1253a26b9 100644
--- a/.github/.cSpellWords.txt
+++ b/.github/.cSpellWords.txt
@@ -45,3 +45,5 @@ Vect
VECT
Werror
Wunused
+MQTTV
+mqttv
diff --git a/CTestTestfile.cmake b/CTestTestfile.cmake
new file mode 100644
index 000000000..2c629cab0
--- /dev/null
+++ b/CTestTestfile.cmake
@@ -0,0 +1,8 @@
+# CMake generated Testfile for
+# Source directory: /home/ubuntu/coreMQTT/test
+# Build directory: /home/ubuntu/coreMQTT
+#
+# This file includes the relevant testing commands required for
+# testing this directory and lists subdirectories to be tested as well.
+subdirs("unit-test/MQTT")
+subdirs("unit-test/MQTTv5")
diff --git a/docs/doxygen/config.doxyfile b/docs/doxygen/config.doxyfile
index 40634095f..7a011b5db 100644
--- a/docs/doxygen/config.doxyfile
+++ b/docs/doxygen/config.doxyfile
@@ -2295,7 +2295,8 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-PREDEFINED = DOXYGEN=1
+PREDEFINED = DOXYGEN=1 \
+ MQTT_VERSION_5_ENABLED=1
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
diff --git a/source/core_mqtt.c b/source/core_mqtt.c
index 943e08e19..463d51a7f 100644
--- a/source/core_mqtt.c
+++ b/source/core_mqtt.c
@@ -90,6 +90,172 @@
*/
#define CORE_MQTT_UNSUBSCRIBE_PER_TOPIC_VECTOR_LENGTH ( 2U )
+#if ( MQTT_VERSION_5_ENABLED )
+
+/**
+ * @brief User Property id.
+ */
+ #define MQTT_USER_PROPERTY_ID ( 0x26 )
+
+/**
+ * @brief Authentication Method id.
+ */
+ #define MQTT_AUTH_METHOD_ID ( 0x15 )
+
+/**
+ * @brief Authentication Data id.
+ */
+ #define MQTT_AUTH_DATA_ID ( 0x16 )
+
+/**
+ * @brief Content Type id.
+ */
+ #define MQTT_CONTENT_TYPE_ID ( 0x03 )
+
+/**
+ * @brief Response Topic id.
+ */
+ #define MQTT_RESPONSE_TOPIC_ID ( 0x08 )
+
+/**
+ * @brief Correlation Data id.
+ */
+ #define MQTT_CORRELATION_DATA_ID ( 0x09 )
+
+/**
+ * @brief Correlation Data id.
+ */
+ #define MQTT_REASON_STRING_ID ( 0x1F )
+
+
+/**
+ * @brief Size of the property id.
+ */
+ #define CORE_MQTT_ID_SIZE ( 1U )
+
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+
+/**
+ * @brief Struct used to deserialize the will properties.
+ *
+ **/
+ typedef struct UserPropertiesVector
+ {
+ /**
+ * @brief Used to encode user key length.
+ *
+ **/
+ uint8_t serializedUserKeyLength[ MAX_USER_PROPERTY ][ 2 ];
+
+ /**
+ * @brief Used to encode user id.
+ *
+ **/
+ uint8_t userId[ MAX_USER_PROPERTY ];
+
+ /**
+ * @brief Used to encode user value length.
+ *
+ **/
+ uint8_t serializedUserValueLength[ MAX_USER_PROPERTY ][ 2 ];
+ } UserPropertyVector_t;
+ #endif /* if ( MQTT_USER_PROPERTY_ENABLED ) */
+
+/**
+ * @brief Struct used to deserialize the will properties.
+ *
+ **/
+ typedef struct WillPropertiesVector
+ {
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+
+ /**
+ * @brief Used to encode user property.
+ *
+ **/
+ UserPropertyVector_t userProperty;
+ #endif
+
+ /**
+ * @brief Used to encode content type length.
+ *
+ **/
+ uint8_t serializedContentTypeLength[ 2 ];
+
+ /**
+ * @brief Used to encode content type id.
+ *
+ **/
+ uint8_t contentTypeId;
+
+ /**
+ * @brief Used to encode response topic length.
+ *
+ **/
+ uint8_t serializedResponseTopicLength[ 2 ];
+
+ /**
+ * @brief Used to encode response topic id.
+ *
+ **/
+ uint8_t responseTopicId;
+
+ /**
+ * @brief Used to encode correlation data length.
+ *
+ **/
+ uint8_t serializedCorrelationLength[ 2 ];
+
+ /**
+ * @brief Used to encode correlation data id.
+ *
+ **/
+ uint8_t correlationDataId;
+ } PublishVector_t;
+
+/**
+ * @brief Struct used to deserialize the connect properties.
+ *
+ **/
+ typedef struct ConnectPropertiesVector
+ {
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+
+ /**
+ * @brief Used to encode user property.
+ *
+ **/
+ UserPropertyVector_t userProperty;
+ #endif
+
+ /**
+ * @brief Used to encode authentication method length.
+ *
+ **/
+ uint8_t serializedAuthMethodLength[ 2 ];
+
+ /**
+ * @brief Used to authentication method id.
+ *
+ **/
+ uint8_t authMethodId;
+
+ /**
+ * @brief Used to encode authentication data length.
+ *
+ **/
+ uint8_t serializedAuthDataLength[ 2 ];
+
+ /**
+ * @brief Used to authentication data id.
+ *
+ **/
+ uint8_t authDataId;
+ } PropertiesVector_t;
+
+
+#endif /* if ( MQTT_VERSION_5_ENABLED ) */
+
/*-----------------------------------------------------------*/
/**
@@ -443,7 +609,6 @@ static MQTTStatus_t receiveConnack( const MQTTContext_t * pContext,
static MQTTStatus_t handleSessionResumption( MQTTContext_t * pContext,
bool sessionPresent );
-
/**
* @brief Send the publish packet without copying the topic string and payload in
* the buffer.
@@ -547,8 +712,560 @@ static bool matchTopicFilter( const char * pTopicName,
const char * pTopicFilter,
uint16_t topicFilterLength );
+
+#if ( MQTT_VERSION_5_ENABLED )
+
+/**
+ * @brief Add a string ,its length and its id after serializing it in a manner outlined by
+ * the MQTT specification.
+ *
+ * @param[in] serializedLength Array of two bytes to which the vector will point.
+ * The array must remain in scope until the message has been sent.
+ * @param[in] string The string to be serialized.
+ * @param[in] length The length of the string to be serialized.
+ * @param[in] iterator The iterator pointing to the first element in the
+ * transport interface IO array.
+ * @param[out] updatedLength This parameter will be added to with the number of
+ * bytes added to the vector.
+ *@param[in] packetId Pointer to one byte used to add packet Id to the packet which must
+ * remain in scope until the message is sent.
+ *
+ * @return The number of vectors added.
+ */
+
+ static size_t addEncodedStringToVectorWithId( uint8_t serializedLength[ CORE_MQTT_SERIALIZED_LENGTH_FIELD_BYTES ],
+ const char * const string,
+ uint16_t length,
+ TransportOutVector_t * iterator,
+ size_t * updatedLength,
+ const uint8_t * packetId );
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+
+/**
+ * @brief Serialize the user properties.
+ *
+ * @param[in] pUserProperty Properties to serialize
+ * @param[in] pUserVector vectors used to encode.
+ * @param[in] pTotalMessageLength The iterator pointing to the first element in the
+ * transport interface IO array.
+ * @param[out] pVectorIterator This parameter will be added to with the number of
+ * bytes added to the vector.
+ *
+ * @return The number of vectors added.
+ */
+
+ static size_t sendUserProperties( const MQTTUserProperties_t * pUserProperty,
+ UserPropertyVector_t * pUserVector,
+ size_t * pTotalMessageLength,
+ TransportOutVector_t ** pVectorIterator );
+
+ #endif
+
+/**
+ * @brief Serialize the variable length publish properties.
+ *
+ * @param[in] pPublishInfo Properties to serialize
+ * @param[in] pPublishVector Vectors used to encode.
+ * @param[in] pTotalMessageLength The iterator pointing to the first element in the
+ * transport interface IO array.
+ * @param[out] pVectorIterator This parameter will be added to with the number of
+ * bytes added to the vector.
+ *
+ * @return The number of vectors added.
+ */
+
+ static size_t sendPublishProperties( const MQTTPublishInfo_t * pPublishInfo,
+ PublishVector_t * pPublishVector,
+ size_t * pTotalMessageLength,
+ TransportOutVector_t ** pVectorIterator );
+
+/**
+ * @brief Serialize the variable length connect properties.
+ *
+ * @param[in] pConnectProperties Properties to serialize
+ * @param[in] pPropertiesVector vectors used to encode.
+ * @param[in] pTotalMessageLength The iterator pointing to the first element in the
+ * transport interface IO array.
+ * @param[out] pVectorIterator This parameter will be added to with the number of
+ * bytes added to the vector.
+ *
+ * @return The number of vectors added.
+ */
+
+ static size_t sendConnectProperties( const MQTTConnectProperties_t * pConnectProperties,
+ PropertiesVector_t * pPropertiesVector,
+ size_t * pTotalMessageLength,
+ TransportOutVector_t ** pVectorIterator );
+
+/**
+ * @brief Send acks for received QoS 1/2 publishes with properties.
+ *
+ * @param[in] pContext MQTT Connection context.
+ * @param[in] packetId packet ID of original PUBLISH.
+ * @param[in] publishState Current publish state in record.
+ * @param[in] pAckInfo Reason code and properties.
+ *
+ *
+ * @return #MQTTSuccess, #MQTTBadParameter, #MQTTIllegalState or #MQTTSendFailed.
+ */
+
+ static MQTTStatus_t sendPublishAcksWithProperty( MQTTContext_t * pContext,
+ uint16_t packetId,
+ MQTTPublishState_t publishState,
+ MQTTAckInfo_t * pAckInfo );
+
+/**
+ * @brief Send acks for received QoS 1/2 publishes with properties.
+ *
+ * @param[in] pContext MQTT Connection context.
+ * @param[in] pDisconnectInfo Reason code and properties.
+ * @param[in] remainingLength Remaining length of the packet.
+ * @param[in] sessionExpiry Session expiry interval.
+ *
+ *
+ *
+ * @return #MQTTSuccess, #MQTTBadParameter, #MQTTIllegalState or #MQTTSendFailed.
+ */
+
+ static MQTTStatus_t sendDisconnectWithoutCopyV5( MQTTContext_t * pContext,
+ const MQTTAckInfo_t * pDisconnectInfo,
+ size_t remainingLength,
+ uint32_t sessionExpiry );
+
+/*-----------------------------------------------------------*/
+
+ static size_t addEncodedStringToVectorWithId( uint8_t serializedLength[ CORE_MQTT_SERIALIZED_LENGTH_FIELD_BYTES ],
+ const char * const string,
+ uint16_t length,
+ TransportOutVector_t * iterator,
+ size_t * updatedLength,
+ const uint8_t * packetId )
+ {
+ TransportOutVector_t * pLocalIterator = iterator;
+ size_t vectorsAdded = 0U;
+
+ /* Encode the property Id. */
+ pLocalIterator[ 0 ].iov_base = packetId;
+ pLocalIterator[ 0 ].iov_len = CORE_MQTT_ID_SIZE;
+ vectorsAdded++;
+ pLocalIterator++;
+
+ ( *updatedLength ) = ( *updatedLength ) + CORE_MQTT_ID_SIZE;
+
+ /* Encode the string. */
+ vectorsAdded += addEncodedStringToVector( serializedLength,
+ string,
+ length,
+ pLocalIterator,
+ updatedLength );
+
+ return vectorsAdded;
+ }
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ static size_t sendUserProperties( const MQTTUserProperties_t * pUserProperty,
+ UserPropertyVector_t * pUserVector,
+ size_t * pTotalMessageLength,
+ TransportOutVector_t ** pVectorIterator )
+ {
+ size_t vectorsAdded = 0U;
+ size_t ioVectorLength = 0U;
+ TransportOutVector_t * iterator = *pVectorIterator;
+ uint32_t i = 0;
+ uint32_t size = pUserProperty->count;
+ const MQTTUserProperty_t * userProperty = pUserProperty->userProperty;
+
+ for( ; i < size; i++ )
+ {
+ pUserVector->userId[ i ] = MQTT_USER_PROPERTY_ID;
+
+ /*Encode the key with the user property id.*/
+ vectorsAdded = addEncodedStringToVectorWithId( pUserVector->serializedUserKeyLength[ i ],
+ userProperty[ i ].pKey,
+ userProperty[ i ].keyLength,
+ iterator,
+ pTotalMessageLength, &pUserVector->userId[ i ] );
+ /* Update the iterator to point to the next empty slot. */
+ iterator = &iterator[ vectorsAdded ];
+ ioVectorLength += vectorsAdded;
+
+ /*Encode the value*/
+ vectorsAdded = addEncodedStringToVector( pUserVector->serializedUserValueLength[ i ],
+ userProperty[ i ].pValue,
+ userProperty[ i ].valueLength,
+ iterator,
+ pTotalMessageLength );
+ /* Update the iterator to point to the next empty slot. */
+ iterator = &iterator[ vectorsAdded ];
+ ioVectorLength += vectorsAdded;
+ }
+
+ *pVectorIterator = iterator;
+ return ioVectorLength;
+ }
+ #endif /* if ( MQTT_USER_PROPERTY_ENABLED ) */
+ static size_t sendPublishProperties( const MQTTPublishInfo_t * pPublishInfo,
+ PublishVector_t * pPublishVector,
+ size_t * pTotalMessageLength,
+ TransportOutVector_t ** pVectorIterator )
+ {
+ size_t vectorsAdded = 0U;
+ size_t ioVectorLength = 0U;
+ TransportOutVector_t * iterator = *pVectorIterator;
+
+ pPublishVector->contentTypeId = MQTT_CONTENT_TYPE_ID;
+ pPublishVector->responseTopicId = MQTT_RESPONSE_TOPIC_ID;
+ pPublishVector->correlationDataId = MQTT_CORRELATION_DATA_ID;
+
+ /* Encode the content type if provided.*/
+ if( pPublishInfo->contentTypeLength != 0U )
+ {
+ /* Serialize the content type string. */
+ vectorsAdded = addEncodedStringToVectorWithId( pPublishVector->serializedContentTypeLength,
+ pPublishInfo->pContentType,
+ pPublishInfo->contentTypeLength,
+ iterator,
+ pTotalMessageLength, &pPublishVector->contentTypeId );
+ /* Update the iterator to point to the next empty slot. */
+ iterator = &iterator[ vectorsAdded ];
+ ioVectorLength += vectorsAdded;
+ }
+
+ /* Encode the response topic if provided. */
+ if( pPublishInfo->responseTopicLength != 0U )
+ {
+ /* Serialize the response topic string. */
+ vectorsAdded = addEncodedStringToVectorWithId( pPublishVector->serializedResponseTopicLength,
+ pPublishInfo->pResponseTopic,
+ pPublishInfo->responseTopicLength,
+ iterator,
+ pTotalMessageLength, &pPublishVector->responseTopicId );
+ /* Update the iterator to point to the next empty slot. */
+ iterator = &iterator[ vectorsAdded ];
+ ioVectorLength += vectorsAdded;
+ }
+
+ /* Encode the correlation length if provided. */
+ if( pPublishInfo->correlationLength != 0U )
+ {
+ /* Serialize the correlation data string. */
+ vectorsAdded = addEncodedStringToVectorWithId( pPublishVector->serializedCorrelationLength,
+ pPublishInfo->pCorrelationData,
+ pPublishInfo->correlationLength,
+ iterator,
+ pTotalMessageLength, &pPublishVector->correlationDataId );
+ /* Update the iterator to point to the next empty slot. */
+ iterator = &iterator[ vectorsAdded ];
+ ioVectorLength += vectorsAdded;
+ }
+
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ /* Encode the user properties if provided. */
+ if( pPublishInfo->pUserProperty != NULL )
+ {
+ ioVectorLength += sendUserProperties( pPublishInfo->pUserProperty, &pPublishVector->userProperty, pTotalMessageLength, &iterator );
+ }
+ #endif
+
+ *pVectorIterator = iterator;
+ return ioVectorLength;
+ }
+
+
+ static size_t sendConnectProperties( const MQTTConnectProperties_t * pConnectProperties,
+ PropertiesVector_t * pPropertiesVector,
+ size_t * pTotalMessageLength,
+ TransportOutVector_t ** pVectorIterator )
+ {
+ size_t vectorsAdded = 0U;
+ size_t ioVectorLength = 0U;
+ TransportOutVector_t * iterator = *pVectorIterator;
+
+ pPropertiesVector->authMethodId = MQTT_AUTH_METHOD_ID;
+ pPropertiesVector->authDataId = MQTT_AUTH_DATA_ID;
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ /* Encode the user properties if provided. */
+ if( pConnectProperties->pOutgoingUserProperty != NULL )
+ {
+ ioVectorLength += sendUserProperties( pConnectProperties->pOutgoingUserProperty, &pPropertiesVector->userProperty, pTotalMessageLength, &iterator );
+ }
+ #endif
+
+ /*Encode the authentication method and data if provided*/
+ if( pConnectProperties->pOutgoingAuth != NULL )
+ {
+ /* Serialize the authentication method string. */
+ vectorsAdded = addEncodedStringToVectorWithId( pPropertiesVector->serializedAuthMethodLength,
+ pConnectProperties->pOutgoingAuth->pAuthMethod,
+ pConnectProperties->pOutgoingAuth->authMethodLength,
+ iterator,
+ pTotalMessageLength, &( pPropertiesVector->authMethodId ) );
+
+ /* Update the iterator to point to the next empty slot. */
+ iterator = &iterator[ vectorsAdded ];
+ ioVectorLength += vectorsAdded;
+
+ if( pConnectProperties->pOutgoingAuth->authDataLength != 0U )
+ {
+ /* Serialize the authentication data string. */
+ vectorsAdded = addEncodedStringToVectorWithId( pPropertiesVector->serializedAuthDataLength,
+ pConnectProperties->pOutgoingAuth->pAuthData,
+ pConnectProperties->pOutgoingAuth->authDataLength,
+ iterator,
+ pTotalMessageLength, &( pPropertiesVector->authDataId ) );
+
+ /* Update the iterator to point to the next empty slot. */
+ iterator = &iterator[ vectorsAdded ];
+ ioVectorLength += vectorsAdded;
+ }
+ }
+
+ *pVectorIterator = iterator;
+ return ioVectorLength;
+ }
+
+ static MQTTStatus_t sendPublishAcksWithProperty( MQTTContext_t * pContext,
+ uint16_t packetId,
+ MQTTPublishState_t publishState,
+ MQTTAckInfo_t * pAckInfo )
+ {
+ int32_t bytesSentOrError;
+ size_t vectorsAdded = 0U;
+ size_t ioVectorLength = 0U;
+ size_t totalMessageLength = 0U;
+ MQTTStatus_t status = MQTTSuccess;
+ MQTTPublishState_t newState = MQTTStateNull;
+ uint8_t packetTypeByte = 0U;
+ MQTTPubAckType_t packetType;
+
+ /* Maximum number of bytes required by the fixed size properties and header.
+ * MQTT Control Byte 0 + 1 = 1
+ * Remaining length (max) + 4 = 5
+ * Packet Identifier + 2 = 7
+ * Reason Code + 1 = 8
+ * Property length (max) + 4 = 12 */
+ uint8_t pubAckHeader[ 12 ];
+ size_t remainingLength = 0U;
+ size_t packetSize = 0U;
+ /* The maximum vectors required to encode and send a publish ack. */
+ TransportOutVector_t pIoVector[ 4 + MAX_USER_PROPERTY * 5 ];
+ uint8_t serializedReasonStringLength[ 2 ];
+ uint8_t reasonStringId = MQTT_REASON_STRING_ID;
+
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ UserPropertyVector_t userVector;
+ #endif
+ uint8_t * pIndex = pubAckHeader;
+ TransportOutVector_t * iterator = pIoVector;
+ assert( pContext != NULL );
+ assert( pAckInfo != NULL );
+ packetTypeByte = getAckTypeToSend( publishState );
+ status = MQTTV5_GetAckPacketSize( pAckInfo, &remainingLength, &packetSize, pContext->pConnectProperties->serverMaxPacketSize );
+
+ if( ( packetTypeByte != 0U ) && ( status == MQTTSuccess ) )
+ {
+ packetType = getAckFromPacketType( packetTypeByte );
+ /* Only for fixed size fields. */
+ pIndex = MQTTV5_SerializeAckFixed( pIndex,
+ packetTypeByte,
+ packetId,
+ remainingLength,
+ pAckInfo->propertyLength );
+ iterator->iov_base = pubAckHeader;
+ /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */
+ /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */
+ /* coverity[misra_c_2012_rule_18_2_violation] */
+ /* coverity[misra_c_2012_rule_10_8_violation] */
+ iterator->iov_len = ( size_t ) ( pIndex - pubAckHeader );
+ totalMessageLength += iterator->iov_len;
+ iterator++;
+ ioVectorLength++;
+
+ /* Encode the reason string if provided. */
+ if( pAckInfo->reasonStringLength != 0U )
+ {
+ vectorsAdded = addEncodedStringToVectorWithId( serializedReasonStringLength,
+ pAckInfo->pReasonString,
+ pAckInfo->reasonStringLength,
+ iterator,
+ &totalMessageLength, &reasonStringId );
+ /* Update the iterator to point to the next empty slot. */
+ iterator = &iterator[ vectorsAdded ];
+ ioVectorLength += vectorsAdded;
+ }
+
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ /*Encode the user properties if provided.*/
+ if( pAckInfo->pUserProperty != NULL )
+ {
+ ioVectorLength += sendUserProperties( pAckInfo->pUserProperty, &userVector, &totalMessageLength, &iterator );
+ }
+ #endif
+
+ bytesSentOrError = sendMessageVector( pContext, pIoVector, ioVectorLength );
+
+ if( bytesSentOrError != ( int32_t ) totalMessageLength )
+ {
+ LogError( ( "Failed to send ACK packet: PacketType=%02x, "
+ "PacketSize=%lu.",
+ ( unsigned int ) packetTypeByte,
+ packetSize ) );
+ status = MQTTSendFailed;
+ }
+
+ if( status == MQTTSuccess )
+ {
+ pContext->controlPacketSent = true;
+
+ MQTT_PRE_STATE_UPDATE_HOOK( pContext );
+
+ status = MQTT_UpdateStateAck( pContext,
+ packetId,
+ packetType,
+ MQTT_SEND,
+ &newState );
+
+ MQTT_POST_STATE_UPDATE_HOOK( pContext );
+
+ if( status != MQTTSuccess )
+ {
+ LogError( ( "Failed to update state of publish %hu.",
+ ( unsigned short ) packetId ) );
+ }
+ }
+ }
+ else
+ {
+ status = MQTTBadParameter;
+ }
+
+ return status;
+ }
+
+ static MQTTStatus_t sendDisconnectWithoutCopyV5( MQTTContext_t * pContext,
+ const MQTTAckInfo_t * pDisconnectInfo,
+ size_t remainingLength,
+ uint32_t sessionExpiry )
+ {
+ int32_t bytesSentOrError;
+ size_t vectorsAdded = 0U;
+ size_t ioVectorLength = 0U;
+ size_t totalMessageLength = 0U;
+ MQTTStatus_t status = MQTTSuccess;
+
+ /* Maximum number of bytes required by the fixed size part of the CONNECT
+ * packet header according to the MQTT specification.
+ * MQTT Control Byte 0 + 1 = 1
+ * Remaining length (max) + 4 = 5
+ * Reason Code + 1 = 6
+ * Property length (max) + 4 = 10
+ * Session Expiry + 5 = 15
+ */
+ uint8_t fixedHeader[ 15 ];
+
+ /* The maximum vectors required to encode and send a disconnect packet. The
+ * breakdown is shown below.
+ * Fixed header 0 + 1 = 1
+ * Reason String + 3 = 4
+ * User Property + MAX_USER_PROPERTY*5*
+ * */
+ TransportOutVector_t pIoVector[ 4 + MAX_USER_PROPERTY * 5 ];
+ uint8_t serializedReasonStringLength[ 2 ];
+ uint8_t reasonStringId = MQTT_REASON_STRING_ID;
+
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ UserPropertyVector_t userVector;
+ #endif
+ uint8_t * pIndex = fixedHeader;
+ TransportOutVector_t * iterator = pIoVector;
+ assert( pContext != NULL );
+ assert( pDisconnectInfo != NULL );
+
+ /* Only for fixed size fields. */
+ pIndex = MQTTV5_SerializeDisconnectFixed( pIndex, pDisconnectInfo, remainingLength, sessionExpiry );
+ iterator->iov_base = fixedHeader;
+ /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */
+ /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */
+ /* coverity[misra_c_2012_rule_18_2_violation] */
+ /* coverity[misra_c_2012_rule_10_8_violation] */
+ iterator->iov_len = ( size_t ) ( pIndex - fixedHeader );
+ totalMessageLength += iterator->iov_len;
+ iterator++;
+ ioVectorLength++;
+
+ /* Encode the reason string if provided. */
+ if( pDisconnectInfo->reasonStringLength != 0U )
+ {
+ vectorsAdded = addEncodedStringToVectorWithId( serializedReasonStringLength,
+ pDisconnectInfo->pReasonString,
+ pDisconnectInfo->reasonStringLength,
+ iterator,
+ &totalMessageLength, &reasonStringId );
+ /* Update the iterator to point to the next empty slot. */
+ iterator = &iterator[ vectorsAdded ];
+ ioVectorLength += vectorsAdded;
+ }
+
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ /*Encode the user properties if provided.*/
+ if( pDisconnectInfo->pUserProperty != NULL )
+ {
+ ioVectorLength += sendUserProperties( pDisconnectInfo->pUserProperty, &userVector, &totalMessageLength, &iterator );
+ }
+ #endif
+
+ bytesSentOrError = sendMessageVector( pContext, pIoVector, ioVectorLength );
+
+ if( bytesSentOrError != ( int32_t ) totalMessageLength )
+ {
+ status = MQTTSendFailed;
+ LogError( ( "Failed to send disconnect packet." ) );
+ }
+
+ return status;
+ }
+
+#endif /* if ( MQTT_VERSION_5_ENABLED ) */
+
/*-----------------------------------------------------------*/
+static size_t addEncodedStringToVector( uint8_t serializedLength[ CORE_MQTT_SERIALIZED_LENGTH_FIELD_BYTES ],
+ const char * const string,
+ uint16_t length,
+ TransportOutVector_t * iterator,
+ size_t * updatedLength )
+{
+ size_t packetLength = 0U;
+ TransportOutVector_t * pLocalIterator = iterator;
+ size_t vectorsAdded = 0U;
+
+ /* When length is non-zero, the string must be non-NULL. */
+ assert( ( length != 0U ) ? ( string != NULL ) : true );
+
+ serializedLength[ 0 ] = ( ( uint8_t ) ( ( length ) >> 8 ) );
+ serializedLength[ 1 ] = ( ( uint8_t ) ( ( length ) & 0x00ffU ) );
+
+ /* Add the serialized length of the string first. */
+ pLocalIterator[ 0 ].iov_base = serializedLength;
+ pLocalIterator[ 0 ].iov_len = CORE_MQTT_SERIALIZED_LENGTH_FIELD_BYTES;
+ vectorsAdded++;
+ packetLength = CORE_MQTT_SERIALIZED_LENGTH_FIELD_BYTES;
+
+ /* Sometimes the string can be NULL that is, of 0 length. In that case,
+ * only the length field should be encoded in the vector. */
+ if( ( string != NULL ) && ( length != 0U ) )
+ {
+ /* Then add the pointer to the string itself. */
+ pLocalIterator[ 1 ].iov_base = string;
+ pLocalIterator[ 1 ].iov_len = length;
+ vectorsAdded++;
+ packetLength += length;
+ }
+
+ ( *updatedLength ) = ( *updatedLength ) + packetLength;
+
+ return vectorsAdded;
+}
+
static bool matchEndWildcardsSpecialCases( const char * pTopicFilter,
uint16_t topicFilterLength,
uint16_t filterIndex )
@@ -1531,6 +2248,13 @@ static MQTTStatus_t handlePublishAcks( MQTTContext_t * pContext,
MQTTEventCallback_t appCallback;
MQTTDeserializedInfo_t deserializedInfo;
+ #if ( MQTT_VERSION_5_ENABLED )
+ MQTTAckInfo_t ackInfo;
+ MQTTAckInfo_t nextAckInfo;
+ ( void ) memset( &ackInfo, 0x0, sizeof( ackInfo ) );
+ ( void ) memset( &nextAckInfo, 0x0, sizeof( nextAckInfo ) );
+ #endif
+
assert( pContext != NULL );
assert( pIncomingPacket != NULL );
assert( pContext->appCallback != NULL );
@@ -1538,7 +2262,11 @@ static MQTTStatus_t handlePublishAcks( MQTTContext_t * pContext,
appCallback = pContext->appCallback;
ackType = getAckFromPacketType( pIncomingPacket->type );
- status = MQTT_DeserializeAck( pIncomingPacket, &packetIdentifier, NULL );
+ #if ( !MQTT_VERSION_5_ENABLED )
+ status = MQTT_DeserializeAck( pIncomingPacket, &packetIdentifier, NULL );
+ #else
+ status = MQTTV5_DeserializeAck( pIncomingPacket, &packetIdentifier, &ackInfo, pContext->pConnectProperties->requestProblemInfo, pContext->pConnectProperties->maxPacketSize );
+ #endif
LogInfo( ( "Ack packet deserialized with result: %s.",
MQTT_Status_strerror( status ) ) );
@@ -1568,22 +2296,53 @@ static MQTTStatus_t handlePublishAcks( MQTTContext_t * pContext,
}
}
- if( status == MQTTSuccess )
- {
- /* Set fields of deserialized struct. */
- deserializedInfo.packetIdentifier = packetIdentifier;
- deserializedInfo.deserializationResult = status;
- deserializedInfo.pPublishInfo = NULL;
+ #if ( !MQTT_VERSION_5_ENABLED )
+ if( status == MQTTSuccess )
+ {
+ /* Set fields of deserialized struct. */
+ deserializedInfo.packetIdentifier = packetIdentifier;
+ deserializedInfo.deserializationResult = status;
+ deserializedInfo.pPublishInfo = NULL;
- /* Invoke application callback to hand the buffer over to application
- * before sending acks. */
- appCallback( pContext, pIncomingPacket, &deserializedInfo );
+ /* Invoke application callback to hand the buffer over to application
+ * before sending acks. */
+ appCallback( pContext, pIncomingPacket, &deserializedInfo );
- /* Send PUBREL or PUBCOMP if necessary. */
- status = sendPublishAcks( pContext,
- packetIdentifier,
- publishRecordState );
- }
+ /* Send PUBREL or PUBCOMP if necessary. */
+ status = sendPublishAcks( pContext,
+ packetIdentifier,
+ publishRecordState );
+ }
+ #else /* if ( !MQTT_VERSION_5_ENABLED ) */
+ if( status == MQTTSuccess )
+ {
+ deserializedInfo.packetIdentifier = packetIdentifier;
+ deserializedInfo.deserializationResult = status;
+ deserializedInfo.pPublishInfo = NULL;
+ deserializedInfo.pAckInfo = &ackInfo;
+ deserializedInfo.pNextAckInfo = &nextAckInfo;
+
+ /* Invoke application callback to hand the buffer over to application
+ * before sending acks. */
+ appCallback( pContext, pIncomingPacket, &deserializedInfo );
+
+ /* Send PUBREL or PUBCOMP if necessary. */
+ if( deserializedInfo.pNextAckInfo == NULL )
+ {
+ status = sendPublishAcks( pContext,
+ packetIdentifier,
+ publishRecordState );
+ }
+ else
+ {
+ MQTT_PRE_SEND_HOOK( pContext );
+
+ status = sendPublishAcksWithProperty( pContext, packetIdentifier, publishRecordState, deserializedInfo.pNextAckInfo );
+
+ MQTT_POST_SEND_HOOK( pContext );
+ }
+ }
+ #endif /* if ( !MQTT_VERSION_5_ENABLED ) */
return status;
}
@@ -1776,22 +2535,48 @@ static MQTTStatus_t receiveSingleIteration( MQTTContext_t * pContext,
{
status = handleIncomingPublish( pContext, &incomingPacket );
}
+
+ #if ( MQTT_VERSION_5_ENABLED )
+ else if( ( incomingPacket.type == MQTT_PACKET_TYPE_DISCONNECT ) )
+ {
+ assert( pContext->pConnectProperties != NULL );
+ assert( pContext->pDisconnectInfo != NULL );
+ status = MQTTV5_DeserializeDisconnect( &incomingPacket,
+ pContext->pDisconnectInfo,
+ &pContext->pConnectProperties->pServerRef,
+ &pContext->pConnectProperties->serverRefLength,
+ pContext->pConnectProperties->maxPacketSize );
+
+ if( status == MQTTSuccess )
+ {
+ LogInfo( ( "Disconnected from the broker." ) );
+ pContext->connectStatus = MQTTNotConnected;
+
+ /* Reset the index and clean the buffer on a successful disconnect. */
+ pContext->index = 0;
+ ( void ) memset( pContext->networkBuffer.pBuffer, 0, pContext->networkBuffer.size );
+ }
+ }
+ #endif /* if ( MQTT_VERSION_5_ENABLED ) */
else
{
status = handleIncomingAck( pContext, &incomingPacket, manageKeepAlive );
}
- /* Update the index to reflect the remaining bytes in the buffer. */
- pContext->index -= totalMQTTPacketLength;
+ if( incomingPacket.type != MQTT_PACKET_TYPE_DISCONNECT )
+ {
+ /* Update the index to reflect the remaining bytes in the buffer. */
+ pContext->index -= totalMQTTPacketLength;
- /* Move the remaining bytes to the front of the buffer. */
- ( void ) memmove( pContext->networkBuffer.pBuffer,
- &( pContext->networkBuffer.pBuffer[ totalMQTTPacketLength ] ),
- pContext->index );
+ /* Move the remaining bytes to the front of the buffer. */
+ ( void ) memmove( pContext->networkBuffer.pBuffer,
+ &( pContext->networkBuffer.pBuffer[ totalMQTTPacketLength ] ),
+ pContext->index );
- if( status == MQTTSuccess )
- {
- pContext->lastPacketRxTime = pContext->getTime();
+ if( status == MQTTSuccess )
+ {
+ pContext->lastPacketRxTime = pContext->getTime();
+ }
}
}
@@ -1858,44 +2643,6 @@ static MQTTStatus_t validateSubscribeUnsubscribeParams( const MQTTContext_t * pC
/*-----------------------------------------------------------*/
-static size_t addEncodedStringToVector( uint8_t serializedLength[ CORE_MQTT_SERIALIZED_LENGTH_FIELD_BYTES ],
- const char * const string,
- uint16_t length,
- TransportOutVector_t * iterator,
- size_t * updatedLength )
-{
- size_t packetLength = 0U;
- TransportOutVector_t * pLocalIterator = iterator;
- size_t vectorsAdded = 0U;
-
- /* When length is non-zero, the string must be non-NULL. */
- assert( ( length != 0U ) ? ( string != NULL ) : true );
-
- serializedLength[ 0 ] = ( ( uint8_t ) ( ( length ) >> 8 ) );
- serializedLength[ 1 ] = ( ( uint8_t ) ( ( length ) & 0x00ffU ) );
-
- /* Add the serialized length of the string first. */
- pLocalIterator[ 0 ].iov_base = serializedLength;
- pLocalIterator[ 0 ].iov_len = CORE_MQTT_SERIALIZED_LENGTH_FIELD_BYTES;
- vectorsAdded++;
- packetLength = CORE_MQTT_SERIALIZED_LENGTH_FIELD_BYTES;
-
- /* Sometimes the string can be NULL that is, of 0 length. In that case,
- * only the length field should be encoded in the vector. */
- if( ( string != NULL ) && ( length != 0U ) )
- {
- /* Then add the pointer to the string itself. */
- pLocalIterator[ 1 ].iov_base = string;
- pLocalIterator[ 1 ].iov_len = length;
- vectorsAdded++;
- packetLength += length;
- }
-
- ( *updatedLength ) = ( *updatedLength ) + packetLength;
-
- return vectorsAdded;
-}
-
/*-----------------------------------------------------------*/
static MQTTStatus_t sendSubscribeWithoutCopy( MQTTContext_t * pContext,
@@ -2113,7 +2860,30 @@ static MQTTStatus_t sendPublishWithoutCopy( MQTTContext_t * pContext,
* Topic string + 1 = 2
* Packet ID (only when QoS > QoS0) + 1 = 3
* Payload + 1 = 4 */
- TransportOutVector_t pIoVector[ 4U ];
+ #if ( !MQTT_VERSION_5_ENABLED )
+ TransportOutVector_t pIoVector[ 4U ];
+ #else
+
+ /*
+ * Fixed sized properties + 1 = 5
+ * Response topic + 3 = 8
+ * Correlation data + 3 = 11
+ * Content type + 3 = 14
+ * User property 5
+ */
+ TransportOutVector_t pIoVector[ 5 * MAX_USER_PROPERTY + 14 ];
+
+ /* Maximum number of bytes required by the fixed size publish properties.
+ * Property length 0 + 4 = 4
+ * Payload Format Indicator + 2 = 6
+ * Message Expiry + 5 = 11
+ * Topic Alias + 3 = 14 */
+ uint8_t serializedProperty[ 14U ];
+ uint8_t * pIndex;
+ PublishVector_t publishVector;
+ TransportOutVector_t * iterator;
+ #endif
+
/* The header is sent first. */
pIoVector[ 0U ].iov_base = pMqttHeader;
@@ -2142,6 +2912,23 @@ static MQTTStatus_t sendPublishWithoutCopy( MQTTContext_t * pContext,
totalMessageLength += sizeof( serializedPacketID );
}
+ #if ( MQTT_VERSION_5_ENABLED )
+ /*Serialize the fixed publish properties.*/
+ pIndex = serializedProperty;
+ iterator = &pIoVector[ ioVectorLength ];
+ pIndex = MQTT_SerializePublishProperties( pPublishInfo, pIndex );
+ iterator->iov_base = serializedProperty;
+ /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */
+ /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */
+ /* coverity[misra_c_2012_rule_18_2_violation] */
+ /* coverity[misra_c_2012_rule_10_8_violation] */
+ iterator->iov_len = ( size_t ) ( pIndex - serializedProperty );
+ totalMessageLength += iterator->iov_len;
+ iterator++;
+ ioVectorLength++;
+ ioVectorLength += sendPublishProperties( pPublishInfo, &publishVector, &totalMessageLength, &iterator );
+ #endif /* if ( MQTT_VERSION_5_ENABLED ) */
+
/* Publish packets are allowed to contain no payload. */
if( pPublishInfo->payloadLength > 0U )
{
@@ -2180,16 +2967,50 @@ static MQTTStatus_t sendConnectWithoutCopy( MQTTContext_t * pContext,
uint8_t serializedPasswordLength[ 2 ];
size_t vectorsAdded;
- /* Maximum number of bytes required by the 'fixed' part of the CONNECT
- * packet header according to the MQTT specification.
- * MQTT Control Byte 0 + 1 = 1
- * Remaining length (max) + 4 = 5
- * Protocol Name Length + 2 = 7
- * Protocol Name (MQTT) + 4 = 11
- * Protocol level + 1 = 12
- * Connect flags + 1 = 13
- * Keep alive + 2 = 15 */
- uint8_t connectPacketHeader[ 15U ];
+ #if ( !MQTT_VERSION_5_ENABLED )
+
+ /* Maximum number of bytes required by the 'fixed' part of the CONNECT
+ * packet header according to the MQTT specification.
+ * MQTT Control Byte 0 + 1 = 1
+ * Remaining length (max) + 4 = 5
+ * Protocol Name Length + 2 = 7
+ * Protocol Name (MQTT) + 4 = 11
+ * Protocol level + 1 = 12
+ * Connect flags + 1 = 13
+ * Keep alive + 2 = 15 */
+ uint8_t connectPacketHeader[ 15U ];
+ #else
+
+ /* Maximum number of bytes required by the fixed part of the CONNECT
+ * packet header according to the MQTT specification.
+ * MQTT Control Byte 0 + 1 = 1
+ * Remaining length (max) + 4 = 5
+ * Protocol Name Length + 2 = 7
+ * Protocol Name (MQTT) + 4 = 11
+ * Protocol level + 1 = 12
+ * Connect flags + 1 = 13
+ * Keep alive + 2 = 15
+ * Properties length + 4 = 19
+ * Session Expiry + 5 = 24
+ * receive Maximum + 3 = 27
+ * Max packet Size + 5 = 32
+ * Topic Alias Maximum + 3 = 35
+ * Request response Information + 2 = 37
+ * Request problem Information + 2 = 39
+ * Total- 39
+ */
+
+ uint8_t connectPacketHeader[ 39U ];
+
+ /* Maximum number of bytes required by the fixed size will properties.
+ * Property length 0 + 4 = 4
+ * Will delay + 5 = 9
+ * Payload Format Indicator + 2 = 11
+ * Message Expiry + 5 = 16 */
+ uint8_t fixedSizeProperties[ 16U ];
+ PublishVector_t willVector;
+ PropertiesVector_t propertiesVector;
+ #endif /* if ( !MQTT_VERSION_5_ENABLED ) */
/* The maximum vectors required to encode and send a connect packet. The
* breakdown is shown below.
@@ -2199,7 +3020,19 @@ static MQTTStatus_t sendConnectWithoutCopy( MQTTContext_t * pContext,
* Will payload + 2 = 7
* Username + 2 = 9
* Password + 2 = 11 */
- TransportOutVector_t pIoVector[ 11U ];
+ #if ( MQTT_VERSION_5_ENABLED == 0 )
+ TransportOutVector_t pIoVector[ 11U ];
+ #else
+
+ /*
+ *
+ * User Property- 11 + 5* Max userProperty
+ * Authentication 17 + 5* Max userProperty
+ * Will Properties 27 + 10* Max userProperty
+ *
+ */
+ TransportOutVector_t pIoVector[ 27 + 10 * MAX_USER_PROPERTY ];
+ #endif
iterator = pIoVector;
pIndex = connectPacketHeader;
@@ -2217,6 +3050,9 @@ static MQTTStatus_t sendConnectWithoutCopy( MQTTContext_t * pContext,
pWillInfo,
remainingLength );
+ #if ( MQTT_VERSION_5_ENABLED )
+ pIndex = MQTTV5_SerializeConnectProperties( pIndex, pContext->pConnectProperties );
+ #endif
assert( ( ( size_t ) ( pIndex - connectPacketHeader ) ) <= sizeof( connectPacketHeader ) );
/* The header gets sent first. */
@@ -2230,6 +3066,11 @@ static MQTTStatus_t sendConnectWithoutCopy( MQTTContext_t * pContext,
iterator++;
ioVectorLength++;
+ #if ( MQTT_VERSION_5_ENABLED )
+ /*Encode the connect Properties if provided*/
+ ioVectorLength += sendConnectProperties( pContext->pConnectProperties, &propertiesVector, &totalMessageLength, &iterator );
+ #endif
+
/* Serialize the client ID. */
vectorsAdded = addEncodedStringToVector( serializedClientIDLength,
pConnectInfo->pClientIdentifier,
@@ -2243,6 +3084,21 @@ static MQTTStatus_t sendConnectWithoutCopy( MQTTContext_t * pContext,
if( pWillInfo != NULL )
{
+ #if ( MQTT_VERSION_5_ENABLED )
+ /*Serialize the will properties*/
+ pIndex = fixedSizeProperties;
+ pIndex = MQTT_SerializePublishProperties( pWillInfo, pIndex );
+ iterator->iov_base = fixedSizeProperties;
+ /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */
+ /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */
+ /* coverity[misra_c_2012_rule_18_2_violation] */
+ /* coverity[misra_c_2012_rule_10_8_violation] */
+ iterator->iov_len = ( size_t ) ( pIndex - fixedSizeProperties );
+ totalMessageLength += iterator->iov_len;
+ iterator++;
+ ioVectorLength++;
+ ioVectorLength += sendPublishProperties( pWillInfo, &willVector, &totalMessageLength, &iterator );
+ #endif /* if ( MQTT_VERSION_5_ENABLED ) */
/* Serialize the topic. */
vectorsAdded = addEncodedStringToVector( serializedTopicLength,
pWillInfo->pTopicName,
@@ -2254,7 +3110,6 @@ static MQTTStatus_t sendConnectWithoutCopy( MQTTContext_t * pContext,
iterator = &iterator[ vectorsAdded ];
ioVectorLength += vectorsAdded;
-
/* Serialize the payload. Payload of last will and testament can be NULL. */
vectorsAdded = addEncodedStringToVector( serializedPayloadLength,
pWillInfo->pPayload,
@@ -2403,7 +3258,11 @@ static MQTTStatus_t receiveConnack( const MQTTContext_t * pContext,
pIncomingPacket->pRemainingData = pContext->networkBuffer.pBuffer;
/* Deserialize CONNACK. */
- status = MQTT_DeserializeAck( pIncomingPacket, NULL, pSessionPresent );
+ #if ( MQTT_VERSION_5_ENABLED == 0 )
+ status = MQTT_DeserializeAck( pIncomingPacket, NULL, pSessionPresent );
+ #else
+ status = MQTTV5_DeserializeConnack( pContext->pConnectProperties, pIncomingPacket, pSessionPresent );
+ #endif
}
/* If a clean session is requested, a session present should not be set by
@@ -2518,6 +3377,15 @@ static MQTTStatus_t validatePublishParams( const MQTTContext_t * pContext,
"to initialize and enable the use of QoS1/QoS2 publishes." ) );
status = MQTTBadParameter;
}
+
+ #if ( MQTT_VERSION_5_ENABLED )
+ else if( pContext->pConnectProperties == NULL )
+ {
+ LogError( ( "Argument cannot be NULL: pConnectProperties=%p. ",
+ ( void * ) pContext->pConnectProperties ) );
+ status = MQTTBadParameter;
+ }
+ #endif
else
{
/* MISRA else */
@@ -2678,7 +3546,7 @@ MQTTStatus_t MQTT_CancelCallback( const MQTTContext_t * pContext,
MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext,
const MQTTConnectInfo_t * pConnectInfo,
- const MQTTPublishInfo_t * pWillInfo,
+ MQTTPublishInfo_t * pWillInfo,
uint32_t timeoutMs,
bool * pSessionPresent )
{
@@ -2700,14 +3568,26 @@ MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext,
if( status == MQTTSuccess )
{
- /* Get MQTT connect packet size and remaining length. */
- status = MQTT_GetConnectPacketSize( pConnectInfo,
- pWillInfo,
- &remainingLength,
- &packetSize );
- LogDebug( ( "CONNECT packet size is %lu and remaining length is %lu.",
- ( unsigned long ) packetSize,
- ( unsigned long ) remainingLength ) );
+ #if ( MQTT_VERSION_5_ENABLED )
+ /* Get MQTT connect packet size and remaining length. */
+ status = MQTTV5_GetConnectPacketSize( pConnectInfo,
+ pWillInfo,
+ pContext->pConnectProperties,
+ &remainingLength,
+ &packetSize );
+ LogDebug( ( "CONNECT packet size is %lu and remaining length is %lu.",
+ ( unsigned long ) packetSize,
+ ( unsigned long ) remainingLength ) );
+ #else
+ /* Get MQTT connect packet size and remaining length. */
+ status = MQTT_GetConnectPacketSize( pConnectInfo,
+ pWillInfo,
+ &remainingLength,
+ &packetSize );
+ LogDebug( ( "CONNECT packet size is %lu and remaining length is %lu.",
+ ( unsigned long ) packetSize,
+ ( unsigned long ) remainingLength ) );
+ #endif /* if ( MQTT_VERSION_5_ENABLED ) */
}
if( status == MQTTSuccess )
@@ -2803,7 +3683,7 @@ MQTTStatus_t MQTT_Subscribe( MQTTContext_t * pContext,
/*-----------------------------------------------------------*/
MQTTStatus_t MQTT_Publish( MQTTContext_t * pContext,
- const MQTTPublishInfo_t * pPublishInfo,
+ MQTTPublishInfo_t * pPublishInfo,
uint16_t packetId )
{
size_t headerSize = 0UL;
@@ -2831,9 +3711,24 @@ MQTTStatus_t MQTT_Publish( MQTTContext_t * pContext,
if( status == MQTTSuccess )
{
/* Get the remaining length and packet size.*/
- status = MQTT_GetPublishPacketSize( pPublishInfo,
- &remainingLength,
- &packetSize );
+ #if ( !MQTT_VERSION_5_ENABLED )
+ status = MQTT_GetPublishPacketSize( pPublishInfo,
+ &remainingLength,
+ &packetSize );
+ #else
+ status = MQTTV5_ValidatePublishParams( pPublishInfo,
+ pContext->pConnectProperties->serverTopicAliasMax,
+ pContext->pConnectProperties->retainAvailable,
+ pContext->pConnectProperties->serverMaxQos );
+
+ if( status == MQTTSuccess )
+ {
+ status = MQTTV5_GetPublishPacketSize( pPublishInfo,
+ &remainingLength,
+ &packetSize,
+ pContext->pConnectProperties->serverMaxPacketSize );
+ }
+ #endif /* if ( !MQTT_VERSION_5_ENABLED ) */
}
if( status == MQTTSuccess )
@@ -3388,4 +4283,53 @@ const char * MQTT_Status_strerror( MQTTStatus_t status )
return str;
}
+#if ( MQTT_VERSION_5_ENABLED )
+ MQTTStatus_t MQTTV5_Disconnect( MQTTContext_t * pContext,
+ MQTTAckInfo_t * pDisconnectInfo,
+ uint32_t sessionExpiry )
+ {
+ size_t packetSize = 0U;
+ size_t remainingLength = 0U;
+ MQTTStatus_t status = MQTTSuccess;
+
+ /* Validate arguments. */
+ if( ( pContext == NULL ) || ( pDisconnectInfo == NULL ) || ( pContext->pConnectProperties == NULL ) )
+ {
+ LogError( ( "pContext, pDisconnectInfo and connect properties cannot be NULL." ) );
+ status = MQTTBadParameter;
+ }
+
+ if( status == MQTTSuccess )
+ {
+ /* Get MQTT DISCONNECT packet size. */
+ status = MQTTV5_GetDisconnectPacketSize( pDisconnectInfo, &remainingLength, &packetSize, pContext->pConnectProperties->serverMaxPacketSize, sessionExpiry, pContext->pConnectProperties->sessionExpiry );
+ LogDebug( ( "MQTT DISCONNECT packet size is %lu.",
+ ( unsigned long ) packetSize ) );
+ }
+
+ if( status == MQTTSuccess )
+ {
+ /* Take the mutex because the below call should not be interrupted. */
+ MQTT_PRE_SEND_HOOK( pContext );
+
+ status = sendDisconnectWithoutCopyV5( pContext, pDisconnectInfo, remainingLength, sessionExpiry );
+
+ /* Give the mutex away. */
+ MQTT_POST_SEND_HOOK( pContext );
+ }
+
+ if( status == MQTTSuccess )
+ {
+ LogInfo( ( "Disconnected from the broker." ) );
+ pContext->connectStatus = MQTTNotConnected;
+
+ /* Reset the index and clean the buffer on a successful disconnect. */
+ pContext->index = 0;
+ ( void ) memset( pContext->networkBuffer.pBuffer, 0, pContext->networkBuffer.size );
+ }
+
+ return status;
+ }
+#endif /* if ( MQTT_VERSION_5_ENABLED ) */
+
/*-----------------------------------------------------------*/
diff --git a/source/core_mqtt_serializer.c b/source/core_mqtt_serializer.c
index 97022034c..055168ac8 100644
--- a/source/core_mqtt_serializer.c
+++ b/source/core_mqtt_serializer.c
@@ -31,8 +31,6 @@
#include "core_mqtt_serializer.h"
-/* Include config defaults header to get default values of configs. */
-#include "core_mqtt_config_defaults.h"
/**
* @brief MQTT protocol version 3.1.1.
@@ -96,559 +94,2880 @@
*/
#define MQTT_MAX_REMAINING_LENGTH ( 268435455UL )
+#if ( MQTT_VERSION_5_ENABLED )
+
/**
- * @brief Set a bit in an 8-bit unsigned integer.
+ * @brief Per the MQTT spec, the max packet size can be of max remaining length + 5 bytes
*/
-#define UINT8_SET_BIT( x, position ) ( ( x ) = ( uint8_t ) ( ( x ) | ( 0x01U << ( position ) ) ) )
+ #define MQTT_MAX_PACKET_SIZE ( 268435460U )
/**
- * @brief Macro for checking if a bit is set in a 1-byte unsigned int.
- *
- * @param[in] x The unsigned int to check.
- * @param[in] position Which bit to check.
+ * @brief Version 5 has the value 5.
*/
-#define UINT8_CHECK_BIT( x, position ) ( ( ( x ) & ( 0x01U << ( position ) ) ) == ( 0x01U << ( position ) ) )
+ #define MQTT_VERSION_5 ( 5U )
/**
- * @brief Get the high byte of a 16-bit unsigned integer.
+ * @brief Per the MQTT 5 spec, the session expiry is of 5 bytes.
*/
-#define UINT16_HIGH_BYTE( x ) ( ( uint8_t ) ( ( x ) >> 8 ) )
+ #define MQTT_SESSION_EXPIRY_SIZE ( 5U )
/**
- * @brief Get the low byte of a 16-bit unsigned integer.
+ * @brief Per the MQTT 5 spec, the receive maximum is of 3 bytes.
*/
-#define UINT16_LOW_BYTE( x ) ( ( uint8_t ) ( ( x ) & 0x00ffU ) )
+ #define MQTT_RECEIVE_MAX_SIZE ( 3U )
/**
- * @brief Macro for decoding a 2-byte unsigned int from a sequence of bytes.
- *
- * @param[in] ptr A uint8_t* that points to the high byte.
+ * @brief Per the MQTT 5 spec, the max packet size is of 5 bytes.
*/
-#define UINT16_DECODE( ptr ) \
- ( uint16_t ) ( ( ( ( uint16_t ) ptr[ 0 ] ) << 8 ) | \
- ( ( uint16_t ) ptr[ 1 ] ) )
+ #define MQTT_MAX_PACKET_PROPERTY_SIZE ( 5U )
/**
- * @brief A value that represents an invalid remaining length.
- *
- * This value is greater than what is allowed by the MQTT specification.
+ * @brief Per the MQTT 5 spec, the topic alias is of 3 bytes.
*/
-#define MQTT_REMAINING_LENGTH_INVALID ( ( size_t ) 268435456 )
+ #define MQTT_TOPIC_ALIAS_SIZE ( 3U )
/**
- * @brief The minimum remaining length for a QoS 0 PUBLISH.
- *
- * Includes two bytes for topic name length and one byte for topic name.
+ * @brief Per the MQTT 5 spec, the request response is of 2 bytes.
*/
-#define MQTT_MIN_PUBLISH_REMAINING_LENGTH_QOS0 ( 3U )
+ #define MQTT_REQUEST_RESPONSE_SIZE ( 2U )
-/*-----------------------------------------------------------*/
+/**
+ * @brief Per the MQTT 5 spec, the request problem is of 2 bytes.
+ */
+ #define MQTT_REQUEST_PROBLEM_SIZE ( 2U )
+
+/**
+ * @brief Utf 8 encoded string has 2 byte length field and 1 byte property id.
+ */
+ #define MQTT_UTF8_LENGTH_SIZE ( 3U )
+/*Publish Properties*/
/**
- * @brief MQTT Subscription packet types.
+ * @brief Per the MQTT 5 spec, the will delay is of 2 bytes.
*/
-typedef enum MQTTSubscriptionType
-{
- MQTT_SUBSCRIBE, /**< @brief The type is a SUBSCRIBE packet. */
- MQTT_UNSUBSCRIBE /**< @brief The type is a UNSUBSCRIBE packet. */
-} MQTTSubscriptionType_t;
+ #define MQTT_WILL_DELAY_SIZE ( 5U )
-/*-----------------------------------------------------------*/
+/**
+ * @brief Per the MQTT 5 spec, the payload format indicator is of 2 bytes.
+ */
+ #define MQTT_PAYLOAD_FORMAT_SIZE ( 2U )
/**
- * @brief Serializes MQTT PUBLISH packet into the buffer provided.
- *
- * This function serializes MQTT PUBLISH packet into #MQTTFixedBuffer_t.pBuffer.
- * Copy of the payload into the buffer is done as part of the serialization
- * only if @p serializePayload is true.
- *
- * @brief param[in] pPublishInfo Publish information.
- * @brief param[in] remainingLength Remaining length of the PUBLISH packet.
- * @brief param[in] packetIdentifier Packet identifier of PUBLISH packet.
- * @brief param[in, out] pFixedBuffer Buffer to which PUBLISH packet will be
- * serialized.
- * @brief param[in] serializePayload Copy payload to the serialized buffer
- * only if true. Only PUBLISH header will be serialized if false.
+ * @brief Per the MQTT 5 spec, the request problem is of 2 bytes.
*/
-static void serializePublishCommon( const MQTTPublishInfo_t * pPublishInfo,
- size_t remainingLength,
- uint16_t packetIdentifier,
- const MQTTFixedBuffer_t * pFixedBuffer,
- bool serializePayload );
+ #define MQTT_MSG_EXPIRY_SIZE ( 5U )
+
+/*CONNECT PROPERTIES*/
/**
- * @brief Calculates the packet size and remaining length of an MQTT
- * PUBLISH packet.
- *
- * @param[in] pPublishInfo MQTT PUBLISH packet parameters.
- * @param[out] pRemainingLength The Remaining Length of the MQTT PUBLISH packet.
- * @param[out] pPacketSize The total size of the MQTT PUBLISH packet.
- *
- * @return false if the packet would exceed the size allowed by the
- * MQTT spec; true otherwise.
+ * @brief Session expiry id.
*/
-static bool calculatePublishPacketSize( const MQTTPublishInfo_t * pPublishInfo,
- size_t * pRemainingLength,
- size_t * pPacketSize );
+ #define MQTT_SESSION_EXPIRY_ID ( 0x11 )
/**
- * @brief Calculates the packet size and remaining length of an MQTT
- * SUBSCRIBE or UNSUBSCRIBE packet.
- *
- * @param[in] pSubscriptionList List of MQTT subscription info.
- * @param[in] subscriptionCount The number of elements in pSubscriptionList.
- * @param[out] pRemainingLength The Remaining Length of the MQTT SUBSCRIBE or
- * UNSUBSCRIBE packet.
- * @param[out] pPacketSize The total size of the MQTT MQTT SUBSCRIBE or
- * UNSUBSCRIBE packet.
- * @param[in] subscriptionType #MQTT_SUBSCRIBE or #MQTT_UNSUBSCRIBE.
- *
- * #MQTTBadParameter if the packet would exceed the size allowed by the
- * MQTT spec or a subscription is empty; #MQTTSuccess otherwise.
+ * @brief Receive maximum id.
*/
-static MQTTStatus_t calculateSubscriptionPacketSize( const MQTTSubscribeInfo_t * pSubscriptionList,
- size_t subscriptionCount,
- size_t * pRemainingLength,
- size_t * pPacketSize,
- MQTTSubscriptionType_t subscriptionType );
+ #define MQTT_RECEIVE_MAX_ID ( 0x21 )
/**
- * @brief Validates parameters of #MQTT_SerializeSubscribe or
- * #MQTT_SerializeUnsubscribe.
- *
- * @param[in] pSubscriptionList List of MQTT subscription info.
- * @param[in] subscriptionCount The number of elements in pSubscriptionList.
- * @param[in] packetId Packet identifier.
- * @param[in] remainingLength Remaining length of the packet.
- * @param[in] pFixedBuffer Buffer for packet serialization.
- *
- * @return #MQTTNoMemory if pBuffer is too small to hold the MQTT packet;
- * #MQTTBadParameter if invalid parameters are passed;
- * #MQTTSuccess otherwise.
+ * @brief Maximum packet size id.
*/
-static MQTTStatus_t validateSubscriptionSerializeParams( const MQTTSubscribeInfo_t * pSubscriptionList,
- size_t subscriptionCount,
- uint16_t packetId,
- size_t remainingLength,
- const MQTTFixedBuffer_t * pFixedBuffer );
+ #define MQTT_MAX_PACKET_SIZE_ID ( 0x27 )
/**
- * @brief Serialize an MQTT CONNECT packet in the given buffer.
- *
- * @param[in] pConnectInfo MQTT CONNECT packet parameters.
- * @param[in] pWillInfo Last Will and Testament. Pass NULL if not used.
- * @param[in] remainingLength Remaining Length of MQTT CONNECT packet.
- * @param[out] pFixedBuffer Buffer for packet serialization.
+ * @brief Topic alias size id.
*/
-static void serializeConnectPacket( const MQTTConnectInfo_t * pConnectInfo,
- const MQTTPublishInfo_t * pWillInfo,
- size_t remainingLength,
- const MQTTFixedBuffer_t * pFixedBuffer );
+ #define MQTT_TOPIC_ALIAS_MAX_ID ( 0x22 )
/**
- * @brief Prints the appropriate message for the CONNACK response code if logs
- * are enabled.
- *
- * @param[in] responseCode MQTT standard CONNACK response code.
+ * @brief Request response id.
*/
-static void logConnackResponse( uint8_t responseCode );
+ #define MQTT_REQUEST_RESPONSE_ID ( 0x19 )
/**
- * @brief Encodes the remaining length of the packet using the variable length
- * encoding scheme provided in the MQTT v3.1.1 specification.
- *
- * @param[out] pDestination The destination buffer to store the encoded remaining
- * length.
- * @param[in] length The remaining length to encode.
- *
- * @return The location of the byte following the encoded value.
+ * @brief Request problem id.
*/
-static uint8_t * encodeRemainingLength( uint8_t * pDestination,
- size_t length );
+ #define MQTT_REQUEST_PROBLEM_ID ( 0x17 )
/**
- * @brief Retrieve the size of the remaining length if it were to be encoded.
- *
- * @param[in] length The remaining length to be encoded.
- *
- * @return The size of the remaining length if it were to be encoded.
+ * @brief User property id.
*/
-static size_t remainingLengthEncodedSize( size_t length );
+ #define MQTT_USER_PROPERTY_ID ( 0x26 )
/**
- * @brief Encode a string whose size is at maximum 16 bits in length.
- *
- * @param[out] pDestination Destination buffer for the encoding.
- * @param[in] pSource The source string to encode.
- * @param[in] sourceLength The length of the source string to encode.
- *
- * @return A pointer to the end of the encoded string.
+ * @brief Authentication method id.
*/
-static uint8_t * encodeString( uint8_t * pDestination,
- const char * pSource,
- uint16_t sourceLength );
+ #define MQTT_AUTH_METHOD_ID ( 0x15 )
/**
- * @brief Retrieves and decodes the Remaining Length from the network interface
- * by reading a single byte at a time.
- *
- * @param[in] recvFunc Network interface receive function.
- * @param[in] pNetworkContext Network interface context to the receive function.
- *
- * @return The Remaining Length of the incoming packet.
+ * @brief Authentication data id.
*/
-static size_t getRemainingLength( TransportRecv_t recvFunc,
- NetworkContext_t * pNetworkContext );
+ #define MQTT_AUTH_DATA_ID ( 0x16 )
+
+/*Publish PROPERTIES*/
/**
- * @brief Retrieves, decodes and stores the Remaining Length from the network
- * interface by reading a single byte at a time.
- *
- * @param[in] pBuffer The buffer holding the raw data to be processed
- * @param[in] pIndex Pointer to the index within the buffer to marking the end of raw data
- * available.
- * @param[in] pIncomingPacket Structure used to hold the fields of the
- * incoming packet.
- *
- * @return MQTTNeedMoreBytes is returned to show that the incoming
- * packet is not yet fully received and decoded. Otherwise, MQTTSuccess
- * shows that processing of the packet was successful.
+ * @brief Will delay id.
*/
-static MQTTStatus_t processRemainingLength( const uint8_t * pBuffer,
- const size_t * pIndex,
- MQTTPacketInfo_t * pIncomingPacket );
+ #define MQTT_WILL_DELAY_ID ( 0x18 )
/**
- * @brief Check if an incoming packet type is valid.
- *
- * @param[in] packetType The packet type to check.
- *
- * @return `true` if the packet type is valid; `false` otherwise.
+ * @brief Payload format id.
*/
-static bool incomingPacketValid( uint8_t packetType );
+ #define MQTT_PAYLOAD_FORMAT_ID ( 0x01 )
/**
- * @brief Check the remaining length of an incoming PUBLISH packet against some
- * value for QoS 0, or for QoS 1 and 2.
- *
- * The remaining length for a QoS 1 and 2 packet will always be two greater than
- * for a QoS 0.
- *
- * @param[in] remainingLength Remaining length of the PUBLISH packet.
- * @param[in] qos The QoS of the PUBLISH.
- * @param[in] qos0Minimum Minimum possible remaining length for a QoS 0 PUBLISH.
- *
- * @return #MQTTSuccess or #MQTTBadResponse.
+ * @brief Message Expiry id.
*/
-static MQTTStatus_t checkPublishRemainingLength( size_t remainingLength,
- MQTTQoS_t qos,
- size_t qos0Minimum );
+ #define MQTT_MSG_EXPIRY_ID ( 0x02 )
/**
- * @brief Process the flags of an incoming PUBLISH packet.
- *
- * @param[in] publishFlags Flags of an incoming PUBLISH.
- * @param[in, out] pPublishInfo Pointer to #MQTTPublishInfo_t struct where
- * output will be written.
- *
- * @return #MQTTSuccess or #MQTTBadResponse.
+ * @brief Content type id.
*/
-static MQTTStatus_t processPublishFlags( uint8_t publishFlags,
- MQTTPublishInfo_t * pPublishInfo );
+ #define MQTT_CONTENT_TYPE_ID ( 0x03 )
/**
- * @brief Deserialize a CONNACK packet.
- *
- * Converts the packet from a stream of bytes to an #MQTTStatus_t.
- *
- * @param[in] pConnack Pointer to an MQTT packet struct representing a
- * CONNACK.
- * @param[out] pSessionPresent Whether a previous session was present.
- *
- * @return #MQTTSuccess if CONNACK specifies that CONNECT was accepted;
- * #MQTTServerRefused if CONNACK specifies that CONNECT was rejected;
- * #MQTTBadResponse if the CONNACK packet doesn't follow MQTT spec.
+ * @brief Response topic id.
*/
-static MQTTStatus_t deserializeConnack( const MQTTPacketInfo_t * pConnack,
- bool * pSessionPresent );
+ #define MQTT_RESPONSE_TOPIC_ID ( 0x08 )
/**
- * @brief Decode the status bytes of a SUBACK packet to a #MQTTStatus_t.
- *
- * @param[in] statusCount Number of status bytes in the SUBACK.
- * @param[in] pStatusStart The first status byte in the SUBACK.
- *
- * @return #MQTTSuccess, #MQTTServerRefused, or #MQTTBadResponse.
+ * @brief Correlation data id.
*/
-static MQTTStatus_t readSubackStatus( size_t statusCount,
- const uint8_t * pStatusStart );
+ #define MQTT_CORRELATION_DATA_ID ( 0x09 )
/**
- * @brief Deserialize a SUBACK packet.
- *
- * Converts the packet from a stream of bytes to an #MQTTStatus_t and extracts
- * the packet identifier.
- *
- * @param[in] pSuback Pointer to an MQTT packet struct representing a SUBACK.
- * @param[out] pPacketIdentifier Packet ID of the SUBACK.
- *
- * @return #MQTTSuccess if SUBACK is valid; #MQTTBadResponse if SUBACK packet
- * doesn't follow the MQTT spec.
+ * @brief Topic alias id.
*/
-static MQTTStatus_t deserializeSuback( const MQTTPacketInfo_t * pSuback,
- uint16_t * pPacketIdentifier );
+ #define MQTT_TOPIC_ALIAS_ID ( 0x23 )
+
+/*CONNACK PROPERTIES*/
/**
- * @brief Deserialize a PUBLISH packet received from the server.
- *
- * Converts the packet from a stream of bytes to an #MQTTPublishInfo_t and
- * extracts the packet identifier. Also prints out debug log messages about the
- * packet.
- *
- * @param[in] pIncomingPacket Pointer to an MQTT packet struct representing a
- * PUBLISH.
- * @param[out] pPacketId Packet identifier of the PUBLISH.
- * @param[out] pPublishInfo Pointer to #MQTTPublishInfo_t where output is
- * written.
- *
- * @return #MQTTSuccess if PUBLISH is valid; #MQTTBadResponse
- * if the PUBLISH packet doesn't follow MQTT spec.
+ * @brief Max qos id.
*/
-static MQTTStatus_t deserializePublish( const MQTTPacketInfo_t * pIncomingPacket,
- uint16_t * pPacketId,
- MQTTPublishInfo_t * pPublishInfo );
+ #define MQTT_MAX_QOS_ID ( 0x24 )
/**
- * @brief Deserialize an UNSUBACK, PUBACK, PUBREC, PUBREL, or PUBCOMP packet.
- *
- * Converts the packet from a stream of bytes to an #MQTTStatus_t and extracts
- * the packet identifier.
- *
- * @param[in] pAck Pointer to the MQTT packet structure representing the packet.
- * @param[out] pPacketIdentifier Packet ID of the ack type packet.
- *
- * @return #MQTTSuccess if UNSUBACK, PUBACK, PUBREC, PUBREL, or PUBCOMP is valid;
- * #MQTTBadResponse if the packet doesn't follow the MQTT spec.
+ * @brief Retain available id.
*/
-static MQTTStatus_t deserializeSimpleAck( const MQTTPacketInfo_t * pAck,
- uint16_t * pPacketIdentifier );
+ #define MQTT_RETAIN_AVAILABLE_ID ( 0x25 )
/**
- * @brief Deserialize a PINGRESP packet.
- *
- * Converts the packet from a stream of bytes to an #MQTTStatus_t.
- *
- * @param[in] pPingresp Pointer to an MQTT packet struct representing a PINGRESP.
- *
- * @return #MQTTSuccess if PINGRESP is valid; #MQTTBadResponse if the PINGRESP
- * packet doesn't follow MQTT spec.
+ * @brief Assigned client identifier id.
*/
-static MQTTStatus_t deserializePingresp( const MQTTPacketInfo_t * pPingresp );
+ #define MQTT_ASSIGNED_CLIENT_ID ( 0x12 )
-/*-----------------------------------------------------------*/
+/**
+ * @brief Reason string id.
+ */
+ #define MQTT_REASON_STRING_ID ( 0x1F )
-static size_t remainingLengthEncodedSize( size_t length )
-{
- size_t encodedSize;
+/**
+ * @brief Wildcard available id.
+ */
+ #define MQTT_WILDCARD_ID ( 0x28 )
- /* Determine how many bytes are needed to encode length.
- * The values below are taken from the MQTT 3.1.1 spec. */
+/**
+ * @brief Subscription available id.
+ */
+ #define MQTT_SUB_AVAILABLE_ID ( 0x29 )
- /* 1 byte is needed to encode lengths between 0 and 127. */
- if( length < 128U )
- {
- encodedSize = 1U;
- }
- /* 2 bytes are needed to encode lengths between 128 and 16,383. */
- else if( length < 16384U )
- {
- encodedSize = 2U;
- }
- /* 3 bytes are needed to encode lengths between 16,384 and 2,097,151. */
- else if( length < 2097152U )
- {
- encodedSize = 3U;
- }
- /* 4 bytes are needed to encode lengths between 2,097,152 and 268,435,455. */
- else
- {
- encodedSize = 4U;
- }
+/**
+ * @brief Shared subscription id.
+ */
+ #define MQTT_SHARED_SUB_ID ( 0x2A )
- LogDebug( ( "Encoded size for length %lu is %lu bytes.",
- ( unsigned long ) length,
- ( unsigned long ) encodedSize ) );
+/**
+ * @brief Server keep alive id.
+ */
+ #define MQTT_SERVER_KEEP_ALIVE_ID ( 0x13 )
- return encodedSize;
-}
+/**
+ * @brief Response information id.
+ */
-/*-----------------------------------------------------------*/
+ #define MQTT_RESPONSE_INFO_ID ( 0x1A )
-static uint8_t * encodeRemainingLength( uint8_t * pDestination,
- size_t length )
-{
- uint8_t lengthByte;
- uint8_t * pLengthEnd = NULL;
- size_t remainingLength = length;
+/**
+ * @brief Server reference id.
+ */
+ #define MQTT_SERVER_REF_ID ( 0x1C )
- assert( pDestination != NULL );
+/**
+ * @brief Size of the property id.
+ */
- pLengthEnd = pDestination;
+ #define CORE_MQTT_ID_SIZE ( 1U )
- /* This algorithm is copied from the MQTT v3.1.1 spec. */
- do
- {
- lengthByte = ( uint8_t ) ( remainingLength % 128U );
- remainingLength = remainingLength / 128U;
+/**
+ * @ingroup mqtt_constants
+ * @brief The size of MQTT PUBACK, PUBREC, PUBREL, and PUBCOMP packets with reason code, packet id.
+ */
+ #define MQTT_PUBLISH_ACK_PACKET_SIZE_WITH_REASON ( 3UL )
- /* Set the high bit of this byte, indicating that there's more data. */
- if( remainingLength > 0U )
- {
- UINT8_SET_BIT( lengthByte, 7 );
- }
+/**
+ * @brief The Connection is accepted.
+ */
+ #define MQTT_REASON_SUCCESS ( 0x00 )
- /* Output a single encoded byte. */
- *pLengthEnd = lengthByte;
- pLengthEnd++;
- } while( remainingLength > 0U );
+/**
+ * @brief The Client wishes to disconnect but requires
+ * that the Server also publishes its Will Message.
+ */
+ #define MQTT_REASON_SEND_WILL ( 0x04 )
- return pLengthEnd;
-}
+/**
+ * @brief The message is accepted but there are no
+ * subscribers.
+ */
+ #define MQTT_REASON_NO_MATCHING_SUBSCRIBERS ( 0x10 )
-/*-----------------------------------------------------------*/
+/**
+ * @brief The Server does not wish to reveal the reason for the
+ * failure, or none of the other Reason Codes apply.
+ */
+ #define MQTT_REASON_UNSPECIFIED_ERR ( 0x80 )
-static uint8_t * encodeString( uint8_t * pDestination,
- const char * pSource,
- uint16_t sourceLength )
-{
- uint8_t * pBuffer = NULL;
+/**
+ * @brief Data within the CONNECT packet could not be
+ * correctly parsed.
+ */
+ #define MQTT_REASON_MALFORMED_PACKET ( 0x81 )
- /* Typecast const char * typed source buffer to const uint8_t *.
- * This is to use same type buffers in memcpy. */
- const uint8_t * pSourceBuffer = ( const uint8_t * ) pSource;
+/**
+ * @brief Data in the CONNECT packet does not conform to this
+ * specification.
+ */
+ #define MQTT_REASON_PROTOCOL_ERR ( 0x82 )
- assert( pDestination != NULL );
+/**
+ * @brief The CONNECT is valid but is not accepted by this
+ * Server.
+ */
+ #define MQTT_REASON_IMPL_SPECIFIC_ERR ( 0x83 )
- pBuffer = pDestination;
+/**
+ * @brief The Server does not support the version of the MQTT
+ * protocol requested by the Client.
+ */
+ #define MQTT_REASON_UNSUPPORTED_PROTO_VER ( 0x84 )
- /* The first byte of a UTF-8 string is the high byte of the string length. */
- *pBuffer = UINT16_HIGH_BYTE( sourceLength );
- pBuffer++;
+/**
+ * @brief The Client Identifier is a valid string but is not allowed
+ * by the Server.
+ */
+ #define MQTT_REASON_CLIENT_ID_NOT_VALID ( 0x85 )
- /* The second byte of a UTF-8 string is the low byte of the string length. */
- *pBuffer = UINT16_LOW_BYTE( sourceLength );
- pBuffer++;
+/**
+ * @brief The Server does not accept the User Name or
+ * Password specified by the Client.
+ */
+ #define MQTT_REASON_BAD_USER_OR_PASS ( 0x86 )
- /* Copy the string into pBuffer. */
- if( pSourceBuffer != NULL )
- {
- ( void ) memcpy( pBuffer, pSourceBuffer, sourceLength );
- }
+/**
+ * @brief The Client is not authorized to connect.
+ */
+ #define MQTT_REASON_NOT_AUTHORIZED ( 0x87 )
- /* Return the pointer to the end of the encoded string. */
- pBuffer = &pBuffer[ sourceLength ];
+/**
+ * @brief The MQTT Server is not available.
+ */
+ #define MQTT_REASON_SERVER_UNAVAILABLE ( 0x88 )
- return pBuffer;
-}
+/**
+ * @brief The Server is busy, try again later.
+ */
+ #define MQTT_REASON_SERVER_BUSY ( 0x89 )
-/*-----------------------------------------------------------*/
+/**
+ * @brief This Client has been banned by administrative action.
+ */
+ #define MQTT_REASON_BANNED ( 0x8A )
-static bool calculatePublishPacketSize( const MQTTPublishInfo_t * pPublishInfo,
- size_t * pRemainingLength,
- size_t * pPacketSize )
-{
- bool status = true;
- size_t packetSize = 0, payloadLimit = 0;
+/**
+ * @brief The Server is shutting down.
+ */
+ #define MQTT_REASON_SERVER_SHUTTING_DOWN ( 0x8B )
- assert( pPublishInfo != NULL );
- assert( pRemainingLength != NULL );
- assert( pPacketSize != NULL );
+/**
+ * @brief The authentication method is not supported or does not
+ * match the authentication method currently in use.
+ */
+ #define MQTT_REASON_BAD_AUTH_METHOD ( 0x8C )
- /* The variable header of a PUBLISH packet always contains the topic name.
- * The first 2 bytes of UTF-8 string contains length of the string.
- */
- packetSize += pPublishInfo->topicNameLength + sizeof( uint16_t );
+/**
+ * @brief The Connection is closed because no packet
+ * has been received for 1.5 times the Keepalive
+ * time.
+ */
+ #define MQTT_REASON_KEEP_ALIVE_TIMEOUT ( 0x8D )
- /* The variable header of a QoS 1 or 2 PUBLISH packet contains a 2-byte
- * packet identifier. */
- if( pPublishInfo->qos > MQTTQoS0 )
- {
- packetSize += sizeof( uint16_t );
- }
+/**
+ * @brief Another Connection using the same ClientID
+ * has connected causing this Connection to be
+ * closed.
+ */
+ #define MQTT_REASON_SESSION_TAKEN_OVER ( 0x8E )
- /* Calculate the maximum allowed size of the payload for the given parameters.
- * This calculation excludes the "Remaining length" encoding, whose size is not
- * yet known. */
- payloadLimit = MQTT_MAX_REMAINING_LENGTH - packetSize - 1U;
+/**
+ * @brief The Topic Filter is correctly formed, but is not
+ * accepted by this Server.
+ */
+ #define MQTT_REASON_TOPIC_FILTER_INVALID ( 0x8F )
- /* Ensure that the given payload fits within the calculated limit. */
- if( pPublishInfo->payloadLength > payloadLimit )
- {
- LogError( ( "PUBLISH payload length of %lu cannot exceed "
- "%lu so as not to exceed the maximum "
- "remaining length of MQTT 3.1.1 packet( %lu ).",
- ( unsigned long ) pPublishInfo->payloadLength,
- ( unsigned long ) payloadLimit,
- MQTT_MAX_REMAINING_LENGTH ) );
- status = false;
- }
- else
- {
- /* Add the length of the PUBLISH payload. At this point, the "Remaining length"
- * has been calculated. */
- packetSize += pPublishInfo->payloadLength;
+/**
+ * @ingroup mqtt_constants
+ * @brief The Topic Name is not malformed, but is not
+ * accepted by this Server.
+ */
+ #define MQTT_REASON_TOPIC_NAME_INVALID ( 0x90 )
- /* Now that the "Remaining length" is known, recalculate the payload limit
- * based on the size of its encoding. */
- payloadLimit -= remainingLengthEncodedSize( packetSize );
+/**
+ * @brief The Packet Identifier is already in use.
+ */
+ #define MQTT_REASON_PACKET_ID_IN_USE ( 0x91 )
- /* Check that the given payload fits within the size allowed by MQTT spec. */
- if( pPublishInfo->payloadLength > payloadLimit )
- {
- LogError( ( "PUBLISH payload length of %lu cannot exceed "
- "%lu so as not to exceed the maximum "
- "remaining length of MQTT 3.1.1 packet( %lu ).",
- ( unsigned long ) pPublishInfo->payloadLength,
- ( unsigned long ) payloadLimit,
- MQTT_MAX_REMAINING_LENGTH ) );
- status = false;
- }
- else
- {
- /* Set the "Remaining length" output parameter and calculate the full
- * size of the PUBLISH packet. */
- *pRemainingLength = packetSize;
+/**
+ * @brief The Packet Identifier is not known.
+ */
+ #define MQTT_REASON_PACKET_ID_NOT_FOUND ( 0x92 )
- packetSize += 1U + remainingLengthEncodedSize( packetSize );
- *pPacketSize = packetSize;
- }
- }
+/**
+ * @brief The Client or Server has received more than
+ * Receive Maximum publication for which it has
+ * not sent PUBACK or PUBCOMP.
+ */
+ #define MQTT_REASON_RX_MAX_EXCEEDED ( 0x93 )
- LogDebug( ( "PUBLISH packet remaining length=%lu and packet size=%lu.",
- ( unsigned long ) *pRemainingLength,
- ( unsigned long ) *pPacketSize ) );
- return status;
-}
+/**
+ * @brief The Connection is accepted.
+ */
+ #define MQTT_REASON_TOPIC_ALIAS_INVALID ( 0x94 )
-/*-----------------------------------------------------------*/
+/**
+ * @brief The packet exceeded the maximum
+ * permissible size.
+ */
+ #define MQTT_REASON_PACKET_TOO_LARGE ( 0x95 )
-MQTTStatus_t MQTT_SerializePublishHeaderWithoutTopic( const MQTTPublishInfo_t * pPublishInfo,
- size_t remainingLength,
- uint8_t * pBuffer,
- size_t * headerSize )
-{
- size_t headerLength;
- uint8_t * pIndex;
- MQTTStatus_t status = MQTTSuccess;
+/**
+ * @brief The Connection is accepted.
+ */
+ #define MQTT_REASON_MSG_RATE_TOO_HIGH ( 0x96 )
- /* The first byte of a PUBLISH packet contains the packet type and flags. */
- uint8_t publishFlags = MQTT_PACKET_TYPE_PUBLISH;
+/**
+ * @brief An implementation or administrative imposed limit has
+ * been exceeded.
+ */
+ #define MQTT_REASON_QUOTA_EXCEEDED ( 0x97 )
- /* Get the start address of the buffer. */
- pIndex = pBuffer;
+/**
+ * @brief The Connection is closed due to an
+ * administrative action.
+ */
+ #define MQTT_REASON_ADMIN_ACTION ( 0x98 )
- /* Length of serialized packet = First byte
+/**
+ * @brief The Payload does not match the specified Payload
+ * Format Indicator.
+ */
+ #define MQTT_REASON_PAYLOAD_FORMAT_INVALID ( 0x99 )
+
+/**
+ * @brief The Server does not support retained messages, and
+ * Will Retain was set to 1.
+ */
+ #define MQTT_REASON_RETAIN_NOT_SUPPORTED ( 0x9A )
+
+/**
+ * @brief The Server does not support the QoS.
+ */
+ #define MQTT_REASON_QOS_NOT_SUPPORTED ( 0x9B )
+
+/**
+ * @brief The Client should temporarily use another server.
+ */
+ #define MQTT_REASON_USE_ANOTHER_SERVER ( 0x9C )
+
+/**
+ * @brief The Client should permanently use another server.
+ */
+ #define MQTT_REASON_SERVER_MOVED ( 0x9D )
+
+/**
+ * @brief The Server does not support Shared
+ * Subscriptions.
+ */
+ #define MQTT_REASON_SS_NOT_SUPPORTED ( 0x9E )
+
+/**
+ * @brief The connection rate limit has been exceeded.
+ */
+ #define MQTT_REASON_CON_RATE_EXCEED ( 0x9F )
+
+/**
+ * @brief The maximum connection time authorized for
+ * this connection has been exceeded.
+ */
+ #define MQTT_REASON_MAX_CON_TIME ( 0xA0 )
+
+/**
+ * @brief The Server does not support Subscription
+ * Identifiers; the subscription is not accepted.
+ */
+ #define MQTT_REASON_SUB_ID_NOT_SUP ( 0xA1 )
+
+/**
+ * @brief The Server does not support Wildcard
+ * Subscriptions; the subscription is not accepted.
+ */
+ #define MQTT_REASON_WILDCARD_SUB_NOT_SUP ( 0xA2 )
+
+#endif /* if ( MQTT_VERSION_5_ENABLED ) */
+
+/**
+ * @brief Set a bit in an 8-bit unsigned integer.
+ */
+#define UINT8_SET_BIT( x, position ) ( ( x ) = ( uint8_t ) ( ( x ) | ( 0x01U << ( position ) ) ) )
+
+/**
+ * @brief Macro for checking if a bit is set in a 1-byte unsigned int.
+ *
+ * @param[in] x The unsigned int to check.
+ * @param[in] position Which bit to check.
+ */
+#define UINT8_CHECK_BIT( x, position ) ( ( ( x ) & ( 0x01U << ( position ) ) ) == ( 0x01U << ( position ) ) )
+
+/**
+ * @brief Get the high byte of a 16-bit unsigned integer.
+ */
+#define UINT16_HIGH_BYTE( x ) ( ( uint8_t ) ( ( x ) >> 8 ) )
+
+/**
+ * @brief Get the low byte of a 16-bit unsigned integer.
+ */
+#define UINT16_LOW_BYTE( x ) ( ( uint8_t ) ( ( x ) & 0x00ffU ) )
+
+/**
+ * @brief Macro for decoding a 2-byte unsigned int from a sequence of bytes.
+ *
+ * @param[in] ptr A uint8_t* that points to the high byte.
+ */
+#define UINT16_DECODE( ptr ) \
+ ( uint16_t ) ( ( ( ( uint16_t ) ptr[ 0 ] ) << 8 ) | \
+ ( ( uint16_t ) ptr[ 1 ] ) )
+
+#if ( MQTT_VERSION_5_ENABLED )
+
+/**
+ * @brief Get the 4th byte of a 32-bit unsigned integer.
+ */
+ #define UINT32_BYTE3( x ) ( ( uint8_t ) ( ( x ) >> 24 ) )
+
+/**
+ * @brief Get the 3rd byte of a 32-bit unsigned integer.
+ */
+ #define UINT32_BYTE2( x ) ( ( uint8_t ) ( ( x ) >> 16 ) )
+
+/**
+ * @brief Get the 2nd byte of a 32-bit unsigned integer.
+ */
+ #define UINT32_BYTE1( x ) ( ( uint8_t ) ( ( x ) >> 8 ) )
+
+/**
+ * @brief Get the 1st byte of a 32-bit unsigned integer.
+ */
+ #define UINT32_BYTE0( x ) ( ( uint8_t ) ( ( x ) & 0x000000FFU ) )
+
+/**
+ * @brief Macro for decoding a 4-byte unsigned int from a sequence of bytes.
+ *
+ * @param[in] ptr A uint8_t* that points to the high byte.
+ */
+ #define UINT32_DECODE( ptr ) \
+ ( uint32_t ) ( ( ( ( uint32_t ) ptr[ 0 ] ) << 24 ) | \
+ ( ( ( uint32_t ) ptr[ 1 ] ) << 16 ) | \
+ ( ( ( uint32_t ) ptr[ 2 ] ) << 8 ) | \
+ ( ( uint32_t ) ptr[ 3 ] ) )
+
+#endif /* if ( MQTT_VERSION_5_ENABLED ) */
+
+/**
+ * @brief A value that represents an invalid remaining length.
+ *
+ * This value is greater than what is allowed by the MQTT specification.
+ */
+#define MQTT_REMAINING_LENGTH_INVALID ( ( size_t ) 268435456 )
+
+/**
+ * @brief The minimum remaining length for a QoS 0 PUBLISH.
+ *
+ * Includes two bytes for topic name length and one byte for topic name.
+ */
+#define MQTT_MIN_PUBLISH_REMAINING_LENGTH_QOS0 ( 3U )
+
+/*-----------------------------------------------------------*/
+
+/**
+ * @brief MQTT Subscription packet types.
+ */
+typedef enum MQTTSubscriptionType
+{
+ MQTT_SUBSCRIBE, /**< @brief The type is a SUBSCRIBE packet. */
+ MQTT_UNSUBSCRIBE /**< @brief The type is a UNSUBSCRIBE packet. */
+} MQTTSubscriptionType_t;
+
+/*-----------------------------------------------------------*/
+
+/**
+ * @brief Serializes MQTT PUBLISH packet into the buffer provided.
+ *
+ * This function serializes MQTT PUBLISH packet into #MQTTFixedBuffer_t.pBuffer.
+ * Copy of the payload into the buffer is done as part of the serialization
+ * only if @p serializePayload is true.
+ *
+ * @brief param[in] pPublishInfo Publish information.
+ * @brief param[in] remainingLength Remaining length of the PUBLISH packet.
+ * @brief param[in] packetIdentifier Packet identifier of PUBLISH packet.
+ * @brief param[in, out] pFixedBuffer Buffer to which PUBLISH packet will be
+ * serialized.
+ * @brief param[in] serializePayload Copy payload to the serialized buffer
+ * only if true. Only PUBLISH header will be serialized if false.
+ */
+static void serializePublishCommon( const MQTTPublishInfo_t * pPublishInfo,
+ size_t remainingLength,
+ uint16_t packetIdentifier,
+ const MQTTFixedBuffer_t * pFixedBuffer,
+ bool serializePayload );
+
+/**
+ * @brief Calculates the packet size and remaining length of an MQTT
+ * PUBLISH packet.
+ *
+ * @param[in] pPublishInfo MQTT PUBLISH packet parameters.
+ * @param[out] pRemainingLength The Remaining Length of the MQTT PUBLISH packet.
+ * @param[out] pPacketSize The total size of the MQTT PUBLISH packet.
+ *
+ * @return false if the packet would exceed the size allowed by the
+ * MQTT spec; true otherwise.
+ */
+static bool calculatePublishPacketSize( const MQTTPublishInfo_t * pPublishInfo,
+ size_t * pRemainingLength,
+ size_t * pPacketSize );
+
+/**
+ * @brief Calculates the packet size and remaining length of an MQTT
+ * SUBSCRIBE or UNSUBSCRIBE packet.
+ *
+ * @param[in] pSubscriptionList List of MQTT subscription info.
+ * @param[in] subscriptionCount The number of elements in pSubscriptionList.
+ * @param[out] pRemainingLength The Remaining Length of the MQTT SUBSCRIBE or
+ * UNSUBSCRIBE packet.
+ * @param[out] pPacketSize The total size of the MQTT MQTT SUBSCRIBE or
+ * UNSUBSCRIBE packet.
+ * @param[in] subscriptionType #MQTT_SUBSCRIBE or #MQTT_UNSUBSCRIBE.
+ *
+ * #MQTTBadParameter if the packet would exceed the size allowed by the
+ * MQTT spec or a subscription is empty; #MQTTSuccess otherwise.
+ */
+static MQTTStatus_t calculateSubscriptionPacketSize( const MQTTSubscribeInfo_t * pSubscriptionList,
+ size_t subscriptionCount,
+ size_t * pRemainingLength,
+ size_t * pPacketSize,
+ MQTTSubscriptionType_t subscriptionType );
+
+/**
+ * @brief Validates parameters of #MQTT_SerializeSubscribe or
+ * #MQTT_SerializeUnsubscribe.
+ *
+ * @param[in] pSubscriptionList List of MQTT subscription info.
+ * @param[in] subscriptionCount The number of elements in pSubscriptionList.
+ * @param[in] packetId Packet identifier.
+ * @param[in] remainingLength Remaining length of the packet.
+ * @param[in] pFixedBuffer Buffer for packet serialization.
+ *
+ * @return #MQTTNoMemory if pBuffer is too small to hold the MQTT packet;
+ * #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSuccess otherwise.
+ */
+static MQTTStatus_t validateSubscriptionSerializeParams( const MQTTSubscribeInfo_t * pSubscriptionList,
+ size_t subscriptionCount,
+ uint16_t packetId,
+ size_t remainingLength,
+ const MQTTFixedBuffer_t * pFixedBuffer );
+
+/**
+ * @brief Serialize an MQTT CONNECT packet in the given buffer.
+ *
+ * @param[in] pConnectInfo MQTT CONNECT packet parameters.
+ * @param[in] pWillInfo Last Will and Testament. Pass NULL if not used.
+ * @param[in] remainingLength Remaining Length of MQTT CONNECT packet.
+ * @param[out] pFixedBuffer Buffer for packet serialization.
+ */
+static void serializeConnectPacket( const MQTTConnectInfo_t * pConnectInfo,
+ const MQTTPublishInfo_t * pWillInfo,
+ size_t remainingLength,
+ const MQTTFixedBuffer_t * pFixedBuffer );
+
+/**
+ * @brief Prints the appropriate message for the CONNACK response code if logs
+ * are enabled.
+ *
+ * @param[in] responseCode MQTT standard CONNACK response code.
+ */
+static void logConnackResponse( uint8_t responseCode );
+
+/**
+ * @brief Encodes the remaining length of the packet using the variable length
+ * encoding scheme provided in the MQTT v3.1.1 specification.
+ *
+ * @param[out] pDestination The destination buffer to store the encoded remaining
+ * length.
+ * @param[in] length The remaining length to encode.
+ *
+ * @return The location of the byte following the encoded value.
+ */
+static uint8_t * encodeRemainingLength( uint8_t * pDestination,
+ size_t length );
+
+/**
+ * @brief Retrieve the size of the remaining length if it were to be encoded.
+ *
+ * @param[in] length The remaining length to be encoded.
+ *
+ * @return The size of the remaining length if it were to be encoded.
+ */
+static size_t remainingLengthEncodedSize( size_t length );
+
+/**
+ * @brief Encode a string whose size is at maximum 16 bits in length.
+ *
+ * @param[out] pDestination Destination buffer for the encoding.
+ * @param[in] pSource The source string to encode.
+ * @param[in] sourceLength The length of the source string to encode.
+ *
+ * @return A pointer to the end of the encoded string.
+ */
+static uint8_t * encodeString( uint8_t * pDestination,
+ const char * pSource,
+ uint16_t sourceLength );
+
+/**
+ * @brief Retrieves and decodes the Remaining Length from the network interface
+ * by reading a single byte at a time.
+ *
+ * @param[in] recvFunc Network interface receive function.
+ * @param[in] pNetworkContext Network interface context to the receive function.
+ *
+ * @return The Remaining Length of the incoming packet.
+ */
+static size_t getRemainingLength( TransportRecv_t recvFunc,
+ NetworkContext_t * pNetworkContext );
+
+/**
+ * @brief Retrieves, decodes and stores the Remaining Length from the network
+ * interface by reading a single byte at a time.
+ *
+ * @param[in] pBuffer The buffer holding the raw data to be processed
+ * @param[in] pIndex Pointer to the index within the buffer to marking the end of raw data
+ * available.
+ * @param[in] pIncomingPacket Structure used to hold the fields of the
+ * incoming packet.
+ *
+ * @return MQTTNeedMoreBytes is returned to show that the incoming
+ * packet is not yet fully received and decoded. Otherwise, MQTTSuccess
+ * shows that processing of the packet was successful.
+ */
+static MQTTStatus_t processRemainingLength( const uint8_t * pBuffer,
+ const size_t * pIndex,
+ MQTTPacketInfo_t * pIncomingPacket );
+
+/**
+ * @brief Check if an incoming packet type is valid.
+ *
+ * @param[in] packetType The packet type to check.
+ *
+ * @return `true` if the packet type is valid; `false` otherwise.
+ */
+static bool incomingPacketValid( uint8_t packetType );
+
+/**
+ * @brief Check the remaining length of an incoming PUBLISH packet against some
+ * value for QoS 0, or for QoS 1 and 2.
+ *
+ * The remaining length for a QoS 1 and 2 packet will always be two greater than
+ * for a QoS 0.
+ *
+ * @param[in] remainingLength Remaining length of the PUBLISH packet.
+ * @param[in] qos The QoS of the PUBLISH.
+ * @param[in] qos0Minimum Minimum possible remaining length for a QoS 0 PUBLISH.
+ *
+ * @return #MQTTSuccess or #MQTTBadResponse.
+ */
+static MQTTStatus_t checkPublishRemainingLength( size_t remainingLength,
+ MQTTQoS_t qos,
+ size_t qos0Minimum );
+
+/**
+ * @brief Process the flags of an incoming PUBLISH packet.
+ *
+ * @param[in] publishFlags Flags of an incoming PUBLISH.
+ * @param[in, out] pPublishInfo Pointer to #MQTTPublishInfo_t struct where
+ * output will be written.
+ *
+ * @return #MQTTSuccess or #MQTTBadResponse.
+ */
+static MQTTStatus_t processPublishFlags( uint8_t publishFlags,
+ MQTTPublishInfo_t * pPublishInfo );
+
+/**
+ * @brief Deserialize a CONNACK packet.
+ *
+ * Converts the packet from a stream of bytes to an #MQTTStatus_t.
+ *
+ * @param[in] pConnack Pointer to an MQTT packet struct representing a
+ * CONNACK.
+ * @param[out] pSessionPresent Whether a previous session was present.
+ *
+ * @return #MQTTSuccess if CONNACK specifies that CONNECT was accepted;
+ * #MQTTServerRefused if CONNACK specifies that CONNECT was rejected;
+ * #MQTTBadResponse if the CONNACK packet doesn't follow MQTT spec.
+ */
+static MQTTStatus_t deserializeConnack( const MQTTPacketInfo_t * pConnack,
+ bool * pSessionPresent );
+
+/**
+ * @brief Decode the status bytes of a SUBACK packet to a #MQTTStatus_t.
+ *
+ * @param[in] statusCount Number of status bytes in the SUBACK.
+ * @param[in] pStatusStart The first status byte in the SUBACK.
+ *
+ * @return #MQTTSuccess, #MQTTServerRefused, or #MQTTBadResponse.
+ */
+static MQTTStatus_t readSubackStatus( size_t statusCount,
+ const uint8_t * pStatusStart );
+
+/**
+ * @brief Deserialize a SUBACK packet.
+ *
+ * Converts the packet from a stream of bytes to an #MQTTStatus_t and extracts
+ * the packet identifier.
+ *
+ * @param[in] pSuback Pointer to an MQTT packet struct representing a SUBACK.
+ * @param[out] pPacketIdentifier Packet ID of the SUBACK.
+ *
+ * @return #MQTTSuccess if SUBACK is valid; #MQTTBadResponse if SUBACK packet
+ * doesn't follow the MQTT spec.
+ */
+static MQTTStatus_t deserializeSuback( const MQTTPacketInfo_t * pSuback,
+ uint16_t * pPacketIdentifier );
+
+/**
+ * @brief Deserialize a PUBLISH packet received from the server.
+ *
+ * Converts the packet from a stream of bytes to an #MQTTPublishInfo_t and
+ * extracts the packet identifier. Also prints out debug log messages about the
+ * packet.
+ *
+ * @param[in] pIncomingPacket Pointer to an MQTT packet struct representing a
+ * PUBLISH.
+ * @param[out] pPacketId Packet identifier of the PUBLISH.
+ * @param[out] pPublishInfo Pointer to #MQTTPublishInfo_t where output is
+ * written.
+ *
+ * @return #MQTTSuccess if PUBLISH is valid; #MQTTBadResponse
+ * if the PUBLISH packet doesn't follow MQTT spec.
+ */
+static MQTTStatus_t deserializePublish( const MQTTPacketInfo_t * pIncomingPacket,
+ uint16_t * pPacketId,
+ MQTTPublishInfo_t * pPublishInfo );
+
+/**
+ * @brief Deserialize an UNSUBACK, PUBACK, PUBREC, PUBREL, or PUBCOMP packet.
+ *
+ * Converts the packet from a stream of bytes to an #MQTTStatus_t and extracts
+ * the packet identifier.
+ *
+ * @param[in] pAck Pointer to the MQTT packet structure representing the packet.
+ * @param[out] pPacketIdentifier Packet ID of the ack type packet.
+ *
+ * @return #MQTTSuccess if UNSUBACK, PUBACK, PUBREC, PUBREL, or PUBCOMP is valid;
+ * #MQTTBadResponse if the packet doesn't follow the MQTT spec.
+ */
+static MQTTStatus_t deserializeSimpleAck( const MQTTPacketInfo_t * pAck,
+ uint16_t * pPacketIdentifier );
+
+/**
+ * @brief Deserialize a PINGRESP packet.
+ *
+ * Converts the packet from a stream of bytes to an #MQTTStatus_t.
+ *
+ * @param[in] pPingresp Pointer to an MQTT packet struct representing a PINGRESP.
+ *
+ * @return #MQTTSuccess if PINGRESP is valid; #MQTTBadResponse if the PINGRESP
+ * packet doesn't follow MQTT spec.
+ */
+static MQTTStatus_t deserializePingresp( const MQTTPacketInfo_t * pPingresp );
+
+#if ( MQTT_VERSION_5_ENABLED )
+
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+
+/**
+ * @brief Get the size of user properties.
+ *
+ * Validates the user properties and calculates the total size of user property.
+ *
+ * @param[in] pUserProperty Pointer to an MQTT packet struct representing user properties.
+ * @param[in] number Total number of user properties
+ * @param[out] pSize Size of the user properties
+ *
+ * @return #MQTTSuccess if user properties are valid and #MQTTBadParameter if the user properties are not valid
+ */
+ static MQTTStatus_t MQTT_GetUserPropertySize( const MQTTUserProperty_t * pUserProperty,
+ uint32_t number,
+ size_t * pSize );
+
+/**
+ * @brief Validate the length and decode a user property.
+ *
+ * @param[out] pUserProperties To store the decoded property.
+ * @param[out] count Number of user properties decoded.
+ * @param[out] pPropertyLength Size of the length.
+ * @param[out] pIndex Pointer to the current index of the buffer.
+ *
+ * @return #MQTTSuccess, #MQTTProtocolError and #MQTTMalformedPacket
+ **/
+ static MQTTStatus_t decodeutf_8pair( MQTTUserProperties_t * pUserProperties,
+ uint32_t * count,
+ size_t * pPropertyLength,
+ const uint8_t ** pIndex );
+
+ #else /* if ( MQTT_USER_PROPERTY_ENABLED ) */
+
+/**
+ * @brief Validate the length and decode a user property.
+ *
+ * @param[out] pPropertyLength Size of the length.
+ * @param[out] pIndex Pointer to the current index of the buffer.
+ *
+ * @return #MQTTSuccess, #MQTTProtocolError and #MQTTMalformedPacket
+ **/
+ static MQTTStatus_t decodeAndDiscard( size_t * pPropertyLength,
+ const uint8_t ** pIndex );
+ #endif /* if ( MQTT_USER_PROPERTY_ENABLED ) */
+
+/**
+ * @brief Get the size of authentication information.
+ *
+ * Validates the authentication method, data and calculates the total size of authentication information.
+ *
+ * @param[in] pAuthInfo Pointer to an MQTT packet struct representing authentication information.
+ * @param[out] pPropertyLength Size of the authentication information.
+ *
+ * @return #MQTTSuccess if user properties are valid and #MQTTBadParameter if the user properties are not valid
+ */
+
+ static MQTTStatus_t MQTT_GetAuthInfoSize( const MQTTAuthInfo_t * pAuthInfo,
+ size_t * pPropertyLength );
+
+/**
+ * @brief Get the size of connect properties.
+ *
+ * Validates the connect properties, calculates the total size of connect properties and stores it int MQTTConnectProperties_t.
+ *
+ * @param[out] pConnectProperties Pointer to an MQTT packet struct representing connect properties.
+ *
+ * @return #MQTTSuccess if connect properties are valid and #MQTTBadParameter if the connect properties are not valid.
+ */
+
+ static MQTTStatus_t MQTT_GetConnectPropertiesSize( MQTTConnectProperties_t * pConnectProperties );
+
+/**
+ * @brief Get the size of will properties.
+ *
+ * Validates the publish properties,calculates the total size of publish properties and stores it in MQTTPublishInfo_t.
+ *
+ * @param[out] pPublishProperties Pointer to an MQTT packet struct representing publish properties.
+ *
+ * @return #MQTTSuccess if publish properties are valid and #MQTTBadParameter if they are not valid.
+ */
+
+ static MQTTStatus_t MQTT_GetPublishPropertiesSize( MQTTPublishInfo_t * pPublishProperties );
+
+/**
+ * @brief Decodes the variable length by reading a single byte at a time.
+ *
+ * Uses the algorithm provided in the spec.
+ *
+ * @param[in] pBuffer Pointer to the buffer.
+ * @param[out] pLength Decoded variable length
+ *
+ * @return #MQTTSuccess if variable length and paramters are valid else #MQTTBadParameter.
+ */
+ static MQTTStatus_t decodeVariableLength( const uint8_t * pBuffer,
+ size_t * pLength );
+
+/**
+ * @brief Validate the connack parameters.
+ *
+ * Converts the packet from a stream of bytes to an #MQTTStatus_t and extracts
+ * the variable header without connack properties.
+ *
+ * @param[in] pIncomingPacket Pointer to an MQTT packet struct representing a incoming packet.
+ * @param[out] pSessionPresent Whether a session is present or not.
+ *
+ *
+ * @return #MQTTSuccess if connack without connack properties is valid; #MQTTBadResponse if the Connack
+ * packet doesn't follow MQTT spec.
+ */
+ static MQTTStatus_t validateConnackParams( const MQTTPacketInfo_t * pIncomingPacket,
+ bool * pSessionPresent );
+
+/**
+ * @brief Prints the appropriate message for the CONNACK response code if logs
+ * are enabled.
+ *
+ * @param[in] responseCode MQTT Verion 5 standard CONNACK response code.
+ *
+ * @return MQTTServerRefused if response code is valid and MQTTProtocolError if responseCode is invalid.
+ */
+ static MQTTStatus_t logConnackResponseV5( uint8_t responseCode );
+
+/**
+ * @brief Serialize a MQTT CONNECT packet in the given buffer.
+ *
+ * @param[in] pConnectInfo MQTT CONNECT packet parameters.
+ * @param[in] pWillInfo Last Will and Testament. Pass NULL if not used.
+ * @param[in] pConnectProperties MQTT CONNECT Properties parameters.
+ * @param[in] remainingLength Remaining Length of MQTT CONNECT packet.
+ * @param[in] pFixedBuffer Buffer for packet serialization.
+ *
+ */
+
+ static void serializeConnectPacketV5( const MQTTConnectInfo_t * pConnectInfo,
+ const MQTTPublishInfo_t * pWillInfo,
+ const MQTTConnectProperties_t * pConnectProperties,
+ size_t remainingLength,
+ const MQTTFixedBuffer_t * pFixedBuffer );
+
+/**
+ * @brief Validate the length and decode a 4 byte value.
+ *
+ * @param[out] pProperty To store the decoded property.
+ * @param[out] pPropertyLength Size of the length.
+ * @param[out] pUsed Whether the property is decoded befire.
+ * @param[out] pIndex Pointer to the current index of the buffer.
+ *
+ * @return #MQTTSuccess, #MQTTProtocolError and #MQTTMalformedPacket
+ **/
+
+ static MQTTStatus_t decodeuint32_t( uint32_t * pProperty,
+ size_t * pPropertyLength,
+ bool * pUsed,
+ const uint8_t ** pIndex );
+
+/**
+ * @brief Validate the length and decode a 2 byte value.
+ *
+ * @param[out] pProperty To store the decoded property.
+ * @param[out] pPropertyLength Size of the length.
+ * @param[out] pUsed Whether the property is decoded befire.
+ * @param[out] pIndex Pointer to the current index of the buffer.
+ *
+ * @return #MQTTSuccess, #MQTTProtocolError and #MQTTMalformedPacket
+ **/
+
+ static MQTTStatus_t decodeuint16_t( uint16_t * pProperty,
+ size_t * pPropertyLength,
+ bool * pUsed,
+ const uint8_t ** pIndex );
+
+/**
+ * @brief Validate the length and decode a 1 byte value.
+ *
+ * @param[out] pProperty To store the decoded property.
+ * @param[out] pPropertyLength Size of the length.
+ * @param[out] pUsed Whether the property is decoded befire.
+ * @param[out] pIndex Pointer to the current index of the buffer.
+ *
+ * @return #MQTTSuccess, #MQTTProtocolError and #MQTTMalformedPacket
+ **/
+ static MQTTStatus_t decodeuint8_t( uint8_t * pProperty,
+ size_t * pPropertyLength,
+ bool * pUsed,
+ const uint8_t ** pIndex );
+
+/**
+ * @brief Validate the length and decode a utf 8 string.
+ *
+ * @param[out] pProperty To store the decoded string.
+ * @param[out] pLength Size of the decoded utf-8 string.
+ * @param[out] pPropertyLength Size of the length.
+ * @param[out] pUsed Whether the property is decoded before.
+ * @param[out] pIndex Pointer to the current index of the buffer.
+ *
+ * @return #MQTTSuccess, #MQTTProtocolError and #MQTTMalformedPacket
+ **/
+ static MQTTStatus_t decodeutf_8( const char ** pProperty,
+ uint16_t * pLength,
+ size_t * pPropertyLength,
+ bool * pUsed,
+ const uint8_t ** pIndex );
+
+
+/**
+ * @brief Validate the length and decode authentication information.
+ *
+ * @param[out] pConnackProperties To store the decoded property.
+ * @param[out] pAuthMethod Whether the authentication method is decoded before.
+ * @param[out] pAuthData Whether the authentication data is decoded before.
+ * @param[out] pPropertyLength Size of the length.
+ * @param[out] pIndex Pointer to the current index of the buffer.
+ * @param[in] id To differentiate between authentication method and authentication data.
+ *
+ * @return #MQTTSuccess, #MQTTProtocolError and #MQTTMalformedPacket
+ **/
+ static MQTTStatus_t decodeAuthInfo( const MQTTConnectProperties_t * pConnackProperties,
+ bool * pAuthMethod,
+ bool * pAuthData,
+ size_t * pPropertyLength,
+ const uint8_t ** pIndex,
+ const uint8_t id );
+
+/**
+ * @brief Validate the length and decode the connack properties.
+ *
+ * @param[out] pConnackProperties To store the decoded property.
+ * @param[out] length Length of the property.
+ * @param[out] pIndex Pointer to the current index of the buffer.
+ *
+ * @return #MQTTSuccess, #MQTTProtocolError and #MQTTMalformedPacket
+ **/
+ static MQTTStatus_t deserializeConnackV5( MQTTConnectProperties_t * pConnackProperties,
+ size_t length,
+ const uint8_t * const * pIndex );
+
+/**
+ * @brief Calculates the packet size and remaining length of an MQTT
+ * PUBLISH packet.
+ *
+ * @param[in] pPublishInfo MQTT PUBLISH packet parameters.
+ * @param[out] pRemainingLength The Remaining Length of the MQTT PUBLISH packet.
+ * @param[out] pPacketSize The total size of the MQTT PUBLISH packet.
+ * @param[in] maxPacketSize Max packet size allowed by the server.
+ *
+ *
+ * @return MQTTBadParameter if the packet would exceed the size allowed by the
+ * MQTT spec; true otherwise.
+ */
+ static MQTTStatus_t calculatePublishPacketSizeV5( MQTTPublishInfo_t * pPublishInfo,
+ size_t * pRemainingLength,
+ size_t * pPacketSize,
+ uint32_t maxPacketSize );
+
+/**
+ * @brief Serialize a MQTT Publish Ack packet in the given buffer.
+ *
+ * @param[in] pAckInfo MQTT PUB ACK packet parameters.
+ * @param[in] packetType Type of pub ack.
+ * @param[in] packetId Packet identifier.
+ * @param[in] remainingLength Remaining Length of PUB ACK packet.
+ * @param[in] pFixedBuffer Buffer for packet serialization.
+ *
+ */
+ static void serializePubAckPacketV5( const MQTTAckInfo_t * pAckInfo,
+ uint8_t packetType,
+ uint16_t packetId,
+ size_t remainingLength,
+ const MQTTFixedBuffer_t * pFixedBuffer );
+
+/**
+ * @brief Prints the appropriate message for the PUBREL, PUBACK response code if logs
+ * are enabled.
+ *
+ * @param[in] reasonCode MQTT Verion 5 standard PUBREL, PUBACK response code.
+ * @param[in] packetIdentifier Packet id of the ack packet.
+ *
+ * @return #MQTTSuccess, #MQTTServerRefused and #MQTTProtocolError.
+ */
+ static MQTTStatus_t logAckResponseV5( uint8_t reasonCode,
+ uint16_t packetIdentifier );
+
+/**
+ * @brief Prints the appropriate message for the CONNACK response code if logs
+ * are enabled.
+ *
+ * @param[in] reasonCode MQTT Verion 5 standard CONNACK response code.
+ * @param[in] packetIdentifier Packet id of the ack packet.
+ *
+ * @return #MQTTSuccess, #MQTTServerRefused and #MQTTProtocolError.
+ */
+ static MQTTStatus_t logSimpleAckResponseV5( uint8_t reasonCode,
+ uint16_t packetIdentifier );
+
+/**
+ * @brief Validate the length and decode the publish ack properties.
+ *
+ * @param[out] pAckInfo To store the decoded property.
+ * @param[out] pIndex Pointer to the current index of the buffer.
+ * @param[out] remainingLength Remaining length of the incoming packet.
+ *
+ *
+ * @return #MQTTSuccess, #MQTTProtocolError and #MQTTMalformedPacket
+ **/
+ static MQTTStatus_t decodeAckProperties( MQTTAckInfo_t * pAckInfo,
+ const uint8_t * pIndex,
+ size_t remainingLength );
+
+/**
+ * @brief Deserialize an PUBACK, PUBREC, PUBREL, or PUBCOMP packet.
+ *
+ * Converts the packet from a stream of bytes to an #MQTTStatus_t and extracts
+ * the packet identifier, reason code, properties.
+ *
+ * @param[in] pAck Pointer to the MQTT packet structure representing the packet.
+ * @param[out] pPacketIdentifier Packet ID of the ack type packet.
+ * @param[out] pAckInfo Structure to store the ack properties.
+ * @param[in] requestProblem To validate the packet.
+ *
+ * @return #MQTTSuccess, #MQTTBadResponse, #MQTTProtocolError and #MQTTMalformedPacket.
+ */
+ static MQTTStatus_t deserializeSimpleAckV5( const MQTTPacketInfo_t * pAck,
+ uint16_t * pPacketIdentifier,
+ MQTTAckInfo_t * pAckInfo,
+ bool requestProblem );
+
+/**
+ * @brief Serialize the publish properties in the given buffer.
+ *
+ * @param[in] pPublishInfo MQTT PUBLISH packet parameters.
+ * @param[in] pIndex Pointer to the current index of the buffer.
+ *
+ * @return Pointer to the new index of the buffer.
+ */
+ static uint8_t * serializePublishProperties( const MQTTPublishInfo_t * pPublishInfo,
+ uint8_t * pIndex );
+
+/**
+ * @brief Serialize an MQTT CONNECT packet in the given buffer.
+ *
+ * @param[in] pDisconnectInfo MQTT DISCONNECT packet parameters.
+ * @param[out] pFixedBuffer Buffer for packet serialization.
+ * @param[in] remainingLength Remaining Length of MQTT DISCONNECT packet.
+ * @param[in] sessionExpiry Session Expiry Interval.
+ *
+ */
+ static void serializeDisconnectPacketV5( const MQTTAckInfo_t * pDisconnectInfo,
+ const MQTTFixedBuffer_t * pFixedBuffer,
+ size_t remainingLength,
+ uint32_t sessionExpiry );
+
+/**
+ * @brief Prints and validates the appropriate message for the Disconnect response code if logs
+ * are enabled.
+ *
+ * @param[in] reasonCode MQTT Verion 5 standard DISCONNECT response code.
+ * @param[in] incoming To differentiate between outgoing and incoming disconnect.
+ *
+ * @return #MQTTSuccess,#MQTTBadParameter and #MQTTProtocolError.
+ */
+ static MQTTStatus_t validateDisconnectResponseV5( uint8_t reasonCode,
+ bool incoming );
+/*-----------------------------------------------------------*/
+
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ static MQTTStatus_t MQTT_GetUserPropertySize( const MQTTUserProperty_t * pUserProperty,
+ uint32_t number,
+ size_t * pSize )
+ {
+ MQTTStatus_t status = MQTTSuccess;
+ uint32_t i = 0;
+
+ /*Number of user properties can't be more than the max user properties specified*/
+ if( number > ( uint32_t ) MAX_USER_PROPERTY )
+ {
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ for( ; ( i < number ); i++ )
+ {
+ /*Validate the key and value*/
+ if( ( pUserProperty[ i ].keyLength == 0U ) || ( pUserProperty[ i ].valueLength == 0U ) || ( pUserProperty[ i ].pKey == NULL ) || ( pUserProperty[ i ].pValue == NULL ) )
+ {
+ status = MQTTBadParameter;
+ break;
+ }
+ else
+ {
+ *pSize += ( pUserProperty[ i ].keyLength );
+ *pSize += MQTT_UTF8_LENGTH_SIZE;
+ *pSize += ( pUserProperty[ i ].valueLength );
+ *pSize += 2U;
+ }
+ }
+ }
+
+ return status;
+ }
+
+ static MQTTStatus_t decodeutf_8pair( MQTTUserProperties_t * pUserProperties,
+ uint32_t * count,
+ size_t * pPropertyLength,
+ const uint8_t ** pIndex )
+ {
+ const uint8_t * pVariableHeader = *pIndex;
+ MQTTStatus_t status = MQTTSuccess;
+ MQTTUserProperty_t discardUserProperty;
+ MQTTUserProperty_t * pUserProperty;
+
+ if( *count == ( uint32_t ) MAX_USER_PROPERTY )
+ {
+ pUserProperty = &discardUserProperty;
+ }
+ else
+ {
+ pUserProperty = pUserProperties->userProperty;
+ pUserProperty = &pUserProperty[ *count ];
+ }
+
+ /*Validate the property length and decode the user property received.*/
+ if( *pPropertyLength < sizeof( uint16_t ) )
+ {
+ status = MQTTMalformedPacket;
+ }
+ else
+ {
+ pUserProperty->keyLength = UINT16_DECODE( pVariableHeader );
+ *pPropertyLength -= sizeof( uint16_t );
+
+ if( *pPropertyLength < pUserProperty->keyLength )
+ {
+ status = MQTTMalformedPacket;
+ }
+ else
+ {
+ pVariableHeader = &pVariableHeader[ sizeof( uint16_t ) ];
+ pUserProperty->pKey = ( const char * ) pVariableHeader;
+ *pPropertyLength -= pUserProperty->keyLength;
+ pVariableHeader = &pVariableHeader[ pUserProperty->keyLength ];
+
+ if( *pPropertyLength < sizeof( uint16_t ) )
+ {
+ status = MQTTMalformedPacket;
+ }
+ else
+ {
+ pUserProperty->valueLength = UINT16_DECODE( pVariableHeader );
+ *pPropertyLength -= sizeof( uint16_t );
+ pVariableHeader = &pVariableHeader[ sizeof( uint16_t ) ];
+
+ if( *pPropertyLength < ( size_t ) ( pUserProperty->valueLength ) )
+ {
+ status = MQTTMalformedPacket;
+ }
+ else
+ {
+ pUserProperty->pValue = ( const char * ) pVariableHeader;
+ pVariableHeader = &pVariableHeader[ pUserProperty->valueLength ];
+ *pPropertyLength -= pUserProperty->valueLength;
+ }
+ }
+ }
+ }
+
+ *pIndex = pVariableHeader;
+
+ if( ( *count == ( uint32_t ) MAX_USER_PROPERTY ) && ( status == MQTTSuccess ) )
+ {
+ LogDebug( ( "Discarded additional user property with key %s and value %s. ",
+ discardUserProperty.pKey,
+ discardUserProperty.pValue ) );
+ }
+ else
+ {
+ *count += 1U;
+ }
+
+ return status;
+ }
+ #else /* if ( MQTT_USER_PROPERTY_ENABLED ) */
+ static MQTTStatus_t decodeAndDiscard( size_t * pPropertyLength,
+ const uint8_t ** pIndex )
+ {
+ const uint8_t * pVariableHeader = *pIndex;
+ MQTTStatus_t status = MQTTSuccess;
+ uint16_t keyLength;
+ uint16_t valueLength;
+ const char * pKey;
+ const char * pValue;
+
+ /*Validate the property length and decode the user property received.*/
+ if( *pPropertyLength < sizeof( uint16_t ) )
+ {
+ status = MQTTMalformedPacket;
+ }
+ else
+ {
+ keyLength = UINT16_DECODE( pVariableHeader );
+ *pPropertyLength -= sizeof( uint16_t );
+
+ if( *pPropertyLength < keyLength )
+ {
+ status = MQTTMalformedPacket;
+ }
+ else
+ {
+ pVariableHeader = &pVariableHeader[ sizeof( uint16_t ) ];
+ pKey = ( const char * ) pVariableHeader;
+ *pPropertyLength -= keyLength;
+ pVariableHeader = &pVariableHeader[ keyLength ];
+
+ if( *pPropertyLength < sizeof( uint16_t ) )
+ {
+ status = MQTTMalformedPacket;
+ }
+ else
+ {
+ valueLength = UINT16_DECODE( pVariableHeader );
+ *pPropertyLength -= sizeof( uint16_t );
+ pVariableHeader = &pVariableHeader[ sizeof( uint16_t ) ];
+
+ if( *pPropertyLength < ( size_t ) ( valueLength ) )
+ {
+ status = MQTTMalformedPacket;
+ }
+ else
+ {
+ pValue = ( const char * ) pVariableHeader;
+ pVariableHeader = &pVariableHeader[ valueLength ];
+ *pPropertyLength -= valueLength;
+ }
+ }
+ }
+ }
+
+ *pIndex = pVariableHeader;
+
+ if( status == MQTTSuccess )
+ {
+ LogDebug( ( "Discarded additional user property with key %s and value %s. ",
+ pKey,
+ pValue ) );
+ }
+
+ return status;
+ }
+
+ #endif /* if ( MQTT_USER_PROPERTY_ENABLED ) */
+
+ static MQTTStatus_t MQTT_GetAuthInfoSize( const MQTTAuthInfo_t * pAuthInfo,
+ size_t * pPropertyLength )
+ {
+ MQTTStatus_t status = MQTTSuccess;
+
+ /*Validate the parameters*/
+ if( ( pAuthInfo->authMethodLength == 0U ) && ( pAuthInfo->authDataLength != 0U ) )
+ {
+ status = MQTTBadParameter;
+ }
+ else if( ( pAuthInfo->authMethodLength != 0U ) && ( pAuthInfo->pAuthMethod == NULL ) )
+ {
+ status = MQTTBadParameter;
+ }
+ else if( ( pAuthInfo->authDataLength != 0U ) && ( pAuthInfo->pAuthData == NULL ) )
+ {
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ /*Add authentication method and data if provided*/
+ if( pAuthInfo->authMethodLength != 0U )
+ {
+ *pPropertyLength += pAuthInfo->authMethodLength;
+ *pPropertyLength += MQTT_UTF8_LENGTH_SIZE;
+
+ if( pAuthInfo->authDataLength != 0U )
+ {
+ *pPropertyLength += pAuthInfo->authDataLength;
+ *pPropertyLength += MQTT_UTF8_LENGTH_SIZE;
+ }
+ }
+ }
+
+ return status;
+ }
+
+ static MQTTStatus_t MQTT_GetConnectPropertiesSize( MQTTConnectProperties_t * pConnectProperties )
+ {
+ size_t propertyLength = 0;
+ MQTTStatus_t status = MQTTSuccess;
+
+ /*Validate the arguments*/
+ if( pConnectProperties->maxPacketSize == 0U )
+ {
+ status = MQTTBadParameter;
+ }
+ else if( pConnectProperties->receiveMax == 0U )
+ {
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ /*Add the lengths of the parameters if applicable*/
+ if( pConnectProperties->sessionExpiry != 0U )
+ {
+ propertyLength += MQTT_SESSION_EXPIRY_SIZE;
+ }
+
+ if( pConnectProperties->receiveMax != ( uint16_t ) UINT16_MAX )
+ {
+ propertyLength += MQTT_RECEIVE_MAX_SIZE;
+ }
+
+ if( pConnectProperties->maxPacketSize != MQTT_MAX_PACKET_SIZE )
+ {
+ propertyLength += MQTT_MAX_PACKET_PROPERTY_SIZE;
+ }
+
+ if( pConnectProperties->topicAliasMax != 0U )
+ {
+ propertyLength += MQTT_TOPIC_ALIAS_SIZE;
+ }
+
+ if( pConnectProperties->requestResponseInfo != false )
+ {
+ propertyLength += MQTT_REQUEST_RESPONSE_SIZE;
+ }
+
+ if( pConnectProperties->requestProblemInfo != true )
+ {
+ propertyLength += MQTT_REQUEST_PROBLEM_SIZE;
+ }
+
+ if( pConnectProperties->pOutgoingAuth != NULL )
+ {
+ /*Valid authentication parameters*/
+ status = MQTT_GetAuthInfoSize( pConnectProperties->pOutgoingAuth, &propertyLength );
+ }
+ }
+
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ /*Get the length of the user properties*/
+ if( ( status == MQTTSuccess ) && ( pConnectProperties->pOutgoingUserProperty != NULL ) )
+ {
+ status = MQTT_GetUserPropertySize( pConnectProperties->pOutgoingUserProperty->userProperty, pConnectProperties->pOutgoingUserProperty->count, &propertyLength );
+ }
+ #endif
+
+ /*Variable length encoded values cannot have a value of more than 268435455U*/
+ if( ( status == MQTTSuccess ) && ( pConnectProperties->propertyLength > MQTT_MAX_REMAINING_LENGTH ) )
+ {
+ status = MQTTBadParameter;
+ }
+
+ if( status == MQTTSuccess )
+ {
+ pConnectProperties->propertyLength = propertyLength;
+ }
+
+ return status;
+ }
+
+ static MQTTStatus_t MQTT_GetPublishPropertiesSize( MQTTPublishInfo_t * pPublishProperties )
+ {
+ size_t propertyLength = 0U;
+ MQTTStatus_t status = MQTTSuccess;
+
+ /*Add the length of all the parameters which are provided*/
+ /*Only applicable for will*/
+ if( pPublishProperties->willDelay != 0U )
+ {
+ propertyLength += MQTT_WILL_DELAY_SIZE;
+ }
+
+ /*Only applicable for publish packet.*/
+ if( pPublishProperties->topicAlias != 0U )
+ {
+ propertyLength += 3U;
+ }
+
+ if( pPublishProperties->payloadFormat != 0U )
+ {
+ propertyLength += MQTT_PAYLOAD_FORMAT_SIZE;
+ }
+
+ if( pPublishProperties->msgExpiryPresent == true )
+ {
+ propertyLength += MQTT_MSG_EXPIRY_SIZE;
+ }
+
+ if( pPublishProperties->contentTypeLength != 0U )
+ {
+ if( pPublishProperties->pContentType == NULL )
+ {
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ propertyLength += ( pPublishProperties->contentTypeLength );
+ propertyLength += MQTT_UTF8_LENGTH_SIZE;
+ }
+ }
+
+ /*Validate if length and pointers are valid*/
+ if( ( status == MQTTSuccess ) && ( pPublishProperties->responseTopicLength != 0U ) )
+ {
+ if( pPublishProperties->pResponseTopic == NULL )
+ {
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ propertyLength += ( pPublishProperties->responseTopicLength );
+ propertyLength += MQTT_UTF8_LENGTH_SIZE;
+ }
+ }
+
+ if( ( status == MQTTSuccess ) && ( pPublishProperties->correlationLength != 0U ) )
+ {
+ if( pPublishProperties->pCorrelationData == NULL )
+ {
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ propertyLength += ( pPublishProperties->correlationLength );
+ propertyLength += MQTT_UTF8_LENGTH_SIZE;
+ }
+ }
+
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ /*Get the length of user properties*/
+ if( ( status == MQTTSuccess ) && ( pPublishProperties->pUserProperty != NULL ) )
+ {
+ status = MQTT_GetUserPropertySize( pPublishProperties->pUserProperty->userProperty, pPublishProperties->pUserProperty->count, &propertyLength );
+ }
+ #endif
+
+ /*Variable encoded can't have a value more than 268435455UL*/
+ if( propertyLength > MQTT_MAX_REMAINING_LENGTH )
+ {
+ status = MQTTBadParameter;
+ }
+
+ if( status == MQTTSuccess )
+ {
+ pPublishProperties->propertyLength = propertyLength;
+ }
+
+ return status;
+ }
+
+
+ static void serializeConnectPacketV5( const MQTTConnectInfo_t * pConnectInfo,
+ const MQTTPublishInfo_t * pWillInfo,
+ const MQTTConnectProperties_t * pConnectProperties,
+ size_t remainingLength,
+ const MQTTFixedBuffer_t * pFixedBuffer )
+ {
+ uint8_t * pIndex = NULL;
+
+ assert( pConnectInfo != NULL );
+ assert( pFixedBuffer != NULL );
+ assert( pFixedBuffer->pBuffer != NULL );
+ assert( pConnectProperties != NULL );
+ pIndex = pFixedBuffer->pBuffer;
+ /* Serialize the header. */
+ pIndex = MQTT_SerializeConnectFixedHeader( pIndex,
+ pConnectInfo,
+ pWillInfo,
+ remainingLength );
+
+ /* Serialize the connect Properties. */
+ pIndex = MQTTV5_SerializeConnectProperties( pIndex, pConnectProperties );
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ if( pConnectProperties->pOutgoingUserProperty != NULL )
+ {
+ uint32_t i = 0;
+ uint32_t size = pConnectProperties->pOutgoingUserProperty->count;
+ const MQTTUserProperty_t * pUserProperty = pConnectProperties->pOutgoingUserProperty->userProperty;
+
+ for( ; i < size; i++ )
+ {
+ *pIndex = MQTT_USER_PROPERTY_ID;
+ pIndex++;
+ pIndex = encodeString( pIndex, pUserProperty[ i ].pKey, pUserProperty[ i ].keyLength );
+ pIndex = encodeString( pIndex, pUserProperty[ i ].pValue, pUserProperty[ i ].valueLength );
+ }
+ }
+ #endif /* if ( MQTT_USER_PROPERTY_ENABLED ) */
+
+ if( pConnectProperties->pOutgoingAuth != NULL )
+ {
+ /* Serialize the authentication method string. */
+ *pIndex = MQTT_AUTH_METHOD_ID;
+ pIndex++;
+ pIndex = encodeString( pIndex, pConnectProperties->pOutgoingAuth->pAuthMethod, pConnectProperties->pOutgoingAuth->authMethodLength );
+
+ if( pConnectProperties->pOutgoingAuth->authDataLength != 0U )
+ {
+ *pIndex = MQTT_AUTH_DATA_ID;
+ pIndex++;
+ pIndex = encodeString( pIndex, pConnectProperties->pOutgoingAuth->pAuthData, pConnectProperties->pOutgoingAuth->authDataLength );
+ }
+ }
+
+ /* Write the client identifier into the CONNECT packet. */
+ pIndex = encodeString( pIndex,
+ pConnectInfo->pClientIdentifier,
+ pConnectInfo->clientIdentifierLength );
+
+ /* Serialize the will properties,topic name and message into the CONNECT packet if provided. */
+ if( pWillInfo != NULL )
+ {
+ pIndex = serializePublishProperties( pWillInfo, pIndex );
+ pIndex = encodeString( pIndex, pWillInfo->pTopicName, pWillInfo->topicNameLength );
+ pIndex = encodeString( pIndex, pWillInfo->pPayload, ( uint16_t ) pWillInfo->payloadLength );
+ }
+
+ /* Encode the user name if provided. */
+ if( pConnectInfo->pUserName != NULL )
+ {
+ pIndex = encodeString( pIndex, pConnectInfo->pUserName, pConnectInfo->userNameLength );
+ }
+
+ /* Encode the password if provided. */
+ if( pConnectInfo->pPassword != NULL )
+ {
+ pIndex = encodeString( pIndex, pConnectInfo->pPassword, pConnectInfo->passwordLength );
+ }
+
+ LogDebug( ( "Length of serialized CONNECT packet is %lu.",
+ ( ( unsigned long ) ( pIndex - pFixedBuffer->pBuffer ) ) ) );
+
+ /* Ensure that the difference between the end and beginning of the buffer
+ * is less than the buffer size. */
+ assert( ( ( size_t ) ( pIndex - pFixedBuffer->pBuffer ) ) <= pFixedBuffer->size );
+ }
+
+
+ static MQTTStatus_t logConnackResponseV5( uint8_t responseCode )
+ {
+ MQTTStatus_t status = MQTTServerRefused;
+
+ /* Log an error based on the CONNACK response code. */
+ switch( responseCode )
+ {
+ case ( uint8_t ) MQTT_REASON_UNSPECIFIED_ERR:
+ LogError( ( "Connection refused: Unspecified error" ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_MALFORMED_PACKET:
+ LogError( ( "Connection refused: Malformed Packet." ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_PROTOCOL_ERR:
+ LogError( ( "Connection refused: Protocol Error." ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_IMPL_SPECIFIC_ERR:
+ LogError( ( "Connection refused: Implementation specific error." ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_UNSUPPORTED_PROTO_VER:
+ LogError( ( "Connection refused: Unsupported Protocol Version." ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_CLIENT_ID_NOT_VALID:
+ LogError( ( "Connection refused: Client Identifier not valid." ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_BAD_USER_OR_PASS:
+ LogError( ( "Connection refused: Bad User Name or Password." ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_NOT_AUTHORIZED:
+ LogError( ( "Connection refused: Not authorized." ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_SERVER_UNAVAILABLE:
+ LogError( ( "Connection refused: Server unavailable." ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_SERVER_BUSY:
+ LogError( ( "Connection refused: Server busy." ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_BANNED:
+ LogError( ( "Connection refused: Banned." ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_BAD_AUTH_METHOD:
+ LogError( ( "Connection refused: Bad authentication method." ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_TOPIC_NAME_INVALID:
+ LogError( ( "Connection refused: Topic Name invalid." ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_PACKET_TOO_LARGE:
+ LogError( ( "Connection refused: Packet too large ." ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_QUOTA_EXCEEDED:
+ LogError( ( "Connection refused: Quota exceeded." ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_PAYLOAD_FORMAT_INVALID:
+ LogError( ( "Connection refused: Payload format invalid." ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_RETAIN_NOT_SUPPORTED:
+ LogError( ( "Connection refused: Retain not supported." ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_QOS_NOT_SUPPORTED:
+ LogError( ( "Connection refused: QoS not supported." ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_USE_ANOTHER_SERVER:
+ LogError( ( "Connection refused: Use another server." ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_SERVER_MOVED:
+ LogError( ( "Connection refused: Server moved." ) );
+ break;
+
+ case ( uint8_t ) MQTT_REASON_CON_RATE_EXCEED:
+ LogError( ( "Connection refused: Connection rate exceeded." ) );
+ break;
+
+ default:
+ status = MQTTProtocolError;
+ break;
+ }
+
+ return status;
+ }
+
+ static MQTTStatus_t validateConnackParams( const MQTTPacketInfo_t * pIncomingPacket,
+ bool * pSessionPresent )
+ {
+ MQTTStatus_t status = MQTTSuccess;
+
+ /*Validate the arguments*/
+ if( pIncomingPacket == NULL )
+ {
+ LogError( ( "pIncomingPacket cannot be NULL." ) );
+ status = MQTTBadParameter;
+ }
+ else if( pSessionPresent == NULL )
+ {
+ LogError( ( "pSessionPresent cannot be NULL for CONNACK packet." ) );
+ status = MQTTBadParameter;
+ }
+ else if( pIncomingPacket->pRemainingData == NULL )
+ {
+ LogError( ( "Remaining data of incoming packet is NULL." ) );
+ status = MQTTBadParameter;
+ }
+ else if( pIncomingPacket->type != MQTT_PACKET_TYPE_CONNACK )
+ {
+ LogError( ( "Packet type is invalid." ) );
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ const uint8_t * pRemainingData = NULL;
+ pRemainingData = pIncomingPacket->pRemainingData;
+
+ if( ( pRemainingData[ 0 ] | 0x01U ) != 0x01U )
+ {
+ LogError( ( "Reserved bits in CONNACK incorrect." ) );
+
+ status = MQTTBadResponse;
+ }
+ else
+ {
+ /* Determine if the "Session Present" bit is set. This is the lowest bit of
+ * the third byte in CONNACK. */
+ if( ( pRemainingData[ 0 ] & MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK ) == MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK )
+ {
+ LogDebug( ( "CONNACK session present bit set." ) );
+ *pSessionPresent = true;
+
+ /* MQTT 5 specifies that the fourth byte in CONNACK must be 0 if the
+ * "Session Present" bit is set. */
+ if( pRemainingData[ 1 ] != 0U )
+ {
+ LogError( ( "Session Present bit is set, but connect return code in CONNACK is %u (nonzero).",
+ ( unsigned int ) pRemainingData[ 1 ] ) );
+ status = MQTTBadResponse;
+ }
+ }
+ else
+ {
+ LogDebug( ( "CONNACK session present bit not set." ) );
+ *pSessionPresent = false;
+ }
+ }
+
+ if( status == MQTTSuccess )
+ {
+ if( pRemainingData[ 1 ] != 0u )
+ {
+ status = logConnackResponseV5( pRemainingData[ 1 ] );
+ }
+ }
+ }
+
+ return status;
+ }
+
+ static MQTTStatus_t decodeVariableLength( const uint8_t * pBuffer,
+ size_t * pLength )
+ {
+ size_t remainingLength = 0;
+ size_t multiplier = 1;
+ size_t bytesDecoded = 0;
+ size_t expectedSize = 0;
+ uint8_t encodedByte = 0;
+ MQTTStatus_t status = MQTTSuccess;
+
+ /* This algorithm is copied from the MQTT v3.1.1 spec. */
+ do
+ {
+ if( multiplier > 2097152U ) /* 128 ^ 3 */
+ {
+ remainingLength = MQTT_REMAINING_LENGTH_INVALID;
+
+ LogError( ( "Invalid remaining length in the packet.\n" ) );
+
+ status = MQTTBadResponse;
+ }
+ else
+ {
+ /* Get the next byte. It is at the next position after the bytes
+ * decoded till now since the header of one byte was read before. */
+ encodedByte = pBuffer[ bytesDecoded ];
+ remainingLength += ( ( size_t ) encodedByte & 0x7FU ) * multiplier;
+ multiplier *= 128U;
+ bytesDecoded++;
+ }
+
+ /* If the response is incorrect then
+ * break out of the loop. */
+ if( remainingLength == MQTT_REMAINING_LENGTH_INVALID )
+ {
+ break;
+ }
+ } while( ( encodedByte & 0x80U ) != 0U );
+
+ if( status == MQTTSuccess )
+ {
+ /* Check that the decoded remaining length conforms to the MQTT specification. */
+ expectedSize = remainingLengthEncodedSize( remainingLength );
+
+ if( bytesDecoded != expectedSize )
+ {
+ LogError( ( "Expected and actual length of decoded bytes do not match.\n" ) );
+ status = MQTTBadResponse;
+ }
+ else
+ {
+ *pLength = remainingLength;
+ }
+ }
+
+ return status;
+ }
+
+ static MQTTStatus_t decodeuint32_t( uint32_t * pProperty,
+ size_t * pPropertyLength,
+ bool * pUsed,
+ const uint8_t ** pIndex )
+ {
+ const uint8_t * pVariableHeader = *pIndex;
+ MQTTStatus_t status = MQTTSuccess;
+
+ /*Protocol error to include the same property twice.*/
+ if( *pUsed == true )
+ {
+ status = MQTTProtocolError;
+ }
+ /*Validate the length and decode.*/
+ else if( *pPropertyLength < sizeof( uint32_t ) )
+ {
+ status = MQTTMalformedPacket;
+ }
+ else
+ {
+ *pProperty = UINT32_DECODE( pVariableHeader );
+ pVariableHeader = &pVariableHeader[ sizeof( uint32_t ) ];
+ *pUsed = true;
+ *pPropertyLength -= sizeof( uint32_t );
+ }
+
+ *pIndex = pVariableHeader;
+ return status;
+ }
+
+ static MQTTStatus_t decodeuint16_t( uint16_t * pProperty,
+ size_t * pPropertyLength,
+ bool * pUsed,
+ const uint8_t ** pIndex )
+ {
+ const uint8_t * pVariableHeader = *pIndex;
+ MQTTStatus_t status = MQTTSuccess;
+
+ /*Protocol error to include the same property twice.*/
+
+ if( *pUsed == true )
+ {
+ status = MQTTProtocolError;
+ }
+ /*Validate the length and decode.*/
+
+ else if( *pPropertyLength < sizeof( uint16_t ) )
+ {
+ status = MQTTMalformedPacket;
+ }
+ else
+ {
+ *pProperty = UINT16_DECODE( pVariableHeader );
+ pVariableHeader = &pVariableHeader[ sizeof( uint16_t ) ];
+ *pUsed = true;
+ *pPropertyLength -= sizeof( uint16_t );
+ }
+
+ *pIndex = pVariableHeader;
+ return status;
+ }
+
+ static MQTTStatus_t decodeuint8_t( uint8_t * pProperty,
+ size_t * pPropertyLength,
+ bool * pUsed,
+ const uint8_t ** pIndex )
+ {
+ const uint8_t * pVariableHeader = *pIndex;
+ MQTTStatus_t status = MQTTSuccess;
+
+ /*Protocol error to include the same property twice.*/
+
+ if( *pUsed == true )
+ {
+ status = MQTTProtocolError;
+ }
+ /*Validate the length and decode.*/
+
+ else if( *pPropertyLength < sizeof( uint8_t ) )
+ {
+ status = MQTTMalformedPacket;
+ }
+ else
+ {
+ *pProperty = *pVariableHeader;
+ pVariableHeader = &pVariableHeader[ sizeof( uint8_t ) ];
+ *pUsed = true;
+ *pPropertyLength -= sizeof( uint8_t );
+
+ if( *pProperty > 1U )
+ {
+ status = MQTTProtocolError;
+ }
+ }
+
+ *pIndex = pVariableHeader;
+ return status;
+ }
+
+ static MQTTStatus_t decodeutf_8( const char ** pProperty,
+ uint16_t * pLength,
+ size_t * pPropertyLength,
+ bool * pUsed,
+ const uint8_t ** pIndex )
+ {
+ const uint8_t * pVariableHeader = *pIndex;
+ MQTTStatus_t status = MQTTSuccess;
+
+ /*Protocol error to include the same property twice.*/
+
+ if( *pUsed == true )
+ {
+ status = MQTTProtocolError;
+ }
+ /*Validate the length and decode.*/
+
+ else if( *pPropertyLength < sizeof( uint16_t ) )
+ {
+ status = MQTTMalformedPacket;
+ }
+ else
+ {
+ *pLength = UINT16_DECODE( pVariableHeader );
+ pVariableHeader = &pVariableHeader[ sizeof( uint16_t ) ];
+ *pPropertyLength -= sizeof( uint16_t );
+
+ if( *pPropertyLength < *pLength )
+ {
+ status = MQTTMalformedPacket;
+ }
+ else
+ {
+ *pProperty = ( const char * ) pVariableHeader;
+ pVariableHeader = &pVariableHeader[ *pLength ];
+ *pPropertyLength -= *pLength;
+ *pUsed = true;
+ }
+ }
+
+ *pIndex = pVariableHeader;
+ return status;
+ }
+
+ static MQTTStatus_t decodeAuthInfo( const MQTTConnectProperties_t * pConnackProperties,
+ bool * pAuthMethod,
+ bool * pAuthData,
+ size_t * pPropertyLength,
+ const uint8_t ** pIndex,
+ const uint8_t id )
+ {
+ MQTTStatus_t status = MQTTSuccess;
+
+ if( pConnackProperties->pOutgoingAuth == NULL )
+ {
+ status = MQTTProtocolError;
+ }
+ else if( id == ( uint8_t ) MQTT_AUTH_METHOD_ID )
+ {
+ /*Decode the authenticaton method */
+ status = decodeutf_8( &pConnackProperties->pIncomingAuth->pAuthMethod, &pConnackProperties->pIncomingAuth->authMethodLength, pPropertyLength, pAuthMethod, pIndex );
+ }
+ else
+ {
+ /*Decode the authentication data */
+ status = decodeutf_8( &pConnackProperties->pIncomingAuth->pAuthData, &pConnackProperties->pIncomingAuth->authDataLength, pPropertyLength, pAuthData, pIndex );
+ }
+
+ return status;
+ }
+
+ static MQTTStatus_t deserializeConnackV5( MQTTConnectProperties_t * pConnackProperties,
+ size_t length,
+ const uint8_t * const * pIndex )
+ {
+ MQTTStatus_t status = MQTTSuccess;
+ const uint8_t * pVariableHeader = *pIndex;
+ size_t propertyLength = length;
+ bool sessionExpiry = false;
+ bool serverReceiveMax = false;
+ bool maxQos = false;
+ bool retain = false;
+ bool maxPacket = false;
+ bool clientId = false;
+ bool topicAlias = false;
+ bool reasonString = false;
+ bool wildcard = false;
+ bool subId = false;
+ bool sharedsub = false;
+ bool keepAlive = false;
+ bool responseInfo = false;
+ bool serverRef = false;
+ bool authMethod = false;
+ bool authData = false;
+
+ pVariableHeader = &pVariableHeader[ remainingLengthEncodedSize( propertyLength ) ];
+
+ /*Decode all the properties received, validate and store them in pConnackProperties.*/
+ while( ( propertyLength > 0U ) && ( status == MQTTSuccess ) )
+ {
+ uint8_t packetId = *pVariableHeader;
+ pVariableHeader = &pVariableHeader[ 1 ];
+ propertyLength -= sizeof( uint8_t );
+
+ switch( packetId )
+ {
+ case MQTT_SESSION_EXPIRY_ID:
+ status = decodeuint32_t( &pConnackProperties->sessionExpiry, &propertyLength, &sessionExpiry, &pVariableHeader );
+ break;
+
+ case MQTT_RECEIVE_MAX_ID:
+ status = decodeuint16_t( &pConnackProperties->serverReceiveMax, &propertyLength, &serverReceiveMax, &pVariableHeader );
+ break;
+
+ case MQTT_MAX_QOS_ID:
+ status = decodeuint8_t( &pConnackProperties->serverMaxQos, &propertyLength, &maxQos, &pVariableHeader );
+ break;
+
+ case MQTT_RETAIN_AVAILABLE_ID:
+ status = decodeuint8_t( &pConnackProperties->retainAvailable, &propertyLength, &retain, &pVariableHeader );
+ break;
+
+ case MQTT_MAX_PACKET_SIZE_ID:
+ status = decodeuint32_t( &pConnackProperties->serverMaxPacketSize, &propertyLength, &maxPacket, &pVariableHeader );
+ break;
+
+ case MQTT_ASSIGNED_CLIENT_ID:
+ status = decodeutf_8( &pConnackProperties->pClientIdentifier, &pConnackProperties->clientIdLength, &propertyLength, &clientId, &pVariableHeader );
+ break;
+
+ case MQTT_TOPIC_ALIAS_MAX_ID:
+ status = decodeuint16_t( &pConnackProperties->serverTopicAliasMax, &propertyLength, &topicAlias, &pVariableHeader );
+ break;
+
+ case MQTT_REASON_STRING_ID:
+ status = decodeutf_8( &pConnackProperties->pReasonString, &pConnackProperties->reasonStringLength, &propertyLength, &reasonString, &pVariableHeader );
+ break;
+
+ case MQTT_USER_PROPERTY_ID:
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ status = decodeutf_8pair( pConnackProperties->pIncomingUserProperty, &pConnackProperties->pIncomingUserProperty->count, &propertyLength, &pVariableHeader );
+ #else
+ status = decodeAndDiscard( &propertyLength, &pVariableHeader );
+ #endif
+ break;
+
+ case MQTT_WILDCARD_ID:
+ status = decodeuint8_t( &pConnackProperties->isWildcardAvaiable, &propertyLength, &wildcard, &pVariableHeader );
+ break;
+
+ case MQTT_SUB_AVAILABLE_ID:
+ status = decodeuint8_t( &pConnackProperties->subscriptionId, &propertyLength, &subId, &pVariableHeader );
+ break;
+
+ case MQTT_SHARED_SUB_ID:
+ status = decodeuint8_t( &pConnackProperties->isSharedAvailable, &propertyLength, &sharedsub, &pVariableHeader );
+ break;
+
+ case MQTT_SERVER_KEEP_ALIVE_ID:
+ status = decodeuint16_t( &pConnackProperties->serverKeepAlive, &propertyLength, &keepAlive, &pVariableHeader );
+ break;
+
+ case MQTT_RESPONSE_INFO_ID:
+ status = decodeutf_8( &pConnackProperties->pResponseInfo, &pConnackProperties->responseInfoLength, &propertyLength, &responseInfo, &pVariableHeader );
+ break;
+
+ case MQTT_SERVER_REF_ID:
+ status = decodeutf_8( &pConnackProperties->pServerRef, &pConnackProperties->serverRefLength, &propertyLength, &serverRef, &pVariableHeader );
+ break;
+
+ case MQTT_AUTH_METHOD_ID:
+ case MQTT_AUTH_DATA_ID:
+
+ status = decodeAuthInfo( pConnackProperties, &authMethod, &authData, &propertyLength, &pVariableHeader, packetId );
+ break;
+
+ /*Protocol error to include any other property id.*/
+ default:
+ status = MQTTProtocolError;
+ break;
+ }
+ }
+
+ if( status == MQTTSuccess )
+ {
+ /*Receive max cannot be 0.*/
+ if( ( serverReceiveMax == true ) && ( pConnackProperties->serverReceiveMax == 0U ) )
+ {
+ status = MQTTProtocolError;
+ }
+
+ /*Maximum packet size cannot be 0.*/
+ else if( ( maxPacket == true ) && ( pConnackProperties->serverMaxPacketSize == 0U ) )
+ {
+ status = MQTTProtocolError;
+ }
+ /*Protocol error to send response information if the client has not requested it.*/
+ else if( ( responseInfo == true ) && ( pConnackProperties->requestResponseInfo == false ) )
+ {
+ status = MQTTProtocolError;
+ }
+
+ else
+ {
+ /* MISRA Empty body */
+ }
+ }
+
+ return status;
+ }
+
+ static MQTTStatus_t calculatePublishPacketSizeV5( MQTTPublishInfo_t * pPublishInfo,
+ size_t * pRemainingLength,
+ size_t * pPacketSize,
+ uint32_t maxPacketSize )
+ {
+ MQTTStatus_t status = MQTTSuccess;
+ size_t packetSize = 0, payloadLimit = 0;
+
+ assert( pPublishInfo != NULL );
+ assert( pRemainingLength != NULL );
+ assert( pPacketSize != NULL );
+
+ /* The variable header of a PUBLISH packet always contains the topic name.
+ * The first 2 bytes of UTF-8 string contains length of the string.
+ */
+ packetSize += pPublishInfo->topicNameLength + sizeof( uint16_t );
+
+ /* The variable header of a QoS 1 or 2 PUBLISH packet contains a 2-byte
+ * packet identifier. */
+ if( pPublishInfo->qos > MQTTQoS0 )
+ {
+ packetSize += sizeof( uint16_t );
+ }
+
+ /*Calculate the size of the publish properties*/
+ status = MQTT_GetPublishPropertiesSize( pPublishInfo );
+
+ if( status == MQTTSuccess )
+ {
+ packetSize += pPublishInfo->propertyLength;
+ packetSize += remainingLengthEncodedSize( pPublishInfo->propertyLength );
+
+ /* Calculate the maximum allowed size of the payload for the given parameters.
+ * This calculation excludes the "Remaining length" encoding, whose size is not
+ * yet known. */
+ payloadLimit = MQTT_MAX_REMAINING_LENGTH - packetSize - 1U;
+
+ /* Ensure that the given payload fits within the calculated limit. */
+ if( pPublishInfo->payloadLength > payloadLimit )
+ {
+ LogError( ( "PUBLISH payload length of %lu cannot exceed "
+ "%lu so as not to exceed the maximum "
+ "remaining length of MQTT 3.1.1 packet( %lu ).",
+ ( unsigned long ) pPublishInfo->payloadLength,
+ ( unsigned long ) payloadLimit,
+ MQTT_MAX_REMAINING_LENGTH ) );
+ status = MQTTBadParameter;
+ }
+ }
+
+ if( status == MQTTSuccess )
+ {
+ /* Add the length of the PUBLISH payload. At this point, the "Remaining length"
+ * has been calculated. */
+ packetSize += pPublishInfo->payloadLength;
+
+ /* Now that the "Remaining length" is known, recalculate the payload limit
+ * based on the size of its encoding. */
+ payloadLimit -= remainingLengthEncodedSize( packetSize );
+
+ /* Check that the given payload fits within the size allowed by MQTT spec. */
+ if( pPublishInfo->payloadLength > payloadLimit )
+ {
+ LogError( ( "PUBLISH payload length of %lu cannot exceed "
+ "%lu so as not to exceed the maximum "
+ "remaining length of MQTT 3.1.1 packet( %lu ).",
+ ( unsigned long ) pPublishInfo->payloadLength,
+ ( unsigned long ) payloadLimit,
+ MQTT_MAX_REMAINING_LENGTH ) );
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ /* Set the "Remaining length" output parameter and calculate the full
+ * size of the PUBLISH packet. */
+ *pRemainingLength = packetSize;
+
+ packetSize += 1U + remainingLengthEncodedSize( packetSize );
+
+ if( packetSize > maxPacketSize )
+ {
+ LogError( ( "Packet size is greater than the allowed maximum packet size." ) );
+ status = MQTTBadParameter;
+ }
+
+ *pPacketSize = packetSize;
+ }
+ }
+
+ LogDebug( ( "PUBLISH packet remaining length=%lu and packet size=%lu.",
+ ( unsigned long ) *pRemainingLength,
+ ( unsigned long ) *pPacketSize ) );
+
+ return status;
+ }
+
+ static void serializePubAckPacketV5( const MQTTAckInfo_t * pAckInfo,
+ uint8_t packetType,
+ uint16_t packetId,
+ size_t remainingLength,
+ const MQTTFixedBuffer_t * pFixedBuffer )
+ {
+ uint8_t * pIndex = NULL;
+
+ assert( pAckInfo != NULL );
+ assert( pFixedBuffer != NULL );
+ assert( pFixedBuffer->pBuffer != NULL );
+ pIndex = pFixedBuffer->pBuffer;
+ /* Serialize the header including reason code and property length */
+ pIndex = MQTTV5_SerializeAckFixed( pIndex,
+ packetType,
+ packetId,
+ remainingLength,
+ pAckInfo->propertyLength );
+
+ /*Serialize the reason string if provided.*/
+ if( pAckInfo->reasonStringLength != 0U )
+ {
+ *pIndex = MQTT_REASON_STRING_ID;
+ pIndex++;
+ pIndex = encodeString( pIndex, pAckInfo->pReasonString, pAckInfo->reasonStringLength );
+ }
+
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ /*Serialize the user properties if provided.*/
+ if( pAckInfo->pUserProperty != NULL )
+ {
+ uint32_t i = 0;
+ uint32_t size = pAckInfo->pUserProperty->count;
+ const MQTTUserProperty_t * pUserProperty = pAckInfo->pUserProperty->userProperty;
+
+ for( ; i < size; i++ )
+ {
+ *pIndex = MQTT_USER_PROPERTY_ID;
+ pIndex++;
+ pIndex = encodeString( pIndex, pUserProperty[ i ].pKey, pUserProperty[ i ].keyLength );
+ pIndex = encodeString( pIndex, pUserProperty[ i ].pValue, pUserProperty[ i ].valueLength );
+ }
+ }
+ #endif /* if ( MQTT_USER_PROPERTY_ENABLED ) */
+
+ /* Ensure that the difference between the end and beginning of the buffer
+ * is less than the buffer size. */
+ assert( ( ( size_t ) ( pIndex - pFixedBuffer->pBuffer ) ) <= pFixedBuffer->size );
+ }
+
+/* coverity[misra_c_2012_rule_2_7_violation] */
+ static MQTTStatus_t logAckResponseV5( uint8_t reasonCode,
+ uint16_t packetIdentifier )
+ {
+ MQTTStatus_t status = MQTTServerRefused;
+
+ switch( reasonCode )
+ {
+ case MQTT_REASON_SUCCESS:
+ status = MQTTSuccess;
+ break;
+
+ case MQTT_REASON_NO_MATCHING_SUBSCRIBERS:
+ LogDebug( ( "Publish accepted with packet id %hu: No matching subscribers.",
+ ( unsigned short ) packetIdentifier ) );
+ status = MQTTSuccess;
+ break;
+
+ case MQTT_REASON_UNSPECIFIED_ERR:
+ LogError( ( "Publish refused with packet id %hu: Connection rate exceeded.",
+ ( unsigned short ) packetIdentifier ) );
+ break;
+
+ case MQTT_REASON_IMPL_SPECIFIC_ERR:
+ LogError( ( "Publish refused with packet id %hu: The PUBLISH is valid but the receiver is not willing to accept it.",
+ ( unsigned short ) packetIdentifier ) );
+ break;
+
+ case MQTT_REASON_NOT_AUTHORIZED:
+ LogError( ( "Publish refused with packet id %hu: The PUBLISH is not authorized.",
+ ( unsigned short ) packetIdentifier ) );
+ break;
+
+ case MQTT_REASON_TOPIC_NAME_INVALID:
+ LogError( ( "Publish refused with packet id %hu: Topic Name not accepted.",
+ ( unsigned short ) packetIdentifier ) );
+ break;
+
+ case MQTT_REASON_PACKET_ID_IN_USE:
+ LogError( ( "Publish refused with packet id %hu: The Packet Identifier is already in use. ",
+ ( unsigned short ) packetIdentifier ) );
+ break;
+
+ case MQTT_REASON_QUOTA_EXCEEDED:
+ LogError( ( "Publish refused with packet id %hu: Quota exceeded.",
+ ( unsigned short ) packetIdentifier ) );
+ break;
+
+ case MQTT_REASON_PAYLOAD_FORMAT_INVALID:
+ LogError( ( "Publish refused with packet id %hu: Payload format indicator is invalid.",
+ ( unsigned short ) packetIdentifier ) );
+ break;
+
+ default:
+ status = MQTTProtocolError;
+ break;
+ }
+
+ return status;
+ }
+
+/* coverity[misra_c_2012_rule_2_7_violation] */
+ static MQTTStatus_t logSimpleAckResponseV5( uint8_t reasonCode,
+ uint16_t packetIdentifier )
+ {
+ MQTTStatus_t status = MQTTServerRefused;
+
+ switch( reasonCode )
+ {
+ case MQTT_REASON_SUCCESS:
+ status = MQTTSuccess;
+ break;
+
+ case MQTT_REASON_PACKET_ID_NOT_FOUND:
+ LogError( ( "Publish refused with packet id %hu: Packet identifier invalid.",
+ ( unsigned short ) packetIdentifier ) );
+ status = MQTTServerRefused;
+ break;
+
+ default:
+ status = MQTTProtocolError;
+ break;
+ }
+
+ return status;
+ }
+
+
+ static MQTTStatus_t decodeAckProperties( MQTTAckInfo_t * pAckInfo,
+ const uint8_t * pIndex,
+ size_t remainingLength )
+ {
+ size_t propertyLength = 0U;
+ MQTTStatus_t status = MQTTSuccess;
+ const uint8_t * pLocalIndex = pIndex;
+
+ /*Decode the property length*/
+ status = decodeVariableLength( pLocalIndex, &propertyLength );
+
+ if( status == MQTTSuccess )
+ {
+ pLocalIndex = &pLocalIndex[ remainingLengthEncodedSize( propertyLength ) ];
+
+ /*Validate the remaining length.*/
+ if( remainingLength != ( propertyLength + remainingLengthEncodedSize( propertyLength ) + 3U ) )
+ {
+ status = MQTTMalformedPacket;
+ }
+ }
+
+ if( status == MQTTSuccess )
+ {
+ while( ( propertyLength > 0U ) && ( status == MQTTSuccess ) )
+ {
+ /*Decode the property id.*/
+ uint8_t packetId = *pLocalIndex;
+ bool reasonString = false;
+ pLocalIndex = &pLocalIndex[ 1 ];
+ propertyLength -= sizeof( uint8_t );
+
+ switch( packetId )
+ {
+ case MQTT_REASON_STRING_ID:
+ status = decodeutf_8( &pAckInfo->pReasonString, &pAckInfo->reasonStringLength, &propertyLength, &reasonString, &pLocalIndex );
+ break;
+
+ case MQTT_USER_PROPERTY_ID:
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ status = decodeutf_8pair( pAckInfo->pUserProperty, &pAckInfo->pUserProperty->count, &propertyLength, &pLocalIndex );
+ #else
+ status = decodeAndDiscard( &propertyLength, &pLocalIndex );
+ #endif
+ break;
+
+ default:
+ status = MQTTProtocolError;
+ break;
+ }
+ }
+ }
+
+ return status;
+ }
+
+ static MQTTStatus_t deserializeSimpleAckV5( const MQTTPacketInfo_t * pAck,
+ uint16_t * pPacketIdentifier,
+ MQTTAckInfo_t * pAckInfo,
+ bool requestProblem )
+ {
+ MQTTStatus_t status = MQTTSuccess;
+ const uint8_t * pIndex = pAck->pRemainingData;
+
+ if( pAck->remainingLength < 2U )
+ {
+ status = MQTTMalformedPacket;
+ }
+
+ if( status == MQTTSuccess )
+ {
+ /* Extract the packet identifier (third and fourth bytes) from ACK. */
+ *pPacketIdentifier = UINT16_DECODE( pIndex );
+ pIndex = &pIndex[ 2 ];
+
+ LogDebug( ( "Packet identifier %hu.",
+ ( unsigned short ) *pPacketIdentifier ) );
+
+ /* Packet identifier cannot be 0. */
+ if( *pPacketIdentifier == 0U )
+ {
+ LogError( ( "Packet identifier cannot be 0." ) );
+ status = MQTTBadResponse;
+ }
+ }
+
+ /* If reason code is success, server can choose to not send the reason code.*/
+ if( ( status == MQTTSuccess ) && ( pAck->remainingLength > 2U ) )
+ {
+ pAckInfo->reasonCode = *pIndex;
+ pIndex++;
+ }
+
+ if( ( pAck->remainingLength > 4U ) )
+ {
+ /*Protocol error to send user property and reason string if client has set request problem to false.*/
+ if( requestProblem == false )
+ {
+ status = MQTTProtocolError;
+ }
+
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ else if( pAckInfo->pUserProperty == NULL )
+ {
+ status = MQTTBadParameter;
+ }
+ #endif
+ else
+ {
+ status = decodeAckProperties( pAckInfo, pIndex, pAck->remainingLength );
+ }
+ }
+
+ return status;
+ }
+
+ static uint8_t * serializePublishProperties( const MQTTPublishInfo_t * pPublishInfo,
+ uint8_t * pIndex )
+ {
+ uint8_t * pIndexLocal = pIndex;
+
+ pIndexLocal = MQTT_SerializePublishProperties( pPublishInfo, pIndexLocal );
+
+ /*Serialize the provided will properties which has variable length.*/
+ if( pPublishInfo->contentTypeLength != 0U )
+ {
+ *pIndexLocal = MQTT_CONTENT_TYPE_ID;
+ pIndexLocal++;
+ pIndexLocal = encodeString( pIndexLocal, pPublishInfo->pContentType, pPublishInfo->contentTypeLength );
+ }
+
+ if( pPublishInfo->responseTopicLength != 0U )
+ {
+ *pIndexLocal = MQTT_RESPONSE_TOPIC_ID;
+ pIndexLocal++;
+ pIndexLocal = encodeString( pIndexLocal, pPublishInfo->pResponseTopic, pPublishInfo->responseTopicLength );
+ }
+
+ if( pPublishInfo->correlationLength != 0U )
+ {
+ *pIndexLocal = MQTT_CORRELATION_DATA_ID;
+ pIndexLocal++;
+ pIndexLocal = encodeString( pIndexLocal, pPublishInfo->pCorrelationData, pPublishInfo->correlationLength );
+ }
+
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ if( pPublishInfo->pUserProperty != NULL )
+ {
+ uint32_t i = 0;
+ uint32_t size = pPublishInfo->pUserProperty->count;
+ const MQTTUserProperty_t * pUserProperty = pPublishInfo->pUserProperty->userProperty;
+
+ for( ; i < size; i++ )
+ {
+ *pIndexLocal = MQTT_USER_PROPERTY_ID;
+ pIndexLocal++;
+ pIndexLocal = encodeString( pIndexLocal, pUserProperty[ i ].pKey, pUserProperty[ i ].keyLength );
+ pIndexLocal = encodeString( pIndexLocal, pUserProperty[ i ].pValue, pUserProperty[ i ].valueLength );
+ }
+ }
+ #endif /* if ( MQTT_USER_PROPERTY_ENABLED ) */
+ return pIndexLocal;
+ }
+
+ static MQTTStatus_t validateDisconnectResponseV5( uint8_t reasonCode,
+ bool incoming )
+ {
+ MQTTStatus_t status;
+
+ /*Validate the reason code.*/
+ switch( reasonCode )
+ {
+ case MQTT_REASON_SEND_WILL:
+
+ if( incoming == true )
+ {
+ status = MQTTProtocolError;
+ }
+ else
+ {
+ status = MQTTSuccess;
+ }
+
+ break;
+
+ case MQTT_REASON_SUCCESS:
+ case MQTT_REASON_UNSPECIFIED_ERR:
+ case MQTT_REASON_MALFORMED_PACKET:
+ case MQTT_REASON_PROTOCOL_ERR:
+ case MQTT_REASON_IMPL_SPECIFIC_ERR:
+ case MQTT_REASON_TOPIC_NAME_INVALID:
+ case MQTT_REASON_RX_MAX_EXCEEDED:
+ case MQTT_REASON_TOPIC_ALIAS_INVALID:
+ case MQTT_REASON_PACKET_TOO_LARGE:
+ case MQTT_REASON_MSG_RATE_TOO_HIGH:
+ case MQTT_REASON_QUOTA_EXCEEDED:
+ case MQTT_REASON_ADMIN_ACTION:
+ case MQTT_REASON_PAYLOAD_FORMAT_INVALID:
+ status = MQTTSuccess;
+ break;
+
+ case MQTT_REASON_NOT_AUTHORIZED:
+ case MQTT_REASON_SERVER_BUSY:
+ case MQTT_REASON_SERVER_SHUTTING_DOWN:
+ case MQTT_REASON_KEEP_ALIVE_TIMEOUT:
+ case MQTT_REASON_SESSION_TAKEN_OVER:
+ case MQTT_REASON_TOPIC_FILTER_INVALID:
+ case MQTT_REASON_RETAIN_NOT_SUPPORTED:
+ case MQTT_REASON_QOS_NOT_SUPPORTED:
+ case MQTT_REASON_USE_ANOTHER_SERVER:
+ case MQTT_REASON_SERVER_MOVED:
+ case MQTT_REASON_MAX_CON_TIME:
+ case MQTT_REASON_SS_NOT_SUPPORTED:
+ case MQTT_REASON_WILDCARD_SUB_NOT_SUP:
+
+ if( incoming == true )
+ {
+ status = MQTTSuccess;
+ }
+ else
+ {
+ status = MQTTBadParameter;
+ }
+
+ break;
+
+ default:
+ status = MQTTProtocolError;
+ break;
+ }
+
+ return status;
+ }
+
+#endif /* if ( MQTT_VERSION_5_ENABLED ) */
+/*-----------------------------------------------------------*/
+
+static size_t remainingLengthEncodedSize( size_t length )
+{
+ size_t encodedSize;
+
+ /* Determine how many bytes are needed to encode length.
+ * The values below are taken from the MQTT 3.1.1 spec. */
+
+ /* 1 byte is needed to encode lengths between 0 and 127. */
+ if( length < 128U )
+ {
+ encodedSize = 1U;
+ }
+ /* 2 bytes are needed to encode lengths between 128 and 16,383. */
+ else if( length < 16384U )
+ {
+ encodedSize = 2U;
+ }
+ /* 3 bytes are needed to encode lengths between 16,384 and 2,097,151. */
+ else if( length < 2097152U )
+ {
+ encodedSize = 3U;
+ }
+ /* 4 bytes are needed to encode lengths between 2,097,152 and 268,435,455. */
+ else
+ {
+ encodedSize = 4U;
+ }
+
+ LogDebug( ( "Encoded size for length %lu is %lu bytes.",
+ ( unsigned long ) length,
+ ( unsigned long ) encodedSize ) );
+
+ return encodedSize;
+}
+
+/*-----------------------------------------------------------*/
+
+static uint8_t * encodeRemainingLength( uint8_t * pDestination,
+ size_t length )
+{
+ uint8_t lengthByte;
+ uint8_t * pLengthEnd = NULL;
+ size_t remainingLength = length;
+
+ assert( pDestination != NULL );
+
+ pLengthEnd = pDestination;
+
+ /* This algorithm is copied from the MQTT v3.1.1 spec. */
+ do
+ {
+ lengthByte = ( uint8_t ) ( remainingLength % 128U );
+ remainingLength = remainingLength / 128U;
+
+ /* Set the high bit of this byte, indicating that there's more data. */
+ if( remainingLength > 0U )
+ {
+ UINT8_SET_BIT( lengthByte, 7 );
+ }
+
+ /* Output a single encoded byte. */
+ *pLengthEnd = lengthByte;
+ pLengthEnd++;
+ } while( remainingLength > 0U );
+
+ return pLengthEnd;
+}
+
+/*-----------------------------------------------------------*/
+
+static uint8_t * encodeString( uint8_t * pDestination,
+ const char * pSource,
+ uint16_t sourceLength )
+{
+ uint8_t * pBuffer = NULL;
+
+ /* Typecast const char * typed source buffer to const uint8_t *.
+ * This is to use same type buffers in memcpy. */
+ const uint8_t * pSourceBuffer = ( const uint8_t * ) pSource;
+
+ assert( pDestination != NULL );
+
+ pBuffer = pDestination;
+
+ /* The first byte of a UTF-8 string is the high byte of the string length. */
+ *pBuffer = UINT16_HIGH_BYTE( sourceLength );
+ pBuffer++;
+
+ /* The second byte of a UTF-8 string is the low byte of the string length. */
+ *pBuffer = UINT16_LOW_BYTE( sourceLength );
+ pBuffer++;
+
+ /* Copy the string into pBuffer. */
+ if( pSourceBuffer != NULL )
+ {
+ ( void ) memcpy( pBuffer, pSourceBuffer, sourceLength );
+ }
+
+ /* Return the pointer to the end of the encoded string. */
+ pBuffer = &pBuffer[ sourceLength ];
+
+ return pBuffer;
+}
+
+/*-----------------------------------------------------------*/
+
+static bool calculatePublishPacketSize( const MQTTPublishInfo_t * pPublishInfo,
+ size_t * pRemainingLength,
+ size_t * pPacketSize )
+{
+ bool status = true;
+ size_t packetSize = 0, payloadLimit = 0;
+
+ assert( pPublishInfo != NULL );
+ assert( pRemainingLength != NULL );
+ assert( pPacketSize != NULL );
+
+ /* The variable header of a PUBLISH packet always contains the topic name.
+ * The first 2 bytes of UTF-8 string contains length of the string.
+ */
+ packetSize += pPublishInfo->topicNameLength + sizeof( uint16_t );
+
+ /* The variable header of a QoS 1 or 2 PUBLISH packet contains a 2-byte
+ * packet identifier. */
+ if( pPublishInfo->qos > MQTTQoS0 )
+ {
+ packetSize += sizeof( uint16_t );
+ }
+
+ /* Calculate the maximum allowed size of the payload for the given parameters.
+ * This calculation excludes the "Remaining length" encoding, whose size is not
+ * yet known. */
+ payloadLimit = MQTT_MAX_REMAINING_LENGTH - packetSize - 1U;
+
+ /* Ensure that the given payload fits within the calculated limit. */
+ if( pPublishInfo->payloadLength > payloadLimit )
+ {
+ LogError( ( "PUBLISH payload length of %lu cannot exceed "
+ "%lu so as not to exceed the maximum "
+ "remaining length of MQTT 3.1.1 packet( %lu ).",
+ ( unsigned long ) pPublishInfo->payloadLength,
+ ( unsigned long ) payloadLimit,
+ MQTT_MAX_REMAINING_LENGTH ) );
+ status = false;
+ }
+ else
+ {
+ /* Add the length of the PUBLISH payload. At this point, the "Remaining length"
+ * has been calculated. */
+ packetSize += pPublishInfo->payloadLength;
+
+ /* Now that the "Remaining length" is known, recalculate the payload limit
+ * based on the size of its encoding. */
+ payloadLimit -= remainingLengthEncodedSize( packetSize );
+
+ /* Check that the given payload fits within the size allowed by MQTT spec. */
+ if( pPublishInfo->payloadLength > payloadLimit )
+ {
+ LogError( ( "PUBLISH payload length of %lu cannot exceed "
+ "%lu so as not to exceed the maximum "
+ "remaining length of MQTT 3.1.1 packet( %lu ).",
+ ( unsigned long ) pPublishInfo->payloadLength,
+ ( unsigned long ) payloadLimit,
+ MQTT_MAX_REMAINING_LENGTH ) );
+ status = false;
+ }
+ else
+ {
+ /* Set the "Remaining length" output parameter and calculate the full
+ * size of the PUBLISH packet. */
+ *pRemainingLength = packetSize;
+
+ packetSize += 1U + remainingLengthEncodedSize( packetSize );
+ *pPacketSize = packetSize;
+ }
+ }
+
+ LogDebug( ( "PUBLISH packet remaining length=%lu and packet size=%lu.",
+ ( unsigned long ) *pRemainingLength,
+ ( unsigned long ) *pPacketSize ) );
+ return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_SerializePublishHeaderWithoutTopic( const MQTTPublishInfo_t * pPublishInfo,
+ size_t remainingLength,
+ uint8_t * pBuffer,
+ size_t * headerSize )
+{
+ size_t headerLength;
+ uint8_t * pIndex;
+ MQTTStatus_t status = MQTTSuccess;
+
+ /* The first byte of a PUBLISH packet contains the packet type and flags. */
+ uint8_t publishFlags = MQTT_PACKET_TYPE_PUBLISH;
+
+ /* Get the start address of the buffer. */
+ pIndex = pBuffer;
+
+ /* Length of serialized packet = First byte
* + Length of encoded remaining length
* + Encoded topic length. */
headerLength = 1U + remainingLengthEncodedSize( remainingLength ) + 2U;
@@ -772,6 +3091,10 @@ static void serializePublishCommon( const MQTTPublishInfo_t * pPublishInfo,
pIndex = &pIndex[ 2U ];
}
+ #if ( MQTT_VERSION_5_ENABLED )
+ pIndex = serializePublishProperties( pPublishInfo, pIndex );
+ #endif
+
/* The payload is placed after the packet identifier.
* Payload is copied over only if required by the flag serializePayload.
* This will help reduce an unnecessary copy of the payload into the buffer.
@@ -948,6 +3271,14 @@ static bool incomingPacketValid( uint8_t packetType )
break;
+ case MQTT_PACKET_TYPE_DISCONNECT:
+ #if ( MQTT_VERSION_5_ENABLED )
+ status = true;
+ #else
+ status = false;
+ #endif
+ break;
+
/* Any other packet type is invalid. */
default:
LogWarn( ( "Incoming packet invalid: Packet type=%u.",
@@ -1114,8 +3445,7 @@ static MQTTStatus_t deserializeConnack( const MQTTPacketInfo_t * pConnack,
{
/* Determine if the "Session Present" bit is set. This is the lowest bit of
* the third byte in CONNACK. */
- if( ( pRemainingData[ 0 ] & MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK )
- == MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK )
+ if( ( pRemainingData[ 0 ] & MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK ) == MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK )
{
LogDebug( ( "CONNACK session present bit set." ) );
*pSessionPresent = true;
@@ -1383,8 +3713,7 @@ static MQTTStatus_t validateSubscriptionSerializeParams( const MQTTSubscribeInfo
/* The serialized packet size = First byte
* + length of encoded size of remaining length
* + remaining length. */
- packetSize = 1U + remainingLengthEncodedSize( remainingLength )
- + remainingLength;
+ packetSize = 1U + remainingLengthEncodedSize( remainingLength ) + remainingLength;
if( packetSize > pFixedBuffer->size )
{
@@ -1572,7 +3901,11 @@ uint8_t * MQTT_SerializeConnectFixedHeader( uint8_t * pIndex,
pIndexLocal = encodeString( pIndexLocal, "MQTT", 4 );
/* The MQTT protocol version is the second field of the variable header. */
- *pIndexLocal = MQTT_VERSION_3_1_1;
+ #if ( MQTT_VERSION_5_ENABLED )
+ *pIndexLocal = MQTT_VERSION_5;
+ #else
+ *pIndexLocal = MQTT_VERSION_3_1_1;
+ #endif
pIndexLocal++;
/* Set the clean session flag if needed. */
@@ -2128,14 +4461,17 @@ MQTTStatus_t MQTT_SerializePublish( const MQTTPublishInfo_t * pPublishInfo,
pPublishInfo->pPayload ) );
status = MQTTBadParameter;
}
- else if( ( pPublishInfo->pTopicName == NULL ) || ( pPublishInfo->topicNameLength == 0U ) )
- {
- LogError( ( "Invalid topic name for PUBLISH: pTopicName=%p, "
- "topicNameLength=%hu.",
- ( void * ) pPublishInfo->pTopicName,
- ( unsigned short ) pPublishInfo->topicNameLength ) );
- status = MQTTBadParameter;
- }
+
+ #if ( !MQTT_VERSION_5_ENABLED )
+ else if( ( pPublishInfo->pTopicName == NULL ) || ( pPublishInfo->topicNameLength == 0U ) )
+ {
+ LogError( ( "Invalid topic name for PUBLISH: pTopicName=%p, "
+ "topicNameLength=%hu.",
+ ( void * ) pPublishInfo->pTopicName,
+ ( unsigned short ) pPublishInfo->topicNameLength ) );
+ status = MQTTBadParameter;
+ }
+ #endif
else if( ( pPublishInfo->qos != MQTTQoS0 ) && ( packetId == 0U ) )
{
LogError( ( "Packet ID is 0 for PUBLISH with QoS=%u.",
@@ -2152,8 +4488,7 @@ MQTTStatus_t MQTT_SerializePublish( const MQTTPublishInfo_t * pPublishInfo,
/* Length of serialized packet = First byte
* + Length of encoded remaining length
* + Remaining length. */
- packetSize = 1U + remainingLengthEncodedSize( remainingLength )
- + remainingLength;
+ packetSize = 1U + remainingLengthEncodedSize( remainingLength ) + remainingLength;
}
if( ( status == MQTTSuccess ) && ( packetSize > pFixedBuffer->size ) )
@@ -2231,31 +4566,247 @@ MQTTStatus_t MQTT_SerializePublishHeader( const MQTTPublishInfo_t * pPublishInfo
* + Remaining length
* - Payload Length.
*/
- packetSize = 1U + remainingLengthEncodedSize( remainingLength )
- + remainingLength
- - pPublishInfo->payloadLength;
+ packetSize = 1U + remainingLengthEncodedSize( remainingLength ) + remainingLength - pPublishInfo->payloadLength;
}
if( ( status == MQTTSuccess ) && ( packetSize > pFixedBuffer->size ) )
{
- LogError( ( "Buffer size of %lu is not sufficient to hold "
- "serialized PUBLISH header packet of size of %lu.",
- ( unsigned long ) pFixedBuffer->size,
- ( unsigned long ) ( packetSize - pPublishInfo->payloadLength ) ) );
- status = MQTTNoMemory;
+ LogError( ( "Buffer size of %lu is not sufficient to hold "
+ "serialized PUBLISH header packet of size of %lu.",
+ ( unsigned long ) pFixedBuffer->size,
+ ( unsigned long ) ( packetSize - pPublishInfo->payloadLength ) ) );
+ status = MQTTNoMemory;
+ }
+
+ if( status == MQTTSuccess )
+ {
+ /* Serialize publish without copying the payload. */
+ serializePublishCommon( pPublishInfo,
+ remainingLength,
+ packetId,
+ pFixedBuffer,
+ false );
+
+ /* Header size is the same as calculated packet size. */
+ *pHeaderSize = packetSize;
+ }
+
+ return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_SerializeAck( const MQTTFixedBuffer_t * pFixedBuffer,
+ uint8_t packetType,
+ uint16_t packetId )
+{
+ MQTTStatus_t status = MQTTSuccess;
+
+ if( pFixedBuffer == NULL )
+ {
+ LogError( ( "Provided buffer is NULL." ) );
+ status = MQTTBadParameter;
+ }
+ else if( pFixedBuffer->pBuffer == NULL )
+ {
+ LogError( ( "pFixedBuffer->pBuffer cannot be NULL." ) );
+ status = MQTTBadParameter;
+ }
+ /* The buffer must be able to fit 4 bytes for the packet. */
+ else if( pFixedBuffer->size < MQTT_PUBLISH_ACK_PACKET_SIZE )
+ {
+ LogError( ( "Insufficient memory for packet." ) );
+ status = MQTTNoMemory;
+ }
+ else if( packetId == 0U )
+ {
+ LogError( ( "Packet ID cannot be 0." ) );
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ switch( packetType )
+ {
+ /* Only publish acks are serialized by the client. */
+ case MQTT_PACKET_TYPE_PUBACK:
+ case MQTT_PACKET_TYPE_PUBREC:
+ case MQTT_PACKET_TYPE_PUBREL:
+ case MQTT_PACKET_TYPE_PUBCOMP:
+ pFixedBuffer->pBuffer[ 0 ] = packetType;
+ pFixedBuffer->pBuffer[ 1 ] = MQTT_PACKET_SIMPLE_ACK_REMAINING_LENGTH;
+ pFixedBuffer->pBuffer[ 2 ] = UINT16_HIGH_BYTE( packetId );
+ pFixedBuffer->pBuffer[ 3 ] = UINT16_LOW_BYTE( packetId );
+ break;
+
+ default:
+ LogError( ( "Packet type is not a publish ACK: Packet type=%02x",
+ ( unsigned int ) packetType ) );
+ status = MQTTBadParameter;
+ break;
+ }
+ }
+
+ return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_GetDisconnectPacketSize( size_t * pPacketSize )
+{
+ MQTTStatus_t status = MQTTSuccess;
+
+ if( pPacketSize == NULL )
+ {
+ LogError( ( "pPacketSize is NULL." ) );
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ /* MQTT DISCONNECT packets always have the same size. */
+ *pPacketSize = MQTT_DISCONNECT_PACKET_SIZE;
+ }
+
+ return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_SerializeDisconnect( const MQTTFixedBuffer_t * pFixedBuffer )
+{
+ MQTTStatus_t status = MQTTSuccess;
+
+ /* Validate arguments. */
+ if( pFixedBuffer == NULL )
+ {
+ LogError( ( "pFixedBuffer cannot be NULL." ) );
+ status = MQTTBadParameter;
+ }
+ else if( pFixedBuffer->pBuffer == NULL )
+ {
+ LogError( ( "pFixedBuffer->pBuffer cannot be NULL." ) );
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ /* Empty else MISRA 15.7 */
+ }
+
+ if( status == MQTTSuccess )
+ {
+ if( pFixedBuffer->size < MQTT_DISCONNECT_PACKET_SIZE )
+ {
+ LogError( ( "Buffer size of %lu is not sufficient to hold "
+ "serialized DISCONNECT packet of size of %lu.",
+ ( unsigned long ) pFixedBuffer->size,
+ MQTT_DISCONNECT_PACKET_SIZE ) );
+ status = MQTTNoMemory;
+ }
+ }
+
+ if( status == MQTTSuccess )
+ {
+ pFixedBuffer->pBuffer[ 0 ] = MQTT_PACKET_TYPE_DISCONNECT;
+ pFixedBuffer->pBuffer[ 1 ] = MQTT_DISCONNECT_REMAINING_LENGTH;
+ }
+
+ return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_GetPingreqPacketSize( size_t * pPacketSize )
+{
+ MQTTStatus_t status = MQTTSuccess;
+
+ if( pPacketSize == NULL )
+ {
+ LogError( ( "pPacketSize is NULL." ) );
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ /* MQTT PINGREQ packets always have the same size. */
+ *pPacketSize = MQTT_PACKET_PINGREQ_SIZE;
+ }
+
+ return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_SerializePingreq( const MQTTFixedBuffer_t * pFixedBuffer )
+{
+ MQTTStatus_t status = MQTTSuccess;
+
+ if( pFixedBuffer == NULL )
+ {
+ LogError( ( "pFixedBuffer is NULL." ) );
+ status = MQTTBadParameter;
+ }
+ else if( pFixedBuffer->pBuffer == NULL )
+ {
+ LogError( ( "pFixedBuffer->pBuffer cannot be NULL." ) );
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ /* Empty else MISRA 15.7 */
+ }
+
+ if( status == MQTTSuccess )
+ {
+ if( pFixedBuffer->size < MQTT_PACKET_PINGREQ_SIZE )
+ {
+ LogError( ( "Buffer size of %lu is not sufficient to hold "
+ "serialized PINGREQ packet of size of %lu.",
+ ( unsigned long ) pFixedBuffer->size,
+ MQTT_PACKET_PINGREQ_SIZE ) );
+ status = MQTTNoMemory;
+ }
+ }
+
+ if( status == MQTTSuccess )
+ {
+ /* Ping request packets are always the same. */
+ pFixedBuffer->pBuffer[ 0 ] = MQTT_PACKET_TYPE_PINGREQ;
+ pFixedBuffer->pBuffer[ 1 ] = 0x00;
+ }
+
+ return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_DeserializePublish( const MQTTPacketInfo_t * pIncomingPacket,
+ uint16_t * pPacketId,
+ MQTTPublishInfo_t * pPublishInfo )
+{
+ MQTTStatus_t status = MQTTSuccess;
+
+ if( ( pIncomingPacket == NULL ) || ( pPacketId == NULL ) || ( pPublishInfo == NULL ) )
+ {
+ LogError( ( "Argument cannot be NULL: pIncomingPacket=%p, "
+ "pPacketId=%p, pPublishInfo=%p",
+ ( void * ) pIncomingPacket,
+ ( void * ) pPacketId,
+ ( void * ) pPublishInfo ) );
+ status = MQTTBadParameter;
+ }
+ else if( ( pIncomingPacket->type & 0xF0U ) != MQTT_PACKET_TYPE_PUBLISH )
+ {
+ LogError( ( "Packet is not publish. Packet type: %02x.",
+ ( unsigned int ) pIncomingPacket->type ) );
+ status = MQTTBadParameter;
+ }
+ else if( pIncomingPacket->pRemainingData == NULL )
+ {
+ LogError( ( "Argument cannot be NULL: "
+ "pIncomingPacket->pRemainingData is NULL." ) );
+ status = MQTTBadParameter;
}
-
- if( status == MQTTSuccess )
+ else
{
- /* Serialize publish without copying the payload. */
- serializePublishCommon( pPublishInfo,
- remainingLength,
- packetId,
- pFixedBuffer,
- false );
-
- /* Header size is the same as calculated packet size. */
- *pHeaderSize = packetSize;
+ status = deserializePublish( pIncomingPacket, pPacketId, pPublishInfo );
}
return status;
@@ -2263,52 +4814,74 @@ MQTTStatus_t MQTT_SerializePublishHeader( const MQTTPublishInfo_t * pPublishInfo
/*-----------------------------------------------------------*/
-MQTTStatus_t MQTT_SerializeAck( const MQTTFixedBuffer_t * pFixedBuffer,
- uint8_t packetType,
- uint16_t packetId )
+MQTTStatus_t MQTT_DeserializeAck( const MQTTPacketInfo_t * pIncomingPacket,
+ uint16_t * pPacketId,
+ bool * pSessionPresent )
{
MQTTStatus_t status = MQTTSuccess;
- if( pFixedBuffer == NULL )
+ if( pIncomingPacket == NULL )
{
- LogError( ( "Provided buffer is NULL." ) );
+ LogError( ( "pIncomingPacket cannot be NULL." ) );
status = MQTTBadParameter;
}
- else if( pFixedBuffer->pBuffer == NULL )
+
+ /* Pointer for packet identifier cannot be NULL for packets other than
+ * CONNACK and PINGRESP. */
+ else if( ( pPacketId == NULL ) &&
+ ( ( pIncomingPacket->type != MQTT_PACKET_TYPE_CONNACK ) &&
+ ( pIncomingPacket->type != MQTT_PACKET_TYPE_PINGRESP ) ) )
{
- LogError( ( "pFixedBuffer->pBuffer cannot be NULL." ) );
+ LogError( ( "pPacketId cannot be NULL for packet type %02x.",
+ ( unsigned int ) pIncomingPacket->type ) );
status = MQTTBadParameter;
}
- /* The buffer must be able to fit 4 bytes for the packet. */
- else if( pFixedBuffer->size < MQTT_PUBLISH_ACK_PACKET_SIZE )
+ /* Pointer for session present cannot be NULL for CONNACK. */
+ else if( ( pSessionPresent == NULL ) &&
+ ( pIncomingPacket->type == MQTT_PACKET_TYPE_CONNACK ) )
{
- LogError( ( "Insufficient memory for packet." ) );
- status = MQTTNoMemory;
+ LogError( ( "pSessionPresent cannot be NULL for CONNACK packet." ) );
+ status = MQTTBadParameter;
}
- else if( packetId == 0U )
+
+ /* Pointer for remaining data cannot be NULL for packets other
+ * than PINGRESP. */
+ else if( ( pIncomingPacket->pRemainingData == NULL ) &&
+ ( pIncomingPacket->type != MQTT_PACKET_TYPE_PINGRESP ) )
{
- LogError( ( "Packet ID cannot be 0." ) );
+ LogError( ( "Remaining data of incoming packet is NULL." ) );
status = MQTTBadParameter;
}
else
{
- switch( packetType )
+ /* Make sure response packet is a valid ack. */
+ switch( pIncomingPacket->type )
{
- /* Only publish acks are serialized by the client. */
+ case MQTT_PACKET_TYPE_CONNACK:
+ status = deserializeConnack( pIncomingPacket, pSessionPresent );
+ break;
+
+ case MQTT_PACKET_TYPE_SUBACK:
+ status = deserializeSuback( pIncomingPacket, pPacketId );
+ break;
+
+ case MQTT_PACKET_TYPE_PINGRESP:
+ status = deserializePingresp( pIncomingPacket );
+ break;
+
+ case MQTT_PACKET_TYPE_UNSUBACK:
case MQTT_PACKET_TYPE_PUBACK:
case MQTT_PACKET_TYPE_PUBREC:
case MQTT_PACKET_TYPE_PUBREL:
case MQTT_PACKET_TYPE_PUBCOMP:
- pFixedBuffer->pBuffer[ 0 ] = packetType;
- pFixedBuffer->pBuffer[ 1 ] = MQTT_PACKET_SIMPLE_ACK_REMAINING_LENGTH;
- pFixedBuffer->pBuffer[ 2 ] = UINT16_HIGH_BYTE( packetId );
- pFixedBuffer->pBuffer[ 3 ] = UINT16_LOW_BYTE( packetId );
+ status = deserializeSimpleAck( pIncomingPacket, pPacketId );
break;
+ /* Any other packet type is invalid. */
default:
- LogError( ( "Packet type is not a publish ACK: Packet type=%02x",
- ( unsigned int ) packetType ) );
- status = MQTTBadParameter;
+ LogError( ( "IotMqtt_DeserializeResponse() called with unknown packet type:(%02x).",
+ ( unsigned int ) pIncomingPacket->type ) );
+ status = MQTTBadResponse;
break;
}
}
@@ -2318,19 +4891,64 @@ MQTTStatus_t MQTT_SerializeAck( const MQTTFixedBuffer_t * pFixedBuffer,
/*-----------------------------------------------------------*/
-MQTTStatus_t MQTT_GetDisconnectPacketSize( size_t * pPacketSize )
+MQTTStatus_t MQTT_GetIncomingPacketTypeAndLength( TransportRecv_t readFunc,
+ NetworkContext_t * pNetworkContext,
+ MQTTPacketInfo_t * pIncomingPacket )
{
MQTTStatus_t status = MQTTSuccess;
+ int32_t bytesReceived = 0;
- if( pPacketSize == NULL )
+ if( pIncomingPacket == NULL )
{
- LogError( ( "pPacketSize is NULL." ) );
+ LogError( ( "Invalid parameter: pIncomingPacket is NULL." ) );
status = MQTTBadParameter;
}
else
{
- /* MQTT DISCONNECT packets always have the same size. */
- *pPacketSize = MQTT_DISCONNECT_PACKET_SIZE;
+ /* Read a single byte. */
+ bytesReceived = readFunc( pNetworkContext,
+ &( pIncomingPacket->type ),
+ 1U );
+ }
+
+ if( bytesReceived == 1 )
+ {
+ /* Check validity. */
+ if( incomingPacketValid( pIncomingPacket->type ) == true )
+ {
+ pIncomingPacket->remainingLength = getRemainingLength( readFunc,
+ pNetworkContext );
+
+ if( pIncomingPacket->remainingLength == MQTT_REMAINING_LENGTH_INVALID )
+ {
+ LogError( ( "Incoming packet remaining length invalid." ) );
+ status = MQTTBadResponse;
+ }
+ }
+ else
+ {
+ LogError( ( "Incoming packet invalid: Packet type=%u.",
+ ( unsigned int ) pIncomingPacket->type ) );
+ status = MQTTBadResponse;
+ }
+ }
+ else if( ( status != MQTTBadParameter ) && ( bytesReceived == 0 ) )
+ {
+ status = MQTTNoDataAvailable;
+ }
+
+ /* If the input packet was valid, then any other number of bytes received is
+ * a failure. */
+ else if( status != MQTTBadParameter )
+ {
+ LogError( ( "A single byte was not read from the transport: "
+ "transportStatus=%ld.",
+ ( long int ) bytesReceived ) );
+ status = MQTTRecvFailed;
+ }
+ else
+ {
+ /* Empty else MISRA 15.7 */
}
return status;
@@ -2338,343 +4956,1118 @@ MQTTStatus_t MQTT_GetDisconnectPacketSize( size_t * pPacketSize )
/*-----------------------------------------------------------*/
-MQTTStatus_t MQTT_SerializeDisconnect( const MQTTFixedBuffer_t * pFixedBuffer )
+MQTTStatus_t MQTT_ProcessIncomingPacketTypeAndLength( const uint8_t * pBuffer,
+ const size_t * pIndex,
+ MQTTPacketInfo_t * pIncomingPacket )
{
MQTTStatus_t status = MQTTSuccess;
- /* Validate arguments. */
- if( pFixedBuffer == NULL )
+ if( pIncomingPacket == NULL )
{
- LogError( ( "pFixedBuffer cannot be NULL." ) );
+ LogError( ( "Invalid parameter: pIncomingPacket is NULL." ) );
status = MQTTBadParameter;
}
- else if( pFixedBuffer->pBuffer == NULL )
+ else if( pIndex == NULL )
{
- LogError( ( "pFixedBuffer->pBuffer cannot be NULL." ) );
+ LogError( ( "Invalid parameter: pIndex is NULL." ) );
+ status = MQTTBadParameter;
+ }
+ else if( pBuffer == NULL )
+ {
+ LogError( ( "Invalid parameter: pBuffer is NULL." ) );
status = MQTTBadParameter;
}
+ /* There should be at least one byte in the buffer */
+ else if( *pIndex < 1U )
+ {
+ /* No data is available. There are 0 bytes received from the network
+ * receive function. */
+ status = MQTTNoDataAvailable;
+ }
else
{
- /* Empty else MISRA 15.7 */
+ /* At least one byte is present which should be deciphered. */
+ pIncomingPacket->type = pBuffer[ 0 ];
}
if( status == MQTTSuccess )
{
- if( pFixedBuffer->size < MQTT_DISCONNECT_PACKET_SIZE )
+ /* Check validity. */
+ if( incomingPacketValid( pIncomingPacket->type ) == true )
{
- LogError( ( "Buffer size of %lu is not sufficient to hold "
- "serialized DISCONNECT packet of size of %lu.",
- ( unsigned long ) pFixedBuffer->size,
- MQTT_DISCONNECT_PACKET_SIZE ) );
- status = MQTTNoMemory;
+ status = processRemainingLength( pBuffer,
+ pIndex,
+ pIncomingPacket );
+ }
+ else
+ {
+ LogError( ( "Incoming packet invalid: Packet type=%u.",
+ ( unsigned int ) pIncomingPacket->type ) );
+ status = MQTTBadResponse;
+ }
+ }
+
+ return status;
+}
+
+#if ( MQTT_VERSION_5_ENABLED )
+
+ MQTTStatus_t MQTTV5_InitConnect( MQTTConnectProperties_t * pConnectProperties )
+ {
+ MQTTStatus_t status = MQTTSuccess;
+
+ if( pConnectProperties == NULL )
+ {
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ pConnectProperties->receiveMax = UINT16_MAX;
+ pConnectProperties->maxPacketSize = MQTT_MAX_PACKET_SIZE;
+ pConnectProperties->requestProblemInfo = true;
+ pConnectProperties->serverReceiveMax = UINT16_MAX;
+ pConnectProperties->serverMaxQos = 1U;
+ pConnectProperties->serverMaxPacketSize = MQTT_MAX_PACKET_SIZE;
+ pConnectProperties->isWildcardAvaiable = 1U;
+ pConnectProperties->subscriptionId = 1U;
+ pConnectProperties->isSharedAvailable = 1U;
+ }
+
+ return status;
+ }
+
+ MQTTStatus_t MQTTV5_GetConnectPacketSize( const MQTTConnectInfo_t * pConnectInfo,
+ MQTTPublishInfo_t * pWillInfo,
+ MQTTConnectProperties_t * pConnectProperties,
+ size_t * pRemainingLength,
+ size_t * pPacketSize )
+ {
+ MQTTStatus_t status = MQTTSuccess;
+ size_t remainingLength;
+
+ /* The CONNECT packet will always include a 10-byte variable header without connect properties. */
+ size_t connectPacketSize = MQTT_PACKET_CONNECT_HEADER_SIZE;
+
+ /* Validate arguments. */
+ if( ( pConnectInfo == NULL ) || ( pRemainingLength == NULL ) ||
+ ( pPacketSize == NULL ) )
+ {
+ LogError( ( "Argument cannot be NULL: pConnectInfo=%p, "
+ "pRemainingLength=%p, pPacketSize=%p.",
+ ( void * ) pConnectInfo,
+ ( void * ) pRemainingLength,
+ ( void * ) pPacketSize ) );
+ status = MQTTBadParameter;
+ }
+ else if( ( pConnectInfo->clientIdentifierLength == 0U ) || ( pConnectInfo->pClientIdentifier == NULL ) )
+ {
+ LogError( ( "Mqtt_GetConnectPacketSize() client identifier must be set." ) );
+ status = MQTTBadParameter;
+ }
+ else if( ( pWillInfo != NULL ) && ( pWillInfo->payloadLength > ( size_t ) UINT16_MAX ) )
+ {
+ /* The MQTTPublishInfo_t is reused for the will message. The payload
+ * length for any other message could be larger than 65,535, but
+ * the will message length is required to be represented in 2 bytes.*/
+ LogError( ( "The Will Message length must not exceed %d. "
+ "pWillInfo->payloadLength=%lu.",
+ UINT16_MAX,
+ ( unsigned long ) pWillInfo->payloadLength ) );
+ status = MQTTBadParameter;
+ }
+ else if( pConnectProperties == NULL )
+ {
+ LogError( ( "Argument cannot be NULL: connectProperties" ) );
+ status = MQTTBadParameter;
+ }
+ else if( ( pConnectProperties->pOutgoingAuth != NULL ) && ( pConnectProperties->pIncomingAuth == NULL ) )
+ {
+ LogError( ( "Incoming Auth cannot be NULL" ) );
+ status = MQTTBadParameter;
+ }
+
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ else if( pConnectProperties->pIncomingUserProperty == NULL )
+ {
+ LogError( ( "Incoming user property cannot be NULL" ) );
+ status = MQTTBadParameter;
+ }
+ #endif
+ else
+ {
+ /* Add the connect properties size. */
+ status = MQTT_GetConnectPropertiesSize( pConnectProperties );
+ }
+
+ if( status == MQTTSuccess )
+ {
+ connectPacketSize += pConnectProperties->propertyLength;
+ connectPacketSize += remainingLengthEncodedSize( pConnectProperties->propertyLength );
+ /* Add the length of the client identifier. */
+ connectPacketSize += pConnectInfo->clientIdentifierLength + sizeof( uint16_t );
+
+ /* Add the lengths of the will properties if provided. */
+ if( pWillInfo != NULL )
+ {
+ status = MQTT_GetPublishPropertiesSize( pWillInfo );
+ }
+ }
+
+ if( status == MQTTSuccess )
+ {
+ /* Add the lengths of the will message and topic name if provided. */
+ if( pWillInfo != NULL )
+ {
+ connectPacketSize += pWillInfo->propertyLength;
+ connectPacketSize += remainingLengthEncodedSize( pWillInfo->propertyLength );
+ connectPacketSize += pWillInfo->topicNameLength + sizeof( uint16_t ) +
+ pWillInfo->payloadLength + sizeof( uint16_t );
+ }
+
+ /* Add the lengths of the user name and password if provided. */
+ if( pConnectInfo->pUserName != NULL )
+ {
+ connectPacketSize += pConnectInfo->userNameLength + sizeof( uint16_t );
+ }
+
+ if( pConnectInfo->pPassword != NULL )
+ {
+ connectPacketSize += pConnectInfo->passwordLength + sizeof( uint16_t );
+ }
+
+ /* At this point, the "Remaining Length" field of the MQTT CONNECT packet has
+ * been calculated. */
+ remainingLength = connectPacketSize;
+
+ /* Calculate the full size of the MQTT CONNECT packet by adding the size of
+ * the "Remaining Length" field plus 1 byte for the "Packet Type" field. */
+ connectPacketSize += 1U + remainingLengthEncodedSize( connectPacketSize );
+
+ /**
+ * 268,435,455 - max remaining length according to spec MQTT-v5;
+ *
+ * */
+ if( remainingLength > MQTT_MAX_REMAINING_LENGTH )
+ {
+ status = MQTTBadParameter;
+ }
+ }
+
+ if( status == MQTTSuccess )
+ {
+ *pRemainingLength = remainingLength;
+ *pPacketSize = connectPacketSize;
+
+ LogDebug( ( "CONNECT packet remaining length=%lu and packet size=%lu.",
+ ( unsigned long ) *pRemainingLength,
+ ( unsigned long ) *pPacketSize ) );
}
+
+ return status;
}
- if( status == MQTTSuccess )
+ uint8_t * MQTT_SerializePublishProperties( const MQTTPublishInfo_t * pPublishInfo,
+ uint8_t * pIndex )
{
- pFixedBuffer->pBuffer[ 0 ] = MQTT_PACKET_TYPE_DISCONNECT;
- pFixedBuffer->pBuffer[ 1 ] = MQTT_DISCONNECT_REMAINING_LENGTH;
- }
+ uint8_t * pIndexLocal = pIndex;
- return status;
-}
+ pIndexLocal = encodeRemainingLength( pIndexLocal, pPublishInfo->propertyLength );
-/*-----------------------------------------------------------*/
+ /*Serialize the will delay if provided.*/
-MQTTStatus_t MQTT_GetPingreqPacketSize( size_t * pPacketSize )
-{
- MQTTStatus_t status = MQTTSuccess;
+ if( pPublishInfo->willDelay != 0U )
+ {
+ *pIndexLocal = MQTT_WILL_DELAY_ID;
+ pIndexLocal++;
+ pIndexLocal[ 0 ] = UINT32_BYTE3( pPublishInfo->willDelay );
+ pIndexLocal[ 1 ] = UINT32_BYTE2( pPublishInfo->willDelay );
+ pIndexLocal[ 2 ] = UINT32_BYTE1( pPublishInfo->willDelay );
+ pIndexLocal[ 3 ] = UINT32_BYTE0( pPublishInfo->willDelay );
+ pIndexLocal = &pIndexLocal[ 4 ];
+ }
- if( pPacketSize == NULL )
- {
- LogError( ( "pPacketSize is NULL." ) );
- status = MQTTBadParameter;
- }
- else
- {
- /* MQTT PINGREQ packets always have the same size. */
- *pPacketSize = MQTT_PACKET_PINGREQ_SIZE;
- }
+ /*Serialize the topic alias if provided*/
- return status;
-}
+ if( pPublishInfo->topicAlias != 0U )
+ {
+ *pIndexLocal = MQTT_TOPIC_ALIAS_ID;
+ pIndexLocal++;
+ pIndexLocal[ 0 ] = UINT16_HIGH_BYTE( pPublishInfo->topicAlias );
+ pIndexLocal[ 1 ] = UINT16_LOW_BYTE( pPublishInfo->topicAlias );
+ pIndexLocal = &pIndexLocal[ 2 ];
+ }
-/*-----------------------------------------------------------*/
+ /*Serialize the payload format if provided.*/
-MQTTStatus_t MQTT_SerializePingreq( const MQTTFixedBuffer_t * pFixedBuffer )
-{
- MQTTStatus_t status = MQTTSuccess;
+ if( pPublishInfo->payloadFormat != 0U )
+ {
+ *pIndexLocal = MQTT_PAYLOAD_FORMAT_ID;
+ pIndexLocal++;
+ *pIndexLocal = pPublishInfo->payloadFormat;
+ pIndexLocal++;
+ }
- if( pFixedBuffer == NULL )
- {
- LogError( ( "pFixedBuffer is NULL." ) );
- status = MQTTBadParameter;
- }
- else if( pFixedBuffer->pBuffer == NULL )
- {
- LogError( ( "pFixedBuffer->pBuffer cannot be NULL." ) );
- status = MQTTBadParameter;
- }
- else
- {
- /* Empty else MISRA 15.7 */
- }
+ /*Serialize the message expiry if provided.*/
- if( status == MQTTSuccess )
- {
- if( pFixedBuffer->size < MQTT_PACKET_PINGREQ_SIZE )
+ if( pPublishInfo->msgExpiryPresent != false )
{
- LogError( ( "Buffer size of %lu is not sufficient to hold "
- "serialized PINGREQ packet of size of %lu.",
- ( unsigned long ) pFixedBuffer->size,
- MQTT_PACKET_PINGREQ_SIZE ) );
- status = MQTTNoMemory;
+ *pIndexLocal = MQTT_MSG_EXPIRY_ID;
+ pIndexLocal++;
+ pIndexLocal[ 0 ] = UINT32_BYTE3( pPublishInfo->msgExpiryInterval );
+ pIndexLocal[ 1 ] = UINT32_BYTE2( pPublishInfo->msgExpiryInterval );
+ pIndexLocal[ 2 ] = UINT32_BYTE1( pPublishInfo->msgExpiryInterval );
+ pIndexLocal[ 3 ] = UINT32_BYTE0( pPublishInfo->msgExpiryInterval );
+ pIndexLocal = &pIndexLocal[ 4 ];
}
+
+ return pIndexLocal;
}
- if( status == MQTTSuccess )
+ uint8_t * MQTTV5_SerializeConnectProperties( uint8_t * pIndex,
+ const MQTTConnectProperties_t * pConnectProperties )
{
- /* Ping request packets are always the same. */
- pFixedBuffer->pBuffer[ 0 ] = MQTT_PACKET_TYPE_PINGREQ;
- pFixedBuffer->pBuffer[ 1 ] = 0x00;
- }
+ uint8_t * pIndexLocal = pIndex;
- return status;
-}
+ pIndexLocal = encodeRemainingLength( pIndexLocal, pConnectProperties->propertyLength );
-/*-----------------------------------------------------------*/
+ /*Serialize session expiry if provided.*/
+ if( pConnectProperties->sessionExpiry != 0U )
+ {
+ *pIndexLocal = MQTT_SESSION_EXPIRY_ID;
+ pIndexLocal++;
+ pIndexLocal[ 0 ] = UINT32_BYTE3( pConnectProperties->sessionExpiry );
+ pIndexLocal[ 1 ] = UINT32_BYTE2( pConnectProperties->sessionExpiry );
+ pIndexLocal[ 2 ] = UINT32_BYTE1( pConnectProperties->sessionExpiry );
+ pIndexLocal[ 3 ] = UINT32_BYTE0( pConnectProperties->sessionExpiry );
+ pIndexLocal = &pIndexLocal[ 4 ];
+ }
-MQTTStatus_t MQTT_DeserializePublish( const MQTTPacketInfo_t * pIncomingPacket,
- uint16_t * pPacketId,
- MQTTPublishInfo_t * pPublishInfo )
-{
- MQTTStatus_t status = MQTTSuccess;
+ /*Serialize receive max if provided.*/
- if( ( pIncomingPacket == NULL ) || ( pPacketId == NULL ) || ( pPublishInfo == NULL ) )
- {
- LogError( ( "Argument cannot be NULL: pIncomingPacket=%p, "
- "pPacketId=%p, pPublishInfo=%p",
- ( void * ) pIncomingPacket,
- ( void * ) pPacketId,
- ( void * ) pPublishInfo ) );
- status = MQTTBadParameter;
- }
- else if( ( pIncomingPacket->type & 0xF0U ) != MQTT_PACKET_TYPE_PUBLISH )
- {
- LogError( ( "Packet is not publish. Packet type: %02x.",
- ( unsigned int ) pIncomingPacket->type ) );
- status = MQTTBadParameter;
+ if( pConnectProperties->receiveMax != ( uint16_t ) UINT16_MAX )
+ {
+ *pIndexLocal = MQTT_RECEIVE_MAX_ID;
+ pIndexLocal++;
+ pIndexLocal[ 0 ] = UINT16_HIGH_BYTE( pConnectProperties->receiveMax );
+ pIndexLocal[ 1 ] = UINT16_LOW_BYTE( pConnectProperties->receiveMax );
+ pIndexLocal = &pIndexLocal[ 2 ];
+ }
+
+ /*Serialize the max packet size if provided.*/
+
+ if( pConnectProperties->maxPacketSize != MQTT_MAX_PACKET_SIZE )
+ {
+ *pIndexLocal = MQTT_MAX_PACKET_SIZE_ID;
+ pIndexLocal++;
+ pIndexLocal[ 0 ] = UINT32_BYTE3( pConnectProperties->maxPacketSize );
+ pIndexLocal[ 1 ] = UINT32_BYTE2( pConnectProperties->maxPacketSize );
+ pIndexLocal[ 2 ] = UINT32_BYTE1( pConnectProperties->maxPacketSize );
+ pIndexLocal[ 3 ] = UINT32_BYTE0( pConnectProperties->maxPacketSize );
+ pIndexLocal = &pIndexLocal[ 4 ];
+ }
+
+ /*Serialize the topic alias if provided.*/
+
+ if( pConnectProperties->topicAliasMax != 0U )
+ {
+ *pIndexLocal = MQTT_TOPIC_ALIAS_MAX_ID;
+ pIndexLocal++;
+ pIndexLocal[ 0 ] = UINT16_HIGH_BYTE( pConnectProperties->topicAliasMax );
+ pIndexLocal[ 1 ] = UINT16_LOW_BYTE( pConnectProperties->topicAliasMax );
+ pIndexLocal = &pIndexLocal[ 2 ];
+ }
+
+ /*Serialize the request response information if provided.*/
+
+ if( pConnectProperties->requestResponseInfo != false )
+ {
+ *pIndexLocal = MQTT_REQUEST_RESPONSE_ID;
+ pIndexLocal++;
+ *pIndexLocal = 1U;
+ pIndexLocal++;
+ }
+
+ /*Serialize request problem information if provided.*/
+
+ if( pConnectProperties->requestProblemInfo != true )
+ {
+ *pIndexLocal = MQTT_REQUEST_PROBLEM_ID;
+ pIndexLocal++;
+ *pIndexLocal = 0U;
+ pIndexLocal++;
+ }
+
+ return pIndexLocal;
}
- else if( pIncomingPacket->pRemainingData == NULL )
+
+ MQTTStatus_t MQTTV5_SerializeConnect( const MQTTConnectInfo_t * pConnectInfo,
+ const MQTTPublishInfo_t * pWillInfo,
+ const MQTTConnectProperties_t * pConnectProperties,
+ size_t remainingLength,
+ const MQTTFixedBuffer_t * pFixedBuffer )
{
- LogError( ( "Argument cannot be NULL: "
- "pIncomingPacket->pRemainingData is NULL." ) );
- status = MQTTBadParameter;
+ MQTTStatus_t status = MQTTSuccess;
+ size_t connectPacketSize = 0;
+
+ /* Validate arguments. */
+ if( ( pConnectInfo == NULL ) || ( pFixedBuffer == NULL ) )
+ {
+ LogError( ( "Argument cannot be NULL: pConnectInfo=%p, "
+ "pFixedBuffer=%p.",
+ ( void * ) pConnectInfo,
+ ( void * ) pFixedBuffer ) );
+ status = MQTTBadParameter;
+ }
+ /* A buffer must be configured for serialization. */
+ else if( pFixedBuffer->pBuffer == NULL )
+ {
+ LogError( ( "Argument cannot be NULL: pFixedBuffer->pBuffer is NULL." ) );
+ status = MQTTBadParameter;
+ }
+ else if( ( pWillInfo != NULL ) && ( pWillInfo->pTopicName == NULL ) )
+ {
+ LogError( ( "pWillInfo->pTopicName cannot be NULL if Will is present." ) );
+ status = MQTTBadParameter;
+ }
+
+ else if( pConnectProperties == NULL )
+ {
+ LogError( ( "Argument cannot be NULL: connectProperties" ) );
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ /* Calculate CONNECT packet size. Overflow in in this addition is not checked
+ * because it is part of the API contract to call Mqtt_GetConnectPacketSize()
+ * before this function. */
+ connectPacketSize = remainingLength + remainingLengthEncodedSize( remainingLength ) + 1U;
+
+ /* Check that the full packet size fits within the given buffer. */
+ if( connectPacketSize > pFixedBuffer->size )
+ {
+ LogError( ( "Buffer size of %lu is not sufficient to hold "
+ "serialized CONNECT packet of size of %lu.",
+ ( unsigned long ) pFixedBuffer->size,
+ ( unsigned long ) connectPacketSize ) );
+ status = MQTTNoMemory;
+ }
+ else
+ {
+ serializeConnectPacketV5( pConnectInfo,
+ pWillInfo,
+ pConnectProperties,
+ remainingLength,
+ pFixedBuffer );
+ }
+ }
+
+ return status;
}
- else
+
+ MQTTStatus_t MQTTV5_DeserializeConnack( MQTTConnectProperties_t * pConnackProperties,
+ const MQTTPacketInfo_t * pIncomingPacket,
+ bool * pSessionPresent )
{
- status = deserializePublish( pIncomingPacket, pPacketId, pPublishInfo );
- }
+ MQTTStatus_t status = MQTTSuccess;
+ size_t propertyLength;
+ size_t remainingLengthSize;
+ const uint8_t * pVariableHeader = NULL;
- return status;
-}
+ /*Validate the arguments*/
+ if( pConnackProperties == NULL )
+ {
+ status = MQTTBadParameter;
+ }
-/*-----------------------------------------------------------*/
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ else if( pConnackProperties->pIncomingUserProperty == NULL )
+ {
+ status = MQTTBadParameter;
+ }
+ #endif
+ else
+ {
+ status = validateConnackParams( pIncomingPacket, pSessionPresent );
+ }
-MQTTStatus_t MQTT_DeserializeAck( const MQTTPacketInfo_t * pIncomingPacket,
- uint16_t * pPacketId,
- bool * pSessionPresent )
-{
- MQTTStatus_t status = MQTTSuccess;
+ if( status == MQTTSuccess )
+ {
+ pVariableHeader = pIncomingPacket->pRemainingData;
+ pVariableHeader = &pVariableHeader[ 2 ];
+ remainingLengthSize = remainingLengthEncodedSize( pIncomingPacket->remainingLength );
+ status = decodeVariableLength( pVariableHeader, &propertyLength );
+ }
- if( pIncomingPacket == NULL )
- {
- LogError( ( "pIncomingPacket cannot be NULL." ) );
- status = MQTTBadParameter;
+ /*Validate the packet size if max packet size is set*/
+ if( status == MQTTSuccess )
+ {
+ if( ( pIncomingPacket->remainingLength + remainingLengthSize + 1U ) > ( pConnackProperties->maxPacketSize ) )
+ {
+ status = MQTTProtocolError;
+ }
+ /*Validate the remaining length*/
+ else if( ( pIncomingPacket->remainingLength ) != ( 2U + propertyLength + remainingLengthEncodedSize( propertyLength ) ) )
+ {
+ status = MQTTMalformedPacket;
+ }
+ /*Deserialize the connack properties.*/
+ else
+ {
+ status = deserializeConnackV5( pConnackProperties, propertyLength, &pVariableHeader );
+ }
+ }
+
+ return status;
}
- /* Pointer for packet identifier cannot be NULL for packets other than
- * CONNACK and PINGRESP. */
- else if( ( pPacketId == NULL ) &&
- ( ( pIncomingPacket->type != MQTT_PACKET_TYPE_CONNACK ) &&
- ( pIncomingPacket->type != MQTT_PACKET_TYPE_PINGRESP ) ) )
+ MQTTStatus_t MQTTV5_ValidatePublishParams( const MQTTPublishInfo_t * pPublishInfo,
+ uint16_t topicAliasMax,
+ uint8_t retainAvailable,
+ uint8_t maxQos )
{
- LogError( ( "pPacketId cannot be NULL for packet type %02x.",
- ( unsigned int ) pIncomingPacket->type ) );
- status = MQTTBadParameter;
+ MQTTStatus_t status;
+
+ if( pPublishInfo == NULL )
+ {
+ LogError( ( "Argument cannot be NULL: pPublishInfo=%p ",
+ ( void * ) pPublishInfo
+ ) );
+ status = MQTTBadParameter;
+ }
+ else if( pPublishInfo->topicAlias > topicAliasMax )
+ {
+ LogError( ( "Invalid topic alias." ) );
+ status = MQTTBadParameter;
+ }
+ else if( ( pPublishInfo->retain == true ) && ( retainAvailable == 0U ) )
+ {
+ LogError( ( "Retain is not available." ) );
+ status = MQTTBadParameter;
+ }
+ else if( ( pPublishInfo->qos != MQTTQoS0 ) && ( maxQos == 0U ) )
+ {
+ LogError( ( "Qos value = %hu is not allowed by the server ",
+ ( unsigned short ) pPublishInfo->topicNameLength ) );
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ status = MQTTSuccess;
+ }
+
+ return status;
}
- /* Pointer for session present cannot be NULL for CONNACK. */
- else if( ( pSessionPresent == NULL ) &&
- ( pIncomingPacket->type == MQTT_PACKET_TYPE_CONNACK ) )
+
+ MQTTStatus_t MQTTV5_GetPublishPacketSize( MQTTPublishInfo_t * pPublishInfo,
+ size_t * pRemainingLength,
+ size_t * pPacketSize,
+ uint32_t maxPacketSize )
{
- LogError( ( "pSessionPresent cannot be NULL for CONNACK packet." ) );
- status = MQTTBadParameter;
+ MQTTStatus_t status = MQTTSuccess;
+
+ if( ( pPublishInfo == NULL ) || ( pRemainingLength == NULL ) || ( pPacketSize == NULL ) )
+ {
+ LogError( ( "Argument cannot be NULL: pPublishInfo=%p, "
+ "pRemainingLength=%p, pPacketSize=%p.",
+ ( void * ) pPublishInfo,
+ ( void * ) pRemainingLength,
+ ( void * ) pPacketSize ) );
+ status = MQTTBadParameter;
+ }
+ else if( ( pPublishInfo->pTopicName == NULL ) && ( pPublishInfo->topicNameLength != 0U ) )
+ {
+ LogError( ( "Invalid topic name for PUBLISH: pTopicName=%p, "
+ "topicNameLength=%hu.",
+ ( void * ) pPublishInfo->pTopicName,
+ ( unsigned short ) pPublishInfo->topicNameLength ) );
+ status = MQTTBadParameter;
+ }
+ else if( ( pPublishInfo->topicAlias == 0U ) && ( pPublishInfo->topicNameLength == 0U ) )
+ {
+ LogError( ( "Invalid topic name for PUBLISH: pTopicName=%p, "
+ "topicNameLength=%hu.",
+ ( void * ) pPublishInfo->pTopicName,
+ ( unsigned short ) pPublishInfo->topicNameLength ) );
+ status = MQTTBadParameter;
+ }
+ else if( maxPacketSize == 0U )
+ {
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ status = calculatePublishPacketSizeV5( pPublishInfo, pRemainingLength, pPacketSize, maxPacketSize );
+ }
+
+ return status;
}
- /* Pointer for remaining data cannot be NULL for packets other
- * than PINGRESP. */
- else if( ( pIncomingPacket->pRemainingData == NULL ) &&
- ( pIncomingPacket->type != MQTT_PACKET_TYPE_PINGRESP ) )
+ MQTTStatus_t MQTTV5_DeserializeAck( const MQTTPacketInfo_t * pIncomingPacket,
+ uint16_t * pPacketId,
+ MQTTAckInfo_t * pAckInfo,
+ bool requestProblem,
+ uint32_t maxPacketSize )
{
- LogError( ( "Remaining data of incoming packet is NULL." ) );
- status = MQTTBadParameter;
+ MQTTStatus_t status = MQTTSuccess;
+
+ if( pIncomingPacket == NULL )
+ {
+ LogError( ( "pIncomingPacket cannot be NULL." ) );
+ status = MQTTBadParameter;
+ }
+
+ /* Pointer for packet identifier cannot be NULL for packets other than
+ * CONNACK and PINGRESP. */
+ else if( pPacketId == NULL )
+ {
+ LogError( ( "pPacketId cannot be NULL for packet type %02x.",
+ ( unsigned int ) pIncomingPacket->type ) );
+ status = MQTTBadParameter;
+ }
+
+ /* Pointer for remaining data cannot be NULL for packets other
+ * than PINGRESP. */
+ else if( pIncomingPacket->pRemainingData == NULL )
+ {
+ LogError( ( "Remaining data of incoming packet is NULL." ) );
+ status = MQTTBadParameter;
+ }
+ /*Max packet size cannot be 0.*/
+ else if( maxPacketSize == 0U )
+ {
+ status = MQTTBadParameter;
+ }
+ else if( ( pIncomingPacket->remainingLength + remainingLengthEncodedSize( pIncomingPacket->remainingLength ) + 1U ) > maxPacketSize )
+ {
+ status = MQTTProtocolError;
+ }
+ else
+ {
+ /* Make sure response packet is a valid ack. */
+ switch( pIncomingPacket->type )
+ {
+ case MQTT_PACKET_TYPE_PUBACK:
+ case MQTT_PACKET_TYPE_PUBREC:
+ status = deserializeSimpleAckV5( pIncomingPacket, pPacketId, pAckInfo, requestProblem );
+
+ if( status == MQTTSuccess )
+ {
+ status = logAckResponseV5( pAckInfo->reasonCode, *pPacketId );
+ }
+
+ break;
+
+ case MQTT_PACKET_TYPE_PUBREL:
+ case MQTT_PACKET_TYPE_PUBCOMP:
+ status = deserializeSimpleAckV5( pIncomingPacket, pPacketId, pAckInfo, requestProblem );
+
+ if( status == MQTTSuccess )
+ {
+ status = logSimpleAckResponseV5( pAckInfo->reasonCode, *pPacketId );
+ }
+
+ break;
+
+ /* Any other packet type is invalid. */
+ default:
+ LogError( ( "IotMqttv5_DeserializeResponse() called with unknown packet type:(%02x).",
+ ( unsigned int ) pIncomingPacket->type ) );
+ status = MQTTBadResponse;
+ break;
+ }
+ }
+
+ return status;
}
- else
+
+ MQTTStatus_t MQTTV5_GetAckPacketSize( MQTTAckInfo_t * pAckInfo,
+ size_t * pRemainingLength,
+ size_t * pPacketSize,
+ uint32_t maxPacketSize )
{
- /* Make sure response packet is a valid ack. */
- switch( pIncomingPacket->type )
+ MQTTStatus_t status = MQTTSuccess;
+ size_t length = 0U;
+ size_t propertyLength = 0U;
+ size_t packetSize = 0U;
+
+ /*Validate the parameters.*/
+ if( ( pAckInfo == NULL ) || ( pRemainingLength == NULL ) || ( pPacketSize == NULL ) )
{
- case MQTT_PACKET_TYPE_CONNACK:
- status = deserializeConnack( pIncomingPacket, pSessionPresent );
- break;
+ status = MQTTBadParameter;
+ }
+ else if( maxPacketSize == 0U )
+ {
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ length += MQTT_PUBLISH_ACK_PACKET_SIZE_WITH_REASON;
- case MQTT_PACKET_TYPE_SUBACK:
- status = deserializeSuback( pIncomingPacket, pPacketId );
- break;
+ if( pAckInfo->reasonStringLength != 0U )
+ {
+ if( pAckInfo->pReasonString == NULL )
+ {
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ propertyLength += pAckInfo->reasonStringLength;
+ propertyLength += MQTT_UTF8_LENGTH_SIZE;
+ }
+ }
+ }
- case MQTT_PACKET_TYPE_PINGRESP:
- status = deserializePingresp( pIncomingPacket );
- break;
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ if( ( status == MQTTSuccess ) && ( pAckInfo->pUserProperty != NULL ) )
+ {
+ status = MQTT_GetUserPropertySize( pAckInfo->pUserProperty->userProperty, pAckInfo->pUserProperty->count, &propertyLength );
+ }
+ #endif
- case MQTT_PACKET_TYPE_UNSUBACK:
- case MQTT_PACKET_TYPE_PUBACK:
- case MQTT_PACKET_TYPE_PUBREC:
- case MQTT_PACKET_TYPE_PUBREL:
- case MQTT_PACKET_TYPE_PUBCOMP:
- status = deserializeSimpleAck( pIncomingPacket, pPacketId );
- break;
+ if( status == MQTTSuccess )
+ {
+ /*Validate the length.*/
+ if( ( propertyLength + 4U ) < MQTT_MAX_REMAINING_LENGTH )
+ {
+ /*We have successfully calculated the property length.*/
+ pAckInfo->propertyLength = propertyLength;
+ length += remainingLengthEncodedSize( propertyLength ) + propertyLength;
+ *pRemainingLength = length;
+ }
+ else
+ {
+ status = MQTTBadParameter;
+ }
+ }
- /* Any other packet type is invalid. */
- default:
- LogError( ( "IotMqtt_DeserializeResponse() called with unknown packet type:(%02x).",
- ( unsigned int ) pIncomingPacket->type ) );
- status = MQTTBadResponse;
- break;
+ if( status == MQTTSuccess )
+ {
+ packetSize = length + 1U + remainingLengthEncodedSize( length );
+
+ if( packetSize > maxPacketSize )
+ {
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ *pPacketSize = packetSize;
+ }
}
- }
- return status;
-}
+ return status;
+ }
-/*-----------------------------------------------------------*/
+ uint8_t * MQTTV5_SerializeAckFixed( uint8_t * pIndex,
+ uint8_t packetType,
+ uint16_t packetId,
+ size_t remainingLength,
+ size_t propertyLength )
+ {
+ uint8_t * pIndexLocal = pIndex;
+
+ /* The first byte in the publish ack packet is the control packet type. */
+ *pIndexLocal = packetType;
+ pIndexLocal++;
+ /*After the packet type fixed header has remaining length.*/
+ pIndexLocal = encodeRemainingLength( pIndexLocal, remainingLength );
+ /*Encode the packet id.*/
+ pIndexLocal[ 0 ] = UINT16_HIGH_BYTE( packetId );
+ pIndexLocal[ 1 ] = UINT16_LOW_BYTE( packetId );
+ pIndexLocal = &pIndexLocal[ 2 ];
+ /*We are only sending the ack back if the reason code is success.*/
+ *pIndexLocal = MQTT_REASON_SUCCESS;
+ pIndexLocal++;
+ /*Encode the property length.*/
+ pIndexLocal = encodeRemainingLength( pIndexLocal, propertyLength );
+ return pIndexLocal;
+ }
+
+ MQTTStatus_t MQTTV5_SerializePubAckWithProperty( const MQTTAckInfo_t * pAckInfo,
+ size_t remainingLength,
+ const MQTTFixedBuffer_t * pFixedBuffer,
+ uint8_t packetType,
+ uint16_t packetId )
+ {
+ MQTTStatus_t status = MQTTSuccess;
+ size_t ackPacketSize = 0;
+
+ /* Validate arguments. */
+ if( ( pAckInfo == NULL ) || ( pFixedBuffer == NULL ) )
+ {
+ LogError( ( "Argument cannot be NULL: pAckInfo=%p, "
+ "pFixedBuffer=%p.",
+ ( void * ) pAckInfo,
+ ( void * ) pFixedBuffer ) );
+ status = MQTTBadParameter;
+ }
+ /* A buffer must be configured for serialization. */
+ else if( pFixedBuffer->pBuffer == NULL )
+ {
+ LogError( ( "Argument cannot be NULL: pFixedBuffer->pBuffer is NULL." ) );
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ ackPacketSize = remainingLength + remainingLengthEncodedSize( remainingLength ) + 1U;
-MQTTStatus_t MQTT_GetIncomingPacketTypeAndLength( TransportRecv_t readFunc,
- NetworkContext_t * pNetworkContext,
- MQTTPacketInfo_t * pIncomingPacket )
-{
- MQTTStatus_t status = MQTTSuccess;
- int32_t bytesReceived = 0;
+ /* Check that the full packet size fits within the given buffer. */
+ if( ackPacketSize > pFixedBuffer->size )
+ {
+ LogError( ( "Buffer size of %lu is not sufficient to hold "
+ "serialized ACK packet of size of %lu.",
+ ( unsigned long ) pFixedBuffer->size,
+ ( unsigned long ) ackPacketSize ) );
+ status = MQTTNoMemory;
+ }
+ else
+ {
+ serializePubAckPacketV5( pAckInfo, packetType, packetId, remainingLength, pFixedBuffer );
+ }
+ }
- if( pIncomingPacket == NULL )
- {
- LogError( ( "Invalid parameter: pIncomingPacket is NULL." ) );
- status = MQTTBadParameter;
- }
- else
- {
- /* Read a single byte. */
- bytesReceived = readFunc( pNetworkContext,
- &( pIncomingPacket->type ),
- 1U );
+ return status;
}
- if( bytesReceived == 1 )
+ MQTTStatus_t MQTTV5_GetDisconnectPacketSize( MQTTAckInfo_t * pDisconnectInfo,
+ size_t * pRemainingLength,
+ size_t * pPacketSize,
+ uint32_t maxPacketSize,
+ uint32_t sessionExpiry,
+ uint32_t prevSessionExpiry )
{
- /* Check validity. */
- if( incomingPacketValid( pIncomingPacket->type ) == true )
+ MQTTStatus_t status = MQTTSuccess;
+ size_t length = 0U;
+ size_t packetSize = 0U;
+ size_t propertyLength = 0U;
+
+ /*Validate the arguments.*/
+ if( ( pDisconnectInfo == NULL ) || ( pRemainingLength == NULL ) || ( pPacketSize == NULL ) )
{
- pIncomingPacket->remainingLength = getRemainingLength( readFunc,
- pNetworkContext );
+ LogError( ( "Argument cannot be NULL: pDisconnectInfo=%p, "
+ "pRemainingLength=%p, pPacketSize=%p.",
+ ( void * ) pDisconnectInfo,
+ ( void * ) pRemainingLength,
+ ( void * ) pPacketSize ) );
+ status = MQTTBadParameter;
+ }
+ else if( maxPacketSize == 0U )
+ {
+ LogError( ( "Max packet size cannot be zero." ) );
+ status = MQTTBadParameter;
+ }
+ /*Cannot overWrite a session expiry of 0.*/
+ else if( ( prevSessionExpiry == 0U ) && ( sessionExpiry != 0U ) )
+ {
+ LogError( ( "If the Session Expiry in the CONNECT packet was zero, then it is a Protocol Error to set a non-zero Session Expiry Interval in the DISCONNECT packet." ) );
+ status = MQTTBadParameter;
+ }
+ else if( validateDisconnectResponseV5( pDisconnectInfo->reasonCode, false ) != MQTTSuccess )
+ {
+ LogError( ( "Invalid reason code." ) );
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ /*Reason code.*/
+ length += 1U;
- if( pIncomingPacket->remainingLength == MQTT_REMAINING_LENGTH_INVALID )
+ /*Add session expiry if provided.*/
+ if( sessionExpiry != 0U )
{
- LogError( ( "Incoming packet remaining length invalid." ) );
- status = MQTTBadResponse;
+ propertyLength += MQTT_SESSION_EXPIRY_SIZE;
+ }
+
+ /*Validate the reason string if provided.*/
+ if( pDisconnectInfo->reasonStringLength != 0U )
+ {
+ if( pDisconnectInfo->pReasonString == NULL )
+ {
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ propertyLength += pDisconnectInfo->reasonStringLength;
+ propertyLength += MQTT_UTF8_LENGTH_SIZE;
+ }
}
}
- else
+
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ if( ( status == MQTTSuccess ) && ( pDisconnectInfo->pUserProperty != NULL ) )
+ {
+ status = MQTT_GetUserPropertySize( pDisconnectInfo->pUserProperty->userProperty, pDisconnectInfo->pUserProperty->count, &propertyLength );
+ }
+ #endif
+
+ if( status == MQTTSuccess )
{
- LogError( ( "Incoming packet invalid: Packet type=%u.",
- ( unsigned int ) pIncomingPacket->type ) );
- status = MQTTBadResponse;
+ /*Validate the length.*/
+ if( ( propertyLength + 4U ) < MQTT_MAX_REMAINING_LENGTH )
+ {
+ /*We have successfully calculated the property length.*/
+ pDisconnectInfo->propertyLength = propertyLength;
+ length += remainingLengthEncodedSize( propertyLength ) + propertyLength;
+ *pRemainingLength = length;
+ }
+ else
+ {
+ status = MQTTBadParameter;
+ }
}
- }
- else if( ( status != MQTTBadParameter ) && ( bytesReceived == 0 ) )
- {
- status = MQTTNoDataAvailable;
+
+ if( status == MQTTSuccess )
+ {
+ /*Packet size should be less than max allowed by the server.*/
+ packetSize = length + 1U + remainingLengthEncodedSize( length );
+
+ if( packetSize > maxPacketSize )
+ {
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ *pPacketSize = packetSize;
+ }
+ }
+
+ return status;
}
- /* If the input packet was valid, then any other number of bytes received is
- * a failure. */
- else if( status != MQTTBadParameter )
+ uint8_t * MQTTV5_SerializeDisconnectFixed( uint8_t * pIndex,
+ const MQTTAckInfo_t * pDisconnectInfo,
+ size_t remainingLength,
+ uint32_t sessionExpiry )
{
- LogError( ( "A single byte was not read from the transport: "
- "transportStatus=%ld.",
- ( long int ) bytesReceived ) );
- status = MQTTRecvFailed;
+ uint8_t * pIndexLocal = pIndex;
+
+ assert( pDisconnectInfo != NULL );
+ assert( pIndex != NULL );
+ /* The first byte in the publish ack packet is the control packet type. */
+ *pIndexLocal = MQTT_PACKET_TYPE_DISCONNECT;
+ pIndexLocal++;
+ /*After the packet type fixed header has remaining length.*/
+ pIndexLocal = encodeRemainingLength( pIndexLocal, remainingLength );
+ /*Encode the reason code.*/
+ *pIndexLocal = pDisconnectInfo->reasonCode;
+ pIndexLocal++;
+ /*Encode the property length.*/
+ pIndexLocal = encodeRemainingLength( pIndexLocal, pDisconnectInfo->propertyLength );
+
+ /*Encode the session expiry if provided. */
+ if( sessionExpiry != 0U )
+ {
+ *pIndexLocal = MQTT_SESSION_EXPIRY_ID;
+ pIndexLocal++;
+ pIndexLocal[ 0 ] = UINT32_BYTE3( sessionExpiry );
+ pIndexLocal[ 1 ] = UINT32_BYTE2( sessionExpiry );
+ pIndexLocal[ 2 ] = UINT32_BYTE1( sessionExpiry );
+ pIndexLocal[ 3 ] = UINT32_BYTE0( sessionExpiry );
+ pIndexLocal = &pIndexLocal[ 4 ];
+ }
+
+ return pIndexLocal;
}
- else
+
+ static void serializeDisconnectPacketV5( const MQTTAckInfo_t * pDisconnectInfo,
+ const MQTTFixedBuffer_t * pFixedBuffer,
+ size_t remainingLength,
+ uint32_t sessionExpiry )
{
- /* Empty else MISRA 15.7 */
- }
+ uint8_t * pIndex = NULL;
- return status;
-}
+ assert( pDisconnectInfo != NULL );
+ assert( pFixedBuffer != NULL );
+ assert( pFixedBuffer->pBuffer != NULL );
+ pIndex = pFixedBuffer->pBuffer;
+ /* Serialize the header including reason code and property length */
+ pIndex = MQTTV5_SerializeDisconnectFixed( pIndex, pDisconnectInfo,
+ remainingLength,
+ sessionExpiry );
-/*-----------------------------------------------------------*/
+ /*Serialize the reason string if provided.*/
+ if( pDisconnectInfo->reasonStringLength != 0U )
+ {
+ *pIndex = MQTT_REASON_STRING_ID;
+ pIndex++;
+ pIndex = encodeString( pIndex, pDisconnectInfo->pReasonString, pDisconnectInfo->reasonStringLength );
+ }
-MQTTStatus_t MQTT_ProcessIncomingPacketTypeAndLength( const uint8_t * pBuffer,
- const size_t * pIndex,
- MQTTPacketInfo_t * pIncomingPacket )
-{
- MQTTStatus_t status = MQTTSuccess;
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ /*Serialize the user properties if provided.*/
+ if( pDisconnectInfo->pUserProperty != NULL )
+ {
+ uint32_t i = 0;
+ uint32_t size = pDisconnectInfo->pUserProperty->count;
+ const MQTTUserProperty_t * pUserProperty = pDisconnectInfo->pUserProperty->userProperty;
+
+ for( ; i < size; i++ )
+ {
+ *pIndex = MQTT_USER_PROPERTY_ID;
+ pIndex++;
+ pIndex = encodeString( pIndex, pUserProperty[ i ].pKey, pUserProperty[ i ].keyLength );
+ pIndex = encodeString( pIndex, pUserProperty[ i ].pValue, pUserProperty[ i ].valueLength );
+ }
+ }
+ #endif /* if ( MQTT_USER_PROPERTY_ENABLED ) */
- if( pIncomingPacket == NULL )
- {
- LogError( ( "Invalid parameter: pIncomingPacket is NULL." ) );
- status = MQTTBadParameter;
- }
- else if( pIndex == NULL )
- {
- LogError( ( "Invalid parameter: pIndex is NULL." ) );
- status = MQTTBadParameter;
- }
- else if( pBuffer == NULL )
- {
- LogError( ( "Invalid parameter: pBuffer is NULL." ) );
- status = MQTTBadParameter;
- }
- /* There should be at least one byte in the buffer */
- else if( *pIndex < 1U )
- {
- /* No data is available. There are 0 bytes received from the network
- * receive function. */
- status = MQTTNoDataAvailable;
+ /* Ensure that the difference between the end and beginning of the buffer
+ * is less than the buffer size. */
+ assert( ( ( size_t ) ( pIndex - pFixedBuffer->pBuffer ) ) <= pFixedBuffer->size );
}
- else
+
+
+ MQTTStatus_t MQTTV5_SerializeDisconnectWithProperty( const MQTTAckInfo_t * pDisconnectInfo,
+ size_t remainingLength,
+ const MQTTFixedBuffer_t * pFixedBuffer,
+ uint32_t sessionExpiry )
{
- /* At least one byte is present which should be deciphered. */
- pIncomingPacket->type = pBuffer[ 0 ];
+ MQTTStatus_t status = MQTTSuccess;
+ size_t packetSize = 0;
+
+ /* Validate arguments. */
+ if( ( pDisconnectInfo == NULL ) || ( pFixedBuffer == NULL ) )
+ {
+ LogError( ( "Argument cannot be NULL: pDisconnectInfo=%p, "
+ "pFixedBuffer=%p.",
+ ( void * ) pDisconnectInfo,
+ ( void * ) pFixedBuffer ) );
+ status = MQTTBadParameter;
+ }
+ /* A buffer must be configured for serialization. */
+ else if( pFixedBuffer->pBuffer == NULL )
+ {
+ LogError( ( "Argument cannot be NULL: pFixedBuffer->pBuffer is NULL." ) );
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ packetSize = remainingLength + remainingLengthEncodedSize( remainingLength ) + 1U;
+
+ /* Check that the full packet size fits within the given buffer. */
+ if( packetSize > pFixedBuffer->size )
+ {
+ LogError( ( "Buffer size of %lu is not sufficient to hold "
+ "serialized ACK packet of size of %lu.",
+ ( unsigned long ) pFixedBuffer->size,
+ ( unsigned long ) packetSize ) );
+ status = MQTTNoMemory;
+ }
+ else
+ {
+ serializeDisconnectPacketV5( pDisconnectInfo, pFixedBuffer, remainingLength, sessionExpiry );
+ }
+ }
+
+ return status;
}
- if( status == MQTTSuccess )
+ MQTTStatus_t MQTTV5_DeserializeDisconnect( const MQTTPacketInfo_t * pPacket,
+ MQTTAckInfo_t * pDisconnectInfo,
+ const char ** pServerRef,
+ uint16_t * pServerRefLength,
+ uint32_t maxPacketSize )
{
- /* Check validity. */
- if( incomingPacketValid( pIncomingPacket->type ) == true )
+ MQTTStatus_t status = MQTTSuccess;
+ const uint8_t * pIndex = NULL;
+ size_t propertyLength = 0U;
+
+ /*Validate the arguments*/
+ if( ( pPacket == NULL ) || ( pPacket->pRemainingData == NULL ) )
{
- status = processRemainingLength( pBuffer,
- pIndex,
- pIncomingPacket );
+ status = MQTTBadParameter;
+ }
+ else if( ( pDisconnectInfo == NULL ) || ( pServerRef == NULL ) || ( pServerRefLength == NULL ) )
+ {
+ status = MQTTBadParameter;
+ }
+ else if( maxPacketSize == 0U )
+ {
+ status = MQTTBadParameter;
+ }
+ else if( pPacket->remainingLength == 0U )
+ {
+ status = MQTTMalformedPacket;
+ }
+ /*Packet size should not be more than the max allowed by the client.*/
+ else if( ( pPacket->remainingLength + remainingLengthEncodedSize( pPacket->remainingLength ) + 1U ) > maxPacketSize )
+ {
+ status = MQTTProtocolError;
}
+
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ else if( pDisconnectInfo->pUserProperty == NULL )
+ {
+ status = MQTTBadParameter;
+ }
+ #endif
else
{
- LogError( ( "Incoming packet invalid: Packet type=%u.",
- ( unsigned int ) pIncomingPacket->type ) );
- status = MQTTBadResponse;
+ /* Extract the reason code */
+ pIndex = pPacket->pRemainingData;
+ pDisconnectInfo->reasonCode = *pIndex;
+ pIndex++;
+ /*Validate the reason code.*/
+ status = validateDisconnectResponseV5( pDisconnectInfo->reasonCode, true );
}
- }
- return status;
-}
+ if( status == MQTTSuccess )
+ {
+ if( ( pPacket->remainingLength > 1U ) )
+ {
+ /*Extract the property length.*/
+ status = decodeVariableLength( pIndex, &propertyLength );
+
+ if( status == MQTTSuccess )
+ {
+ pIndex = &pIndex[ remainingLengthEncodedSize( propertyLength ) ];
+
+ /*Validate the remaining length.*/
+ if( pPacket->remainingLength != ( propertyLength + remainingLengthEncodedSize( propertyLength ) + 1U ) )
+ {
+ status = MQTTMalformedPacket;
+ }
+ }
+ }
+ }
+
+ if( status == MQTTSuccess )
+ {
+ while( ( propertyLength > 0U ) && ( status == MQTTSuccess ) )
+ {
+ /*Decode the property id.*/
+ uint8_t propertyId = *pIndex;
+ bool reasonString = false;
+ bool serverRef = false;
+ pIndex = &pIndex[ 1 ];
+ propertyLength -= sizeof( uint8_t );
+
+ /*Validate the property id and decode accordingly.*/
+ switch( propertyId )
+ {
+ case MQTT_REASON_STRING_ID:
+ status = decodeutf_8( &pDisconnectInfo->pReasonString, &pDisconnectInfo->reasonStringLength, &propertyLength, &reasonString, &pIndex );
+ break;
+
+ case MQTT_USER_PROPERTY_ID:
+ #if ( MQTT_USER_PROPERTY_ENABLED )
+ status = decodeutf_8pair( pDisconnectInfo->pUserProperty, &pDisconnectInfo->pUserProperty->count, &propertyLength, &pIndex );
+ #else
+ status = decodeAndDiscard( &propertyLength, &pIndex );
+ #endif
+ break;
+
+ case MQTT_SERVER_REF_ID:
+ status = decodeutf_8( pServerRef, pServerRefLength, &propertyLength, &serverRef, &pIndex );
+ break;
+
+ default:
+ status = MQTTProtocolError;
+ break;
+ }
+ }
+ }
+ return status;
+ }
+#endif /* if ( MQTT_VERSION_5_ENABLED ) */
/*-----------------------------------------------------------*/
diff --git a/source/include/core_mqtt.h b/source/include/core_mqtt.h
index 8e0bad49e..43c811b24 100644
--- a/source/include/core_mqtt.h
+++ b/source/include/core_mqtt.h
@@ -165,6 +165,7 @@ typedef struct MQTTPubAckInfo
MQTTPublishState_t publishState; /**< @brief The current state of the publish process. */
} MQTTPubAckInfo_t;
+
/**
* @ingroup mqtt_struct_types
* @brief A struct representing an MQTT connection.
@@ -246,6 +247,18 @@ typedef struct MQTTContext
uint16_t keepAliveIntervalSec; /**< @brief Keep Alive interval. */
uint32_t pingReqSendTimeMs; /**< @brief Timestamp of the last sent PINGREQ. */
bool waitingForPingResp; /**< @brief If the library is currently awaiting a PINGRESP. */
+ #if ( MQTT_VERSION_5_ENABLED )
+
+ /**
+ * @brief Connect and Connack Properties.
+ */
+ MQTTConnectProperties_t * pConnectProperties;
+
+ /**
+ * @brief To store disconnect information.
+ */
+ MQTTAckInfo_t * pDisconnectInfo;
+ #endif
} MQTTContext_t;
/**
@@ -258,6 +271,10 @@ typedef struct MQTTDeserializedInfo
uint16_t packetIdentifier; /**< @brief Packet ID of deserialized packet. */
MQTTPublishInfo_t * pPublishInfo; /**< @brief Pointer to deserialized publish info. */
MQTTStatus_t deserializationResult; /**< @brief Return code of deserialization. */
+ #if ( MQTT_VERSION_5_ENABLED )
+ MQTTAckInfo_t * pAckInfo; /**< @brief Pointer to deserialized ack info. */
+ MQTTAckInfo_t * pNextAckInfo; /**< @brief Pointer to next ack info to send. */
+ #endif
} MQTTDeserializedInfo_t;
/**
@@ -520,7 +537,7 @@ MQTTStatus_t MQTT_InitStatefulQoS( MQTTContext_t * pContext,
/* @[declare_mqtt_connect] */
MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext,
const MQTTConnectInfo_t * pConnectInfo,
- const MQTTPublishInfo_t * pWillInfo,
+ MQTTPublishInfo_t * pWillInfo,
uint32_t timeoutMs,
bool * pSessionPresent );
/* @[declare_mqtt_connect] */
@@ -625,7 +642,7 @@ MQTTStatus_t MQTT_Subscribe( MQTTContext_t * pContext,
*/
/* @[declare_mqtt_publish] */
MQTTStatus_t MQTT_Publish( MQTTContext_t * pContext,
- const MQTTPublishInfo_t * pPublishInfo,
+ MQTTPublishInfo_t * pPublishInfo,
uint16_t packetId );
/* @[declare_mqtt_publish] */
@@ -1017,6 +1034,24 @@ MQTTStatus_t MQTT_GetSubAckStatusCodes( const MQTTPacketInfo_t * pSubackPacket,
const char * MQTT_Status_strerror( MQTTStatus_t status );
/* @[declare_mqtt_status_strerror] */
+/**
+ * @brief Disconnect an MQTT session.
+ *
+ * @param[in] pContext Initialized and connected MQTT context.
+ * @param[in] pDisconnectInfo Reason code and properties to encode
+ * @param[in] sessionExpiry Session expiry interval.
+ * @return #MQTTNoMemory if the #MQTTContext_t.networkBuffer is too small to
+ * hold the MQTT packet;
+ * #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSendFailed if transport send failed;
+ * #MQTTSuccess otherwise.
+ */
+/* @[declare_mqttv5_disconnect] */
+MQTTStatus_t MQTTV5_Disconnect( MQTTContext_t * pContext,
+ MQTTAckInfo_t * pDisconnectInfo,
+ uint32_t sessionExpiry );
+/* @[declare_mqttv5_disconnect] */
+
/* *INDENT-OFF* */
#ifdef __cplusplus
}
diff --git a/source/include/core_mqtt_config_defaults.h b/source/include/core_mqtt_config_defaults.h
index 3fd79f974..3e7596520 100644
--- a/source/include/core_mqtt_config_defaults.h
+++ b/source/include/core_mqtt_config_defaults.h
@@ -68,6 +68,34 @@
#define MQTT_DO_NOT_USE_CUSTOM_CONFIG
#endif
+/**
+ * @ingroup mqtt_constants
+ * @brief Version of the MQTT protocol with default value as 3.1.1
+ */
+#ifndef MQTT_VERSION_5_ENABLED
+ #define MQTT_VERSION_5_ENABLED ( false )
+
+/**
+ * @ingroup mqtt_constants
+ * @brief Maximum number of user properties with default value as 5.
+ */
+ #ifndef MAX_USER_PROPERTY
+ #define MAX_USER_PROPERTY ( 5U )
+ #endif
+ /*Disable the user properties if max user property is set to 0.*/
+ #if ( MAX_USER_PROPERTY == 0U )
+
+/**
+ * @ingroup mqtt_constants
+ * @brief Enable and disable user properties.
+ */
+ #define MQTT_USER_PROPERTY_ENABLED ( false )
+ #else
+ #define MQTT_USER_PROPERTY_ENABLED ( true )
+ #endif
+
+#endif /* ifndef MQTT_VERSION_5_ENABLED */
+
/**
* @ingroup mqtt_constants
* @brief Maximum number of vectors in subscribe and unsubscribe packet.
diff --git a/source/include/core_mqtt_serializer.h b/source/include/core_mqtt_serializer.h
index 4837ee654..151ce76da 100644
--- a/source/include/core_mqtt_serializer.h
+++ b/source/include/core_mqtt_serializer.h
@@ -43,6 +43,7 @@
/* *INDENT-ON */
#include "transport_interface.h"
+#include "core_mqtt_config_defaults.h"
/* MQTT packet types. */
@@ -79,6 +80,10 @@ struct MQTTSubscribeInfo;
struct MQTTPublishInfo;
struct MQTTPacketInfo;
+struct MQTTConnectProperties;
+struct MQTTUserProperty;
+struct MQTTUserProperties;
+struct MQTTAuthInfo;
/**
* @ingroup mqtt_enum_types
* @brief Return codes from MQTT functions.
@@ -96,9 +101,13 @@ typedef enum MQTTStatus
MQTTIllegalState, /**< An illegal state in the state record. */
MQTTStateCollision, /**< A collision with an existing state record entry. */
MQTTKeepAliveTimeout, /**< Timeout while waiting for PINGRESP. */
- MQTTNeedMoreBytes /**< MQTT_ProcessLoop/MQTT_ReceiveLoop has received
+ MQTTNeedMoreBytes, /**< MQTT_ProcessLoop/MQTT_ReceiveLoop has received
incomplete data; it should be called again (probably after
a delay). */
+
+ MQTTMalformedPacket=0x81,/**A malformed packet was received from the server. */
+ MQTTProtocolError=0x82 /**A packet with protocol error was received from the server. */
+
} MQTTStatus_t;
/**
@@ -194,6 +203,230 @@ typedef struct MQTTSubscribeInfo
uint16_t topicFilterLength;
} MQTTSubscribeInfo_t;
+
+ /**
+ * @ingroup mqtt_struct_types
+ * @brief Struct to hold authentication method and authentication data.
+ */
+typedef struct MQTTAuthInfo
+{
+ /**
+ * @brief Authentication method used for authentication.
+ */
+ const char* pAuthMethod;
+ /**
+ * @brief Length of the authentication mathod.
+ */
+ uint16_t authMethodLength;
+ /**
+ * @brief Authentication data used for authentication.
+ */
+ const char* pAuthData;
+ /**
+ * @brief Length of the authentication data.
+ */
+ uint16_t authDataLength;
+} MQTTAuthInfo_t;
+
+#if(MQTT_USER_PROPERTY_ENABLED)
+ /**
+ * @ingroup mqtt_struct_types
+ * @brief Struct to hold user property.
+ */
+typedef struct MQTTUserProperty
+{
+ /**
+ * @brief key.
+ */
+ const char* pKey;
+ /**
+ * @brief Length of the key.
+ */
+ uint16_t keyLength;
+ /**
+ * @brief value.
+ */
+ const char* pValue;
+ /**
+ * @brief Length of the value.
+ */
+ uint16_t valueLength;
+} MQTTUserProperty_t;
+
+ /**
+ * @ingroup mqtt_struct_types
+ * @brief Struct to hold user property.
+ */
+typedef struct MQTTUserProperties
+{
+ /**
+ * @brief Array to store the user properties.
+ */
+ MQTTUserProperty_t userProperty[MAX_USER_PROPERTY];
+ /**
+ * @brief Number of user property;
+ */
+ uint32_t count;
+
+} MQTTUserProperties_t;
+
+#endif
+ /**
+ * @ingroup mqtt_struct_types
+ * @brief Struct to hold connect and connack properties.
+ */
+typedef struct MQTTConnectProperties
+{
+ /**
+ * @brief Four Byte Integer representing the Session Expiry Interval in seconds.
+ */
+ uint32_t sessionExpiry;
+ /**
+ * @brief Maximum number of unacknowledged PUBLISH packets client is willing to receive.
+ */
+ uint16_t receiveMax;
+ /**
+ * @brief Four Byte Integer representing the Maximum Packet Size the Client is willing to accept.
+ */
+ uint32_t maxPacketSize;
+ /**
+ * @brief Two Byte Integer representing the Topic Alias Maximum value.
+ */
+ uint16_t topicAliasMax;
+ /**
+ * @brief A value of 0 indicates that the Server MUST NOT return Response Information.
+ */
+ bool requestResponseInfo;
+ /**
+ * @brief The Client uses this value to indicate whether the Reason String or User Properties
+ * are sent in the case of failures
+ */
+ bool requestProblemInfo;
+ /**
+ * @brief Length of the connect properties.
+ */
+ size_t propertyLength;
+ /**
+ * @brief Pointer to the incoming authentication information.
+ */
+ MQTTAuthInfo_t *pOutgoingAuth;
+
+ /**
+ * @brief Maximum number of unacknowledged PUBLISH packets client is willing to receive.
+ */
+ uint16_t serverReceiveMax;
+ /**
+ * @brief Max qos supported by the server.
+ */
+ uint8_t serverMaxQos;
+ /**
+ * @brief Byte declares whether the Server supports retained messages.
+ */
+ uint8_t retainAvailable;
+ /**
+ * @brief Four Byte Integer representing the Maximum Packet Size the Server is willing to accept.
+ */
+ uint32_t serverMaxPacketSize;
+ /**
+ * @brief Client identifier assigned by the client.
+ */
+ const char* pClientIdentifier;
+ /**
+ * @brief Length of the assigned client identifier.
+ */
+ uint16_t clientIdLength;
+ /**
+ * @brief Two Byte Integer representing the Topic Alias Maximum value.
+ */
+ uint16_t serverTopicAliasMax;
+ /**
+ * @brief Reason String is a human readable string designed for diagnostics.
+ */
+ const char* pReasonString;
+ /**
+ * @brief Length of reason string.
+ */
+ uint16_t reasonStringLength;
+ /**
+ * @brief Whether wildcard subscription is available.
+ */
+ uint8_t isWildcardAvaiable;
+ /**
+ * @brief Whether the Server supports Subscription Identifiers.
+ */
+ uint8_t subscriptionId;
+ /**
+ * @brief Whether the Server supports Shared Subscription.
+ */
+ uint8_t isSharedAvailable;
+ /**
+ * @brief Keep Alive value given by the server.
+ */
+ uint16_t serverKeepAlive;
+ /**
+ * @brief UTF-8 Encoded String which is used as the basis for creating a Response Topic.
+ */
+ const char* pResponseInfo;
+ /**
+ * @brief Length of the response information.
+ */
+ uint16_t responseInfoLength;
+ /**
+ * @brief UTF-8 Encoded String which can be used by the Client to identify another Server to use
+ */
+ const char* pServerRef;
+ /**
+ * @brief Length of the server reference.
+ */
+ uint16_t serverRefLength;
+ /**
+ * @brief Pointer to the incoming authentication information.
+ */
+ MQTTAuthInfo_t *pIncomingAuth;
+ #if(MQTT_USER_PROPERTY_ENABLED)
+ /**
+ * @brief Pointer to the outgoing user properties.
+ */
+ MQTTUserProperties_t *pOutgoingUserProperty;
+ /**
+ * @brief Pointer to the incoming user properties.
+ */
+ MQTTUserProperties_t *pIncomingUserProperty;
+ #endif
+
+} MQTTConnectProperties_t;
+
+ /**
+ * @ingroup mqtt_struct_types
+ * @brief Struct to hold acknowledgment properties.
+ */
+typedef struct MQTTAckInfo
+{
+ /**
+ * @brief Response code;
+ */
+ uint8_t reasonCode;
+ /**
+ * @brief Property Length.
+ */
+ size_t propertyLength;
+ #if(MQTT_USER_PROPERTY_ENABLED)
+ /**
+ * @brief To store a key value pair.
+ */
+ MQTTUserProperties_t* pUserProperty;
+ #endif
+ /**
+ * @brief Reason String is a human readable string designed for diagnostics.
+ */
+ const char* pReasonString;
+ /**
+ * @brief Length of reason string.
+ */
+ uint16_t reasonStringLength;
+} MQTTAckInfo_t;
+
+
/**
* @ingroup mqtt_struct_types
* @brief MQTT PUBLISH packet parameters.
@@ -234,6 +467,64 @@ typedef struct MQTTPublishInfo
* @brief Message payload length.
*/
size_t payloadLength;
+
+#if (MQTT_VERSION_5_ENABLED)
+ /**
+ * @brief Length of the properties.
+ */
+ size_t propertyLength;
+ /**
+ * @brief Four Byte Integer representing the Will Delay Interval in seconds.
+ */
+ uint32_t willDelay;
+ /**
+ * @brief Payload Format Indicator.
+ **/
+ uint8_t payloadFormat;
+ /**
+ * @brief Topic alias value.
+ **/
+ uint16_t topicAlias;
+ /**
+ * @brief Four Byte Integer representing the Message Expiry Interval.
+ */
+ uint32_t msgExpiryInterval;
+ /**
+ * @brief Whether the message expiry is specified.
+ */
+ bool msgExpiryPresent;
+ /**
+ * @brief Length of the content type.
+ */
+ uint16_t contentTypeLength;
+ /**
+ * @brief UTF-8 Encoded String describing the content of the Will Message.
+ */
+ const char *pContentType;
+ /**
+ * @brief Length of the response topic.
+ */
+ uint16_t responseTopicLength;
+ /**
+ * @brief UTF-8 Encoded String which is used as the Topic Name for a response message.
+ */
+ const char *pResponseTopic;
+ /**
+ * @brief Length of the correlation data.
+ */
+ uint16_t correlationLength;
+ /**
+ * @brief To identify which request the Response Message is for.
+ */
+ const void *pCorrelationData;
+ #if(MQTT_USER_PROPERTY_ENABLED)
+ /**
+ * @brief Pointer to the user properties.
+ */
+ MQTTUserProperties_t* pUserProperty;
+ #endif
+#endif
+
} MQTTPublishInfo_t;
/**
@@ -675,7 +966,7 @@ MQTTStatus_t MQTT_GetPublishPacketSize( const MQTTPublishInfo_t * pPublishInfo,
* consider using #MQTT_SerializePublishHeader, which will serialize
* only the PUBLISH header into the buffer.
*
- * #MQTT_GetPublishPacketSize should be called with @p pPublishInfo before
+ * #MQTT_GetPublishPacketSize or #MQTTV5_GetPublishPacketSize (for version 5 users) should be called with @p pPublishInfo before
* invoking this function to get the size of the required #MQTTFixedBuffer_t and
* @p remainingLength. The @p remainingLength must be the same as returned by
* #MQTT_GetPublishPacketSize. The #MQTTFixedBuffer_t must be at least as large
@@ -712,6 +1003,7 @@ MQTTStatus_t MQTT_GetPublishPacketSize( const MQTTPublishInfo_t * pPublishInfo,
* status = MQTT_GetPublishPacketSize(
* &publishInfo, &remainingLength, &packetSize
* );
+ * // or call MQTTV5_GetPublishPacketSize
* assert( status == MQTTSuccess );
* assert( packetSize <= BUFFER_SIZE );
*
@@ -1285,6 +1577,718 @@ uint8_t * MQTT_SerializeUnsubscribeHeader( size_t remainingLength,
uint16_t packetId );
/** @endcond */
+/**
+ * @fn uint8_t * MQTTV5_SerializeConnectProperties(uint8_t * pIndex,const MQTTConnectProperties_t * pConnectProperties);
+ * @brief Serialize the connect properties of the connect packet header.
+ *
+ * @param[out] pIndex Pointer to the buffer where the header is to
+ * be serialized.
+ * @param[in] pConnectProperties The connect properties information.
+ *
+ * @return A pointer to the end of the encoded string.
+ */
+
+/**
+ * @cond DOXYGEN_IGNORE
+ * Doxygen should ignore this definition, this function is private.
+ */
+uint8_t * MQTTV5_SerializeConnectProperties( uint8_t * pIndex,
+ const MQTTConnectProperties_t * pConnectProperties);
+/** @endcond */
+
+
+/**
+ * @fn uint8_t * MQTT_SerializePublishProperties(const MQTTPublishInfo_t * pPublishInfo, uint8_t * pIndex);
+ * @brief Serialize the will properties of the connect packet.
+ * @param[in] pPublishInfo The publish/will properties information.
+ * @param[out] pIndex Pointer to the buffer where the header is to
+ * be serialized.
+ *
+ * @return A pointer to the end of the encoded string.
+ */
+
+/**
+ * @cond DOXYGEN_IGNORE
+ * Doxygen should ignore this definition, this function is private.
+ */
+uint8_t * MQTT_SerializePublishProperties( const MQTTPublishInfo_t * pPublishInfo,
+ uint8_t * pIndex);
+/** @endcond */
+
+
+/**
+ * @brief Deserialize an MQTT CONNACK packet.
+ *
+ * @param[out] pConnackProperties To store the deserialized connack properties.
+ * @param[in] pIncomingPacket #MQTTPacketInfo_t containing the buffer.
+ * @param[out] pSessionPresent Whether a previous session was present.
+ *
+ * @return #MQTTBadParameter, #MQTTBadResponse, #MQTTSuccess, #MQTTMalformedPacket, #MQTTProtocolError, #MQTTServerRefused
+ *
+ * Example
+ * @code{c}
+ *
+ * // TransportRecv_t function for reading from the network.
+ * int32_t socket_recv(
+ * NetworkContext_t * pNetworkContext,
+ * void * pBuffer,
+ * size_t bytesToRecv
+ * );
+ * // Some context to be used with the above transport receive function.
+ * NetworkContext_t networkContext;
+ *
+ * // Other variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTPacketInfo_t incomingPacket;
+ * MQTTConnectProperties_t properties = {0};
+ * uint16_t packetId;
+ *
+ * int32_t bytesRecvd;
+ * // A buffer to hold remaining data of the incoming packet.
+ * uint8_t buffer[ BUFFER_SIZE ];
+ *
+ * // Populate all fields of the incoming packet.
+ * status = MQTT_GetIncomingPacketTypeAndLength(
+ * socket_recv,
+ * &networkContext,
+ * &incomingPacket
+ * );
+ * assert( status == MQTTSuccess );
+ * assert( incomingPacket.remainingLength <= BUFFER_SIZE );
+ * bytesRecvd = socket_recv(
+ * &networkContext,
+ * ( void * ) buffer,
+ * incomingPacket.remainingLength
+ * );
+ * incomingPacket.pRemainingData = buffer;
+ *
+ * // Deserialize the publish information if the incoming packet is a publish.
+ * if( ( incomingPacket.type & 0xF0 ) == MQTT_PACKET_TYPE_CONNACK )
+ * {
+ * status = MQTT_DeserializeConnack(&properties, &incomingPacket, &session);
+ * if( status == MQTTSuccess )
+ * {
+ * // The deserialized connack information can now be used from `properties`.
+ * }
+ * }
+ * @endcode
+ */
+/* @[declare_mqttv5_deserializeconnack] */
+MQTTStatus_t MQTTV5_DeserializeConnack( MQTTConnectProperties_t *pConnackProperties,
+ const MQTTPacketInfo_t * pIncomingPacket,
+ bool * pSessionPresent );
+/* @[declare_mqttv5_deserializeconnack] */
+
+/**
+ * @brief Initialize an MQTTConnectProperties_t.
+ *
+ * This function can be called on an #MQTTConnectProperties_t to set the properties to their default value before calling MQTT_Connect or #MQTTV5_GetConnectPacketSize.
+ *
+ * @param[in] pConnectProperties The connect properties to initialize.
+ *
+ * @return #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSuccess otherwise.
+ *
+ * Example
+ * @code{c}
+ *
+ * MQTTConnectProperties_t connectProperties;
+ *
+ * // Clear context.
+ * memset( ( void * ) &connectProperties, 0x00, sizeof( MQTTConnectProperties_t ) );
+ *
+ * status = MQTTV5_InitConnect(&connectProperties);
+ *
+ * if( status == MQTTSuccess )
+ * {
+ * //Set the values for connect properties.
+ * }
+ * @endcode
+ */
+/* @[declare_mqttv5_initconnect] */
+MQTTStatus_t MQTTV5_InitConnect(MQTTConnectProperties_t *pConnectProperties);
+/* @[declare_mqttv5_initconnect] */
+
+/**
+ * @brief Get the size and Remaining Length of an MQTT Version 5 CONNECT packet .
+ *
+ * This function must be called before #MQTTV5_SerializeConnect in order to get
+ * the size of the MQTT CONNECT packet that is generated from #MQTTConnectInfo_t,
+ * MQTTConnectProperties_t and optional #MQTTPublishInfo_t. The size of the #MQTTFixedBuffer_t supplied
+ * to #MQTT_SerializeConnect must be at least @p pPacketSize. The provided
+ * @p pConnectInfo, @p pConnectProperties and @p pWillInfo are valid for serialization with
+ * #MQTT_SerializeConnect only if this function returns #MQTTSuccess. The
+ * remaining length returned in @p pRemainingLength and the packet size returned
+ * in @p pPacketSize are valid only if this function returns #MQTTSuccess.
+ *
+ * @param[in] pConnectInfo MQTT CONNECT packet parameters.
+ * @param[in] pWillInfo Last Will and Testament. Pass NULL if not used.
+ * @param[in] pConnectProperties MQTT CONNECT properties parameters.
+ * @param[out] pRemainingLength The Remaining Length of the MQTT CONNECT packet.
+ * @param[out] pPacketSize The total size of the MQTT CONNECT packet.
+ *
+ * @return #MQTTBadParameter if the packet would exceed the size allowed by the
+ * MQTT spec; #MQTTSuccess otherwise.
+ *
+ * Example
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTConnectInfo_t connectInfo = { 0 };
+ * MQTTPublishInfo_t willInfo = { 0 };
+ * MQTTConnectProperties_t connectProperties ={0};
+ * size_t remainingLength = 0, packetSize = 0;
+ *
+ * // Initialize the connection info, the details are out of scope for this example.
+ * initializeConnectInfo( &connectInfo );
+ *
+ * // Initialize the optional will info, the details are out of scope for this example.
+ * initializeWillInfo( &willInfo );
+ *
+ * // Initialize the connection properties, the details are out of scope for this example.
+ * initializeConnectProperties( &connectProperties );
+ *
+ * // Get the size requirement for the connect packet.
+ * status = MQTTV5_GetConnectPacketSize(
+ * &connectInfo, &willInfo, &connectProperties, &remainingLength, &packetSize
+ * );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ * // The application should allocate or use a static #MQTTFixedBuffer_t
+ * // of size >= packetSize to serialize the connect request.
+ * }
+ * @endcode
+ */
+/* @[declare_mqttv5_getconnectpacketsize] */
+MQTTStatus_t MQTTV5_GetConnectPacketSize(const MQTTConnectInfo_t* pConnectInfo,
+ MQTTPublishInfo_t* pWillInfo,
+ MQTTConnectProperties_t* pConnectProperties,
+ size_t* pRemainingLength,
+ size_t* pPacketSize);
+/* @[declare_mqttv5_getconnectpacketsize] */
+
+/**
+ * @brief Serialize an MQTT CONNECT packet in the given fixed buffer @p pFixedBuffer.
+ *
+ * #MQTTV5_GetConnectPacketSize should be called with @p pConnectInfo, @p pConnectProperties and
+ * @p pWillInfo before invoking this function to get the size of the required
+ * #MQTTFixedBuffer_t and @p remainingLength. The @p remainingLength must be
+ * the same as returned by #MQTTV5_GetConnectPacketSize. The #MQTTFixedBuffer_t
+ * must be at least as large as the size returned by #MQTTV5_GetConnectPacketSize.
+ *
+ * @param[in] pConnectInfo MQTT CONNECT packet parameters.
+ * @param[in] pWillInfo Last Will and Testament. Pass NULL if not used.
+ * @param[in] pConnectProperties MQTT CONNECT properties parameters.
+ * @param[in] remainingLength Remaining Length provided by #MQTTV5_GetConnectPacketSize.
+ * @param[out] pFixedBuffer Buffer for packet serialization.
+ *
+ * @return #MQTTNoMemory if pFixedBuffer is too small to hold the MQTT packet;
+ * #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSuccess otherwise.
+ *
+ * Example
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTConnectInfo_t connectInfo = { 0 };
+ * MQTTPublishInfo_t willInfo = { 0 };
+ * MQTTConnectProperties_t connectProperties ={0};
+ * MQTTFixedBuffer_t fixedBuffer;
+ * uint8_t buffer[ BUFFER_SIZE ];
+ * size_t remainingLength = 0, packetSize = 0;
+ *
+ * fixedBuffer.pBuffer = buffer;
+ * fixedBuffer.size = BUFFER_SIZE;
+ *
+ * // Assume connectInfo and willInfo are initialized. Get the size requirement for
+ * // the connect packet.
+ * status = MQTTV5_GetConnectPacketSize(
+ * &connectInfo, &willInfo,&connectProperties &remainingLength, &packetSize
+ * );
+ * assert( status == MQTTSuccess );
+ * assert( packetSize <= BUFFER_SIZE );
+ *
+ * // Serialize the connect packet into the fixed buffer.
+ * status = MQTTV5_SerializeConnect( &connectInfo, &willInfo,&connectProperties,remainingLength, &fixedBuffer );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ * // The connect packet can now be sent to the broker.
+ * }
+ * @endcode
+ */
+/* @[declare_mqttv5_serializeconnect] */
+MQTTStatus_t MQTTV5_SerializeConnect(const MQTTConnectInfo_t* pConnectInfo,
+ const MQTTPublishInfo_t* pWillInfo,
+ const MQTTConnectProperties_t *pConnectProperties,
+ size_t remainingLength,
+ const MQTTFixedBuffer_t* pFixedBuffer);
+/* @[declare_mqttv5_serializeconnect] */
+
+
+/**
+ * @brief Validate the publish parameters present in the given publish structure @p pPublishInfo.
+ *
+ * This function must be called before #MQTTV5_GetPublishPacketSize in order to validate the publish parameters.
+ *
+ * @param[in] pPublishInfo MQTT publish packet parameters.
+ * @param[in] topicAliasMax Maximum topic alias allowed by the server.
+ * @param[in] retainAvailable Whether server allows retain or not.
+ * @param[in] maxQos Maximum QoS supported by the server.
+ *
+ * @return #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSuccess otherwise.
+ *
+ * Example
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTPublishInfo_t publishInfo = {0};
+ * uint16_t topicAliasMax;
+ * uint8_t retainAvailable;
+ * uint8_t maxQos;
+ *
+ * //Set the publish info parameters.
+ *
+ * //Validate the publish packet
+ * status = MQTTV5_ValidatePublishParams(&publishInfo,topicAliasMax,retainAvailable,maxQos);
+ *
+ * if( status == MQTTSuccess )
+ * {
+ * // Get the packet size and serialize the publish packet.
+ * }
+ * @endcode
+ */
+/* @[declare_mqttv5_validatepublishparams] */
+MQTTStatus_t MQTTV5_ValidatePublishParams(const MQTTPublishInfo_t* pPublishInfo, uint16_t topicAliasMax, uint8_t retainAvailable, uint8_t maxQos);
+/* @[declare_mqttv5_validatepublishparams] */
+
+
+/**
+ * @brief Get the packet size and remaining length of an MQTT PUBLISH packet.
+ *
+ * #MQTTV5_ValidatePublishParams should be called with @p pPublishInfo before invoking this function
+ * to validate the publish parameters.This function must be called before #MQTT_SerializePublish
+ * in order to get the size of the MQTT PUBLISH packet that is generated from #MQTTPublishInfo_t.
+ * The size of the #MQTTFixedBuffer_t supplied to #MQTT_SerializePublish must be
+ * at least @p pPacketSize. The provided @p pPublishInfo is valid for
+ * serialization with #MQTT_SerializePublish only if this function returns
+ * #MQTTSuccess. The remaining length returned in @p pRemainingLength and the
+ * packet size returned in @p pPacketSize are valid only if this function
+ * returns #MQTTSuccess.
+ *
+ * @param[in] pPublishInfo MQTT PUBLISH packet parameters.
+ * @param[out] pRemainingLength The Remaining Length of the MQTT PUBLISH packet.
+ * @param[out] pPacketSize The total size of the MQTT PUBLISH packet.
+ * @param[in] maxPacketSize Maximum packet size allowed by the server.
+ *
+ * @return #MQTTBadParameter if the packet would exceed the size allowed by the
+ * MQTT spec or if invalid parameters are passed; #MQTTSuccess otherwise.
+ *
+ * Example
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTPublishInfo_t publishInfo = { 0 };
+ * uint16_t topicAliasMax;
+ * uint8_t retainAvailable;
+ * uint8_t maxQos;
+ * size_t remainingLength = 0, packetSize = 0;
+ *
+ * // Initialize the publish info.
+ * publishInfo.qos = MQTTQoS0;
+ * publishInfo.pTopicName = "/some/topic/name";
+ * publishInfo.topicNameLength = strlen( publishInfo.pTopicName );
+ * publishInfo.pPayload = "Hello World!";
+ * publishInfo.payloadLength = strlen( "Hello World!" );
+ *
+ * //Set the publish properties.
+ *
+ * status = MQTTV5_ValidatePublishParams(&publishInfo,topicAliasMax,retainAvailable,maxQos);
+
+ * // Get the size requirement for the publish packet.
+ * status = MQTTV5_GetPublishPacketSize(
+ * &publishInfo, &remainingLength, &packetSize, maxPacketSize
+ * );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ * // The application should allocate or use a static #MQTTFixedBuffer_t
+ * // of size >= packetSize to serialize the publish.
+ * }
+ * @endcode
+ */
+/* @[declare_mqttv5_getpublishpacketsize] */
+MQTTStatus_t MQTTV5_GetPublishPacketSize(MQTTPublishInfo_t * pPublishInfo,
+ size_t * pRemainingLength,
+ size_t * pPacketSize ,
+ uint32_t maxPacketSize);
+/* @[declare_mqttv5_getpublishpacketsize] */
+
+
+/**
+ * @brief Deserialize an MQTT PUBACK, PUBREC, PUBREL, PUBCOMP, or PINGRESP.
+ *
+ * @param[in] pIncomingPacket #MQTTPacketInfo_t containing the buffer.
+ * @param[out] pPacketId The packet ID of obtained from the buffer.
+ * @param[out] pAckInfo Struct to store the deserialized ack information.
+ * @param[in] requestProblem Request problem value set in the connect packet.
+ * @param[in] maxPacketSize Maximum packet size allowed by the client.
+ *
+ * @return #MQTTBadParameter, #MQTTBadResponse, #MQTTServerRefused, #MQTTProtocolError, #MQTTMalformedPacket or #MQTTSuccess.
+ *
+ * Example
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTPacketInfo_t incomingPacket;
+ * uint16_t packetId;
+ * MQTTAckInfo_t ackInfo = {0};
+ * bool requestProblem;
+ * uint32_t maxPacketSize;
+ * bool sessionPresent;
+ *
+ * // Receive an incoming packet and populate all fields. The details are out of scope
+ * // for this example.
+ * receiveIncomingPacket( &incomingPacket );
+ *
+ * // Deserialize ack information if the incoming packet is a publish ack.
+ * status = MQTTV5_DeserializeAck(&incomingPacket,
+ &packetId,
+ &ackInfo,
+ requestProblem,
+ maxPacketSize);
+ * if( status == MQTTSuccess )
+ * {
+ * // Ack information is now available.
+ * }
+ * }
+ * @endcode
+ */
+/* @[declare_mqttv5_deserializeack] */
+MQTTStatus_t MQTTV5_DeserializeAck( const MQTTPacketInfo_t * pIncomingPacket,
+ uint16_t * pPacketId, MQTTAckInfo_t *pAckInfo, bool requestProblem,uint32_t maxPacketSize);
+/* @[declare_mqttv5_deserializeack] */
+
+/**
+ * @brief Get the size of an MQTT DISCONNECT packet.
+ *
+ * @param[in] pAckInfo MQTT PUBLISH ACK packet parameters.
+ * @param[out] pRemainingLength The Remaining Length of the MQTT DISCONNECT packet.
+ * @param[out] pPacketSize The size of the MQTT DISCONNECT packet.
+ * @param[in] maxPacketSize Maximum packet size allowed by the server.
+ *
+ * @return #MQTTSuccess, or #MQTTBadParameter if parameters are invalid
+ *
+ * Example
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTAckInfo_t disconnectInfo ={0};
+ * size_t remainingLength =0;
+ * size_t packetSize = 0;
+ * uint32_t maxPacketSize;
+ *
+ * //Set the parameters.
+ * // Get the size requirement for the disconnect packet.
+ * status = MQTTV5_GetAckPacketSize(&disconnectInfo,&remainingLength,&packetSize,maxPacketSize);
+ *
+ * if( status == MQTTSuccess )
+ * {
+ * // The application should allocate or use a static #MQTTFixedBuffer_t
+ * // of size >= packetSize to serialize the ack packet.
+ * }
+ * @endcode
+ */
+/* @[declare_mqttv5_getackpacketsize] */
+MQTTStatus_t MQTTV5_GetAckPacketSize( MQTTAckInfo_t *pAckInfo, size_t* pRemainingLength,size_t * pPacketSize, uint32_t maxPacketSize);
+/* @[declare_mqttv5_getackpacketsize] */
+
+/**
+ * @fn uint8_t * MQTTV5_SerializeAckFixed(uint8_t * pIndex,
+ uint8_t packetType,
+ uint16_t packetId,
+ size_t remainingLength,
+ size_t propertyLength);
+ * @brief Serialize the fixed size part of the ack packet header.
+ *
+ * @param[out] pIndex Pointer to the buffer where the header is to
+ * be serialized.
+ * @param[in] packetType Type of publish ack
+ * @param[in] packetId Packed identifier of the ack packet.
+ * @param[in] remainingLength Remaining length of the ack packet.
+ * @param[in] propertyLength Property length of the ack packet.
+ *
+ *
+ * @return A pointer to the end of the encoded string.
+ */
+
+/**
+ * @cond DOXYGEN_IGNORE
+ * Doxygen should ignore this definition, this function is private.
+ */
+uint8_t * MQTTV5_SerializeAckFixed(uint8_t * pIndex,
+ uint8_t packetType,
+ uint16_t packetId,
+ size_t remainingLength,
+ size_t propertyLength);
+/** @endcond */
+
+/**
+ * @brief Serialize an MQTT DISCONNECT packet into the given buffer.
+ *
+ * The input #MQTTFixedBuffer_t.size must be at least as large as the size
+ * returned by #MQTTV5_GetAckPacketSize.
+ *
+ * @note If reason code is success and property length is zero then #MQTT_SerializeAck can also be used.
+ *
+ * @param[in] pAckInfo Struct containing information about the publish ack.
+ * @param[in] remainingLength The remaining length of the packet to be
+ * serialized.
+ * @param[out] pFixedBuffer Buffer for packet serialization.
+ * @param[in] packetType Type of publish ack.
+ * @param[in] packetId Packed identifier of the ack packet.
+ *
+ * @return #MQTTNoMemory if pFixedBuffer is too small to hold the MQTT packet;
+ * #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSuccess otherwise.
+ *
+ * Example
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTFixedBuffer_t fixedBuffer;
+ * uint8_t buffer[ BUFFER_SIZE ];
+ * MQTTAckInfo_t ackInfo;
+ * uint16_t sessionExpiry;
+ *
+ * fixedBuffer.pBuffer = buffer;
+ * fixedBuffer.size = BUFFER_SIZE;
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTAckInfo_t disconnectInfo ={0};
+ * size_t remainingLength =0;
+ * size_t packetSize = 0;
+ * uint8_t packetType;
+ * uint8_t packedId;
+ * uint32_t maxPacketSize;
+ * //set the parameters.
+ * // Get the size requirement for the ack packet.
+ * status = MQTTV5_GetAckPacketSize(&disconnectInfo,&remainingLength,&packetSize,maxPacketSize);
+ *
+ *
+ * assert( status == MQTTSuccess );
+ * assert( packetSize <= BUFFER_SIZE );
+ *
+ * // Serialize the disconnect into the fixed buffer.
+ * MQTTV5_SerializePubAckWithProperty( &ackInfo,
+ remainingLength,
+ &fixedBuffer,
+ packetType,
+ packetId);
+ * * if( status == MQTTSuccess )
+ * {
+ * // The ack packet can now be sent to the broker.
+ * }
+ *
+ * @endcode
+ */
+/* @[declare_mqttv5_serializepubackwithproperty] */
+MQTTStatus_t MQTTV5_SerializePubAckWithProperty( const MQTTAckInfo_t *pAckInfo,
+ size_t remainingLength,
+ const MQTTFixedBuffer_t * pFixedBuffer,
+ uint8_t packetType,
+ uint16_t packetId);
+/* @[declare_mqttv5_serializepubackwithproperty] */
+
+
+/**
+ * @brief Get the size of an MQTT DISCONNECT packet.
+ *
+ * @param[in] pDisconnectInfo MQTT DISCONNECT packet parameters.
+ * @param[out] pRemainingLength The Remaining Length of the MQTT DISCONNECT packet.
+ * @param[out] pPacketSize The size of the MQTT DISCONNECT packet.
+ * @param[in] maxPacketSize Maximum packet size allowed by the server.
+ * @param[in] sessionExpiry Session expiry interval in the disconnect packet.
+ * @param[in] prevSessionExpiry Session expiry interval set in the connect packet.
+ *
+ * @return #MQTTSuccess, or #MQTTBadParameter if parameters are invalid
+ *
+ * Example
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTAckInfo_t disconnectInfo ={0};
+ * size_t remainingLength =0;
+ * size_t packetSize = 0;
+ * uint32_t maxPacketSize;
+ * uint32_t sessionExpiry;
+ * uint32_t prevSessionExpiry
+ *
+ * //Set the parameters.
+ * // Get the size requirement for the disconnect packet.
+ * status = MQTTV5_GetDisconnectPacketSize(&disconnectInfo,&remainingLength,&packetSize,maxPacketSize, sessionExpiry,prevSessionExpiry );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ * // The application should allocate or use a static #MQTTFixedBuffer_t
+ * // of size >= packetSize to serialize the disconnect.
+ * }
+ * @endcode
+ */
+/* @[declare_mqttv5_getdisconnectpacketsize] */
+MQTTStatus_t MQTTV5_GetDisconnectPacketSize( MQTTAckInfo_t* pDisconnectInfo, size_t * pRemainingLength, size_t * pPacketSize,uint32_t maxPacketSize, uint32_t sessionExpiry,uint32_t prevSessionExpiry );
+/* @[declare_mqttv5_getdisconnectpacketsize] */
+
+
+/**
+ * @fn uint8_t * MQTTV5_SerializeDisconnectFixed(uint8_t * pIndex,
+ const MQTTAckInfo_t * pDisconnectInfo,
+ size_t remainingLength,
+ uint32_t sessionExpiry);
+ * @brief Serialize the fixed part of the disconnect packet header.
+ *
+ * @param[out] pIndex Pointer to the buffer where the fixed size parameters is to
+ * be serialized.
+ * @param[in] pDisconnectInfo The disconnect information.
+ * @param[in] remainingLength The remaining length of the packet to be
+ * serialized.
+ * @param[in] sessionExpiry Session expiry to be serialized.
+ *
+ * @return A pointer to the end of the encoded string.
+ */
+
+/**
+ * @cond DOXYGEN_IGNORE
+ * Doxygen should ignore this definition, this function is private.
+ */
+uint8_t * MQTTV5_SerializeDisconnectFixed(uint8_t * pIndex,
+ const MQTTAckInfo_t * pDisconnectInfo,
+ size_t remainingLength,
+ uint32_t sessionExpiry);
+/** @endcond */
+
+/**
+ * @brief Deserialize an MQTT Disconnect packet.
+ *
+ * @param[in] pPacket #MQTTPacketInfo_t containing the buffer.
+ * @param[out] pDisconnectInfo Struct containing information about the disconnect.
+ * @param[out] pServerRef The server reference obtained from the buffer.
+ * @param[out] pServerRefLength The server reference length obtained from the buffer.
+ * @param[in] maxPacketSize MAximum packet size allowed by the client.
+ *
+ * @return #MQTTBadParameter, #MQTTBadResponse, #MQTTServerRefused, #MQTTProtocolError, #MQTTMalformedPacket or #MQTTSuccess.
+ *
+ * Example
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTPacketInfo_t incomingPacket;
+ * MQTTAckInfo_t disconnectInfo;
+ * const char * serverRef;
+ * uint16_t serverRefLength;
+ * uint32_t maxPacketSize;
+ * // Receive an incoming packet and populate all fields. The details are out of scope
+ * // for this example.
+ * receiveIncomingPacket( &incomingPacket );
+ *
+ * // Deserialize disconnect information.
+ * if( ( incomingPacket.type) == MQTT_PACKET_TYPE_DISCONNECT )
+ * {
+ * status = MQTTV5_DeserializeDisconnect(&incomingPacket,
+ &disconnectInfo,
+ &serverRef,
+ &serverRefLength,
+ maxPacketSize );
+ * if( status == MQTTSuccess )
+ * {
+ * // Disconnect information is available.
+ * }
+ * }
+ * @endcode
+ */
+/* @[declare_mqttv5_deserializedisconnect] */
+ MQTTStatus_t MQTTV5_DeserializeDisconnect( const MQTTPacketInfo_t * pPacket,
+ MQTTAckInfo_t *pDisconnectInfo,
+ const char ** pServerRef,
+ uint16_t * pServerRefLength,
+ uint32_t maxPacketSize );
+/* @[declare_mqttv5_deserializedisconnect] */
+
+
+/**
+ * @brief Serialize an MQTT DISCONNECT packet into the given buffer.
+ *
+ * The input #MQTTFixedBuffer_t.size must be at least as large as the size
+ * returned by #MQTTV5_GetDisconnectPacketSize.
+ *
+ * @param[in] pDisconnectInfo Struct containing information about the disconnect.
+ * @param[in] remainingLength The remaining length of the packet to be
+ * serialized.
+ * @param[out] pFixedBuffer Buffer for packet serialization.
+ * @param[in] sessionExpiry Session expiry to be serialized.
+ *
+ * @return #MQTTNoMemory if pFixedBuffer is too small to hold the MQTT packet;
+ * #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSuccess otherwise.
+ *
+ * Example
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTFixedBuffer_t fixedBuffer;
+ * uint8_t buffer[ BUFFER_SIZE ];
+ * MQTTAckInfo_t disconnectInfo;
+ * uint16_t sessionExpiry;
+ *
+ * fixedBuffer.pBuffer = buffer;
+ * fixedBuffer.size = BUFFER_SIZE;
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTAckInfo_t disconnectInfo ={0};
+ * size_t remainingLength =0;
+ * size_t packetSize = 0;
+ * uint32_t maxPacketSize;
+ * uint32_t sessionExpiry;
+ * uint32_t prevSessionExpiry
+ *
+ * //set the parameters.
+ * // Get the size requirement for the disconnect packet.
+ * status = MQTTV5_GetDisconnectPacketSize(&disconnectInfo,&remainingLength,&packetSize,maxPacketSize, sessionExpiry,prevSessionExpiry );
+ *
+ * assert( status == MQTTSuccess );
+ * assert( packetSize <= BUFFER_SIZE );
+ *
+ * // Serialize the disconnect into the fixed buffer.
+ * MQTTV5_SerializeDisconnectWithProperty( &disconnectInfo,
+ remainingLength,
+ &fixedBuffer,
+ sessionExpiry);
+ *
+ * if( status == MQTTSuccess )
+ * {
+ * // The disconnect packet can now be sent to the broker.
+ * }
+ * @endcode
+ */
+/* @[declare_mqttv5_serializedisconnectwithproperty] */
+MQTTStatus_t MQTTV5_SerializeDisconnectWithProperty( const MQTTAckInfo_t *pDisconnectInfo,
+ size_t remainingLength,
+ const MQTTFixedBuffer_t * pFixedBuffer,
+ uint32_t sessionExpiry);
+/* @[declare_mqttv5_serializedisconnectwithproperty] */
+
/* *INDENT-OFF* */
#ifdef __cplusplus
}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 675bce541..6d8aae2a5 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -52,6 +52,9 @@ if( COV_ANALYSIS )
# Build MQTT library target without custom config dependency.
target_compile_definitions( coverity_analysis PUBLIC MQTT_DO_NOT_USE_CUSTOM_CONFIG=1 )
+ target_compile_definitions( coverity_analysis PUBLIC MQTT_VERSION_5_ENABLED=1 )
+ target_compile_definitions( coverity_analysis PUBLIC MAX_USER_PROPERTY=5)
+ target_compile_definitions( coverity_analysis PUBLIC MQTT_USER_PROPERTY_ENABLED=1)
# MQTT public include path.
target_include_directories( coverity_analysis PUBLIC ${MQTT_INCLUDE_PUBLIC_DIRS} )
@@ -93,7 +96,11 @@ if( UNITTEST )
include( ${MODULE_ROOT_DIR}/tools/cmock/create_test.cmake )
# Include build configuration for unit tests.
- add_subdirectory( unit-test )
+ add_subdirectory( unit-test/MQTT)
+ add_subdirectory( unit-test/MQTTv5)
+ add_subdirectory( unit-test/WithoutUP)
+
+
# ==================================== Coverage Analysis configuration ========================================
diff --git a/test/unit-test/CMakeLists.txt b/test/unit-test/MQTT/CMakeLists.txt
similarity index 100%
rename from test/unit-test/CMakeLists.txt
rename to test/unit-test/MQTT/CMakeLists.txt
diff --git a/test/unit-test/cmock_opaque_types.h b/test/unit-test/MQTT/cmock_opaque_types.h
similarity index 100%
rename from test/unit-test/cmock_opaque_types.h
rename to test/unit-test/MQTT/cmock_opaque_types.h
diff --git a/test/unit-test/core_mqtt_config.h b/test/unit-test/MQTT/core_mqtt_config.h
similarity index 97%
rename from test/unit-test/core_mqtt_config.h
rename to test/unit-test/MQTT/core_mqtt_config.h
index 24b577f57..57b97c82a 100644
--- a/test/unit-test/core_mqtt_config.h
+++ b/test/unit-test/MQTT/core_mqtt_config.h
@@ -43,7 +43,7 @@
* 3. Include the header file "logging_stack.h", if logging is enabled for MQTT.
*/
-#include "logging_levels.h"
+#include "../logging/logging_levels.h"
/* Logging configuration for the MQTT library. */
#ifndef LIBRARY_LOG_NAME
@@ -54,7 +54,7 @@
#define LIBRARY_LOG_LEVEL LOG_NONE
#endif
-#include "logging_stack.h"
+#include "../logging/logging_stack.h"
/************ End of logging configuration ****************/
diff --git a/test/unit-test/core_mqtt_serializer_utest.c b/test/unit-test/MQTT/core_mqtt_serializer_utest.c
similarity index 99%
rename from test/unit-test/core_mqtt_serializer_utest.c
rename to test/unit-test/MQTT/core_mqtt_serializer_utest.c
index e91aae72a..84efbfc21 100644
--- a/test/unit-test/core_mqtt_serializer_utest.c
+++ b/test/unit-test/MQTT/core_mqtt_serializer_utest.c
@@ -1878,6 +1878,12 @@ void test_MQTT_GetIncomingPacketTypeAndLength( void )
status = MQTT_GetIncomingPacketTypeAndLength( mockReceive, &networkContext, &mqttPacket );
TEST_ASSERT_EQUAL( MQTTBadResponse, status );
+ /* Test with disconnect packet type. */
+ bufPtr = buffer;
+ buffer[ 0 ] = MQTT_PACKET_TYPE_DISCONNECT;
+ status = MQTT_GetIncomingPacketTypeAndLength( mockReceive, &networkContext, &mqttPacket );
+ TEST_ASSERT_EQUAL( MQTTBadResponse, status );
+
/* Test with invalid remaining length. */
bufPtr = buffer;
buffer[ 0 ] = 0x20; /* CONN ACK */
diff --git a/test/unit-test/core_mqtt_state_utest.c b/test/unit-test/MQTT/core_mqtt_state_utest.c
similarity index 100%
rename from test/unit-test/core_mqtt_state_utest.c
rename to test/unit-test/MQTT/core_mqtt_state_utest.c
diff --git a/test/unit-test/core_mqtt_utest.c b/test/unit-test/MQTT/core_mqtt_utest.c
similarity index 100%
rename from test/unit-test/core_mqtt_utest.c
rename to test/unit-test/MQTT/core_mqtt_utest.c
diff --git a/test/unit-test/MQTTv5/CMakeLists.txt b/test/unit-test/MQTTv5/CMakeLists.txt
new file mode 100644
index 000000000..3c32bf467
--- /dev/null
+++ b/test/unit-test/MQTTv5/CMakeLists.txt
@@ -0,0 +1,115 @@
+# Include filepaths for source and include.
+include( ${MODULE_ROOT_DIR}/mqttFilePaths.cmake )
+
+# ==================== Define your project name (edit) ========================
+set(project_name "core_mqttv5")
+
+
+# ===================== Create your mock here (edit) ========================
+
+# list the files to mock here
+list(APPEND mock_list
+ "${MODULE_ROOT_DIR}/source/include/core_mqtt_serializer.h"
+ "${MODULE_ROOT_DIR}/source/include/core_mqtt_state.h"
+ )
+# list the directories your mocks need
+list(APPEND mock_include_list
+ .
+ ${CMAKE_CURRENT_LIST_DIR}/logging
+ ${MQTT_INCLUDE_PUBLIC_DIRS}
+ )
+#list the definitions of your mocks to control what to be included
+list(APPEND mock_define_list
+ ""
+ )
+
+# ================= Create the library under test here (edit) ==================
+
+# list the files you would like to test here
+list(APPEND real_source_files
+ ${MQTT_SOURCES}
+ ${MQTT_SERIALIZER_SOURCES}
+ )
+# list the directories the module under test includes
+list(APPEND real_include_directories
+ .
+ ${CMAKE_CURRENT_LIST_DIR}/logging
+ ${MQTT_INCLUDE_PUBLIC_DIRS}
+ )
+
+# ===================== Create UnitTest Code here (edit) =====================
+
+# list the directories your test needs to include
+list(APPEND test_include_directories
+ .
+ ${MQTT_INCLUDE_PUBLIC_DIRS}
+ )
+
+# ============================= (end edit) ===================================
+
+set(mock_name "${project_name}_mock")
+set(real_name "${project_name}_real")
+
+create_mock_list(${mock_name}
+ "${mock_list}"
+ "${MODULE_ROOT_DIR}/tools/cmock/project.yml"
+ "${mock_include_list}"
+ "${mock_define_list}"
+ )
+
+create_real_library(${real_name}
+ "${real_source_files}"
+ "${real_include_directories}"
+ "${mock_name}"
+ )
+
+list(APPEND utest_link_list
+ -l${mock_name}
+ lib${real_name}.a
+ )
+
+list(APPEND utest_dep_list
+ ${real_name}
+ )
+
+set(utest_name "${project_name}_utest")
+set(utest_source "${project_name}_utest.c")
+create_test(${utest_name}
+ ${utest_source}
+ "${utest_link_list}"
+ "${utest_dep_list}"
+ "${test_include_directories}"
+ )
+
+# need to redefine because the tests below don't use any mocks
+set(utest_link_list "")
+list(APPEND utest_link_list
+ lib${real_name}.a
+ )
+
+# mqtt_state_utest
+set(utest_name "${project_name}_state_utest")
+set(utest_source "${project_name}_state_utest.c")
+
+create_test(${utest_name}
+ ${utest_source}
+ "${utest_link_list}"
+ "${utest_dep_list}"
+ "${test_include_directories}"
+ )
+
+# mqtt_serializer_utest
+set(utest_name "${project_name}_serializer_utest")
+set(utest_source "${project_name}_serializer_utest.c")
+
+set(utest_link_list "")
+list(APPEND utest_link_list
+ lib${real_name}.a
+ )
+
+create_test(${utest_name}
+ ${utest_source}
+ "${utest_link_list}"
+ "${utest_dep_list}"
+ "${test_include_directories}"
+ )
\ No newline at end of file
diff --git a/test/unit-test/MQTTv5/cmock_opaque_types.h b/test/unit-test/MQTTv5/cmock_opaque_types.h
new file mode 100644
index 000000000..d29ef7b9b
--- /dev/null
+++ b/test/unit-test/MQTTv5/cmock_opaque_types.h
@@ -0,0 +1,36 @@
+/*
+ * coreMQTT
+ * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef CMOCK_OPAQUE_TYPES_H_
+#define CMOCK_OPAQUE_TYPES_H_
+
+/* CMock does not support opaque types so needs concrete definitions for them.
+ * This file is included in CMock .c files. */
+
+struct NetworkContext
+{
+ int a;
+};
+
+#endif /* ifndef CMOCK_OPAQUE_TYPES_H_ */
diff --git a/test/unit-test/MQTTv5/core_mqtt_config.h b/test/unit-test/MQTTv5/core_mqtt_config.h
new file mode 100644
index 000000000..35eec5b01
--- /dev/null
+++ b/test/unit-test/MQTTv5/core_mqtt_config.h
@@ -0,0 +1,79 @@
+/*
+ * coreMQTT
+ * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @file core_mqtt_config.h
+ * @brief This header sets configuration macros for the MQTT library.
+ */
+#ifndef CORE_MQTT_CONFIG_H_
+#define CORE_MQTT_CONFIG_H_
+
+/* Standard include. */
+#include
+
+/**************************************************/
+/******* DO NOT CHANGE the following order ********/
+/**************************************************/
+
+/* Include logging header files and define logging macros in the following order:
+ * 1. Include the header file "logging_levels.h".
+ * 2. Define the LIBRARY_LOG_NAME and LIBRARY_LOG_LEVEL macros depending on
+ * the logging configuration for MQTT.
+ * 3. Include the header file "logging_stack.h", if logging is enabled for MQTT.
+ */
+
+#include "../logging/logging_levels.h"
+
+/* Logging configuration for the MQTT library. */
+#ifndef LIBRARY_LOG_NAME
+ #define LIBRARY_LOG_NAME "MQTT"
+#endif
+
+#ifndef LIBRARY_LOG_LEVEL
+ #define LIBRARY_LOG_LEVEL LOG_NONE
+#endif
+
+#include "../logging/logging_stack.h"
+
+/************ End of logging configuration ****************/
+
+/**
+ * @brief Retry count for reading CONNACK from network.
+ *
+ * #MQTT_Connect() can be using retries. If timeout passed as 0 to MQTT_Connect(),
+ * retries are used to attempt to read from network. The maximum retry count is
+ * specified by this config.
+ *
+ * These unit tests expect retrying only twice.
+ */
+#define MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT ( 2U )
+
+#define MQTT_SUB_UNSUB_MAX_VECTORS ( 6U )
+
+#define MQTT_SEND_TIMEOUT_MS ( 200U )
+#define MQTT_VERSION_5_ENABLED ( true )
+#define MAX_USER_PROPERTY ( 5000U )
+#define MQTT_USER_PROPERTY_ENABLED ( true )
+
+#endif /* ifndef CORE_MQTT_CONFIG_H_ */
diff --git a/test/unit-test/MQTTv5/core_mqttv5_serializer_utest.c b/test/unit-test/MQTTv5/core_mqttv5_serializer_utest.c
new file mode 100644
index 000000000..2da446624
--- /dev/null
+++ b/test/unit-test/MQTTv5/core_mqttv5_serializer_utest.c
@@ -0,0 +1,3280 @@
+/*
+ * coreMQTT
+ * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @file core_mqtt_serializer_utest.c
+ * @brief Unit tests for functions in core_mqtt_serializer.h.
+ */
+#include
+#include
+#include
+#include "unity.h"
+
+/* Include paths for public enums, structures, and macros. */
+#include "core_mqtt_serializer.h"
+
+/* Set network context to double pointer to buffer (uint8_t**). */
+struct NetworkContext
+{
+ uint8_t ** buffer;
+};
+
+#define MQTT_MAX_REMAINING_LENGTH ( 268435455UL )
+#define MQTT_PACKET_CONNACK_REMAINING_LENGTH ( ( uint8_t ) 2U ) /**< @brief A CONNACK packet always has a "Remaining length" of 2. */
+#define MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK ( ( uint8_t ) 0x01U ) /**< @brief The "Session Present" bit is always the lowest bit. */
+#define MQTT_PACKET_SIMPLE_ACK_REMAINING_LENGTH ( ( uint8_t ) 2 ) /**< @brief PUBACK, PUBREC, PUBREl, PUBCOMP, UNSUBACK Remaining length. */
+#define MQTT_PACKET_PINGRESP_REMAINING_LENGTH ( 0U ) /**< @brief A PINGRESP packet always has a "Remaining length" of 0. */
+#define MQTT_PACKET_PUBACK_REMAINING_LENGTH ( 2U )
+#define MQTT_PACKET_UNSUBACK_REMAINING_LENGTH ( 2U )
+
+/*
+ * MQTT client identifier.
+ */
+#define MQTT_CLIENT_IDENTIFIER "testclient"
+
+/*
+ * Client identifier and length to use for the MQTT API tests.
+ */
+#define CLIENT_IDENTIFIER ( "test" ) /**< @brief Client identifier. */
+#define CLIENT_IDENTIFIER_LENGTH ( ( uint16_t ) ( sizeof( CLIENT_IDENTIFIER ) - 1 ) ) /**< @brief Length of client identifier. */
+
+/*
+ * Topic name and length to use for the MQTT API tests.
+ */
+#define TEST_TOPIC_NAME ( "/test/topic" ) /**< @brief An arbitrary topic name. */
+#define TEST_TOPIC_NAME_LENGTH ( ( uint16_t ) ( sizeof( TEST_TOPIC_NAME ) - 1 ) ) /**< @brief Length of topic name. */
+
+/**
+ * @brief MQTT protocol version 3.1.1.
+ */
+#define MQTT_VERSION_3_1_1 ( ( uint8_t ) 4U )
+
+/**
+ * @brief Test-defined macro for MQTT username.
+ */
+#define MQTT_TEST_USERNAME "username"
+#define MQTT_TEST_USERNAME_LEN ( sizeof( MQTT_TEST_USERNAME ) - 1 )
+
+/**
+ * @brief Test-defined macro for MQTT password.
+ */
+#define MQTT_TEST_PASSWORD "password"
+#define MQTT_TEST_PASSWORD_LEN ( sizeof( MQTT_TEST_PASSWORD ) - 1 )
+
+/**
+ * @brief Length of the client identifier.
+ */
+#define MQTT_CLIENT_IDENTIFIER_LEN ( sizeof( MQTT_CLIENT_IDENTIFIER ) - 1 )
+
+/**
+ * @brief Sample payload.
+ */
+#define MQTT_SAMPLE_PAYLOAD "Hello World"
+#define MQTT_SAMPLE_PAYLOAD_LEN ( sizeof( MQTT_SAMPLE_PAYLOAD ) - 1 )
+
+#define TEST_TOPIC_ALIAS ( 2U )
+#define TEST_MSG_EXPIRY ( 100U )
+
+
+#define MQTT_TEST_UTF8_STRING ( "test" )
+#define MQTT_TEST_UTF8_STRING_LENGTH ( sizeof( MQTT_TEST_UTF8_STRING ) - 1 )
+#define MQTT_TEST_UINT8 ( 1U )
+#define MQTT_TEST_UINT16 ( 32U )
+#define MQTT_TEST_UINT32 ( 300U )
+
+/* MQTT CONNECT flags. */
+#define MQTT_CONNECT_FLAG_CLEAN ( 1 ) /**< @brief Clean session. */
+#define MQTT_CONNECT_FLAG_WILL ( 2 ) /**< @brief Will present. */
+#define MQTT_CONNECT_FLAG_WILL_QOS1 ( 3 ) /**< @brief Will QoS 1. */
+#define MQTT_CONNECT_FLAG_WILL_QOS2 ( 4 ) /**< @brief Will QoS 2. */
+#define MQTT_CONNECT_FLAG_WILL_RETAIN ( 5 ) /**< @brief Will retain. */
+#define MQTT_CONNECT_FLAG_PASSWORD ( 6 ) /**< @brief Password present. */
+#define MQTT_CONNECT_FLAG_USERNAME ( 7 ) /**< @brief User name present. */
+
+/*Default connect properties. */
+#define DEFAULT_RECEIVE_MAX ( 65535U )
+#define DEFAULT_REQUEST_PROBLEM ( 1 )
+
+/**
+ * @brief The Remaining Length field of MQTT disconnect packets, per MQTT spec.
+ */
+#define MQTT_DISCONNECT_REMAINING_LENGTH ( ( uint8_t ) 0 )
+
+/**
+ * @brief Set a bit in an 8-bit unsigned integer.
+ */
+#define UINT8_SET_BIT( x, position ) ( ( x ) = ( uint8_t ) ( ( x ) | ( 0x01U << ( position ) ) ) )
+
+/**
+ * @brief Macro for checking if a bit is set in a 1-byte unsigned int.
+ *
+ * @param[in] x The unsigned int to check.
+ * @param[in] position Which bit to check.
+ */
+#define UINT8_CHECK_BIT( x, position ) ( ( ( x ) & ( 0x01U << ( position ) ) ) == ( 0x01U << ( position ) ) )
+
+/**
+ * @brief Get the high byte of a 16-bit unsigned integer.
+ */
+#define UINT16_HIGH_BYTE( x ) ( ( uint8_t ) ( ( x ) >> 8 ) )
+
+/**
+ * @brief Get the low byte of a 16-bit unsigned integer.
+ */
+#define UINT16_LOW_BYTE( x ) ( ( uint8_t ) ( ( x ) & 0x00ffU ) )
+
+/**
+ * @brief Maximum number of bytes in the Remaining Length field is four according
+ * to MQTT 3.1.1 spec.
+ */
+#define MQTT_REMAINING_BUFFER_MAX_LENGTH ( 4 )
+
+/**
+ * @brief Length of buffer padding to use in under/overflow checks.
+ */
+#define BUFFER_PADDING_LENGTH ( 4 )
+
+/**
+ * @brief Byte to use for buffer padding in under/overflow checks.
+ */
+#define BUFFER_PADDING_BYTE ( 0xA5 )
+
+/**
+ * @brief Length of the MQTT network buffer.
+ */
+#define MQTT_TEST_BUFFER_LENGTH ( 1024 )
+
+#define UINT16_DECODE( ptr ) \
+ ( uint16_t ) ( ( ( ( uint16_t ) ptr[ 0 ] ) << 8 ) | \
+ ( ( uint16_t ) ptr[ 1 ] ) )
+
+#define UINT32_DECODE( ptr ) \
+ ( uint32_t ) ( ( ( ( uint32_t ) ptr[ 0 ] ) << 24 ) | \
+ ( ( ( uint32_t ) ptr[ 1 ] ) << 16 ) | \
+ ( ( ( uint32_t ) ptr[ 2 ] ) << 8 ) | \
+ ( ( uint32_t ) ptr[ 3 ] ) )
+
+#define UINT32_BYTE3( x ) ( ( uint8_t ) ( ( x ) >> 24 ) )
+
+#define UINT32_BYTE2( x ) ( ( uint8_t ) ( ( x ) >> 16 ) )
+
+#define UINT32_BYTE1( x ) ( ( uint8_t ) ( ( x ) >> 8 ) )
+
+#define UINT32_BYTE0( x ) ( ( uint8_t ) ( ( x ) & 0x000000FFU ) )
+
+
+#define MQTT_MAX_PACKET_SIZE ( 268435460UL )
+#define MQTT_VERSION_5 ( 5U )
+#define MQTT_SESSION_EXPIRY_SIZE ( 5U )
+#define MQTT_RECEIVE_MAX_SIZE ( 3U )
+#define MQTT_MAX_PACKET_PROPERTY_SIZE ( 5U )
+#define MQTT_TOPIC_ALIAS_SIZE ( 3U )
+#define MQTT_REQUEST_RESPONSE_SIZE ( 2U )
+#define MQTT_REQUEST_PROBLEM_SIZE ( 2U )
+
+#define MQTT_SESSION_EXPIRY_ID ( 0x11 )
+#define MQTT_RECEIVE_MAX_ID ( 0x21 )
+#define MQTT_MAX_PACKET_SIZE_ID ( 0x27 )
+#define MQTT_TOPIC_ALIAS_MAX_ID ( 0x22 )
+#define MQTT_REQUEST_RESPONSE_ID ( 0x19 )
+#define MQTT_REQUEST_PROBLEM_ID ( 0x17 )
+#define MQTT_USER_PROPERTY_ID ( 0x26 )
+#define MQTT_AUTH_METHOD_ID ( 0x15 )
+#define MQTT_AUTH_DATA_ID ( 0x16 )
+
+#define MQTT_WILL_DELAY_ID ( 0x18 )
+#define MQTT_PAYLOAD_FORMAT_ID ( 0x01 )
+#define MQTT_MSG_EXPIRY_ID ( 0x02 )
+#define MQTT_CONTENT_TYPE_ID ( 0x03 )
+#define MQTT_RESPONSE_TOPIC_ID ( 0x08 )
+#define MQTT_CORRELATION_DATA_ID ( 0x09 )
+
+#define MQTT_MAX_QOS_ID ( 0x24 )
+#define MQTT_RETAIN_AVAILABLE_ID ( 0x25 )
+#define MQTT_ASSIGNED_CLIENT_ID ( 0x12 )
+#define MQTT_REASON_STRING_ID ( 0x1F )
+#define MQTT_WILDCARD_ID ( 0x28 )
+#define MQTT_SUB_AVAILABLE_ID ( 0x29 )
+#define MQTT_SHARED_SUB_ID ( 0x2A )
+#define MQTT_SERVER_KEEP_ALIVE_ID ( 0x13 )
+#define MQTT_RESPONSE_INFO_ID ( 0x1A )
+#define MQTT_SERVER_REF_ID ( 0x1C )
+
+#define MQTT_REASON_SUCCESS ( 0x00 )
+#define MQTT_REASON_SEND_WILL ( 0x04 )
+#define MQTT_REASON_NO_MATCHING_SUBSCRIBERS ( 0x10 )
+#define MQTT_REASON_UNSPECIFIED_ERR ( 0x80 )
+#define MQTT_REASON_MALFORMED_PACKET ( 0x81 )
+#define MQTT_REASON_PROTOCOL_ERR ( 0x82 )
+#define MQTT_REASON_IMPL_SPECIFIC_ERR ( 0x83 )
+#define MQTT_REASON_UNSUPPORTED_PROTO_VER ( 0x84 )
+#define MQTT_REASON_CLIENT_ID_NOT_VALID ( 0x85 )
+#define MQTT_REASON_BAD_USER_OR_PASS ( 0x86 )
+#define MQTT_REASON_NOT_AUTHORIZED ( 0x87 )
+#define MQTT_REASON_SERVER_UNAVAILABLE ( 0x88 )
+#define MQTT_REASON_SERVER_BUSY ( 0x89 )
+#define MQTT_REASON_BANNED ( 0x8A )
+#define MQTT_REASON_SERVER_SHUTTING_DOWN ( 0x8B )
+#define MQTT_REASON_BAD_AUTH_METHOD ( 0x8C )
+#define MQTT_REASON_KEEP_ALIVE_TIMEOUT ( 0x8D )
+#define MQTT_REASON_SESSION_TAKEN_OVER ( 0x8E )
+#define MQTT_REASON_TOPIC_FILTER_INVALID ( 0x8F )
+#define MQTT_REASON_TOPIC_NAME_INVALID ( 0x90 )
+#define MQTT_REASON_PACKET_ID_IN_USE ( 0x91 )
+#define MQTT_REASON_PACKET_ID_NOT_FOUND ( 0x92 )
+#define MQTT_REASON_RX_MAX_EXCEEDED ( 0x93 )
+#define MQTT_REASON_TOPIC_ALIAS_INVALID ( 0x94 )
+#define MQTT_REASON_PACKET_TOO_LARGE ( 0x95 )
+#define MQTT_REASON_MSG_RATE_TOO_HIGH ( 0x96 )
+#define MQTT_REASON_QUOTA_EXCEEDED ( 0x97 )
+#define MQTT_REASON_ADMIN_ACTION ( 0x98 )
+#define MQTT_REASON_PAYLOAD_FORMAT_INVALID ( 0x99 )
+#define MQTT_REASON_RETAIN_NOT_SUPPORTED ( 0x9A )
+#define MQTT_REASON_QOS_NOT_SUPPORTED ( 0x9B )
+#define MQTT_REASON_USE_ANOTHER_SERVER ( 0x9C )
+#define MQTT_REASON_SERVER_MOVED ( 0x9D )
+#define MQTT_REASON_SS_NOT_SUPPORTED ( 0x9E )
+#define MQTT_REASON_CON_RATE_EXCEED ( 0x9F )
+#define MQTT_REASON_MAX_CON_TIME ( 0xA0 )
+#define MQTT_REASON_SUB_ID_NOT_SUP ( 0xA1 )
+#define MQTT_REASON_WILDCARD_SUB_NOT_SUP ( 0xA2 )
+
+#define CORE_MQTT_ID_SIZE ( 1U )
+#define MQTT_REMAINING_LENGTH_INVALID ( ( size_t ) 268435456 )
+
+static uint8_t remainingLengthBuffer[ MQTT_REMAINING_BUFFER_MAX_LENGTH ] = { 0 };
+
+static uint8_t encodedStringBuffer[ MQTT_TEST_BUFFER_LENGTH ] = { 0 };
+
+static uint8_t mqttBuffer[ MQTT_TEST_BUFFER_LENGTH ] = { 0 };
+
+
+/* Variables common to testcases */
+MQTTConnectProperties_t properties;
+MQTTUserProperties_t userProperties;
+MQTTPublishInfo_t publishInfo;
+MQTTConnectInfo_t connectInfo;
+MQTTPacketInfo_t packetInfo;
+MQTTStatus_t status;
+
+/* ============================ UNITY FIXTURES ============================ */
+
+/* Called before each test method. */
+void setUp( void )
+{
+ memset( &properties, 0x0, sizeof( properties ) );
+ memset( &userProperties, 0x0, sizeof( userProperties ) );
+ memset( &publishInfo, 0x0, sizeof( publishInfo ) );
+ memset( &connectInfo, 0x0, sizeof( connectInfo ) );
+ memset( &packetInfo, 0x0, sizeof( packetInfo ) );
+}
+
+/* Called after each test method. */
+void tearDown( void )
+{
+}
+
+/* Called at the beginning of the whole suite. */
+void suiteSetUp()
+{
+}
+
+/* Called at the end of the whole suite. */
+int suiteTearDown( int numFailures )
+{
+ return numFailures;
+}
+
+/* ========================================================================== */
+
+/**
+ * @brief Initialize pNetworkBuffer using static buffer.
+ *
+ * @param[in] pNetworkBuffer Network buffer provided for the context.
+ */
+static void setupNetworkBuffer( MQTTFixedBuffer_t * const pNetworkBuffer )
+{
+ pNetworkBuffer->pBuffer = mqttBuffer;
+ pNetworkBuffer->size = 2048;
+}
+
+/**
+ * @brief Initialize pConnectInfo using test-defined macros.
+ *
+ * @param[in] pConnectInfo MQTT CONNECT packet parameters.
+ */
+static void setupConnectInfo( MQTTConnectInfo_t * const pConnectInfo )
+{
+ pConnectInfo->cleanSession = true;
+ pConnectInfo->pClientIdentifier = MQTT_CLIENT_IDENTIFIER;
+ pConnectInfo->clientIdentifierLength = MQTT_CLIENT_IDENTIFIER_LEN;
+ pConnectInfo->keepAliveSeconds = 0;
+ pConnectInfo->pUserName = MQTT_TEST_USERNAME;
+ pConnectInfo->userNameLength = MQTT_TEST_USERNAME_LEN;
+ pConnectInfo->pPassword = MQTT_TEST_PASSWORD;
+ pConnectInfo->passwordLength = MQTT_TEST_PASSWORD_LEN;
+}
+
+static size_t remainingLengthEncodedSize( size_t length )
+{
+ size_t encodedSize;
+
+ /* Determine how many bytes are needed to encode length.
+ * The values below are taken from the MQTT 3.1.1 spec. */
+
+ /* 1 byte is needed to encode lengths between 0 and 127. */
+ if( length < 128U )
+ {
+ encodedSize = 1U;
+ }
+ /* 2 bytes are needed to encode lengths between 128 and 16,383. */
+ else if( length < 16384U )
+ {
+ encodedSize = 2U;
+ }
+ /* 3 bytes are needed to encode lengths between 16,384 and 2,097,151. */
+ else if( length < 2097152U )
+ {
+ encodedSize = 3U;
+ }
+ /* 4 bytes are needed to encode lengths between 2,097,152 and 268,435,455. */
+ else
+ {
+ encodedSize = 4U;
+ }
+
+ LogDebug( ( "Encoded size for length %lu is %lu bytes.",
+ ( unsigned long ) length,
+ ( unsigned long ) encodedSize ) );
+
+ return encodedSize;
+}
+
+/**
+ * @brief Initialize pPublishInfo using test-defined macros.
+ *
+ * @param[in] pPublishInfo Publish information.
+ */
+static void setupPublishInfo( MQTTPublishInfo_t * pPublishInfo )
+{
+ pPublishInfo->pTopicName = TEST_TOPIC_NAME;
+ pPublishInfo->topicNameLength = TEST_TOPIC_NAME_LENGTH;
+ pPublishInfo->pPayload = MQTT_SAMPLE_PAYLOAD;
+ pPublishInfo->payloadLength = MQTT_SAMPLE_PAYLOAD_LEN;
+ pPublishInfo->qos = MQTTQoS0;
+ pPublishInfo->dup = false;
+ pPublishInfo->retain = false;
+}
+
+/**
+ * @brief Initialize pPublishInfo using test-defined macros.
+ *
+ * @param[in] pPublishInfo Publish information.
+ */
+static void setupPublishProperties( MQTTPublishInfo_t * pPublishInfo )
+{
+ pPublishInfo->payloadFormat = 1;
+ pPublishInfo->topicAlias = TEST_TOPIC_ALIAS;
+ pPublishInfo->msgExpiryInterval = TEST_MSG_EXPIRY;
+ pPublishInfo->msgExpiryPresent = 1;
+ pPublishInfo->contentTypeLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ pPublishInfo->pContentType = MQTT_TEST_UTF8_STRING;
+ pPublishInfo->responseTopicLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ pPublishInfo->pResponseTopic = MQTT_TEST_UTF8_STRING;
+ pPublishInfo->correlationLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ pPublishInfo->pCorrelationData = MQTT_TEST_UTF8_STRING;
+}
+
+/**
+ * @brief Encode remaining length into pDestination for packet serialization
+ * using MQTT v3.1.1 spec.
+ *
+ * @param[in] pDestination Buffer to write encoded remaining length.
+ * @param[in] length Actual remaining length.
+ */
+static size_t encodeRemainingLength( uint8_t * pDestination,
+ size_t length )
+{
+ uint8_t lengthByte;
+ uint8_t * pLengthEnd = NULL;
+ size_t remainingLength = length;
+
+ TEST_ASSERT_NOT_NULL( pDestination );
+
+ pLengthEnd = pDestination;
+
+ /* This algorithm is copied from the MQTT v3.1.1 spec. */
+ do
+ {
+ lengthByte = ( uint8_t ) ( remainingLength % 128U );
+ remainingLength = remainingLength / 128U;
+
+ /* Set the high bit of this byte, indicating that there's more data. */
+ if( remainingLength > 0U )
+ {
+ UINT8_SET_BIT( lengthByte, 7 );
+ }
+
+ /* Output a single encoded byte. */
+ *pLengthEnd = lengthByte;
+ pLengthEnd++;
+ } while( remainingLength > 0U );
+
+ return ( size_t ) ( pLengthEnd - pDestination );
+}
+
+/**
+ * @brief Encode UTF-8 string and its length into pDestination for
+ * packet serialization.
+ *
+ * @param[in] pDestination Buffer to write encoded string.
+ * @param[in] source String to encode.
+ * @param[in] sourceLength Length of the string to encode.
+ */
+static size_t encodeString( uint8_t * pDestination,
+ const char * source,
+ uint16_t sourceLength )
+{
+ uint8_t * pBuffer = NULL;
+
+ /* Typecast const char * typed source buffer to const uint8_t *.
+ * This is to use same type buffers in memcpy. */
+ const uint8_t * pSourceBuffer = ( const uint8_t * ) source;
+
+ TEST_ASSERT_NOT_NULL( pSourceBuffer );
+ TEST_ASSERT_NOT_NULL( pDestination );
+
+ pBuffer = pDestination;
+
+ /* The first byte of a UTF-8 string is the high byte of the string length. */
+ *pBuffer = UINT16_HIGH_BYTE( sourceLength );
+ pBuffer++;
+
+ /* The second byte of a UTF-8 string is the low byte of the string length. */
+ *pBuffer = UINT16_LOW_BYTE( sourceLength );
+ pBuffer++;
+
+ /* Copy the string into pBuffer. */
+ ( void ) memcpy( pBuffer, pSourceBuffer, sourceLength );
+
+ /* Return the pointer to the end of the encoded string. */
+ pBuffer += sourceLength;
+
+ return ( size_t ) ( pBuffer - pDestination );
+}
+
+/**
+ * @brief Pad beginning and end of buffer with non-zero bytes to be used in
+ * checking for under/overflow after serialization.
+ *
+ * @param[in] pBuffer Buffer to pad.
+ * @param[in] bufferLength Total length of buffer.
+ */
+static void padAndResetBuffer( uint8_t * pBuffer,
+ size_t bufferLength )
+{
+ int i = 0;
+
+ for( i = 0; i < BUFFER_PADDING_LENGTH; i++ )
+ {
+ pBuffer[ i ] = BUFFER_PADDING_BYTE;
+ pBuffer[ bufferLength - 1 - i ] = BUFFER_PADDING_BYTE;
+ }
+
+ /* Zero out rest of buffer. */
+ memset( &pBuffer[ BUFFER_PADDING_LENGTH ], 0x0, bufferLength - 2 * BUFFER_PADDING_LENGTH );
+}
+
+/**
+ * @brief Test buffer for under/overflow.
+ *
+ * @param[in] pBuffer Buffer to check.
+ * @param[in] bufferLength Total length of buffer.
+ */
+static void checkBufferOverflow( uint8_t * pBuffer,
+ size_t bufferLength )
+{
+ /* Check beginning of buffer. */
+ TEST_ASSERT_EACH_EQUAL_UINT8( BUFFER_PADDING_BYTE,
+ pBuffer,
+ BUFFER_PADDING_LENGTH );
+ /* Check end. */
+ TEST_ASSERT_EACH_EQUAL_UINT8( BUFFER_PADDING_BYTE,
+ pBuffer + bufferLength - BUFFER_PADDING_LENGTH,
+ BUFFER_PADDING_LENGTH );
+}
+
+/**
+ * @brief Mock successful transport receive by reading data from a buffer.
+ */
+static int32_t mockReceive( NetworkContext_t * pNetworkContext,
+ void * pBuffer,
+ size_t bytesToRecv )
+{
+ uint8_t * returnBuffer = ( uint8_t * ) pBuffer;
+ uint8_t * mockNetwork;
+ size_t bytesRead = 0;
+
+ /* Treat network context as pointer to buffer for mocking */
+ mockNetwork = *( pNetworkContext->buffer );
+
+ while( bytesRead++ < bytesToRecv )
+ {
+ /* Read single byte and advance buffer. */
+ *returnBuffer++ = *mockNetwork++;
+ }
+
+ /* Move stream by bytes read. */
+ *( pNetworkContext->buffer ) = mockNetwork;
+
+ return bytesToRecv;
+}
+
+
+static void setupProperties( MQTTConnectProperties_t * pProperties )
+{
+ pProperties->receiveMax = DEFAULT_RECEIVE_MAX;
+ pProperties->requestProblemInfo = DEFAULT_REQUEST_PROBLEM;
+ pProperties->maxPacketSize = MQTT_MAX_PACKET_SIZE;
+}
+
+static uint8_t * initializeDeserialize( MQTTPacketInfo_t * packetInfo,
+ uint8_t * pIndex )
+{
+ uint8_t * pIndexLocal = pIndex;
+
+ packetInfo->pRemainingData = pIndexLocal;
+ packetInfo->type = MQTT_PACKET_TYPE_CONNACK;
+ *pIndexLocal = 0x01;
+ pIndexLocal++;
+ *pIndexLocal = 0x00;
+ pIndexLocal++;
+ return pIndexLocal;
+}
+
+static uint8_t * serializeuint_32( uint8_t * pIndex,
+ uint8_t propertyId )
+{
+ uint8_t * pIndexLocal = pIndex;
+
+ *pIndexLocal = propertyId;
+ pIndexLocal++;
+ pIndexLocal[ 0 ] = UINT32_BYTE3( MQTT_TEST_UINT32 );
+ pIndexLocal[ 1 ] = UINT32_BYTE2( MQTT_TEST_UINT32 );
+ pIndexLocal[ 2 ] = UINT32_BYTE1( MQTT_TEST_UINT32 );
+ pIndexLocal[ 3 ] = UINT32_BYTE0( MQTT_TEST_UINT32 );
+ pIndexLocal = &pIndexLocal[ 4 ];
+ return pIndexLocal;
+}
+
+
+static uint8_t * serializeuint_16( uint8_t * pIndex,
+ uint8_t propertyId )
+{
+ uint8_t * pIndexLocal = pIndex;
+
+ *pIndexLocal = propertyId;
+ pIndexLocal++;
+ pIndexLocal[ 0 ] = UINT16_HIGH_BYTE( MQTT_TEST_UINT16 );
+ pIndexLocal[ 1 ] = UINT16_LOW_BYTE( MQTT_TEST_UINT16 );
+ pIndexLocal = &pIndexLocal[ 2 ];
+ return pIndexLocal;
+}
+
+static uint8_t * serializeuint_8( uint8_t * pIndex,
+ uint8_t propertyId )
+{
+ uint8_t * pIndexLocal = pIndex;
+
+ *pIndexLocal = propertyId;
+ pIndexLocal++;
+ pIndexLocal[ 0 ] = MQTT_TEST_UINT8;
+ pIndexLocal++;
+ return pIndexLocal;
+}
+static uint8_t * serializeutf_8( uint8_t * pIndex,
+ uint8_t propertyId )
+{
+ uint8_t * pIndexLocal = pIndex;
+
+ *pIndexLocal = propertyId;
+ pIndexLocal++;
+ size_t dummy = encodeString( pIndexLocal, MQTT_TEST_UTF8_STRING, MQTT_TEST_UTF8_STRING_LENGTH );
+ pIndexLocal = &pIndexLocal[ dummy ];
+ return pIndexLocal;
+}
+
+static uint8_t * serializeutf_8pair( uint8_t * pIndex )
+{
+ uint8_t * pIndexLocal = pIndex;
+
+ *pIndexLocal = MQTT_USER_PROPERTY_ID;
+ pIndexLocal++;
+ size_t dummy = encodeString( pIndexLocal, MQTT_TEST_UTF8_STRING, MQTT_TEST_UTF8_STRING_LENGTH );
+ pIndexLocal = &pIndexLocal[ dummy ];
+ dummy = encodeString( pIndexLocal, MQTT_TEST_UTF8_STRING, MQTT_TEST_UTF8_STRING_LENGTH );
+ pIndexLocal = &pIndexLocal[ dummy ];
+ return pIndexLocal;
+}
+static MQTTStatus_t MQTT_GetUserPropertySize( const MQTTUserProperty_t * pUserProperty,
+ uint32_t number,
+ size_t * pSize )
+{
+ MQTTStatus_t status = MQTTSuccess;
+ uint32_t i = 0;
+
+ /*Number of user properties can't be more than the max user properties specified*/
+ if( number > ( uint32_t ) MAX_USER_PROPERTY )
+ {
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ for( ; ( i < number ); i++ )
+ {
+ /*Validate the key and value*/
+ if( ( pUserProperty[ i ].keyLength == 0U ) || ( pUserProperty[ i ].valueLength == 0U ) || ( pUserProperty[ i ].pKey == NULL ) || ( pUserProperty[ i ].pValue == NULL ) )
+ {
+ status = MQTTBadParameter;
+ break;
+ }
+ else
+ {
+ *pSize += ( pUserProperty[ i ].keyLength );
+ *pSize += 3U;
+ *pSize += ( pUserProperty[ i ].valueLength );
+ *pSize += 2U;
+ }
+ }
+ }
+
+ return status;
+}
+
+static MQTTStatus_t MQTT_GetPublishPropertiesSize( MQTTPublishInfo_t * pPublishProperties )
+{
+ size_t propertyLength = 0U;
+ MQTTStatus_t status = MQTTSuccess;
+
+ /*Add the length of all the parameters which are applicable*/
+ if( pPublishProperties->willDelay != 0U )
+ {
+ propertyLength += 5U;
+ }
+
+ if( pPublishProperties->payloadFormat != 0U )
+ {
+ propertyLength += 2U;
+ }
+
+ if( pPublishProperties->msgExpiryPresent == true )
+ {
+ propertyLength += 5U;
+ }
+
+ if( pPublishProperties->contentTypeLength != 0U )
+ {
+ if( pPublishProperties->pContentType == NULL )
+ {
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ propertyLength += pPublishProperties->contentTypeLength + 3U;
+ }
+ }
+
+ /*Validate if length and pointers are valid*/
+ if( ( status == MQTTSuccess ) && ( pPublishProperties->responseTopicLength != 0U ) )
+ {
+ if( pPublishProperties->pResponseTopic == NULL )
+ {
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ propertyLength += pPublishProperties->responseTopicLength + 3U;
+ }
+ }
+
+ if( ( status == MQTTSuccess ) && ( pPublishProperties->correlationLength != 0U ) )
+ {
+ if( pPublishProperties->pCorrelationData == NULL )
+ {
+ status = MQTTBadParameter;
+ }
+ else
+ {
+ propertyLength += pPublishProperties->correlationLength + 3U;
+ }
+ }
+
+ /*Get the length of user properties*/
+ if( ( status == MQTTSuccess ) && ( pPublishProperties->pUserProperty != NULL ) )
+ {
+ status = MQTT_GetUserPropertySize( pPublishProperties->pUserProperty->userProperty, pPublishProperties->pUserProperty->count, &propertyLength );
+ }
+
+ /*Variable encoded can't have a value more than 268435455UL*/
+ if( propertyLength > MQTT_MAX_REMAINING_LENGTH )
+ {
+ status = MQTTBadParameter;
+ }
+
+ if( status == MQTTSuccess )
+ {
+ pPublishProperties->propertyLength = propertyLength;
+ }
+
+ return status;
+}
+
+static uint8_t * serializeUserProperties( uint8_t * pIndex,
+ const MQTTUserProperty_t * pUserProperty,
+ uint16_t size )
+{
+ uint16_t i = 0;
+
+ assert( pIndex != NULL );
+ uint8_t * pIndexLocal = pIndex;
+ uint8_t dummy;
+
+ for( ; i < size; i++ )
+ {
+ *pIndexLocal = MQTT_USER_PROPERTY_ID;
+ pIndexLocal++;
+ dummy = encodeString( pIndexLocal, ( pUserProperty + i )->pKey, ( pUserProperty + i )->keyLength );
+ pIndexLocal += dummy;
+ dummy = encodeString( pIndexLocal, ( pUserProperty + i )->pValue, ( pUserProperty + i )->valueLength );
+ pIndexLocal += dummy;
+ }
+
+ i = dummy;
+ return pIndexLocal;
+}
+
+MQTTStatus_t decodeVariableLength( const uint8_t * pBuffer,
+ size_t * length )
+{
+ size_t remainingLength = 0;
+ size_t multiplier = 1;
+ size_t bytesDecoded = 0;
+ size_t expectedSize = 0;
+ uint8_t encodedByte = 0;
+ MQTTStatus_t status = MQTTSuccess;
+
+ /* This algorithm is copied from the MQTT v3.1.1 spec. */
+ do
+ {
+ if( multiplier > 2097152U ) /* 128 ^ 3 */
+ {
+ remainingLength = MQTT_REMAINING_LENGTH_INVALID;
+
+ LogError( ( "Invalid remaining length in the packet.\n" ) );
+
+ status = MQTTBadResponse;
+ }
+ else
+ {
+ /* Get the next byte. It is at the next position after the bytes
+ * decoded till now since the header of one byte was read before. */
+ encodedByte = pBuffer[ bytesDecoded ];
+ remainingLength += ( ( size_t ) encodedByte & 0x7FU ) * multiplier;
+ multiplier *= 128U;
+ bytesDecoded++;
+ }
+
+ /* If the response is incorrect, or no more data is available, then
+ * break out of the loop. */
+ if( ( remainingLength == MQTT_REMAINING_LENGTH_INVALID ) ||
+ ( status != MQTTSuccess ) )
+ {
+ break;
+ }
+ } while( ( encodedByte & 0x80U ) != 0U );
+
+ if( status == MQTTSuccess )
+ {
+ /* Check that the decoded remaining length conforms to the MQTT specification. */
+ expectedSize = remainingLengthEncodedSize( remainingLength );
+
+ if( bytesDecoded != expectedSize )
+ {
+ LogError( ( "Expected and actual length of decoded bytes do not match.\n" ) );
+ status = MQTTBadResponse;
+ }
+ else
+ {
+ *length = remainingLength;
+ }
+ }
+
+ return status;
+}
+
+static void verifySerializedConnectPacket( const MQTTConnectInfo_t * const pConnectInfo,
+ const MQTTPublishInfo_t * const pWillInfo,
+ const MQTTConnectProperties_t * pConnectProperties,
+ size_t remainingLength,
+ const MQTTFixedBuffer_t * const pBuffer )
+{
+ uint8_t connectFlags = 0U;
+ uint8_t encodedRemainingLength = 0U;
+ uint8_t encodedStringLength = 0U;
+ uint8_t * pIndex = NULL;
+
+ pIndex = pBuffer->pBuffer;
+ /* The first byte in the CONNECT packet is the control packet type. */
+ TEST_ASSERT_EQUAL_MESSAGE( MQTT_PACKET_TYPE_CONNECT, *pIndex, "MQTT_PACKET_TYPE_CONNECT is not equal to *pIndex" );
+ pIndex++;
+
+ /* The remaining length of the CONNECT packet is encoded starting from the
+ * second byte. The remaining length does not include the length of the fixed
+ * header or the encoding of the remaining length. */
+ encodedRemainingLength = encodeRemainingLength( remainingLengthBuffer, remainingLength );
+ TEST_ASSERT_EQUAL_MEMORY( remainingLengthBuffer, pIndex, encodedRemainingLength );
+ pIndex += encodedRemainingLength;
+
+ /* The string "MQTT" is placed at the beginning of the CONNECT packet's variable
+ * header. This string is 4 bytes long. */
+ encodedStringLength = encodeString( encodedStringBuffer, "MQTT", 4 );
+ TEST_ASSERT_EQUAL_MEMORY( encodedStringBuffer, pIndex, encodedStringLength );
+ pIndex += encodedStringLength;
+
+ /* The MQTT protocol version is the second field of the variable header. */
+ TEST_ASSERT_EQUAL( 5, *pIndex );
+ pIndex++;
+
+ /* Set the clean session flag if needed. */
+ if( pConnectInfo->cleanSession == true )
+ {
+ UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_CLEAN );
+ }
+
+ /* Set the flags for username and password if provided. */
+ if( pConnectInfo->pUserName != NULL )
+ {
+ UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_USERNAME );
+ }
+
+ if( pConnectInfo->pPassword != NULL )
+ {
+ UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_PASSWORD );
+ }
+
+ /* Set will flag if a Last Will and Testament is provided. */
+ if( pWillInfo != NULL )
+ {
+ UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL );
+
+ /* Flags only need to be changed for Will QoS 1 or 2. */
+ if( pWillInfo->qos == MQTTQoS1 )
+ {
+ UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL_QOS1 );
+ }
+ else if( pWillInfo->qos == MQTTQoS2 )
+ {
+ UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL_QOS2 );
+ }
+ else
+ {
+ /* Empty else MISRA 15.7 */
+ }
+
+ if( pWillInfo->retain == true )
+ {
+ UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL_RETAIN );
+ }
+ }
+
+ TEST_ASSERT_EQUAL( connectFlags, *pIndex );
+ pIndex++;
+
+ /* Verify the 2 bytes of the keep alive interval into the CONNECT packet. */
+ TEST_ASSERT_EQUAL( UINT16_HIGH_BYTE( pConnectInfo->keepAliveSeconds ),
+ *pIndex );
+ pIndex++;
+ TEST_ASSERT_EQUAL( UINT16_LOW_BYTE( pConnectInfo->keepAliveSeconds ),
+ *pIndex );
+ pIndex++;
+ /* Verify the connect properties into the CONNECT packet. */
+ pIndex = MQTTV5_SerializeConnectProperties( pIndex, pConnectProperties );
+
+ if( pConnectProperties->pOutgoingUserProperty != NULL )
+ {
+ pIndex = serializeUserProperties( pIndex, pConnectProperties->pOutgoingUserProperty->userProperty, pConnectProperties->pOutgoingUserProperty->count );
+ }
+
+ if( pConnectProperties->pOutgoingAuth != NULL )
+ {
+ if( pConnectProperties->pOutgoingAuth->authMethodLength != 0 )
+ {
+ encodedStringLength = encodeString( encodedStringBuffer,
+ pConnectProperties->pOutgoingAuth->pAuthMethod,
+ pConnectProperties->pOutgoingAuth->authMethodLength );
+ TEST_ASSERT_EQUAL_MEMORY( encodedStringBuffer, pIndex, encodedStringLength );
+ pIndex += encodedStringLength;
+
+ if( pConnectProperties->pOutgoingAuth->authDataLength != 0 )
+ {
+ encodedStringLength = encodeString( encodedStringBuffer,
+ pConnectProperties->pOutgoingAuth->pAuthData,
+ pConnectProperties->pOutgoingAuth->authDataLength );
+ TEST_ASSERT_EQUAL_MEMORY( encodedStringBuffer, pIndex, encodedStringLength );
+ pIndex += encodedStringLength;
+ }
+ }
+ }
+
+ /* Verify the client identifier into the CONNECT packet. */
+ encodedStringLength = encodeString( encodedStringBuffer,
+ pConnectInfo->pClientIdentifier,
+ pConnectInfo->clientIdentifierLength );
+ TEST_ASSERT_EQUAL_MEMORY( encodedStringBuffer, pIndex, encodedStringLength );
+ pIndex += encodedStringLength;
+
+ /* Verify the will topic name and message into the CONNECT packet if provided. */
+ if( pWillInfo != NULL )
+ {
+ /*Will Properties*/
+ pIndex = MQTT_SerializePublishProperties( pWillInfo, pIndex );
+
+ if( pWillInfo->contentTypeLength != 0U )
+ {
+ TEST_ASSERT_EQUAL_INT( MQTT_CONTENT_TYPE_ID, *pIndex );
+ pIndex++;
+ encodedStringLength = encodeString( encodedStringBuffer,
+ pWillInfo->pContentType,
+ pWillInfo->contentTypeLength );
+ TEST_ASSERT_EQUAL_MEMORY( encodedStringBuffer, pIndex, encodedStringLength );
+ pIndex += encodedStringLength;
+ }
+
+ if( pWillInfo->responseTopicLength != 0U )
+ {
+ TEST_ASSERT_EQUAL_INT( MQTT_RESPONSE_TOPIC_ID, *pIndex );
+ pIndex++;
+ encodedStringLength = encodeString( encodedStringBuffer,
+ pWillInfo->pResponseTopic,
+ pWillInfo->responseTopicLength );
+ TEST_ASSERT_EQUAL_MEMORY( encodedStringBuffer, pIndex, encodedStringLength );
+ pIndex += encodedStringLength;
+ }
+
+ if( pWillInfo->correlationLength != 0U )
+ {
+ TEST_ASSERT_EQUAL_INT( MQTT_CORRELATION_DATA_ID, *pIndex );
+ pIndex++;
+ encodedStringLength = encodeString( encodedStringBuffer,
+ pWillInfo->pCorrelationData,
+ pWillInfo->correlationLength );
+ TEST_ASSERT_EQUAL_MEMORY( encodedStringBuffer, pIndex, encodedStringLength );
+ pIndex += encodedStringLength;
+ }
+
+ if( pWillInfo->pUserProperty != NULL )
+ {
+ pIndex = serializeUserProperties( pIndex, pWillInfo->pUserProperty->userProperty, pWillInfo->pUserProperty->count );
+ }
+
+ encodedStringLength = encodeString( encodedStringBuffer,
+ pWillInfo->pTopicName,
+ pWillInfo->topicNameLength );
+ TEST_ASSERT_EQUAL_MEMORY( encodedStringBuffer, pIndex, encodedStringLength );
+ pIndex += encodedStringLength;
+ encodedStringLength = encodeString( encodedStringBuffer,
+ pWillInfo->pPayload,
+ ( uint16_t ) pWillInfo->payloadLength );
+ TEST_ASSERT_EQUAL_MEMORY( encodedStringBuffer, pIndex, encodedStringLength );
+ pIndex += encodedStringLength;
+ }
+
+ /* Verify the user name if provided. */
+ if( pConnectInfo->pUserName != NULL )
+ {
+ encodedStringLength = encodeString( encodedStringBuffer,
+ pConnectInfo->pUserName,
+ pConnectInfo->userNameLength );
+ TEST_ASSERT_EQUAL_MEMORY( encodedStringBuffer, pIndex, encodedStringLength );
+ pIndex += encodedStringLength;
+ }
+
+ /* Verify the password if provided. */
+ if( pConnectInfo->pPassword != NULL )
+ {
+ encodedStringLength = encodeString( encodedStringBuffer,
+ pConnectInfo->pPassword,
+ pConnectInfo->passwordLength );
+ TEST_ASSERT_EQUAL_MEMORY( encodedStringBuffer, pIndex, encodedStringLength );
+ pIndex += encodedStringLength;
+ }
+}
+
+void test_MQTT_GetPublishPropertiesSize( void )
+{
+ MQTTStatus_t status = MQTTSuccess;
+
+ /* Call MQTT_GetPublishPropertiesSize() with various combinations of
+ * incorrect paramters */
+
+ /*
+ * Max Packet Size cannot be null
+ */
+
+ status = MQTT_GetPublishPropertiesSize( &publishInfo );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 0, publishInfo.propertyLength );
+
+ publishInfo.willDelay = 10;
+ status = MQTT_GetPublishPropertiesSize( &publishInfo );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 5, publishInfo.propertyLength );
+
+ publishInfo.payloadFormat = 1;
+ status = MQTT_GetPublishPropertiesSize( &publishInfo );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 7, publishInfo.propertyLength );
+
+ publishInfo.msgExpiryPresent = 1;
+ publishInfo.msgExpiryInterval = 10;
+ status = MQTT_GetPublishPropertiesSize( &publishInfo );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 12, publishInfo.propertyLength );
+
+ publishInfo.msgExpiryPresent = 1;
+ publishInfo.msgExpiryInterval = 10;
+ status = MQTT_GetPublishPropertiesSize( &publishInfo );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 12, publishInfo.propertyLength );
+
+ publishInfo.contentTypeLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ status = MQTT_GetPublishPropertiesSize( &publishInfo );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ publishInfo.pContentType = MQTT_TEST_UTF8_STRING;
+ status = MQTT_GetPublishPropertiesSize( &publishInfo );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 19, publishInfo.propertyLength );
+
+ publishInfo.responseTopicLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ status = MQTT_GetPublishPropertiesSize( &publishInfo );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ publishInfo.pResponseTopic = MQTT_TEST_UTF8_STRING;
+ status = MQTT_GetPublishPropertiesSize( &publishInfo );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 26, publishInfo.propertyLength );
+
+ publishInfo.correlationLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ status = MQTT_GetPublishPropertiesSize( &publishInfo );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ publishInfo.pCorrelationData = MQTT_TEST_UTF8_STRING;
+ status = MQTT_GetPublishPropertiesSize( &publishInfo );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 33, publishInfo.propertyLength );
+
+ MQTTUserProperties_t userProperties;
+ userProperties.userProperty[ 0 ].pKey = MQTT_TEST_UTF8_STRING;
+ userProperties.userProperty[ 0 ].keyLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ userProperties.userProperty[ 0 ].valueLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ userProperties.userProperty[ 0 ].pValue = MQTT_TEST_UTF8_STRING;
+ userProperties.userProperty[ 1 ].pKey = MQTT_TEST_UTF8_STRING;
+ userProperties.userProperty[ 1 ].keyLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ userProperties.userProperty[ 1 ].valueLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ userProperties.userProperty[ 1 ].pValue = MQTT_TEST_UTF8_STRING;
+ publishInfo.pUserProperty = &userProperties;
+ userProperties.count = 2;
+ status = MQTT_GetPublishPropertiesSize( &publishInfo );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 59, publishInfo.propertyLength );
+}
+
+void test_MQTTV5_SerializeConnectProperties( void )
+{
+ uint8_t properties[ 24U ];
+ uint8_t * pIndex = properties;
+ uint8_t * index = properties;
+ MQTTConnectProperties_t connect;
+
+ size_t propertyLength;
+
+ memset( &connect, 0x0, sizeof( connect ) );
+ connect.sessionExpiry = 12;
+ connect.receiveMax = 32;
+ connect.maxPacketSize = 56;
+ connect.topicAliasMax = 11;
+ connect.requestResponseInfo = 1;
+ connect.requestProblemInfo = 0;
+ connect.propertyLength = 20;
+ pIndex = MQTTV5_SerializeConnectProperties( pIndex, &connect );
+ TEST_ASSERT_EQUAL_INT( 21, ( pIndex - properties ) );
+ status = decodeVariableLength( properties, &propertyLength );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( connect.propertyLength, propertyLength );
+ index++;
+
+ TEST_ASSERT_EQUAL_INT( MQTT_SESSION_EXPIRY_ID, *index );
+ index++;
+ TEST_ASSERT_EQUAL_UINT32( connect.sessionExpiry, UINT32_DECODE( index ) );
+ index += 4;
+
+ TEST_ASSERT_EQUAL_INT( MQTT_RECEIVE_MAX_ID, *index );
+ index++;
+ TEST_ASSERT_EQUAL_UINT32( connect.receiveMax, UINT16_DECODE( index ) );
+ index += 2;
+
+ TEST_ASSERT_EQUAL_INT( MQTT_MAX_PACKET_SIZE_ID, *index );
+ index++;
+ TEST_ASSERT_EQUAL_INT( connect.maxPacketSize, UINT32_DECODE( index ) );
+ index += 4;
+
+ TEST_ASSERT_EQUAL_INT( MQTT_TOPIC_ALIAS_MAX_ID, *index );
+ index++;
+ TEST_ASSERT_EQUAL_INT( connect.topicAliasMax, UINT16_DECODE( index ) );
+ index += 2;
+
+ TEST_ASSERT_EQUAL_INT( MQTT_REQUEST_RESPONSE_ID, *index );
+ index++;
+ TEST_ASSERT_EQUAL_INT( connect.requestResponseInfo, *index );
+ index++;
+
+ TEST_ASSERT_EQUAL_INT( MQTT_REQUEST_PROBLEM_ID, *index );
+ index++;
+ TEST_ASSERT_EQUAL_INT( connect.requestProblemInfo, *index );
+ index++;
+}
+
+
+void test_MQTTV5_DeserializeConnackOnlyStatus( void )
+{
+ uint8_t buffer[ 50 ];
+ uint8_t * pIndex = buffer;
+ MQTTUserProperties_t incomingProperty;
+
+ properties.pIncomingUserProperty = &incomingProperty;
+ status = MQTTV5_DeserializeConnack( NULL, NULL, NULL );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ bool sessionPresent;
+ status = MQTTV5_DeserializeConnack( NULL, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ status = MQTTV5_DeserializeConnack( &properties, NULL, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, NULL );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ packetInfo.type = MQTT_PACKET_TYPE_CONNACK;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ packetInfo.pRemainingData = pIndex;
+ packetInfo.type = MQTT_PACKET_TYPE_CONNECT;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+ /*Reserved bit incorrect*/
+ buffer[ 0 ] = 0x11;
+ packetInfo.type = MQTT_PACKET_TYPE_CONNACK;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTBadResponse, status );
+
+ /*
+ * Session Present Bit is set but reason code is not equal to 0;
+ */
+ buffer[ 0 ] = 0x01;
+ buffer[ 1 ] = 0x01;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTBadResponse, status );
+
+ /* 5 + 1 + 2 = 8 */
+ size_t propertyLength = encodeRemainingLength( pIndex, 5 );
+ packetInfo.remainingLength = propertyLength + 7;
+ /*Not a valid reason code*/
+ buffer[ 0 ] = 0x00;
+ buffer[ 1 ] = 0x03;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTProtocolError, status );
+ /*All the valid response code*/
+ buffer[ 1 ] = 0x80;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x80;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x81;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x82;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x83;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x80;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x84;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x80;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x85;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x86;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x87;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x88;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x89;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x8A;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x8C;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x88;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x90;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x95;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x97;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x99;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x9A;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x9A;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x9B;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x9C;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x9D;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ buffer[ 1 ] = 0x9F;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTServerRefused, status );
+
+ /*Exceeds the max packet size set by the client*/
+ properties.maxPacketSize = 2;
+ buffer[ 1 ] = 0x00;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTProtocolError, status );
+
+ /*Validate the remaining length*/
+ properties.maxPacketSize = MQTT_MAX_PACKET_SIZE;
+ packetInfo.remainingLength = 7;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTMalformedPacket, status );
+
+ /*Invalid property length*/
+ packetInfo.remainingLength = 20;
+ pIndex = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndex, 20971556356235 );
+ LogDebug( ( "Encoded size for length is %lu bytes.",
+ ( unsigned long ) propertyLength ) );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status );
+
+ /*Invalid property length*/
+ pIndex = &buffer[ 2 ];
+ *pIndex = 0x81;
+ pIndex++;
+ *pIndex = 0x00;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status );
+
+ /*Incoming user property not initialized*/
+ properties.pIncomingUserProperty = NULL;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &sessionPresent );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+}
+
+void test_MQTTV5_DeserializeConnackOnlyuint_32( void )
+{
+ uint8_t buffer[ 200 ] = { 0 };
+ bool session = false;
+ uint8_t * pIndexLocal = initializeDeserialize( &packetInfo, buffer );
+ size_t propertyLength = encodeRemainingLength( pIndexLocal, 10 );
+
+ packetInfo.remainingLength = propertyLength + 12;
+ properties.maxPacketSize = 150;
+ properties.pIncomingUserProperty = &userProperties;
+ pIndexLocal++;
+ pIndexLocal = serializeuint_32( pIndexLocal, MQTT_SESSION_EXPIRY_ID );
+ pIndexLocal = serializeuint_32( pIndexLocal, MQTT_MAX_PACKET_SIZE_ID );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT32, properties.sessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT32, properties.serverMaxPacketSize );
+
+ /*Protocol error to include the same property twice*/
+ packetInfo.remainingLength = 13;
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 10 );
+ pIndexLocal++;
+ pIndexLocal = serializeuint_32( pIndexLocal, MQTT_SESSION_EXPIRY_ID );
+ pIndexLocal = serializeuint_32( pIndexLocal, MQTT_SESSION_EXPIRY_ID );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+
+ /*Invalid property length*/
+ packetInfo.remainingLength = 7;
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 4 );
+ pIndexLocal++;
+ *pIndexLocal = MQTT_SESSION_EXPIRY_ID;
+ pIndexLocal++;
+ pIndexLocal = serializeuint_32( pIndexLocal, MQTT_SESSION_EXPIRY_ID );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTMalformedPacket, status );
+
+ /*Invalid id*/
+ packetInfo.remainingLength = 8;
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 5 );
+ pIndexLocal++;
+ pIndexLocal = serializeuint_32( pIndexLocal, 0x00 );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+
+ /* Max packet size cannot have a value 0*/
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 5 );
+ packetInfo.remainingLength = propertyLength + 7;
+ pIndexLocal++;
+ *pIndexLocal = MQTT_MAX_PACKET_SIZE_ID;
+ pIndexLocal++;
+ pIndexLocal[ 0 ] = UINT32_BYTE3( 0 );
+ pIndexLocal[ 1 ] = UINT32_BYTE2( 0 );
+ pIndexLocal[ 2 ] = UINT32_BYTE1( 0 );
+ pIndexLocal[ 3 ] = UINT32_BYTE0( 0 );
+ pIndexLocal = &pIndexLocal[ 4 ];
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+}
+
+void test_MQTTV5_DeserializeConnackOnlyuint_16( void )
+{
+ uint8_t buffer[ 200 ] = { 0 };
+ uint8_t * pIndexLocal = buffer;
+
+ buffer[ 0 ] = 0x01;
+ buffer[ 1 ] = 0x00;
+ bool session = false;
+ packetInfo.pRemainingData = buffer;
+ packetInfo.type = MQTT_PACKET_TYPE_CONNACK;
+ pIndexLocal = &buffer[ 2 ];
+ properties.pIncomingUserProperty = &userProperties;
+ properties.maxPacketSize = MQTT_MAX_PACKET_SIZE;
+ size_t propertyLength = encodeRemainingLength( pIndexLocal, 9 );
+ packetInfo.remainingLength = propertyLength + 11;
+ pIndexLocal++;
+ pIndexLocal = serializeuint_16( pIndexLocal, MQTT_RECEIVE_MAX_ID );
+ pIndexLocal = serializeuint_16( pIndexLocal, MQTT_TOPIC_ALIAS_MAX_ID );
+ pIndexLocal = serializeuint_16( pIndexLocal, MQTT_SERVER_KEEP_ALIVE_ID );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT16, properties.serverReceiveMax );
+ TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT16, properties.serverTopicAliasMax );
+ TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT16, properties.serverKeepAlive );
+
+ /*Receive Max cannot have a value 0*/
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 3 );
+ packetInfo.remainingLength = propertyLength + 5;
+ pIndexLocal++;
+ *pIndexLocal = MQTT_RECEIVE_MAX_ID;
+ pIndexLocal++;
+ pIndexLocal[ 0 ] = UINT16_HIGH_BYTE( 0 );
+ pIndexLocal[ 1 ] = UINT16_LOW_BYTE( 0 );
+ pIndexLocal = &pIndexLocal[ 2 ];
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+
+ /*Protocol error to include the same property twice*/
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 6 );
+ packetInfo.remainingLength = propertyLength + 8;
+ pIndexLocal++;
+ pIndexLocal = serializeuint_16( pIndexLocal, MQTT_RECEIVE_MAX_ID );
+ pIndexLocal = serializeuint_16( pIndexLocal, MQTT_RECEIVE_MAX_ID );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+
+ /*Invalid property length*/
+ packetInfo.remainingLength = 5;
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 2 );
+ pIndexLocal++;
+ pIndexLocal = serializeuint_16( pIndexLocal, MQTT_RECEIVE_MAX_ID );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTMalformedPacket, status );
+}
+
+void test_MQTTV5_DeserializeConnackOnlyuint_8( void )
+{
+ uint8_t buffer[ 200 ] = { 0 };
+ uint8_t * pIndexLocal = buffer;
+
+ buffer[ 0 ] = 0x01;
+ buffer[ 1 ] = 0x00;
+ bool session = false;
+ packetInfo.pRemainingData = buffer;
+ packetInfo.type = MQTT_PACKET_TYPE_CONNACK;
+ packetInfo.remainingLength = 13;
+ pIndexLocal = &buffer[ 2 ];
+ properties.pIncomingUserProperty = &userProperties;
+ properties.maxPacketSize = MQTT_MAX_PACKET_SIZE;
+ size_t propertyLength = encodeRemainingLength( pIndexLocal, 10 );
+ pIndexLocal++;
+ pIndexLocal = serializeuint_8( pIndexLocal, MQTT_MAX_QOS_ID );
+ pIndexLocal = serializeuint_8( pIndexLocal, MQTT_RETAIN_AVAILABLE_ID );
+ pIndexLocal = serializeuint_8( pIndexLocal, MQTT_WILDCARD_ID );
+ pIndexLocal = serializeuint_8( pIndexLocal, MQTT_SHARED_SUB_ID );
+ pIndexLocal = serializeuint_8( pIndexLocal, MQTT_SUB_AVAILABLE_ID );
+
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT8, properties.serverMaxQos );
+ TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT8, properties.retainAvailable );
+ TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT8, properties.isWildcardAvaiable );
+ TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT8, properties.isSharedAvailable );
+ TEST_ASSERT_EQUAL_INT( MQTT_TEST_UINT8, properties.subscriptionId );
+
+ /*Protocol error to have a value other than 0 or 1*/
+ packetInfo.remainingLength = 5;
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 2 );
+ pIndexLocal++;
+ *pIndexLocal = MQTT_MAX_QOS_ID;
+ pIndexLocal++;
+ pIndexLocal[ 0 ] = 3;
+ pIndexLocal++;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+
+ /*Protocol error to include the same property twice*/
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 4 );
+ packetInfo.remainingLength = propertyLength + 6;
+ pIndexLocal++;
+ pIndexLocal = serializeuint_8( pIndexLocal, MQTT_MAX_QOS_ID );
+ pIndexLocal = serializeuint_8( pIndexLocal, MQTT_MAX_QOS_ID );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+
+ /*Invalid property length*/
+ packetInfo.remainingLength = 4;
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 1 );
+ pIndexLocal++;
+ *pIndexLocal = MQTT_MAX_QOS_ID;
+ pIndexLocal++;
+ pIndexLocal[ 0 ] = 0;
+ pIndexLocal++;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTMalformedPacket, status );
+}
+
+
+void test_MQTTV5_DeserializeConnackOnlyutf_8( void )
+{
+ uint8_t buffer[ 200 ] = { 0 };
+ uint8_t * pIndexLocal = buffer;
+
+ buffer[ 0 ] = 0x01;
+ buffer[ 1 ] = 0x00;
+
+ bool session = false;
+ packetInfo.pRemainingData = buffer;
+ packetInfo.type = MQTT_PACKET_TYPE_CONNACK;
+ pIndexLocal = &buffer[ 2 ];
+ properties.pIncomingUserProperty = &userProperties;
+ properties.requestResponseInfo = 1;
+ properties.maxPacketSize = MQTT_MAX_PACKET_SIZE;
+ size_t propertyLength = encodeRemainingLength( pIndexLocal, 28 );
+ packetInfo.remainingLength = propertyLength + 28 + 2;
+ pIndexLocal++;
+ pIndexLocal = serializeutf_8( pIndexLocal, MQTT_ASSIGNED_CLIENT_ID );
+ pIndexLocal = serializeutf_8( pIndexLocal, MQTT_REASON_STRING_ID );
+ pIndexLocal = serializeutf_8( pIndexLocal, MQTT_RESPONSE_INFO_ID );
+ pIndexLocal = serializeutf_8( pIndexLocal, MQTT_SERVER_REF_ID );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+
+ /*Protocol error to include the same property twice*/
+ packetInfo.remainingLength = 17;
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 14 );
+ pIndexLocal++;
+ pIndexLocal = serializeutf_8( pIndexLocal, MQTT_ASSIGNED_CLIENT_ID );
+ pIndexLocal = serializeutf_8( pIndexLocal, MQTT_ASSIGNED_CLIENT_ID );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+
+ /*Invalid property length*/
+ packetInfo.remainingLength = 7;
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 4 );
+ pIndexLocal++;
+ pIndexLocal = serializeutf_8( pIndexLocal, MQTT_ASSIGNED_CLIENT_ID );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTMalformedPacket, status );
+
+ /*Invalid property length*/
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 2 );
+ packetInfo.remainingLength = propertyLength + 4;
+ pIndexLocal++;
+ serializeutf_8( pIndexLocal, MQTT_ASSIGNED_CLIENT_ID );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTMalformedPacket, status );
+
+ /*Protocol error to include response information if is is set to false by client*/
+ properties.requestResponseInfo = 0;
+ packetInfo.remainingLength = 10;
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 7 );
+ pIndexLocal++;
+ pIndexLocal = serializeutf_8( pIndexLocal, MQTT_RESPONSE_INFO_ID );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+}
+
+
+void test_MQTTV5_DeserializeConnackOnlyUserProperty( void )
+{
+ properties.pIncomingUserProperty = &userProperties;
+ uint8_t buffer[ 70000 ] = { 0 };
+ uint8_t * pIndexLocal = buffer;
+ buffer[ 0 ] = 0x01;
+ buffer[ 1 ] = 0x00;
+
+ bool session = false;
+ properties.maxPacketSize = MQTT_MAX_PACKET_SIZE;
+ packetInfo.pRemainingData = buffer;
+ packetInfo.type = MQTT_PACKET_TYPE_CONNACK;
+ packetInfo.remainingLength = 16;
+ pIndexLocal = &buffer[ 2 ];
+ size_t propertyLength = encodeRemainingLength( pIndexLocal, 13 );
+ pIndexLocal++;
+ pIndexLocal = serializeutf_8pair( pIndexLocal );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL( 1, properties.pIncomingUserProperty->count );
+ TEST_ASSERT_EQUAL( MQTT_TEST_UTF8_STRING_LENGTH, ( properties.pIncomingUserProperty->userProperty[ 0 ].valueLength ) );
+ TEST_ASSERT_EQUAL( MQTT_TEST_UTF8_STRING_LENGTH, ( properties.pIncomingUserProperty->userProperty[ 0 ].keyLength ) );
+
+ /*Invalid property length*/
+ packetInfo.remainingLength = 5;
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 2 );
+ pIndexLocal++;
+ pIndexLocal = serializeutf_8pair( pIndexLocal );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTMalformedPacket, status );
+
+ /*Invalid property length*/
+ packetInfo.remainingLength = 6;
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 3 );
+ pIndexLocal++;
+ pIndexLocal = serializeutf_8pair( pIndexLocal );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTMalformedPacket, status );
+
+ /*Invalid property length*/
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 8 );
+ packetInfo.remainingLength = propertyLength + 10;
+ pIndexLocal++;
+ pIndexLocal = serializeutf_8pair( pIndexLocal );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTMalformedPacket, status );
+
+ /*Invalid property length*/
+ packetInfo.remainingLength = 15;
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 12 );
+ pIndexLocal++;
+ pIndexLocal = serializeutf_8pair( pIndexLocal );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTMalformedPacket, status );
+
+ /*Discard user property*/
+ packetInfo.remainingLength = 65018;
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 65013 );
+ pIndexLocal += 3;
+ uint32_t i = 0U;
+
+ for( ; i < 5001; i++ )
+ {
+ pIndexLocal = serializeutf_8pair( pIndexLocal );
+ }
+
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+
+ properties.pIncomingUserProperty->count = 0;
+ packetInfo.remainingLength = 65017;
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 65012 );
+ pIndexLocal += 3;
+
+ for( ; i < 5001; i++ )
+ {
+ pIndexLocal = serializeutf_8pair( pIndexLocal );
+ }
+
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTMalformedPacket, status );
+}
+
+
+
+void test_MQTTV5_DeserializeConnackOnlyAuthInfo( void )
+{
+ MQTTAuthInfo_t auth;
+ MQTTAuthInfo_t auth1;
+
+ properties.pIncomingAuth = &auth;
+ properties.pOutgoingAuth = &auth1;
+ uint8_t buffer[ 200 ] = { 0 };
+ uint8_t * pIndexLocal = buffer;
+ buffer[ 0 ] = 0x01;
+ buffer[ 1 ] = 0x00;
+
+ bool session = false;
+ packetInfo.pRemainingData = buffer;
+ packetInfo.type = MQTT_PACKET_TYPE_CONNACK;
+ packetInfo.remainingLength = 17;
+ pIndexLocal = &buffer[ 2 ];
+ properties.pIncomingUserProperty = &userProperties;
+ properties.maxPacketSize = MQTT_MAX_PACKET_SIZE;
+ size_t propertyLength = encodeRemainingLength( pIndexLocal, 14 );
+ packetInfo.remainingLength = propertyLength + 14 + 2;
+ pIndexLocal++;
+ pIndexLocal = serializeutf_8( pIndexLocal, MQTT_AUTH_METHOD_ID );
+ pIndexLocal = serializeutf_8( pIndexLocal, MQTT_AUTH_DATA_ID );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL( MQTT_TEST_UTF8_STRING_LENGTH, properties.pIncomingAuth->authMethodLength );
+ TEST_ASSERT_EQUAL( MQTT_TEST_UTF8_STRING_LENGTH, properties.pIncomingAuth->authDataLength );
+
+
+ /*Outgoing auth is null*/
+ properties.pOutgoingAuth = NULL;
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+}
+
+
+void test_MQTTV5_GetConnectPacketSize( void )
+{
+ size_t remainingLength = 0;
+ size_t packetSize = 0;
+ MQTTUserProperties_t incomingProperty;
+
+
+ /* Call MQTTV5_GetConnectPacketSize() with various combinations of
+ * incorrect paramters */
+ properties.receiveMax = 65535U;
+ properties.maxPacketSize = MQTT_MAX_PACKET_SIZE;
+ properties.requestProblemInfo = 1;
+ properties.pIncomingUserProperty = &incomingProperty;
+ status = MQTTV5_GetConnectPacketSize( NULL, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, NULL, &packetSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, NULL );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /* Verify empty connect info fails. */
+ memset( &connectInfo, 0x0, sizeof( connectInfo ) );
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /* Verify empty client identifier fails. */
+ connectInfo.pClientIdentifier = CLIENT_IDENTIFIER;
+ connectInfo.clientIdentifierLength = 0;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ connectInfo.pClientIdentifier = NULL;
+ connectInfo.clientIdentifierLength = CLIENT_IDENTIFIER_LENGTH;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, NULL, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /* Test a will message payload length that is too large. */
+ connectInfo.pClientIdentifier = CLIENT_IDENTIFIER;
+ connectInfo.clientIdentifierLength = UINT16_MAX;
+ connectInfo.pPassword = "";
+ connectInfo.passwordLength = UINT16_MAX;
+ connectInfo.pUserName = "";
+ connectInfo.userNameLength = UINT16_MAX;
+ publishInfo.pTopicName = TEST_TOPIC_NAME;
+ publishInfo.topicNameLength = UINT16_MAX;
+ /* A valid will message payload is less than the maximum 16 bit integer. */
+ publishInfo.payloadLength = UINT16_MAX + 2;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ /* Verify good case */
+ memset( &connectInfo, 0x0, sizeof( connectInfo ) );
+ connectInfo.cleanSession = true;
+ connectInfo.pClientIdentifier = "TEST";
+ connectInfo.clientIdentifierLength = 4;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_INT( 0, properties.propertyLength );
+ /* Make sure remaining size returned is 17. */
+ TEST_ASSERT_EQUAL_INT( 17, remainingLength );
+ /* Make sure packet size is 19. */
+ TEST_ASSERT_EQUAL_INT( 19, packetSize );
+
+ /* With will. These parameters will cause the packet to be
+ * 4 + 2 + 8 + 2 = 16 bytes larger. */
+ publishInfo.pTopicName = "test";
+ publishInfo.topicNameLength = 4;
+ publishInfo.pPayload = "testload";
+ publishInfo.payloadLength = 8;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ /* Make sure remaining size returned is 32 = 16 + 16 + 2. */
+ TEST_ASSERT_EQUAL_INT( 34, remainingLength );
+ /* Make sure packet size is 34 = 18 + 16 + 2. */
+ TEST_ASSERT_EQUAL_INT( 36, packetSize );
+
+ /* With username and password. This will add 4 + 2 + 4 + 2 = 12 bytes. */
+ connectInfo.cleanSession = true;
+ connectInfo.pUserName = "USER";
+ connectInfo.userNameLength = 4;
+ connectInfo.pPassword = "PASS";
+ connectInfo.passwordLength = 4;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ /* Make sure remaining size returned is 28 = 16 + 12. */
+ TEST_ASSERT_EQUAL_INT( 29, remainingLength );
+ /* Make sure packet size is 30 = 18 + 12. */
+ TEST_ASSERT_EQUAL_INT( 31, packetSize );
+
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, NULL, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /*Validating the function Get Connect properties*/
+
+ /* Call MQTT_GetConnectPropertiesSize() with various combinations of
+ * incorrect paramters */
+
+ /*
+ * Max Packet Size cannot be null
+ */
+ properties.receiveMax = 24;
+ properties.maxPacketSize = 0;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ /*
+ * Receive Maximum cannot be 0
+ */
+ properties.maxPacketSize = 40;
+ properties.receiveMax = 0;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ properties.receiveMax = 24;
+ properties.requestProblemInfo = 1;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 8, properties.propertyLength );
+
+ properties.sessionExpiry = 24;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 13, properties.propertyLength );
+
+ properties.topicAliasMax = 24;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 16, properties.propertyLength );
+
+ properties.requestResponseInfo = 1;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 18, properties.propertyLength );
+
+ properties.requestProblemInfo = 0;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 20, properties.propertyLength );
+
+ MQTTUserProperties_t userProperties;
+ memset( &userProperties, 0x0, sizeof( userProperties ) );
+
+ userProperties.count = 1;
+ properties.pOutgoingUserProperty = &userProperties;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ userProperties.userProperty[ 0 ].keyLength = 3;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ userProperties.userProperty[ 0 ].valueLength = 1;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ userProperties.userProperty[ 0 ].pValue = "1";
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ userProperties.userProperty[ 0 ].pKey = "2";
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+
+ userProperties.userProperty[ 0 ].pValue = NULL;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ userProperties.userProperty[ 0 ].keyLength = 0;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ userProperties.userProperty[ 0 ].valueLength = 0;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ userProperties.userProperty[ 0 ].keyLength = 1;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ userProperties.userProperty[ 0 ].valueLength = 1;
+ userProperties.userProperty[ 0 ].keyLength = 0;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ properties.pOutgoingUserProperty->count = 6000;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+ properties.pOutgoingUserProperty->count = 1;
+
+ /*
+ * Incoming AuthInfo not intialized.
+ */
+ properties.pOutgoingUserProperty = &userProperties;
+ MQTTAuthInfo_t auth;
+ memset( &auth, 0x0, sizeof( auth ) );
+ properties.pOutgoingAuth = &auth;
+ auth.authDataLength = 1;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ MQTTAuthInfo_t auth2;
+ memset( &auth2, 0x0, sizeof( auth2 ) );
+ properties.pIncomingAuth = &auth2;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ auth.authDataLength = 0;
+ auth.authMethodLength = 0;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ /*
+ * Protocol Error to include Authentication Data if there is no Authentication Method
+ */
+ auth.pAuthData = "1";
+ auth.authDataLength = 1;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ auth.authMethodLength = 3;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ auth.pAuthMethod = "234";
+ auth.authDataLength = 0;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ auth.pAuthData = NULL;
+ auth.authDataLength = 1;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ /*Connect Properties*/
+ memset( &properties, 0x0, sizeof( properties ) );
+ properties.sessionExpiry = 22;
+ properties.receiveMax = 34;
+ properties.maxPacketSize = 32;
+ properties.topicAliasMax = 12;
+ properties.requestResponseInfo = 1;
+ properties.requestProblemInfo = 0;
+ userProperties.userProperty[ 0 ].keyLength = 3;
+ userProperties.userProperty[ 0 ].valueLength = 1;
+ userProperties.userProperty[ 0 ].pValue = "1";
+ userProperties.userProperty[ 0 ].pKey = "211";
+ userProperties.count = 1;
+ properties.pOutgoingUserProperty = &userProperties;
+ properties.pIncomingUserProperty = &incomingProperty;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ /* Make sure remaining size returned is 58. */
+ TEST_ASSERT_EQUAL_INT( 58, remainingLength );
+ /* Make sure packet size is 60. */
+ TEST_ASSERT_EQUAL_INT( 60, packetSize );
+
+ /*Validating the will properties*/
+ memset( &publishInfo, 0x0, sizeof( publishInfo ) );
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 0, publishInfo.propertyLength );
+
+ publishInfo.willDelay = 10;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 5, publishInfo.propertyLength );
+
+ publishInfo.payloadFormat = 1;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 7, publishInfo.propertyLength );
+
+ publishInfo.msgExpiryPresent = 1;
+ publishInfo.msgExpiryInterval = 10;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 12, publishInfo.propertyLength );
+
+ publishInfo.contentTypeLength = 2;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ publishInfo.pContentType = MQTT_TEST_UTF8_STRING;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 17, publishInfo.propertyLength );
+
+ publishInfo.responseTopicLength = 2;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ publishInfo.pResponseTopic = MQTT_TEST_UTF8_STRING;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 22, publishInfo.propertyLength );
+
+ publishInfo.correlationLength = 2;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ publishInfo.pCorrelationData = MQTT_TEST_UTF8_STRING;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 27, publishInfo.propertyLength );
+
+ MQTTUserProperties_t userProperties1;
+ userProperties1.userProperty[ 0 ].pKey = "2";
+ userProperties1.userProperty[ 0 ].keyLength = 1;
+ userProperties1.userProperty[ 0 ].valueLength = 3;
+ userProperties1.userProperty[ 0 ].pValue = "abc";
+ userProperties1.userProperty[ 1 ].pKey = "2";
+ userProperties1.userProperty[ 1 ].keyLength = 1;
+ userProperties1.userProperty[ 1 ].valueLength = 2;
+ userProperties1.userProperty[ 1 ].pValue = MQTT_TEST_UTF8_STRING;
+ userProperties1.count = 2;
+ publishInfo.pUserProperty = &userProperties1;
+ status = MQTT_GetPublishPropertiesSize( &publishInfo );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_size_t( 44, publishInfo.propertyLength );
+
+ publishInfo.payloadFormat = 1;
+ publishInfo.msgExpiryPresent = 1;
+ publishInfo.msgExpiryInterval = 10;
+ publishInfo.msgExpiryPresent = 1;
+ publishInfo.msgExpiryInterval = 10;
+ publishInfo.contentTypeLength = 2;
+ publishInfo.pContentType = MQTT_TEST_UTF8_STRING;
+ publishInfo.responseTopicLength = 2;
+ publishInfo.pResponseTopic = MQTT_TEST_UTF8_STRING;
+ publishInfo.correlationLength = 2;
+ publishInfo.pCorrelationData = MQTT_TEST_UTF8_STRING;
+ publishInfo.willDelay = 3;
+ /* 34 + 12 + 29 */
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ /* Make sure remaining size returned is 107. */
+ TEST_ASSERT_EQUAL_INT( 107, remainingLength );
+ /* Make sure packet size is 109. */
+ TEST_ASSERT_EQUAL_INT( 109, packetSize );
+ /*Limit of property length*/
+ memset( &properties, 0x0, sizeof( properties ) );
+ memset( &publishInfo, 0x0, sizeof( publishInfo ) );
+ /*5*/
+ properties.receiveMax = UINT16_MAX;
+ properties.maxPacketSize = MQTT_MAX_PACKET_SIZE;
+ properties.requestProblemInfo = 1;
+ userProperties.count = 2078;
+ properties.pOutgoingUserProperty = &userProperties;
+ properties.pIncomingUserProperty = &incomingProperty;
+ uint16_t i = 0;
+ char str[ 65535 ];
+ memset( str, '.', 65535 * sizeof( char ) );
+
+ for( ; i < 3500; i++ )
+ {
+ userProperties.userProperty[ i ].keyLength = UINT16_MAX;
+ userProperties.userProperty[ i ].pKey = str;
+ userProperties.userProperty[ i ].pValue = str;
+ userProperties.userProperty[ i ].valueLength = UINT16_MAX;
+ }
+
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ properties.pOutgoingUserProperty = NULL;
+ userProperties.count = 2048;
+ publishInfo.pUserProperty = &userProperties;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ userProperties.count = 2051;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ /*Incoming user property not initialized*/
+ properties.pIncomingUserProperty = NULL;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+}
+
+/* / ** */
+/* * @brief Tests that MQTTV5_SerializeConnect works as intended. */
+/* * / */
+void test_MQTTV5_SerializeConnect( void )
+{
+ size_t remainingLength = 0;
+ uint8_t buffer[ 140 + 2 * BUFFER_PADDING_LENGTH ];
+ size_t bufferSize = sizeof( buffer ) - 2 * BUFFER_PADDING_LENGTH;
+ size_t packetSize = bufferSize;
+ MQTTStatus_t status = MQTTSuccess;
+ MQTTUserProperties_t incomingProperty;
+
+ properties.pIncomingUserProperty = &incomingProperty;
+ setupProperties( &properties );
+ MQTTFixedBuffer_t fixedBuffer = { .pBuffer = &buffer[ BUFFER_PADDING_LENGTH ], .size = bufferSize };
+
+ /* Verify bad parameter errors. */
+ status = MQTTV5_SerializeConnect( NULL, &publishInfo, &properties, remainingLength, &fixedBuffer );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ status = MQTTV5_SerializeConnect( &connectInfo, &publishInfo, &properties, remainingLength, NULL );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ status = MQTTV5_SerializeConnect( &connectInfo, &publishInfo, NULL, remainingLength, &fixedBuffer );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ memset( &connectInfo, 0x0, sizeof( connectInfo ) );
+ status = MQTTV5_SerializeConnect( &connectInfo, NULL, &properties, 112345, &fixedBuffer );
+ TEST_ASSERT_EQUAL_INT( MQTTNoMemory, status );
+
+ /* Create a good connection info. */
+ connectInfo.pClientIdentifier = "TEST";
+ connectInfo.clientIdentifierLength = 4;
+
+ /* Inject a invalid fixed buffer test with a good connectInfo. */
+ memset( &fixedBuffer, 0x0, sizeof( fixedBuffer ) );
+ status = MQTTV5_SerializeConnect( &connectInfo, NULL, &properties, remainingLength, &fixedBuffer );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /* Good case succeeds. */
+ /* Set the fixedBuffer properly for the rest of the succeeding test. */
+ fixedBuffer.pBuffer = &buffer[ BUFFER_PADDING_LENGTH ];
+ fixedBuffer.size = bufferSize;
+
+ /* Calculate a good packet size. */
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ /* Make sure buffer has enough space */
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
+ /* Make sure test succeeds. */
+ padAndResetBuffer( buffer, sizeof( buffer ) );
+ status = MQTTV5_SerializeConnect( &connectInfo, NULL, &properties, remainingLength, &fixedBuffer );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ checkBufferOverflow( buffer, sizeof( buffer ) );
+
+ /* Encode user name. Also try clean session. */
+ connectInfo.cleanSession = true;
+ connectInfo.pUserName = "USER";
+ connectInfo.userNameLength = 4;
+ connectInfo.pPassword = "PASS";
+ connectInfo.passwordLength = 4;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
+ padAndResetBuffer( buffer, sizeof( buffer ) );
+ status = MQTTV5_SerializeConnect( &connectInfo, NULL, &properties, remainingLength, &fixedBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ checkBufferOverflow( buffer, sizeof( buffer ) );
+
+ /* Serialize connect with LWT. */
+ /* Test for NULL topic name. */
+ ( void ) memset( &publishInfo, 0x00, sizeof( MQTTPublishInfo_t ) );
+ publishInfo.retain = true;
+ publishInfo.qos = MQTTQoS1;
+ publishInfo.pPayload = "test";
+ publishInfo.payloadLength = ( uint16_t ) strlen( publishInfo.pPayload );
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
+ status = MQTTV5_SerializeConnect( &connectInfo, &publishInfo, &properties, remainingLength, &fixedBuffer );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ /* Success. */
+ ( void ) memset( &publishInfo, 0x00, sizeof( MQTTPublishInfo_t ) );
+ publishInfo.retain = true;
+ publishInfo.qos = MQTTQoS1;
+ publishInfo.pTopicName = "test";
+ publishInfo.topicNameLength = ( uint16_t ) strlen( publishInfo.pTopicName );
+ publishInfo.pPayload = "test";
+ publishInfo.payloadLength = ( uint16_t ) strlen( publishInfo.pPayload );
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
+ padAndResetBuffer( buffer, sizeof( buffer ) );
+ status = MQTTV5_SerializeConnect( &connectInfo, &publishInfo, &properties, remainingLength, &fixedBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ checkBufferOverflow( buffer, sizeof( buffer ) );
+
+ /* Again with QoS 2 and 0. */
+
+ publishInfo.qos = MQTTQoS2;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
+ padAndResetBuffer( buffer, sizeof( buffer ) );
+ status = MQTTV5_SerializeConnect( &connectInfo, &publishInfo, &properties, remainingLength, &fixedBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ checkBufferOverflow( buffer, sizeof( buffer ) );
+
+ publishInfo.qos = MQTTQoS0;
+ publishInfo.retain = false;
+ /* NULL payload is acceptable. */
+ publishInfo.pPayload = NULL;
+ publishInfo.payloadLength = 0;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
+ padAndResetBuffer( buffer, sizeof( buffer ) );
+ status = MQTTV5_SerializeConnect( &connectInfo, &publishInfo, &properties, remainingLength, &fixedBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ checkBufferOverflow( buffer, sizeof( buffer ) );
+
+ /* Success right on the buffer boundary. */
+ connectInfo.pUserName = "USER";
+ connectInfo.userNameLength = 4;
+ /* Throwing in a possible valid zero length password. */
+ connectInfo.pPassword = "PASS";
+ connectInfo.passwordLength = 0;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
+ /* Set the fixed buffer to exactly the size of the packet. */
+ fixedBuffer.size = packetSize;
+ padAndResetBuffer( buffer, sizeof( buffer ) );
+ status = MQTTV5_SerializeConnect( &connectInfo, NULL, &properties, remainingLength, &fixedBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ checkBufferOverflow( buffer, sizeof( buffer ) );
+ /*Connect properties not initialized*/
+ status = MQTTV5_SerializeConnect( &connectInfo, &publishInfo, NULL, remainingLength, &fixedBuffer );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /*Connect Properties*/
+ properties.sessionExpiry = 22;
+ properties.receiveMax = 34;
+ properties.maxPacketSize = 32;
+ properties.topicAliasMax = 12;
+ properties.requestResponseInfo = 1;
+ properties.requestProblemInfo = 0;
+ MQTTAuthInfo_t auth;
+ memset( &auth, 0x0, sizeof( auth ) );
+ auth.pAuthMethod = MQTT_TEST_UTF8_STRING;
+ auth.authMethodLength = 2;
+ auth.pAuthData = "abc";
+ auth.authDataLength = 3;
+ userProperties.userProperty[ 0 ].keyLength = 3;
+ userProperties.userProperty[ 0 ].valueLength = 1;
+ userProperties.userProperty[ 0 ].pValue = "1";
+ userProperties.userProperty[ 0 ].pKey = "211";
+ userProperties.count = 1;
+ properties.pOutgoingUserProperty = &userProperties;
+ properties.pOutgoingAuth = &auth;
+ properties.pIncomingAuth = &auth;
+ /* 29 */
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
+ /* Set the fixed buffer to exactly the size of the packet. */
+ fixedBuffer.size = packetSize;
+ padAndResetBuffer( buffer, sizeof( buffer ) );
+ status = MQTTV5_SerializeConnect( &connectInfo, NULL, &properties, remainingLength, &fixedBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ checkBufferOverflow( buffer, sizeof( buffer ) );
+
+ auth.authDataLength = 0;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
+ /* Set the fixed buffer to exactly the size of the packet. */
+ fixedBuffer.size = packetSize;
+ padAndResetBuffer( buffer, sizeof( buffer ) );
+ status = MQTTV5_SerializeConnect( &connectInfo, NULL, &properties, remainingLength, &fixedBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ checkBufferOverflow( buffer, sizeof( buffer ) );
+
+ properties.pOutgoingAuth = NULL;
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, NULL, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
+ /* Set the fixed buffer to exactly the size of the packet. */
+ fixedBuffer.size = packetSize;
+ padAndResetBuffer( buffer, sizeof( buffer ) );
+ status = MQTTV5_SerializeConnect( &connectInfo, NULL, &properties, remainingLength, &fixedBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ checkBufferOverflow( buffer, sizeof( buffer ) );
+
+ /*Will Properties*/
+ publishInfo.payloadFormat = 1;
+ publishInfo.msgExpiryPresent = 1;
+ publishInfo.msgExpiryInterval = 10;
+ publishInfo.msgExpiryPresent = 1;
+ publishInfo.msgExpiryInterval = 10;
+ publishInfo.contentTypeLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ publishInfo.pContentType = MQTT_TEST_UTF8_STRING;
+ publishInfo.responseTopicLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ publishInfo.pResponseTopic = MQTT_TEST_UTF8_STRING;
+ publishInfo.correlationLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ publishInfo.pCorrelationData = MQTT_TEST_UTF8_STRING;
+ publishInfo.willDelay = 3;
+ publishInfo.pUserProperty = &userProperties;
+ /* 27 */
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
+ /* Set the fixed buffer to exactly the size of the packet. */
+ fixedBuffer.size = packetSize;
+ padAndResetBuffer( buffer, sizeof( buffer ) );
+ status = MQTTV5_SerializeConnect( &connectInfo, &publishInfo, &properties, remainingLength, &fixedBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ checkBufferOverflow( buffer, sizeof( buffer ) );
+}
+
+/**
+ * @brief This method calls MQTT_SerializeConnect successfully using different parameters
+ * until we have full coverage on the private method, serializeConnectPacket(...).
+ */
+void test_MQTTV5_SerializeConnect_Happy_Paths()
+{
+ MQTTStatus_t mqttStatus = MQTTSuccess;
+ size_t remainingLength = 0;
+ size_t packetSize = 0;
+ MQTTFixedBuffer_t networkBuffer;
+ MQTTUserProperties_t incomingProperty;
+
+ properties.sessionExpiry = 22;
+ properties.receiveMax = 34;
+ properties.maxPacketSize = 32;
+ properties.topicAliasMax = 12;
+ properties.requestResponseInfo = 1;
+ properties.requestProblemInfo = 0;
+ properties.pIncomingUserProperty = &incomingProperty;
+ publishInfo.payloadFormat = 1;
+ publishInfo.msgExpiryPresent = 1;
+ publishInfo.msgExpiryInterval = 10;
+ publishInfo.msgExpiryPresent = 1;
+ publishInfo.msgExpiryInterval = 10;
+ publishInfo.contentTypeLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ publishInfo.pContentType = MQTT_TEST_UTF8_STRING;
+ publishInfo.responseTopicLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ publishInfo.pResponseTopic = MQTT_TEST_UTF8_STRING;
+ publishInfo.correlationLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ publishInfo.pCorrelationData = MQTT_TEST_UTF8_STRING;
+ publishInfo.willDelay = 3;
+ /* Fill structs to pass into methods to be tested. */
+ setupNetworkBuffer( &networkBuffer );
+ setupConnectInfo( &connectInfo );
+ setupPublishInfo( &publishInfo );
+ publishInfo.dup = true;
+ publishInfo.retain = true;
+ /* Get MQTT connect packet size and remaining length. */
+ mqttStatus = MQTTV5_GetConnectPacketSize( &connectInfo,
+ &publishInfo,
+ &properties,
+ &remainingLength,
+ &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
+ /* Make sure buffer has enough space. */
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, networkBuffer.size );
+ mqttStatus = MQTTV5_SerializeConnect( &connectInfo, &publishInfo, &properties,
+ remainingLength, &networkBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
+ verifySerializedConnectPacket( &connectInfo, &publishInfo, &properties,
+ remainingLength, &networkBuffer );
+
+ /* / * Repeat with MQTTQoS1. * / */
+ publishInfo.qos = MQTTQoS1;
+ mqttStatus = MQTTV5_GetConnectPacketSize( &connectInfo,
+ &publishInfo,
+ &properties,
+ &remainingLength,
+ &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
+ /* Make sure buffer has enough space. */
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, networkBuffer.size );
+ mqttStatus = MQTTV5_SerializeConnect( &connectInfo, &publishInfo, &properties,
+ remainingLength, &networkBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
+ verifySerializedConnectPacket( &connectInfo, &publishInfo, &properties,
+ remainingLength, &networkBuffer );
+
+ /* Re-initialize objects for branch coverage. */
+ publishInfo.pPayload = MQTT_SAMPLE_PAYLOAD;
+ publishInfo.payloadLength = MQTT_SAMPLE_PAYLOAD_LEN;
+ publishInfo.pTopicName = MQTT_CLIENT_IDENTIFIER;
+ publishInfo.topicNameLength = MQTT_CLIENT_IDENTIFIER_LEN;
+ publishInfo.dup = true;
+ publishInfo.qos = MQTTQoS2;
+ publishInfo.retain = false;
+ connectInfo.cleanSession = false;
+ connectInfo.pClientIdentifier = MQTT_CLIENT_IDENTIFIER;
+ connectInfo.clientIdentifierLength = MQTT_CLIENT_IDENTIFIER_LEN;
+ connectInfo.pUserName = NULL;
+ connectInfo.userNameLength = 0;
+ connectInfo.pPassword = NULL;
+ connectInfo.passwordLength = 0;
+
+ mqttStatus = MQTTV5_GetConnectPacketSize( &connectInfo,
+ &publishInfo,
+ &properties,
+ &remainingLength,
+ &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
+ /* Make sure buffer has enough space. */
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, networkBuffer.size );
+ mqttStatus = MQTTV5_SerializeConnect( &connectInfo, &publishInfo, &properties,
+ remainingLength, &networkBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
+ verifySerializedConnectPacket( &connectInfo, &publishInfo, &properties,
+ remainingLength, &networkBuffer );
+
+ /* Repeat with NULL publishInfo. */
+ mqttStatus = MQTTV5_GetConnectPacketSize( &connectInfo,
+ NULL,
+ &properties,
+ &remainingLength,
+ &packetSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
+ /* Make sure buffer has enough space. */
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, networkBuffer.size );
+ mqttStatus = MQTTV5_SerializeConnect( &connectInfo, NULL, &properties,
+ remainingLength, &networkBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, mqttStatus );
+ verifySerializedConnectPacket( &connectInfo, NULL, &properties,
+ remainingLength, &networkBuffer );
+}
+
+
+void test_RemaininglengthLimit( void )
+{
+ /* Test will property length more than the max value allowed. */
+ size_t remainingLength = 0;
+ size_t packetSize = 0;
+ uint32_t maxPacketSize;
+ MQTTStatus_t status = MQTTSuccess;
+ MQTTUserProperties_t incomingProperty;
+ MQTTAckInfo_t ackInfo;
+
+ connectInfo.pClientIdentifier = CLIENT_IDENTIFIER;
+ connectInfo.clientIdentifierLength = UINT16_MAX;
+ connectInfo.pPassword = "";
+ connectInfo.passwordLength = UINT16_MAX;
+ connectInfo.pUserName = "";
+ properties.receiveMax = UINT16_MAX;
+ properties.requestProblemInfo = 1;
+ properties.pIncomingUserProperty = &incomingProperty;
+ publishInfo.payloadFormat = 1;
+ publishInfo.msgExpiryPresent = 1;
+ publishInfo.msgExpiryInterval = 10;
+ publishInfo.contentTypeLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ publishInfo.pContentType = MQTT_TEST_UTF8_STRING;
+ publishInfo.responseTopicLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ publishInfo.pResponseTopic = MQTT_TEST_UTF8_STRING;
+ publishInfo.correlationLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ publishInfo.pCorrelationData = MQTT_TEST_UTF8_STRING;
+ publishInfo.willDelay = 3;
+ uint16_t i = 0;
+ char str[ 65535 ];
+ memset( str, '.', 65535 * sizeof( char ) );
+
+ for( ; i < 3500; i++ )
+ {
+ userProperties.userProperty[ i ].keyLength = UINT16_MAX;
+ userProperties.userProperty[ i ].pKey = str;
+ userProperties.userProperty[ i ].pValue = str;
+ userProperties.userProperty[ i ].valueLength = UINT16_MAX;
+ }
+
+ userProperties.count = 2048;
+ publishInfo.pUserProperty = &userProperties;
+ status = MQTT_GetPublishPropertiesSize( &publishInfo );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ status = MQTTV5_GetConnectPacketSize( &connectInfo, &publishInfo, &properties, &remainingLength, &packetSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ publishInfo.topicNameLength = 0U;
+ publishInfo.topicAlias = 1U;
+ status = MQTTV5_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize, MQTT_MAX_REMAINING_LENGTH );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ memset( &ackInfo, 0x0, sizeof( ackInfo ) );
+ ackInfo.pUserProperty = &userProperties;
+ maxPacketSize = UINT32_MAX;
+ status = MQTTV5_GetAckPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ status = MQTTV5_GetDisconnectPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize, 0, 0 );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+}
+
+void test_MQTTV5_ValidatePublishParams()
+{
+ uint16_t topicAliasMax = 10U;
+ uint8_t maxQos = 0U;
+ uint8_t retain = 0U;
+
+ /*Publish info cannot be null*/
+ status = MQTTV5_ValidatePublishParams( NULL, topicAliasMax, retain, maxQos );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ /*Topic alias greater than the allowed value. */
+ publishInfo.topicAlias = 12U;
+ status = MQTTV5_ValidatePublishParams( &publishInfo, topicAliasMax, retain, maxQos );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ /*Retain is not allowed. */
+ publishInfo.topicAlias = 2U;
+ publishInfo.retain = true;
+ status = MQTTV5_ValidatePublishParams( &publishInfo, topicAliasMax, retain, maxQos );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ /*Qos invalid*/
+ publishInfo.retain = false;
+ publishInfo.qos = 1;
+ status = MQTTV5_ValidatePublishParams( &publishInfo, topicAliasMax, retain, maxQos );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ /*Valid parameters should return success*/
+ publishInfo.qos = 1;
+ maxQos = 1;
+ publishInfo.retain = true;
+ retain = 1;
+ status = MQTTV5_ValidatePublishParams( &publishInfo, topicAliasMax, retain, maxQos );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+
+ /*Valid parameters should return success*/
+ publishInfo.qos = 0;
+ status = MQTTV5_ValidatePublishParams( &publishInfo, topicAliasMax, retain, maxQos );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+}
+
+void test_MQTTV5_GetPublishPacketSize()
+{
+ size_t remainingLength = 0U;
+ size_t packetSize = 0U;
+ uint32_t maxPacketSize = 0U;
+
+ setupPublishInfo( &publishInfo );
+ /*Test with invalid paramters*/
+ status = MQTTV5_GetPublishPacketSize( NULL, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ status = MQTTV5_GetPublishPacketSize( &publishInfo, NULL, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ status = MQTTV5_GetPublishPacketSize( &publishInfo, &remainingLength, NULL, maxPacketSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ status = MQTTV5_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ /*Topic name invalid*/
+ publishInfo.pTopicName = NULL;
+ status = MQTTV5_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ publishInfo.pTopicName = TEST_TOPIC_NAME;
+
+ /*Topic alias is not allowed and topic name is not provided.*/
+ publishInfo.topicNameLength = 0;
+ status = MQTTV5_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ maxPacketSize = 100;
+ publishInfo.topicNameLength = TEST_TOPIC_NAME_LENGTH;
+ /*Packet size too large*/
+ publishInfo.payloadLength = MQTT_MAX_REMAINING_LENGTH;
+ status = MQTTV5_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ publishInfo.payloadLength = MQTT_MAX_REMAINING_LENGTH - TEST_TOPIC_NAME_LENGTH - 4;
+ status = MQTTV5_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+
+ /* Good case succeeds. */
+ publishInfo.pPayload = "";
+ publishInfo.payloadLength = 0;
+ status = MQTTV5_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+
+ /* Again with QoS 2. */
+ publishInfo.qos = MQTTQoS2;
+ status = MQTTV5_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+
+ setupPublishProperties( &publishInfo );
+ publishInfo.retain = true;
+ /*Valid properties*/
+ status = MQTTV5_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+
+ /* No topic name*/
+ publishInfo.topicNameLength = 0U;
+ publishInfo.pTopicName = NULL;
+ status = MQTTV5_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+
+ /*Packet size is more than the server allowed max packet size*/
+ maxPacketSize = 4;
+ status = MQTTV5_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL( MQTTBadParameter, status );
+}
+
+void test_MQTT_SerializePublish()
+{
+ uint32_t maxPacketSize = 200U;
+ size_t remainingLength = 98;
+ uint8_t buffer[ 200 + 2 * BUFFER_PADDING_LENGTH ];
+ size_t bufferSize = sizeof( buffer ) - 2 * BUFFER_PADDING_LENGTH;
+ size_t packetSize = bufferSize;
+ MQTTStatus_t status = MQTTSuccess;
+ MQTTFixedBuffer_t fixedBuffer = { .pBuffer = &buffer[ BUFFER_PADDING_LENGTH ], .size = bufferSize };
+ const uint16_t PACKET_ID = 1;
+
+ setupPublishInfo( &publishInfo );
+ fixedBuffer.size = bufferSize;
+ /* Calculate exact packet size and remaining length. */
+ status = MQTTV5_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ /* Make sure buffer has enough space */
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
+
+ padAndResetBuffer( buffer, sizeof( buffer ) );
+ status = MQTT_SerializePublish( &publishInfo,
+ PACKET_ID,
+ remainingLength,
+ &fixedBuffer );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ checkBufferOverflow( buffer, sizeof( buffer ) );
+
+ fixedBuffer.pBuffer = &buffer[ BUFFER_PADDING_LENGTH ];
+ setupPublishProperties( &publishInfo );
+ /* Calculate exact packet size and remaining length. */
+ status = MQTTV5_GetPublishPacketSize( &publishInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ /* Make sure buffer has enough space */
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
+
+ padAndResetBuffer( buffer, sizeof( buffer ) );
+ status = MQTT_SerializePublish( &publishInfo,
+ PACKET_ID,
+ remainingLength,
+ &fixedBuffer );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ checkBufferOverflow( buffer, sizeof( buffer ) );
+}
+
+void test_MQTTV5_DeserializeAck_puback( void )
+{
+ MQTTPacketInfo_t mqttPacketInfo;
+ MQTTAckInfo_t ackInfo;
+ uint16_t packetIdentifier;
+ uint32_t maxPacketSize = 0U;
+ bool requestProblem = false;
+ MQTTStatus_t status = MQTTSuccess;
+ uint8_t buffer[ 100 ] = { 0 };
+ uint8_t * pIndex = buffer;
+ size_t dummy;
+
+ /* Verify parameters */
+ memset( &mqttPacketInfo, 0x00, sizeof( mqttPacketInfo ) );
+ memset( &ackInfo, 0x00, sizeof( ackInfo ) );
+ mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBACK;
+ status = MQTTV5_DeserializeAck( NULL, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, NULL, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+ /* ackInfo not set. */
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, NULL, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /*Remaining data cannot be NULL.*/
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /*Max packet size cannot be 0*/
+ mqttPacketInfo.pRemainingData = buffer;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ maxPacketSize = 200U;
+ /* Packet identifier 0 is not valid (per spec). */
+ buffer[ 0 ] = 0;
+ buffer[ 1 ] = 0;
+ mqttPacketInfo.remainingLength = MQTT_PACKET_SIMPLE_ACK_REMAINING_LENGTH;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status );
+
+
+ /*Remaining length connot be less than 2*/
+ mqttPacketInfo.remainingLength = 1;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTMalformedPacket, status );
+
+ /*Packet size greater than allowed.*/
+ mqttPacketInfo.remainingLength = 1000U;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+
+ /* Process a valid PUBACK. */
+ mqttPacketInfo.remainingLength = 2;
+ buffer[ 1 ] = 1;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_INT( 1, packetIdentifier );
+
+
+ mqttPacketInfo.remainingLength = 3;
+ buffer[ 2 ] = 0x00;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ TEST_ASSERT_EQUAL_INT( 1, packetIdentifier );
+
+ /*Property length should be zero when request problem is set to false*/
+ mqttPacketInfo.remainingLength = 24;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+
+ requestProblem = true;
+ /*User properties not initialized.*/
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /*Valid parameters.*/
+ ackInfo.pUserProperty = &userProperties;
+ pIndex = &buffer[ 3 ];
+ dummy = encodeRemainingLength( pIndex, 20 );
+ pIndex++;
+ pIndex = serializeutf_8( pIndex, MQTT_REASON_STRING_ID );
+ pIndex = serializeutf_8pair( pIndex );
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+
+ /*Invalid property id*/
+ pIndex = &buffer[ 3 ];
+ dummy = encodeRemainingLength( pIndex, 7 );
+ mqttPacketInfo.remainingLength = dummy + 7 + 3;
+ pIndex++;
+ pIndex = serializeutf_8( pIndex, MQTT_CORRELATION_DATA_ID );
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+
+ /*Invalid remaining length*/
+ pIndex = &buffer[ 3 ];
+ dummy = encodeRemainingLength( pIndex, 12 );
+ pIndex++;
+ pIndex = serializeutf_8( pIndex, MQTT_REASON_STRING_ID );
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTMalformedPacket, status );
+
+
+ /*Invalid property length*/
+ pIndex = &buffer[ 3 ];
+ dummy = encodeRemainingLength( pIndex, 20971556356235 );
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status );
+}
+
+void test_MQTTV5_DeserializeAck_LogPuback()
+{
+ MQTTPacketInfo_t mqttPacketInfo;
+ MQTTAckInfo_t ackInfo;
+ uint16_t packetIdentifier;
+ uint32_t maxPacketSize = 10U;
+ bool requestProblem = false;
+ MQTTStatus_t status = MQTTSuccess;
+ uint8_t buffer[ 4 ] = { 0 };
+
+ memset( &mqttPacketInfo, 0x00, sizeof( mqttPacketInfo ) );
+ memset( &ackInfo, 0x00, sizeof( ackInfo ) );
+ mqttPacketInfo.pRemainingData = buffer;
+ mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBACK;
+ mqttPacketInfo.remainingLength = 4;
+ /*Validate all the correct reason codes.*/
+ buffer[ 1 ] = 1;
+ buffer[ 2 ] = MQTT_REASON_SUCCESS;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+
+ buffer[ 2 ] = MQTT_REASON_NO_MATCHING_SUBSCRIBERS;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+
+ buffer[ 2 ] = MQTT_REASON_UNSPECIFIED_ERR;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status );
+
+ buffer[ 2 ] = MQTT_REASON_IMPL_SPECIFIC_ERR;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status );
+
+ buffer[ 2 ] = MQTT_REASON_NOT_AUTHORIZED;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status );
+
+ buffer[ 2 ] = MQTT_REASON_TOPIC_NAME_INVALID;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status );
+
+ buffer[ 2 ] = MQTT_REASON_PACKET_ID_IN_USE;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status );
+
+ buffer[ 2 ] = MQTT_REASON_QUOTA_EXCEEDED;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status );
+
+ buffer[ 2 ] = MQTT_REASON_PAYLOAD_FORMAT_INVALID;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status );
+
+ /*Invlaid reason code.*/
+ buffer[ 2 ] = MQTT_REASON_BANNED;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+}
+
+void test_MQTTV5_DeserializeAck_Pubrel()
+{
+ MQTTPacketInfo_t mqttPacketInfo;
+ MQTTAckInfo_t ackInfo;
+ uint32_t maxPacketSize = 10U;
+ uint16_t packetIdentifier;
+ bool requestProblem = false;
+ MQTTStatus_t status = MQTTSuccess;
+ uint8_t buffer[ 4 ] = { 0 };
+
+ memset( &mqttPacketInfo, 0x00, sizeof( mqttPacketInfo ) );
+ memset( &ackInfo, 0x00, sizeof( ackInfo ) );
+ mqttPacketInfo.pRemainingData = buffer;
+ mqttPacketInfo.type = MQTT_PACKET_TYPE_PUBREL;
+ mqttPacketInfo.remainingLength = 4;
+ /*Validate all the correct reason codes.*/
+ buffer[ 1 ] = 1;
+ buffer[ 2 ] = MQTT_REASON_SUCCESS;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+
+ buffer[ 2 ] = MQTT_REASON_PACKET_ID_NOT_FOUND;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTServerRefused, status );
+
+ /*Invalid reason code.*/
+ buffer[ 2 ] = MQTT_REASON_BANNED;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+
+ /*Invalid reason code.*/
+ buffer[ 2 ] = MQTT_REASON_SEND_WILL;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+
+ /*Invalid packet id*/
+ buffer[ 1 ] = 0;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status );
+
+ /*Invalid packet type. */
+ mqttPacketInfo.type = MQTT_PACKET_TYPE_CONNACK;
+ status = MQTTV5_DeserializeAck( &mqttPacketInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status );
+}
+
+
+void test_MQTTV5_GetAckPacketSize()
+{
+ MQTTStatus_t status;
+ MQTTAckInfo_t ackInfo;
+ MQTTUserProperties_t userProperties;
+ size_t remainingLength;
+ size_t packetSize;
+ uint32_t maxPacketSize = 0U;
+
+ memset( &ackInfo, 0x0, sizeof( ackInfo ) );
+ memset( &userProperties, 0x0, sizeof( userProperties ) );
+ /*Invalid parameters*/
+ status = MQTTV5_GetAckPacketSize( NULL, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ status = MQTTV5_GetAckPacketSize( &ackInfo, NULL, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+
+ status = MQTTV5_GetAckPacketSize( &ackInfo, &remainingLength, NULL, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+/*Max packet size cannot be 0*/
+ status = MQTTV5_GetAckPacketSize( &ackInfo, &remainingLength, NULL, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+/*Valid parameters*/
+ maxPacketSize = UINT32_MAX;
+ status = MQTTV5_GetAckPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+
+/*With properties*/
+ ackInfo.pReasonString = MQTT_TEST_UTF8_STRING;
+ ackInfo.reasonStringLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ ackInfo.pUserProperty = &userProperties;
+ userProperties.count = 0;
+ status = MQTTV5_GetAckPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+
+/*Packet size greater than max allowed.*/
+ maxPacketSize = 2;
+ status = MQTTV5_GetAckPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+/*Max packet size cannot be 0*/
+ maxPacketSize = 0;
+ status = MQTTV5_GetAckPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+
+/*Reason string is null but length is not 0*/
+ ackInfo.pReasonString = NULL;
+ maxPacketSize = 30;
+ status = MQTTV5_GetAckPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+}
+
+void test_MQTTV5_SerializePubAckWithProperty()
+{
+ MQTTStatus_t status;
+ MQTTAckInfo_t ackInfo;
+ size_t remainingLength = 0U;
+ size_t packetSize;
+ uint8_t packetType = MQTT_PACKET_TYPE_PUBREL;
+ uint16_t packetId = 1U;
+ uint32_t maxPacketSize = 1000U;
+ uint8_t buffer[ 440 + 2 * BUFFER_PADDING_LENGTH ];
+ size_t bufferSize = sizeof( buffer ) - 2 * BUFFER_PADDING_LENGTH;
+ MQTTFixedBuffer_t fixedBuffer = { .pBuffer = &buffer[ BUFFER_PADDING_LENGTH ], .size = bufferSize };
+
+ /*Invalid parameters*/
+ status = MQTTV5_SerializePubAckWithProperty( NULL, remainingLength, &fixedBuffer, packetType, packetId );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ status = MQTTV5_SerializePubAckWithProperty( &ackInfo, remainingLength, NULL, packetType, packetId );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ fixedBuffer.pBuffer = NULL;
+ status = MQTTV5_SerializePubAckWithProperty( &ackInfo, remainingLength, &fixedBuffer, packetType, packetId );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /*Buffer size not sufficient*/
+ status = MQTTV5_GetAckPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize );
+ fixedBuffer.size = 5;
+ fixedBuffer.pBuffer = &buffer[ BUFFER_PADDING_LENGTH ];
+ status = MQTTV5_SerializePubAckWithProperty( &ackInfo, remainingLength, &fixedBuffer, packetType, packetId );
+ TEST_ASSERT_EQUAL_INT( MQTTNoMemory, status );
+
+ /*With correct parameters*/
+ memset( &ackInfo, 0x0, sizeof( ackInfo ) );
+ status = MQTTV5_GetAckPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ /* Make sure buffer has enough space */
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
+ /* Make sure test succeeds. */
+ fixedBuffer.size = 200;
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ status = MQTTV5_SerializePubAckWithProperty( &ackInfo, remainingLength, &fixedBuffer, packetType, packetId );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+
+ /*With properties*/
+ ackInfo.reasonStringLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ ackInfo.pReasonString = MQTT_TEST_UTF8_STRING;
+ ackInfo.pUserProperty = &userProperties;
+ userProperties.count = 1;
+ userProperties.userProperty->keyLength = 1;
+ userProperties.userProperty->valueLength = 1;
+ userProperties.userProperty->pKey = "a";
+ userProperties.userProperty->pValue = "a";
+ status = MQTTV5_GetAckPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ /* Make sure buffer has enough space */
+ fixedBuffer.size = 400;
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ status = MQTTV5_SerializePubAckWithProperty( &ackInfo, remainingLength, &fixedBuffer, packetType, packetId );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+}
+
+void test_MQTTV5_GetDisconnectPacketSize()
+{
+ MQTTAckInfo_t ackInfo;
+ size_t remainingLength;
+ size_t packetSize;
+ uint32_t maxPacketSize = 0U;
+ uint32_t sessionExpiry = 0U;
+ uint32_t prevSessionExpiry = 0U;
+ MQTTStatus_t status;
+
+ memset( &ackInfo, 0x0, sizeof( ackInfo ) );
+
+ /*Invalid arguments*/
+ status = MQTTV5_GetDisconnectPacketSize( NULL, &remainingLength, &packetSize, maxPacketSize, sessionExpiry, prevSessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ status = MQTTV5_GetDisconnectPacketSize( &ackInfo, NULL, &packetSize, maxPacketSize, sessionExpiry, prevSessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ status = MQTTV5_GetDisconnectPacketSize( &ackInfo, &remainingLength, NULL, maxPacketSize, sessionExpiry, prevSessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /*Max packet size cannot be 0.*/
+ status = MQTTV5_GetDisconnectPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize, sessionExpiry, prevSessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /*Session expiry cannot overwrite zero.*/
+ sessionExpiry = 10U;
+ maxPacketSize = 6U;
+ status = MQTTV5_GetDisconnectPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize, sessionExpiry, prevSessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ prevSessionExpiry = 5;
+ /*Invalid reason code*/
+ ackInfo.reasonCode = 2;
+ status = MQTTV5_GetDisconnectPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize, sessionExpiry, prevSessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /*Reason string not initialized.*/
+ ackInfo.reasonCode = 0;
+ ackInfo.reasonStringLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ status = MQTTV5_GetDisconnectPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize, sessionExpiry, prevSessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ ackInfo.pReasonString = MQTT_TEST_UTF8_STRING;
+ /*Packet size greater than allowed.*/
+ prevSessionExpiry = 10;
+ status = MQTTV5_GetDisconnectPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize, sessionExpiry, prevSessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /*Invalid Reason code*/
+ maxPacketSize = 60U;
+ ackInfo.reasonCode = MQTT_REASON_SERVER_BUSY;
+ status = MQTTV5_GetDisconnectPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize, sessionExpiry, prevSessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /*Valid parameters*/
+ ackInfo.reasonCode = MQTT_REASON_SEND_WILL;
+ status = MQTTV5_GetDisconnectPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize, sessionExpiry, prevSessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+
+ /*Valid parameters*/
+ ackInfo.reasonCode = MQTT_REASON_PACKET_TOO_LARGE;
+ status = MQTTV5_GetDisconnectPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize, sessionExpiry, prevSessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+}
+
+void test_MQTTV5_SerializeDisconnectWithProperty()
+{
+ MQTTAckInfo_t ackInfo;
+ size_t remainingLength = 0U;
+ uint32_t maxPacketSize = 200U;
+ uint32_t sessionExpiry = 0U;
+ MQTTStatus_t status = MQTTSuccess;
+ uint8_t buffer[ 200 + 2 * BUFFER_PADDING_LENGTH ];
+ size_t bufferSize = sizeof( buffer ) - 2 * BUFFER_PADDING_LENGTH;
+ size_t packetSize = bufferSize;
+ MQTTFixedBuffer_t fixedBuffer = { 0 };
+
+ memset( &ackInfo, 0x0, sizeof( ackInfo ) );
+ fixedBuffer.size = bufferSize;
+
+ /*Invalid Parameters*/
+ status = MQTTV5_SerializeDisconnectWithProperty( NULL, remainingLength, &fixedBuffer, sessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ status = MQTTV5_SerializeDisconnectWithProperty( &ackInfo, remainingLength, NULL, sessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ status = MQTTV5_SerializeDisconnectWithProperty( &ackInfo, remainingLength, &fixedBuffer, sessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ fixedBuffer.pBuffer = &buffer[ BUFFER_PADDING_LENGTH ];
+ fixedBuffer.size = 2;
+ remainingLength = 20;
+ /*Buffer size not enough.*/
+ status = MQTTV5_SerializeDisconnectWithProperty( &ackInfo, remainingLength, &fixedBuffer, sessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTNoMemory, status );
+
+ fixedBuffer.size = bufferSize;
+ /* Calculate exact packet size and remaining length. */
+ status = MQTTV5_GetDisconnectPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize, sessionExpiry, 10 );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ /* Make sure buffer has enough space */
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
+
+ padAndResetBuffer( buffer, sizeof( buffer ) );
+ status = MQTTV5_SerializeDisconnectWithProperty( &ackInfo, remainingLength, &fixedBuffer, sessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ checkBufferOverflow( buffer, sizeof( buffer ) );
+
+ ackInfo.pReasonString = MQTT_TEST_UTF8_STRING;
+ ackInfo.reasonStringLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ ackInfo.pUserProperty = &userProperties;
+ userProperties.count = 1;
+ userProperties.userProperty[ 0 ].pKey = MQTT_TEST_UTF8_STRING;
+ userProperties.userProperty[ 0 ].pValue = MQTT_TEST_UTF8_STRING;
+ userProperties.userProperty[ 0 ].keyLength = MQTT_TEST_UTF8_STRING_LENGTH;
+ userProperties.userProperty[ 0 ].valueLength = MQTT_TEST_UTF8_STRING_LENGTH;
+
+ sessionExpiry = 10;
+ fixedBuffer.pBuffer = &buffer[ BUFFER_PADDING_LENGTH ];
+ /* Calculate exact packet size and remaining length. */
+ status = MQTTV5_GetDisconnectPacketSize( &ackInfo, &remainingLength, &packetSize, maxPacketSize, sessionExpiry, 10 );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ /* Make sure buffer has enough space */
+ TEST_ASSERT_GREATER_OR_EQUAL( packetSize, bufferSize );
+
+ padAndResetBuffer( buffer, sizeof( buffer ) );
+ status = MQTTV5_SerializeDisconnectWithProperty( &ackInfo, remainingLength, &fixedBuffer, sessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+ checkBufferOverflow( buffer, sizeof( buffer ) );
+}
+
+void test_MQTTV5_DeserializeDisconnect()
+{
+ MQTTAckInfo_t disconnectInfo;
+ const char * pServerRef;
+ uint16_t serverRefLength;
+ size_t dummy;
+ int32_t maxPacketSize = 0U;
+ uint8_t buffer[ 100 ] = { 0 };
+ uint8_t * pIndex = buffer;
+
+ memset( &disconnectInfo, 0x0, sizeof( disconnectInfo ) );
+
+
+ /*Invalid parameters*/
+ status = MQTTV5_DeserializeDisconnect( NULL, &disconnectInfo, &pServerRef, &serverRefLength, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /*Remaining data not initialized.*/
+ status = MQTTV5_DeserializeDisconnect( &packetInfo, &disconnectInfo, &pServerRef, &serverRefLength, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ packetInfo.pRemainingData = buffer;
+ status = MQTTV5_DeserializeDisconnect( &packetInfo, NULL, &pServerRef, &serverRefLength, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ status = MQTTV5_DeserializeDisconnect( &packetInfo, &disconnectInfo, NULL, &serverRefLength, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ status = MQTTV5_DeserializeDisconnect( &packetInfo, &disconnectInfo, &pServerRef, NULL, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /*Max packet size cannot be 0.*/
+ status = MQTTV5_DeserializeDisconnect( &packetInfo, &disconnectInfo, &pServerRef, &serverRefLength, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+ maxPacketSize = 100;
+
+
+ /*Remaining length cannot be 0*/
+ status = MQTTV5_DeserializeDisconnect( &packetInfo, &disconnectInfo, &pServerRef, &serverRefLength, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTMalformedPacket, status );
+
+ /*Remaining Length invalid. */
+ packetInfo.remainingLength = 200;
+ status = MQTTV5_DeserializeDisconnect( &packetInfo, &disconnectInfo, &pServerRef, &serverRefLength, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+
+ /*User property not initialized.*/
+ packetInfo.remainingLength = 1;
+ status = MQTTV5_DeserializeDisconnect( &packetInfo, &disconnectInfo, &pServerRef, &serverRefLength, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /*Invalid reason code.*/
+ disconnectInfo.pUserProperty = &userProperties;
+ buffer[ 0 ] = MQTT_REASON_SEND_WILL;
+ status = MQTTV5_DeserializeDisconnect( &packetInfo, &disconnectInfo, &pServerRef, &serverRefLength, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+
+ packetInfo.remainingLength = 1;
+ buffer[ 0 ] = MQTT_REASON_NOT_AUTHORIZED;
+ status = MQTTV5_DeserializeDisconnect( &packetInfo, &disconnectInfo, &pServerRef, &serverRefLength, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+
+ /*Property length is 0.*/
+ packetInfo.remainingLength = 2;
+ pIndex = &buffer[ 1 ];
+ dummy = encodeRemainingLength( pIndex, 0 );
+ status = MQTTV5_DeserializeDisconnect( &packetInfo, &disconnectInfo, &pServerRef, &serverRefLength, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+
+ /*With properties*/
+ pIndex = &buffer[ 1 ];
+ packetInfo.remainingLength = 29;
+ dummy = encodeRemainingLength( pIndex, 27 );
+ pIndex++;
+ pIndex = serializeutf_8( pIndex, MQTT_REASON_STRING_ID );
+ pIndex = serializeutf_8pair( pIndex );
+ pIndex = serializeutf_8( pIndex, MQTT_SERVER_REF_ID );
+ status = MQTTV5_DeserializeDisconnect( &packetInfo, &disconnectInfo, &pServerRef, &serverRefLength, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+
+ /*Invalid property id.*/
+ pIndex = &buffer[ 1 ];
+ packetInfo.remainingLength = 9;
+ dummy = encodeRemainingLength( pIndex, 7 );
+ pIndex++;
+ pIndex = serializeutf_8( pIndex, MQTT_SESSION_EXPIRY_ID );
+ status = MQTTV5_DeserializeDisconnect( &packetInfo, &disconnectInfo, &pServerRef, &serverRefLength, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+ TEST_ASSERT_EQUAL_INT( 1, dummy );
+
+ /*Invalid property length.*/
+ pIndex = &buffer[ 1 ];
+ packetInfo.remainingLength = 9;
+ dummy = encodeRemainingLength( pIndex, 4 );
+ status = MQTTV5_DeserializeDisconnect( &packetInfo, &disconnectInfo, &pServerRef, &serverRefLength, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTMalformedPacket, status );
+
+ buffer[ 1 ] = 0x81;
+ buffer[ 2 ] = 0x00;
+ status = MQTTV5_DeserializeDisconnect( &packetInfo, &disconnectInfo, &pServerRef, &serverRefLength, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status );
+}
+
+void test_MQTT_GetIncomingPacketTypeAndLength( void )
+{
+ MQTTPacketInfo_t mqttPacket;
+ NetworkContext_t networkContext;
+ uint8_t buffer[ 10 ];
+ uint8_t * bufPtr = buffer;
+
+ /* Dummy network context - pointer to pointer to a buffer. */
+ networkContext.buffer = &bufPtr;
+ /* Check when network receive fails. */
+ memset( buffer, 0x00, 10 );
+ /* Branch coverage for Disconnect. */
+ bufPtr = buffer;
+ buffer[ 0 ] = MQTT_PACKET_TYPE_DISCONNECT;
+ status = MQTT_GetIncomingPacketTypeAndLength( mockReceive, &networkContext, &mqttPacket );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+}
+
+void test_MQTTV5_InitConnect()
+{
+ status = MQTTV5_InitConnect( NULL );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+ status = MQTTV5_InitConnect( &properties );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+}
diff --git a/test/unit-test/MQTTv5/core_mqttv5_state_utest.c b/test/unit-test/MQTTv5/core_mqttv5_state_utest.c
new file mode 100644
index 000000000..d3bdc4278
--- /dev/null
+++ b/test/unit-test/MQTTv5/core_mqttv5_state_utest.c
@@ -0,0 +1,59 @@
+/*
+ * coreMQTT
+ * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @file core_mqtt_state_utest.c
+ * @brief Unit tests for functions in core_mqtt_state.h.
+ */
+#include
+#include "unity.h"
+
+#include "core_mqtt_state.h"
+#include "mock_core_mqtt_serializer.h"
+
+#define MQTT_PACKET_ID_INVALID ( ( uint16_t ) 0U )
+#define MQTT_STATE_ARRAY_MAX_COUNT 10
+
+/* ============================ UNITY FIXTURES ============================ */
+void setUp( void )
+{
+}
+
+/* called before each testcase */
+void tearDown( void )
+{
+}
+
+/* called at the beginning of the whole suite */
+void suiteSetUp()
+{
+}
+
+/* called at the end of the whole suite */
+int suiteTearDown( int numFailures )
+{
+ return numFailures;
+}
+
+/* ========================================================================== */
diff --git a/test/unit-test/MQTTv5/core_mqttv5_utest.c b/test/unit-test/MQTTv5/core_mqttv5_utest.c
new file mode 100644
index 000000000..bb8f4afd9
--- /dev/null
+++ b/test/unit-test/MQTTv5/core_mqttv5_utest.c
@@ -0,0 +1,1104 @@
+/*
+ * coreMQTT
+ * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @file core_mqtt_utest.c
+ * @brief Unit tests for functions in core_mqtt.h.
+ */
+#include
+#include
+#include
+
+#include "unity.h"
+
+/* Include paths for public enums, structures, and macros. */
+#include "core_mqtt.h"
+
+/* #include "mock_core_mqttv5_serializer.h" */
+/* #include "mock_core_mqttv5_state.h" */
+
+#include "core_mqtt_config_defaults.h"
+#include "mock_core_mqtt_serializer.h"
+#include "mock_core_mqtt_state.h"
+/* Set network context to double pointer to buffer (uint8_t**). */
+struct NetworkContext
+{
+ uint8_t ** buffer;
+};
+
+/**
+ * @brief MQTT client identifier.
+ */
+#define MQTT_CLIENT_IDENTIFIER "testclient"
+
+/**
+ * @brief A valid starting packet ID per MQTT spec. Start from 1.
+ */
+#define MQTT_FIRST_VALID_PACKET_ID ( 1 )
+
+/**
+ * @brief A PINGREQ packet is always 2 bytes in size, defined by MQTT 3.1.1 spec.
+ */
+#define MQTT_PACKET_PINGREQ_SIZE ( 2U )
+
+/**
+ * @brief A packet type not handled by MQTT_ProcessLoop.
+ */
+#define MQTT_PACKET_TYPE_INVALID ( 0U )
+
+/**
+ * @brief Number of milliseconds in a second.
+ */
+#define MQTT_ONE_SECOND_TO_MS ( 1000U )
+
+/**
+ * @brief Length of the MQTT network buffer.
+ */
+#define MQTT_TEST_BUFFER_LENGTH ( 128 )
+
+/**
+ * @brief Sample keep-alive interval that should be greater than 0.
+ */
+#define MQTT_SAMPLE_KEEPALIVE_INTERVAL_S ( 1U )
+
+/**
+ * @brief Length of time spent for single test case with
+ * multiple iterations spent in the process loop for coverage.
+ */
+#define MQTT_SAMPLE_PROCESS_LOOP_TIMEOUT_MS ( 1U )
+
+/**
+ * @brief Zero timeout in the process loop implies one iteration.
+ */
+#define MQTT_NO_TIMEOUT_MS ( 0U )
+
+/**
+ * @brief Sample length of remaining serialized data.
+ */
+#define MQTT_SAMPLE_REMAINING_LENGTH ( 64 )
+
+/**
+ * @brief Subtract this value from max value of global entry time
+ * for the timer overflow test.
+ */
+#define MQTT_OVERFLOW_OFFSET ( 3 )
+
+/**
+ * @brief The number of times the "getTime()" function is called
+ * within a single iteration of the #MQTT_ProcessLoop.
+ *
+ * This constant is used for the timer overflow test which checks
+ * that the API can support normal behavior even if the timer
+ * overflows.
+ *
+ * @note Currently, there are 6 calls within a single iteration.
+ * This can change when the implementation changes which would be
+ * caught through unit test failure.
+ */
+#define MQTT_TIMER_CALLS_PER_ITERATION ( 6 )
+
+/**
+ * @brief Timeout for the timer overflow test.
+ */
+#define MQTT_TIMER_OVERFLOW_TIMEOUT_MS ( 10 )
+
+/**
+ * @brief A sample network context that we set to NULL.
+ */
+#define MQTT_SAMPLE_NETWORK_CONTEXT ( NULL )
+
+/**
+ * @brief Sample topic filter to subscribe to.
+ */
+#define MQTT_SAMPLE_TOPIC_FILTER "iot"
+
+/**
+ * @brief Length of sample topic filter.
+ */
+#define MQTT_SAMPLE_TOPIC_FILTER_LENGTH ( sizeof( MQTT_SAMPLE_TOPIC_FILTER ) - 1 )
+
+/**
+ * @brief Sample topic filter to subscribe to.
+ */
+#define MQTT_SAMPLE_TOPIC_FILTER1 "TopicFilter2"
+
+/**
+ * @brief Length of sample topic filter.
+ */
+#define MQTT_SAMPLE_TOPIC_FILTER_LENGTH1 ( sizeof( MQTT_SAMPLE_TOPIC_FILTER1 ) - 1 )
+
+/**
+ * @brief Sample topic filter to subscribe to.
+ */
+#define MQTT_SAMPLE_TOPIC_FILTER2 "SomeTopic"
+
+/**
+ * @brief Length of sample topic filter.
+ */
+#define MQTT_SAMPLE_TOPIC_FILTER_LENGTH2 ( sizeof( MQTT_SAMPLE_TOPIC_FILTER2 ) - 1 )
+
+/**
+ * @brief Sample topic filter to subscribe to.
+ */
+#define MQTT_SAMPLE_TOPIC_FILTER3 "iotTopicFilter"
+
+/**
+ * @brief Length of sample topic filter.
+ */
+#define MQTT_SAMPLE_TOPIC_FILTER_LENGTH3 ( sizeof( MQTT_SAMPLE_TOPIC_FILTER3 ) - 1 )
+
+
+#define TEST_TOPIC_ALIAS ( 2U )
+#define TEST_MSG_EXPIRY ( 100U )
+#define TEST_CONTENT_TYPE_LENGTH ( 2 )
+#define TEST_CONTENT_TYPE ( "ab" )
+#define TEST_RESPONSE_TOPIC_LENGTH ( 10 )
+#define TEST_RESPONSE_TOPIC ( "aaaaaaaaaa" )
+#define TEST_CORRELATION_DATA_LENGTH ( 5 )
+#define TEST_CORRELATION_DATA ( "abcde" )
+
+/**
+ * @brief Return values of mocked calls in MQTT_ProcessLoop(). Used by
+ * `expectProcessLoopCalls`
+ */
+typedef struct ProcessLoopReturns
+{
+ MQTTStatus_t deserializeStatus; /**< @brief Status after deserializing incoming packet. */
+ MQTTPublishState_t stateAfterDeserialize; /**< @brief Publish state after deserializing incoming packet. */
+ MQTTStatus_t updateStateStatus; /**< @brief Status after updating publish state. */
+ MQTTStatus_t serializeStatus; /**< @brief Status after serializing a publish ack to send. */
+ MQTTPublishState_t stateAfterSerialize; /**< @brief Publish state after serializing an ack to send. */
+ MQTTStatus_t processLoopStatus; /**< @brief Return value of the process loop. */
+ bool incomingPublish; /**< @brief Whether the incoming packet is a publish. */
+ MQTTPublishInfo_t * pPubInfo; /**< @brief Publish information to be returned by the deserializer. */
+ uint32_t timeoutMs; /**< @brief The timeout value to call MQTT_ProcessLoop API with. */
+} ProcessLoopReturns_t;
+
+
+/**
+ * @brief Time at the beginning of each test. Note that this is not updated with
+ * a real clock. Instead, we simply increment this variable.
+ */
+static uint32_t globalEntryTime = 0;
+
+/**
+ * @brief A static buffer used by the MQTT library for storing packet data.
+ */
+static uint8_t mqttBuffer[ MQTT_TEST_BUFFER_LENGTH ] = { 0 };
+
+/**
+ * @brief A flag to indicate whether event callback is called from
+ * MQTT_ProcessLoop.
+ */
+static bool isEventCallbackInvoked = false;
+static bool receiveOnce = false;
+static MQTTStatus_t modifyIncomingPacketStatus = MQTTSuccess;
+
+
+/* ============================ UNITY FIXTURES ============================ */
+
+/* Called before each test method. */
+void setUp()
+{
+ memset( mqttBuffer, 0x0, sizeof( mqttBuffer ) );
+ MQTT_State_strerror_IgnoreAndReturn( "DUMMY_MQTT_STATE" );
+
+ globalEntryTime = 0;
+}
+
+/* Called after each test method. */
+void tearDown()
+{
+}
+
+/* Called at the beginning of the whole suite. */
+void suiteSetUp()
+{
+ receiveOnce = 0;
+}
+
+/* Called at the end of the whole suite. */
+int suiteTearDown( int numFailures )
+{
+ return numFailures;
+}
+
+
+/* ========================================================================== */
+
+/**
+ * @brief A mocked timer query function that increments on every call. This
+ * guarantees that only a single iteration runs in the ProcessLoop for ease
+ * of testing.
+ */
+static uint32_t getTime( void )
+{
+ return globalEntryTime++;
+}
+
+/**
+ * @brief Mocked failed transport read.
+ */
+static int32_t transportRecvFailure( NetworkContext_t * pNetworkContext,
+ void * pBuffer,
+ size_t bytesToRead )
+{
+ ( void ) pNetworkContext;
+ ( void ) pBuffer;
+ ( void ) bytesToRead;
+ return -1;
+}
+
+/**
+ * @brief Mocked MQTT event callback.
+ *
+ * @param[in] pContext MQTT context pointer.
+ * @param[in] pPacketInfo Packet Info pointer for the incoming packet.
+ * @param[in] pDeserializedInfo Deserialized information from the incoming packet.
+ */
+static void eventCallback( MQTTContext_t * pContext,
+ MQTTPacketInfo_t * pPacketInfo,
+ MQTTDeserializedInfo_t * pDeserializedInfo )
+{
+ ( void ) pContext;
+ ( void ) pPacketInfo;
+ ( void ) pDeserializedInfo;
+
+ /* Update the global state to indicate that event callback is invoked. */
+ isEventCallbackInvoked = true;
+ pDeserializedInfo->pNextAckInfo = NULL;
+}
+
+/**
+ * @brief Mocked MQTT event callback.
+ *
+ * @param[in] pContext MQTT context pointer.
+ * @param[in] pPacketInfo Packet Info pointer for the incoming packet.
+ * @param[in] pDeserializedInfo Deserialized information from the incoming packet.
+ */
+static void eventCallback1( MQTTContext_t * pContext,
+ MQTTPacketInfo_t * pPacketInfo,
+ MQTTDeserializedInfo_t * pDeserializedInfo )
+{
+ ( void ) pContext;
+ ( void ) pPacketInfo;
+ ( void ) pDeserializedInfo;
+ MQTTAckInfo_t ackInfo;
+ MQTTUserProperties_t user;
+ memset( &user, 0x0, sizeof( user ) );
+ memset( &ackInfo, 0x0, sizeof( ackInfo ) );
+ /* Update the global state to indicate that event callback is invoked. */
+ isEventCallbackInvoked = true;
+ pDeserializedInfo->pNextAckInfo = &ackInfo;
+
+ if( pDeserializedInfo->packetIdentifier == 1 )
+ {
+ ackInfo.reasonStringLength = 2;
+ ackInfo.pReasonString = "ab";
+ ackInfo.pUserProperty = &user;
+ user.count = 0;
+ }
+}
+
+
+/**
+ * @brief Mocked transport send that always returns 0 bytes sent.
+ */
+static int32_t transportSendNoBytes( NetworkContext_t * pNetworkContext,
+ const void * pBuffer,
+ size_t bytesToWrite )
+{
+ ( void ) pNetworkContext;
+ ( void ) pBuffer;
+ ( void ) bytesToWrite;
+ return 0;
+}
+
+static uint8_t * MQTTV5_SerializeAckFixed_cb( uint8_t * pIndex,
+ uint8_t packetType,
+ uint16_t packetId,
+ size_t remainingLength,
+ size_t propertyLength,
+ int numcallbacks )
+{
+ ( void ) packetType;
+ ( void ) packetId;
+ ( void ) remainingLength;
+ ( void ) propertyLength;
+ ( void ) numcallbacks;
+
+ return pIndex;
+}
+
+static uint8_t * MQTTV5_SerializeDisconnectFixed_cb( uint8_t * pIndex,
+ const MQTTAckInfo_t * pAckInfo,
+ size_t remainingLength,
+ uint32_t sessionExpiry,
+ int numcallbacks )
+{
+ ( void ) pIndex;
+ ( void ) pAckInfo;
+ ( void ) remainingLength;
+ ( void ) sessionExpiry;
+ ( void ) numcallbacks;
+
+ return pIndex;
+}
+
+
+
+static uint8_t * MQTT_SerializeConnectFixedHeader_cb( uint8_t * pIndex,
+ const MQTTConnectInfo_t * pConnectInfo,
+ const MQTTPublishInfo_t * pWillInfo,
+ size_t remainingLength,
+ int numcallbacks )
+{
+ ( void ) pConnectInfo;
+ ( void ) pWillInfo;
+ ( void ) remainingLength;
+ ( void ) numcallbacks;
+
+ return pIndex;
+}
+
+static uint8_t * MQTTV5_SerializeConnectProperties_cb( uint8_t * pIndex,
+ const MQTTConnectProperties_t * pConnectProperties
+ ,
+ int numcallbacks )
+{
+ ( void ) pConnectProperties;
+ ( void ) numcallbacks;
+
+ return pIndex;
+}
+
+static uint8_t * MQTT_SerializePublishProperties_cb( const MQTTPublishInfo_t * pPublishInfo,
+ uint8_t * pIndex
+ ,
+ int numcallbacks )
+{
+ ( void ) pPublishInfo;
+ ( void ) numcallbacks;
+
+ return pIndex;
+}
+
+
+/**
+ * @brief Mocked successful transport writev.
+ *
+ * @param[in] tcpSocket TCP socket.
+ * @param[in] pMessage vectors to send
+ * @param[in] bytesToWrite number of vectors
+ *
+ * @return Number of bytes sent; negative value on error;
+ * 0 for timeout or 0 bytes sent.
+ */
+static int32_t transportWritevSuccess( NetworkContext_t * pNetworkContext,
+ TransportOutVector_t * pIoVectorIterator,
+ size_t vectorsToBeSent )
+{
+ TEST_ASSERT_EQUAL( MQTT_SAMPLE_NETWORK_CONTEXT, pNetworkContext );
+ int32_t bytesToWrite = 0;
+ size_t i;
+
+ for( i = 0; i < vectorsToBeSent; ++i )
+ {
+ bytesToWrite += pIoVectorIterator->iov_len;
+ pIoVectorIterator++;
+ }
+
+ return bytesToWrite;
+}
+
+
+/**
+ * @brief Mocked successful transport send.
+ *
+ * @param[in] tcpSocket TCP socket.
+ * @param[in] pMessage Data to send.
+ * @param[in] bytesToWrite Length of data to send.
+ *
+ * @return Number of bytes sent; negative value on error;
+ * 0 for timeout or 0 bytes sent.
+ */
+static int32_t transportSendSuccess( NetworkContext_t * pNetworkContext,
+ const void * pBuffer,
+ size_t bytesToWrite )
+{
+ TEST_ASSERT_EQUAL( MQTT_SAMPLE_NETWORK_CONTEXT, pNetworkContext );
+ ( void ) pBuffer;
+ return bytesToWrite;
+}
+
+/**
+ * @brief Mocked failed transport send.
+ */
+static int32_t transportSendFailure( NetworkContext_t * pNetworkContext,
+ const void * pBuffer,
+ size_t bytesToWrite )
+{
+ ( void ) pNetworkContext;
+ ( void ) pBuffer;
+ ( void ) bytesToWrite;
+ return -1;
+}
+
+/**
+ * @brief Mocked successful transport read.
+ *
+ * @param[in] tcpSocket TCP socket.
+ * @param[out] pBuffer Buffer for receiving data.
+ * @param[in] bytesToRead Size of pBuffer.
+ *
+ * @return Number of bytes received; negative value on error.
+ */
+static int32_t transportRecvSuccess( NetworkContext_t * pNetworkContext,
+ void * pBuffer,
+ size_t bytesToRead )
+{
+ TEST_ASSERT_EQUAL( MQTT_SAMPLE_NETWORK_CONTEXT, pNetworkContext );
+ ( void ) pBuffer;
+ return bytesToRead;
+}
+
+
+/**
+ * @brief Initialize the transport interface with the mocked functions for
+ * send and receive.
+ *
+ * @brief param[in] pTransport The transport interface to use with the context.
+ */
+static void setupTransportInterface( TransportInterface_t * pTransport )
+{
+ pTransport->pNetworkContext = MQTT_SAMPLE_NETWORK_CONTEXT;
+ pTransport->send = transportSendSuccess;
+ pTransport->recv = transportRecvSuccess;
+ pTransport->writev = transportWritevSuccess;
+}
+
+/**
+ * @brief Initialize pNetworkBuffer using static buffer.
+ *
+ * @param[in] pNetworkBuffer Network buffer provided for the context.
+ */
+static void setupNetworkBuffer( MQTTFixedBuffer_t * const pNetworkBuffer )
+{
+ pNetworkBuffer->pBuffer = mqttBuffer;
+ pNetworkBuffer->size = MQTT_TEST_BUFFER_LENGTH;
+}
+
+static void setupPublishProperties( MQTTPublishInfo_t * pPublishInfo )
+{
+ pPublishInfo->payloadFormat = 1;
+ pPublishInfo->topicAlias = TEST_TOPIC_ALIAS;
+ pPublishInfo->msgExpiryInterval = TEST_MSG_EXPIRY;
+ pPublishInfo->msgExpiryPresent = 1;
+ pPublishInfo->contentTypeLength = TEST_CONTENT_TYPE_LENGTH;
+ pPublishInfo->pContentType = TEST_CONTENT_TYPE;
+ pPublishInfo->responseTopicLength = TEST_RESPONSE_TOPIC_LENGTH;
+ pPublishInfo->pResponseTopic = TEST_RESPONSE_TOPIC;
+ pPublishInfo->correlationLength = TEST_CORRELATION_DATA_LENGTH;
+ pPublishInfo->pCorrelationData = TEST_CORRELATION_DATA;
+}
+
+
+void test_MQTT_Connect()
+{
+ MQTTContext_t mqttContext = { 0 };
+ MQTTConnectInfo_t connectInfo = { 0 };
+ bool sessionPresent = false;
+ MQTTStatus_t status;
+ TransportInterface_t transport = { 0 };
+ MQTTFixedBuffer_t networkBuffer = { 0 };
+
+ setupTransportInterface( &transport );
+ setupNetworkBuffer( &networkBuffer );
+
+ memset( &mqttContext, 0x0, sizeof( mqttContext ) );
+ MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer );
+
+/* No connect Properties */
+ MQTTV5_GetConnectPacketSize_ExpectAnyArgsAndReturn( MQTTBadParameter );
+ status = MQTT_Connect( &mqttContext, &connectInfo, NULL, 0U, &sessionPresent );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+}
+
+void test_MQTT_Connect_error_path()
+{
+ MQTTContext_t mqttContext = { 0 };
+ MQTTConnectInfo_t connectInfo = { 0 };
+ MQTTPublishInfo_t willInfo = { 0 };
+ uint32_t timeout = 2;
+ bool sessionPresent;
+ MQTTStatus_t status;
+ TransportInterface_t transport = { 0 };
+ MQTTFixedBuffer_t networkBuffer = { 0 };
+ MQTTConnectProperties_t properties;
+ MQTTAuthInfo_t authInfo;
+ MQTTAuthInfo_t authInfo2;
+ MQTTUserProperties_t userProperties;
+
+ memset( &properties, 0x0, sizeof( properties ) );
+ memset( &authInfo, 0x0, sizeof( authInfo ) );
+ memset( &authInfo2, 0x0, sizeof( authInfo2 ) );
+ memset( &userProperties, 0x0, sizeof( userProperties ) );
+ userProperties.userProperty[ 0 ].pKey = "ab";
+ userProperties.userProperty[ 0 ].pValue = "ab";
+ userProperties.userProperty[ 0 ].keyLength = 2;
+ userProperties.userProperty[ 0 ].valueLength = 2;
+ userProperties.count = 1;
+ authInfo.pAuthMethod = "2";
+ authInfo.authMethodLength = 1;
+ authInfo.pAuthData = "ab";
+ authInfo.authDataLength = 2;
+ setupTransportInterface( &transport );
+ setupNetworkBuffer( &networkBuffer );
+ memset( &mqttContext, 0x0, sizeof( mqttContext ) );
+ memset( &willInfo, 0x0, sizeof( MQTTPublishInfo_t ) );
+ memset( &connectInfo, 0x0, sizeof( MQTTConnectInfo_t ) );
+ MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer );
+
+ /* With non-NULL Will. */
+ mqttContext.connectStatus = MQTTNotConnected;
+ mqttContext.keepAliveIntervalSec = 0;
+ mqttContext.pConnectProperties = &properties;
+ properties.sessionExpiry = 13;
+ properties.receiveMax = 12;
+ properties.maxPacketSize = 1000;
+ properties.topicAliasMax = 13;
+ properties.requestProblemInfo = 0;
+ properties.requestResponseInfo = 1;
+ properties.pOutgoingUserProperty = &userProperties;
+ properties.pOutgoingAuth = &authInfo;
+ properties.pIncomingAuth = &authInfo2;
+
+ willInfo.pTopicName = "test";
+ willInfo.topicNameLength = 4;
+ willInfo.pPayload = "Payload";
+ willInfo.payloadLength = 7;
+ willInfo.payloadFormat = 1;
+ willInfo.msgExpiryPresent = 1;
+ willInfo.msgExpiryInterval = 10;
+ willInfo.msgExpiryPresent = 1;
+ willInfo.msgExpiryInterval = 10;
+ willInfo.contentTypeLength = 2;
+ willInfo.pContentType = "ab";
+ willInfo.responseTopicLength = 2;
+ willInfo.pResponseTopic = "ab";
+ willInfo.correlationLength = 2;
+ willInfo.pCorrelationData = "ab";
+ willInfo.willDelay = 3;
+ willInfo.pUserProperty = &userProperties;
+ connectInfo.pUserName = "abcd";
+ connectInfo.userNameLength = 3;
+ connectInfo.passwordLength = 4;
+ connectInfo.pPassword = "1234";
+ mqttContext.transportInterface.send = transportSendSuccess;
+ MQTTV5_GetConnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb );
+ MQTTV5_SerializeConnectProperties_Stub( MQTTV5_SerializeConnectProperties_cb );
+ MQTT_SerializePublishProperties_Stub( MQTT_SerializePublishProperties_cb );
+ MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTRecvFailed );
+ status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
+ TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status );
+
+ properties.pOutgoingUserProperty = NULL;
+ MQTTV5_GetConnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb );
+ MQTTV5_SerializeConnectProperties_Stub( MQTTV5_SerializeConnectProperties_cb );
+ MQTT_SerializePublishProperties_Stub( MQTT_SerializePublishProperties_cb );
+ MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTRecvFailed );
+ status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent );
+ TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status );
+ properties.pOutgoingAuth->authDataLength = 0;
+ MQTTV5_GetConnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb );
+ MQTTV5_SerializeConnectProperties_Stub( MQTTV5_SerializeConnectProperties_cb );
+ MQTT_SerializePublishProperties_Stub( MQTT_SerializePublishProperties_cb );
+ MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTRecvFailed );
+ status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent );
+ TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status );
+
+ properties.pOutgoingAuth = NULL;
+ willInfo.contentTypeLength = 0;
+ willInfo.responseTopicLength = 0;
+ willInfo.correlationLength = 0;
+ willInfo.pUserProperty = NULL;
+ MQTTV5_GetConnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb );
+ MQTTV5_SerializeConnectProperties_Stub( MQTTV5_SerializeConnectProperties_cb );
+ MQTT_SerializePublishProperties_Stub( MQTT_SerializePublishProperties_cb );
+ MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTRecvFailed );
+ status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent );
+ TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status );
+
+ willInfo.pTopicName = NULL;
+ mqttContext.transportInterface.send = transportSendSuccess;
+ MQTTV5_GetConnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb );
+ MQTTV5_SerializeConnectProperties_Stub( MQTTV5_SerializeConnectProperties_cb );
+ MQTT_SerializePublishProperties_Stub( MQTT_SerializePublishProperties_cb );
+ status = MQTT_Connect( &mqttContext, &connectInfo, &willInfo, timeout, &sessionPresent );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+}
+
+void test_MQTT_Connect_receiveConnack( void )
+{
+ MQTTContext_t mqttContext = { 0 };
+ MQTTConnectInfo_t connectInfo = { 0 };
+ uint32_t timeout = 0;
+ bool sessionPresent;
+ MQTTStatus_t status;
+ TransportInterface_t transport = { 0 };
+ MQTTFixedBuffer_t networkBuffer = { 0 };
+ MQTTConnectProperties_t properties = { 0 };
+ MQTTPacketInfo_t incomingPacket = { 0 };
+
+ setupTransportInterface( &transport );
+ setupNetworkBuffer( &networkBuffer );
+ transport.recv = transportRecvFailure;
+
+ memset( &mqttContext, 0x0, sizeof( mqttContext ) );
+ memset( &properties, 0x0, sizeof( properties ) );
+ MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer );
+ mqttContext.pConnectProperties = &properties;
+ /* Everything before receiving the CONNACK should succeed. */
+ MQTTV5_SerializeConnect_IgnoreAndReturn( MQTTSuccess );
+ MQTTV5_GetConnectPacketSize_IgnoreAndReturn( MQTTSuccess );
+
+ /* Nothing received from transport interface. Set timeout to 2 for branch coverage. */
+ timeout = 2;
+ MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTNoDataAvailable );
+ MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTNoDataAvailable );
+ MQTT_SerializeConnectFixedHeader_Stub( MQTT_SerializeConnectFixedHeader_cb );
+ MQTTV5_SerializeConnectProperties_Stub( MQTTV5_SerializeConnectProperties_cb );
+ MQTT_SerializePublishProperties_Stub( MQTT_SerializePublishProperties_cb );
+ status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
+ TEST_ASSERT_EQUAL_INT( MQTTNoDataAvailable, status );
+
+ /* Did not receive a CONNACK. */
+ incomingPacket.type = MQTT_PACKET_TYPE_PINGRESP;
+ incomingPacket.remainingLength = 0;
+ MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
+ status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
+ TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status );
+
+ /* Transport receive failure when receiving rest of packet. */
+ incomingPacket.type = MQTT_PACKET_TYPE_CONNACK;
+ incomingPacket.remainingLength = 2;
+ timeout = 2;
+ mqttContext.transportInterface.recv = transportRecvFailure;
+ MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
+ status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
+ TEST_ASSERT_EQUAL_INT( MQTTRecvFailed, status );
+
+ mqttContext.transportInterface.recv = transportRecvSuccess;
+ MQTT_GetIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_GetIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
+ MQTTV5_DeserializeConnack_ExpectAnyArgsAndReturn( MQTTBadResponse );
+ status = MQTT_Connect( &mqttContext, &connectInfo, NULL, timeout, &sessionPresent );
+ TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status );
+}
+
+void test_MQTT_Publish( void )
+{
+ MQTTContext_t mqttContext = { 0 };
+ MQTTPublishInfo_t publishInfo = { 0 };
+ MQTTConnectProperties_t properties = { 0 };
+ TransportInterface_t transport = { 0 };
+ MQTTFixedBuffer_t networkBuffer = { 0 };
+ MQTTStatus_t status;
+
+ const uint16_t PACKET_ID = 1;
+
+ setupTransportInterface( &transport );
+ setupNetworkBuffer( &networkBuffer );
+ transport.send = transportSendFailure;
+
+ memset( &mqttContext, 0x0, sizeof( mqttContext ) );
+ memset( &publishInfo, 0x0, sizeof( publishInfo ) );
+ MQTT_Init( &mqttContext, &transport, getTime, eventCallback, &networkBuffer );
+
+ /*Connect properties not defined*/
+ status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+ mqttContext.pConnectProperties = &properties;
+
+ properties.maxPacketSize = 10000;
+ MQTTV5_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTTV5_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_SerializePublishProperties_Stub( MQTT_SerializePublishProperties_cb );
+ status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+
+ MQTTV5_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTBadParameter );
+ status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /*Send With properties*/
+ setupPublishProperties( &publishInfo );
+ MQTTV5_ValidatePublishParams_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTTV5_GetPublishPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_SerializePublishHeaderWithoutTopic_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_SerializePublishProperties_Stub( MQTT_SerializePublishProperties_cb );
+ status = MQTT_Publish( &mqttContext, &publishInfo, PACKET_ID );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+}
+
+void test_MQTT_ProcessLoop_handleIncomingAck_Happy_Paths( void )
+{
+ MQTTStatus_t status;
+ MQTTPublishState_t stateAfterDeserialize;
+ MQTTContext_t context = { 0 };
+ TransportInterface_t transport = { 0 };
+ MQTTFixedBuffer_t networkBuffer = { 0 };
+ MQTTConnectProperties_t properties;
+ MQTTPacketInfo_t incomingPacket = { 0 };
+
+ setupTransportInterface( &transport );
+ setupNetworkBuffer( &networkBuffer );
+ /* Modify incoming packet depending on type to be tested. */
+ incomingPacket.type = MQTT_PACKET_TYPE_PUBREC;
+ incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH;
+ incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH;
+ status = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ properties.requestProblemInfo = 1;
+ context.pConnectProperties = &properties;
+ modifyIncomingPacketStatus = MQTTSuccess;
+ stateAfterDeserialize = MQTTPubRelSend;
+ MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
+ MQTTV5_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_SerializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterDeserialize );
+ MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess );
+ status = MQTT_ProcessLoop( &context );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+}
+
+void test_MQTT_ProcessLoop_handleIncomingAck_Error_Paths1( void )
+{
+ MQTTStatus_t status;
+ MQTTPublishState_t stateAfterDeserialize;
+ MQTTPublishState_t stateAfterSerialize;
+ MQTTContext_t context = { 0 };
+ TransportInterface_t transport = { 0 };
+ MQTTFixedBuffer_t networkBuffer = { 0 };
+ MQTTConnectProperties_t properties;
+ MQTTPacketInfo_t incomingPacket = { 0 };
+
+ /*Update state returns bad response.*/
+ setupTransportInterface( &transport );
+ setupNetworkBuffer( &networkBuffer );
+ incomingPacket.type = MQTT_PACKET_TYPE_PUBREC;
+ incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH;
+ incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH;
+ status = MQTT_Init( &context, &transport, getTime, eventCallback1, &networkBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ properties.requestProblemInfo = 1;
+ context.pConnectProperties = &properties;
+ modifyIncomingPacketStatus = MQTTSuccess;
+ stateAfterDeserialize = MQTTPubRelSend;
+ stateAfterSerialize = MQTTPubCompPending;
+ MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
+ MQTTV5_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterDeserialize );
+ MQTTV5_GetAckPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTTV5_SerializeAckFixed_Stub( MQTTV5_SerializeAckFixed_cb );
+ MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTBadResponse );
+ MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterSerialize );
+ status = MQTT_ProcessLoop( &context );
+ TEST_ASSERT_EQUAL_INT( MQTTBadResponse, status );
+}
+
+void test_MQTT_ProcessLoop_handleIncomingAck_Error_Paths2( void )
+{
+ MQTTStatus_t status;
+ MQTTContext_t context = { 0 };
+ TransportInterface_t transport = { 0 };
+ MQTTFixedBuffer_t networkBuffer = { 0 };
+ MQTTConnectProperties_t properties;
+ MQTTPacketInfo_t incomingPacket = { 0 };
+
+ /*Update state returns bad response.*/
+ setupTransportInterface( &transport );
+ setupNetworkBuffer( &networkBuffer );
+ status = MQTT_Init( &context, &transport, getTime, eventCallback1, &networkBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ incomingPacket.type = MQTT_PACKET_TYPE_PUBREC;
+ incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH;
+ incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH;
+ properties.requestProblemInfo = 1;
+ context.pConnectProperties = &properties;
+ modifyIncomingPacketStatus = MQTTSuccess;
+ MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
+ MQTTV5_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTBadParameter );
+ status = MQTT_ProcessLoop( &context );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+}
+
+void test_MQTT_ProcessLoop_handleIncomingAck_Error_Paths3( void )
+{
+ MQTTStatus_t status;
+ MQTTPublishState_t stateAfterDeserialize;
+ MQTTContext_t context = { 0 };
+ TransportInterface_t transport = { 0 };
+ MQTTFixedBuffer_t networkBuffer = { 0 };
+ MQTTConnectProperties_t properties;
+ uint16_t packetId = 2;
+ MQTTPacketInfo_t incomingPacket = { 0 };
+
+ /*Invalid packet parameters.*/
+ setupTransportInterface( &transport );
+ setupNetworkBuffer( &networkBuffer );
+ incomingPacket.type = MQTT_PACKET_TYPE_PUBREC;
+ incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH;
+ incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH;
+ status = MQTT_Init( &context, &transport, getTime, eventCallback1, &networkBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ properties.requestProblemInfo = 1;
+ context.pConnectProperties = &properties;
+ modifyIncomingPacketStatus = MQTTSuccess;
+ stateAfterDeserialize = MQTTPubRelSend;
+ MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
+ MQTTV5_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTTV5_DeserializeAck_ReturnThruPtr_pPacketId( &packetId );
+ MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterDeserialize );
+ MQTTV5_GetAckPacketSize_ExpectAnyArgsAndReturn( MQTTBadParameter );
+ status = MQTT_ProcessLoop( &context );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+}
+
+void test_MQTT_ProcessLoop_handleIncomingAck_Happy_Paths2( void )
+{
+ MQTTStatus_t status;
+ MQTTPublishState_t stateAfterDeserialize;
+ MQTTPublishState_t stateAfterSerialize;
+ MQTTContext_t context = { 0 };
+ TransportInterface_t transport = { 0 };
+ MQTTFixedBuffer_t networkBuffer = { 0 };
+ MQTTConnectProperties_t properties;
+ uint16_t packetId = 1;
+ MQTTPacketInfo_t incomingPacket = { 0 };
+
+ /*Using event call back to set reason string and user properties,*/
+ setupTransportInterface( &transport );
+ setupNetworkBuffer( &networkBuffer );
+ incomingPacket.type = MQTT_PACKET_TYPE_PUBREC;
+ incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH;
+ incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH;
+ status = MQTT_Init( &context, &transport, getTime, eventCallback1, &networkBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ properties.requestProblemInfo = 1;
+ context.pConnectProperties = &properties;
+ modifyIncomingPacketStatus = MQTTSuccess;
+ stateAfterDeserialize = MQTTPubRelSend;
+ stateAfterSerialize = MQTTPubCompPending;
+ MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
+ MQTTV5_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTTV5_DeserializeAck_ReturnThruPtr_pPacketId( &packetId );
+ MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterDeserialize );
+ MQTTV5_GetAckPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTTV5_SerializeAckFixed_Stub( MQTTV5_SerializeAckFixed_cb );
+ MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterSerialize );
+ status = MQTT_ProcessLoop( &context );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+}
+
+void test_MQTT_ProcessLoop_handleIncomingAck_Error_Paths4( void )
+{
+ MQTTStatus_t status;
+ MQTTPublishState_t stateAfterDeserialize;
+ MQTTContext_t context = { 0 };
+ TransportInterface_t transport = { 0 };
+ MQTTFixedBuffer_t networkBuffer = { 0 };
+ MQTTConnectProperties_t properties;
+ MQTTPacketInfo_t incomingPacket = { 0 };
+
+ /*Invalid packet parameters.*/
+ setupTransportInterface( &transport );
+ setupNetworkBuffer( &networkBuffer );
+ incomingPacket.type = MQTT_PACKET_TYPE_PUBREC;
+ incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH;
+ incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH;
+ status = MQTT_Init( &context, &transport, getTime, eventCallback1, &networkBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ properties.requestProblemInfo = 1;
+ context.pConnectProperties = &properties;
+ modifyIncomingPacketStatus = MQTTSuccess;
+ stateAfterDeserialize = MQTTPubRelPending;
+ MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
+ MQTTV5_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterDeserialize );
+ MQTTV5_GetAckPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
+ status = MQTT_ProcessLoop( &context );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+}
+
+void test_MQTT_ProcessLoop_handleIncomingAck_Error_Paths5( void )
+{
+ MQTTStatus_t status;
+ MQTTPublishState_t stateAfterDeserialize;
+ MQTTContext_t context = { 0 };
+ TransportInterface_t transport = { 0 };
+ MQTTFixedBuffer_t networkBuffer = { 0 };
+ MQTTConnectProperties_t properties;
+ MQTTPacketInfo_t incomingPacket = { 0 };
+ uint16_t packetId = 1;
+
+ /*Unable to send the packet using transport interface.*/
+ setupTransportInterface( &transport );
+ setupNetworkBuffer( &networkBuffer );
+ incomingPacket.type = MQTT_PACKET_TYPE_PUBREC;
+ incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH;
+ incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH;
+ status = MQTT_Init( &context, &transport, getTime, eventCallback1, &networkBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ context.transportInterface.send = transportSendNoBytes;
+ context.transportInterface.writev = NULL;
+ properties.requestProblemInfo = 1;
+ context.pConnectProperties = &properties;
+ modifyIncomingPacketStatus = MQTTSuccess;
+ stateAfterDeserialize = MQTTPubRelSend;
+ MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
+ MQTTV5_DeserializeAck_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTTV5_DeserializeAck_ReturnThruPtr_pPacketId( &packetId );
+ MQTT_UpdateStateAck_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_UpdateStateAck_ReturnThruPtr_pNewState( &stateAfterDeserialize );
+ MQTTV5_GetAckPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTTV5_SerializeAckFixed_Stub( MQTTV5_SerializeAckFixed_cb );
+ status = MQTT_ProcessLoop( &context );
+ TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status );
+}
+
+void test_MQTTV5_Disconnect()
+{
+ uint32_t sessionExpiry = 0U;
+ MQTTStatus_t status;
+ MQTTConnectProperties_t properties = { 0 };
+ MQTTAckInfo_t ackInfo = { 0 };
+ MQTTContext_t context = { 0 };
+ TransportInterface_t transport = { 0 };
+ MQTTFixedBuffer_t networkBuffer = { 0 };
+ MQTTUserProperties_t userProperties = { 0 };
+
+ setupTransportInterface( &transport );
+ setupNetworkBuffer( &networkBuffer );
+ status = MQTT_Init( &context, &transport, getTime, eventCallback, &networkBuffer );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+
+ /*Invalid parameters*/
+ status = MQTTV5_Disconnect( NULL, &ackInfo, sessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ status = MQTTV5_Disconnect( &context, NULL, sessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /*Connect properties not initialized*/
+ status = MQTTV5_Disconnect( &context, &ackInfo, sessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /*Bad Parameters*/
+ context.pConnectProperties = &properties;
+ MQTTV5_GetDisconnectPacketSize_ExpectAnyArgsAndReturn( MQTTBadParameter );
+ status = MQTTV5_Disconnect( &context, &ackInfo, sessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTBadParameter, status );
+
+ /*Valid parameters*/
+ properties.maxPacketSize = 100U;
+ properties.sessionExpiry = 10U;
+ MQTTV5_GetDisconnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTTV5_SerializeDisconnectFixed_Stub( MQTTV5_SerializeDisconnectFixed_cb );
+ status = MQTTV5_Disconnect( &context, &ackInfo, sessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+
+ /*With reason string and user property*/
+ ackInfo.pUserProperty = &userProperties;
+ ackInfo.pReasonString = "test";
+ ackInfo.reasonStringLength = 4;
+ MQTTV5_GetDisconnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTTV5_SerializeDisconnectFixed_Stub( MQTTV5_SerializeDisconnectFixed_cb );
+ status = MQTTV5_Disconnect( &context, &ackInfo, sessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+
+ /*Send failed*/
+ context.transportInterface.send = transportSendFailure;
+ context.transportInterface.writev = NULL;
+ MQTTV5_GetDisconnectPacketSize_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTTV5_SerializeDisconnectFixed_Stub( MQTTV5_SerializeDisconnectFixed_cb );
+ status = MQTTV5_Disconnect( &context, &ackInfo, sessionExpiry );
+ TEST_ASSERT_EQUAL_INT( MQTTSendFailed, status );
+}
+
+void test_MQTT_ProcessLoop_handleIncomingDisconnect( void )
+{
+ MQTTStatus_t status;
+ MQTTContext_t context = { 0 };
+ TransportInterface_t transport = { 0 };
+ MQTTFixedBuffer_t networkBuffer = { 0 };
+ MQTTConnectProperties_t properties;
+ MQTTAckInfo_t disconnectInfo;
+ MQTTPacketInfo_t incomingPacket = { 0 };
+
+ memset( &disconnectInfo, 0x0, sizeof( disconnectInfo ) );
+ setupTransportInterface( &transport );
+ setupNetworkBuffer( &networkBuffer );
+ status = MQTT_Init( &context, &transport, getTime, eventCallback1, &networkBuffer );
+ TEST_ASSERT_EQUAL( MQTTSuccess, status );
+ context.pDisconnectInfo = &disconnectInfo;
+ context.pConnectProperties = &properties;
+ incomingPacket.type = MQTT_PACKET_TYPE_DISCONNECT;
+ incomingPacket.remainingLength = MQTT_SAMPLE_REMAINING_LENGTH;
+ incomingPacket.headerLength = MQTT_SAMPLE_REMAINING_LENGTH;
+ MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
+ MQTTV5_DeserializeDisconnect_IgnoreAndReturn( MQTTSuccess );
+ status = MQTT_ProcessLoop( &context );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+
+ /*Invalid packet parameters.*/
+ MQTT_ProcessIncomingPacketTypeAndLength_ExpectAnyArgsAndReturn( MQTTSuccess );
+ MQTT_ProcessIncomingPacketTypeAndLength_ReturnThruPtr_pIncomingPacket( &incomingPacket );
+ MQTTV5_DeserializeDisconnect_IgnoreAndReturn( MQTTProtocolError );
+ status = MQTT_ProcessLoop( &context );
+ TEST_ASSERT_EQUAL_INT( MQTTProtocolError, status );
+}
diff --git a/test/unit-test/WithoutUP/CMakeLists.txt b/test/unit-test/WithoutUP/CMakeLists.txt
new file mode 100644
index 000000000..db4a66df3
--- /dev/null
+++ b/test/unit-test/WithoutUP/CMakeLists.txt
@@ -0,0 +1,95 @@
+# Include filepaths for source and include.
+include( ${MODULE_ROOT_DIR}/mqttFilePaths.cmake )
+
+# ==================== Define your project name (edit) ========================
+set(project_name "core_mqttv5WUP")
+
+
+# ===================== Create your mock here (edit) ========================
+
+# list the files to mock here
+list(APPEND mock_list
+ "${MODULE_ROOT_DIR}/source/include/core_mqtt_serializer.h"
+ "${MODULE_ROOT_DIR}/source/include/core_mqtt_state.h"
+ )
+# list the directories your mocks need
+list(APPEND mock_include_list
+ .
+ ${CMAKE_CURRENT_LIST_DIR}/logging
+ ${MQTT_INCLUDE_PUBLIC_DIRS}
+ )
+#list the definitions of your mocks to control what to be included
+list(APPEND mock_define_list
+ ""
+ )
+
+# ================= Create the library under test here (edit) ==================
+
+# list the files you would like to test here
+list(APPEND real_source_files
+ ${MQTT_SOURCES}
+ ${MQTT_SERIALIZER_SOURCES}
+ )
+# list the directories the module under test includes
+list(APPEND real_include_directories
+ .
+ ${CMAKE_CURRENT_LIST_DIR}/logging
+ ${MQTT_INCLUDE_PUBLIC_DIRS}
+ )
+
+# ===================== Create UnitTest Code here (edit) =====================
+
+# list the directories your test needs to include
+list(APPEND test_include_directories
+ .
+ ${MQTT_INCLUDE_PUBLIC_DIRS}
+ )
+
+# ============================= (end edit) ===================================
+
+set(mock_name "${project_name}_mock")
+set(real_name "${project_name}_real")
+
+create_mock_list(${mock_name}
+ "${mock_list}"
+ "${MODULE_ROOT_DIR}/tools/cmock/project.yml"
+ "${mock_include_list}"
+ "${mock_define_list}"
+ )
+
+create_real_library(${real_name}
+ "${real_source_files}"
+ "${real_include_directories}"
+ "${mock_name}"
+ )
+
+list(APPEND utest_link_list
+ -l${mock_name}
+ lib${real_name}.a
+ )
+
+list(APPEND utest_dep_list
+ ${real_name}
+ )
+
+# need to redefine because the tests below don't use any mocks
+set(utest_link_list "")
+list(APPEND utest_link_list
+ lib${real_name}.a
+ )
+
+# mqtt_serializer_utest
+set(utest_name "${project_name}_serializer_utest")
+set(utest_source "${project_name}_serializer_utest.c")
+
+set(utest_link_list "")
+list(APPEND utest_link_list
+ lib${real_name}.a
+ )
+
+create_test(${utest_name}
+ ${utest_source}
+ "${utest_link_list}"
+ "${utest_dep_list}"
+ "${test_include_directories}"
+ )
\ No newline at end of file
diff --git a/test/unit-test/WithoutUP/cmock_opaque_types.h b/test/unit-test/WithoutUP/cmock_opaque_types.h
new file mode 100644
index 000000000..d29ef7b9b
--- /dev/null
+++ b/test/unit-test/WithoutUP/cmock_opaque_types.h
@@ -0,0 +1,36 @@
+/*
+ * coreMQTT
+ * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef CMOCK_OPAQUE_TYPES_H_
+#define CMOCK_OPAQUE_TYPES_H_
+
+/* CMock does not support opaque types so needs concrete definitions for them.
+ * This file is included in CMock .c files. */
+
+struct NetworkContext
+{
+ int a;
+};
+
+#endif /* ifndef CMOCK_OPAQUE_TYPES_H_ */
diff --git a/test/unit-test/WithoutUP/core_mqtt_config.h b/test/unit-test/WithoutUP/core_mqtt_config.h
new file mode 100644
index 000000000..636473e4d
--- /dev/null
+++ b/test/unit-test/WithoutUP/core_mqtt_config.h
@@ -0,0 +1,79 @@
+/*
+ * coreMQTT
+ * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @file core_mqtt_config.h
+ * @brief This header sets configuration macros for the MQTT library.
+ */
+#ifndef CORE_MQTT_CONFIG_H_
+#define CORE_MQTT_CONFIG_H_
+
+/* Standard include. */
+#include
+
+/**************************************************/
+/******* DO NOT CHANGE the following order ********/
+/**************************************************/
+
+/* Include logging header files and define logging macros in the following order:
+ * 1. Include the header file "logging_levels.h".
+ * 2. Define the LIBRARY_LOG_NAME and LIBRARY_LOG_LEVEL macros depending on
+ * the logging configuration for MQTT.
+ * 3. Include the header file "logging_stack.h", if logging is enabled for MQTT.
+ */
+
+#include "../logging/logging_levels.h"
+
+/* Logging configuration for the MQTT library. */
+#ifndef LIBRARY_LOG_NAME
+ #define LIBRARY_LOG_NAME "MQTT"
+#endif
+
+#ifndef LIBRARY_LOG_LEVEL
+ #define LIBRARY_LOG_LEVEL LOG_NONE
+#endif
+
+#include "../logging/logging_stack.h"
+
+/************ End of logging configuration ****************/
+
+/**
+ * @brief Retry count for reading CONNACK from network.
+ *
+ * #MQTT_Connect() can be using retries. If timeout passed as 0 to MQTT_Connect(),
+ * retries are used to attempt to read from network. The maximum retry count is
+ * specified by this config.
+ *
+ * These unit tests expect retrying only twice.
+ */
+#define MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT ( 2U )
+
+#define MQTT_SUB_UNSUB_MAX_VECTORS ( 6U )
+
+#define MQTT_SEND_TIMEOUT_MS ( 200U )
+#define MQTT_VERSION_5_ENABLED ( true )
+#define MAX_USER_PROPERTY ( 0U )
+#define MQTT_USER_PROPERTY_ENABLED ( false )
+
+#endif /* ifndef CORE_MQTT_CONFIG_H_ */
diff --git a/test/unit-test/WithoutUP/core_mqttv5WUP_serializer_utest.c b/test/unit-test/WithoutUP/core_mqttv5WUP_serializer_utest.c
new file mode 100644
index 000000000..2e19d9285
--- /dev/null
+++ b/test/unit-test/WithoutUP/core_mqttv5WUP_serializer_utest.c
@@ -0,0 +1,322 @@
+/*
+ * coreMQTT
+ * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @file core_mqtt_serializer_utest.c
+ * @brief Unit tests for functions in core_mqtt_serializer.h.
+ */
+#include
+#include
+#include
+#include "unity.h"
+
+/* Include paths for public enums, structures, and macros. */
+#include "core_mqtt_serializer.h"
+
+/* Set network context to double pointer to buffer (uint8_t**). */
+struct NetworkContext
+{
+ uint8_t ** buffer;
+};
+
+
+#define MQTT_TEST_UTF8_STRING ( "test" )
+#define MQTT_TEST_UTF8_STRING_LENGTH ( sizeof( MQTT_TEST_UTF8_STRING ) - 1 )
+
+/**
+ * @brief Set a bit in an 8-bit unsigned integer.
+ */
+#define UINT8_SET_BIT( x, position ) ( ( x ) = ( uint8_t ) ( ( x ) | ( 0x01U << ( position ) ) ) )
+
+/**
+ * @brief Macro for checking if a bit is set in a 1-byte unsigned int.
+ *
+ * @param[in] x The unsigned int to check.
+ * @param[in] position Which bit to check.
+ */
+#define UINT8_CHECK_BIT( x, position ) ( ( ( x ) & ( 0x01U << ( position ) ) ) == ( 0x01U << ( position ) ) )
+
+/**
+ * @brief Get the high byte of a 16-bit unsigned integer.
+ */
+#define UINT16_HIGH_BYTE( x ) ( ( uint8_t ) ( ( x ) >> 8 ) )
+
+/**
+ * @brief Get the low byte of a 16-bit unsigned integer.
+ */
+#define UINT16_LOW_BYTE( x ) ( ( uint8_t ) ( ( x ) & 0x00ffU ) )
+
+#define MQTT_USER_PROPERTY_ID ( 0x26 )
+#define MQTT_REASON_STRING_ID ( 0x1F )
+#define MQTT_SERVER_REF_ID ( 0x1C )
+
+
+#define CORE_MQTT_ID_SIZE ( 1U )
+#define MQTT_REMAINING_LENGTH_INVALID ( ( size_t ) 268435456 )
+
+/* Variables common to testcases */
+MQTTConnectProperties_t properties;
+MQTTConnectInfo_t connectInfo;
+MQTTPacketInfo_t packetInfo;
+MQTTStatus_t status;
+
+/* ============================ UNITY FIXTURES ============================ */
+
+/* Called before each test method. */
+void setUp( void )
+{
+ memset( &properties, 0x0, sizeof( properties ) );
+ memset( &connectInfo, 0x0, sizeof( connectInfo ) );
+ memset( &packetInfo, 0x0, sizeof( packetInfo ) );
+}
+
+/* Called after each test method. */
+void tearDown( void )
+{
+}
+
+/* Called at the beginning of the whole suite. */
+void suiteSetUp()
+{
+}
+
+/* Called at the end of the whole suite. */
+int suiteTearDown( int numFailures )
+{
+ return numFailures;
+}
+
+/* ========================================================================== */
+
+/**
+ * @brief Encode remaining length into pDestination for packet serialization
+ * using MQTT v3.1.1 spec.
+ *
+ * @param[in] pDestination Buffer to write encoded remaining length.
+ * @param[in] length Actual remaining length.
+ */
+static size_t encodeRemainingLength( uint8_t * pDestination,
+ size_t length )
+{
+ uint8_t lengthByte;
+ uint8_t * pLengthEnd = NULL;
+ size_t remainingLength = length;
+
+ TEST_ASSERT_NOT_NULL( pDestination );
+
+ pLengthEnd = pDestination;
+
+ /* This algorithm is copied from the MQTT v3.1.1 spec. */
+ do
+ {
+ lengthByte = ( uint8_t ) ( remainingLength % 128U );
+ remainingLength = remainingLength / 128U;
+
+ /* Set the high bit of this byte, indicating that there's more data. */
+ if( remainingLength > 0U )
+ {
+ UINT8_SET_BIT( lengthByte, 7 );
+ }
+
+ /* Output a single encoded byte. */
+ *pLengthEnd = lengthByte;
+ pLengthEnd++;
+ } while( remainingLength > 0U );
+
+ return ( size_t ) ( pLengthEnd - pDestination );
+}
+
+/**
+ * @brief Encode UTF-8 string and its length into pDestination for
+ * packet serialization.
+ *
+ * @param[in] pDestination Buffer to write encoded string.
+ * @param[in] source String to encode.
+ * @param[in] sourceLength Length of the string to encode.
+ */
+static size_t encodeString( uint8_t * pDestination,
+ const char * source,
+ uint16_t sourceLength )
+{
+ uint8_t * pBuffer = NULL;
+
+ /* Typecast const char * typed source buffer to const uint8_t *.
+ * This is to use same type buffers in memcpy. */
+ const uint8_t * pSourceBuffer = ( const uint8_t * ) source;
+
+ TEST_ASSERT_NOT_NULL( pSourceBuffer );
+ TEST_ASSERT_NOT_NULL( pDestination );
+
+ pBuffer = pDestination;
+
+ /* The first byte of a UTF-8 string is the high byte of the string length. */
+ *pBuffer = UINT16_HIGH_BYTE( sourceLength );
+ pBuffer++;
+
+ /* The second byte of a UTF-8 string is the low byte of the string length. */
+ *pBuffer = UINT16_LOW_BYTE( sourceLength );
+ pBuffer++;
+
+ /* Copy the string into pBuffer. */
+ ( void ) memcpy( pBuffer, pSourceBuffer, sourceLength );
+
+ /* Return the pointer to the end of the encoded string. */
+ pBuffer += sourceLength;
+
+ return ( size_t ) ( pBuffer - pDestination );
+}
+
+static uint8_t * serializeutf_8( uint8_t * pIndex,
+ uint8_t propertyId )
+{
+ uint8_t * pIndexLocal = pIndex;
+
+ *pIndexLocal = propertyId;
+ pIndexLocal++;
+ size_t dummy = encodeString( pIndexLocal, MQTT_TEST_UTF8_STRING, MQTT_TEST_UTF8_STRING_LENGTH );
+ pIndexLocal = &pIndexLocal[ dummy ];
+ return pIndexLocal;
+}
+
+static uint8_t * serializeutf_8pair( uint8_t * pIndex )
+{
+ uint8_t * pIndexLocal = pIndex;
+
+ *pIndexLocal = MQTT_USER_PROPERTY_ID;
+ pIndexLocal++;
+ size_t dummy = encodeString( pIndexLocal, MQTT_TEST_UTF8_STRING, MQTT_TEST_UTF8_STRING_LENGTH );
+ pIndexLocal = &pIndexLocal[ dummy ];
+ dummy = encodeString( pIndexLocal, MQTT_TEST_UTF8_STRING, MQTT_TEST_UTF8_STRING_LENGTH );
+ pIndexLocal = &pIndexLocal[ dummy ];
+ return pIndexLocal;
+}
+
+void test_MQTTV5_DeserializeConnackOnlyUserProperty( void )
+{
+ uint8_t buffer[ 100 ] = { 0 };
+ uint8_t * pIndexLocal = buffer;
+
+ buffer[ 0 ] = 0x01;
+ buffer[ 1 ] = 0x00;
+
+ bool session = false;
+ properties.maxPacketSize = 100U;
+ packetInfo.pRemainingData = buffer;
+ packetInfo.type = MQTT_PACKET_TYPE_CONNACK;
+ packetInfo.remainingLength = 16;
+ pIndexLocal = &buffer[ 2 ];
+ size_t propertyLength = encodeRemainingLength( pIndexLocal, 13 );
+ pIndexLocal++;
+ pIndexLocal = serializeutf_8pair( pIndexLocal );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+
+ /*Invalid property length*/
+ packetInfo.remainingLength = 5;
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 2 );
+ pIndexLocal++;
+ pIndexLocal = serializeutf_8pair( pIndexLocal );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTMalformedPacket, status );
+
+ /*Invalid property length*/
+ packetInfo.remainingLength = 6;
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 3 );
+ pIndexLocal++;
+ pIndexLocal = serializeutf_8pair( pIndexLocal );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTMalformedPacket, status );
+
+ /*Invalid property length*/
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 8 );
+ packetInfo.remainingLength = propertyLength + 10;
+ pIndexLocal++;
+ pIndexLocal = serializeutf_8pair( pIndexLocal );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTMalformedPacket, status );
+
+ /*Invalid property length*/
+ packetInfo.remainingLength = 15;
+ pIndexLocal = &buffer[ 2 ];
+ propertyLength = encodeRemainingLength( pIndexLocal, 12 );
+ pIndexLocal++;
+ pIndexLocal = serializeutf_8pair( pIndexLocal );
+ status = MQTTV5_DeserializeConnack( &properties, &packetInfo, &session );
+ TEST_ASSERT_EQUAL_INT( MQTTMalformedPacket, status );
+}
+void test_MQTTV5_DeserializeAck( void )
+{
+ MQTTAckInfo_t ackInfo;
+ uint16_t packetIdentifier;
+ uint32_t maxPacketSize = 200U;
+ bool requestProblem = true;
+ MQTTStatus_t status = MQTTSuccess;
+ uint8_t buffer[ 100 ] = { 0 };
+ uint8_t * pIndex = buffer;
+ size_t dummy;
+
+ /* Verify parameters */
+ memset( &ackInfo, 0x00, sizeof( ackInfo ) );
+ packetInfo.pRemainingData = buffer;
+ buffer[ 0 ] = 0;
+ buffer[ 1 ] = 1;
+ buffer[ 2 ] = 0x00;
+ pIndex = &buffer[ 3 ];
+ packetInfo.type = MQTT_PACKET_TYPE_PUBACK;
+ dummy = encodeRemainingLength( pIndex, 20 );
+ packetInfo.remainingLength = dummy + 23;
+ pIndex++;
+ pIndex = serializeutf_8( pIndex, MQTT_REASON_STRING_ID );
+ pIndex = serializeutf_8pair( pIndex );
+ status = MQTTV5_DeserializeAck( &packetInfo, &packetIdentifier, &ackInfo, requestProblem, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+}
+
+
+void test_MQTTV5_DeserializeDisconnect()
+{
+ MQTTAckInfo_t disconnectInfo;
+ const char * pServerRef;
+ uint16_t serverRefLength;
+ int32_t maxPacketSize = 100U;
+ uint8_t buffer[ 100 ] = { 0 };
+ uint8_t * pIndex = buffer;
+ size_t dummy;
+
+ memset( &disconnectInfo, 0x0, sizeof( disconnectInfo ) );
+ /*With properties*/
+ pIndex = &buffer[ 1 ];
+ packetInfo.pRemainingData = buffer;
+ dummy = encodeRemainingLength( pIndex, 27 );
+ packetInfo.remainingLength = 28 + dummy;
+ pIndex++;
+ pIndex = serializeutf_8( pIndex, MQTT_REASON_STRING_ID );
+ pIndex = serializeutf_8pair( pIndex );
+ pIndex = serializeutf_8( pIndex, MQTT_SERVER_REF_ID );
+ status = MQTTV5_DeserializeDisconnect( &packetInfo, &disconnectInfo, &pServerRef, &serverRefLength, maxPacketSize );
+ TEST_ASSERT_EQUAL_INT( MQTTSuccess, status );
+}