447 lines
21 KiB
C
447 lines
21 KiB
C
/**
|
|
* Copyright (c) 2015 - 2019, Nordic Semiconductor ASA
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form, except as embedded into a Nordic
|
|
* Semiconductor ASA integrated circuit in a product or a software update for
|
|
* such product, must reproduce the above copyright notice, this list of
|
|
* conditions and the following disclaimer in the documentation and/or other
|
|
* materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
|
* contributors may be used to endorse or promote products derived from this
|
|
* software without specific prior written permission.
|
|
*
|
|
* 4. This software, with or without modification, must only be used with a
|
|
* Nordic Semiconductor ASA integrated circuit.
|
|
*
|
|
* 5. Any software provided in binary form under this license must not be reverse
|
|
* engineered, decompiled, modified and/or disassembled.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
/** @file mqtt_internal.h
|
|
*
|
|
* @brief Function and data structures internal to MQTT module.
|
|
*/
|
|
|
|
#ifndef MQTT_INTERNAL_H_
|
|
#define MQTT_INTERNAL_H_
|
|
|
|
#include "nordic_common.h"
|
|
#include "sdk_common.h"
|
|
#include "sdk_config.h"
|
|
#include "mqtt.h"
|
|
#include "iot_errors.h"
|
|
#include "nrf_tls.h"
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include "nrf_error.h"
|
|
#include "nrf_tls.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#ifndef MQTT_MAX_CLIENTS
|
|
#define MQTT_MAX_CLIENTS 1 /**< Maximum number of clients that can be managed by the module. */
|
|
#endif //MQTT_MAX_CLIENTS
|
|
|
|
#ifndef MQTT_KEEPALIVE
|
|
#define MQTT_KEEPALIVE 60 /**< Keep alive time for MQTT (in seconds). Sending of Ping Requests to be keep the connection alive are governed by this value. */
|
|
#endif //MQTT_KEEPALIVE
|
|
|
|
#ifndef MQTT_MAX_PACKET_LENGTH
|
|
#define MQTT_MAX_PACKET_LENGTH 128 /**< Maximum MQTT packet size that can be sent (including the fixed and variable header). */
|
|
#endif // MQTT_MAX_PACKET_LENGTH
|
|
|
|
#define MQTT_FIXED_HEADER_SIZE 2 /**< Fixed header minimum size. Remaining length size is 1 in this case. */
|
|
#define MQTT_FIXED_HEADER_EXTENDED_SIZE 5 /**< Maximum size of the fixed header. Remaining length size is 4 in this case. */
|
|
|
|
/**@brief MQTT Control Packet Types. */
|
|
#define MQTT_PKT_TYPE_CONNECT 0x10
|
|
#define MQTT_PKT_TYPE_CONNACK 0x20
|
|
#define MQTT_PKT_TYPE_PUBLISH 0x30
|
|
#define MQTT_PKT_TYPE_PUBACK 0x40
|
|
#define MQTT_PKT_TYPE_PUBREC 0x50
|
|
#define MQTT_PKT_TYPE_PUBREL 0x60
|
|
#define MQTT_PKT_TYPE_PUBCOMP 0x70
|
|
#define MQTT_PKT_TYPE_SUBSCRIBE 0x82 // QoS 1 for subscribe
|
|
#define MQTT_PKT_TYPE_SUBACK 0x90
|
|
#define MQTT_PKT_TYPE_UNSUBSCRIBE 0xA2
|
|
#define MQTT_PKT_TYPE_UNSUBACK 0xB0
|
|
#define MQTT_PKT_TYPE_PINGREQ 0xC0
|
|
#define MQTT_PKT_TYPE_PINGRSP 0xD0
|
|
#define MQTT_PKT_TYPE_DISCONNECT 0xE0
|
|
|
|
|
|
/**@brief Masks for MQTT header flags. */
|
|
#define MQTT_HEADER_DUP_MASK 0x08
|
|
#define MQTT_HEADER_QOS_MASK 0x06
|
|
#define MQTT_HEADER_RETAIN_MASK 0x01
|
|
#define MQTT_HEADER_CONNACK_MASK 0x0F
|
|
|
|
/**@brief Masks for MQTT header flags. */
|
|
#define MQTT_CONNECT_FLAG_CLEAN_SESSION 0x02
|
|
#define MQTT_CONNECT_FLAG_WILL_TOPIC 0x04
|
|
#define MQTT_CONNECT_FLAG_WILL_RETAIN 0x20
|
|
#define MQTT_CONNECT_FLAG_PASSWORD 0x40
|
|
#define MQTT_CONNECT_FLAG_USERNAME 0x80
|
|
|
|
/**@brief Size of mandatory header of MQTT Packet. */
|
|
#define MQTT_PKT_HEADER_SIZE 2
|
|
|
|
/**@brief */
|
|
#define MQTT_MAX_PAYLOAD_SIZE 0x0FFFFFFF
|
|
|
|
/**@brief Maximum size of variable and payload in the packet. */
|
|
#define MQTT_MAX_VARIABLE_HEADER_N_PAYLOAD (MQTT_MAX_PACKET_LENGTH - MQTT_FIXED_HEADER_EXTENDED_SIZE)
|
|
|
|
/**@brief Defines size of uint8 in bytes. */
|
|
#define SIZE_OF_UINT8 1
|
|
|
|
/**@brief Defines size of uint8 in bytes. */
|
|
#define SIZE_OF_UINT16 2
|
|
|
|
/**@brief Computes total size needed to pack a UTF8 string. */
|
|
#define GET_UT8STR_BUFFER_SIZE(STR) (SIZE_OF_UINT16 + (STR)->utf_strlen)
|
|
|
|
/**@brief Computes total size needed to pack a binary stream. */
|
|
#define GET_BINSTR_BUFFER_SIZE(STR) ((STR)->bin_strlen)
|
|
|
|
/**@brief Unsubscribe packet size. */
|
|
#define MQTT_UNSUBSCRIBE_PKT_SIZE 4
|
|
|
|
/**@brief Sets MQTT Client's state with one indicated in 'STATE'. */
|
|
#define MQTT_SET_STATE(CLIENT, STATE) ((CLIENT)->state |= (STATE))
|
|
|
|
/**@brief Sets MQTT Client's state exclusive to 'STATE'. */
|
|
#define MQTT_SET_STATE_EXCLUSIVE(CLIENT, STATE) ((CLIENT)->state = (STATE))
|
|
|
|
/**@brief Verifies if MQTT Client's state is set with one indicated in 'STATE'. */
|
|
#define MQTT_VERIFY_STATE(CLIENT, STATE) ((CLIENT)->state & (STATE))
|
|
|
|
/**@brief Reset 'STATE' in MQTT Client's state. */
|
|
#define MQTT_RESET_STATE(CLIENT, STATE) ((CLIENT)->state &= ~(STATE))
|
|
|
|
/**@brief Initialize MQTT Client's state. */
|
|
#define MQTT_STATE_INIT(CLIENT) (CLIENT)->state = MQTT_STATE_IDLE
|
|
|
|
/**@brief Computes the first byte of MQTT message header based on message type, duplication flag,
|
|
* QoS and the retain flag.
|
|
*/
|
|
#define MQTT_MESSAGES_OPTIONS(TYPE, DUP, QOS, RETAIN) \
|
|
(((TYPE) & 0xF0) | \
|
|
(((DUP) << 3) & 0x08) | \
|
|
(((QOS) << 1) & 0x06) | \
|
|
((RETAIN) & 0x01))
|
|
|
|
|
|
#define MQTT_EVT_FLAG_NONE 0x00000000
|
|
#define MQTT_EVT_FLAG_INSTANCE_RESET 0x00000001
|
|
|
|
|
|
/**
|
|
* @defgroup iot_mqtt_mutex_lock_unlock Module's Mutex Lock/Unlock Macros.
|
|
*
|
|
* @details Macros used to lock and unlock modules. Currently, SDK does not use mutexes but
|
|
* framework is provided in case the need to use an alternative architecture arises.
|
|
* @{
|
|
*/
|
|
#define MQTT_MUTEX_LOCK() SDK_MUTEX_LOCK(m_mqtt_mutex) /**< Lock module using mutex */
|
|
#define MQTT_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_mqtt_mutex) /**< Unlock module using mutex */
|
|
|
|
/** @} */
|
|
|
|
/**@brief Check if the input pointer is NULL, if so it returns NRF_ERROR_NULL.
|
|
*/
|
|
#define NULL_PARAM_CHECK(PARAM) \
|
|
if ((PARAM) == NULL) \
|
|
{ \
|
|
return (NRF_ERROR_NULL | IOT_MQTT_ERR_BASE); \
|
|
}
|
|
|
|
#define NULL_PARAM_CHECK_VOID(PARAM) \
|
|
if ((PARAM) == NULL) \
|
|
{ \
|
|
return; \
|
|
}
|
|
|
|
/**@brief MQTT States. */
|
|
typedef enum
|
|
{
|
|
MQTT_STATE_IDLE = 0x00000000, /**< Idle state, implying the client entry in the table is unused/free. */
|
|
MQTT_STATE_TCP_CONNECTING = 0x00000001, /**< TCP Connection has been requested, awaiting result of the request. */
|
|
MQTT_STATE_TCP_CONNECTED = 0x00000002, /**< TCP Connection successfully established. */
|
|
MQTT_STATE_CONNECTED = 0x00000004, /**< MQTT Connection successful. */
|
|
MQTT_STATE_PENDING_WRITE = 0x00000008, /**< State that indicates write callback is awaited for an issued request. */
|
|
MQTT_STATE_DISCONNECTING = 0x00000010 /**< TCP Disconnect has been requested, awaiting result of the request. */
|
|
} mqtt_state_t;
|
|
|
|
|
|
/**@brief Packs unsigned 8 bit value to the buffer at the offset requested.
|
|
*
|
|
* @param[in] val Value to be packed.
|
|
* @param[in] buffer_len Total size of the buffer on which value is to be packed.
|
|
* This shall not be zero.
|
|
* @param[out] p_buffer Buffer where the value is to be packed.
|
|
* @param[inout] p_offset Offset on the buffer where the value is to be packed. If the procedure
|
|
* is successful, the offset is incremented to point to the next write/pack
|
|
* location on the buffer.
|
|
*
|
|
* @retval NRF_SUCCESS if procedure is successful.
|
|
* @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length.
|
|
*/
|
|
uint32_t pack_uint8(uint8_t val,
|
|
uint32_t buffer_len,
|
|
uint8_t * const p_buffer,
|
|
uint32_t * const p_offset);
|
|
|
|
|
|
/**@brief Packs unsigned 16 bit value to the buffer at the offset requested.
|
|
*
|
|
* @param[in] val Value to be packed.
|
|
* @param[in] buffer_len Total size of the buffer on which value is to be packed.
|
|
* This shall not be zero.
|
|
* @param[out] p_buffer Buffer where the value is to be packed.
|
|
* @param[inout] p_offset Offset on the buffer where the value is to be packed. If the procedure
|
|
* is successful, the offset is incremented to point to the next write/pack
|
|
* location on the buffer.
|
|
*
|
|
* @retval NRF_SUCCESS if the procedure is successful.
|
|
* @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length minus
|
|
* the size of unsigned 16 bit integer.
|
|
*/
|
|
uint32_t pack_uint16(uint16_t val,
|
|
uint32_t buffer_len,
|
|
uint8_t * const p_buffer,
|
|
uint32_t * const p_offset);
|
|
|
|
|
|
/**@brief Packs utf8 string to the buffer at the offset requested.
|
|
*
|
|
* @param[in] p_str UTF-8 string and its length to be packed.
|
|
* @param[in] buffer_len Total size of the buffer on which string is to be packed.
|
|
* This shall not be zero.
|
|
* @param[out] p_buffer Buffer where the string is to be packed.
|
|
* @param[inout] p_offset Offset on the buffer where the string is to be packed. If the procedure
|
|
* is successful, the offset is incremented to point to the next write/pack
|
|
* location on the buffer.
|
|
*
|
|
* @retval NRF_SUCCESS if the procedure is successful.
|
|
* @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length minus
|
|
* the size of unsigned 16 bit integer.
|
|
* @retval NRF_ERROR_NO_MEM if there is no room on the buffer to pack the string.
|
|
*/
|
|
uint32_t pack_utf8_str(mqtt_utf8_t const * const p_str,
|
|
uint32_t buffer_len,
|
|
uint8_t * const p_buffer,
|
|
uint32_t * const p_offset);
|
|
|
|
|
|
/**@brief Packs binary string to the buffer at the offset requested.
|
|
*
|
|
* @param[in] p_str Binary string and its length to be packed.
|
|
* @param[in] buffer_len Total size of the buffer on which string is to be packed.
|
|
* @param[in] p_buffer Buffer where the string is to be packed.
|
|
* @param[inout] p_offset Offset on the buffer where the string is to be packed. If the procedure
|
|
* is successful, the offset is incremented to point to the next write/pack
|
|
* location on the buffer.
|
|
*
|
|
* @retval NRF_SUCCESS if the procedure is successful.
|
|
* @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length.
|
|
* @retval NRF_ERROR_NO_MEM if there is no room on the buffer to pack the string.
|
|
*/
|
|
uint32_t pack_bin_str(mqtt_binstr_t const * const p_str,
|
|
uint32_t buffer_len,
|
|
uint8_t * const p_buffer,
|
|
uint32_t * const p_offset);
|
|
|
|
|
|
/**@brief Unpacks unsigned 8 bit value from the buffer from the offset requested.
|
|
*
|
|
* @param[out] p_val Memory where the value is to be unpacked.
|
|
* @param[in] buffer_len Total size of the buffer. This shall not be zero.
|
|
* @param[in] p_buffer Buffer from which the value is to be unpacked.
|
|
* @param[inout] p_offset Offset on the buffer from where the value is to be unpacked. If the
|
|
* procedure is successful, the offset is incremented to point to the next
|
|
* read/unpack location on the buffer.
|
|
*
|
|
* @retval NRF_SUCCESS if the procedure is successful.
|
|
* @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length.
|
|
*/
|
|
uint32_t unpack_uint8(uint8_t * p_val,
|
|
uint32_t buffer_len,
|
|
uint8_t * const p_buffer,
|
|
uint32_t * const p_offset);
|
|
|
|
/**@brief Unpacks unsigned 16 bit value from the buffer from the offset requested.
|
|
*
|
|
* @param[out] p_val Memory where the value is to be unpacked.
|
|
* @param[in] buffer_len Total size of the buffer. This shall not be zero.
|
|
* @param[in] p_buffer Buffer from which the value is to be unpacked.
|
|
* @param[inout] p_offset Offset on the buffer from where the value is to be unpacked. If the
|
|
* procedure is successful, the offset is incremented to point to the next
|
|
* read/unpack location on the buffer.
|
|
*
|
|
* @retval NRF_SUCCESS if the procedure is successful.
|
|
* @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length.
|
|
*/
|
|
uint32_t unpack_uint16(uint16_t * p_val,
|
|
uint32_t buffer_len,
|
|
uint8_t * const p_buffer,
|
|
uint32_t * const p_offset);
|
|
|
|
|
|
/**@brief Unpacks unsigned 16 bit value from the buffer from the offset requested.
|
|
*
|
|
* @param[out] p_str Memory where the utf8 string and its value are to be unpacked.
|
|
* No copy of data is performed for the string.
|
|
* @param[in] buffer_len Total size of the buffer. This shall not be zero.
|
|
* @param[in] p_buffer Buffer from which the string is to be unpacked.
|
|
* @param[inout] p_offset Offset on the buffer from where the value is to be unpacked. If the
|
|
* procedure is successful, the offset is incremented to point to the next
|
|
* read/unpack location on the buffer.
|
|
*
|
|
* @retval NRF_SUCCESS if the procedure is successful.
|
|
* @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length.
|
|
*/
|
|
uint32_t unpack_utf8_str(mqtt_utf8_t * const p_str,
|
|
uint32_t buffer_len,
|
|
uint8_t * const p_buffer,
|
|
uint32_t * const p_offset);
|
|
|
|
|
|
/**@brief Unpacks binary string from the buffer from the offset requested.
|
|
*
|
|
* @param[out] p_str Memory where the binary string and its length are to be unpacked.
|
|
* No copy of data is performed for the string.
|
|
* @param[in] buffer_len Total size of the buffer. This shall not be zero.
|
|
* @param[in] p_buffer Buffer where the value is to be unpacked.
|
|
* @param[inout] p_offset Offset on the buffer from where the value is to be unpacked. If the
|
|
* procedure is successful, the offset is incremented to point to the next
|
|
* read/unpack location on the buffer.
|
|
*
|
|
* @retval NRF_SUCCESS if the procedure is successful.
|
|
* @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length.
|
|
*/
|
|
uint32_t unpack_bin_str(mqtt_binstr_t * const p_str,
|
|
uint32_t buffer_len,
|
|
uint8_t * const p_buffer,
|
|
uint32_t * const p_offset);
|
|
|
|
|
|
/**@brief Unpacks utf8 string from the buffer from the offset requested.
|
|
*
|
|
* @param[out] p_str Memory where the utf8 string and its length are to be unpacked.
|
|
* @param[in] buffer_len Total size of the buffer. This shall not be zero.
|
|
* @param[in] p_buffer Buffer where the value is to be unpacked.
|
|
* @param[inout] p_offset Offset on the buffer from where the value is to be unpacked. If the
|
|
* procedure is successful, the offset is incremented to point to the next
|
|
* read/unpack location on the buffer.
|
|
*
|
|
* @retval NRF_SUCCESS if the procedure is successful.
|
|
* @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length.
|
|
*/
|
|
uint32_t zero_len_str_encode(uint32_t buffer_len,
|
|
uint8_t * const p_buffer,
|
|
uint32_t * const p_offset);
|
|
|
|
|
|
/**@brief Computes and encodes length for the MQTT fixed header.
|
|
*
|
|
* @note The remaining length is not packed as a fixed unsigned 32 bit integer. Instead it is packed
|
|
* on algorithm below:
|
|
*
|
|
* @code
|
|
* do
|
|
* encodedByte = X MOD 128
|
|
* X = X DIV 128
|
|
* // if there are more data to encode, set the top bit of this byte
|
|
* if ( X > 0 )
|
|
* encodedByte = encodedByte OR 128
|
|
* endif
|
|
* 'output' encodedByte
|
|
* while ( X > 0 )
|
|
* @endcode
|
|
*
|
|
* @param[in] remaining_length Length of variable header and payload in the MQTT message.
|
|
* @param[out] p_buffer Buffer where the length is to be packed.
|
|
* @param[inout] p_offset Offset on the buffer where the length is to be packed.
|
|
*/
|
|
void packet_length_encode(uint32_t remaining_length, uint8_t * p_buffer, uint32_t * p_offset);
|
|
|
|
|
|
/**@brief Decode MQTT Packet Length in the MQTT fixed header.
|
|
*
|
|
* @param[in] p_buffer Buffer where the length is to be decoded.
|
|
* @param[in] buffer_len Length of p_buffer
|
|
* @param[out] p_remaining_length Length of variable header and payload in the MQTT message.
|
|
* @param[inout] p_offset Offset on the buffer from where the length is to be unpacked.
|
|
*
|
|
* @retval NRF_SUCCESS if the procedure is successful.
|
|
* @retval NRF_ERROR_DATA_SIZE if the offset is greater than or equal to the buffer length.
|
|
*/
|
|
uint32_t packet_length_decode(uint8_t * p_buffer,
|
|
uint32_t buffer_len,
|
|
uint32_t * p_remaining_length,
|
|
uint32_t * p_offset);
|
|
|
|
|
|
/**@brief Encodes fixed header for the MQTT message and provides pointer to start of the header.
|
|
*
|
|
* @param[in] message_type Message type containing packet type and the flags.
|
|
* Use @ref MQTT_MESSAGES_OPTIONS to construct the message_type.
|
|
* @param[in] length Buffer where the message payload along with variable header.
|
|
* @param[inout] pp_packet Pointer to the MQTT message variable header and payload.
|
|
* The 5 bytes before the start of the message are assumed by the
|
|
* routine to be available to pack the fixed header. However, since
|
|
* the fixed header length is variable length, the pointer to the
|
|
* start of the MQTT message along with encoded fixed header is
|
|
* supplied as output parameter if the procedure was successful.
|
|
*
|
|
* @retval 0xFFFFFFFF if the procedure failed, else length of total MQTT message along with the
|
|
* fixed header.
|
|
*/
|
|
uint32_t mqtt_encode_fixed_header(uint8_t message_type, uint32_t length, uint8_t ** pp_packet);
|
|
|
|
|
|
/**@brief Constructs/encodes connect packet.
|
|
*
|
|
* @param[in] p_client Identifies the client for which the procedure is requested.
|
|
* All information required for creating the packet like client id,
|
|
* clean session flag, retain session flag etc are assumed to be
|
|
* populated for the client instance when this procedure is requested.
|
|
* @param[out] pp_packet Pointer to the MQTT connect message.
|
|
* @param[out] p_packet_length Length of the connect request.
|
|
*
|
|
* @retval 0xFFFFFFFF if the procedure failed, else length of total MQTT message along with the
|
|
* fixed header.
|
|
*/
|
|
void connect_request_encode(const mqtt_client_t * p_client,
|
|
uint8_t ** pp_packet,
|
|
uint32_t * p_packet_length);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif // MQTT_INTERNAL_H_
|