blob: ebdb57d4e34e72670df77c9bdb851c676497c350 [file] [log] [blame]
Baptiste Assmanne279ca62020-10-27 18:10:06 +01001/*
2 * MQTT Protocol
3 *
4 * Copyright 2020 Baptiste Assmann <bedis9@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <haproxy/chunk.h>
14#include <haproxy/mqtt.h>
15
16uint8_t mqtt_cpt_flags[MQTT_CPT_ENTRIES] = {
17 [MQTT_CPT_INVALID] = 0x00,
18 [MQTT_CPT_CONNECT] = 0x00,
19 [MQTT_CPT_CONNACK] = 0x00,
20
21 /* MQTT_CPT_PUBLISH flags can have different values (DUP, QoS, RETAIN), must be
22 * check more carefully
23 */
24 [MQTT_CPT_PUBLISH] = 0x0F,
25
26 [MQTT_CPT_PUBACK] = 0x00,
27 [MQTT_CPT_PUBREC] = 0x00,
28 [MQTT_CPT_PUBREL] = 0x02,
29 [MQTT_CPT_PUBCOMP] = 0x00,
30 [MQTT_CPT_SUBSCRIBE] = 0x02,
31 [MQTT_CPT_SUBACK] = 0x00,
32 [MQTT_CPT_UNSUBSCRIBE] = 0x02,
33 [MQTT_CPT_UNSUBACK] = 0x00,
34 [MQTT_CPT_PINGREQ] = 0x00,
35 [MQTT_CPT_PINGRESP] = 0x00,
36 [MQTT_CPT_DISCONNECT] = 0x00,
37 [MQTT_CPT_AUTH] = 0x00,
38};
39
40const struct ist mqtt_fields_string[MQTT_FN_ENTRIES] = {
41 [MQTT_FN_INVALID] = IST(""),
42
43 /* it's MQTT 3.1.1 and 5.0, those fields have no unique id, so we use strings */
44 [MQTT_FN_FLAGS] = IST("flags"),
45 [MQTT_FN_REASON_CODE] = IST("reason_code"), /* MQTT 3.1.1: return_code */
46 [MQTT_FN_PROTOCOL_NAME] = IST("protocol_name"),
47 [MQTT_FN_PROTOCOL_VERSION] = IST("protocol_version"), /* MQTT 3.1.1: protocol_level */
48 [MQTT_FN_CLIENT_IDENTIFIER] = IST("client_identifier"),
49 [MQTT_FN_WILL_TOPIC] = IST("will_topic"),
50 [MQTT_FN_WILL_PAYLOAD] = IST("will_payload"), /* MQTT 3.1.1: will_message */
51 [MQTT_FN_USERNAME] = IST("username"),
52 [MQTT_FN_PASSWORD] = IST("password"),
53 [MQTT_FN_KEEPALIVE] = IST("keepalive"),
54 /* from here, it's MQTT 5.0 only */
55 [MQTT_FN_PAYLOAD_FORMAT_INDICATOR] = IST("1"),
56 [MQTT_FN_MESSAGE_EXPIRY_INTERVAL] = IST("2"),
57 [MQTT_FN_CONTENT_TYPE] = IST("3"),
58 [MQTT_FN_RESPONSE_TOPIC] = IST("8"),
59 [MQTT_FN_CORRELATION_DATA] = IST("9"),
60 [MQTT_FN_SUBSCRIPTION_IDENTIFIER] = IST("11"),
61 [MQTT_FN_SESSION_EXPIRY_INTERVAL] = IST("17"),
62 [MQTT_FN_ASSIGNED_CLIENT_IDENTIFIER] = IST("18"),
63 [MQTT_FN_SERVER_KEEPALIVE] = IST("19"),
64 [MQTT_FN_AUTHENTICATION_METHOD] = IST("21"),
65 [MQTT_FN_AUTHENTICATION_DATA] = IST("22"),
66 [MQTT_FN_REQUEST_PROBLEM_INFORMATION] = IST("23"),
67 [MQTT_FN_DELAY_INTERVAL] = IST("24"),
68 [MQTT_FN_REQUEST_RESPONSE_INFORMATION] = IST("25"),
69 [MQTT_FN_RESPONSE_INFORMATION] = IST("26"),
70 [MQTT_FN_SERVER_REFERENCE] = IST("28"),
71 [MQTT_FN_REASON_STRING] = IST("31"),
72 [MQTT_FN_RECEIVE_MAXIMUM] = IST("33"),
73 [MQTT_FN_TOPIC_ALIAS_MAXIMUM] = IST("34"),
74 [MQTT_FN_TOPIC_ALIAS] = IST("35"),
75 [MQTT_FN_MAXIMUM_QOS] = IST("36"),
76 [MQTT_FN_RETAIN_AVAILABLE] = IST("37"),
77 [MQTT_FN_USER_PROPERTY] = IST("38"),
78 [MQTT_FN_MAXIMUM_PACKET_SIZE] = IST("39"),
79 [MQTT_FN_WILDCARD_SUBSCRIPTION_AVAILABLE] = IST("40"),
80 [MQTT_FN_SUBSCRIPTION_IDENTIFIERS_AVAILABLE] = IST("41"),
81 [MQTT_FN_SHARED_SUBSCRIPTION_AVAILABLE] = IST("42"),
82};
83
84/* list of supported capturable field names for each MQTT control packet type */
85const uint64_t mqtt_fields_per_packet[MQTT_CPT_ENTRIES] = {
86 [MQTT_CPT_INVALID] = 0,
87
88 [MQTT_CPT_CONNECT] = MQTT_FN_BIT_PROTOCOL_NAME | MQTT_FN_BIT_PROTOCOL_VERSION |
89 MQTT_FN_BIT_FLAGS | MQTT_FN_BIT_KEEPALIVE |
90 MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL | MQTT_FN_BIT_RECEIVE_MAXIMUM |
91 MQTT_FN_BIT_MAXIMUM_PACKET_SIZE | MQTT_FN_BIT_TOPIC_ALIAS_MAXIMUM |
92 MQTT_FN_BIT_REQUEST_RESPONSE_INFORMATION | MQTT_FN_BIT_REQUEST_PROBLEM_INFORMATION |
93 MQTT_FN_BIT_USER_PROPERTY | MQTT_FN_BIT_AUTHENTICATION_METHOD |
94 MQTT_FN_BIT_AUTHENTICATION_DATA | MQTT_FN_BIT_CLIENT_IDENTIFIER |
95 MQTT_FN_BIT_DELAY_INTERVAL | MQTT_FN_BIT_PAYLOAD_FORMAT_INDICATOR |
96 MQTT_FN_BIT_MESSAGE_EXPIRY_INTERVAL | MQTT_FN_BIT_CONTENT_TYPE |
97 MQTT_FN_BIT_RESPONSE_TOPIC | MQTT_FN_BIT_CORRELATION_DATA |
98 MQTT_FN_BIT_USER_PROPERTY | MQTT_FN_BIT_WILL_TOPIC |
99 MQTT_FN_BIT_WILL_PAYLOAD | MQTT_FN_BIT_USERNAME |
100 MQTT_FN_BIT_PASSWORD,
101
102 [MQTT_CPT_CONNACK] = MQTT_FN_BIT_FLAGS | MQTT_FN_BIT_PROTOCOL_VERSION |
103 MQTT_FN_BIT_REASON_CODE | MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL |
104 MQTT_FN_BIT_RECEIVE_MAXIMUM | MQTT_FN_BIT_MAXIMUM_QOS |
105 MQTT_FN_BIT_RETAIN_AVAILABLE | MQTT_FN_BIT_MAXIMUM_PACKET_SIZE |
106 MQTT_FN_BIT_ASSIGNED_CLIENT_IDENTIFIER | MQTT_FN_BIT_TOPIC_ALIAS_MAXIMUM |
107 MQTT_FN_BIT_REASON_STRING | MQTT_FN_BIT_WILDCARD_SUBSCRIPTION_AVAILABLE |
108 MQTT_FN_BIT_SUBSCRIPTION_IDENTIFIERS_AVAILABLE| MQTT_FN_BIT_SHARED_SUBSCRIPTION_AVAILABLE |
109 MQTT_FN_BIT_SERVER_KEEPALIVE | MQTT_FN_BIT_RESPONSE_INFORMATION |
110 MQTT_FN_BIT_SERVER_REFERENCE | MQTT_FN_BIT_USER_PROPERTY |
111 MQTT_FN_BIT_AUTHENTICATION_METHOD | MQTT_FN_BIT_AUTHENTICATION_DATA,
112
113 [MQTT_CPT_PUBLISH] = MQTT_FN_BIT_PAYLOAD_FORMAT_INDICATOR | MQTT_FN_BIT_MESSAGE_EXPIRY_INTERVAL |
114 MQTT_FN_BIT_CONTENT_TYPE | MQTT_FN_BIT_RESPONSE_TOPIC |
115 MQTT_FN_BIT_CORRELATION_DATA | MQTT_FN_BIT_SUBSCRIPTION_IDENTIFIER |
116 MQTT_FN_BIT_TOPIC_ALIAS | MQTT_FN_BIT_USER_PROPERTY,
117
118 [MQTT_CPT_PUBACK] = MQTT_FN_BIT_REASON_CODE | MQTT_FN_BIT_REASON_STRING | MQTT_FN_BIT_USER_PROPERTY,
119
120 [MQTT_CPT_PUBREC] = MQTT_FN_BIT_REASON_CODE | MQTT_FN_BIT_REASON_STRING | MQTT_FN_BIT_USER_PROPERTY,
121
122 [MQTT_CPT_PUBREL] = MQTT_FN_BIT_REASON_CODE | MQTT_FN_BIT_REASON_STRING | MQTT_FN_BIT_USER_PROPERTY,
123
124 [MQTT_CPT_PUBCOMP] = MQTT_FN_BIT_REASON_CODE | MQTT_FN_BIT_REASON_STRING | MQTT_FN_BIT_USER_PROPERTY,
125
126 [MQTT_CPT_SUBSCRIBE] = MQTT_FN_BIT_SUBSCRIPTION_IDENTIFIER | MQTT_FN_BIT_USER_PROPERTY,
127
128 [MQTT_CPT_SUBACK] = MQTT_FN_BIT_REASON_STRING | MQTT_FN_BIT_USER_PROPERTY,
129
130 [MQTT_CPT_UNSUBSCRIBE] = MQTT_FN_BIT_USER_PROPERTY,
131
132 [MQTT_CPT_UNSUBACK] = MQTT_FN_BIT_REASON_STRING | MQTT_FN_BIT_USER_PROPERTY,
133
134 [MQTT_CPT_PINGREQ] = 0,
135
136 [MQTT_CPT_PINGRESP] = 0,
137
138 [MQTT_CPT_DISCONNECT] = MQTT_FN_BIT_REASON_CODE | MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL |
139 MQTT_FN_BIT_SERVER_REFERENCE | MQTT_FN_BIT_REASON_STRING |
140 MQTT_FN_BIT_USER_PROPERTY,
141
142 [MQTT_CPT_AUTH] = MQTT_FN_BIT_AUTHENTICATION_METHOD | MQTT_FN_BIT_AUTHENTICATION_DATA |
143 MQTT_FN_BIT_REASON_STRING | MQTT_FN_BIT_USER_PROPERTY,
144};
145
146/* Checks the first byte of a message to read the fixed header and extract the
147 * packet type and flags. <parser> is supposed to point to the fix header byte.
148 *
149 * Fix header looks like:
150 * +-------+-----------+-----------+-----------+---------+----------+----------+---------+------------+
151 * | bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
152 * +-------+-----------+-----------+-----------+---------+----------+----------+---------+------------+
153 * | field | MQTT Control Packet Type | Flags specific to each Control Packet type |
154 * +-------+---------------------------------------------+--------------------------------------------+
155 *
156 * On success, <ptk> is updated with the packet type and flags and the new parser
157 * state is returned. On error, IST_NULL is returned.
158 */
159static inline struct ist mqtt_read_fixed_hdr(struct ist parser, struct mqtt_pkt *pkt)
160{
161 uint8_t type = (uint8_t)*istptr(parser);
162 uint8_t ptype = (type & 0xF0) >> 4;
163 uint8_t flags = type & 0x0F;
164
165 if (ptype == MQTT_CPT_INVALID || ptype >= MQTT_CPT_ENTRIES || flags != mqtt_cpt_flags[ptype])
166 return IST_NULL;
167
168 pkt->fixed_hdr.type = ptype;
169 pkt->fixed_hdr.flags = flags;
170 return istnext(parser);
171}
172
173/* Reads a one byte integer. more information here :
174 * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901007
175 *
176 * <parser> is supposed to point to the first byte of the integer. On success
177 * the integer is stored in <*i>, if provided, and the new parser state is returned. On
178 * error, IST_NULL is returned.
179*/
180static inline struct ist mqtt_read_1byte_int(struct ist parser, uint8_t *i)
181{
182 if (istlen(parser) < 1)
183 return IST_NULL;
184 if (i)
185 *i = (uint8_t)*istptr(parser);
Tim Duesterhus284fbe12021-11-04 22:35:44 +0100186 parser = istnext(parser);
Baptiste Assmanne279ca62020-10-27 18:10:06 +0100187 return parser;
188}
189
190/* Reads a two byte integer. more information here :
191 * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901008
192 *
193 * <parser> is supposed to point to the first byte of the integer. On success
194 * the integer is stored in <*i>, if provided, and the new parser state is returned. On
195 * error, IST_NULL is returned.
196*/
197static inline struct ist mqtt_read_2byte_int(struct ist parser, uint16_t *i)
198{
199 if (istlen(parser) < 2)
200 return IST_NULL;
201 if (i) {
202 *i = (uint8_t)*istptr(parser) << 8;
203 *i += (uint8_t)*(istptr(parser) + 1);
204 }
205 parser = istadv(parser, 2);
206 return parser;
207}
208
209/* Reads a four byte integer. more information here :
210 * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901009
211 *
212 * <parser> is supposed to point to the first byte of the integer. On success
213 * the integer is stored in <*i>, if provided, and the new parser state is returned. On
214 * error, IST_NULL is returned.
215*/
216static inline struct ist mqtt_read_4byte_int(struct ist parser, uint32_t *i)
217{
218 if (istlen(parser) < 4)
219 return IST_NULL;
220 if (i) {
221 *i = (uint8_t)*istptr(parser) << 24;
222 *i += (uint8_t)*(istptr(parser) + 1) << 16;
223 *i += (uint8_t)*(istptr(parser) + 2) << 8;
224 *i += (uint8_t)*(istptr(parser) + 3);
225 }
226 parser = istadv(parser, 4);
227 return parser;
228}
229
230/* Reads a variable byte integer. more information here :
231 * https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718023
232 * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901011
233 *
234 * It is encoded using a variable length encoding scheme which uses a single
235 * byte for values up to 127. Larger values are handled as follows. The least
236 * significant seven bits of each byte encode the data, and the most significant
237 * bit is used to indicate that there are following bytes in the representation.
238 * Thus each byte encodes 128 values and a "continuation bit".
239 *
240 * The maximum number of bytes in the Remaining Length field is four
241 * (MQTT_REMAINING_LENGHT_MAX_SIZE).
242 *
243 * <parser> is supposed to point to the first byte of the integer. On success
244 * the integer is stored in <*i> and the new parser state is returned. On
245 * error, IST_NULL is returned.
246 */
247static inline struct ist mqtt_read_varint(struct ist parser, uint32_t *i)
248{
249 int off, m;
250
251 off = m = 0;
252 if (i)
253 *i = 0;
254 for (off = 0; off < MQTT_REMAINING_LENGHT_MAX_SIZE && istlen(parser); off++) {
255 uint8_t byte = (uint8_t)*istptr(parser);
256
257 if (i) {
258 *i += (byte & 127) << m;
259 m += 7; /* preparing <m> for next byte */
260 }
261 parser = istnext(parser);
262
263 /* we read the latest byte for the remaining length field */
264 if (byte <= 127)
265 break;
266 }
267
268 if (off == MQTT_REMAINING_LENGHT_MAX_SIZE)
269 return IST_NULL;
270 return parser;
271}
272
273/* Reads a MQTT string. more information here :
274 * http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718016
275 * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901010
276 *
277 * In MQTT, strings are prefixed by their size, encoded over 2 bytes:
278 * byte 1: length MSB
279 * byte 2: length LSB
280 * byte 3: string
281 * ...
282 *
283 * string size is MSB * 256 + LSB
284 *
285 * <parser> is supposed to point to the first byte of the string. On success the
286 * string is stored in <*str>, if provided, and the new parser state is
287 * returned. On error, IST_NULL is returned.
288 */
289static inline struct ist mqtt_read_string(struct ist parser, struct ist *str)
290{
Christopher Fauletca925c92021-06-28 15:26:00 +0200291 uint16_t len = 0;
Baptiste Assmanne279ca62020-10-27 18:10:06 +0100292
293 /* read and compute the string length */
Christopher Faulet0de0bec2021-06-28 15:37:59 +0200294 if (istlen(parser) < 2)
Baptiste Assmanne279ca62020-10-27 18:10:06 +0100295 goto error;
296
Christopher Fauletca925c92021-06-28 15:26:00 +0200297 parser = mqtt_read_2byte_int(parser, &len);
298 if (!isttest(parser) || istlen(parser) < len)
Baptiste Assmanne279ca62020-10-27 18:10:06 +0100299 goto error;
300
301 if (str) {
302 str->ptr = istptr(parser);
303 str->len = len;
304 }
305
306 return istadv(parser, len);
307
308 error:
309 return IST_NULL;
310}
311
312/* Helper function to convert a unsigned integer to a string. The result is
313 * written in <buf>. On success, the written size is returned, otherwise, on
314 * error, 0 is returned.
315 */
316static inline size_t mqtt_uint2str(struct buffer *buf, uint32_t i)
317{
318 char *end;
319
320 end = ultoa_o(i, buf->area, buf->size);
321 if (!end)
322 return 0;
323 buf->data = end - buf->area;
324 return buf->data;
325}
326
327/* Extracts the value of a <fieldname_id> of type <type> from a given MQTT
Ilya Shipitsinf38a0182020-12-21 01:16:17 +0500328 * message <msg>. IST_NULL is returned if an error occurred while parsing or if
Baptiste Assmanne279ca62020-10-27 18:10:06 +0100329 * the field could not be found. If more data are required, the message with a
330 * length set to 0 is returned. If the field is found, the response is returned
331 * as a struct ist.
332 */
333struct ist mqtt_field_value(struct ist msg, int type, int fieldname_id)
334{
335 struct buffer *trash = get_trash_chunk();
336 struct mqtt_pkt mpkt;
337 struct ist res;
338
339 switch (mqtt_validate_message(msg, &mpkt)) {
340 case MQTT_VALID_MESSAGE:
341 if (mpkt.fixed_hdr.type != type)
342 goto not_found_or_invalid;
343 break;
344 case MQTT_NEED_MORE_DATA:
345 goto need_more;
346 case MQTT_INVALID_MESSAGE:
347 goto not_found_or_invalid;
348 }
349
350 switch (type) {
351 case MQTT_CPT_CONNECT:
352 switch (fieldname_id) {
353 case MQTT_FN_FLAGS:
354 if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.flags))
355 goto not_found_or_invalid;
356 res = ist2(trash->area, trash->data);
357 goto end;
358
359 case MQTT_FN_PROTOCOL_NAME:
360 if (!istlen(mpkt.data.connect.var_hdr.protocol_name))
361 goto not_found_or_invalid;
362 res = mpkt.data.connect.var_hdr.protocol_name;
363 goto end;
364
365 case MQTT_FN_PROTOCOL_VERSION:
366 if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.protocol_version))
367 goto not_found_or_invalid;
368 res = ist2(trash->area, trash->data);
369 goto end;
370
371 case MQTT_FN_CLIENT_IDENTIFIER:
372 if (!istlen(mpkt.data.connect.payload.client_identifier))
373 goto not_found_or_invalid;
374 res = mpkt.data.connect.payload.client_identifier;
375 goto end;
376
377 case MQTT_FN_WILL_TOPIC:
378 if (!istlen(mpkt.data.connect.payload.will_topic))
379 goto not_found_or_invalid;
380 res = mpkt.data.connect.payload.will_topic;
381 goto end;
382
383 case MQTT_FN_WILL_PAYLOAD:
384 if (!istlen(mpkt.data.connect.payload.will_payload))
385 goto not_found_or_invalid;
386 res = mpkt.data.connect.payload.will_payload;
387 goto end;
388
389 case MQTT_FN_USERNAME:
390 if (!istlen(mpkt.data.connect.payload.username))
391 goto not_found_or_invalid;
392 res = mpkt.data.connect.payload.username;
393 goto end;
394
395 case MQTT_FN_PASSWORD:
396 if (!istlen(mpkt.data.connect.payload.password))
397 goto not_found_or_invalid;
398 res = mpkt.data.connect.payload.password;
399 goto end;
400
401 case MQTT_FN_KEEPALIVE:
402 if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.keepalive))
403 goto not_found_or_invalid;
404 res = ist2(trash->area, trash->data);
405 goto end;
406
407 case MQTT_FN_PAYLOAD_FORMAT_INDICATOR:
408 if ((mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) ||
409 !(mpkt.data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL))
410 goto not_found_or_invalid;
411 if (!mqtt_uint2str(trash, mpkt.data.connect.payload.will_props.payload_format_indicator))
412 goto not_found_or_invalid;
413 res = ist2(trash->area, trash->data);
414 goto end;
415
416 case MQTT_FN_MESSAGE_EXPIRY_INTERVAL:
417 if ((mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) ||
418 !(mpkt.data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL))
419 goto not_found_or_invalid;
420 if (!mqtt_uint2str(trash, mpkt.data.connect.payload.will_props.message_expiry_interval))
421 goto not_found_or_invalid;
422 res = ist2(trash->area, trash->data);
423 goto end;
424
425 case MQTT_FN_CONTENT_TYPE:
426 if ((mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) ||
427 !(mpkt.data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL))
428 goto not_found_or_invalid;
429 if (!istlen(mpkt.data.connect.payload.will_props.content_type))
430 goto not_found_or_invalid;
431 res = mpkt.data.connect.payload.will_props.content_type;
432 goto end;
433
434 case MQTT_FN_RESPONSE_TOPIC:
435 if ((mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) ||
436 !(mpkt.data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL))
437 goto not_found_or_invalid;
438 if (!istlen(mpkt.data.connect.payload.will_props.response_topic))
439 goto not_found_or_invalid;
440 res = mpkt.data.connect.payload.will_props.response_topic;
441 goto end;
442
443 case MQTT_FN_CORRELATION_DATA:
444 if ((mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) ||
445 !(mpkt.data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL))
446 goto not_found_or_invalid;
447 if (!istlen(mpkt.data.connect.payload.will_props.correlation_data))
448 goto not_found_or_invalid;
449 res = mpkt.data.connect.payload.will_props.correlation_data;
450 goto end;
451
452 case MQTT_FN_SESSION_EXPIRY_INTERVAL:
453 if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
454 goto not_found_or_invalid;
455 if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.props.session_expiry_interval))
456 goto not_found_or_invalid;
457 res = ist2(trash->area, trash->data);
458 goto end;
459
460 case MQTT_FN_AUTHENTICATION_METHOD:
461 if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
462 goto not_found_or_invalid;
463 if (!istlen(mpkt.data.connect.var_hdr.props.authentication_method))
464 goto not_found_or_invalid;
465 res = mpkt.data.connect.var_hdr.props.authentication_method;
466 goto end;
467
468 case MQTT_FN_AUTHENTICATION_DATA:
469 if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
470 goto not_found_or_invalid;
471 if (!istlen(mpkt.data.connect.var_hdr.props.authentication_data))
472 goto not_found_or_invalid;
473 res = mpkt.data.connect.var_hdr.props.authentication_data;
474 goto end;
475
476 case MQTT_FN_REQUEST_PROBLEM_INFORMATION:
477 if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
478 goto not_found_or_invalid;
479 if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.props.request_problem_information))
480 goto not_found_or_invalid;
481 res = ist2(trash->area, trash->data);
482 goto end;
483
484 case MQTT_FN_DELAY_INTERVAL:
485 if ((mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) ||
486 !(mpkt.data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL))
487 goto not_found_or_invalid;
488 if (!mqtt_uint2str(trash, mpkt.data.connect.payload.will_props.delay_interval))
489 goto not_found_or_invalid;
490 res = ist2(trash->area, trash->data);
491 goto end;
492
493 case MQTT_FN_REQUEST_RESPONSE_INFORMATION:
494 if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
495 goto not_found_or_invalid;
496 if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.props.request_response_information))
497 goto not_found_or_invalid;
498 res = ist2(trash->area, trash->data);
499 goto end;
500
501 case MQTT_FN_RECEIVE_MAXIMUM:
502 if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
503 goto not_found_or_invalid;
504 if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.props.receive_maximum))
505 goto not_found_or_invalid;
506 res = ist2(trash->area, trash->data);
507 goto end;
508
509 case MQTT_FN_TOPIC_ALIAS_MAXIMUM:
510 if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
511 goto not_found_or_invalid;
512 if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.props.topic_alias_maximum))
513 goto not_found_or_invalid;
514 res = ist2(trash->area, trash->data);
515 goto end;
516
517 case MQTT_FN_MAXIMUM_PACKET_SIZE:
518 if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
519 goto not_found_or_invalid;
520 if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.props.maximum_packet_size))
521 goto not_found_or_invalid;
522 res = ist2(trash->area, trash->data);
523 goto end;
524
525 default:
526 goto not_found_or_invalid;
527 }
528 break;
529
530 case MQTT_CPT_CONNACK:
531 switch (fieldname_id) {
532 case MQTT_FN_FLAGS:
533 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.flags))
534 goto not_found_or_invalid;
535 res = ist2(trash->area, trash->data);
536 goto end;
537
538 case MQTT_FN_REASON_CODE:
539 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.reason_code))
540 goto not_found_or_invalid;
541 res = ist2(trash->area, trash->data);
542 goto end;
543
544 case MQTT_FN_PROTOCOL_VERSION:
545 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.protocol_version))
546 goto not_found_or_invalid;
547 res = ist2(trash->area, trash->data);
548 goto end;
549
550 case MQTT_FN_SESSION_EXPIRY_INTERVAL:
551 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
552 goto not_found_or_invalid;
553 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.session_expiry_interval))
554 goto not_found_or_invalid;
555 res = ist2(trash->area, trash->data);
556 goto end;
557
558 case MQTT_FN_ASSIGNED_CLIENT_IDENTIFIER:
559 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
560 goto not_found_or_invalid;
561 if (!istlen(mpkt.data.connack.var_hdr.props.assigned_client_identifier))
562 goto not_found_or_invalid;
563 res = mpkt.data.connack.var_hdr.props.assigned_client_identifier;
564 goto end;
565
566 case MQTT_FN_SERVER_KEEPALIVE:
567 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
568 goto not_found_or_invalid;
569 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.server_keepalive))
570 goto not_found_or_invalid;
571 res = ist2(trash->area, trash->data);
572 goto end;
573
574 case MQTT_FN_AUTHENTICATION_METHOD:
575 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
576 goto not_found_or_invalid;
577 if (!istlen(mpkt.data.connack.var_hdr.props.authentication_method))
578 goto not_found_or_invalid;
579 res = mpkt.data.connack.var_hdr.props.authentication_method;
580 goto end;
581
582 case MQTT_FN_AUTHENTICATION_DATA:
583 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
584 goto not_found_or_invalid;
585 if (!istlen(mpkt.data.connack.var_hdr.props.authentication_data))
586 goto not_found_or_invalid;
587 res = mpkt.data.connack.var_hdr.props.authentication_data;
588 goto end;
589
590 case MQTT_FN_RESPONSE_INFORMATION:
591 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
592 goto not_found_or_invalid;
593 if (!istlen(mpkt.data.connack.var_hdr.props.response_information))
594 goto not_found_or_invalid;
595 res = mpkt.data.connack.var_hdr.props.response_information;
596 goto end;
597
598 case MQTT_FN_SERVER_REFERENCE:
599 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
600 goto not_found_or_invalid;
601 if (!istlen(mpkt.data.connack.var_hdr.props.server_reference))
602 goto not_found_or_invalid;
603 res = mpkt.data.connack.var_hdr.props.server_reference;
604 goto end;
605
606 case MQTT_FN_REASON_STRING:
607 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
608 goto not_found_or_invalid;
609 if (!istlen(mpkt.data.connack.var_hdr.props.reason_string))
610 goto not_found_or_invalid;
611 res = mpkt.data.connack.var_hdr.props.reason_string;
612 goto end;
613
614 case MQTT_FN_RECEIVE_MAXIMUM:
615 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
616 goto not_found_or_invalid;
617 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.receive_maximum))
618 goto not_found_or_invalid;
619 res = ist2(trash->area, trash->data);
620 goto end;
621
622 case MQTT_FN_TOPIC_ALIAS_MAXIMUM:
623 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
624 goto not_found_or_invalid;
625 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.topic_alias_maximum))
626 goto not_found_or_invalid;
627 res = ist2(trash->area, trash->data);
628 goto end;
629
630 case MQTT_FN_MAXIMUM_QOS:
631 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
632 goto not_found_or_invalid;
633 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.maximum_qos))
634 goto not_found_or_invalid;
635 res = ist2(trash->area, trash->data);
636 goto end;
637
638 case MQTT_FN_RETAIN_AVAILABLE:
639 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
640 goto not_found_or_invalid;
641 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.retain_available))
642 goto not_found_or_invalid;
643 res = ist2(trash->area, trash->data);
644 goto end;
645
646 case MQTT_FN_MAXIMUM_PACKET_SIZE:
647 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
648 goto not_found_or_invalid;
649 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.maximum_packet_size))
650 goto not_found_or_invalid;
651 res = ist2(trash->area, trash->data);
652 goto end;
653
654 case MQTT_FN_WILDCARD_SUBSCRIPTION_AVAILABLE:
655 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
656 goto not_found_or_invalid;
657 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.wildcard_subscription_available))
658 goto not_found_or_invalid;
659 res = ist2(trash->area, trash->data);
660 goto end;
661
662 case MQTT_FN_SUBSCRIPTION_IDENTIFIERS_AVAILABLE:
663 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
664 goto not_found_or_invalid;
665 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.subscription_identifiers_available))
666 goto not_found_or_invalid;
667 res = ist2(trash->area, trash->data);
668 goto end;
669
670 case MQTT_FN_SHARED_SUBSCRIPTION_AVAILABLE:
671 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
672 goto not_found_or_invalid;
673 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.shared_subsription_available))
674 goto not_found_or_invalid;
675 res = ist2(trash->area, trash->data);
676 goto end;
677
678 default:
679 goto not_found_or_invalid;
680 }
681 break;
682
683 default:
684 goto not_found_or_invalid;
685 }
686
687 end:
688 return res;
689
690 need_more:
691 return ist2(istptr(msg), 0);
692
693 not_found_or_invalid:
694 return IST_NULL;
695}
696
697/* Parses a CONNECT packet :
698 * https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718028
699 * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901033
700 *
701 * <parser> should point right after the MQTT fixed header. The remaining length
702 * was already checked, thus missing data is an error. On success, the result of
703 * the parsing is stored in <mpkt>.
704 *
705 * Returns:
706 * MQTT_INVALID_MESSAGE if the CONNECT message is invalid
707 * MQTT_VALID_MESSAGE if the CONNECT message looks valid
708 */
709static int mqtt_parse_connect(struct ist parser, struct mqtt_pkt *mpkt)
710{
711 /* The parser length is stored to be sure exactly consumed the announced
712 * remaining length. */
713 size_t orig_len = istlen(parser);
714 int ret = MQTT_INVALID_MESSAGE;
715
716 /*
717 * parsing variable header
718 */
719 /* read protocol_name */
720 parser = mqtt_read_string(parser, &mpkt->data.connect.var_hdr.protocol_name);
721 if (!isttest(parser) || !isteqi(mpkt->data.connect.var_hdr.protocol_name, ist("MQTT")))
722 goto end;
723
724 /* read protocol_version */
725 parser = mqtt_read_1byte_int(parser, &mpkt->data.connect.var_hdr.protocol_version);
726 if (!isttest(parser))
727 goto end;
728 if (mpkt->data.connect.var_hdr.protocol_version != MQTT_VERSION_3_1_1 &&
729 mpkt->data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
730 goto end;
731
732 /* read flags */
733 /* bit 1 is 'reserved' and must be set to 0 in CONNECT message flags */
734 parser = mqtt_read_1byte_int(parser, &mpkt->data.connect.var_hdr.flags);
735 if (!isttest(parser) || (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_RESERVED))
736 goto end;
737
738 /* if WILL flag must be set to have WILL_QOS flag or WILL_RETAIN set */
739 if ((mpkt->data.connect.var_hdr.flags & (MQTT_CONNECT_FL_WILL|MQTT_CONNECT_FL_WILL_QOS|MQTT_CONNECT_FL_WILL_RETAIN)) == MQTT_CONNECT_FL_WILL_QOS)
740 goto end;
741
742 /* read keepalive */
743 parser = mqtt_read_2byte_int(parser, &mpkt->data.connect.var_hdr.keepalive);
744 if (!isttest(parser))
745 goto end;
746
747 /* read properties, only available in MQTT_VERSION_5_0 */
748 if (mpkt->data.connect.var_hdr.protocol_version == MQTT_VERSION_5_0) {
749 struct ist props;
750 unsigned int user_prop_idx = 0;
751 uint64_t fields = 0;
752 uint32_t plen = 0;
753
754 parser = mqtt_read_varint(parser, &plen);
755 if (!isttest(parser) || istlen(parser) < plen)
756 goto end;
757 props = ist2(istptr(parser), plen);
758 parser = istadv(parser, props.len);
759
760 while (istlen(props) > 0) {
761 switch (*istptr(props)) {
762 case MQTT_PROP_SESSION_EXPIRY_INTERVAL:
763 if (fields & MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL)
764 goto end;
765 props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.session_expiry_interval);
766 fields |= MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL;
767 break;
768
769 case MQTT_PROP_RECEIVE_MAXIMUM:
770 if (fields & MQTT_FN_BIT_RECEIVE_MAXIMUM)
771 goto end;
772 props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.receive_maximum);
773 /* cannot be 0 */
774 if (!mpkt->data.connect.var_hdr.props.receive_maximum)
775 goto end;
776 fields |= MQTT_FN_BIT_RECEIVE_MAXIMUM;
777 break;
778
779 case MQTT_PROP_MAXIMUM_PACKET_SIZE:
780 if (fields & MQTT_FN_BIT_MAXIMUM_PACKET_SIZE)
781 goto end;
782 props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.maximum_packet_size);
783 /* cannot be 0 */
784 if (!mpkt->data.connect.var_hdr.props.maximum_packet_size)
785 goto end;
786 fields |= MQTT_FN_BIT_MAXIMUM_PACKET_SIZE;
787 break;
788
789 case MQTT_PROP_TOPIC_ALIAS_MAXIMUM:
790 if (fields & MQTT_FN_BIT_TOPIC_ALIAS)
791 goto end;
792 props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.topic_alias_maximum);
793 fields |= MQTT_FN_BIT_TOPIC_ALIAS;
794 break;
795
796 case MQTT_PROP_REQUEST_RESPONSE_INFORMATION:
797 if (fields & MQTT_FN_BIT_REQUEST_RESPONSE_INFORMATION)
798 goto end;
799 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.request_response_information);
800 /* can have only 2 values: 0 or 1 */
801 if (mpkt->data.connect.var_hdr.props.request_response_information > 1)
802 goto end;
803 fields |= MQTT_FN_BIT_REQUEST_RESPONSE_INFORMATION;
804 break;
805
806 case MQTT_PROP_REQUEST_PROBLEM_INFORMATION:
807 if (fields & MQTT_FN_BIT_REQUEST_PROBLEM_INFORMATION)
808 goto end;
809 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.request_problem_information);
810 /* can have only 2 values: 0 or 1 */
811 if (mpkt->data.connect.var_hdr.props.request_problem_information > 1)
812 goto end;
813 fields |= MQTT_FN_BIT_REQUEST_PROBLEM_INFORMATION;
814 break;
815
816 case MQTT_PROP_USER_PROPERTIES:
817 /* if we reached MQTT_PROP_USER_PROPERTY_ENTRIES already, then
818 * we start writing over the first property */
819 if (user_prop_idx >= MQTT_PROP_USER_PROPERTY_ENTRIES)
820 user_prop_idx = 0;
821
822 /* read user property name and value */
823 props = mqtt_read_string(istnext(props), &mpkt->data.connect.var_hdr.props.user_props[user_prop_idx].name);
824 if (!isttest(props))
825 goto end;
826 props = mqtt_read_string(props, &mpkt->data.connect.var_hdr.props.user_props[user_prop_idx].value);
827 ++user_prop_idx;
828 break;
829
830 case MQTT_PROP_AUTHENTICATION_METHOD:
831 if (fields & MQTT_FN_BIT_AUTHENTICATION_METHOD)
832 goto end;
833 props = mqtt_read_string(istnext(props), &mpkt->data.connect.var_hdr.props.authentication_method);
834 fields |= MQTT_FN_BIT_AUTHENTICATION_METHOD;
835 break;
836
837 case MQTT_PROP_AUTHENTICATION_DATA:
838 if (fields & MQTT_FN_BIT_AUTHENTICATION_DATA)
839 goto end;
840 props = mqtt_read_string(istnext(props), &mpkt->data.connect.var_hdr.props.authentication_data);
841 fields |= MQTT_FN_BIT_AUTHENTICATION_DATA;
842 break;
843
844 default:
845 goto end;
846 }
847
848 if (!isttest(props))
849 goto end;
850 }
851 }
852
853 /* cannot have auth data without auth method */
854 if (!istlen(mpkt->data.connect.var_hdr.props.authentication_method) &&
855 istlen(mpkt->data.connect.var_hdr.props.authentication_data))
856 goto end;
857
858 /* parsing payload
859 *
Ilya Shipitsinf38a0182020-12-21 01:16:17 +0500860 * Content of payload is related to flags parsed above and the field order is pre-defined:
Baptiste Assmanne279ca62020-10-27 18:10:06 +0100861 * Client Identifier, Will Topic, Will Message, User Name, Password
862 */
863 /* read client identifier */
864 parser = mqtt_read_string(parser, &mpkt->data.connect.payload.client_identifier);
Christopher Faulet0de0bec2021-06-28 15:37:59 +0200865 if (!isttest(parser))
Baptiste Assmanne279ca62020-10-27 18:10:06 +0100866 goto end;
867
868 /* read Will Properties, for MQTT v5 only
869 * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901060
870 */
871 if ((mpkt->data.connect.var_hdr.protocol_version == MQTT_VERSION_5_0) &&
872 (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL)) {
873 struct ist props;
874 unsigned int user_prop_idx = 0;
875 uint64_t fields = 0;
876 uint32_t plen = 0;
877
878 parser = mqtt_read_varint(parser, &plen);
879 if (!isttest(parser) || istlen(parser) < plen)
880 goto end;
881 props = ist2(istptr(parser), plen);
882 parser = istadv(parser, props.len);
883
884 while (istlen(props) > 0) {
885 switch (*istptr(props)) {
886 case MQTT_PROP_WILL_DELAY_INTERVAL:
887 if (fields & MQTT_FN_BIT_DELAY_INTERVAL)
888 goto end;
889 props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connect.payload.will_props.delay_interval);
890 fields |= MQTT_FN_BIT_DELAY_INTERVAL;
891 break;
892
893 case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR:
894 if (fields & MQTT_FN_BIT_PAYLOAD_FORMAT_INDICATOR)
895 goto end;
896 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connect.payload.will_props.payload_format_indicator);
897 /* can have only 2 values: 0 or 1 */
898 if (mpkt->data.connect.payload.will_props.payload_format_indicator > 1)
899 goto end;
900 fields |= MQTT_FN_BIT_PAYLOAD_FORMAT_INDICATOR;
901 break;
902
903 case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL:
904 if (fields & MQTT_FN_BIT_MESSAGE_EXPIRY_INTERVAL)
905 goto end;
906 props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connect.payload.will_props.message_expiry_interval);
907 fields |= MQTT_FN_BIT_MESSAGE_EXPIRY_INTERVAL;
908 break;
909
910 case MQTT_PROP_CONTENT_TYPE:
911 if (fields & MQTT_FN_BIT_CONTENT_TYPE)
912 goto end;
913 props = mqtt_read_string(istnext(props), &mpkt->data.connect.payload.will_props.content_type);
914 fields |= MQTT_FN_BIT_CONTENT_TYPE;
915 break;
916
917 case MQTT_PROP_RESPONSE_TOPIC:
918 if (fields & MQTT_FN_BIT_RESPONSE_TOPIC)
919 goto end;
920 props = mqtt_read_string(istnext(props), &mpkt->data.connect.payload.will_props.response_topic);
921 fields |= MQTT_FN_BIT_RESPONSE_TOPIC;
922 break;
923
924 case MQTT_PROP_CORRELATION_DATA:
925 if (fields & MQTT_FN_BIT_CORRELATION_DATA)
926 goto end;
927 props = mqtt_read_string(istnext(props), &mpkt->data.connect.payload.will_props.correlation_data);
928 fields |= MQTT_FN_BIT_CORRELATION_DATA;
929 break;
930
931 case MQTT_PROP_USER_PROPERTIES:
932 /* if we reached MQTT_PROP_USER_PROPERTY_ENTRIES already, then
933 * we start writing over the first property */
934 if (user_prop_idx >= MQTT_PROP_USER_PROPERTY_ENTRIES)
935 user_prop_idx = 0;
936
937 /* read user property name and value */
938 props = mqtt_read_string(istnext(props), &mpkt->data.connect.payload.will_props.user_props[user_prop_idx].name);
939 if (!isttest(props))
940 goto end;
941 props = mqtt_read_string(props, &mpkt->data.connect.payload.will_props.user_props[user_prop_idx].value);
942 ++user_prop_idx;
943 break;
944
945 default:
946 goto end;
947 }
948
949 if (!isttest(props))
950 goto end;
951 }
952 }
953
954 /* read Will Topic and Will Message (MQTT 3.1.1) or Payload (MQTT 5.0) */
955 if (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL) {
956 parser = mqtt_read_string(parser, &mpkt->data.connect.payload.will_topic);
957 if (!isttest(parser))
958 goto end;
959 parser = mqtt_read_string(parser, &mpkt->data.connect.payload.will_payload);
960 if (!isttest(parser))
961 goto end;
962 }
963
964 /* read User Name */
965 if (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_USERNAME) {
966 parser = mqtt_read_string(parser, &mpkt->data.connect.payload.username);
967 if (!isttest(parser))
968 goto end;
969 }
970
971 /* read Password */
972 if (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_PASSWORD) {
973 parser = mqtt_read_string(parser, &mpkt->data.connect.payload.password);
974 if (!isttest(parser))
975 goto end;
976 }
977
978 if ((orig_len - istlen(parser)) == mpkt->fixed_hdr.remaining_length)
979 ret = MQTT_VALID_MESSAGE;
980
981 end:
982 return ret;
983}
984
985/* Parses a CONNACK packet :
986 * https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718033
987 * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901074
988 *
989 * <parser> should point right after the MQTT fixed header. The remaining length
990 * was already checked, thus missing data is an error. On success, the result of
991 * the parsing is stored in <mpkt>.
992 *
993 * Returns:
994 * MQTT_INVALID_MESSAGE if the CONNECT message is invalid
995 * MQTT_VALID_MESSAGE if the CONNECT message looks valid
996 */
997static int mqtt_parse_connack(struct ist parser, struct mqtt_pkt *mpkt)
998{
999 /* The parser length is stored to be sure exactly consumed the announced
1000 * remaining length. */
1001 size_t orig_len = istlen(parser);
1002 int ret = MQTT_INVALID_MESSAGE;
1003
1004 if (istlen(parser) < 2)
1005 goto end;
1006 else if (istlen(parser) == 2)
1007 mpkt->data.connack.var_hdr.protocol_version = MQTT_VERSION_3_1_1;
1008 else
1009 mpkt->data.connack.var_hdr.protocol_version = MQTT_VERSION_5_0;
1010
1011 /*
1012 * parsing variable header
1013 */
1014 /* read flags */
1015 /* bits 7 to 1 on flags are reserved and must be 0 */
1016 parser = mqtt_read_1byte_int(parser, &mpkt->data.connack.var_hdr.flags);
1017 if (!isttest(parser) || (mpkt->data.connack.var_hdr.flags & 0xFE))
1018 goto end;
1019
1020 /* read reason_code */
1021 parser = mqtt_read_1byte_int(parser, &mpkt->data.connack.var_hdr.reason_code);
1022 if (!isttest(parser))
1023 goto end;
1024
1025 /* we can leave here for MQTT 3.1.1 */
1026 if (mpkt->data.connack.var_hdr.protocol_version == MQTT_VERSION_3_1_1) {
1027 if ((orig_len - istlen(parser)) == mpkt->fixed_hdr.remaining_length)
1028 ret = MQTT_VALID_MESSAGE;
1029 goto end;
1030 }
1031
1032 /* read properties, only available in MQTT_VERSION_5_0 */
1033 if (mpkt->data.connack.var_hdr.protocol_version == MQTT_VERSION_5_0) {
1034 struct ist props;
1035 unsigned int user_prop_idx = 0;
1036 uint64_t fields = 0;
1037 uint32_t plen = 0;
1038
1039 parser = mqtt_read_varint(parser, &plen);
1040 if (!isttest(parser) || istlen(parser) < plen)
1041 goto end;
1042 props = ist2(istptr(parser), plen);
1043 parser = istadv(parser, props.len);
1044
1045 while (istlen(props) > 0) {
1046 switch (*istptr(props)) {
1047 case MQTT_PROP_SESSION_EXPIRY_INTERVAL:
1048 if (fields & MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL)
1049 goto end;
1050 props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.session_expiry_interval);
1051 fields |= MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL;
1052 break;
1053
1054 case MQTT_PROP_RECEIVE_MAXIMUM:
1055 if (fields & MQTT_FN_BIT_RECEIVE_MAXIMUM)
1056 goto end;
1057 props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.receive_maximum);
1058 /* cannot be 0 */
1059 if (!mpkt->data.connack.var_hdr.props.receive_maximum)
1060 goto end;
1061 fields |= MQTT_FN_BIT_RECEIVE_MAXIMUM;
1062 break;
1063
1064 case MQTT_PROP_MAXIMUM_QOS:
1065 if (fields & MQTT_FN_BIT_MAXIMUM_QOS)
1066 goto end;
1067 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.maximum_qos);
1068 /* can have only 2 values: 0 or 1 */
1069 if (mpkt->data.connack.var_hdr.props.maximum_qos > 1)
1070 goto end;
1071 fields |= MQTT_FN_BIT_MAXIMUM_QOS;
1072 break;
1073
1074 case MQTT_PROP_RETAIN_AVAILABLE:
1075 if (fields & MQTT_FN_BIT_RETAIN_AVAILABLE)
1076 goto end;
1077 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.retain_available);
1078 /* can have only 2 values: 0 or 1 */
1079 if (mpkt->data.connack.var_hdr.props.retain_available > 1)
1080 goto end;
1081 fields |= MQTT_FN_BIT_RETAIN_AVAILABLE;
1082 break;
1083
1084 case MQTT_PROP_MAXIMUM_PACKET_SIZE:
1085 if (fields & MQTT_FN_BIT_MAXIMUM_PACKET_SIZE)
1086 goto end;
1087 props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.maximum_packet_size);
1088 /* cannot be 0 */
1089 if (!mpkt->data.connack.var_hdr.props.maximum_packet_size)
1090 goto end;
1091 fields |= MQTT_FN_BIT_MAXIMUM_PACKET_SIZE;
1092 break;
1093
1094 case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER:
1095 if (fields & MQTT_FN_BIT_ASSIGNED_CLIENT_IDENTIFIER)
1096 goto end;
1097 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.assigned_client_identifier);
1098 if (!istlen(mpkt->data.connack.var_hdr.props.assigned_client_identifier))
1099 goto end;
1100 fields |= MQTT_FN_BIT_ASSIGNED_CLIENT_IDENTIFIER;
1101 break;
1102
1103 case MQTT_PROP_TOPIC_ALIAS_MAXIMUM:
1104 if (fields & MQTT_FN_BIT_TOPIC_ALIAS_MAXIMUM)
1105 goto end;
1106 props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.topic_alias_maximum);
1107 fields |= MQTT_FN_BIT_TOPIC_ALIAS_MAXIMUM;
1108 break;
1109
1110 case MQTT_PROP_REASON_STRING:
1111 if (fields & MQTT_FN_BIT_REASON_STRING)
1112 goto end;
1113 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.reason_string);
1114 fields |= MQTT_FN_BIT_REASON_STRING;
1115 break;
1116
1117 case MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE:
1118 if (fields & MQTT_FN_BIT_WILDCARD_SUBSCRIPTION_AVAILABLE)
1119 goto end;
1120 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.wildcard_subscription_available);
1121 /* can have only 2 values: 0 or 1 */
1122 if (mpkt->data.connack.var_hdr.props.wildcard_subscription_available > 1)
1123 goto end;
1124 fields |= MQTT_FN_BIT_WILDCARD_SUBSCRIPTION_AVAILABLE;
1125 break;
1126
1127 case MQTT_PROP_SUBSCRIPTION_IDENTIFIERS_AVAILABLE:
1128 if (fields & MQTT_FN_BIT_SUBSCRIPTION_IDENTIFIER)
1129 goto end;
1130 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.subscription_identifiers_available);
1131 /* can have only 2 values: 0 or 1 */
1132 if (mpkt->data.connack.var_hdr.props.subscription_identifiers_available > 1)
1133 goto end;
1134 fields |= MQTT_FN_BIT_SUBSCRIPTION_IDENTIFIER;
1135 break;
1136
1137 case MQTT_PROP_SHARED_SUBSRIPTION_AVAILABLE:
1138 if (fields & MQTT_FN_BIT_SHARED_SUBSCRIPTION_AVAILABLE)
1139 goto end;
1140 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.shared_subsription_available);
1141 /* can have only 2 values: 0 or 1 */
1142 if (mpkt->data.connack.var_hdr.props.shared_subsription_available > 1)
1143 goto end;
1144 fields |= MQTT_FN_BIT_SHARED_SUBSCRIPTION_AVAILABLE;
1145 break;
1146
1147 case MQTT_PROP_SERVER_KEEPALIVE:
1148 if (fields & MQTT_FN_BIT_SERVER_KEEPALIVE)
1149 goto end;
1150 props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.server_keepalive);
1151 fields |= MQTT_FN_BIT_SERVER_KEEPALIVE;
1152 break;
1153
1154 case MQTT_PROP_RESPONSE_INFORMATION:
1155 if (fields & MQTT_FN_BIT_RESPONSE_INFORMATION)
1156 goto end;
1157 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.response_information);
1158 fields |= MQTT_FN_BIT_RESPONSE_INFORMATION;
1159 break;
1160
1161 case MQTT_PROP_SERVER_REFERENCE:
1162 if (fields & MQTT_FN_BIT_SERVER_REFERENCE)
1163 goto end;
1164 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.server_reference);
1165 fields |= MQTT_FN_BIT_SERVER_REFERENCE;
1166 break;
1167
1168 case MQTT_PROP_USER_PROPERTIES:
1169 /* if we reached MQTT_PROP_USER_PROPERTY_ENTRIES already, then
1170 * we start writing over the first property */
1171 if (user_prop_idx >= MQTT_PROP_USER_PROPERTY_ENTRIES)
1172 user_prop_idx = 0;
1173
1174 /* read user property name and value */
1175 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.user_props[user_prop_idx].name);
1176 if (!isttest(props))
1177 goto end;
1178 props = mqtt_read_string(props, &mpkt->data.connack.var_hdr.props.user_props[user_prop_idx].value);
1179 ++user_prop_idx;
1180 break;
1181
1182 case MQTT_PROP_AUTHENTICATION_METHOD:
1183 if (fields & MQTT_FN_BIT_AUTHENTICATION_METHOD)
1184 goto end;
1185 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.authentication_method);
1186 fields |= MQTT_FN_BIT_AUTHENTICATION_METHOD;
1187 break;
1188
1189 case MQTT_PROP_AUTHENTICATION_DATA:
1190 if (fields & MQTT_FN_BIT_AUTHENTICATION_DATA)
1191 goto end;
1192 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.authentication_data);
1193 fields |= MQTT_FN_BIT_AUTHENTICATION_DATA;
1194 break;
1195
1196 default:
1197 return 0;
1198 }
1199
1200 if (!isttest(props))
1201 goto end;
1202 }
1203 }
1204
1205 if ((orig_len - istlen(parser)) == mpkt->fixed_hdr.remaining_length)
1206 ret = MQTT_VALID_MESSAGE;
1207 end:
1208 return ret;
1209}
1210
1211
1212/* Parses and validates a MQTT packet
1213 * https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718028
1214 *
1215 * For now, due to HAProxy limitation, only validation of CONNECT and CONNACK packets
1216 * are supported.
1217 *
1218 * - check FIXED_HDR
1219 * - check remaining length
1220 * - check variable headers and payload
1221 *
1222 * if <mpkt> is not NULL, then this structure will be filled up as well. An
1223 * unsupported packet type is considered as invalid. It is not a problem for now
1224 * because only the first packet on each side can be parsed (CONNECT for the
1225 * client and CONNACK for the server).
1226 *
1227 * Returns:
1228 * MQTT_INVALID_MESSAGE if the message is invalid
1229 * MQTT_NEED_MORE_DATA if we need more data to fully validate the message
1230 * MQTT_VALID_MESSAGE if the message looks valid
1231 */
1232int mqtt_validate_message(const struct ist msg, struct mqtt_pkt *mpkt)
1233{
1234 struct ist parser;
1235 struct mqtt_pkt tmp_mpkt;
1236 int ret = MQTT_INVALID_MESSAGE;
1237
1238 if (!mpkt)
1239 mpkt = &tmp_mpkt;
1240 memset(mpkt, 0, sizeof(*mpkt));
1241
1242 parser = msg;
1243 if (istlen(msg) < MQTT_MIN_PKT_SIZE) {
1244 ret = MQTT_NEED_MORE_DATA;
1245 goto end;
1246 }
1247
1248 /* parse the MQTT fixed header */
1249 parser = mqtt_read_fixed_hdr(parser, mpkt);
1250 if (!isttest(parser)) {
1251 ret = MQTT_INVALID_MESSAGE;
1252 goto end;
1253 }
1254
1255 /* Now parsing "remaining length" field */
1256 parser = mqtt_read_varint(parser, &mpkt->fixed_hdr.remaining_length);
1257 if (!isttest(parser)) {
1258 ret = MQTT_INVALID_MESSAGE;
1259 goto end;
1260 }
1261
1262 if (istlen(parser) < mpkt->fixed_hdr.remaining_length)
1263 return MQTT_NEED_MORE_DATA;
1264
1265 /* Now parsing the variable header and payload, which is based on the packet type */
1266 switch (mpkt->fixed_hdr.type) {
1267 case MQTT_CPT_CONNECT:
1268 ret = mqtt_parse_connect(parser, mpkt);
1269 break;
1270 case MQTT_CPT_CONNACK:
1271 ret = mqtt_parse_connack(parser, mpkt);
1272 break;
1273 default:
1274 break;
1275 }
1276
1277 end:
1278 return ret;
1279}