blob: 28ec461705a96f7a06c2ed31d4a8f31b994460ac [file] [log] [blame]
/*
* IoT MQTT V2.1.0
* Copyright (C) 2018 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 iot_mqtt_serialize.c
* @brief Implements functions that generate and decode MQTT network packets.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <string.h>
/* Error handling include. */
#include "iot_error.h"
/* MQTT internal includes. */
#include "private/iot_mqtt_internal.h"
/* Platform layer includes. */
#include "platform/iot_threads.h"
/* Atomic operations. */
#include "iot_atomic.h"
/*-----------------------------------------------------------*/
/*
* Macros for reading the high and low byte of a 2-byte unsigned int.
*/
#define UINT16_HIGH_BYTE( x ) ( ( uint8_t ) ( x >> 8 ) ) /**< @brief Get high byte. */
#define UINT16_LOW_BYTE( x ) ( ( uint8_t ) ( x & 0x00ff ) ) /**< @brief Get low byte. */
/**
* @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 Macro for setting a bit in a 1-byte unsigned int.
*
* @param[in] x The unsigned int to set.
* @param[in] position Which bit to set.
*/
#define UINT8_SET_BIT( x, position ) ( x = ( uint8_t ) ( x | ( 0x01 << 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 & ( 0x01 << position ) ) == ( 0x01 << position ) )
/*
* Positions of each flag in the "Connect Flag" field of an MQTT CONNECT
* packet.
*/
#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 QoS1. */
#define MQTT_CONNECT_FLAG_WILL_QOS2 ( 4 ) /**< @brief Will QoS2. */
#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 Username present. */
/*
* Positions of each flag in the first byte of an MQTT PUBLISH packet's
* fixed header.
*/
#define MQTT_PUBLISH_FLAG_RETAIN ( 0 ) /**< @brief Message retain flag. */
#define MQTT_PUBLISH_FLAG_QOS1 ( 1 ) /**< @brief Publish QoS 1. */
#define MQTT_PUBLISH_FLAG_QOS2 ( 2 ) /**< @brief Publish QoS 2. */
#define MQTT_PUBLISH_FLAG_DUP ( 3 ) /**< @brief Duplicate message. */
/**
* @brief The constant specifying MQTT version 3.1.1. Placed in the CONNECT packet.
*/
#define MQTT_VERSION_3_1_1 ( ( uint8_t ) 4U )
/**
* @brief Per the MQTT 3.1.1 spec, the largest "Remaining Length" of an MQTT
* packet is this value.
*/
#define MQTT_MAX_REMAINING_LENGTH ( 268435455UL )
/**
* @brief The maximum possible size of a CONNECT packet.
*
* All strings in a CONNECT packet are constrained to 2-byte lengths, giving a
* maximum length smaller than the max "Remaining Length" constant above.
*/
#define MQTT_PACKET_CONNECT_MAX_SIZE ( 327700UL )
/*
* Constants relating to CONNACK packets, defined by MQTT 3.1.1 spec.
*/
#define MQTT_PACKET_CONNACK_REMAINING_LENGTH ( ( uint8_t ) 2 ) /**< @brief A CONNACK packet always has a "Remaining length" of 2. */
#define MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK ( ( uint8_t ) 0x01 ) /**< @brief The "Session Present" bit is always the lowest bit. */
/*
* Constants relating to PUBLISH and PUBACK packets, defined by MQTT
* 3.1.1 spec.
*/
#define MQTT_PACKET_PUBACK_SIZE ( 4 ) /**< @brief A PUBACK packet is always 4 bytes in size. */
#define MQTT_PACKET_PUBACK_REMAINING_LENGTH ( ( uint8_t ) 2 ) /**< @brief A PUBACK packet always has a "Remaining length" of 2. */
/*
* Constants relating to SUBACK and UNSUBACK packets, defined by MQTT
* 3.1.1 spec.
*/
#define MQTT_PACKET_SUBACK_MINIMUM_SIZE ( 5 ) /**< @brief The size of the smallest valid SUBACK packet. */
#define MQTT_PACKET_UNSUBACK_REMAINING_LENGTH ( ( uint8_t ) 2 ) /**< @brief An UNSUBACK packet always has a "Remaining length" of 2. */
/*
* Constants relating to PINGREQ and PINGRESP packets, defined by MQTT 3.1.1 spec.
*/
#define MQTT_PACKET_PINGREQ_SIZE ( 2 ) /**< @brief A PINGREQ packet is always 2 bytes in size. */
#define MQTT_PACKET_PINGRESP_REMAINING_LENGTH ( 0 ) /**< @brief A PINGRESP packet always has a "Remaining length" of 0. */
/*
* Constants relating to DISCONNECT packets, defined by MQTT 3.1.1 spec.
*/
#define MQTT_PACKET_DISCONNECT_SIZE ( 2 ) /**< @brief A DISCONNECT packet is always 2 bytes in size. */
/* Username for metrics with AWS IoT. */
#if AWS_IOT_MQTT_ENABLE_METRICS == 1 || DOXYGEN == 1
#ifndef AWS_IOT_METRICS_USERNAME
/**
* @brief Specify C SDK and version.
*/
#define AWS_IOT_METRICS_USERNAME "?SDK=C&Version=4.0.0"
/**
* @brief The length of #AWS_IOT_METRICS_USERNAME.
*/
#define AWS_IOT_METRICS_USERNAME_LENGTH ( ( uint16_t ) sizeof( AWS_IOT_METRICS_USERNAME ) - 1 )
#endif /* ifndef AWS_IOT_METRICS_USERNAME */
#endif /* if AWS_IOT_MQTT_ENABLE_METRICS == 1 || DOXYGEN == 1 */
/*-----------------------------------------------------------*/
/**
* @brief Generate and return a 2-byte packet identifier.
*
* This packet identifier will be nonzero.
*
* @return The packet identifier.
*/
static uint16_t _nextPacketIdentifier( void );
/**
* @brief Calculate the number of bytes required to encode an MQTT
* "Remaining length" field.
*
* @param[in] length The value of the "Remaining length" to encode.
*
* @return The size of the encoding of length. This is always `1`, `2`, `3`, or `4`.
*/
static size_t _remainingLengthEncodedSize( size_t length );
/**
* @brief Encode the "Remaining length" field per MQTT spec.
*
* @param[out] pDestination Where to write the encoded "Remaining length".
* @param[in] length The "Remaining length" to encode.
*
* @return Pointer to the end of the encoded "Remaining length", which is 1-4
* bytes greater than `pDestination`.
*
* @warning This function does not check the size of `pDestination`! Ensure that
* `pDestination` is large enough to hold the encoded "Remaining length" using
* the function #_remainingLengthEncodedSize to avoid buffer overflows.
*/
static uint8_t * _encodeRemainingLength( uint8_t * pDestination,
size_t length );
/**
* @brief Encode a C string as a UTF-8 string, per MQTT 3.1.1 spec.
*
* @param[out] pDestination Where to write the encoded string.
* @param[in] source The string to encode.
* @param[in] sourceLength The length of source.
*
* @return Pointer to the end of the encoded string, which is `sourceLength+2`
* bytes greater than `pDestination`.
*
* @warning This function does not check the size of `pDestination`! Ensure that
* `pDestination` is large enough to hold `sourceLength+2` bytes to avoid a buffer
* overflow.
*/
static uint8_t * _encodeString( uint8_t * pDestination,
const char * source,
uint16_t sourceLength );
/**
* @brief Calculate the size and "Remaining length" of a CONNECT packet generated
* from the given parameters.
*
* @param[in] pConnectInfo User-provided CONNECT information struct.
* @param[out] pRemainingLength Output for calculated "Remaining length" field.
* @param[out] pPacketSize Output for calculated total packet size.
*
* @return `true` if the packet is within the length allowed by MQTT 3.1.1 spec; `false`
* otherwise. If this function returns `false`, the output parameters should be ignored.
*/
static bool _connectPacketSize( const IotMqttConnectInfo_t * pConnectInfo,
size_t * pRemainingLength,
size_t * pPacketSize );
/**
* @brief Calculate the size and "Remaining length" of a PUBLISH packet generated
* from the given parameters.
*
* @param[in] pPublishInfo User-provided PUBLISH information struct.
* @param[out] pRemainingLength Output for calculated "Remaining length" field.
* @param[out] pPacketSize Output for calculated total packet size.
*
* @return `true` if the packet is within the length allowed by MQTT 3.1.1 spec; `false`
* otherwise. If this function returns `false`, the output parameters should be ignored.
*/
static bool _publishPacketSize( const IotMqttPublishInfo_t * pPublishInfo,
size_t * pRemainingLength,
size_t * pPacketSize );
/**
* @brief Calculate the size and "Remaining length" of a SUBSCRIBE or UNSUBSCRIBE
* packet generated from the given parameters.
*
* @param[in] type Either IOT_MQTT_SUBSCRIBE or IOT_MQTT_UNSUBSCRIBE.
* @param[in] pSubscriptionList User-provided array of subscriptions.
* @param[in] subscriptionCount Size of `pSubscriptionList`.
* @param[out] pRemainingLength Output for calculated "Remaining length" field.
* @param[out] pPacketSize Output for calculated total packet size.
*
* @return `true` if the packet is within the length allowed by MQTT 3.1.1 spec; `false`
* otherwise. If this function returns `false`, the output parameters should be ignored.
*/
static bool _subscriptionPacketSize( IotMqttOperationType_t type,
const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
size_t * pRemainingLength,
size_t * pPacketSize );
/**
* @brief Generate a CONNECT packet from the given parameters.
*
* @param[in] pConnectInfo User-provided CONNECT information.
* @param[in] remainingLength User provided remaining length.
* @param[in, out] pBuffer User provided buffer where the CONNECT packet is written.
* @param[in] connectPacketSize Size of the buffer pointed to by `pBuffer`.
*
*/
void _serializeConnect( const IotMqttConnectInfo_t * pConnectInfo,
size_t remainingLength,
uint8_t * pBuffer,
size_t connectPacketSize );
/**
* @brief Generate a PUBLISH packet from the given parameters.
*
* @param[in] pPublishInfo User-provided PUBLISH information.
* @param[in] remainingLength User provided remaining length.
* @param[out] pPacketIdentifier The packet identifier generated for this PUBLISH.
* @param[out] pPacketIdentifierHigh Where the high byte of the packet identifier
* is written.
* @param[in, out] pBuffer User provided buffer where the PUBLISH packet is written.
* @param[in] publishPacketSize Size of buffer pointed to by `pBuffer`.
*
*/
void _serializePublish( const IotMqttPublishInfo_t * pPublishInfo,
size_t remainingLength,
uint16_t * pPacketIdentifier,
uint8_t ** pPacketIdentifierHigh,
uint8_t * pBuffer,
size_t publishPacketSize );
/**
* @brief Generate a SUBSCRIBE packet from the given parameters.
*
* @param[in] pSubscriptionList User-provided array of subscriptions.
* @param[in] subscriptionCount Size of `pSubscriptionList`.
* @param[in] remainingLength User provided remaining length.
* @param[out] pPacketIdentifier The packet identifier generated for this SUBSCRIBE.
* @param[in, out] pBuffer User provided buffer where the SUBSCRIBE packet is written.
* @param[in] subscribePacketSize Size of the buffer pointed to by `pBuffer`.
*
*/
void _serializeSubscribe( const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
size_t remainingLength,
uint16_t * pPacketIdentifier,
uint8_t * pBuffer,
size_t subscribePacketSize );
/**
* @brief Generate an UNSUBSCRIBE packet from the given parameters.
*
* @param[in] pSubscriptionList User-provided array of subscriptions to remove.
* @param[in] subscriptionCount Size of `pSubscriptionList`.
* @param[in] remainingLength User provided remaining length.
* @param[out] pPacketIdentifier The packet identifier generated for this UNSUBSCRIBE.
* @param[in, out] pBuffer User provided buffer where the UNSUBSCRIBE packet is written.
* @param[in] unsubscribePacketSize size of the buffer pointed to by `pBuffer`.
*
*/
void _serializeUnsubscribe( const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
size_t remainingLength,
uint16_t * pPacketIdentifier,
uint8_t * pBuffer,
size_t unsubscribePacketSize );
/*-----------------------------------------------------------*/
#if LIBRARY_LOG_LEVEL > IOT_LOG_NONE
/**
* @brief If logging is enabled, define a log configuration that only prints the log
* string. This is used when printing out details of deserialized MQTT packets.
*/
static const IotLogConfig_t _logHideAll =
{
.hideLibraryName = true,
.hideLogLevel = true,
.hideTimestring = true
};
#endif
/*-----------------------------------------------------------*/
static uint16_t _nextPacketIdentifier( void )
{
/* MQTT specifies 2 bytes for the packet identifier; however, operating on
* 32-bit integers is generally faster. */
static uint32_t nextPacketIdentifier = 1;
/* The next packet identifier will be greater by 2. This prevents packet
* identifiers from ever being 0, which is not allowed by MQTT 3.1.1. Packet
* identifiers will follow the sequence 1,3,5...65535,1,3,5... */
return ( uint16_t ) Atomic_Add_u32( &nextPacketIdentifier, 2 );
}
/*-----------------------------------------------------------*/
static size_t _remainingLengthEncodedSize( size_t length )
{
size_t encodedSize = 0;
/* length should have already been checked before calling this function. */
IotMqtt_Assert( length <= MQTT_MAX_REMAINING_LENGTH );
/* 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 < 128 )
{
encodedSize = 1;
}
/* 2 bytes are needed to encode lengths between 128 and 16,383. */
else if( length < 16384 )
{
encodedSize = 2;
}
/* 3 bytes are needed to encode lengths between 16,384 and 2,097,151. */
else if( length < 2097152 )
{
encodedSize = 3;
}
/* 4 bytes are needed to encode lengths between 2,097,152 and 268,435,455. */
else
{
encodedSize = 4;
}
return encodedSize;
}
/*-----------------------------------------------------------*/
static uint8_t * _encodeRemainingLength( uint8_t * pDestination,
size_t length )
{
uint8_t lengthByte = 0, * pLengthEnd = pDestination;
/* This algorithm is copied from the MQTT v3.1.1 spec. */
do
{
lengthByte = length % 128;
length = length / 128;
/* Set the high bit of this byte, indicating that there's more data. */
if( length > 0 )
{
UINT8_SET_BIT( lengthByte, 7 );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Output a single encoded byte. */
*pLengthEnd = lengthByte;
pLengthEnd++;
} while( length > 0 );
return pLengthEnd;
}
/*-----------------------------------------------------------*/
static uint8_t * _encodeString( uint8_t * pDestination,
const char * source,
uint16_t sourceLength )
{
/* The first byte of a UTF-8 string is the high byte of the string length. */
*pDestination = UINT16_HIGH_BYTE( sourceLength );
pDestination++;
/* The second byte of a UTF-8 string is the low byte of the string length. */
*pDestination = UINT16_LOW_BYTE( sourceLength );
pDestination++;
/* Copy the string into pDestination. */
( void ) memcpy( pDestination, source, sourceLength );
/* Return the pointer to the end of the encoded string. */
pDestination += sourceLength;
return pDestination;
}
/*-----------------------------------------------------------*/
static bool _connectPacketSize( const IotMqttConnectInfo_t * pConnectInfo,
size_t * pRemainingLength,
size_t * pPacketSize )
{
bool status = true;
size_t connectPacketSize = 0, remainingLength = 0;
/* The CONNECT packet will always include a 10-byte variable header. */
connectPacketSize += 10U;
/* Add the length of the client identifier if provided. */
connectPacketSize += pConnectInfo->clientIdentifierLength + sizeof( uint16_t );
/* Add the lengths of the will message and topic name if provided. */
if( pConnectInfo->pWillInfo != NULL )
{
connectPacketSize += pConnectInfo->pWillInfo->topicNameLength + sizeof( uint16_t ) +
pConnectInfo->pWillInfo->payloadLength + sizeof( uint16_t );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Depending on the status of metrics, add the length of the metrics username
* or the user-provided username. */
if( pConnectInfo->awsIotMqttMode == true )
{
#if AWS_IOT_MQTT_ENABLE_METRICS == 1
connectPacketSize += AWS_IOT_METRICS_USERNAME_LENGTH + sizeof( uint16_t );
#endif
}
else
{
/* Add the lengths of the username and password if provided and not
* connecting to an AWS IoT MQTT server. */
if( pConnectInfo->pUserName != NULL )
{
connectPacketSize += pConnectInfo->userNameLength + sizeof( uint16_t );
}
else
{
EMPTY_ELSE_MARKER;
}
if( pConnectInfo->pPassword != NULL )
{
connectPacketSize += pConnectInfo->passwordLength + sizeof( uint16_t );
}
else
{
EMPTY_ELSE_MARKER;
}
}
/* 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 += 1 + _remainingLengthEncodedSize( connectPacketSize );
/* Check that the CONNECT packet is within the bounds of the MQTT spec. */
if( connectPacketSize > MQTT_PACKET_CONNECT_MAX_SIZE )
{
status = false;
}
else
{
*pRemainingLength = remainingLength;
*pPacketSize = connectPacketSize;
}
return status;
}
/*-----------------------------------------------------------*/
static bool _publishPacketSize( const IotMqttPublishInfo_t * pPublishInfo,
size_t * pRemainingLength,
size_t * pPacketSize )
{
bool status = true;
size_t publishPacketSize = 0, payloadLimit = 0;
/* The variable header of a PUBLISH packet always contains the topic name. */
publishPacketSize += 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 > IOT_MQTT_QOS_0 )
{
publishPacketSize += sizeof( uint16_t );
}
else
{
EMPTY_ELSE_MARKER;
}
/* 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 - publishPacketSize - 1;
/* Ensure that the given payload fits within the calculated limit. */
if( pPublishInfo->payloadLength > payloadLimit )
{
status = false;
}
else
{
/* Add the length of the PUBLISH payload. At this point, the "Remaining length"
* has been calculated. */
publishPacketSize += pPublishInfo->payloadLength;
/* Now that the "Remaining length" is known, recalculate the payload limit
* based on the size of its encoding. */
payloadLimit -= _remainingLengthEncodedSize( publishPacketSize );
/* Check that the given payload fits within the size allowed by MQTT spec. */
if( pPublishInfo->payloadLength > payloadLimit )
{
status = false;
}
else
{
/* Set the "Remaining length" output parameter and calculate the full
* size of the PUBLISH packet. */
*pRemainingLength = publishPacketSize;
publishPacketSize += 1 + _remainingLengthEncodedSize( publishPacketSize );
*pPacketSize = publishPacketSize;
}
}
return status;
}
/*-----------------------------------------------------------*/
static bool _subscriptionPacketSize( IotMqttOperationType_t type,
const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
size_t * pRemainingLength,
size_t * pPacketSize )
{
bool status = true;
size_t i = 0, subscriptionPacketSize = 0;
/* Only SUBSCRIBE and UNSUBSCRIBE operations should call this function. */
IotMqtt_Assert( ( type == IOT_MQTT_SUBSCRIBE ) || ( type == IOT_MQTT_UNSUBSCRIBE ) );
/* The variable header of a subscription packet consists of a 2-byte packet
* identifier. */
subscriptionPacketSize += sizeof( uint16_t );
/* Sum the lengths of all subscription topic filters; add 1 byte for each
* subscription's QoS if type is IOT_MQTT_SUBSCRIBE. */
for( i = 0; i < subscriptionCount; i++ )
{
/* Add the length of the topic filter. */
subscriptionPacketSize += pSubscriptionList[ i ].topicFilterLength + sizeof( uint16_t );
/* Only SUBSCRIBE packets include the QoS. */
if( type == IOT_MQTT_SUBSCRIBE )
{
subscriptionPacketSize += 1;
}
else
{
EMPTY_ELSE_MARKER;
}
}
/* 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( subscriptionPacketSize > MQTT_MAX_REMAINING_LENGTH )
{
status = false;
}
else
{
*pRemainingLength = subscriptionPacketSize;
/* Calculate the full size of the subscription packet by adding the size of the
* "Remaining length" field plus 1 byte for the "Packet type" field. Set the
* pPacketSize output parameter. */
subscriptionPacketSize += 1 + _remainingLengthEncodedSize( subscriptionPacketSize );
*pPacketSize = subscriptionPacketSize;
}
return status;
}
/*-----------------------------------------------------------*/
void _serializeConnect( const IotMqttConnectInfo_t * pConnectInfo,
size_t remainingLength,
uint8_t * pBuffer,
size_t connectPacketSize )
{
uint8_t connectFlags = 0;
uint8_t * pConnectPacket = pBuffer;
/* The first byte in the CONNECT packet is the control packet type. */
*pBuffer = MQTT_PACKET_TYPE_CONNECT;
pBuffer++;
/* 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. */
pBuffer = _encodeRemainingLength( pBuffer, remainingLength );
/* The string "MQTT" is placed at the beginning of the CONNECT packet's variable
* header. This string is 4 bytes long. */
pBuffer = _encodeString( pBuffer, "MQTT", 4 );
/* The MQTT protocol version is the second byte of the variable header. */
*pBuffer = MQTT_VERSION_3_1_1;
pBuffer++;
/* Set the CONNECT flags based on the given parameters. */
if( pConnectInfo->cleanSession == true )
{
UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_CLEAN );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Username and password depend on MQTT mode. */
if( pConnectInfo->awsIotMqttMode == true )
{
/* Set the username flag for AWS IoT metrics. The AWS IoT MQTT server
* never uses a password. */
#if AWS_IOT_MQTT_ENABLE_METRICS == 1
UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_USERNAME );
#endif
}
else
{
/* Set the flags for username and password if provided. */
if( pConnectInfo->pUserName != NULL )
{
UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_USERNAME );
}
else
{
EMPTY_ELSE_MARKER;
}
if( pConnectInfo->pPassword != NULL )
{
UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_PASSWORD );
}
else
{
EMPTY_ELSE_MARKER;
}
}
/* Set will flag if an LWT is provided. */
if( pConnectInfo->pWillInfo != NULL )
{
UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL );
/* Flags only need to be changed for will QoS 1 and 2. */
switch( pConnectInfo->pWillInfo->qos )
{
case IOT_MQTT_QOS_1:
UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL_QOS1 );
break;
case IOT_MQTT_QOS_2:
UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL_QOS2 );
break;
default:
break;
}
if( pConnectInfo->pWillInfo->retain == true )
{
UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL_RETAIN );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
*pBuffer = connectFlags;
pBuffer++;
/* Write the 2 bytes of the keep alive interval into the CONNECT packet. */
*pBuffer = UINT16_HIGH_BYTE( pConnectInfo->keepAliveSeconds );
*( pBuffer + 1 ) = UINT16_LOW_BYTE( pConnectInfo->keepAliveSeconds );
pBuffer += 2;
/* Write the client identifier into the CONNECT packet. */
pBuffer = _encodeString( pBuffer,
pConnectInfo->pClientIdentifier,
pConnectInfo->clientIdentifierLength );
/* Write the will topic name and message into the CONNECT packet if provided. */
if( pConnectInfo->pWillInfo != NULL )
{
pBuffer = _encodeString( pBuffer,
pConnectInfo->pWillInfo->pTopicName,
pConnectInfo->pWillInfo->topicNameLength );
pBuffer = _encodeString( pBuffer,
pConnectInfo->pWillInfo->pPayload,
( uint16_t ) pConnectInfo->pWillInfo->payloadLength );
}
else
{
EMPTY_ELSE_MARKER;
}
/* If metrics are enabled, write the metrics username into the CONNECT packet.
* Otherwise, write the username and password only when not connecting to an
* AWS IoT MQTT server. */
if( pConnectInfo->awsIotMqttMode == true )
{
#if AWS_IOT_MQTT_ENABLE_METRICS == 1
IotLogInfo( "Anonymous metrics (SDK language, SDK version) will be provided to AWS IoT. "
"Recompile with AWS_IOT_MQTT_ENABLE_METRICS set to 0 to disable." );
pBuffer = _encodeString( pBuffer,
AWS_IOT_METRICS_USERNAME,
AWS_IOT_METRICS_USERNAME_LENGTH );
#endif
}
else
{
if( pConnectInfo->pUserName != NULL )
{
pBuffer = _encodeString( pBuffer,
pConnectInfo->pUserName,
pConnectInfo->userNameLength );
}
else
{
EMPTY_ELSE_MARKER;
}
if( pConnectInfo->pPassword != NULL )
{
pBuffer = _encodeString( pBuffer,
pConnectInfo->pPassword,
pConnectInfo->passwordLength );
}
else
{
EMPTY_ELSE_MARKER;
}
}
/* Ensure that the difference between the end and beginning of the buffer
* is equal to connectPacketSize, i.e. pBuffer did not overflow. */
IotMqtt_Assert( ( size_t ) ( pBuffer - pConnectPacket ) == connectPacketSize );
/* Print out the serialized CONNECT packet for debugging purposes. */
IotLog_PrintBuffer( "MQTT CONNECT packet:", pConnectPacket, connectPacketSize );
}
/*-----------------------------------------------------------*/
void _serializePublish( const IotMqttPublishInfo_t * pPublishInfo,
size_t remainingLength,
uint16_t * pPacketIdentifier,
uint8_t ** pPacketIdentifierHigh,
uint8_t * pBuffer,
size_t publishPacketSize )
{
uint8_t publishFlags = 0;
uint16_t packetIdentifier = 0;
uint8_t * pPublishPacket = pBuffer;
/* The first byte of a PUBLISH packet contains the packet type and flags. */
publishFlags = MQTT_PACKET_TYPE_PUBLISH;
if( pPublishInfo->qos == IOT_MQTT_QOS_1 )
{
UINT8_SET_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS1 );
}
else if( pPublishInfo->qos == IOT_MQTT_QOS_2 )
{
UINT8_SET_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS2 );
}
else
{
EMPTY_ELSE_MARKER;
}
if( pPublishInfo->retain == true )
{
UINT8_SET_BIT( publishFlags, MQTT_PUBLISH_FLAG_RETAIN );
}
else
{
EMPTY_ELSE_MARKER;
}
*pBuffer = publishFlags;
pBuffer++;
/* The "Remaining length" is encoded from the second byte. */
pBuffer = _encodeRemainingLength( pBuffer, remainingLength );
/* The topic name is placed after the "Remaining length". */
pBuffer = _encodeString( pBuffer,
pPublishInfo->pTopicName,
pPublishInfo->topicNameLength );
/* A packet identifier is required for QoS 1 and 2 messages. */
if( pPublishInfo->qos > IOT_MQTT_QOS_0 )
{
/* Get the next packet identifier. It should always be nonzero. */
packetIdentifier = _nextPacketIdentifier();
IotMqtt_Assert( packetIdentifier != 0 );
/* Set the packet identifier output parameters. */
*pPacketIdentifier = packetIdentifier;
if( pPacketIdentifierHigh != NULL )
{
*pPacketIdentifierHigh = pBuffer;
}
else
{
EMPTY_ELSE_MARKER;
}
/* Place the packet identifier into the PUBLISH packet. */
*pBuffer = UINT16_HIGH_BYTE( packetIdentifier );
*( pBuffer + 1 ) = UINT16_LOW_BYTE( packetIdentifier );
pBuffer += 2;
}
else
{
EMPTY_ELSE_MARKER;
}
/* The payload is placed after the packet identifier. */
if( pPublishInfo->payloadLength > 0 )
{
( void ) memcpy( pBuffer, pPublishInfo->pPayload, pPublishInfo->payloadLength );
pBuffer += pPublishInfo->payloadLength;
}
else
{
EMPTY_ELSE_MARKER;
}
/* Ensure that the difference between the end and beginning of the buffer
* is equal to publishPacketSize, i.e. pBuffer did not overflow. */
IotMqtt_Assert( ( size_t ) ( pBuffer - pPublishPacket ) == publishPacketSize );
/* Print out the serialized PUBLISH packet for debugging purposes. */
IotLog_PrintBuffer( "MQTT PUBLISH packet:", pPublishPacket, publishPacketSize );
}
/*-----------------------------------------------------------*/
void _serializeSubscribe( const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
size_t remainingLength,
uint16_t * pPacketIdentifier,
uint8_t * pBuffer,
size_t subscribePacketSize )
{
uint16_t packetIdentifier = 0;
size_t i = 0;
uint8_t * pSubscribePacket = pBuffer;
/* The first byte in SUBSCRIBE is the packet type. */
*pBuffer = MQTT_PACKET_TYPE_SUBSCRIBE;
pBuffer++;
/* Encode the "Remaining length" starting from the second byte. */
pBuffer = _encodeRemainingLength( pBuffer, remainingLength );
/* Get the next packet identifier. It should always be nonzero. */
packetIdentifier = _nextPacketIdentifier();
*pPacketIdentifier = packetIdentifier;
IotMqtt_Assert( packetIdentifier != 0 );
/* Place the packet identifier into the SUBSCRIBE packet. */
*pBuffer = UINT16_HIGH_BYTE( packetIdentifier );
*( pBuffer + 1 ) = UINT16_LOW_BYTE( packetIdentifier );
pBuffer += 2;
/* Serialize each subscription topic filter and QoS. */
for( i = 0; i < subscriptionCount; i++ )
{
pBuffer = _encodeString( pBuffer,
pSubscriptionList[ i ].pTopicFilter,
pSubscriptionList[ i ].topicFilterLength );
/* Place the QoS in the SUBSCRIBE packet. */
*pBuffer = ( uint8_t ) ( pSubscriptionList[ i ].qos );
pBuffer++;
}
/* Ensure that the difference between the end and beginning of the buffer
* is equal to subscribePacketSize, i.e. pBuffer did not overflow. */
IotMqtt_Assert( ( size_t ) ( pBuffer - pSubscribePacket ) == subscribePacketSize );
/* Print out the serialized SUBSCRIBE packet for debugging purposes. */
IotLog_PrintBuffer( "MQTT SUBSCRIBE packet:", pSubscribePacket, subscribePacketSize );
}
/*-----------------------------------------------------------*/
void _serializeUnsubscribe( const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
size_t remainingLength,
uint16_t * pPacketIdentifier,
uint8_t * pBuffer,
size_t unsubscribePacketSize )
{
uint16_t packetIdentifier = 0;
size_t i = 0;
uint8_t * pUnsubscribePacket = pBuffer;
/* The first byte in UNSUBSCRIBE is the packet type. */
*pBuffer = MQTT_PACKET_TYPE_UNSUBSCRIBE;
pBuffer++;
/* Encode the "Remaining length" starting from the second byte. */
pBuffer = _encodeRemainingLength( pBuffer, remainingLength );
/* Get the next packet identifier. It should always be nonzero. */
packetIdentifier = _nextPacketIdentifier();
*pPacketIdentifier = packetIdentifier;
IotMqtt_Assert( packetIdentifier != 0 );
/* Place the packet identifier into the UNSUBSCRIBE packet. */
*pBuffer = UINT16_HIGH_BYTE( packetIdentifier );
*( pBuffer + 1 ) = UINT16_LOW_BYTE( packetIdentifier );
pBuffer += 2;
/* Serialize each subscription topic filter. */
for( i = 0; i < subscriptionCount; i++ )
{
pBuffer = _encodeString( pBuffer,
pSubscriptionList[ i ].pTopicFilter,
pSubscriptionList[ i ].topicFilterLength );
}
/* Ensure that the difference between the end and beginning of the buffer
* is equal to unsubscribePacketSize, i.e. pBuffer did not overflow. */
IotMqtt_Assert( ( size_t ) ( pBuffer - pUnsubscribePacket ) == unsubscribePacketSize );
/* Print out the serialized UNSUBSCRIBE packet for debugging purposes. */
IotLog_PrintBuffer( "MQTT UNSUBSCRIBE packet:", pUnsubscribePacket, unsubscribePacketSize );
}
/*-----------------------------------------------------------*/
uint8_t _IotMqtt_GetPacketType( void * pNetworkConnection,
const IotNetworkInterface_t * pNetworkInterface )
{
uint8_t packetType = 0xff;
/* The MQTT packet type is in the first byte of the packet. */
( void ) _IotMqtt_GetNextByte( pNetworkConnection,
pNetworkInterface,
&packetType );
return packetType;
}
/*-----------------------------------------------------------*/
size_t _IotMqtt_GetRemainingLength( void * pNetworkConnection,
const IotNetworkInterface_t * pNetworkInterface )
{
uint8_t encodedByte = 0;
size_t remainingLength = 0, multiplier = 1, bytesDecoded = 0, expectedSize = 0;
/* This algorithm is copied from the MQTT v3.1.1 spec. */
do
{
if( multiplier > 2097152 ) /* 128 ^ 3 */
{
remainingLength = MQTT_REMAINING_LENGTH_INVALID;
break;
}
else
{
if( _IotMqtt_GetNextByte( pNetworkConnection,
pNetworkInterface,
&encodedByte ) == true )
{
remainingLength += ( encodedByte & 0x7F ) * multiplier;
multiplier *= 128;
bytesDecoded++;
}
else
{
remainingLength = MQTT_REMAINING_LENGTH_INVALID;
break;
}
}
} while( ( encodedByte & 0x80 ) != 0 );
/* 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;
}
else
{
/* Valid remaining length should be at most 4 bytes. */
IotMqtt_Assert( bytesDecoded <= 4 );
}
}
else
{
EMPTY_ELSE_MARKER;
}
return remainingLength;
}
/*-----------------------------------------------------------*/
size_t _IotMqtt_GetRemainingLength_Generic( void * pNetworkConnection,
IotMqttGetNextByte_t getNextByte )
{
uint8_t encodedByte = 0;
size_t remainingLength = 0, multiplier = 1, bytesDecoded = 0, expectedSize = 0;
/* This algorithm is copied from the MQTT v3.1.1 spec. */
do
{
if( multiplier > 2097152 ) /* 128 ^ 3 */
{
remainingLength = MQTT_REMAINING_LENGTH_INVALID;
break;
}
else
{
if( getNextByte( pNetworkConnection, &encodedByte ) == IOT_MQTT_SUCCESS )
{
remainingLength += ( encodedByte & 0x7F ) * multiplier;
multiplier *= 128;
bytesDecoded++;
}
else
{
remainingLength = MQTT_REMAINING_LENGTH_INVALID;
break;
}
}
} while( ( encodedByte & 0x80 ) != 0 );
/* 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;
}
else
{
/* Valid remaining length should be at most 4 bytes. */
IotMqtt_Assert( bytesDecoded <= 4 );
}
}
else
{
EMPTY_ELSE_MARKER;
}
return remainingLength;
}
/*-----------------------------------------------------------*/
IotMqttError_t _IotMqtt_SerializeConnect( const IotMqttConnectInfo_t * pConnectInfo,
uint8_t ** pConnectPacket,
size_t * pPacketSize )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
size_t remainingLength = 0, connectPacketSize = 0;
uint8_t * pBuffer = NULL;
/* Calculate the "Remaining length" field and total packet size. If it exceeds
* what is allowed in the MQTT standard, return an error. */
if( _connectPacketSize( pConnectInfo, &remainingLength, &connectPacketSize ) == false )
{
IotLogError( "Connect packet length exceeds %lu, which is the maximum"
" size allowed by MQTT 3.1.1.",
MQTT_PACKET_CONNECT_MAX_SIZE );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Total size of the connect packet should be larger than the "Remaining length"
* field. */
IotMqtt_Assert( connectPacketSize > remainingLength );
/* Allocate memory to hold the CONNECT packet. */
pBuffer = IotMqtt_MallocMessage( connectPacketSize );
/* Check that sufficient memory was allocated. */
if( pBuffer == NULL )
{
IotLogError( "Failed to allocate memory for CONNECT packet." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_NO_MEMORY );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Set the output parameters. The remainder of this function always succeeds. */
*pConnectPacket = pBuffer;
*pPacketSize = connectPacketSize;
_serializeConnect( pConnectInfo, remainingLength, pBuffer, connectPacketSize );
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
IotMqttError_t _IotMqtt_DeserializeConnack( _mqttPacket_t * pConnack )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
const uint8_t * pRemainingData = pConnack->pRemainingData;
/* If logging is enabled, declare the CONNACK response code strings. The
* fourth byte of CONNACK indexes into this array for the corresponding response. */
#if LIBRARY_LOG_LEVEL > IOT_LOG_NONE
static const char * 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 */
};
#endif
/* Check that the control packet type is 0x20. */
if( pConnack->type != MQTT_PACKET_TYPE_CONNACK )
{
IotLog( IOT_LOG_ERROR,
&_logHideAll,
"Bad control packet type 0x%02x.",
pConnack->type );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
/* 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 )
{
IotLog( IOT_LOG_ERROR,
&_logHideAll,
"CONNACK does not have remaining length of %d.",
MQTT_PACKET_CONNACK_REMAINING_LENGTH );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check the reserved bits in CONNACK. The high 7 bits of the second byte
* in CONNACK must be 0. */
if( ( pRemainingData[ 0 ] | 0x01 ) != 0x01 )
{
IotLog( IOT_LOG_ERROR,
&_logHideAll,
"Reserved bits in CONNACK incorrect." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Determine if the "Session Present" bit it 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 )
{
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"CONNACK session present bit set." );
/* MQTT 3.1.1 specifies that the fourth byte in CONNACK must be 0 if the
* "Session Present" bit is set. */
if( pRemainingData[ 1 ] != 0 )
{
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"CONNACK session present bit not set." );
}
/* In MQTT 3.1.1, only values 0 through 5 are valid CONNACK response codes. */
if( pRemainingData[ 1 ] > 5 )
{
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"CONNACK response %hhu is not valid.",
pRemainingData[ 1 ] );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Print the appropriate message for the CONNACK response code if logs are
* enabled. */
#if LIBRARY_LOG_LEVEL > IOT_LOG_NONE
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"%s",
pConnackResponses[ pRemainingData[ 1 ] ] );
#endif
/* A nonzero CONNACK response code means the connection was refused. */
if( pRemainingData[ 1 ] > 0 )
{
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_SERVER_REFUSED );
}
else
{
EMPTY_ELSE_MARKER;
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
IotMqttError_t _IotMqtt_SerializePublish( const IotMqttPublishInfo_t * pPublishInfo,
uint8_t ** pPublishPacket,
size_t * pPacketSize,
uint16_t * pPacketIdentifier,
uint8_t ** pPacketIdentifierHigh )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
size_t remainingLength = 0, publishPacketSize = 0;
uint8_t * pBuffer = NULL;
/* Calculate the "Remaining length" field and total packet size. If it exceeds
* what is allowed in the MQTT standard, return an error. */
if( _publishPacketSize( pPublishInfo, &remainingLength, &publishPacketSize ) == false )
{
IotLogError( "Publish packet remaining length exceeds %lu, which is the "
"maximum size allowed by MQTT 3.1.1.",
MQTT_MAX_REMAINING_LENGTH );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Total size of the publish packet should be larger than the "Remaining length"
* field. */
IotMqtt_Assert( publishPacketSize > remainingLength );
/* Allocate memory to hold the PUBLISH packet. */
pBuffer = IotMqtt_MallocMessage( publishPacketSize );
/* Check that sufficient memory was allocated. */
if( pBuffer == NULL )
{
IotLogError( "Failed to allocate memory for PUBLISH packet." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_NO_MEMORY );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Set the output parameters. The remainder of this function always succeeds. */
*pPublishPacket = pBuffer;
*pPacketSize = publishPacketSize;
/* Serialize publish into buffer pointed to by pBuffer */
_serializePublish( pPublishInfo,
remainingLength,
pPacketIdentifier,
pPacketIdentifierHigh,
pBuffer,
publishPacketSize );
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
void _IotMqtt_PublishSetDup( uint8_t * pPublishPacket,
uint8_t * pPacketIdentifierHigh,
uint16_t * pNewPacketIdentifier )
{
uint16_t newPacketIdentifier = 0;
/* For an AWS IoT MQTT server, change the packet identifier. */
if( pPacketIdentifierHigh != NULL )
{
/* Output parameter for new packet identifier must be provided. */
IotMqtt_Assert( pNewPacketIdentifier != NULL );
/* Generate a new packet identifier. */
newPacketIdentifier = _nextPacketIdentifier();
IotLogDebug( "Changing PUBLISH packet identifier %hu to %hu.",
UINT16_DECODE( pPacketIdentifierHigh ),
newPacketIdentifier );
/* Replace the packet identifier. */
*pPacketIdentifierHigh = UINT16_HIGH_BYTE( newPacketIdentifier );
*( pPacketIdentifierHigh + 1 ) = UINT16_LOW_BYTE( newPacketIdentifier );
*pNewPacketIdentifier = newPacketIdentifier;
}
else
{
/* For a compliant MQTT 3.1.1 server, set the DUP flag. */
UINT8_SET_BIT( *pPublishPacket, MQTT_PUBLISH_FLAG_DUP );
IotLogDebug( "PUBLISH DUP flag set." );
}
}
/*-----------------------------------------------------------*/
IotMqttError_t _IotMqtt_DeserializePublish( _mqttPacket_t * pPublish )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
IotMqttPublishInfo_t * pOutput = &( pPublish->u.pIncomingPublish->u.publish.publishInfo );
uint8_t publishFlags = 0;
const uint8_t * pVariableHeader = pPublish->pRemainingData, * pPacketIdentifierHigh = NULL;
/* The flags are the lower 4 bits of the first byte in PUBLISH. */
publishFlags = pPublish->type;
/* Parse the Retain bit. */
pOutput->retain = UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_RETAIN );
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"Retain bit is %d.", pOutput->retain );
/* Check for QoS 2. */
if( UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS2 ) == true )
{
/* PUBLISH packet is invalid if both QoS 1 and QoS 2 bits are set. */
if( UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS1 ) == true )
{
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"Bad QoS: 3." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
pOutput->qos = IOT_MQTT_QOS_2;
}
/* Check for QoS 1. */
else if( UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS1 ) == true )
{
pOutput->qos = IOT_MQTT_QOS_1;
}
/* If the PUBLISH isn't QoS 1 or 2, then it's QoS 0. */
else
{
pOutput->qos = IOT_MQTT_QOS_0;
}
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"QoS is %d.", pOutput->qos );
/* Parse the DUP bit. */
if( UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_DUP ) == true )
{
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"DUP is 1." );
}
else
{
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"DUP is 0." );
}
/* Sanity checks for "Remaining length". */
if( pOutput->qos == IOT_MQTT_QOS_0 )
{
/* 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). */
if( pPublish->remainingLength < 3 )
{
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"QoS 0 PUBLISH cannot have a remaining length less than 3." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
/* A QoS 1 or 2 PUBLISH must have a remaining length of at least 5 to
* accommodate a packet identifier as well as the topic name length and
* topic name. */
if( pPublish->remainingLength < 5 )
{
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"QoS 1 or 2 PUBLISH cannot have a remaining length less than 5." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
}
/* 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. */
pOutput->topicNameLength = UINT16_DECODE( pVariableHeader );
/* Sanity checks for topic name length and "Remaining length". */
if( pOutput->qos == IOT_MQTT_QOS_0 )
{
/* Check that the "Remaining length" is at least as large as the variable
* header. */
if( pPublish->remainingLength < pOutput->topicNameLength + sizeof( uint16_t ) )
{
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"Remaining length cannot be less than variable header length." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
/* Check that the "Remaining length" is at least as large as the variable
* header. */
if( pPublish->remainingLength < pOutput->topicNameLength + 2 * sizeof( uint16_t ) )
{
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"Remaining length cannot be less than variable header length." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
}
/* Parse the topic. */
pOutput->pTopicName = ( const char * ) ( pVariableHeader + sizeof( uint16_t ) );
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"Topic name length %hu: %.*s",
pOutput->topicNameLength,
pOutput->topicNameLength,
pOutput->pTopicName );
/* Extract the packet identifier for QoS 1 or 2 PUBLISH packets. Packet
* identifier starts immediately after the topic name. */
pPacketIdentifierHigh = ( const uint8_t * ) ( pOutput->pTopicName + pOutput->topicNameLength );
if( pOutput->qos > IOT_MQTT_QOS_0 )
{
pPublish->packetIdentifier = UINT16_DECODE( pPacketIdentifierHigh );
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"Packet identifier %hu.", pPublish->packetIdentifier );
/* Packet identifier cannot be 0. */
if( pPublish->packetIdentifier == 0 )
{
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Calculate the length of the payload. QoS 1 or 2 PUBLISH packets contain
* a packet identifier, but QoS 0 PUBLISH packets do not. */
if( pOutput->qos == IOT_MQTT_QOS_0 )
{
pOutput->payloadLength = ( pPublish->remainingLength - pOutput->topicNameLength - sizeof( uint16_t ) );
pOutput->pPayload = pPacketIdentifierHigh;
}
else
{
pOutput->payloadLength = ( pPublish->remainingLength - pOutput->topicNameLength - 2 * sizeof( uint16_t ) );
pOutput->pPayload = pPacketIdentifierHigh + sizeof( uint16_t );
}
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"Payload length %hu.", pOutput->payloadLength );
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
IotMqttError_t _IotMqtt_SerializePuback( uint16_t packetIdentifier,
uint8_t ** pPubackPacket,
size_t * pPacketSize )
{
IotMqttError_t status = IOT_MQTT_SUCCESS;
/* Allocate memory for PUBACK. */
uint8_t * pBuffer = IotMqtt_MallocMessage( MQTT_PACKET_PUBACK_SIZE );
if( pBuffer == NULL )
{
IotLogError( "Failed to allocate memory for PUBACK packet" );
status = IOT_MQTT_NO_MEMORY;
}
else
{
/* Set the output parameters. The remainder of this function always succeeds. */
*pPubackPacket = pBuffer;
*pPacketSize = MQTT_PACKET_PUBACK_SIZE;
/* Set the 4 bytes in PUBACK. */
pBuffer[ 0 ] = MQTT_PACKET_TYPE_PUBACK;
pBuffer[ 1 ] = MQTT_PACKET_PUBACK_REMAINING_LENGTH;
pBuffer[ 2 ] = UINT16_HIGH_BYTE( packetIdentifier );
pBuffer[ 3 ] = UINT16_LOW_BYTE( packetIdentifier );
/* Print out the serialized PUBACK packet for debugging purposes. */
IotLog_PrintBuffer( "MQTT PUBACK packet:", *pPubackPacket, MQTT_PACKET_PUBACK_SIZE );
}
return status;
}
/*-----------------------------------------------------------*/
IotMqttError_t _IotMqtt_DeserializePuback( _mqttPacket_t * pPuback )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
/* Check the "Remaining length" of the received PUBACK. */
if( pPuback->remainingLength != MQTT_PACKET_PUBACK_REMAINING_LENGTH )
{
IotLog( IOT_LOG_ERROR,
&_logHideAll,
"PUBACK does not have remaining length of %d.",
MQTT_PACKET_PUBACK_REMAINING_LENGTH );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Extract the packet identifier (third and fourth bytes) from PUBACK. */
pPuback->packetIdentifier = UINT16_DECODE( pPuback->pRemainingData );
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"Packet identifier %hu.", pPuback->packetIdentifier );
/* Packet identifier cannot be 0. */
if( pPuback->packetIdentifier == 0 )
{
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check that the control packet type is 0x40 (this must be done after the
* packet identifier is parsed). */
if( pPuback->type != MQTT_PACKET_TYPE_PUBACK )
{
IotLog( IOT_LOG_ERROR,
&_logHideAll,
"Bad control packet type 0x%02x.",
pPuback->type );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
IotMqttError_t _IotMqtt_SerializeSubscribe( const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
uint8_t ** pSubscribePacket,
size_t * pPacketSize,
uint16_t * pPacketIdentifier )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
size_t subscribePacketSize = 0, remainingLength = 0;
uint8_t * pBuffer = NULL;
/* Calculate the "Remaining length" field and total packet size. If it exceeds
* what is allowed in the MQTT standard, return an error. */
if( _subscriptionPacketSize( IOT_MQTT_SUBSCRIBE,
pSubscriptionList,
subscriptionCount,
&remainingLength,
&subscribePacketSize ) == false )
{
IotLogError( "Subscribe packet remaining length exceeds %lu, which is the "
"maximum size allowed by MQTT 3.1.1.",
MQTT_MAX_REMAINING_LENGTH );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Total size of the subscribe packet should be larger than the "Remaining length"
* field. */
IotMqtt_Assert( subscribePacketSize > remainingLength );
/* Allocate memory to hold the SUBSCRIBE packet. */
pBuffer = IotMqtt_MallocMessage( subscribePacketSize );
/* Check that sufficient memory was allocated. */
if( pBuffer == NULL )
{
IotLogError( "Failed to allocate memory for SUBSCRIBE packet." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_NO_MEMORY );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Set the output parameters. The remainder of this function always succeeds. */
*pSubscribePacket = pBuffer;
*pPacketSize = subscribePacketSize;
/* Serialize subscribe into buffer pointed to by pBuffer */
_serializeSubscribe( pSubscriptionList,
subscriptionCount,
remainingLength,
pPacketIdentifier,
pBuffer,
subscribePacketSize );
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
IotMqttError_t _IotMqtt_DeserializeSuback( _mqttPacket_t * pSuback )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
size_t i = 0, remainingLength = pSuback->remainingLength;
uint8_t subscriptionStatus = 0;
const uint8_t * pVariableHeader = pSuback->pRemainingData;
/* A SUBACK must have a remaining length of at least 3 to accommodate the
* packet identifier and at least one return code. */
if( remainingLength < 3 )
{
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"SUBACK cannot have a remaining length less than 3." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Extract the packet identifier (first 2 bytes of variable header) from SUBACK. */
pSuback->packetIdentifier = UINT16_DECODE( pVariableHeader );
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"Packet identifier %hu.", pSuback->packetIdentifier );
/* Check that the control packet type is 0x90 (this must be done after the
* packet identifier is parsed). */
if( pSuback->type != MQTT_PACKET_TYPE_SUBACK )
{
IotLog( IOT_LOG_ERROR,
&_logHideAll,
"Bad control packet type 0x%02x.",
pSuback->type );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Iterate through each status byte in the SUBACK packet. */
for( i = 0; i < remainingLength - sizeof( uint16_t ); i++ )
{
/* Read a single status byte in SUBACK. */
subscriptionStatus = *( pVariableHeader + sizeof( uint16_t ) + i );
/* MQTT 3.1.1 defines the following values as status codes. */
switch( subscriptionStatus )
{
case 0x00:
case 0x01:
case 0x02:
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"Topic filter %lu accepted, max QoS %hhu.",
( unsigned long ) i, subscriptionStatus );
break;
case 0x80:
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"Topic filter %lu refused.", ( unsigned long ) i );
/* Remove a rejected subscription from the subscription manager. */
_IotMqtt_RemoveSubscriptionByPacket( pSuback->u.pMqttConnection,
pSuback->packetIdentifier,
( int32_t ) i );
status = IOT_MQTT_SERVER_REFUSED;
break;
default:
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"Bad SUBSCRIBE status %hhu.", subscriptionStatus );
status = IOT_MQTT_BAD_RESPONSE;
break;
}
/* Stop parsing the subscription statuses if a bad response was received. */
if( status == IOT_MQTT_BAD_RESPONSE )
{
break;
}
else
{
EMPTY_ELSE_MARKER;
}
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
IotMqttError_t _IotMqtt_SerializeUnsubscribe( const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
uint8_t ** pUnsubscribePacket,
size_t * pPacketSize,
uint16_t * pPacketIdentifier )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
size_t unsubscribePacketSize = 0, remainingLength = 0;
uint8_t * pBuffer = NULL;
/* Calculate the "Remaining length" field and total packet size. If it exceeds
* what is allowed in the MQTT standard, return an error. */
if( _subscriptionPacketSize( IOT_MQTT_UNSUBSCRIBE,
pSubscriptionList,
subscriptionCount,
&remainingLength,
&unsubscribePacketSize ) == false )
{
IotLogError( "Unsubscribe packet remaining length exceeds %lu, which is the "
"maximum size allowed by MQTT 3.1.1.",
MQTT_MAX_REMAINING_LENGTH );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Total size of the unsubscribe packet should be larger than the "Remaining length"
* field. */
IotMqtt_Assert( unsubscribePacketSize > remainingLength );
/* Allocate memory to hold the UNSUBSCRIBE packet. */
pBuffer = IotMqtt_MallocMessage( unsubscribePacketSize );
/* Check that sufficient memory was allocated. */
if( pBuffer == NULL )
{
IotLogError( "Failed to allocate memory for UNSUBSCRIBE packet." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_NO_MEMORY );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Set the output parameters. The remainder of this function always succeeds. */
*pUnsubscribePacket = pBuffer;
*pPacketSize = unsubscribePacketSize;
/* Serialize unsubscribe into buffer pointed to by pBuffer */
_serializeUnsubscribe( pSubscriptionList,
subscriptionCount,
remainingLength,
pPacketIdentifier,
pBuffer,
unsubscribePacketSize );
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
IotMqttError_t _IotMqtt_DeserializeUnsuback( _mqttPacket_t * pUnsuback )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
/* Check the "Remaining length" (second byte) of the received UNSUBACK. */
if( pUnsuback->remainingLength != MQTT_PACKET_UNSUBACK_REMAINING_LENGTH )
{
IotLog( IOT_LOG_ERROR,
&_logHideAll,
"UNSUBACK does not have remaining length of %d.",
MQTT_PACKET_UNSUBACK_REMAINING_LENGTH );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Extract the packet identifier (third and fourth bytes) from UNSUBACK. */
pUnsuback->packetIdentifier = UINT16_DECODE( pUnsuback->pRemainingData );
/* Packet identifier cannot be 0. */
if( pUnsuback->packetIdentifier == 0 )
{
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
IotLog( IOT_LOG_DEBUG,
&_logHideAll,
"Packet identifier %hu.", pUnsuback->packetIdentifier );
/* Check that the control packet type is 0xb0 (this must be done after the
* packet identifier is parsed). */
if( pUnsuback->type != MQTT_PACKET_TYPE_UNSUBACK )
{
IotLog( IOT_LOG_ERROR,
&_logHideAll,
"Bad control packet type 0x%02x.",
pUnsuback->type );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
IotMqttError_t _IotMqtt_SerializePingreq( uint8_t ** pPingreqPacket,
size_t * pPacketSize )
{
/* PINGREQ packets are always the same. */
static const uint8_t pPingreq[ MQTT_PACKET_PINGREQ_SIZE ] =
{
MQTT_PACKET_TYPE_PINGREQ,
0x00
};
/* Set the output parameters. */
*pPingreqPacket = ( uint8_t * ) pPingreq;
*pPacketSize = MQTT_PACKET_PINGREQ_SIZE;
/* Print out the PINGREQ packet for debugging purposes. */
IotLog_PrintBuffer( "MQTT PINGREQ packet:", pPingreq, MQTT_PACKET_PINGREQ_SIZE );
return IOT_MQTT_SUCCESS;
}
/*-----------------------------------------------------------*/
IotMqttError_t _IotMqtt_DeserializePingresp( _mqttPacket_t * pPingresp )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
/* Check that the control packet type is 0xd0. */
if( pPingresp->type != MQTT_PACKET_TYPE_PINGRESP )
{
IotLog( IOT_LOG_ERROR,
&_logHideAll,
"Bad control packet type 0x%02x.",
pPingresp->type );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check the "Remaining length" (second byte) of the received PINGRESP. */
if( pPingresp->remainingLength != MQTT_PACKET_PINGRESP_REMAINING_LENGTH )
{
IotLog( IOT_LOG_ERROR,
&_logHideAll,
"PINGRESP does not have remaining length of %d.",
MQTT_PACKET_PINGRESP_REMAINING_LENGTH );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
IotMqttError_t _IotMqtt_SerializeDisconnect( uint8_t ** pDisconnectPacket,
size_t * pPacketSize )
{
/* DISCONNECT packets are always the same. */
static const uint8_t pDisconnect[ MQTT_PACKET_DISCONNECT_SIZE ] =
{
MQTT_PACKET_TYPE_DISCONNECT,
0x00
};
/* Set the output parameters. */
*pDisconnectPacket = ( uint8_t * ) pDisconnect;
*pPacketSize = MQTT_PACKET_DISCONNECT_SIZE;
/* Print out the DISCONNECT packet for debugging purposes. */
IotLog_PrintBuffer( "MQTT DISCONNECT packet:", pDisconnect, MQTT_PACKET_DISCONNECT_SIZE );
return IOT_MQTT_SUCCESS;
}
/*-----------------------------------------------------------*/
void _IotMqtt_FreePacket( uint8_t * pPacket )
{
uint8_t packetType = *pPacket;
/* Don't call free on DISCONNECT and PINGREQ; those are allocated from static
* memory. */
if( packetType != MQTT_PACKET_TYPE_DISCONNECT )
{
if( packetType != MQTT_PACKET_TYPE_PINGREQ )
{
IotMqtt_FreeMessage( pPacket );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
}
/*-----------------------------------------------------------*/
/* Public interface functions for serialization */
/*-----------------------------------------------------------*/
IotMqttError_t IotMqtt_GetConnectPacketSize( const IotMqttConnectInfo_t * pConnectInfo,
size_t * pRemainingLength,
size_t * pPacketSize )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
if( ( pConnectInfo == NULL ) || ( pRemainingLength == NULL ) || ( pPacketSize == NULL ) )
{
IotLogError( "IotMqtt_GetConnectPacketSize() called with required parameter(s) set to NULL." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
if( ( pConnectInfo->clientIdentifierLength == 0 ) || ( pConnectInfo->pClientIdentifier == NULL ) )
{
IotLogError( "IotMqtt_GetConnectPacketSize() client identifier must be set." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
/* Calculate the "Remaining length" field and total packet size. If it exceeds
* what is allowed in the MQTT standard, return an error. */
if( _connectPacketSize( pConnectInfo, pRemainingLength, pPacketSize ) == false )
{
IotLogError( "Connect packet length exceeds %lu, which is the maximum"
" size allowed by MQTT 3.1.1.",
MQTT_PACKET_CONNECT_MAX_SIZE );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Total size of the subscribe packet should be larger than the "Remaining length"
* field. */
if( ( *pPacketSize ) < ( *pRemainingLength ) )
{
IotLogError( "Connection packet remaining length (%lu) exceeds packet size (%lu)",
( *pRemainingLength ), ( *pPacketSize ) );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
IotMqttError_t IotMqtt_SerializeConnect( const IotMqttConnectInfo_t * pConnectInfo,
size_t remainingLength,
uint8_t * pBuffer,
size_t bufferSize )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
if( ( pBuffer == NULL ) || ( pConnectInfo == NULL ) )
{
IotLogError( "IotMqtt_SerializeConnect() called with required parameter(s) set to NULL." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
if( ( pConnectInfo->clientIdentifierLength == 0 ) || ( pConnectInfo->pClientIdentifier == NULL ) )
{
IotLogError( "IotMqtt_SerializeConnect() client identifier must be set." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
if( remainingLength > bufferSize )
{
IotLogError( " Serialize Connect packet remaining length (%lu) exceeds buffer size (%lu)",
remainingLength, bufferSize );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
_serializeConnect( pConnectInfo,
remainingLength,
pBuffer,
bufferSize );
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
IotMqttError_t IotMqtt_GetSubscriptionPacketSize( IotMqttOperationType_t type,
const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
size_t * pRemainingLength,
size_t * pPacketSize )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
if( ( pSubscriptionList == NULL ) || ( pRemainingLength == NULL ) || ( pPacketSize == NULL ) )
{
IotLogError( "IotMqtt_GetSubscriptionPacketSize() called with required parameter(s) set to NULL." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
if( ( type != IOT_MQTT_SUBSCRIBE ) && ( type != IOT_MQTT_UNSUBSCRIBE ) )
{
IotLogError( "IotMqtt_GetSubscriptionPacketSize() called with unknown type." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
if( subscriptionCount == 0 )
{
IotLogError( "IotMqtt_GetSubscriptionPacketSize() called with zero subscription count." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
if( _subscriptionPacketSize( type,
pSubscriptionList,
subscriptionCount,
pRemainingLength,
pPacketSize ) == false )
{
IotLogError( "Unsubscribe packet remaining length exceeds %lu, which is the "
"maximum size allowed by MQTT 3.1.1.",
MQTT_MAX_REMAINING_LENGTH );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Total size of the subscribe packet should be larger than the "Remaining length"
* field. */
if( ( *pPacketSize ) < ( *pRemainingLength ) )
{
IotLogError( "Subscription packet remaining length (%lu) exceeds packet size (%lu)",
( *pRemainingLength ), ( *pPacketSize ) );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
IotMqttError_t IotMqtt_SerializeSubscribe( const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
size_t remainingLength,
uint16_t * pPacketIdentifier,
uint8_t * pBuffer,
size_t bufferSize )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
if( ( pBuffer == NULL ) || ( pSubscriptionList == NULL ) || ( pPacketIdentifier == NULL ) )
{
IotLogError( "IotMqtt_SerializeSubscribe() called with required parameter(s) set to NULL." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
if( subscriptionCount == 0 )
{
IotLogError( "IotMqtt_SerializeSubscribe() called with zero subscription count." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
if( remainingLength > bufferSize )
{
IotLogError( " Subscribe packet remaining length (%lu) exceeds buffer size (%lu).",
remainingLength, bufferSize );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
_serializeSubscribe( pSubscriptionList,
subscriptionCount,
remainingLength,
pPacketIdentifier,
pBuffer,
bufferSize );
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
IotMqttError_t IotMqtt_GetPublishPacketSize( IotMqttPublishInfo_t * pPublishInfo,
size_t * pRemainingLength,
size_t * pPacketSize )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
if( ( pPublishInfo == NULL ) || ( pRemainingLength == NULL ) || ( pPacketSize == NULL ) )
{
IotLogError( "IotMqtt_GetPublishPacketSize() called with required parameter(s) set to NULL." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
if( ( pPublishInfo->pTopicName == NULL ) || ( pPublishInfo->topicNameLength == 0 ) )
{
IotLogError( "IotMqtt_GetPublishPacketSize() called with no topic." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
/* Calculate the "Remaining length" field and total packet size. If it exceeds
* what is allowed in the MQTT standard, return an error. */
if( _publishPacketSize( pPublishInfo, pRemainingLength, pPacketSize ) == false )
{
IotLogError( "Publish packet remaining length exceeds %lu, which is the "
"maximum size allowed by MQTT 3.1.1.",
MQTT_MAX_REMAINING_LENGTH );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Total size of the publish packet should be larger than the "Remaining length"
* field. */
if( ( *pPacketSize ) < ( *pRemainingLength ) )
{
IotLogError( "Publish packet remaining length (%lu) exceeds packet size (%lu).",
( *pRemainingLength ), ( *pPacketSize ) );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
IotMqttError_t IotMqtt_SerializePublish( IotMqttPublishInfo_t * pPublishInfo,
size_t remainingLength,
uint16_t * pPacketIdentifier,
uint8_t ** pPacketIdentifierHigh,
uint8_t * pBuffer,
size_t bufferSize )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
if( ( pBuffer == NULL ) || ( pPublishInfo == NULL ) || ( pPacketIdentifier == NULL ) )
{
IotLogError( "IotMqtt_SerializePublish() called with required parameter(s) set to NULL." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
if( ( pPublishInfo->pTopicName == NULL ) || ( pPublishInfo->topicNameLength == 0 ) )
{
IotLogError( "IotMqtt_SerializePublish() called with no topic." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
if( remainingLength > bufferSize )
{
IotLogError( "Publish packet remaining length (%lu) exceeds buffer size (%lu).",
remainingLength, bufferSize );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
_serializePublish( pPublishInfo,
remainingLength,
pPacketIdentifier,
pPacketIdentifierHigh,
pBuffer,
bufferSize );
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
IotMqttError_t IotMqtt_SerializeUnsubscribe( const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
size_t remainingLength,
uint16_t * pPacketIdentifier,
uint8_t * pBuffer,
size_t bufferSize )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
if( ( pBuffer == NULL ) || ( pPacketIdentifier == NULL ) || ( pSubscriptionList == NULL ) )
{
IotLogError( "IotMqtt_SerializeUnsubscribe() called with required parameter(s) set to NULL." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
if( subscriptionCount == 0 )
{
IotLogError( "IotMqtt_SerializeUnsubscribe() called with zero subscription count." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
if( remainingLength > bufferSize )
{
IotLogError( "Unsubscribe packet remaining length (%lu) exceeds buffer size (%lu).",
remainingLength, bufferSize );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
_serializeUnsubscribe( pSubscriptionList,
subscriptionCount,
remainingLength,
pPacketIdentifier,
pBuffer,
bufferSize );
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
IotMqttError_t IotMqtt_SerializeDisconnect( uint8_t * pBuffer,
size_t bufferSize )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
uint8_t * pDisconnectPacket = NULL;
size_t remainingLength = 0;
if( pBuffer == NULL )
{
IotLogError( "IotMqtt_SerializeDisconnect() called with NULL buffer pointer." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
if( bufferSize < MQTT_PACKET_DISCONNECT_SIZE )
{
IotLogError( "Disconnect packet length (%lu) exceeds buffer size (%lu).",
MQTT_PACKET_DISCONNECT_SIZE, bufferSize );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
/* Call internal function with local variables, as disconnect uses
* static memory, there is no need to pass the buffer
* Note: _IotMqtt_SerializeDisconnect always succeeds */
_IotMqtt_SerializeDisconnect( &pDisconnectPacket, &remainingLength );
memcpy( pBuffer, pDisconnectPacket, MQTT_PACKET_DISCONNECT_SIZE );
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
IotMqttError_t IotMqtt_SerializePingreq( uint8_t * pBuffer,
size_t bufferSize )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
uint8_t * pPingreqPacket = NULL;
size_t packetSize = 0;
if( pBuffer == NULL )
{
IotLogError( "IotMqtt_SerializePingreq() called with NULL buffer pointer." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
if( bufferSize < MQTT_PACKET_PINGREQ_SIZE )
{
IotLogError( "Pingreq length (%lu) exceeds buffer size (%lu).",
MQTT_PACKET_DISCONNECT_SIZE, bufferSize );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
/* Call internal function with local variables, as ping request uses
* static memory, there is no need to pass the buffer
* Note: _IotMqtt_SerializePingReq always succeeds */
_IotMqtt_SerializePingreq( &pPingreqPacket, &packetSize );
memcpy( pBuffer, pPingreqPacket, MQTT_PACKET_PINGREQ_SIZE );
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
IotMqttError_t IotMqtt_DeserializePublish( IotMqttPacketInfo_t * pMqttPacket )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
/* Internal MQTT packet structure */
_mqttPacket_t mqttPacket;
/* Internal MQTT operation structure needed for deserializing publish */
_mqttOperation_t mqttOperation;
if( pMqttPacket == NULL )
{
IotLogError( "IotMqtt_DeserializePublish()called with NULL pMqttPacket pointer." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
if( ( pMqttPacket->type & 0xf0 ) != MQTT_PACKET_TYPE_PUBLISH )
{
IotLogError( "IotMqtt_DeserializePublish() called with incorrect packet type:(%lu).", pMqttPacket->type );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
/* Set internal mqtt packet parameters. */
memset( ( void * ) &mqttPacket, 0x00, sizeof( _mqttPacket_t ) );
mqttPacket.pRemainingData = pMqttPacket->pRemainingData;
mqttPacket.remainingLength = pMqttPacket->remainingLength;
mqttPacket.type = pMqttPacket->type;
/* Set Publish specific parameters */
memset( ( void * ) &mqttOperation, 0x00, sizeof( _mqttOperation_t ) );
mqttOperation.incomingPublish = true;
mqttPacket.u.pIncomingPublish = &mqttOperation;
status = _IotMqtt_DeserializePublish( &mqttPacket );
if( status == IOT_MQTT_SUCCESS )
{
pMqttPacket->pubInfo = mqttOperation.u.publish.publishInfo;
pMqttPacket->packetIdentifier = mqttPacket.packetIdentifier;
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
IotMqttError_t IotMqtt_DeserializeResponse( IotMqttPacketInfo_t * pMqttPacket )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
/* Internal MQTT packet structure */
_mqttPacket_t mqttPacket;
if( ( pMqttPacket == NULL ) || ( pMqttPacket->pRemainingData == NULL ) )
{
IotLogError( "IotMqtt_DeserializeResponse() called with NULL pMqttPacket pointer or NULL pRemainingLength." );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
/* Set internal mqtt packet parameters. */
memset( ( void * ) &mqttPacket, 0x00, sizeof( _mqttPacket_t ) );
mqttPacket.pRemainingData = pMqttPacket->pRemainingData;
mqttPacket.remainingLength = pMqttPacket->remainingLength;
mqttPacket.type = pMqttPacket->type;
/* Call internal deserialize */
switch( pMqttPacket->type & 0xf0 )
{
case MQTT_PACKET_TYPE_CONNACK:
status = _IotMqtt_DeserializeConnack( &mqttPacket );
break;
case MQTT_PACKET_TYPE_PUBACK:
status = _IotMqtt_DeserializePuback( &mqttPacket );
break;
case MQTT_PACKET_TYPE_SUBACK:
status = _IotMqtt_DeserializeSuback( &mqttPacket );
break;
case MQTT_PACKET_TYPE_UNSUBACK:
status = _IotMqtt_DeserializeUnsuback( &mqttPacket );
break;
case MQTT_PACKET_TYPE_PINGRESP:
status = _IotMqtt_DeserializePingresp( &mqttPacket );
break;
/* Any other packet type is invalid. */
default:
IotLogError( "IotMqtt_DeserializeResponse() called with unknown packet type:(%lu).", pMqttPacket->type );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_PARAMETER );
}
if( status != IOT_MQTT_SUCCESS )
{
IOT_SET_AND_GOTO_CLEANUP( status );
}
else
{
/* Set packetIdentifier only if success is returned. */
pMqttPacket->packetIdentifier = mqttPacket.packetIdentifier;
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/