From: Nathael Pajani Date: Sat, 16 Mar 2019 16:25:30 +0000 (+0100) Subject: replace "response" by "reply" and add support for subscribe (under test) and ping... X-Git-Url: http://git.techno-innov.fr/?a=commitdiff_plain;h=465c9df3a7c85797f38eee2c600c533d5ac7bc28;p=soft%2Flpc122x%2Fcore replace "response" by "reply" and add support for subscribe (under test) and ping (not tested) --- diff --git a/include/lib/protocols/mqtt.h b/include/lib/protocols/mqtt.h index b75a33b..dc995a1 100644 --- a/include/lib/protocols/mqtt.h +++ b/include/lib/protocols/mqtt.h @@ -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 */ diff --git a/lib/protocols/mqtt.c b/lib/protocols/mqtt.c index 005029d..d9e9ef3 100644 --- a/lib/protocols/mqtt.c +++ b/lib/protocols/mqtt.c @@ -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); +}