1 /*
2 * Copyright (c) 2018 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /** @file mqtt_encoder.c
8 *
9 * @brief Encoding functions needed to create packet to be sent to the broker.
10 */
11
12 #include <logging/log.h>
13 LOG_MODULE_REGISTER(net_mqtt_enc, CONFIG_MQTT_LOG_LEVEL);
14
15 #include "mqtt_internal.h"
16 #include "mqtt_os.h"
17
18 static const struct mqtt_utf8 mqtt_3_1_0_proto_desc =
19 MQTT_UTF8_LITERAL("MQIsdp");
20
21 static const struct mqtt_utf8 mqtt_3_1_1_proto_desc =
22 MQTT_UTF8_LITERAL("MQTT");
23
24 /** Never changing ping request, needed for Keep Alive. */
25 static const uint8_t ping_packet[MQTT_FIXED_HEADER_MIN_SIZE] = {
26 MQTT_PKT_TYPE_PINGREQ,
27 0x00
28 };
29
30 /** Never changing disconnect request. */
31 static const uint8_t disc_packet[MQTT_FIXED_HEADER_MIN_SIZE] = {
32 MQTT_PKT_TYPE_DISCONNECT,
33 0x00
34 };
35
36 /**
37 * @brief Packs unsigned 8 bit value to the buffer at the offset requested.
38 *
39 * @param[in] val Value to be packed.
40 * @param[inout] buf A pointer to the buf_ctx structure containing current
41 * buffer position.
42 *
43 * @retval 0 if procedure is successful.
44 * @retval -ENOMEM if there is no place in the buffer to store the value.
45 */
pack_uint8(uint8_t val,struct buf_ctx * buf)46 static int pack_uint8(uint8_t val, struct buf_ctx *buf)
47 {
48 if ((buf->end - buf->cur) < sizeof(uint8_t)) {
49 return -ENOMEM;
50 }
51
52 MQTT_TRC(">> val:%02x cur:%p, end:%p", val, buf->cur, buf->end);
53
54 /* Pack value. */
55 *(buf->cur++) = val;
56
57 return 0;
58 }
59
60 /**
61 * @brief Packs unsigned 16 bit value to the buffer at the offset requested.
62 *
63 * @param[in] val Value to be packed.
64 * @param[inout] buf A pointer to the buf_ctx structure containing current
65 * buffer position.
66 *
67 * @retval 0 if the procedure is successful.
68 * @retval -ENOMEM if there is no place in the buffer to store the value.
69 */
pack_uint16(uint16_t val,struct buf_ctx * buf)70 static int pack_uint16(uint16_t val, struct buf_ctx *buf)
71 {
72 if ((buf->end - buf->cur) < sizeof(uint16_t)) {
73 return -ENOMEM;
74 }
75
76 MQTT_TRC(">> val:%04x cur:%p, end:%p", val, buf->cur, buf->end);
77
78 /* Pack value. */
79 *(buf->cur++) = (val >> 8) & 0xFF;
80 *(buf->cur++) = val & 0xFF;
81
82 return 0;
83 }
84
85 /**
86 * @brief Packs utf8 string to the buffer at the offset requested.
87 *
88 * @param[in] str UTF-8 string and its length to be packed.
89 * @param[inout] buf A pointer to the buf_ctx structure containing current
90 * buffer position.
91 *
92 * @retval 0 if the procedure is successful.
93 * @retval -ENOMEM if there is no place in the buffer to store the string.
94 */
pack_utf8_str(const struct mqtt_utf8 * str,struct buf_ctx * buf)95 static int pack_utf8_str(const struct mqtt_utf8 *str, struct buf_ctx *buf)
96 {
97 if ((buf->end - buf->cur) < GET_UT8STR_BUFFER_SIZE(str)) {
98 return -ENOMEM;
99 }
100
101 MQTT_TRC(">> str_size:%08x cur:%p, end:%p",
102 (uint32_t)GET_UT8STR_BUFFER_SIZE(str), buf->cur, buf->end);
103
104 /* Pack length followed by string. */
105 (void)pack_uint16(str->size, buf);
106
107 memcpy(buf->cur, str->utf8, str->size);
108 buf->cur += str->size;
109
110 return 0;
111 }
112
113 /**
114 * @brief Computes and encodes length for the MQTT fixed header.
115 *
116 * @note The remaining length is not packed as a fixed unsigned 32 bit integer.
117 * Instead it is packed on algorithm below:
118 *
119 * @code
120 * do
121 * encodedByte = X MOD 128
122 * X = X DIV 128
123 * // if there are more data to encode, set the top bit of this byte
124 * if ( X > 0 )
125 * encodedByte = encodedByte OR 128
126 * endif
127 * 'output' encodedByte
128 * while ( X > 0 )
129 * @endcode
130 *
131 * @param[in] length Length of variable header and payload in the MQTT message.
132 * @param[inout] buf A pointer to the buf_ctx structure containing current
133 * buffer position. May be NULL (in this case function will
134 * only calculate number of bytes needed).
135 *
136 * @return Number of bytes needed to encode length value.
137 */
packet_length_encode(uint32_t length,struct buf_ctx * buf)138 static uint8_t packet_length_encode(uint32_t length, struct buf_ctx *buf)
139 {
140 uint8_t encoded_bytes = 0U;
141
142 MQTT_TRC(">> length:0x%08x cur:%p, end:%p", length,
143 (buf == NULL) ? 0 : buf->cur, (buf == NULL) ? 0 : buf->end);
144
145 do {
146 encoded_bytes++;
147
148 if (buf != NULL) {
149 *(buf->cur) = length & MQTT_LENGTH_VALUE_MASK;
150 }
151
152 length >>= MQTT_LENGTH_SHIFT;
153
154 if (buf != NULL) {
155 if (length > 0) {
156 *(buf->cur) |= MQTT_LENGTH_CONTINUATION_BIT;
157 }
158 buf->cur++;
159 }
160 } while (length > 0);
161
162 return encoded_bytes;
163 }
164
165 /**
166 * @brief Encodes fixed header for the MQTT message and provides pointer to
167 * start of the header.
168 *
169 * @param[in] message_type Message type containing packet type and the flags.
170 * Use @ref MQTT_MESSAGES_OPTIONS to construct the
171 * message_type.
172 * @param[in] start Pointer to the start of the variable header.
173 * @param[inout] buf Buffer context used to encode the frame.
174 * The 5 bytes before the start of the message are assumed
175 * by the routine to be available to pack the fixed header.
176 * However, since the fixed header length is variable
177 * length, the pointer to the start of the MQTT message
178 * along with encoded fixed header is supplied as output
179 * parameter if the procedure was successful.
180 * As output, the pointers will point to beginning and the end
181 * of the frame.
182 *
183 * @retval 0 if the procedure is successful.
184 * @retval -EMSGSIZE if the message is too big for MQTT.
185 */
mqtt_encode_fixed_header(uint8_t message_type,uint8_t * start,struct buf_ctx * buf)186 static uint32_t mqtt_encode_fixed_header(uint8_t message_type, uint8_t *start,
187 struct buf_ctx *buf)
188 {
189 uint32_t length = buf->cur - start;
190 uint8_t fixed_header_length;
191
192 if (length > MQTT_MAX_PAYLOAD_SIZE) {
193 return -EMSGSIZE;
194 }
195
196 MQTT_TRC("<< msg type:0x%02x length:0x%08x", message_type, length);
197
198 fixed_header_length = packet_length_encode(length, NULL);
199 fixed_header_length += sizeof(uint8_t);
200
201 MQTT_TRC("Fixed header length = %02x", fixed_header_length);
202
203 /* Set the pointer at the start of the frame before encoding. */
204 buf->cur = start - fixed_header_length;
205
206 (void)pack_uint8(message_type, buf);
207 (void)packet_length_encode(length, buf);
208
209 /* Set the cur pointer back at the start of the frame,
210 * and end pointer to the end of the frame.
211 */
212 buf->cur = buf->cur - fixed_header_length;
213 buf->end = buf->cur + length + fixed_header_length;
214
215 return 0;
216 }
217
218 /**
219 * @brief Encodes a string of a zero length.
220 *
221 * @param[in] buffer_len Total size of the buffer on which string will be
222 * encoded. This shall not be zero.
223 * @param[inout] buf A pointer to the buf_ctx structure containing current
224 * buffer position.
225 *
226 * @retval 0 if the procedure is successful.
227 * @retval -ENOMEM if there is no place in the buffer to store the binary
228 * string.
229 */
zero_len_str_encode(struct buf_ctx * buf)230 static int zero_len_str_encode(struct buf_ctx *buf)
231 {
232 return pack_uint16(0x0000, buf);
233 }
234
235 /**
236 * @brief Encodes and sends messages that contain only message id in
237 * the variable header.
238 *
239 * @param[in] message_type Message type and reserved bit fields.
240 * @param[in] message_id Message id to be encoded in the variable header.
241 * @param[inout] buf_ctx Pointer to the buffer context structure,
242 * containing buffer for the encoded message.
243 *
244 * @retval 0 or an error code indicating a reason for failure.
245 */
mqtt_message_id_only_enc(uint8_t message_type,uint16_t message_id,struct buf_ctx * buf)246 static int mqtt_message_id_only_enc(uint8_t message_type, uint16_t message_id,
247 struct buf_ctx *buf)
248 {
249 int err_code;
250 uint8_t *start;
251
252 /* Message id zero is not permitted by spec. */
253 if (message_id == 0U) {
254 return -EINVAL;
255 }
256
257 /* Reserve space for fixed header. */
258 buf->cur += MQTT_FIXED_HEADER_MAX_SIZE;
259 start = buf->cur;
260
261 err_code = pack_uint16(message_id, buf);
262 if (err_code != 0) {
263 return err_code;
264 }
265
266 return mqtt_encode_fixed_header(message_type, start, buf);
267 }
268
connect_request_encode(const struct mqtt_client * client,struct buf_ctx * buf)269 int connect_request_encode(const struct mqtt_client *client,
270 struct buf_ctx *buf)
271 {
272 uint8_t connect_flags = client->clean_session << 1;
273 const uint8_t message_type =
274 MQTT_MESSAGES_OPTIONS(MQTT_PKT_TYPE_CONNECT, 0, 0, 0);
275 const struct mqtt_utf8 *mqtt_proto_desc;
276 uint8_t *connect_flags_pos;
277 int err_code;
278 uint8_t *start;
279
280 if (client->protocol_version == MQTT_VERSION_3_1_1) {
281 mqtt_proto_desc = &mqtt_3_1_1_proto_desc;
282 } else {
283 mqtt_proto_desc = &mqtt_3_1_0_proto_desc;
284 }
285
286 /* Reserve space for fixed header. */
287 buf->cur += MQTT_FIXED_HEADER_MAX_SIZE;
288 start = buf->cur;
289
290 MQTT_HEXDUMP_TRC(mqtt_proto_desc->utf8, mqtt_proto_desc->size,
291 "Encoding Protocol Description.");
292
293 err_code = pack_utf8_str(mqtt_proto_desc, buf);
294 if (err_code != 0) {
295 return err_code;
296 }
297
298 MQTT_TRC("Encoding Protocol Version %02x.", client->protocol_version);
299 err_code = pack_uint8(client->protocol_version, buf);
300 if (err_code != 0) {
301 return err_code;
302 }
303
304 /* Remember position of connect flag and leave one byte for it to
305 * be packed once we determine its value.
306 */
307 connect_flags_pos = buf->cur;
308
309 err_code = pack_uint8(0, buf);
310 if (err_code != 0) {
311 return err_code;
312 }
313
314 MQTT_TRC("Encoding Keep Alive Time %04x.", client->keepalive);
315 err_code = pack_uint16(client->keepalive, buf);
316 if (err_code != 0) {
317 return err_code;
318 }
319
320 MQTT_HEXDUMP_TRC(client->client_id.utf8, client->client_id.size,
321 "Encoding Client Id.");
322 err_code = pack_utf8_str(&client->client_id, buf);
323 if (err_code != 0) {
324 return err_code;
325 }
326
327 /* Pack will topic and QoS */
328 if (client->will_topic != NULL) {
329 connect_flags |= MQTT_CONNECT_FLAG_WILL_TOPIC;
330 /* QoS is always 1 as of now. */
331 connect_flags |= ((client->will_topic->qos & 0x03) << 3);
332 connect_flags |= client->will_retain << 5;
333
334 MQTT_HEXDUMP_TRC(client->will_topic->topic.utf8,
335 client->will_topic->topic.size,
336 "Encoding Will Topic.");
337 err_code = pack_utf8_str(&client->will_topic->topic, buf);
338 if (err_code != 0) {
339 return err_code;
340 }
341
342 if (client->will_message != NULL) {
343 MQTT_HEXDUMP_TRC(client->will_message->utf8,
344 client->will_message->size,
345 "Encoding Will Message.");
346 err_code = pack_utf8_str(client->will_message, buf);
347 if (err_code != 0) {
348 return err_code;
349 }
350 } else {
351 MQTT_TRC("Encoding Zero Length Will Message.");
352 err_code = zero_len_str_encode(buf);
353 if (err_code != 0) {
354 return err_code;
355 }
356 }
357 }
358
359 /* Pack Username if any. */
360 if (client->user_name != NULL) {
361 connect_flags |= MQTT_CONNECT_FLAG_USERNAME;
362
363 MQTT_HEXDUMP_TRC(client->user_name->utf8,
364 client->user_name->size,
365 "Encoding Username.");
366 err_code = pack_utf8_str(client->user_name, buf);
367 if (err_code != 0) {
368 return err_code;
369 }
370 }
371
372 /* Pack Password if any. */
373 if (client->password != NULL) {
374 connect_flags |= MQTT_CONNECT_FLAG_PASSWORD;
375
376 MQTT_HEXDUMP_TRC(client->password->utf8,
377 client->password->size,
378 "Encoding Password.");
379 err_code = pack_utf8_str(client->password, buf);
380 if (err_code != 0) {
381 return err_code;
382 }
383 }
384
385 /* Write the flags the connect flags. */
386 *connect_flags_pos = connect_flags;
387
388 return mqtt_encode_fixed_header(message_type, start, buf);
389 }
390
publish_encode(const struct mqtt_publish_param * param,struct buf_ctx * buf)391 int publish_encode(const struct mqtt_publish_param *param, struct buf_ctx *buf)
392 {
393 const uint8_t message_type = MQTT_MESSAGES_OPTIONS(
394 MQTT_PKT_TYPE_PUBLISH, param->dup_flag,
395 param->message.topic.qos, param->retain_flag);
396 int err_code;
397 uint8_t *start;
398
399 /* Message id zero is not permitted by spec. */
400 if ((param->message.topic.qos) && (param->message_id == 0U)) {
401 return -EINVAL;
402 }
403
404 /* Reserve space for fixed header. */
405 buf->cur += MQTT_FIXED_HEADER_MAX_SIZE;
406 start = buf->cur;
407
408 err_code = pack_utf8_str(¶m->message.topic.topic, buf);
409 if (err_code != 0) {
410 return err_code;
411 }
412
413 if (param->message.topic.qos) {
414 err_code = pack_uint16(param->message_id, buf);
415 if (err_code != 0) {
416 return err_code;
417 }
418 }
419
420 /* Do not copy payload. We move the buffer pointer to ensure that
421 * message length in fixed header is encoded correctly.
422 */
423 buf->cur += param->message.payload.len;
424
425 err_code = mqtt_encode_fixed_header(message_type, start, buf);
426 if (err_code != 0) {
427 return err_code;
428 }
429
430 buf->end -= param->message.payload.len;
431
432 return 0;
433 }
434
publish_ack_encode(const struct mqtt_puback_param * param,struct buf_ctx * buf)435 int publish_ack_encode(const struct mqtt_puback_param *param,
436 struct buf_ctx *buf)
437 {
438 const uint8_t message_type =
439 MQTT_MESSAGES_OPTIONS(MQTT_PKT_TYPE_PUBACK, 0, 0, 0);
440
441 return mqtt_message_id_only_enc(message_type, param->message_id, buf);
442 }
443
publish_receive_encode(const struct mqtt_pubrec_param * param,struct buf_ctx * buf)444 int publish_receive_encode(const struct mqtt_pubrec_param *param,
445 struct buf_ctx *buf)
446 {
447 const uint8_t message_type =
448 MQTT_MESSAGES_OPTIONS(MQTT_PKT_TYPE_PUBREC, 0, 0, 0);
449
450 return mqtt_message_id_only_enc(message_type, param->message_id, buf);
451 }
452
publish_release_encode(const struct mqtt_pubrel_param * param,struct buf_ctx * buf)453 int publish_release_encode(const struct mqtt_pubrel_param *param,
454 struct buf_ctx *buf)
455 {
456 const uint8_t message_type =
457 MQTT_MESSAGES_OPTIONS(MQTT_PKT_TYPE_PUBREL, 0, 1, 0);
458
459 return mqtt_message_id_only_enc(message_type, param->message_id, buf);
460 }
461
publish_complete_encode(const struct mqtt_pubcomp_param * param,struct buf_ctx * buf)462 int publish_complete_encode(const struct mqtt_pubcomp_param *param,
463 struct buf_ctx *buf)
464 {
465 const uint8_t message_type =
466 MQTT_MESSAGES_OPTIONS(MQTT_PKT_TYPE_PUBCOMP, 0, 0, 0);
467
468 return mqtt_message_id_only_enc(message_type, param->message_id, buf);
469 }
470
disconnect_encode(struct buf_ctx * buf)471 int disconnect_encode(struct buf_ctx *buf)
472 {
473 if (buf->end - buf->cur < sizeof(disc_packet)) {
474 return -ENOMEM;
475 }
476
477 memcpy(buf->cur, disc_packet, sizeof(disc_packet));
478 buf->end = buf->cur + sizeof(disc_packet);
479
480 return 0;
481 }
482
subscribe_encode(const struct mqtt_subscription_list * param,struct buf_ctx * buf)483 int subscribe_encode(const struct mqtt_subscription_list *param,
484 struct buf_ctx *buf)
485 {
486 const uint8_t message_type = MQTT_MESSAGES_OPTIONS(
487 MQTT_PKT_TYPE_SUBSCRIBE, 0, 1, 0);
488 int err_code, i;
489 uint8_t *start;
490
491 /* Message id zero is not permitted by spec. */
492 if (param->message_id == 0U) {
493 return -EINVAL;
494 }
495
496 /* Reserve space for fixed header. */
497 buf->cur += MQTT_FIXED_HEADER_MAX_SIZE;
498 start = buf->cur;
499
500 err_code = pack_uint16(param->message_id, buf);
501 if (err_code != 0) {
502 return err_code;
503 }
504
505 for (i = 0; i < param->list_count; i++) {
506 err_code = pack_utf8_str(¶m->list[i].topic, buf);
507 if (err_code != 0) {
508 return err_code;
509 }
510
511 err_code = pack_uint8(param->list[i].qos, buf);
512 if (err_code != 0) {
513 return err_code;
514 }
515 }
516
517 return mqtt_encode_fixed_header(message_type, start, buf);
518 }
519
unsubscribe_encode(const struct mqtt_subscription_list * param,struct buf_ctx * buf)520 int unsubscribe_encode(const struct mqtt_subscription_list *param,
521 struct buf_ctx *buf)
522 {
523 const uint8_t message_type = MQTT_MESSAGES_OPTIONS(
524 MQTT_PKT_TYPE_UNSUBSCRIBE, 0, MQTT_QOS_1_AT_LEAST_ONCE, 0);
525 int err_code, i;
526 uint8_t *start;
527
528 /* Reserve space for fixed header. */
529 buf->cur += MQTT_FIXED_HEADER_MAX_SIZE;
530 start = buf->cur;
531
532 err_code = pack_uint16(param->message_id, buf);
533 if (err_code != 0) {
534 return err_code;
535 }
536
537 for (i = 0; i < param->list_count; i++) {
538 err_code = pack_utf8_str(¶m->list[i].topic, buf);
539 if (err_code != 0) {
540 return err_code;
541 }
542 }
543
544 return mqtt_encode_fixed_header(message_type, start, buf);
545 }
546
ping_request_encode(struct buf_ctx * buf)547 int ping_request_encode(struct buf_ctx *buf)
548 {
549 if (buf->end - buf->cur < sizeof(ping_packet)) {
550 return -ENOMEM;
551 }
552
553 memcpy(buf->cur, ping_packet, sizeof(ping_packet));
554 buf->end = buf->cur + sizeof(ping_packet);
555
556 return 0;
557 }
558