uint16_t keep_alive_seconds; /* network endian */
};
+#define MQTT_SESSION_RESUME 0x00
+#define MQTT_SESSION_NEW 0x01
struct mqtt_connect_pkt {
uint16_t keep_alive_seconds;
- uint8_t clean_session_flag;
+ uint8_t clean_session_flag; /* Either MQTT_SESSION_RESUME or MQTT_SESSION_NEW */
char* client_id;
char* will_topic;
uint16_t will_msg_size; /* stored in host endianness */
*
* Caller must provide a buffer "buf" big enougth to receive the whole packet and indicate it's size
* in "buf_size".
- * The return value is the used buffer size (packet size) on success.
- * The return value is negative on error :
+ * Returns the used buffer size (packet size) on success.
+ * Return value is negative on error :
+ * -EINVAL if either buf or pkt is NULL,
* -EINVAL if client_id string provided in mqtt_connect_pkt struct is NULL (it's length may be 0,
* but pointer cannot bu NULL).
* -E2BIG if buffer is not big enough for packet.
*/
-int mqtt_pack_connect_packet(struct mqtt_connect_pkt* pkt, uint8_t* buf, uint32_t buf_size);
+int mqtt_pack_connect_packet(const struct mqtt_connect_pkt* pkt, uint8_t* buf, uint32_t buf_size);
/* Connack return codes returned in a CONNACK packet */
uint8_t rem_len; /* Remaining length : must be 0x02 */
uint8_t flags; /* Connect acknowledge flags */
uint8_t ret_code; /* Connect return code */
-};
+} __attribute__ ((__packed__));
/* Connect acknowledge flags bit 0 set to 1 only if connect with Clean session flag was set to 0 and
* the server accepts the connection and has a stored session for this client id */
* This function may get called to check a supposed connect acknowledge packet.
* The function checks for conformance to the MQTT protocol and returns 0 if the packet is valid and
* the server accepted the connection.
- * The function returns -EPROTO in case of invalid connack packet or -EREMOTEIO in case of connection
- * refused by the server (ret_code is then valid in the packet).
+ * The function returns -EINVAL if pkt is NULL, -EPROTO in case of invalid connack packet or
+ * -EREMOTEIO in case of connection refused by the server (ret_code is then valid in the packet).
*/
-int mqtt_check_connack_response_pkt(struct mqtt_connack_response_pkt* pkt);
+int mqtt_check_connack_response_pkt(const struct mqtt_connack_response_pkt* pkt);
/***************************************************************************** */
/* publish and puback packets */
#define MQTT_PUBLISH_DUP (0x01 << 3)
#define MQTT_PUBLISH_QoS(x) (((x) & 0x03) << 1)
#define MQTT_PUBLISH_RETAIN (0x01 << 0)
-#define MQTT_PUBREL_FLAG (0x01 << 1)
/* MQTT publish paquet
* A publish control packet is sent from a Client to a Server or from Server to a Client to transport
struct mqtt_publish_pkt {
char* topic;
uint16_t packet_id; /* Packet identifier is required for publish if QoS > 0 */
- uint8_t QoS;
- uint8_t dup_flags;
+ uint8_t QoS; /* 0, 1 or 2 */
+ uint8_t dup_flag; /* Accept 1 or MQTT_PUBLISH_DUP as flag set */
uint8_t retain_flag;
+ uint32_t message_size;
uint8_t* application_message;
};
+/* Create MQTT publish packet
+ * This function must be called in order to create a publish MQTT packet used to publish data on a
+ * topic (send data to the server).
+ * The application message size can be 0, in which case the application_message pointer in the
+ * mqtt_publish_pkt struct may be NULL
+ *
+ * Caller must provide a buffer "buf" big enougth to receive the whole packet and indicate it's size
+ * in "buf_size".
+ * Returns the used buffer size (packet size) on success.
+ * Return value is negative on error :
+ * -EINVAL if either buf or pkt is NULL,
+ * -EINVAL if client_id string provided in mqtt_connect_pkt struct is NULL (it's length may be 0,
+ * but pointer cannot bu NULL).
+ * -E2BIG if buffer is not big enough for packet.
+ */
+int mqtt_pack_publish_packet(const struct mqtt_publish_pkt* pkt, uint8_t* buf, uint32_t buf_size);
+
+
+
+#define MQTT_PUBREL_FLAG (0x01 << 1)
+
/* MQTT publish responses packet, used for puback, pubrec, pubrel and pubcomp */
/* control paquet type must be either (MQTT_CONTROL_PUBACK << 4) if QoS = 1 (no further response required)
* or (MQTT_CONTROL_PUBREC << 4) if QoS = 2 and then a publish release must be received or sent
uint8_t control; /* Packet type */
uint8_t rem_len; /* Remaining length : must be 0x02 */
uint16_t acked_pkt_id; /* Id of packet that is being acknowledged, in network endianness */
-};
+} __attribute__ ((__packed__));
+
+/* Build MQTT puback, pubrec, pubrel or pubcomp packet, used in the publish acknowledge one-way or
+ * two-way hand-check mechanism.
+ * The provided buf must be big enough to hold 4 bytes.
+ * Note that buf may safely be cast to or from a mqtt_publish_response_pkt struct pointer.
+ * type is one of MQTT_CONTROL_PUBACK, MQTT_CONTROL_PUBREC, MQTT_CONTROL_PUBREL or MQTT_CONTROL_PUBCOMP.
+ * acked_pkt_id is the ID of the concerned publish packet, in host endianness.
+ * Returns the used buffer size (4 bytes) on success, or -EINVAL in case of a NULL buf pointer.
+ */
+int mqtt_pack_publish_response_pkt(uint8_t* buf, uint16_t acked_pkt_id, uint8_t type);
+
+/* Check MQTT puback, pubrec, pubrel or pubcomp packet
+ * This function may get called to check a supposed publish acknowledge packet in either one-way or
+ * two-way hand-check mechanism.
+ * type is either MQTT_CONTROL_PUBACK, MQTT_CONTROL_PUBREC, MQTT_CONTROL_PUBREL or MQTT_CONTROL_PUBCOMP.
+ * The function checks for conformance to the MQTT protocol and returns 0 if the packet is valid.
+ * The function returns -EPROTO in case of protocol error or -EINVAL in case of a NULL pkt pointer.
+ */
+int mqtt_check_publish_response_pkt(struct mqtt_publish_response_pkt* pkt, uint8_t type);
uint8_t rem_len; /* Remaining length : must be 0x03 in our "single subscription" case */
uint16_t acked_pkt_id; /* Id of packet that is being acknowledged, in network endianness */
uint8_t ret_code; /* equals accepted QoS or 0x80 in case of error */
-};
+} __attribute__ ((__packed__));
#define MQTT_SUBSCRIBE_FAILURE (0x80)
uint8_t control; /* Packet type */
uint8_t rem_len; /* Remaining length : must be 0x02 in our "single subscription" case */
uint16_t acked_pkt_id; /* Id of packet that is being acknowledged, in network endianness */
-};
+} __attribute__ ((__packed__));
struct mqtt_ping_pkt {
uint8_t control; /* Packet type : either (MQTT_CONTROL_PINGREQ << 4) or (MQTT_CONTROL_PINGRESP << 4) */
uint8_t rem_len; /* Remaining length : must be 0x00 */
-};
+} __attribute__ ((__packed__));
/***************************************************************************** */
struct mqtt_disconnect_pkt {
uint8_t control; /* Packet type : (MQTT_CONTROL_DISCONNECT << 4) */
uint8_t rem_len; /* Remaining length : must be 0x00 */
-};
+} __attribute__ ((__packed__));
* The size is encoded in little endianness (least significant 7 bits on first byte and so on).
* Refer to section 2.2.3 of mqtt-v3.1.1-os for translation of remaining_length on wire.
*/
-int encode_remaining_length(uint8_t* buf, uint32_t length)
+static int encode_remaining_length(uint8_t* buf, uint32_t length)
{
int idx = 0;
return idx;
}
/* Decode remaining length */
-int decode_remaining_length(uint8_t* buf, uint8_t* length)
+static int decode_remaining_length(uint8_t* buf, uint32_t* length)
{
int idx = 0;
}
/* Pack fixed header */
-int mqtt_pack_fixed_header(uint8_t *buf, uint8_t control, uint8_t flags, uint32_t length)
+static int mqtt_pack_fixed_header(uint8_t *buf, uint8_t control, uint8_t flags, uint32_t length)
{
*buf = (control << 4) | flags;
return 1 + encode_remaining_length((buf + 1), length);
/***************************************************************************** */
/* Create MQTT connect packet
- * This function must be called in order to connectte the connect MQTT packet used to
+ * This function must be called in order to create the connect MQTT packet used to
* connect to the server.
*/
-int mqtt_pack_connect_packet(struct mqtt_connect_pkt* pkt, uint8_t* buf, uint32_t buf_size)
+int mqtt_pack_connect_packet(const struct mqtt_connect_pkt* pkt, uint8_t* buf, uint32_t buf_size)
{
struct mqtt_connect_pkt_fixed_payload fxpl;
uint32_t remaining_length = 0;
uint32_t len = 0;
uint16_t client_id_len = 0, will_topic_len = 0, username_len = 0, password_len = 0;
+ if ((pkt == NULL) || (buf == NULL)) {
+ return -EINVAL;
+ }
/* Fixed payload part */
memset(&fxpl, 0, sizeof(struct mqtt_connect_pkt_fixed_payload));
fxpl.proto_name_len = htons(4);
* This function may get called to check a supposed connect acknowledge packet.
* The function checks for conformance to the MQTT protocol and returns 0 if the packet is valid,
* regardless of the retrun code value.
- * The function returns -EPROTO in case of protocol error.
*/
-int mqtt_check_connack_response_pkt(struct mqtt_connack_response_pkt* pkt)
+int mqtt_check_connack_response_pkt(const struct mqtt_connack_response_pkt* pkt)
{
+ if (pkt == NULL) {
+ return -EINVAL;
+ }
if (pkt->control != (MQTT_CONTROL_CONNACK << 4)) {
return -EPROTO;
}
/***************************************************************************** */
+/* Create MQTT publish packet
+ * This function must be called in order to create a publish MQTT packet used to
+ * publish data on a topic (send data to the server).
+ */
+int mqtt_pack_publish_packet(const struct mqtt_publish_pkt* pkt, uint8_t* buf, uint32_t buf_size)
+{
+ uint32_t remaining_length = 0;
+ uint32_t len = 0;
+ uint16_t topic_len = 0;
+ uint8_t publish_flags = 0;
+
+ if ((pkt == NULL) || (buf == NULL)) {
+ return -EINVAL;
+ }
+ /* Check packet consistency */
+ if ((pkt->message_size != 0) && (pkt->application_message == NULL)) {
+ return -ENODATA;
+ }
+ /* Compute message size
+ * Packet ID is 2 bytes long. There is no "message size" field in the publish packet, as the
+ * message size can be computed by substracting the topic string size and packet id size from
+ * the packet "remaining length" field. */
+ remaining_length = 2 + pkt->message_size;
+ /* Topic is mandatory */
+ if (pkt->topic == NULL) {
+ return -EINVAL;
+ }
+ topic_len = strlen(pkt->topic);
+ /* Update packet payload size */
+ remaining_length += topic_len + 2;
+
+ /* Set publish flags */
+ publish_flags = MQTT_PUBLISH_QoS(pkt->QoS);
+ if (pkt->dup_flag) {
+ publish_flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION;
+ }
+ if (pkt->retain_flag) {
+ publish_flags |= MQTT_PUBLISH_RETAIN;
+ }
+
+ /* Build MQTT Publish packet */
+ /* Fixed header */
+ len = mqtt_pack_fixed_header(buf, MQTT_CONTROL_PUBLISH, publish_flags, remaining_length);
+ if (remaining_length + len > buf_size) {
+ return -E2BIG;
+ }
+ /* Topic is mandatory */
+ len += mqtt_pack_str((buf + len), pkt->topic, topic_len);
+ /* Packet ID */
+ *(uint16_t*)(buf + len) = htons(pkt->packet_id);
+ len += 2;
+ /* Add optionnal application message */
+ if (pkt->message_size != 0) {
+ memcpy((buf + len), pkt->application_message, pkt->message_size);
+ len += pkt->message_size;
+ }
+
+ return len;
+}
+
+
+/***************************************************************************** */
+/* Unpack MQTT publish packet
+ * This function must be called in order to transform a received publish MQTT packet to a
+ * mqtt_publish_pkt structure.
+ * The function also checks the validity of the packet.
+ * All returned pointers within the struct will point to parts of the provided buffer, so the buffer
+ * must not be discarded after the call.
+ */
+int mqtt_unpack_publish_packet(struct mqtt_publish_pkt* pkt, uint8_t* buf, uint32_t size)
+{
+ int ret = 0;
+ uint32_t idx = 0, pkt_len = 0;
+ uint16_t tmp = 0, topic_len = 0;
+ if ((pkt == NULL) || (buf == NULL)) {
+ return -EINVAL;
+ }
+ /* Check type */
+ if ((buf[0] & 0xF0) != (MQTT_CONTROL_PUBLISH << 4)) {
+ return -EPROTO;
+ }
+ /* Check packet size */
+ ret = decode_remaining_length((buf + 1), &pkt_len);
+ if ((ret > 4) || ((ret + pkt_len + 1) != size)) {
+ return -EPROTO;
+ }
+ idx = 1 + ret;
+ /* Decode flags */
+ pkt->QoS = (buf[0] >> 1) & 0x03;
+ pkt->dup_flag = (buf[0] & MQTT_PUBLISH_DUP);
+ pkt->retain_flag = (buf[0] & MQTT_PUBLISH_RETAIN);
+ if (pkt->QoS > 2) {
+ return -EPROTO;
+ }
+ /* Decode topic string */
+ tmp = *((uint16_t*)(buf + idx));
+ topic_len = ntohs(tmp);
+ pkt->topic = (char*)(buf + idx + 2);
+ idx += topic_len + 2;
+ if ((idx + 2) > size) {
+ return -EPROTO;
+ }
+ /* Decode packet ID */
+ tmp = *((uint16_t*)(buf + idx));
+ pkt->packet_id = ntohs(tmp);
+ idx += 2;
+ /* Get application message */
+ pkt->message_size = size - idx;
+ if (pkt->message_size != 0) {
+ pkt->application_message = (buf + idx);
+ }
+ return 0;
+}
+
+/***************************************************************************** */
+/* Build MQTT puback, pubrec, pubrel or pubcomp packet, used in the publish acknowledge one-way or
+ * two-way hand-check mechanism.
+ */
+int mqtt_pack_publish_response_pkt(uint8_t* buf, uint16_t acked_pkt_id, uint8_t type)
+{
+ if (buf == NULL) {
+ return -EINVAL;
+ }
+ buf[0] = (type << 4);
+ if (type == MQTT_CONTROL_PUBREL) {
+ buf[0] |= MQTT_PUBREL_FLAG;
+ }
+ buf[1] = 0x02;
+ *(uint16_t*)(buf + 2) = htons(acked_pkt_id);
+
+ return 4;
+}
+
+/***************************************************************************** */
+/* Check MQTT puback, pubrec, pubrel or pubcomp packet
+ * This function may get called to check a supposed publish acknowledge packet in either one-way or
+ * two-way hand-check mechanism.
+ */
+int mqtt_check_publish_response_pkt(struct mqtt_publish_response_pkt* pkt, uint8_t type)
+{
+ uint8_t awaited_control = 0;
+
+ if (pkt == NULL) {
+ return -EINVAL;
+ }
+ /* Check control byte */
+ awaited_control = (type << 4);
+ /* The pubrel packet must also have a "pubrel" flag ... the protocol provides no indication about the
+ * reason behind this */
+ if (type == MQTT_CONTROL_PUBREL) {
+ awaited_control |= MQTT_PUBREL_FLAG;
+ }
+ if (pkt->control != awaited_control) {
+ return -EPROTO;
+ }
+ /* Check size */
+ if (pkt->rem_len != 2) {
+ return -EPROTO;
+ }
+ /* Valid packet */
+ return 0;
+}
+
+