replace "response" by "reply" and add support for subscribe (under test) and ping...
authorNathael Pajani <nathael.pajani@ed3l.fr>
Sat, 16 Mar 2019 16:25:30 +0000 (17:25 +0100)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Tue, 8 Nov 2022 16:03:05 +0000 (17:03 +0100)
include/lib/protocols/mqtt.h
lib/protocols/mqtt.c

index b75a33b..dc995a1 100644 (file)
@@ -143,7 +143,7 @@ enum MQTT_connack_return_codes {
 };
 
 /* MQTT connack packet */
-struct mqtt_connack_response_pkt {
+struct mqtt_connack_reply_pkt {
        uint8_t control; /* Paquet type : must be (MQTT_CONTROL_CONNACK << 4) */
        uint8_t rem_len; /* Remaining length : must be 0x02 */
        uint8_t flags;  /* Connect acknowledge flags */
@@ -161,7 +161,7 @@ struct mqtt_connack_response_pkt {
  * 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(const struct mqtt_connack_response_pkt* pkt);
+int mqtt_check_connack_reply_pkt(const struct mqtt_connack_reply_pkt* pkt);
 
 /***************************************************************************** */
 /* publish and puback packets */
@@ -200,14 +200,24 @@ struct mqtt_publish_pkt {
 int mqtt_pack_publish_packet(const struct mqtt_publish_pkt* pkt, uint8_t* buf, uint32_t buf_size);
 
 
+/* 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.
+ * if the return value is positive, it is the topic string length.
+ */
+int mqtt_unpack_publish_packet(struct mqtt_publish_pkt* pkt, uint8_t* buf, uint32_t 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)
+/* MQTT publish replies packet, used for puback, pubrec, pubrel and pubcomp */
+/* control paquet type must be either (MQTT_CONTROL_PUBACK << 4) if QoS = 1 (no further reply required)
  * or (MQTT_CONTROL_PUBREC << 4) if QoS = 2 and then a publish release must be received or sent
  * (MQTT_CONTROL_PUBREL << 4), answered by a publish complete packet (MQTT_CONTROL_PUBCOMP << 4) */
-struct mqtt_publish_response_pkt {
+struct mqtt_publish_reply_pkt {
        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 */
@@ -216,12 +226,12 @@ struct mqtt_publish_response_pkt {
 /* 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.
+ * Note that buf may safely be cast to or from a mqtt_publish_reply_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);
+int mqtt_pack_publish_reply_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
@@ -230,7 +240,7 @@ int mqtt_pack_publish_response_pkt(uint8_t* buf, uint16_t acked_pkt_id, uint8_t
  * 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);
+int mqtt_check_publish_reply_pkt(struct mqtt_publish_reply_pkt* pkt, uint8_t type);
 
 
 
@@ -238,56 +248,76 @@ int mqtt_check_publish_response_pkt(struct mqtt_publish_response_pkt* pkt, uint8
 /* subsribe, unsubsribe, suback and unsuback packets */
 
 #define MQTT_SUBSCRIBE_FLAG  (0x01 << 1)
+#define MQTT_UNSUBSCRIBE_FLAG  MQTT_SUBSCRIBE_FLAG
 
-/* MQTT subscribe packet */
-struct mqtt_subscribe_pkt {
+/* MQTT subscribe or unsubsribe packet */
+struct mqtt_sub_pkt {
        uint16_t packet_id; /* Packet identifier */
        uint8_t nb_topics; /* Number of topics in the topics table. Limited to 125 */
        struct mqtt_topic* topics; /* Table of topics */
 };
 
-/* MQTT subscribe response packet */
-struct mqtt_subscribe_response_pkt {
+/* Build MQTT subscribe packet
+ * This function must be called in order to create a subscribe MQTT packet used to
+ * subscibe on a topic (or multiple topics) in order to receive data published on this
+ * or these topics.
+ * We limit the number of subscriptions sent at once to 125 in order to get a fixed size
+ * subscription acknoledgement packet.
+ */
+int mqtt_pack_subscribe_pkt(const struct mqtt_sub_pkt* pkt, uint8_t* buf, uint32_t buf_size);
+
+/* Build MQTT unsubscribe packet
+ * This function must be called in order to create an unsubscribe MQTT packet used to unsubscibe
+ * from a topic (or multiple topics) in order to stop receiving data published on this or these
+ * topics.
+ */
+int mqtt_pack_unsubscribe_pkt(const struct mqtt_sub_pkt* pkt, uint8_t* buf, uint32_t buf_size);
+
+
+
+/* MQTT subscribe or unsubscribe reply packet */
+struct mqtt_sub_reply_pkt {
        uint8_t control; /* Packet type */
-       uint8_t rem_len; /* Remaining length : must be 0x03 in our "single subscription" case */
+       uint8_t rem_len; /* Remaining length : this is valid for up to 125 subscriptions sent at once ... */
        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)
 
-
-/***************************************************************************** */
-/* unsubsribe and unsuback packets */
-
-/* NOTE : Same as subscription, I choosed not to implement the possibility to send multiple unsubscriptions
- * at once here in order to avoid unnecessary complexity of code and dynamic allocation of memory.
+/* Check MQTT suback packet
+ * This function may get called to check a supposed subscribe acknowledge packet.
+ * The function checks for conformance to the MQTT protocol and returns 0 if the packet is valid,
+ * regardless of the return codes received.
+ * len must be the length of the full packet received, which includes the mqtt_subscribe_reply_pkt
+ * structure and all the return codes received.
  */
+int mqtt_check_suback_reply_pkt(const struct mqtt_sub_reply_pkt* pkt, uint8_t len);
 
-#define MQTT_UNSUBSCRIBE_FLAG  (0x01 << 1)
-
-/* MQTT unsubscribe packet */
-struct mqtt_unsubscribe_pkt {
-       uint16_t packet_id; /* Packet identifier */
-       char* topic;
-};
+/* Check MQTT unsuback packet
+ * This function may get called to check a supposed unsubscribe acknowledge packet.
+ * The function checks for conformance to the MQTT protocol and returns 0 if the packet is valid.
+ */
+int mqtt_check_unsuback_reply_pkt(const struct mqtt_sub_reply_pkt* pkt);
 
-/* MQTT unsubscribe response packet */
-struct mqtt_unsubscribe_response_pkt {
-       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__));
 
 
 
 /***************************************************************************** */
-/* MQTT ping request and response packet */
+/* MQTT ping request and reply packet */
 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__));
 
+/* Build MQTT ping packet
+ * This one is a fixed packet, easy.
+ */
+int mqtt_pack_ping_pkt(uint8_t* buf);
+
+/* Check MQTT ping reply packet */
+int mqtt_check_ping_reply_pkt(const struct mqtt_ping_pkt* pkt);
+
+
 
 /***************************************************************************** */
 /* MQTT disconnect */
index 005029d..d9e9ef3 100644 (file)
@@ -207,7 +207,7 @@ int mqtt_pack_connect_packet(const struct mqtt_connect_pkt* pkt, uint8_t* buf, u
  * The function checks for conformance to the MQTT protocol and returns 0 if the packet is valid,
  * regardless of the retrun code value.
  */
-int mqtt_check_connack_response_pkt(const struct mqtt_connack_response_pkt* pkt)
+int mqtt_check_connack_reply_pkt(const struct mqtt_connack_reply_pkt* pkt)
 {
        if (pkt == NULL) {
                return -EINVAL;
@@ -353,7 +353,7 @@ int mqtt_unpack_publish_packet(struct mqtt_publish_pkt* pkt, uint8_t* buf, uint3
 /* 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)
+int mqtt_pack_publish_reply_pkt(uint8_t* buf, uint16_t acked_pkt_id, uint8_t type)
 {
        uint16_t tmp_acked_pkt_id = 0;
        if (buf == NULL) {
@@ -375,7 +375,7 @@ int mqtt_pack_publish_response_pkt(uint8_t* buf, uint16_t acked_pkt_id, uint8_t
  * 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)
+int mqtt_check_publish_reply_pkt(struct mqtt_publish_reply_pkt* pkt, uint8_t type)
 {
        uint8_t awaited_control = 0;
 
@@ -401,4 +401,196 @@ int mqtt_check_publish_response_pkt(struct mqtt_publish_response_pkt* pkt, uint8
 }
 
 
+/* Subscribe and unsubscribe packets only differ by the addition of the QoS byte after each topic. */
+static int mqtt_pack_sub_unsub(const struct mqtt_sub_pkt* pkt,
+                                                               uint8_t* buf, uint32_t buf_size, uint8_t type)
+{
+       uint32_t remaining_length = 0;
+       uint32_t len = 0, i = 0;
+       uint16_t topic_len = 0;
+       uint16_t tmp_packet_id = 0;
+
+       if ((pkt == NULL) || (buf == NULL)) {
+               return -EINVAL;
+       }
+       if ((type != MQTT_CONTROL_SUBSCRIBE) && (type != MQTT_CONTROL_UNSUBSCRIBE)) {
+               return -EINVAL;
+       }
+       /* Check packet consistency */
+       if ((pkt->nb_topics == 0) || (pkt->topics == NULL)) {
+               return -ENODATA;
+       }
+       /* Limit the number of topics to 125 in orer to keep the variable length field on one byte in
+        * the subscribe acknowledge packet. */
+       if (pkt->nb_topics > 125) {
+               return -EINVAL;
+       }
+       /* Compute message size
+        * Packet ID is 2 bytes long. */
+       remaining_length = 2;
+       for (i = 0; i < pkt->nb_topics; i++) {
+               if (pkt->topics[i].name == NULL) {
+                       return -EINVAL;
+               }
+               topic_len = strlen(pkt->topics[i].name);
+               /* Update packet payload size. */
+               remaining_length += topic_len + 2;
+               if (type == MQTT_CONTROL_SUBSCRIBE) {
+                       /* Add one for the associated QoS for each topic */
+                       remaining_length += 1;
+               }
+       }
+
+       /* Build MQTT Publish packet */
+       /* Fixed header */
+       len = mqtt_pack_fixed_header(buf, type, MQTT_SUBSCRIBE_FLAG, remaining_length);
+       if (remaining_length + len > buf_size) {
+               return -E2BIG;
+       }
+       /* Packet ID */
+       tmp_packet_id = htons(pkt->packet_id);
+       memcpy((buf + len), &tmp_packet_id, 2);
+       len += 2;
+       /* Topic(s) */
+       for (i = 0; i < pkt->nb_topics; i++) {
+               topic_len = strlen(pkt->topics[i].name);
+               len += mqtt_pack_str((buf + len), pkt->topics[i].name, topic_len);
+               if (type == MQTT_CONTROL_SUBSCRIBE) {
+                       /* Add the associated QoS */
+                       buf[len++] = pkt->topics[i].QoS & 0x03;
+               }
+       }
+
+       return len;
+}
+
+/***************************************************************************** */
+/* Build MQTT subscribe packet
+ * This function must be called in order to create a subscribe MQTT packet used to
+ * subscibe on a topic (or multiple topics) in order to receive data published on this
+ * or these topics.
+ * We limit the number of subscriptions sent at once to 125 in order to get a fixed size
+ * subscription acknoledgement packet.
+ */
+int mqtt_pack_subscribe_pkt(const struct mqtt_sub_pkt* pkt, uint8_t* buf, uint32_t buf_size)
+{
+       return mqtt_pack_sub_unsub(pkt, buf, buf_size, MQTT_CONTROL_SUBSCRIBE);
+}
+
+/***************************************************************************** */
+/* Build MQTT unsubscribe packet
+ * This function must be called in order to create an unsubscribe MQTT packet used to unsubscibe
+ * from a topic (or multiple topics) in order to stop receiving data published on this or these
+ * topics.
+ */
+int mqtt_pack_unsubscribe_pkt(const struct mqtt_sub_pkt* pkt, uint8_t* buf, uint32_t buf_size)
+{
+       return mqtt_pack_sub_unsub(pkt, buf, buf_size, MQTT_CONTROL_UNSUBSCRIBE);
+}
+
+
+/***************************************************************************** */
+/* Check MQTT suback packet
+ * This function may get called to check a supposed subscribe acknowledge packet.
+ * The function checks for conformance to the MQTT protocol and returns 0 if the packet is valid,
+ * regardless of the return codes received.
+ * len must be the length of the full packet received, which includes the mqtt_subscribe_reply_pkt
+ * structure and all the return codes received.
+ */
+int mqtt_check_suback_reply_pkt(const struct mqtt_sub_reply_pkt* pkt, uint8_t len)
+{
+       int i = 0;
+       uint8_t* ret_codes = NULL;
+       if (pkt == NULL) {
+               return -EINVAL;
+       }
+       if (pkt->control != (MQTT_CONTROL_SUBACK << 4)) {
+               return -EPROTO;
+       }
+       if ((pkt->rem_len > 127) || (pkt->rem_len != (len - 2))) {
+               return -EPROTO;
+       }
+       ret_codes = (uint8_t*)pkt + 4;
+       for (i = 0; i < (pkt->rem_len - 2); i++) {
+               if ((ret_codes[i] != 0x80) && (ret_codes[i] > MQTT_QoS_2)) {
+                       return -EREMOTEIO;
+               }
+       }
+       /* Valid packet */
+       return 0;
+}
+
+
+/***************************************************************************** */
+/* Check MQTT unsuback packet
+ * This function may get called to check a supposed unsubscribe acknowledge packet.
+ * The function checks for conformance to the MQTT protocol and returns 0 if the packet is valid.
+ */
+int mqtt_check_unsuback_reply_pkt(const struct mqtt_sub_reply_pkt* pkt)
+{
+       if (pkt == NULL) {
+               return -EINVAL;
+       }
+       if (pkt->control != (MQTT_CONTROL_UNSUBACK << 4)) {
+               return -EPROTO;
+       }
+       if (pkt->rem_len != 2) {
+               return -EPROTO;
+       }
+       /* Valid packet */
+       return 0;
+}
+
+
+/***************************************************************************** */
+/* Build MQTT ping packet
+ * This one is a fixed packet, easy.
+ */
+int mqtt_pack_ping_pkt(uint8_t* buf)
+{
+       struct mqtt_ping_pkt pkt = {
+               .control = (MQTT_CONTROL_PINGREQ << 4),
+               .rem_len = 0,
+       };
+       if (buf == NULL) {
+               return -EINVAL;
+       }
+       memcpy(buf, &pkt, sizeof(struct mqtt_ping_pkt));
+       return sizeof(struct mqtt_ping_pkt);
+}
+
+
+/***************************************************************************** */
+/* Check MQTT ping reply packet */
+int mqtt_check_ping_reply_pkt(const struct mqtt_ping_pkt* pkt)
+{
+       if (pkt == NULL) {
+               return -EINVAL;
+       }
+       if (pkt->control != (MQTT_CONTROL_PINGRESP << 4)) {
+               return -EPROTO;
+       }
+       if (pkt->rem_len != 0) {
+               return -EPROTO;
+       }
+       return 0;
+}
+
+
+/***************************************************************************** */
+/* Build MQTT disconnect packet
+ * This one is a fixed packet, easy.
+ */
+int mqtt_pack_disconnect_pkt(uint8_t* buf)
+{
+       struct mqtt_disconnect_pkt pkt = {
+               .control = (MQTT_CONTROL_DISCONNECT << 4),
+               .rem_len = 0,
+       };
+       if (buf == NULL) {
+               return -EINVAL;
+       }
+       memcpy(buf, &pkt, sizeof(struct mqtt_disconnect_pkt));
+       return sizeof(struct mqtt_disconnect_pkt);
+}