| /* |
| * FreeRTOS MQTT V2.1.1 |
| * 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. |
| * |
| * http://aws.amazon.com/freertos |
| * http://www.FreeRTOS.org |
| */ |
| |
| /** |
| * @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 "private/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 ); |
| |
| /*-----------------------------------------------------------*/ |
| |
| #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. */ |
| if( pConnectInfo->clientIdentifierLength > 0 ) |
| { |
| connectPacketSize += pConnectInfo->clientIdentifierLength + sizeof( uint16_t ); |
| } |
| else |
| { |
| EMPTY_ELSE_MARKER; |
| } |
| |
| /* 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; |
| } |
| |
| /*-----------------------------------------------------------*/ |
| |
| 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; |
| } |
| |
| /*-----------------------------------------------------------*/ |
| |
| IotMqttError_t _IotMqtt_SerializeConnect( const IotMqttConnectInfo_t * pConnectInfo, |
| uint8_t ** pConnectPacket, |
| size_t * pPacketSize ) |
| { |
| IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS ); |
| uint8_t connectFlags = 0; |
| 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; |
| |
| /* 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 ); |
| |
| 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 ); |
| uint8_t publishFlags = 0; |
| uint16_t packetIdentifier = 0; |
| 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; |
| |
| /* 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 ); |
| |
| 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 identifer, but QoS 0 PUBLISH packets do not. */ |
| if( pOutput->qos == IOT_MQTT_QOS_0 ) |
| { |
| pOutput->payloadLength = ( uint16_t ) ( pPublish->remainingLength - pOutput->topicNameLength - sizeof( uint16_t ) ); |
| pOutput->pPayload = pPacketIdentifierHigh; |
| } |
| else |
| { |
| pOutput->payloadLength = ( uint16_t ) ( 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 i = 0, subscribePacketSize = 0, remainingLength = 0; |
| uint16_t packetIdentifier = 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; |
| |
| /* 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 ); |
| |
| 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 identifer and at least 1 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 i = 0, unsubscribePacketSize = 0, remainingLength = 0; |
| uint16_t packetIdentifier = 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; |
| |
| /* 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 ); |
| |
| 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; |
| } |
| } |
| |
| /*-----------------------------------------------------------*/ |