| /* |
| * 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) |
| { |
| NET_DBG(">> cur:%p, end:%p", buf->cur, buf->end); |
| |
| if ((buf->end - buf->cur) < sizeof(uint8_t)) { |
| return -EINVAL; |
| } |
| |
| *val = *(buf->cur++); |
| |
| 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) |
| { |
| NET_DBG(">> cur:%p, end:%p", buf->cur, buf->end); |
| |
| if ((buf->end - buf->cur) < sizeof(uint16_t)) { |
| return -EINVAL; |
| } |
| |
| *val = *(buf->cur++) << 8; /* MSB */ |
| *val |= *(buf->cur++); /* LSB */ |
| |
| 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", buf->cur, 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. |
| * |
| * @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_data(uint32_t length, struct buf_ctx *buf, |
| struct mqtt_binstr *str) |
| { |
| NET_DBG(">> cur:%p, end:%p", buf->cur, 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; |
| } |
| |
| /**@brief Decode MQTT Packet Length in the MQTT fixed header. |
| * |
| * @param[inout] buf A pointer to the buf_ctx structure containing current |
| * buffer position. |
| * @param[out] length Length of variable header and payload in the |
| * MQTT message. |
| * |
| * @retval 0 if the procedure is successful. |
| * @retval -EINVAL if the length decoding would use more that 4 bytes. |
| * @retval -EAGAIN if the buffer would be exceeded during the read. |
| */ |
| static int packet_length_decode(struct buf_ctx *buf, uint32_t *length) |
| { |
| uint8_t shift = 0U; |
| uint8_t bytes = 0U; |
| |
| *length = 0U; |
| do { |
| if (bytes >= MQTT_MAX_LENGTH_BYTES) { |
| return -EINVAL; |
| } |
| |
| if (buf->cur >= buf->end) { |
| return -EAGAIN; |
| } |
| |
| *length += ((uint32_t)*(buf->cur) & MQTT_LENGTH_VALUE_MASK) |
| << shift; |
| shift += MQTT_LENGTH_SHIFT; |
| bytes++; |
| } while ((*(buf->cur++) & MQTT_LENGTH_CONTINUATION_BIT) != 0U); |
| |
| if (*length > MQTT_MAX_PAYLOAD_SIZE) { |
| return -EINVAL; |
| } |
| |
| NET_DBG("length:0x%08x", *length); |
| |
| return 0; |
| } |
| |
| 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; |
| } |
| |
| return packet_length_decode(buf, length); |
| } |
| |
| 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; |
| } |
| |
| if (client->protocol_version == MQTT_VERSION_3_1_1) { |
| param->session_present_flag = |
| flags & MQTT_CONNACK_FLAG_SESSION_PRESENT; |
| |
| NET_DBG("[CID %p]: session_present_flag: %d", client, |
| param->session_present_flag); |
| } |
| |
| param->return_code = (enum mqtt_conn_return_code)ret_code; |
| |
| return 0; |
| } |
| |
| int publish_decode(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 (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; |
| } |
| |
| int publish_ack_decode(struct buf_ctx *buf, struct mqtt_puback_param *param) |
| { |
| return unpack_uint16(buf, ¶m->message_id); |
| } |
| |
| int publish_receive_decode(struct buf_ctx *buf, struct mqtt_pubrec_param *param) |
| { |
| return unpack_uint16(buf, ¶m->message_id); |
| } |
| |
| int publish_release_decode(struct buf_ctx *buf, struct mqtt_pubrel_param *param) |
| { |
| return unpack_uint16(buf, ¶m->message_id); |
| } |
| |
| int publish_complete_decode(struct buf_ctx *buf, |
| struct mqtt_pubcomp_param *param) |
| { |
| return unpack_uint16(buf, ¶m->message_id); |
| } |
| |
| int subscribe_ack_decode(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; |
| } |
| |
| return unpack_data(buf->end - buf->cur, buf, ¶m->return_codes); |
| } |
| |
| int unsubscribe_ack_decode(struct buf_ctx *buf, |
| struct mqtt_unsuback_param *param) |
| { |
| return unpack_uint16(buf, ¶m->message_id); |
| } |