1
0
mirror of https://github.com/FreeRTOS/coreMQTT synced 2025-05-15 06:37:18 +08:00
coreMQTT/source/core_mqtt_serializer.c
Oscar Michael Abrina 7f0478d13a
Remove const qualifier from send/recv in transport interface (#86)
This follows changes from FreeRTOS/FreeRTOS@398abba. The const qualifier is removed from send/recv because there are transport implementations that require a member of the network context to be modified such as in the case of mbedtls.
2020-10-21 11:12:28 -07:00

2405 lines
85 KiB
C

/*
* coreMQTT v1.0.0
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* 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.c
* @brief Implements the user-facing functions in core_mqtt_serializer.h.
*/
#include <string.h>
#include <assert.h>
#include "core_mqtt_serializer.h"
/**
* @brief MQTT protocol version 3.1.1.
*/
#define MQTT_VERSION_3_1_1 ( ( uint8_t ) 4U )
/**
* @brief Size of the fixed and variable header of a CONNECT packet.
*/
#define MQTT_PACKET_CONNECT_HEADER_SIZE ( 10UL )
/* 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. */
/*
* Positions of each flag in the first byte of an MQTT PUBLISH packet's
* fixed header.
*/
#define MQTT_PUBLISH_FLAG_RETAIN ( 0 ) /**< @brief MQTT PUBLISH retain flag. */
#define MQTT_PUBLISH_FLAG_QOS1 ( 1 ) /**< @brief MQTT PUBLISH QoS1 flag. */
#define MQTT_PUBLISH_FLAG_QOS2 ( 2 ) /**< @brief MQTT PUBLISH QoS2 flag. */
#define MQTT_PUBLISH_FLAG_DUP ( 3 ) /**< @brief MQTT PUBLISH duplicate flag. */
/**
* @brief The size of MQTT DISCONNECT packets, per MQTT spec.
*/
#define MQTT_DISCONNECT_PACKET_SIZE ( 2UL )
/**
* @brief A PINGREQ packet is always 2 bytes in size, defined by MQTT 3.1.1 spec.
*/
#define MQTT_PACKET_PINGREQ_SIZE ( 2U )
/**
* @brief The Remaining Length field of MQTT disconnect packets, per MQTT spec.
*/
#define MQTT_DISCONNECT_REMAINING_LENGTH ( ( uint8_t ) 0 )
/*
* Constants relating to CONNACK packets, defined by MQTT 3.1.1 spec.
*/
#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. */
/*
* UNSUBACK, PUBACK, PUBREC, PUBREL, and PUBCOMP always have a remaining length
* of 2.
*/
#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. */
/**
* @brief Per the MQTT 3.1.1 spec, the largest "Remaining Length" of an MQTT
* packet is this value, 256 MB.
*/
#define MQTT_MAX_REMAINING_LENGTH ( 268435455UL )
/**
* @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 ) ) ) << 8 ) | \
( ( uint16_t ) ( *( ( ptr ) + 1 ) ) ) )
/**
* @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.
*
* @return Total number of bytes sent; -1 if there is an error.
*/
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; #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 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 );
/*-----------------------------------------------------------*/
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 += 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;
}
/*-----------------------------------------------------------*/
static void serializePublishCommon( const MQTTPublishInfo_t * pPublishInfo,
size_t remainingLength,
uint16_t packetIdentifier,
const MQTTFixedBuffer_t * pFixedBuffer,
bool serializePayload )
{
uint8_t * pIndex = NULL;
const uint8_t * pPayloadBuffer = NULL;
/* The first byte of a PUBLISH packet contains the packet type and flags. */
uint8_t publishFlags = MQTT_PACKET_TYPE_PUBLISH;
assert( pPublishInfo != NULL );
assert( pFixedBuffer != NULL );
assert( pFixedBuffer->pBuffer != NULL );
/* Packet Id should be non zero for Qos 1 and Qos 2. */
assert( ( pPublishInfo->qos == MQTTQoS0 ) || ( packetIdentifier != 0U ) );
/* Duplicate flag should be set only for Qos 1 or Qos 2. */
assert( ( pPublishInfo->dup != true ) || ( pPublishInfo->qos != MQTTQoS0 ) );
/* Get the start address of the buffer. */
pIndex = pFixedBuffer->pBuffer;
if( pPublishInfo->qos == MQTTQoS1 )
{
LogDebug( ( "Adding QoS as QoS1 in PUBLISH flags." ) );
UINT8_SET_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS1 );
}
else if( pPublishInfo->qos == MQTTQoS2 )
{
LogDebug( ( "Adding QoS as QoS2 in PUBLISH flags." ) );
UINT8_SET_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS2 );
}
else
{
/* Empty else MISRA 15.7 */
}
if( pPublishInfo->retain == true )
{
LogDebug( ( "Adding retain bit in PUBLISH flags." ) );
UINT8_SET_BIT( publishFlags, MQTT_PUBLISH_FLAG_RETAIN );
}
if( pPublishInfo->dup == true )
{
LogDebug( ( "Adding dup bit in PUBLISH flags." ) );
UINT8_SET_BIT( publishFlags, MQTT_PUBLISH_FLAG_DUP );
}
*pIndex = publishFlags;
pIndex++;
/* The "Remaining length" is encoded from the second byte. */
pIndex = encodeRemainingLength( pIndex, remainingLength );
/* The topic name is placed after the "Remaining length". */
pIndex = encodeString( pIndex,
pPublishInfo->pTopicName,
pPublishInfo->topicNameLength );
/* A packet identifier is required for QoS 1 and 2 messages. */
if( pPublishInfo->qos > MQTTQoS0 )
{
LogDebug( ( "Adding packet Id in PUBLISH packet." ) );
/* Place the packet identifier into the PUBLISH packet. */
*pIndex = UINT16_HIGH_BYTE( packetIdentifier );
*( pIndex + 1 ) = UINT16_LOW_BYTE( packetIdentifier );
pIndex += 2;
}
/* 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.
*/
if( ( pPublishInfo->payloadLength > 0U ) &&
( serializePayload == true ) )
{
LogDebug( ( "Copying PUBLISH payload of length =%lu to buffer",
( unsigned long ) pPublishInfo->payloadLength ) );
/* Typecast const void * typed payload buffer to const uint8_t *.
* This is to use same type buffers in memcpy. */
pPayloadBuffer = ( const uint8_t * ) pPublishInfo->pPayload;
( void ) memcpy( pIndex, pPayloadBuffer, pPublishInfo->payloadLength );
pIndex += pPublishInfo->payloadLength;
}
/* 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 size_t getRemainingLength( TransportRecv_t recvFunc,
NetworkContext_t * pNetworkContext )
{
size_t remainingLength = 0, multiplier = 1, bytesDecoded = 0, expectedSize = 0;
uint8_t encodedByte = 0;
int32_t bytesReceived = 0;
/* This algorithm is copied from the MQTT v3.1.1 spec. */
do
{
if( multiplier > 2097152U ) /* 128 ^ 3 */
{
remainingLength = MQTT_REMAINING_LENGTH_INVALID;
}
else
{
bytesReceived = recvFunc( pNetworkContext, &encodedByte, 1U );
if( bytesReceived == 1 )
{
remainingLength += ( ( size_t ) encodedByte & 0x7FU ) * multiplier;
multiplier *= 128U;
bytesDecoded++;
}
else
{
remainingLength = MQTT_REMAINING_LENGTH_INVALID;
}
}
if( remainingLength == MQTT_REMAINING_LENGTH_INVALID )
{
break;
}
} while( ( encodedByte & 0x80U ) != 0U );
/* Check that the decoded remaining length conforms to the MQTT specification. */
if( remainingLength != MQTT_REMAINING_LENGTH_INVALID )
{
expectedSize = remainingLengthEncodedSize( remainingLength );
if( bytesDecoded != expectedSize )
{
remainingLength = MQTT_REMAINING_LENGTH_INVALID;
}
}
return remainingLength;
}
/*-----------------------------------------------------------*/
static bool incomingPacketValid( uint8_t packetType )
{
bool status = false;
/* Check packet type. Mask out lower bits to ignore flags. */
switch( packetType & 0xF0U )
{
/* Valid incoming packet types. */
case MQTT_PACKET_TYPE_CONNACK:
case MQTT_PACKET_TYPE_PUBLISH:
case MQTT_PACKET_TYPE_PUBACK:
case MQTT_PACKET_TYPE_PUBREC:
case MQTT_PACKET_TYPE_PUBCOMP:
case MQTT_PACKET_TYPE_SUBACK:
case MQTT_PACKET_TYPE_UNSUBACK:
case MQTT_PACKET_TYPE_PINGRESP:
status = true;
break;
case ( MQTT_PACKET_TYPE_PUBREL & 0xF0U ):
/* The second bit of a PUBREL must be set. */
if( ( packetType & 0x02U ) > 0U )
{
status = true;
}
break;
/* Any other packet type is invalid. */
default:
LogWarn( ( "Incoming packet invalid: Packet type=%u.",
packetType ) );
break;
}
return status;
}
/*-----------------------------------------------------------*/
static MQTTStatus_t checkPublishRemainingLength( size_t remainingLength,
MQTTQoS_t qos,
size_t qos0Minimum )
{
MQTTStatus_t status = MQTTSuccess;
/* Sanity checks for "Remaining length". */
if( qos == MQTTQoS0 )
{
/* Check that the "Remaining length" is greater than the minimum. */
if( remainingLength < qos0Minimum )
{
LogDebug( ( "QoS 0 PUBLISH cannot have a remaining length less than %lu.",
( unsigned long ) qos0Minimum ) );
status = MQTTBadResponse;
}
}
else
{
/* Check that the "Remaining length" is greater than the minimum. For
* QoS 1 or 2, this will be two bytes greater than for QoS 0 due to the
* packet identifier. */
if( remainingLength < ( qos0Minimum + 2U ) )
{
LogDebug( ( "QoS 1 or 2 PUBLISH cannot have a remaining length less than %lu.",
( unsigned long ) ( qos0Minimum + 2U ) ) );
status = MQTTBadResponse;
}
}
return status;
}
/*-----------------------------------------------------------*/
static MQTTStatus_t processPublishFlags( uint8_t publishFlags,
MQTTPublishInfo_t * pPublishInfo )
{
MQTTStatus_t status = MQTTSuccess;
assert( pPublishInfo != NULL );
/* Check for QoS 2. */
if( UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS2 ) )
{
/* PUBLISH packet is invalid if both QoS 1 and QoS 2 bits are set. */
if( UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS1 ) )
{
LogDebug( ( "Bad QoS: 3." ) );
status = MQTTBadResponse;
}
else
{
pPublishInfo->qos = MQTTQoS2;
}
}
/* Check for QoS 1. */
else if( UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS1 ) )
{
pPublishInfo->qos = MQTTQoS1;
}
/* If the PUBLISH isn't QoS 1 or 2, then it's QoS 0. */
else
{
pPublishInfo->qos = MQTTQoS0;
}
if( status == MQTTSuccess )
{
LogDebug( ( "QoS is %d.", pPublishInfo->qos ) );
/* Parse the Retain bit. */
pPublishInfo->retain = ( UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_RETAIN ) ) ? true : false;
LogDebug( ( "Retain bit is %d.", pPublishInfo->retain ) );
/* Parse the DUP bit. */
pPublishInfo->dup = ( UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_DUP ) ) ? true : false;
LogDebug( ( "DUP bit is %d.", pPublishInfo->dup ) );
}
return status;
}
/*-----------------------------------------------------------*/
static void logConnackResponse( uint8_t responseCode )
{
const char * const pConnackResponses[ 6 ] =
{
"Connection accepted.", /* 0 */
"Connection refused: unacceptable protocol version.", /* 1 */
"Connection refused: identifier rejected.", /* 2 */
"Connection refused: server unavailable", /* 3 */
"Connection refused: bad user name or password.", /* 4 */
"Connection refused: not authorized." /* 5 */
};
/* Avoid unused parameter warning when assert and logs are disabled. */
( void ) responseCode;
( void ) pConnackResponses;
assert( responseCode <= 5 );
if( responseCode == 0u )
{
/* Log at Info level for a success CONNACK response. */
LogInfo( ( "%s", pConnackResponses[ 0 ] ) );
}
else
{
/* Log an error based on the CONNACK response code. */
LogError( ( "%s", pConnackResponses[ responseCode ] ) );
}
}
/*-----------------------------------------------------------*/
static MQTTStatus_t deserializeConnack( const MQTTPacketInfo_t * pConnack,
bool * pSessionPresent )
{
MQTTStatus_t status = MQTTSuccess;
const uint8_t * pRemainingData = NULL;
assert( pConnack != NULL );
assert( pSessionPresent != NULL );
pRemainingData = pConnack->pRemainingData;
/* According to MQTT 3.1.1, the second byte of CONNACK must specify a
* "Remaining length" of 2. */
if( pConnack->remainingLength != MQTT_PACKET_CONNACK_REMAINING_LENGTH )
{
LogError( ( "CONNACK does not have remaining length of %d.",
MQTT_PACKET_CONNACK_REMAINING_LENGTH ) );
status = MQTTBadResponse;
}
/* Check the reserved bits in CONNACK. The high 7 bits of the second byte
* in CONNACK must be 0. */
else 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 second byte in CONNACK. */
if( ( pRemainingData[ 0 ] & MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK )
== MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK )
{
LogInfo( ( "CONNACK session present bit set." ) );
*pSessionPresent = true;
/* MQTT 3.1.1 specifies that the fourth byte in CONNACK must be 0 if the
* "Session Present" bit is set. */
if( pRemainingData[ 1 ] != 0U )
{
status = MQTTBadResponse;
}
}
else
{
LogInfo( ( "CONNACK session present bit not set." ) );
*pSessionPresent = false;
}
}
if( status == MQTTSuccess )
{
/* In MQTT 3.1.1, only values 0 through 5 are valid CONNACK response codes. */
if( pRemainingData[ 1 ] > 5U )
{
LogError( ( "CONNACK response %u is invalid.", pRemainingData[ 1 ] ) );
status = MQTTBadResponse;
}
else
{
/* Print the appropriate message for the CONNACK response code if logs are
* enabled. */
logConnackResponse( pRemainingData[ 1 ] );
/* A nonzero CONNACK response code means the connection was refused. */
if( pRemainingData[ 1 ] > 0U )
{
status = MQTTServerRefused;
}
}
}
return status;
}
/*-----------------------------------------------------------*/
static MQTTStatus_t calculateSubscriptionPacketSize( const MQTTSubscribeInfo_t * pSubscriptionList,
size_t subscriptionCount,
size_t * pRemainingLength,
size_t * pPacketSize,
MQTTSubscriptionType_t subscriptionType )
{
MQTTStatus_t status = MQTTSuccess;
size_t i = 0, packetSize = 0;
assert( pSubscriptionList != NULL );
assert( subscriptionCount != 0U );
assert( pRemainingLength != NULL );
assert( pPacketSize != NULL );
/* The variable header of a subscription packet consists of a 2-byte packet
* identifier. */
packetSize += sizeof( uint16_t );
/* Sum the lengths of all subscription topic filters; add 1 byte for each
* subscription's QoS if type is MQTT_SUBSCRIBE. */
for( i = 0; i < subscriptionCount; i++ )
{
/* Add the length of the topic filter. MQTT strings are prepended
* with 2 byte string length field. Hence 2 bytes are added to size. */
packetSize += pSubscriptionList[ i ].topicFilterLength + sizeof( uint16_t );
/* Only SUBSCRIBE packets include the QoS. */
if( subscriptionType == MQTT_SUBSCRIBE )
{
packetSize += 1U;
}
}
/* At this point, the "Remaining length" has been calculated. Return error
* if the "Remaining length" exceeds what is allowed by MQTT 3.1.1. Otherwise,
* set the output parameter.*/
if( packetSize > MQTT_MAX_REMAINING_LENGTH )
{
LogError( ( "Subscription packet length of %lu exceeds"
"the MQTT 3.1.1 maximum packet length of %lu.",
( unsigned long ) packetSize,
MQTT_MAX_REMAINING_LENGTH ) );
status = MQTTBadParameter;
}
else
{
*pRemainingLength = packetSize;
/* Calculate the full size of the subscription packet by adding
* number of bytes required to encode the "Remaining length" field
* plus 1 byte for the "Packet type" field. */
packetSize += 1U + remainingLengthEncodedSize( packetSize );
/*Set the pPacketSize output parameter. */
*pPacketSize = packetSize;
}
LogDebug( ( "Subscription packet remaining length=%lu and packet size=%lu.",
( unsigned long ) *pRemainingLength,
( unsigned long ) *pPacketSize ) );
return status;
}
/*-----------------------------------------------------------*/
static MQTTStatus_t readSubackStatus( size_t statusCount,
const uint8_t * pStatusStart )
{
MQTTStatus_t status = MQTTSuccess;
uint8_t subscriptionStatus = 0;
size_t i = 0;
assert( pStatusStart != NULL );
/* Iterate through each status byte in the SUBACK packet. */
for( i = 0; i < statusCount; i++ )
{
/* Read a single status byte in SUBACK. */
subscriptionStatus = pStatusStart[ i ];
/* MQTT 3.1.1 defines the following values as status codes. */
switch( subscriptionStatus )
{
case 0x00:
case 0x01:
case 0x02:
LogDebug( ( "Topic filter %lu accepted, max QoS %u.",
( unsigned long ) i, subscriptionStatus ) );
break;
case 0x80:
LogWarn( ( "Topic filter %lu refused.", ( unsigned long ) i ) );
/* Application should remove subscription from the list */
status = MQTTServerRefused;
break;
default:
LogDebug( ( "Bad SUBSCRIBE status %u.", subscriptionStatus ) );
status = MQTTBadResponse;
break;
}
/* Stop parsing the subscription statuses if a bad response was received. */
if( status == MQTTBadResponse )
{
break;
}
}
return status;
}
/*-----------------------------------------------------------*/
static MQTTStatus_t deserializeSuback( const MQTTPacketInfo_t * pSuback,
uint16_t * pPacketIdentifier )
{
MQTTStatus_t status = MQTTSuccess;
size_t remainingLength;
const uint8_t * pVariableHeader = NULL;
assert( pSuback != NULL );
assert( pPacketIdentifier != NULL );
remainingLength = pSuback->remainingLength;
pVariableHeader = pSuback->pRemainingData;
/* A SUBACK must have a remaining length of at least 3 to accommodate the
* packet identifier and at least 1 return code. */
if( remainingLength < 3U )
{
LogDebug( ( "SUBACK cannot have a remaining length less than 3." ) );
status = MQTTBadResponse;
}
else
{
/* Extract the packet identifier (first 2 bytes of variable header) from SUBACK. */
*pPacketIdentifier = UINT16_DECODE( pVariableHeader );
LogDebug( ( "Packet identifier %hu.", *pPacketIdentifier ) );
status = readSubackStatus( remainingLength - sizeof( uint16_t ),
pVariableHeader + sizeof( uint16_t ) );
}
return status;
}
/*-----------------------------------------------------------*/
static MQTTStatus_t validateSubscriptionSerializeParams( const MQTTSubscribeInfo_t * pSubscriptionList,
size_t subscriptionCount,
uint16_t packetId,
size_t remainingLength,
const MQTTFixedBuffer_t * pFixedBuffer )
{
MQTTStatus_t status = MQTTSuccess;
size_t packetSize = 0;
/* Validate all the parameters. */
if( ( pFixedBuffer == NULL ) || ( pSubscriptionList == NULL ) )
{
LogError( ( "Argument cannot be NULL: pFixedBuffer=%p, "
"pSubscriptionList=%p.",
( void * ) pFixedBuffer,
( void * ) pSubscriptionList ) );
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( subscriptionCount == 0U )
{
LogError( ( "Subscription count is 0." ) );
status = MQTTBadParameter;
}
else if( packetId == 0U )
{
LogError( ( "Packet Id for subscription packet is 0." ) );
status = MQTTBadParameter;
}
else
{
/* The serialized packet size = First byte
* + length of encoded size of remaining length
* + remaining length. */
packetSize = 1U + remainingLengthEncodedSize( remainingLength )
+ remainingLength;
if( packetSize > pFixedBuffer->size )
{
LogError( ( "Buffer size of %lu is not sufficient to hold "
"serialized packet of size of %lu.",
( unsigned long ) pFixedBuffer->size,
( unsigned long ) packetSize ) );
status = MQTTNoMemory;
}
}
return status;
}
/*-----------------------------------------------------------*/
static MQTTStatus_t deserializePublish( const MQTTPacketInfo_t * pIncomingPacket,
uint16_t * pPacketId,
MQTTPublishInfo_t * pPublishInfo )
{
MQTTStatus_t status = MQTTSuccess;
const uint8_t * pVariableHeader, * pPacketIdentifierHigh = NULL;
assert( pIncomingPacket != NULL );
assert( pPacketId != NULL );
assert( pPublishInfo != NULL );
assert( pIncomingPacket->pRemainingData != NULL );
pVariableHeader = pIncomingPacket->pRemainingData;
/* The flags are the lower 4 bits of the first byte in PUBLISH. */
status = processPublishFlags( ( pIncomingPacket->type & 0x0FU ), pPublishInfo );
if( status == MQTTSuccess )
{
/* Sanity checks for "Remaining length". A QoS 0 PUBLISH must have a remaining
* length of at least 3 to accommodate topic name length (2 bytes) and topic
* name (at least 1 byte). A QoS 1 or 2 PUBLISH must have a remaining length of
* at least 5 for the packet identifier in addition to the topic name length and
* topic name. */
status = checkPublishRemainingLength( pIncomingPacket->remainingLength,
pPublishInfo->qos,
MQTT_MIN_PUBLISH_REMAINING_LENGTH_QOS0 );
}
if( status == MQTTSuccess )
{
/* Extract the topic name starting from the first byte of the variable header.
* The topic name string starts at byte 3 in the variable header. */
pPublishInfo->topicNameLength = UINT16_DECODE( pVariableHeader );
/* Sanity checks for topic name length and "Remaining length". The remaining
* length must be at least as large as the variable length header. */
status = checkPublishRemainingLength( pIncomingPacket->remainingLength,
pPublishInfo->qos,
pPublishInfo->topicNameLength + sizeof( uint16_t ) );
}
if( status == MQTTSuccess )
{
/* Parse the topic. */
pPublishInfo->pTopicName = ( const char * ) ( pVariableHeader + sizeof( uint16_t ) );
LogDebug( ( "Topic name length %hu: %.*s",
pPublishInfo->topicNameLength,
pPublishInfo->topicNameLength,
pPublishInfo->pTopicName ) );
/* Extract the packet identifier for QoS 1 or 2 PUBLISH packets. Packet
* identifier starts immediately after the topic name. */
pPacketIdentifierHigh = ( const uint8_t * ) ( pPublishInfo->pTopicName + pPublishInfo->topicNameLength );
if( pPublishInfo->qos > MQTTQoS0 )
{
*pPacketId = UINT16_DECODE( pPacketIdentifierHigh );
LogDebug( ( "Packet identifier %hu.", *pPacketId ) );
/* Advance pointer two bytes to start of payload as in the QoS 0 case. */
pPacketIdentifierHigh += sizeof( uint16_t );
/* Packet identifier cannot be 0. */
if( *pPacketId == 0U )
{
status = MQTTBadResponse;
}
}
}
if( status == MQTTSuccess )
{
/* Calculate the length of the payload. QoS 1 or 2 PUBLISH packets contain
* a packet identifier, but QoS 0 PUBLISH packets do not. */
pPublishInfo->payloadLength = pIncomingPacket->remainingLength - pPublishInfo->topicNameLength - sizeof( uint16_t );
if( pPublishInfo->qos != MQTTQoS0 )
{
/* Two more bytes for the packet identifier. */
pPublishInfo->payloadLength -= sizeof( uint16_t );
}
/* Set payload if it exists. */
pPublishInfo->pPayload = ( pPublishInfo->payloadLength != 0U ) ? pPacketIdentifierHigh : NULL;
LogDebug( ( "Payload length %lu.", ( unsigned long ) pPublishInfo->payloadLength ) );
}
return status;
}
/*-----------------------------------------------------------*/
static MQTTStatus_t deserializeSimpleAck( const MQTTPacketInfo_t * pAck,
uint16_t * pPacketIdentifier )
{
MQTTStatus_t status = MQTTSuccess;
assert( pAck != NULL );
assert( pPacketIdentifier != NULL );
/* Check that the "Remaining length" of the received ACK is 2. */
if( pAck->remainingLength != MQTT_PACKET_SIMPLE_ACK_REMAINING_LENGTH )
{
LogError( ( "ACK does not have remaining length of %d.",
MQTT_PACKET_SIMPLE_ACK_REMAINING_LENGTH ) );
status = MQTTBadResponse;
}
else
{
/* Extract the packet identifier (third and fourth bytes) from ACK. */
*pPacketIdentifier = UINT16_DECODE( pAck->pRemainingData );
LogDebug( ( "Packet identifier %hu.", *pPacketIdentifier ) );
/* Packet identifier cannot be 0. */
if( *pPacketIdentifier == 0U )
{
status = MQTTBadResponse;
}
}
return status;
}
/*-----------------------------------------------------------*/
static MQTTStatus_t deserializePingresp( const MQTTPacketInfo_t * pPingresp )
{
MQTTStatus_t status = MQTTSuccess;
assert( pPingresp != NULL );
/* Check the "Remaining length" (second byte) of the received PINGRESP is 0. */
if( pPingresp->remainingLength != MQTT_PACKET_PINGRESP_REMAINING_LENGTH )
{
LogError( ( "PINGRESP does not have remaining length of %d.",
MQTT_PACKET_PINGRESP_REMAINING_LENGTH ) );
status = MQTTBadResponse;
}
return status;
}
/*-----------------------------------------------------------*/
static void serializeConnectPacket( const MQTTConnectInfo_t * pConnectInfo,
const MQTTPublishInfo_t * pWillInfo,
size_t remainingLength,
const MQTTFixedBuffer_t * pFixedBuffer )
{
uint8_t connectFlags = 0U;
uint8_t * pIndex = NULL;
assert( pConnectInfo != NULL );
assert( pFixedBuffer != NULL );
assert( pFixedBuffer->pBuffer != NULL );
pIndex = pFixedBuffer->pBuffer;
/* The first byte in the CONNECT packet is the control packet type. */
*pIndex = MQTT_PACKET_TYPE_CONNECT;
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. */
pIndex = encodeRemainingLength( pIndex, remainingLength );
/* The string "MQTT" is placed at the beginning of the CONNECT packet's variable
* header. This string is 4 bytes long. */
pIndex = encodeString( pIndex, "MQTT", 4 );
/* The MQTT protocol version is the second field of the variable header. */
*pIndex = MQTT_VERSION_3_1_1;
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 );
}
}
*pIndex = connectFlags;
pIndex++;
/* Write the 2 bytes of the keep alive interval into the CONNECT packet. */
*pIndex = UINT16_HIGH_BYTE( pConnectInfo->keepAliveSeconds );
*( pIndex + 1 ) = UINT16_LOW_BYTE( pConnectInfo->keepAliveSeconds );
pIndex += 2;
/* Write the client identifier into the CONNECT packet. */
pIndex = encodeString( pIndex,
pConnectInfo->pClientIdentifier,
pConnectInfo->clientIdentifierLength );
/* Write the will topic name and message into the CONNECT packet if provided. */
if( pWillInfo != NULL )
{
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 );
}
/*-----------------------------------------------------------*/
MQTTStatus_t MQTT_GetConnectPacketSize( const MQTTConnectInfo_t * pConnectInfo,
const MQTTPublishInfo_t * pWillInfo,
size_t * pRemainingLength,
size_t * pPacketSize )
{
MQTTStatus_t status = MQTTSuccess;
size_t remainingLength;
/* The CONNECT packet will always include a 10-byte variable header. */
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.
* By bounding the payloadLength of the will message, the CONNECT
* packet will never be larger than 327699 bytes. */
LogError( ( "The Will Message length must not exceed %d. "
"pWillInfo->payloadLength=%lu.",
UINT16_MAX,
( unsigned long ) pWillInfo->payloadLength ) );
status = MQTTBadParameter;
}
else
{
/* Add the length of the client identifier. */
connectPacketSize += pConnectInfo->clientIdentifierLength + sizeof( uint16_t );
/* Add the lengths of the will message and topic name if provided. */
if( pWillInfo != NULL )
{
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 );
/* The connectPacketSize calculated from this function's parameters is
* guaranteed to be less than the maximum MQTT CONNECT packet size, which
* is 327700. If the maximum client identifier length, the maximum will
* message topic length, the maximum will topic payload length, the
* maximum username length, and the maximum password length are all present
* in the MQTT CONNECT packet, the total size will be calculated to be
* 327699:
* (variable length header)10 +
* (maximum client identifier length) 65535 + (encoded length) 2 +
* (maximum will message topic name length) 65535 + (encoded length)2 +
* (maximum will message payload length) 65535 + 2 +
* (maximum username length) 65535 + (encoded length) 2 +
* (maximum password length) 65535 + (encoded length) 2 +
* (packet type field length) 1 +
* (CONNECT packet encoded length) 3 = 327699 */
*pRemainingLength = remainingLength;
*pPacketSize = connectPacketSize;
LogDebug( ( "CONNECT packet remaining length=%lu and packet size=%lu.",
( unsigned long ) *pRemainingLength,
( unsigned long ) *pPacketSize ) );
}
return status;
}
/*-----------------------------------------------------------*/
MQTTStatus_t MQTT_SerializeConnect( const MQTTConnectInfo_t * pConnectInfo,
const MQTTPublishInfo_t * pWillInfo,
size_t remainingLength,
const MQTTFixedBuffer_t * pFixedBuffer )
{
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
{
/* 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
{
serializeConnectPacket( pConnectInfo,
pWillInfo,
remainingLength,
pFixedBuffer );
}
}
return status;
}
/*-----------------------------------------------------------*/
MQTTStatus_t MQTT_GetSubscribePacketSize( const MQTTSubscribeInfo_t * pSubscriptionList,
size_t subscriptionCount,
size_t * pRemainingLength,
size_t * pPacketSize )
{
MQTTStatus_t status = MQTTSuccess;
/* Validate parameters. */
if( ( pSubscriptionList == NULL ) || ( pRemainingLength == NULL ) ||
( pPacketSize == NULL ) )
{
LogError( ( "Argument cannot be NULL: pSubscriptionList=%p, "
"pRemainingLength=%p, pPacketSize=%p.",
( void * ) pSubscriptionList,
( void * ) pRemainingLength,
( void * ) pPacketSize ) );
status = MQTTBadParameter;
}
else if( subscriptionCount == 0U )
{
LogError( ( "subscriptionCount is 0." ) );
status = MQTTBadParameter;
}
else
{
/* Calculate the MQTT UNSUBSCRIBE packet size. */
status = calculateSubscriptionPacketSize( pSubscriptionList,
subscriptionCount,
pRemainingLength,
pPacketSize,
MQTT_SUBSCRIBE );
if( status == MQTTBadParameter )
{
LogError( ( "SUBSCRIBE packet remaining length exceeds %lu, which is the "
"maximum size allowed by MQTT 3.1.1.",
MQTT_MAX_REMAINING_LENGTH ) );
}
}
return status;
}
/*-----------------------------------------------------------*/
MQTTStatus_t MQTT_SerializeSubscribe( const MQTTSubscribeInfo_t * pSubscriptionList,
size_t subscriptionCount,
uint16_t packetId,
size_t remainingLength,
const MQTTFixedBuffer_t * pFixedBuffer )
{
size_t i = 0;
uint8_t * pIndex = NULL;
/* Validate all the parameters. */
MQTTStatus_t status =
validateSubscriptionSerializeParams( pSubscriptionList,
subscriptionCount,
packetId,
remainingLength,
pFixedBuffer );
if( status == MQTTSuccess )
{
pIndex = pFixedBuffer->pBuffer;
/* The first byte in SUBSCRIBE is the packet type. */
*pIndex = MQTT_PACKET_TYPE_SUBSCRIBE;
pIndex++;
/* Encode the "Remaining length" starting from the second byte. */
pIndex = encodeRemainingLength( pIndex, remainingLength );
/* Place the packet identifier into the SUBSCRIBE packet. */
*pIndex = UINT16_HIGH_BYTE( packetId );
*( pIndex + 1 ) = UINT16_LOW_BYTE( packetId );
pIndex += 2;
/* Serialize each subscription topic filter and QoS. */
for( i = 0; i < subscriptionCount; i++ )
{
pIndex = encodeString( pIndex,
pSubscriptionList[ i ].pTopicFilter,
pSubscriptionList[ i ].topicFilterLength );
/* Place the QoS in the SUBSCRIBE packet. */
*pIndex = ( uint8_t ) ( pSubscriptionList[ i ].qos );
pIndex++;
}
LogDebug( ( "Length of serialized SUBSCRIBE packet is %lu.",
( ( unsigned long ) ( pIndex - pFixedBuffer->pBuffer ) ) ) );
}
return status;
}
/*-----------------------------------------------------------*/
MQTTStatus_t MQTT_GetUnsubscribePacketSize( const MQTTSubscribeInfo_t * pSubscriptionList,
size_t subscriptionCount,
size_t * pRemainingLength,
size_t * pPacketSize )
{
MQTTStatus_t status = MQTTSuccess;
/* Validate parameters. */
if( ( pSubscriptionList == NULL ) || ( pRemainingLength == NULL ) ||
( pPacketSize == NULL ) )
{
LogError( ( "Argument cannot be NULL: pSubscriptionList=%p, "
"pRemainingLength=%p, pPacketSize=%p.",
( void * ) pSubscriptionList,
( void * ) pRemainingLength,
( void * ) pPacketSize ) );
status = MQTTBadParameter;
}
else if( subscriptionCount == 0U )
{
LogError( ( "Subscription count is 0." ) );
status = MQTTBadParameter;
}
else
{
/* Calculate the MQTT SUBSCRIBE packet size. */
status = calculateSubscriptionPacketSize( pSubscriptionList,
subscriptionCount,
pRemainingLength,
pPacketSize,
MQTT_UNSUBSCRIBE );
if( status == MQTTBadParameter )
{
LogError( ( "UNSUBSCRIBE packet remaining length exceeds %lu, which is the "
"maximum size allowed by MQTT 3.1.1.",
MQTT_MAX_REMAINING_LENGTH ) );
}
}
return status;
}
/*-----------------------------------------------------------*/
MQTTStatus_t MQTT_SerializeUnsubscribe( const MQTTSubscribeInfo_t * pSubscriptionList,
size_t subscriptionCount,
uint16_t packetId,
size_t remainingLength,
const MQTTFixedBuffer_t * pFixedBuffer )
{
MQTTStatus_t status = MQTTSuccess;
size_t i = 0;
uint8_t * pIndex = NULL;
/* Validate all the parameters. */
status = validateSubscriptionSerializeParams( pSubscriptionList,
subscriptionCount,
packetId,
remainingLength,
pFixedBuffer );
if( status == MQTTSuccess )
{
/* Get the start of the buffer to the iterator variable. */
pIndex = pFixedBuffer->pBuffer;
/* The first byte in UNSUBSCRIBE is the packet type. */
*pIndex = MQTT_PACKET_TYPE_UNSUBSCRIBE;
pIndex++;
/* Encode the "Remaining length" starting from the second byte. */
pIndex = encodeRemainingLength( pIndex, remainingLength );
/* Place the packet identifier into the UNSUBSCRIBE packet. */
*pIndex = UINT16_HIGH_BYTE( packetId );
*( pIndex + 1 ) = UINT16_LOW_BYTE( packetId );
pIndex += 2;
/* Serialize each subscription topic filter. */
for( i = 0; i < subscriptionCount; i++ )
{
pIndex = encodeString( pIndex,
pSubscriptionList[ i ].pTopicFilter,
pSubscriptionList[ i ].topicFilterLength );
}
LogDebug( ( "Length of serialized UNSUBSCRIBE packet is %lu.",
( ( unsigned long ) ( pIndex - pFixedBuffer->pBuffer ) ) ) );
}
return status;
}
/*-----------------------------------------------------------*/
MQTTStatus_t MQTT_GetPublishPacketSize( const MQTTPublishInfo_t * pPublishInfo,
size_t * pRemainingLength,
size_t * pPacketSize )
{
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=%u.",
pPublishInfo->pTopicName,
pPublishInfo->topicNameLength ) );
status = MQTTBadParameter;
}
else
{
/* Calculate the "Remaining length" field and total packet size. If it exceeds
* what is allowed in the MQTT standard, return an error. */
if( calculatePublishPacketSize( pPublishInfo, pRemainingLength, pPacketSize ) == false )
{
LogError( ( "PUBLISH packet remaining length exceeds %lu, which is the "
"maximum size allowed by MQTT 3.1.1.",
MQTT_MAX_REMAINING_LENGTH ) );
status = MQTTBadParameter;
}
}
return status;
}
/*-----------------------------------------------------------*/
MQTTStatus_t MQTT_SerializePublish( const MQTTPublishInfo_t * pPublishInfo,
uint16_t packetId,
size_t remainingLength,
const MQTTFixedBuffer_t * pFixedBuffer )
{
MQTTStatus_t status = MQTTSuccess;
size_t packetSize = 0;
if( ( pFixedBuffer == NULL ) || ( pPublishInfo == NULL ) )
{
LogError( ( "Argument cannot be NULL: pFixedBuffer=%p, "
"pPublishInfo=%p.",
( void * ) pFixedBuffer,
( void * ) pPublishInfo ) );
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;
}
/* For serializing a publish, if there exists a payload, then the buffer
* cannot be NULL. */
else if( ( pPublishInfo->payloadLength > 0U ) && ( pPublishInfo->pPayload == NULL ) )
{
LogError( ( "A nonzero payload length requires a non-NULL payload: "
"payloadLength=%lu, pPayload=%p.",
( unsigned long ) pPublishInfo->payloadLength,
pPublishInfo->pPayload ) );
status = MQTTBadParameter;
}
else if( ( pPublishInfo->pTopicName == NULL ) || ( pPublishInfo->topicNameLength == 0U ) )
{
LogError( ( "Invalid topic name for PUBLISH: pTopicName=%p, "
"topicNameLength=%u.",
pPublishInfo->pTopicName,
pPublishInfo->topicNameLength ) );
status = MQTTBadParameter;
}
else if( ( pPublishInfo->qos != MQTTQoS0 ) && ( packetId == 0U ) )
{
LogError( ( "Packet ID is 0 for PUBLISH with QoS=%u.",
pPublishInfo->qos ) );
status = MQTTBadParameter;
}
else if( ( pPublishInfo->dup == true ) && ( pPublishInfo->qos == MQTTQoS0 ) )
{
LogError( ( "Duplicate flag is set for PUBLISH with Qos 0," ) );
status = MQTTBadParameter;
}
else
{
/* Length of serialized packet = First byte
* + Length of encoded remaining length
* + Remaining length. */
packetSize = 1U + remainingLengthEncodedSize( remainingLength )
+ remainingLength;
}
if( ( status == MQTTSuccess ) && ( packetSize > pFixedBuffer->size ) )
{
LogError( ( "Buffer size of %lu is not sufficient to hold "
"serialized PUBLISH packet of size of %lu.",
( unsigned long ) pFixedBuffer->size,
( unsigned long ) packetSize ) );
status = MQTTNoMemory;
}
if( status == MQTTSuccess )
{
/* Serialize publish with header and payload. */
serializePublishCommon( pPublishInfo,
remainingLength,
packetId,
pFixedBuffer,
true );
}
return status;
}
/*-----------------------------------------------------------*/
MQTTStatus_t MQTT_SerializePublishHeader( const MQTTPublishInfo_t * pPublishInfo,
uint16_t packetId,
size_t remainingLength,
const MQTTFixedBuffer_t * pFixedBuffer,
size_t * pHeaderSize )
{
MQTTStatus_t status = MQTTSuccess;
size_t packetSize = 0;
if( ( pFixedBuffer == NULL ) || ( pPublishInfo == NULL ) ||
( pHeaderSize == NULL ) )
{
LogError( ( "Argument cannot be NULL: pFixedBuffer=%p, "
"pPublishInfo=%p, pHeaderSize=%p.",
( void * ) pFixedBuffer,
( void * ) pPublishInfo,
( void * ) pHeaderSize ) );
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( ( pPublishInfo->pTopicName == NULL ) || ( pPublishInfo->topicNameLength == 0U ) )
{
LogError( ( "Invalid topic name for publish: pTopicName=%p, "
"topicNameLength=%u.",
pPublishInfo->pTopicName,
pPublishInfo->topicNameLength ) );
status = MQTTBadParameter;
}
else if( ( pPublishInfo->qos != MQTTQoS0 ) && ( packetId == 0U ) )
{
LogError( ( "Packet Id is 0 for publish with QoS=%u.",
pPublishInfo->qos ) );
status = MQTTBadParameter;
}
else if( ( pPublishInfo->dup == true ) && ( pPublishInfo->qos == MQTTQoS0 ) )
{
LogError( ( "Duplicate flag is set for PUBLISH with Qos 0," ) );
status = MQTTBadParameter;
}
else
{
/* Length of serialized packet = First byte
* + Length of encoded remaining length
* + Remaining length
* - Payload Length.
*/
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;
}
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",
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 %u.",
( 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.",
pIncomingPacket->type ) );
status = MQTTBadParameter;
}
else if( pIncomingPacket->pRemainingData == NULL )
{
LogError( ( "Argument cannot be NULL: "
"pIncomingPacket->pRemainingData is NULL." ) );
status = MQTTBadParameter;
}
else
{
status = deserializePublish( pIncomingPacket, pPacketId, pPublishInfo );
}
return status;
}
/*-----------------------------------------------------------*/
MQTTStatus_t MQTT_DeserializeAck( const MQTTPacketInfo_t * pIncomingPacket,
uint16_t * pPacketId,
bool * pSessionPresent )
{
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 ) &&
( ( pIncomingPacket->type != MQTT_PACKET_TYPE_CONNACK ) &&
( pIncomingPacket->type != MQTT_PACKET_TYPE_PINGRESP ) ) )
{
LogError( ( "pPacketId cannot be NULL for packet type %02x.",
pIncomingPacket->type ) );
status = MQTTBadParameter;
}
/* Pointer for session present cannot be NULL for CONNACK. */
else if( ( pSessionPresent == NULL ) &&
( pIncomingPacket->type == MQTT_PACKET_TYPE_CONNACK ) )
{
LogError( ( "pSessionPresent cannot be NULL for CONNACK packet." ) );
status = MQTTBadParameter;
}
/* Pointer for remaining data cannot be NULL for packets other
* than PINGRESP. */
else if( ( pIncomingPacket->pRemainingData == NULL ) &&
( pIncomingPacket->type != MQTT_PACKET_TYPE_PINGRESP ) )
{
LogError( ( "Remaining data of incoming packet is NULL." ) );
status = MQTTBadParameter;
}
else
{
/* Make sure response packet is a valid ack. */
switch( pIncomingPacket->type )
{
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:
status = deserializeSimpleAck( pIncomingPacket, pPacketId );
break;
/* Any other packet type is invalid. */
default:
LogError( ( "IotMqtt_DeserializeResponse() called with unknown packet type:(%02x).", pIncomingPacket->type ) );
status = MQTTBadResponse;
break;
}
}
return status;
}
/*-----------------------------------------------------------*/
MQTTStatus_t MQTT_GetIncomingPacketTypeAndLength( TransportRecv_t readFunc,
NetworkContext_t * pNetworkContext,
MQTTPacketInfo_t * pIncomingPacket )
{
MQTTStatus_t status = MQTTSuccess;
int32_t bytesReceived = 0;
if( pIncomingPacket == NULL )
{
LogError( ( "Invalid parameter: pIncomingPacket is NULL." ) );
status = MQTTBadParameter;
}
else
{
/* 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 )
{
status = MQTTBadResponse;
}
}
else
{
LogError( ( "Incoming packet invalid: Packet type=%u.",
pIncomingPacket->type ) );
status = MQTTBadResponse;
}
}
else if( ( status != MQTTBadParameter ) && ( bytesReceived == 0 ) )
{
LogDebug( ( "No data was received from the transport." ) );
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=%d.",
bytesReceived ) );
status = MQTTRecvFailed;
}
else
{
/* Empty else MISRA 15.7 */
}
return status;
}
/*-----------------------------------------------------------*/