* which is not part of the code found in mqtt.c and mqtt.h, making it possible to use any underlying
* communication layer which fulfills the above requirements.
*
- * The application using this implementation of the MQTT protocol must provide two comminication
- * funcions : mqtt_app_send() and mqtt_app_recv(), which are respectively responsible for sending and
- * receiving MQTT packets to and from the server.
+ * This code only handles the packet encoding and decoding according to the MQTT specification and
+ * provides usage information in order to respect the protocol flow, but does not provide the communication
+ * parts of the protocol, which should be application specific.
*
*/
#define MQTT_QoS_1 (1)
#define MQTT_QoS_2 (2)
-/* Control byte flags */
-#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)
-#define MQTT_SUBSCRIBE_FLAG (0x01 << 1)
-#define MQTT_UNSUBSCRIBE_FLAG (0x01 << 1)
-
-/**
- * Generic MQTT packet, with unkown payload size.
- * These will have to be translated before being sent on the wire, so all fields are in host endianness.
- */
-struct mqtt_pkt {
- /* The type of packet, stored on the 4 LSB bytes */
- uint8_t control_type;
- /* Associated flags */
- uint8_t flags;
- /* Packet identifier, stored in host endianness in this structure */
- /* packet_id is required for publish (QoS > 0), puback, pubrec, pubrel, pubcomp,
- * subscribe, suback, unsubscribe, and unsuback.
- * The packet id has been indicated as a comment in the different structures where it is used but
- * which already include this header structure in order to indicate where it must be included in
- * the final packet sent on the wire.
- */
- uint16_t packet_id;
- /* The remaining size of the packet in bytes (i.e. the size of variable header and payload).
- * Refer to section 2.2.3 of mqtt-v3.1.1-os for translation of remaining_length on wire */
- uint32_t remaining_length;
- /* Pointer to the payload */
- /* payload is required for connect, subscribe, suback, unsubscribe, and optionnal for publish */
- uint8_t* payload;
-};
struct mqtt_str {
- uint16_t len; /* Big endian encoded size of string */
+ uint16_t len; /* size of string, stored in host endianness */
char* str; /* UTF-8 encoded string */
};
-enum MQTTErrors {
- MQTT_ERROR_UNKNOWN,
- MQTT_OK,
- MQTT_ERROR_NULLPTR,
- MQTT_ERROR_CONTROL_FORBIDDEN_TYPE,
- MQTT_ERROR_CONTROL_INVALID_FLAGS,
- MQTT_ERROR_CONTROL_WRONG_TYPE,
- MQTT_ERROR_CONNECT_NULL_CLIENT_ID,
- MQTT_ERROR_CONNECT_NULL_WILL_MESSAGE,
- MQTT_ERROR_CONNECT_FORBIDDEN_WILL_QOS,
- MQTT_ERROR_CONNACK_FORBIDDEN_FLAGS,
- MQTT_ERROR_CONNACK_FORBIDDEN_CODE,
- MQTT_ERROR_PUBLISH_FORBIDDEN_QOS,
- MQTT_ERROR_SUBSCRIBE_TOO_MANY_TOPICS,
- MQTT_ERROR_MALFORMED_RESPONSE,
- MQTT_ERROR_UNSUBSCRIBE_TOO_MANY_TOPICS,
- MQTT_ERROR_RESPONSE_INVALID_CONTROL_TYPE,
- MQTT_ERROR_CONNECT_NOT_CALLED,
- MQTT_ERROR_SEND_BUFFER_IS_FULL,
- MQTT_ERROR_SOCKET_ERROR,
- MQTT_ERROR_MALFORMED_REQUEST,
- MQTT_ERROR_RECV_BUFFER_TOO_SMALL,
- MQTT_ERROR_ACK_OF_UNKNOWN,
- MQTT_ERROR_NOT_IMPLEMENTED,
- MQTT_ERROR_CONNECTION_REFUSED,
- MQTT_ERROR_SUBSCRIBE_FAILED,
- MQTT_ERROR_CONNECTION_CLOSED,
- MQTT_ERROR_INITIAL_RECONNECT,
- MQTT_ERROR_INVALID_REMAINING_LENGTH,
-};
-
/***************************************************************************** */
/* Connect and Connack packets */
#define MQTT_CONNECT_FLAG_USER_NAME (0x01 << 7)
struct mqtt_connect_pkt_fixed_payload {
- uint16_t name_len; /* network endian */
+ uint16_t proto_name_len; /* network endian */
uint8_t protocol_name[4]; /* "MQTT" : use MQTT_PROTOCOL_NAME */
uint8_t protocol_level; /* use MQTT_PROTOCOL_LEVEL */
uint8_t connect_flags;
uint16_t keep_alive_seconds; /* network endian */
};
-struct mqtt_connect_pkt_strings {
+struct mqtt_connect_pkt {
+ uint16_t keep_alive_seconds;
+ uint8_t clean_session_flag;
struct mqtt_str client_id;
struct mqtt_str will_topic;
- struct mqtt_str will_message;
+ uint16_t will_msg_size; /* stored in host endianness */
+ uint8_t will_QoS; /* 0, 1 or 2 */
+ uint8_t will_retain; /* 0 or 1 */
+ uint8_t* will_msg;
struct mqtt_str username;
struct mqtt_str password;
};
-/* The MQTT connect packet.
- * note that the packet id field is unused in this case */
-struct mqtt_connect_pkt {
- struct mqtt_pkt header;
- struct mqtt_connect_pkt_fixed_payload fixed_payload;
- struct mqtt_connect_pkt_strings conn_str;
-};
+/* Create MQTT connect packet
+ * This function must be called in order to connectte the connect MQTT packet used to
+ * connect to the server.
+ * The client must send a connect packet whenever the server does not acknoledge a publish,
+ * subscribe, unsubscribe, or ping packet, or when the server explicitely closes the connection.
+ *
+ * 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 :
+ * -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);
+
/* Connack return codes returned in a CONNACK packet */
-enum MQTTConnackReturnCode {
+enum MQTT_connack_return_codes {
MQTT_CONNACK_ACCEPTED = 0,
MQTT_CONNACK_REFUSED_PROTOCOL_VERSION = 1,
MQTT_CONNACK_REFUSED_IDENTIFIER_REJECTED = 2,
* the server accepts the connection and has a stored session for this client id */
#define MQTT_CONNACK_SESSION_PRESENT (0x01 << 0)
+/* Check and Decode MQTT connack packet */
+int mqtt_unpackconnack_response_pkt(uint8_t* buf, uint8_t buf_size);
/***************************************************************************** */
/* 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
* an Application Message. */
struct mqtt_publish_pkt {
- struct mqtt_pkt header;
struct mqtt_str topic;
- /* uint16_t packet_id : Packet identifier, stored in network endianness */
+ uint16_t packet_id; /* Packet identifier is required for publish if QoS > 0 */
+ uint8_t QoS;
+ uint8_t dup_flags;
+ uint8_t retain_flag;
uint8_t* application_message;
};
+
/* 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
struct mqtt_publish_response_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, stored in network endianness */
+ uint16_t acked_pkt_id; /* Id of packet that is being acknowledged, in network endianness */
};
* struct mqtt_subscribe_response_pkt should also be adapted for multiple subscription support.
*/
+#define MQTT_SUBSCRIBE_FLAG (0x01 << 1)
+
/* MQTT subscribe packet */
struct mqtt_subscribe_pkt {
- struct mqtt_pkt header;
- /* uint16_t packet_id : Packet identifier, stored in network endianness */
+ uint16_t packet_id; /* Packet identifier */
struct mqtt_str topic;
- uint8_t qos; /* unshifted QoS value */
+ uint8_t QoS; /* unshifted QoS value */
};
/* MQTT subscribe response packet */
struct mqtt_subscribe_response_pkt {
uint8_t control; /* Packet type */
uint8_t rem_len; /* Remaining length : must be 0x03 in our "single subscription" case */
- uint16_t packet_id; /* Packet identifier, stored in network endianness */
+ 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 */
};
#define MQTT_SUBSCRIBE_FAILURE (0x80)
-
/***************************************************************************** */
/* unsubsribe and unsuback packets */
* at once here in order to avoid unnecessary complexity of code and dynamic allocation of memory.
*/
+#define MQTT_UNSUBSCRIBE_FLAG (0x01 << 1)
+
/* MQTT unsubscribe packet */
struct mqtt_unsubscribe_pkt {
- struct mqtt_pkt header;
- /* uint16_t packet_id : Packet identifier, stored in network endianness */
+ uint16_t packet_id; /* Packet identifier */
struct mqtt_str topic;
};
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 packet_id; /* Packet identifier, stored in network endianness */
+ uint16_t acked_pkt_id; /* Id of packet that is being acknowledged, in network endianness */
};
-
-
#endif /* MQTT_H */
*************************************************************************** */
#include "core/system.h"
+#include "lib/utils.h"
+#include "lib/string.h"
#include "lib/errno.h"
#include "lib/protocols/mqtt.h"
* which is not part of the code found in mqtt.c and mqtt.h, making it possible to use any underlying
* communication layer which fulfills the above requirements.
*
+ * This code only handles the packet encoding and decoding according to the MQTT specification and
+ * provides usage information in order to respect the protocol flow, but does not provide the communication
+ * parts of the protocol, which should be application specific.
+ *
+ */
+
+
+
+/***************************************************************************** */
+/* MQTT helper functions */
+
+/* Encode remaining length
+ * Remaining length is encoded on 1 to four bytes. The least significant seven bits of each byte encode the
+ * data, and the most significant bit is used to indicate that there are following bytes in the representation.
+ * 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)
+{
+ int idx = 0;
+
+ do {
+ buf[idx] = length & 0x7F;
+ length >>= 7;
+ if (length > 0) {
+ buf[idx] |= 0x80;
+ }
+ idx++;
+ } while (length > 0);
+
+ return idx;
+}
+/* Decode remaining length */
+int decode_remaining_length(uint8_t* buf, uint8_t* length)
+{
+ int idx = 0;
+
+ do {
+ length += (buf[idx] & 0x7F) << (7 * idx);
+ } while ((buf[idx++] & 0x80) && (idx <= 3));
+
+ return idx;
+}
+
+
+/* Pack a string to MQTT strings format.
+ * The string must already be UTF8 encoded.
+ */
+int mqtt_pack_str(uint8_t *buf, struct mqtt_str* str)
+{
+ *(uint16_t*)buf = htons(str->len);
+ memcpy((buf + 2), str->str, str->len);
+ return str->len + 2;
+}
+
+/* Pack fixed header */
+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
+ * connect to the server.
*/
+int mqtt_pack_connect_packet(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;
+
+ /* Fixed payload part */
+ memset(&fxpl, 0, sizeof(struct mqtt_connect_pkt_fixed_payload));
+ fxpl.proto_name_len = htons(4);
+ memcpy(&(fxpl.protocol_name), MQTT_PROTOCOL_NAME, 4);
+ fxpl.protocol_level = MQTT_PROTOCOL_LEVEL;
+ fxpl.keep_alive_seconds = htons(pkt->keep_alive_seconds);
+ remaining_length = sizeof(struct mqtt_connect_pkt_fixed_payload);
+
+ /* Client id is (almost) mandatory */
+ if (pkt->client_id.str != NULL) {
+ pkt->client_id.len = strlen(pkt->client_id.str);
+ /* Update packet payload size */
+ remaining_length += pkt->client_id.len + 2;
+ } else {
+ return -EINVAL;
+ }
+
+ /* Set connect flags and compute message size for optionnal parts */
+ if (pkt->clean_session_flag) {
+ fxpl.connect_flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION;
+ }
+ /* Optionnal will message */
+ if ((pkt->will_msg_size != 0) && (pkt->will_topic.str != NULL) && (pkt->will_msg != NULL)) {
+ fxpl.connect_flags |= MQTT_CONNECT_FLAG_WILL_FLAG;
+ fxpl.connect_flags |= MQTT_CONNECT_FLAG_WILL_QoS(pkt->will_QoS);
+ if (pkt->will_retain != 0) {
+ fxpl.connect_flags |= MQTT_CONNECT_FLAG_WILL_RETAIN;
+ }
+ pkt->will_topic.len = strlen(pkt->will_topic.str);
+ /* Update packet payload size */
+ remaining_length += pkt->will_topic.len + 2;
+ remaining_length += pkt->will_msg_size + 2;
+ }
+ /* Optionnal username and password */
+ if (pkt->username.str != NULL) {
+ pkt->username.len = strlen(pkt->username.str);
+ fxpl.connect_flags |= MQTT_CONNECT_FLAG_USER_NAME;
+ /* Update packet payload size */
+ remaining_length += pkt->username.len + 2;
+ if (pkt->password.str != NULL) {
+ pkt->password.len = strlen(pkt->password.str);
+ fxpl.connect_flags |= MQTT_CONNECT_FLAG_PASSWORD;
+ /* Update packet payload size */
+ remaining_length += pkt->password.len + 2;
+ }
+ }
+
+ /* Build MQTT Connect packet */
+ /* Fixed header */
+ len = mqtt_pack_fixed_header(buf, MQTT_CONTROL_CONNECT, 0, remaining_length);
+ if (remaining_length + len > buf_size) {
+ return -E2BIG;
+ }
+ /* Fixed payload */
+ memcpy((buf + len), &fxpl, sizeof(struct mqtt_connect_pkt_fixed_payload));
+ len += sizeof(struct mqtt_connect_pkt_fixed_payload);
+
+ /* Client ID is mandatory, even if length of ID string is 0 */
+ len += mqtt_pack_str((buf + len), &pkt->client_id);
+
+ /* Add optionnal Will message */
+ if ((pkt->will_msg_size != 0) && (pkt->will_topic.str != NULL) && (pkt->will_msg != NULL)) {
+ len += mqtt_pack_str((buf + len), &pkt->will_topic);
+ *(uint16_t*)(buf + len) = htons(pkt->will_msg_size);
+ memcpy((buf + len + 2), pkt->will_msg, pkt->will_msg_size);
+ len += pkt->will_msg_size + 2;
+ }
+ /* Add optionnal username and password */
+ if (pkt->username.str != NULL) {
+ len += mqtt_pack_str((buf + len), &pkt->username);
+ /* Add password too ? */
+ if (pkt->password.str != NULL) {
+ len += mqtt_pack_str((buf + len), &pkt->password);
+ }
+ }
-/* These two functions must be defined in the application code */
-extern int mqtt_app_send(const void* buf, uint16_t len);
-extern int mqtt_app_recv(void* buf, uint16_t bufsz);
+ return len;
+}