As MQTT 5.0 allows to specify the disconnect reason in the Disconnect packet, use this new feature to improve error notification to the broker, according to the error guidelines in the MQTT 5.0 spec. For most cases, a generic arbitrary mapping between errno values and reason codes is used, however the parser can specify the disconnect reason code manually to better handle certain corner cases (like invalid topic alias used). Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
1209 lines
26 KiB
C
1209 lines
26 KiB
C
/*
|
|
* Copyright (c) 2018 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/** @file mqtt_decoder.c
|
|
*
|
|
* @brief Decoder functions needed for decoding packets received from the
|
|
* broker.
|
|
*/
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(net_mqtt_dec, CONFIG_MQTT_LOG_LEVEL);
|
|
|
|
#include "mqtt_internal.h"
|
|
#include "mqtt_os.h"
|
|
|
|
/**
|
|
* @brief Unpacks unsigned 8 bit value from the buffer from the offset
|
|
* requested.
|
|
*
|
|
* @param[inout] buf A pointer to the buf_ctx structure containing current
|
|
* buffer position.
|
|
* @param[out] val Memory where the value is to be unpacked.
|
|
*
|
|
* @retval 0 if the procedure is successful.
|
|
* @retval -EINVAL if the buffer would be exceeded during the read
|
|
*/
|
|
static int unpack_uint8(struct buf_ctx *buf, uint8_t *val)
|
|
{
|
|
uint8_t *cur = buf->cur;
|
|
uint8_t *end = buf->end;
|
|
|
|
NET_DBG(">> cur:%p, end:%p", (void *)cur, (void *)end);
|
|
|
|
if ((end - cur) < sizeof(uint8_t)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
*val = cur[0];
|
|
buf->cur = (cur + sizeof(uint8_t));
|
|
|
|
NET_DBG("<< val:%02x", *val);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Unpacks unsigned 16 bit value from the buffer from the offset
|
|
* requested.
|
|
*
|
|
* @param[inout] buf A pointer to the buf_ctx structure containing current
|
|
* buffer position.
|
|
* @param[out] val Memory where the value is to be unpacked.
|
|
*
|
|
* @retval 0 if the procedure is successful.
|
|
* @retval -EINVAL if the buffer would be exceeded during the read
|
|
*/
|
|
static int unpack_uint16(struct buf_ctx *buf, uint16_t *val)
|
|
{
|
|
uint8_t *cur = buf->cur;
|
|
uint8_t *end = buf->end;
|
|
|
|
NET_DBG(">> cur:%p, end:%p", (void *)cur, (void *)end);
|
|
|
|
if ((end - cur) < sizeof(uint16_t)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
*val = sys_get_be16(cur);
|
|
buf->cur = (cur + sizeof(uint16_t));
|
|
|
|
NET_DBG("<< val:%04x", *val);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Unpacks utf8 string from the buffer from the offset requested.
|
|
*
|
|
* @param[inout] buf A pointer to the buf_ctx structure containing current
|
|
* buffer position.
|
|
* @param[out] str Pointer to a string that will hold the string location
|
|
* in the buffer.
|
|
*
|
|
* @retval 0 if the procedure is successful.
|
|
* @retval -EINVAL if the buffer would be exceeded during the read
|
|
*/
|
|
static int unpack_utf8_str(struct buf_ctx *buf, struct mqtt_utf8 *str)
|
|
{
|
|
uint16_t utf8_strlen;
|
|
int err_code;
|
|
|
|
NET_DBG(">> cur:%p, end:%p", (void *)buf->cur, (void *)buf->end);
|
|
|
|
err_code = unpack_uint16(buf, &utf8_strlen);
|
|
if (err_code != 0) {
|
|
return err_code;
|
|
}
|
|
|
|
if ((buf->end - buf->cur) < utf8_strlen) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
str->size = utf8_strlen;
|
|
/* Zero length UTF8 strings permitted. */
|
|
if (utf8_strlen) {
|
|
/* Point to right location in buffer. */
|
|
str->utf8 = buf->cur;
|
|
buf->cur += utf8_strlen;
|
|
} else {
|
|
str->utf8 = NULL;
|
|
}
|
|
|
|
NET_DBG("<< str_size:%08x", (uint32_t)GET_UT8STR_BUFFER_SIZE(str));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Unpacks binary string from the buffer from the offset requested with
|
|
* the length provided.
|
|
*
|
|
* @param[in] length Binary string length.
|
|
* @param[inout] buf A pointer to the buf_ctx structure containing current
|
|
* buffer position.
|
|
* @param[out] str Pointer to a binary string that will hold the binary string
|
|
* location in the buffer.
|
|
*
|
|
* @retval 0 if the procedure is successful.
|
|
* @retval -EINVAL if the buffer would be exceeded during the read
|
|
*/
|
|
static int unpack_raw_data(uint32_t length, struct buf_ctx *buf,
|
|
struct mqtt_binstr *str)
|
|
{
|
|
NET_DBG(">> cur:%p, end:%p", (void *)buf->cur, (void *)buf->end);
|
|
|
|
if ((buf->end - buf->cur) < length) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
str->len = length;
|
|
|
|
/* Zero length binary strings are permitted. */
|
|
if (length > 0) {
|
|
str->data = buf->cur;
|
|
buf->cur += length;
|
|
} else {
|
|
str->data = NULL;
|
|
}
|
|
|
|
NET_DBG("<< bin len:%08x", GET_BINSTR_BUFFER_SIZE(str));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int unpack_variable_int(struct buf_ctx *buf, uint32_t *val)
|
|
{
|
|
uint8_t shift = 0U;
|
|
int bytes = 0;
|
|
|
|
*val = 0U;
|
|
do {
|
|
if (bytes >= MQTT_MAX_LENGTH_BYTES) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (buf->cur >= buf->end) {
|
|
return -EAGAIN;
|
|
}
|
|
|
|
*val += ((uint32_t)*(buf->cur) & MQTT_LENGTH_VALUE_MASK)
|
|
<< shift;
|
|
shift += MQTT_LENGTH_SHIFT;
|
|
bytes++;
|
|
} while ((*(buf->cur++) & MQTT_LENGTH_CONTINUATION_BIT) != 0U);
|
|
|
|
if (*val > MQTT_MAX_PAYLOAD_SIZE) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
NET_DBG("variable int:0x%08x", *val);
|
|
|
|
return bytes;
|
|
}
|
|
|
|
int fixed_header_decode(struct buf_ctx *buf, uint8_t *type_and_flags,
|
|
uint32_t *length)
|
|
{
|
|
int err_code;
|
|
|
|
err_code = unpack_uint8(buf, type_and_flags);
|
|
if (err_code != 0) {
|
|
return err_code;
|
|
}
|
|
|
|
err_code = unpack_variable_int(buf, length);
|
|
if (err_code < 0) {
|
|
return err_code;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_MQTT_VERSION_5_0)
|
|
/**
|
|
* @brief Unpacks unsigned 32 bit value from the buffer from the offset
|
|
* requested.
|
|
*
|
|
* @param[inout] buf A pointer to the buf_ctx structure containing current
|
|
* buffer position.
|
|
* @param[out] val Memory where the value is to be unpacked.
|
|
*
|
|
* @retval 0 if the procedure is successful.
|
|
* @retval -EINVAL if the buffer would be exceeded during the read
|
|
*/
|
|
static int unpack_uint32(struct buf_ctx *buf, uint32_t *val)
|
|
{
|
|
uint8_t *cur = buf->cur;
|
|
uint8_t *end = buf->end;
|
|
|
|
NET_DBG(">> cur:%p, end:%p", (void *)cur, (void *)end);
|
|
|
|
if ((end - cur) < sizeof(uint32_t)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
*val = sys_get_be32(cur);
|
|
buf->cur = (cur + sizeof(uint32_t));
|
|
|
|
NET_DBG("<< val:%08x", *val);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Unpacks binary string from the buffer from the offset requested.
|
|
* Binary string length is decoded from the first two bytes of the buffer.
|
|
*
|
|
* @param[inout] buf A pointer to the buf_ctx structure containing current
|
|
* buffer position.
|
|
* @param[out] bin Pointer to a binary string that will hold the binary string
|
|
* location in the buffer.
|
|
*
|
|
* @retval 0 if the procedure is successful.
|
|
* @retval -EINVAL if the buffer would be exceeded during the read
|
|
*/
|
|
static int unpack_binary_data(struct buf_ctx *buf, struct mqtt_binstr *bin)
|
|
{
|
|
uint16_t len;
|
|
int err;
|
|
|
|
NET_DBG(">> cur:%p, end:%p", (void *)buf->cur, (void *)buf->end);
|
|
|
|
err = unpack_uint16(buf, &len);
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
if ((buf->end - buf->cur) < len) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
bin->len = len;
|
|
/* Zero length binary strings are permitted. */
|
|
if (len > 0) {
|
|
bin->data = buf->cur;
|
|
buf->cur += len;
|
|
} else {
|
|
bin->data = NULL;
|
|
}
|
|
|
|
NET_DBG("<< bin len:%08x", GET_BINSTR_BUFFER_SIZE(bin));
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct property_decoder {
|
|
void *data;
|
|
bool *found;
|
|
uint8_t type;
|
|
};
|
|
|
|
int decode_uint32_property(struct property_decoder *prop,
|
|
uint32_t *remaining_len,
|
|
struct buf_ctx *buf)
|
|
{
|
|
uint32_t *value = prop->data;
|
|
|
|
if (*remaining_len < sizeof(uint32_t)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (unpack_uint32(buf, value) < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
*remaining_len -= sizeof(uint32_t);
|
|
*prop->found = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int decode_uint16_property(struct property_decoder *prop,
|
|
uint32_t *remaining_len,
|
|
struct buf_ctx *buf)
|
|
{
|
|
uint16_t *value = prop->data;
|
|
|
|
if (*remaining_len < sizeof(uint16_t)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (unpack_uint16(buf, value) < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
*remaining_len -= sizeof(uint16_t);
|
|
*prop->found = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int decode_uint8_property(struct property_decoder *prop,
|
|
uint32_t *remaining_len,
|
|
struct buf_ctx *buf)
|
|
{
|
|
uint8_t *value = prop->data;
|
|
|
|
if (*remaining_len < sizeof(uint8_t)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (unpack_uint8(buf, value) < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
*remaining_len -= sizeof(uint8_t);
|
|
*prop->found = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int decode_string_property(struct property_decoder *prop,
|
|
uint32_t *remaining_len,
|
|
struct buf_ctx *buf)
|
|
{
|
|
struct mqtt_utf8 *str = prop->data;
|
|
|
|
if (unpack_utf8_str(buf, str) < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (*remaining_len < sizeof(uint16_t) + str->size) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
*remaining_len -= sizeof(uint16_t) + str->size;
|
|
*prop->found = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int decode_binary_property(struct property_decoder *prop,
|
|
uint32_t *remaining_len,
|
|
struct buf_ctx *buf)
|
|
{
|
|
struct mqtt_binstr *bin = prop->data;
|
|
|
|
if (unpack_binary_data(buf, bin) < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (*remaining_len < sizeof(uint16_t) + bin->len) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
*remaining_len -= sizeof(uint16_t) + bin->len;
|
|
*prop->found = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int decode_user_property(struct property_decoder *prop,
|
|
uint32_t *remaining_len,
|
|
struct buf_ctx *buf)
|
|
{
|
|
struct mqtt_utf8_pair *user_prop = prop->data;
|
|
struct mqtt_utf8_pair *chosen = NULL;
|
|
struct mqtt_utf8_pair temp = { 0 };
|
|
size_t prop_len;
|
|
|
|
if (unpack_utf8_str(buf, &temp.name) < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (unpack_utf8_str(buf, &temp.value) < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
prop_len = (2 * sizeof(uint16_t)) + temp.name.size + temp.value.size;
|
|
if (*remaining_len < prop_len) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
*remaining_len -= prop_len;
|
|
*prop->found = true;
|
|
|
|
for (int i = 0; i < CONFIG_MQTT_USER_PROPERTIES_MAX; i++) {
|
|
if (user_prop[i].name.utf8 == NULL) {
|
|
chosen = &user_prop[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (chosen == NULL) {
|
|
NET_DBG("Cannot parse all user properties, ignore excess");
|
|
} else {
|
|
memcpy(chosen, &temp, sizeof(struct mqtt_utf8_pair));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int decode_sub_id_property(struct property_decoder *prop,
|
|
uint32_t *remaining_len,
|
|
struct buf_ctx *buf)
|
|
{
|
|
uint32_t *sub_id_array = prop->data;
|
|
uint32_t *chosen = NULL;
|
|
uint32_t value;
|
|
int bytes;
|
|
|
|
bytes = unpack_variable_int(buf, &value);
|
|
if (bytes < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (*remaining_len < bytes) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
*remaining_len -= bytes;
|
|
*prop->found = true;
|
|
|
|
for (int i = 0; i < CONFIG_MQTT_SUBSCRIPTION_ID_PROPERTIES_MAX; i++) {
|
|
if (sub_id_array[i] == 0) {
|
|
chosen = &sub_id_array[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (chosen == NULL) {
|
|
NET_DBG("Cannot parse all subscription id properties, ignore excess");
|
|
} else {
|
|
*chosen = value;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int properties_decode(struct property_decoder *prop, uint8_t cnt,
|
|
struct buf_ctx *buf)
|
|
{
|
|
uint32_t properties_len;
|
|
int bytes;
|
|
int err;
|
|
|
|
bytes = unpack_variable_int(buf, &properties_len);
|
|
if (bytes < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
bytes += (int)properties_len;
|
|
|
|
while (properties_len > 0) {
|
|
struct property_decoder *current_prop = NULL;
|
|
uint8_t type;
|
|
|
|
/* Decode property type */
|
|
err = unpack_uint8(buf, &type);
|
|
if (err < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
properties_len--;
|
|
|
|
/* Search if the property is supported in the provided property
|
|
* array.
|
|
*/
|
|
for (int i = 0; i < cnt; i++) {
|
|
if (type == prop[i].type) {
|
|
current_prop = &prop[i];
|
|
}
|
|
}
|
|
|
|
if (current_prop == NULL) {
|
|
NET_DBG("Unsupported property %u", type);
|
|
return -EBADMSG;
|
|
}
|
|
|
|
/* Decode property value. */
|
|
switch (type) {
|
|
case MQTT_PROP_SESSION_EXPIRY_INTERVAL:
|
|
case MQTT_PROP_MAXIMUM_PACKET_SIZE:
|
|
case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL:
|
|
err = decode_uint32_property(current_prop,
|
|
&properties_len, buf);
|
|
break;
|
|
case MQTT_PROP_RECEIVE_MAXIMUM:
|
|
case MQTT_PROP_TOPIC_ALIAS_MAXIMUM:
|
|
case MQTT_PROP_SERVER_KEEP_ALIVE:
|
|
case MQTT_PROP_TOPIC_ALIAS:
|
|
err = decode_uint16_property(current_prop,
|
|
&properties_len, buf);
|
|
break;
|
|
case MQTT_PROP_MAXIMUM_QOS:
|
|
case MQTT_PROP_RETAIN_AVAILABLE:
|
|
case MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE:
|
|
case MQTT_PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE:
|
|
case MQTT_PROP_SHARED_SUBSCRIPTION_AVAILABLE:
|
|
case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR:
|
|
err = decode_uint8_property(current_prop,
|
|
&properties_len, buf);
|
|
break;
|
|
case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER:
|
|
case MQTT_PROP_REASON_STRING:
|
|
case MQTT_PROP_RESPONSE_INFORMATION:
|
|
case MQTT_PROP_SERVER_REFERENCE:
|
|
case MQTT_PROP_AUTHENTICATION_METHOD:
|
|
case MQTT_PROP_RESPONSE_TOPIC:
|
|
case MQTT_PROP_CONTENT_TYPE:
|
|
err = decode_string_property(current_prop,
|
|
&properties_len, buf);
|
|
break;
|
|
case MQTT_PROP_USER_PROPERTY:
|
|
err = decode_user_property(current_prop,
|
|
&properties_len, buf);
|
|
break;
|
|
case MQTT_PROP_AUTHENTICATION_DATA:
|
|
case MQTT_PROP_CORRELATION_DATA:
|
|
err = decode_binary_property(current_prop,
|
|
&properties_len, buf);
|
|
break;
|
|
case MQTT_PROP_SUBSCRIPTION_IDENTIFIER:
|
|
err = decode_sub_id_property(current_prop,
|
|
&properties_len, buf);
|
|
break;
|
|
default:
|
|
err = -ENOTSUP;
|
|
}
|
|
|
|
if (err < 0) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
return bytes;
|
|
}
|
|
|
|
static int connack_properties_decode(struct buf_ctx *buf,
|
|
struct mqtt_connack_param *param)
|
|
{
|
|
struct property_decoder prop[] = {
|
|
{
|
|
¶m->prop.session_expiry_interval,
|
|
¶m->prop.rx.has_session_expiry_interval,
|
|
MQTT_PROP_SESSION_EXPIRY_INTERVAL
|
|
},
|
|
{
|
|
¶m->prop.receive_maximum,
|
|
¶m->prop.rx.has_receive_maximum,
|
|
MQTT_PROP_RECEIVE_MAXIMUM
|
|
},
|
|
{
|
|
¶m->prop.maximum_qos,
|
|
¶m->prop.rx.has_maximum_qos,
|
|
MQTT_PROP_MAXIMUM_QOS
|
|
},
|
|
{
|
|
¶m->prop.retain_available,
|
|
¶m->prop.rx.has_retain_available,
|
|
MQTT_PROP_RETAIN_AVAILABLE
|
|
},
|
|
{
|
|
¶m->prop.maximum_packet_size,
|
|
¶m->prop.rx.has_maximum_packet_size,
|
|
MQTT_PROP_MAXIMUM_PACKET_SIZE
|
|
},
|
|
{
|
|
¶m->prop.assigned_client_id,
|
|
¶m->prop.rx.has_assigned_client_id,
|
|
MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER
|
|
},
|
|
{
|
|
¶m->prop.topic_alias_maximum,
|
|
¶m->prop.rx.has_topic_alias_maximum,
|
|
MQTT_PROP_TOPIC_ALIAS_MAXIMUM
|
|
},
|
|
{
|
|
¶m->prop.reason_string,
|
|
¶m->prop.rx.has_reason_string,
|
|
MQTT_PROP_REASON_STRING
|
|
},
|
|
{
|
|
¶m->prop.user_prop,
|
|
¶m->prop.rx.has_user_prop,
|
|
MQTT_PROP_USER_PROPERTY
|
|
},
|
|
{
|
|
¶m->prop.wildcard_sub_available,
|
|
¶m->prop.rx.has_wildcard_sub_available,
|
|
MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE
|
|
},
|
|
{
|
|
¶m->prop.subscription_ids_available,
|
|
¶m->prop.rx.has_subscription_ids_available,
|
|
MQTT_PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE
|
|
},
|
|
{
|
|
¶m->prop.shared_sub_available,
|
|
¶m->prop.rx.has_shared_sub_available,
|
|
MQTT_PROP_SHARED_SUBSCRIPTION_AVAILABLE
|
|
},
|
|
{
|
|
¶m->prop.server_keep_alive,
|
|
¶m->prop.rx.has_server_keep_alive,
|
|
MQTT_PROP_SERVER_KEEP_ALIVE
|
|
},
|
|
{
|
|
¶m->prop.response_information,
|
|
¶m->prop.rx.has_response_information,
|
|
MQTT_PROP_RESPONSE_INFORMATION
|
|
},
|
|
{
|
|
¶m->prop.server_reference,
|
|
¶m->prop.rx.has_server_reference,
|
|
MQTT_PROP_SERVER_REFERENCE
|
|
},
|
|
{
|
|
¶m->prop.auth_method,
|
|
¶m->prop.rx.has_auth_method,
|
|
MQTT_PROP_AUTHENTICATION_METHOD
|
|
},
|
|
{
|
|
¶m->prop.auth_data,
|
|
¶m->prop.rx.has_auth_data,
|
|
MQTT_PROP_AUTHENTICATION_DATA
|
|
}
|
|
};
|
|
|
|
return properties_decode(prop, ARRAY_SIZE(prop), buf);
|
|
}
|
|
#else
|
|
static int connack_properties_decode(struct buf_ctx *buf,
|
|
struct mqtt_connack_param *param)
|
|
{
|
|
ARG_UNUSED(param);
|
|
ARG_UNUSED(buf);
|
|
|
|
return -ENOTSUP;
|
|
}
|
|
#endif /* CONFIG_MQTT_VERSION_5_0 */
|
|
|
|
int connect_ack_decode(const struct mqtt_client *client, struct buf_ctx *buf,
|
|
struct mqtt_connack_param *param)
|
|
{
|
|
int err_code;
|
|
uint8_t flags, ret_code;
|
|
|
|
err_code = unpack_uint8(buf, &flags);
|
|
if (err_code != 0) {
|
|
return err_code;
|
|
}
|
|
|
|
err_code = unpack_uint8(buf, &ret_code);
|
|
if (err_code != 0) {
|
|
return err_code;
|
|
}
|
|
|
|
param->return_code = ret_code;
|
|
|
|
if (client->protocol_version == MQTT_VERSION_3_1_0) {
|
|
goto out;
|
|
}
|
|
|
|
param->session_present_flag = flags & MQTT_CONNACK_FLAG_SESSION_PRESENT;
|
|
NET_DBG("[CID %p]: session_present_flag: %d", client,
|
|
param->session_present_flag);
|
|
|
|
if (mqtt_is_version_5_0(client)) {
|
|
err_code = connack_properties_decode(buf, param);
|
|
if (err_code < 0) {
|
|
return err_code;
|
|
}
|
|
}
|
|
|
|
out:
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_MQTT_VERSION_5_0)
|
|
static int publish_properties_decode(struct buf_ctx *buf,
|
|
struct mqtt_publish_param *param)
|
|
{
|
|
struct property_decoder prop[] = {
|
|
{
|
|
¶m->prop.payload_format_indicator,
|
|
¶m->prop.rx.has_payload_format_indicator,
|
|
MQTT_PROP_PAYLOAD_FORMAT_INDICATOR
|
|
},
|
|
{
|
|
¶m->prop.message_expiry_interval,
|
|
¶m->prop.rx.has_message_expiry_interval,
|
|
MQTT_PROP_MESSAGE_EXPIRY_INTERVAL
|
|
},
|
|
{
|
|
¶m->prop.topic_alias,
|
|
¶m->prop.rx.has_topic_alias,
|
|
MQTT_PROP_TOPIC_ALIAS
|
|
},
|
|
{
|
|
¶m->prop.response_topic,
|
|
¶m->prop.rx.has_response_topic,
|
|
MQTT_PROP_RESPONSE_TOPIC
|
|
},
|
|
{
|
|
¶m->prop.correlation_data,
|
|
¶m->prop.rx.has_correlation_data,
|
|
MQTT_PROP_CORRELATION_DATA
|
|
},
|
|
{
|
|
¶m->prop.user_prop,
|
|
¶m->prop.rx.has_user_prop,
|
|
MQTT_PROP_USER_PROPERTY
|
|
},
|
|
{
|
|
¶m->prop.subscription_identifier,
|
|
¶m->prop.rx.has_subscription_identifier,
|
|
MQTT_PROP_SUBSCRIPTION_IDENTIFIER
|
|
},
|
|
{
|
|
¶m->prop.content_type,
|
|
¶m->prop.rx.has_content_type,
|
|
MQTT_PROP_CONTENT_TYPE
|
|
}
|
|
};
|
|
|
|
return properties_decode(prop, ARRAY_SIZE(prop), buf);
|
|
}
|
|
|
|
static int publish_topic_alias_check(struct mqtt_client *client,
|
|
struct mqtt_publish_param *param)
|
|
{
|
|
uint16_t alias = param->prop.topic_alias;
|
|
struct mqtt_utf8 *topic_str = ¶m->message.topic.topic;
|
|
struct mqtt_topic_alias *topic_alias;
|
|
|
|
/* Topic alias must not exceed configured CONFIG_MQTT_TOPIC_ALIAS_MAX */
|
|
if (alias > CONFIG_MQTT_TOPIC_ALIAS_MAX) {
|
|
set_disconnect_reason(client, MQTT_DISCONNECT_TOPIC_ALIAS_INVALID);
|
|
return -EBADMSG;
|
|
}
|
|
|
|
if (topic_str->size == 0) {
|
|
/* In case there's no topic, topic alias must be set */
|
|
if (alias == 0) {
|
|
return -EBADMSG;
|
|
}
|
|
|
|
/* Topic alias number corresponds to the aliases array index. */
|
|
topic_alias = &client->internal.topic_aliases[alias - 1];
|
|
|
|
/* In case topic alias has not been configured yet, report an error. */
|
|
if (topic_alias->topic_size == 0) {
|
|
return -EBADMSG;
|
|
}
|
|
|
|
topic_str->utf8 = topic_alias->topic_buf;
|
|
topic_str->size = topic_alias->topic_size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* If topic is present and topic alias is set, configure the alias locally. */
|
|
if (alias > 0) {
|
|
topic_alias = &client->internal.topic_aliases[alias - 1];
|
|
|
|
if (topic_str->size > ARRAY_SIZE(topic_alias->topic_buf)) {
|
|
NET_ERR("Topic too long (%d bytes) to store, increase "
|
|
"CONFIG_MQTT_TOPIC_ALIAS_STRING_MAX",
|
|
topic_str->size);
|
|
set_disconnect_reason(client, MQTT_DISCONNECT_IMPL_SPECIFIC_ERROR);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memcpy(topic_alias->topic_buf, topic_str->utf8, topic_str->size);
|
|
topic_alias->topic_size = topic_str->size;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static int publish_properties_decode(struct buf_ctx *buf,
|
|
struct mqtt_publish_param *param)
|
|
{
|
|
ARG_UNUSED(param);
|
|
ARG_UNUSED(buf);
|
|
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
static int publish_topic_alias_check(struct mqtt_client *client,
|
|
struct mqtt_publish_param *param)
|
|
{
|
|
ARG_UNUSED(client);
|
|
ARG_UNUSED(param);
|
|
|
|
return -ENOTSUP;
|
|
}
|
|
#endif /* CONFIG_MQTT_VERSION_5_0 */
|
|
|
|
int publish_decode(struct mqtt_client *client, uint8_t flags,
|
|
uint32_t var_length, struct buf_ctx *buf,
|
|
struct mqtt_publish_param *param)
|
|
{
|
|
int err_code;
|
|
uint32_t var_header_length;
|
|
|
|
param->dup_flag = flags & MQTT_HEADER_DUP_MASK;
|
|
param->retain_flag = flags & MQTT_HEADER_RETAIN_MASK;
|
|
param->message.topic.qos = ((flags & MQTT_HEADER_QOS_MASK) >> 1);
|
|
|
|
err_code = unpack_utf8_str(buf, ¶m->message.topic.topic);
|
|
if (err_code != 0) {
|
|
return err_code;
|
|
}
|
|
|
|
var_header_length = param->message.topic.topic.size + sizeof(uint16_t);
|
|
|
|
if (param->message.topic.qos > MQTT_QOS_0_AT_MOST_ONCE) {
|
|
err_code = unpack_uint16(buf, ¶m->message_id);
|
|
if (err_code != 0) {
|
|
return err_code;
|
|
}
|
|
|
|
var_header_length += sizeof(uint16_t);
|
|
}
|
|
|
|
if (mqtt_is_version_5_0(client)) {
|
|
err_code = publish_properties_decode(buf, param);
|
|
if (err_code < 0) {
|
|
return err_code;
|
|
}
|
|
|
|
/* Add parsed properties length */
|
|
var_header_length += err_code;
|
|
|
|
err_code = publish_topic_alias_check(client, param);
|
|
if (err_code < 0) {
|
|
return err_code;
|
|
}
|
|
}
|
|
|
|
if (var_length < var_header_length) {
|
|
NET_ERR("Corrupted PUBLISH message, header length (%u) larger "
|
|
"than total length (%u)", var_header_length,
|
|
var_length);
|
|
return -EINVAL;
|
|
}
|
|
|
|
param->message.payload.data = NULL;
|
|
param->message.payload.len = var_length - var_header_length;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_MQTT_VERSION_5_0)
|
|
static int common_ack_properties_decode(struct buf_ctx *buf,
|
|
struct mqtt_common_ack_properties *prop)
|
|
{
|
|
struct property_decoder prop_dec[] = {
|
|
{
|
|
&prop->reason_string,
|
|
&prop->rx.has_reason_string,
|
|
MQTT_PROP_REASON_STRING
|
|
},
|
|
{
|
|
&prop->user_prop,
|
|
&prop->rx.has_user_prop,
|
|
MQTT_PROP_USER_PROPERTY
|
|
}
|
|
};
|
|
|
|
return properties_decode(prop_dec, ARRAY_SIZE(prop_dec), buf);
|
|
}
|
|
#else
|
|
static int common_ack_properties_decode(struct buf_ctx *buf,
|
|
struct mqtt_common_ack_properties *prop)
|
|
{
|
|
ARG_UNUSED(prop);
|
|
ARG_UNUSED(buf);
|
|
|
|
return -ENOTSUP;
|
|
}
|
|
#endif /* CONFIG_MQTT_VERSION_5_0 */
|
|
|
|
static int common_pub_ack_decode(struct buf_ctx *buf, uint16_t *message_id,
|
|
uint8_t *reason_code,
|
|
struct mqtt_common_ack_properties *prop)
|
|
{
|
|
size_t remaining_len;
|
|
int err;
|
|
|
|
err = unpack_uint16(buf, message_id);
|
|
if (err < 0) {
|
|
return err;
|
|
}
|
|
|
|
remaining_len = buf->end - buf->cur;
|
|
|
|
/* For MQTT < 5.0 properties are NULL. */
|
|
if (prop != NULL && reason_code != NULL) {
|
|
if (remaining_len > 0) {
|
|
err = unpack_uint8(buf, reason_code);
|
|
if (err < 0) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
if (remaining_len > 1) {
|
|
err = common_ack_properties_decode(buf, prop);
|
|
if (err < 0) {
|
|
return err;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int publish_ack_decode(const struct mqtt_client *client, struct buf_ctx *buf,
|
|
struct mqtt_puback_param *param)
|
|
{
|
|
struct mqtt_common_ack_properties *prop = NULL;
|
|
uint8_t *reason_code = NULL;
|
|
|
|
#if defined(CONFIG_MQTT_VERSION_5_0)
|
|
if (mqtt_is_version_5_0(client)) {
|
|
prop = ¶m->prop;
|
|
reason_code = ¶m->reason_code;
|
|
}
|
|
#endif
|
|
|
|
return common_pub_ack_decode(buf, ¶m->message_id, reason_code, prop);
|
|
}
|
|
|
|
int publish_receive_decode(const struct mqtt_client *client, struct buf_ctx *buf,
|
|
struct mqtt_pubrec_param *param)
|
|
{
|
|
struct mqtt_common_ack_properties *prop = NULL;
|
|
uint8_t *reason_code = NULL;
|
|
|
|
#if defined(CONFIG_MQTT_VERSION_5_0)
|
|
if (mqtt_is_version_5_0(client)) {
|
|
prop = ¶m->prop;
|
|
reason_code = ¶m->reason_code;
|
|
}
|
|
#endif
|
|
|
|
return common_pub_ack_decode(buf, ¶m->message_id, reason_code, prop);
|
|
}
|
|
|
|
int publish_release_decode(const struct mqtt_client *client, struct buf_ctx *buf,
|
|
struct mqtt_pubrel_param *param)
|
|
{
|
|
struct mqtt_common_ack_properties *prop = NULL;
|
|
uint8_t *reason_code = NULL;
|
|
|
|
#if defined(CONFIG_MQTT_VERSION_5_0)
|
|
if (mqtt_is_version_5_0(client)) {
|
|
prop = ¶m->prop;
|
|
reason_code = ¶m->reason_code;
|
|
}
|
|
#endif
|
|
|
|
return common_pub_ack_decode(buf, ¶m->message_id, reason_code, prop);
|
|
}
|
|
|
|
int publish_complete_decode(const struct mqtt_client *client, struct buf_ctx *buf,
|
|
struct mqtt_pubcomp_param *param)
|
|
{
|
|
struct mqtt_common_ack_properties *prop = NULL;
|
|
uint8_t *reason_code = NULL;
|
|
|
|
#if defined(CONFIG_MQTT_VERSION_5_0)
|
|
if (mqtt_is_version_5_0(client)) {
|
|
prop = ¶m->prop;
|
|
reason_code = ¶m->reason_code;
|
|
}
|
|
#endif
|
|
|
|
return common_pub_ack_decode(buf, ¶m->message_id, reason_code, prop);
|
|
}
|
|
|
|
#if defined(CONFIG_MQTT_VERSION_5_0)
|
|
static int suback_properties_decode(struct buf_ctx *buf,
|
|
struct mqtt_suback_param *param)
|
|
{
|
|
return common_ack_properties_decode(buf, ¶m->prop);
|
|
}
|
|
#else
|
|
static int suback_properties_decode(struct buf_ctx *buf,
|
|
struct mqtt_suback_param *param)
|
|
{
|
|
ARG_UNUSED(param);
|
|
ARG_UNUSED(buf);
|
|
|
|
return -ENOTSUP;
|
|
}
|
|
#endif /* CONFIG_MQTT_VERSION_5_0 */
|
|
|
|
int subscribe_ack_decode(const struct mqtt_client *client, struct buf_ctx *buf,
|
|
struct mqtt_suback_param *param)
|
|
{
|
|
int err_code;
|
|
|
|
err_code = unpack_uint16(buf, ¶m->message_id);
|
|
if (err_code != 0) {
|
|
return err_code;
|
|
}
|
|
|
|
if (mqtt_is_version_5_0(client)) {
|
|
err_code = suback_properties_decode(buf, param);
|
|
if (err_code < 0) {
|
|
return err_code;
|
|
}
|
|
}
|
|
|
|
return unpack_raw_data(buf->end - buf->cur, buf, ¶m->return_codes);
|
|
}
|
|
|
|
#if defined(CONFIG_MQTT_VERSION_5_0)
|
|
static int unsuback_5_0_decode(struct buf_ctx *buf,
|
|
struct mqtt_unsuback_param *param)
|
|
{
|
|
int err;
|
|
|
|
err = common_ack_properties_decode(buf, ¶m->prop);
|
|
if (err < 0) {
|
|
return err;
|
|
}
|
|
|
|
return unpack_raw_data(buf->end - buf->cur, buf, ¶m->reason_codes);
|
|
}
|
|
#else
|
|
static int unsuback_5_0_decode(struct buf_ctx *buf,
|
|
struct mqtt_unsuback_param *param)
|
|
{
|
|
ARG_UNUSED(param);
|
|
ARG_UNUSED(buf);
|
|
|
|
return -ENOTSUP;
|
|
}
|
|
#endif /* CONFIG_MQTT_VERSION_5_0 */
|
|
|
|
int unsubscribe_ack_decode(const struct mqtt_client *client, struct buf_ctx *buf,
|
|
struct mqtt_unsuback_param *param)
|
|
{
|
|
int err;
|
|
|
|
err = unpack_uint16(buf, ¶m->message_id);
|
|
if (err < 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (mqtt_is_version_5_0(client)) {
|
|
return unsuback_5_0_decode(buf, param);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_MQTT_VERSION_5_0)
|
|
static int disconnect_properties_decode(struct buf_ctx *buf,
|
|
struct mqtt_disconnect_param *param)
|
|
{
|
|
struct property_decoder prop[] = {
|
|
{
|
|
¶m->prop.session_expiry_interval,
|
|
¶m->prop.rx.has_session_expiry_interval,
|
|
MQTT_PROP_SESSION_EXPIRY_INTERVAL
|
|
},
|
|
{
|
|
¶m->prop.reason_string,
|
|
¶m->prop.rx.has_reason_string,
|
|
MQTT_PROP_REASON_STRING
|
|
},
|
|
{
|
|
¶m->prop.user_prop,
|
|
¶m->prop.rx.has_user_prop,
|
|
MQTT_PROP_USER_PROPERTY
|
|
},
|
|
{
|
|
¶m->prop.server_reference,
|
|
¶m->prop.rx.has_server_reference,
|
|
MQTT_PROP_SERVER_REFERENCE
|
|
}
|
|
};
|
|
|
|
return properties_decode(prop, ARRAY_SIZE(prop), buf);
|
|
}
|
|
|
|
int disconnect_decode(const struct mqtt_client *client, struct buf_ctx *buf,
|
|
struct mqtt_disconnect_param *param)
|
|
{
|
|
|
|
size_t remaining_len;
|
|
uint8_t reason_code;
|
|
int err;
|
|
|
|
if (!mqtt_is_version_5_0(client)) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
remaining_len = buf->end - buf->cur;
|
|
|
|
if (remaining_len > 0) {
|
|
err = unpack_uint8(buf, &reason_code);
|
|
if (err < 0) {
|
|
return err;
|
|
}
|
|
|
|
param->reason_code = reason_code;
|
|
}
|
|
|
|
if (remaining_len > 1) {
|
|
err = disconnect_properties_decode(buf, param);
|
|
if (err < 0) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int auth_properties_decode(struct buf_ctx *buf,
|
|
struct mqtt_auth_param *param)
|
|
{
|
|
struct property_decoder prop[] = {
|
|
{
|
|
¶m->prop.auth_method,
|
|
¶m->prop.rx.has_auth_method,
|
|
MQTT_PROP_AUTHENTICATION_METHOD
|
|
},
|
|
{
|
|
¶m->prop.auth_data,
|
|
¶m->prop.rx.has_auth_data,
|
|
MQTT_PROP_AUTHENTICATION_DATA
|
|
},
|
|
{
|
|
¶m->prop.reason_string,
|
|
¶m->prop.rx.has_reason_string,
|
|
MQTT_PROP_REASON_STRING
|
|
},
|
|
{
|
|
¶m->prop.user_prop,
|
|
¶m->prop.rx.has_user_prop,
|
|
MQTT_PROP_USER_PROPERTY
|
|
}
|
|
};
|
|
|
|
return properties_decode(prop, ARRAY_SIZE(prop), buf);
|
|
}
|
|
|
|
int auth_decode(const struct mqtt_client *client, struct buf_ctx *buf,
|
|
struct mqtt_auth_param *param)
|
|
{
|
|
size_t remaining_len;
|
|
uint8_t reason_code;
|
|
int err;
|
|
|
|
if (!mqtt_is_version_5_0(client)) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
remaining_len = buf->end - buf->cur;
|
|
|
|
if (remaining_len > 0) {
|
|
err = unpack_uint8(buf, &reason_code);
|
|
if (err < 0) {
|
|
return err;
|
|
}
|
|
|
|
param->reason_code = reason_code;
|
|
}
|
|
|
|
if (remaining_len > 1) {
|
|
err = auth_properties_decode(buf, param);
|
|
if (err < 0) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_MQTT_VERSION_5_0 */
|