blob: 5688296e52bc66152a4445d4480a0f36e8dc0410 [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
Dhruv Jain12957982022-03-21 20:04:00 +053043 /* it's MQTT 3.1, 3.1.1 and 5.0, those fields have no unique id, so we use strings */
Baptiste Assmanne279ca62020-10-27 18:10:06 +010044 [MQTT_FN_FLAGS] = IST("flags"),
Dhruv Jain12957982022-03-21 20:04:00 +053045 [MQTT_FN_REASON_CODE] = IST("reason_code"), /* MQTT 3.1 and 3.1.1: return_code */
Baptiste Assmanne279ca62020-10-27 18:10:06 +010046 [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"),
Dhruv Jain12957982022-03-21 20:04:00 +053050 [MQTT_FN_WILL_PAYLOAD] = IST("will_payload"), /* MQTT 3.1 and 3.1.1: will_message */
Baptiste Assmanne279ca62020-10-27 18:10:06 +010051 [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 :
Dhruv Jain12957982022-03-21 20:04:00 +0530698 * https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#connect
Baptiste Assmanne279ca62020-10-27 18:10:06 +0100699 * https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718028
700 * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901033
701 *
702 * <parser> should point right after the MQTT fixed header. The remaining length
703 * was already checked, thus missing data is an error. On success, the result of
704 * the parsing is stored in <mpkt>.
705 *
706 * Returns:
707 * MQTT_INVALID_MESSAGE if the CONNECT message is invalid
708 * MQTT_VALID_MESSAGE if the CONNECT message looks valid
709 */
710static int mqtt_parse_connect(struct ist parser, struct mqtt_pkt *mpkt)
711{
712 /* The parser length is stored to be sure exactly consumed the announced
713 * remaining length. */
714 size_t orig_len = istlen(parser);
715 int ret = MQTT_INVALID_MESSAGE;
716
717 /*
718 * parsing variable header
719 */
720 /* read protocol_name */
721 parser = mqtt_read_string(parser, &mpkt->data.connect.var_hdr.protocol_name);
Dhruv Jain12957982022-03-21 20:04:00 +0530722 if (!isttest(parser) || !(isteqi(mpkt->data.connect.var_hdr.protocol_name, ist("MQTT")) || isteqi(mpkt->data.connect.var_hdr.protocol_name, ist("MQIsdp"))))
Baptiste Assmanne279ca62020-10-27 18:10:06 +0100723 goto end;
724
725 /* read protocol_version */
726 parser = mqtt_read_1byte_int(parser, &mpkt->data.connect.var_hdr.protocol_version);
727 if (!isttest(parser))
728 goto end;
Dhruv Jain12957982022-03-21 20:04:00 +0530729 if (mpkt->data.connect.var_hdr.protocol_version != MQTT_VERSION_3_1 &&
730 mpkt->data.connect.var_hdr.protocol_version != MQTT_VERSION_3_1_1 &&
Baptiste Assmanne279ca62020-10-27 18:10:06 +0100731 mpkt->data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
732 goto end;
733
734 /* read flags */
735 /* bit 1 is 'reserved' and must be set to 0 in CONNECT message flags */
736 parser = mqtt_read_1byte_int(parser, &mpkt->data.connect.var_hdr.flags);
737 if (!isttest(parser) || (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_RESERVED))
738 goto end;
739
740 /* if WILL flag must be set to have WILL_QOS flag or WILL_RETAIN set */
741 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)
742 goto end;
743
744 /* read keepalive */
745 parser = mqtt_read_2byte_int(parser, &mpkt->data.connect.var_hdr.keepalive);
746 if (!isttest(parser))
747 goto end;
748
749 /* read properties, only available in MQTT_VERSION_5_0 */
750 if (mpkt->data.connect.var_hdr.protocol_version == MQTT_VERSION_5_0) {
751 struct ist props;
752 unsigned int user_prop_idx = 0;
753 uint64_t fields = 0;
754 uint32_t plen = 0;
755
756 parser = mqtt_read_varint(parser, &plen);
757 if (!isttest(parser) || istlen(parser) < plen)
758 goto end;
759 props = ist2(istptr(parser), plen);
760 parser = istadv(parser, props.len);
761
762 while (istlen(props) > 0) {
763 switch (*istptr(props)) {
764 case MQTT_PROP_SESSION_EXPIRY_INTERVAL:
765 if (fields & MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL)
766 goto end;
767 props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.session_expiry_interval);
768 fields |= MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL;
769 break;
770
771 case MQTT_PROP_RECEIVE_MAXIMUM:
772 if (fields & MQTT_FN_BIT_RECEIVE_MAXIMUM)
773 goto end;
774 props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.receive_maximum);
775 /* cannot be 0 */
776 if (!mpkt->data.connect.var_hdr.props.receive_maximum)
777 goto end;
778 fields |= MQTT_FN_BIT_RECEIVE_MAXIMUM;
779 break;
780
781 case MQTT_PROP_MAXIMUM_PACKET_SIZE:
782 if (fields & MQTT_FN_BIT_MAXIMUM_PACKET_SIZE)
783 goto end;
784 props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.maximum_packet_size);
785 /* cannot be 0 */
786 if (!mpkt->data.connect.var_hdr.props.maximum_packet_size)
787 goto end;
788 fields |= MQTT_FN_BIT_MAXIMUM_PACKET_SIZE;
789 break;
790
791 case MQTT_PROP_TOPIC_ALIAS_MAXIMUM:
792 if (fields & MQTT_FN_BIT_TOPIC_ALIAS)
793 goto end;
794 props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.topic_alias_maximum);
795 fields |= MQTT_FN_BIT_TOPIC_ALIAS;
796 break;
797
798 case MQTT_PROP_REQUEST_RESPONSE_INFORMATION:
799 if (fields & MQTT_FN_BIT_REQUEST_RESPONSE_INFORMATION)
800 goto end;
801 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.request_response_information);
802 /* can have only 2 values: 0 or 1 */
803 if (mpkt->data.connect.var_hdr.props.request_response_information > 1)
804 goto end;
805 fields |= MQTT_FN_BIT_REQUEST_RESPONSE_INFORMATION;
806 break;
807
808 case MQTT_PROP_REQUEST_PROBLEM_INFORMATION:
809 if (fields & MQTT_FN_BIT_REQUEST_PROBLEM_INFORMATION)
810 goto end;
811 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.request_problem_information);
812 /* can have only 2 values: 0 or 1 */
813 if (mpkt->data.connect.var_hdr.props.request_problem_information > 1)
814 goto end;
815 fields |= MQTT_FN_BIT_REQUEST_PROBLEM_INFORMATION;
816 break;
817
818 case MQTT_PROP_USER_PROPERTIES:
819 /* if we reached MQTT_PROP_USER_PROPERTY_ENTRIES already, then
820 * we start writing over the first property */
821 if (user_prop_idx >= MQTT_PROP_USER_PROPERTY_ENTRIES)
822 user_prop_idx = 0;
823
824 /* read user property name and value */
825 props = mqtt_read_string(istnext(props), &mpkt->data.connect.var_hdr.props.user_props[user_prop_idx].name);
826 if (!isttest(props))
827 goto end;
828 props = mqtt_read_string(props, &mpkt->data.connect.var_hdr.props.user_props[user_prop_idx].value);
829 ++user_prop_idx;
830 break;
831
832 case MQTT_PROP_AUTHENTICATION_METHOD:
833 if (fields & MQTT_FN_BIT_AUTHENTICATION_METHOD)
834 goto end;
835 props = mqtt_read_string(istnext(props), &mpkt->data.connect.var_hdr.props.authentication_method);
836 fields |= MQTT_FN_BIT_AUTHENTICATION_METHOD;
837 break;
838
839 case MQTT_PROP_AUTHENTICATION_DATA:
840 if (fields & MQTT_FN_BIT_AUTHENTICATION_DATA)
841 goto end;
842 props = mqtt_read_string(istnext(props), &mpkt->data.connect.var_hdr.props.authentication_data);
843 fields |= MQTT_FN_BIT_AUTHENTICATION_DATA;
844 break;
845
846 default:
847 goto end;
848 }
849
850 if (!isttest(props))
851 goto end;
852 }
853 }
854
855 /* cannot have auth data without auth method */
856 if (!istlen(mpkt->data.connect.var_hdr.props.authentication_method) &&
857 istlen(mpkt->data.connect.var_hdr.props.authentication_data))
858 goto end;
859
860 /* parsing payload
861 *
Ilya Shipitsinf38a0182020-12-21 01:16:17 +0500862 * Content of payload is related to flags parsed above and the field order is pre-defined:
Baptiste Assmanne279ca62020-10-27 18:10:06 +0100863 * Client Identifier, Will Topic, Will Message, User Name, Password
864 */
865 /* read client identifier */
866 parser = mqtt_read_string(parser, &mpkt->data.connect.payload.client_identifier);
Christopher Faulet0de0bec2021-06-28 15:37:59 +0200867 if (!isttest(parser))
Baptiste Assmanne279ca62020-10-27 18:10:06 +0100868 goto end;
869
870 /* read Will Properties, for MQTT v5 only
871 * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901060
872 */
873 if ((mpkt->data.connect.var_hdr.protocol_version == MQTT_VERSION_5_0) &&
874 (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL)) {
875 struct ist props;
876 unsigned int user_prop_idx = 0;
877 uint64_t fields = 0;
878 uint32_t plen = 0;
879
880 parser = mqtt_read_varint(parser, &plen);
881 if (!isttest(parser) || istlen(parser) < plen)
882 goto end;
883 props = ist2(istptr(parser), plen);
884 parser = istadv(parser, props.len);
885
886 while (istlen(props) > 0) {
887 switch (*istptr(props)) {
888 case MQTT_PROP_WILL_DELAY_INTERVAL:
889 if (fields & MQTT_FN_BIT_DELAY_INTERVAL)
890 goto end;
891 props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connect.payload.will_props.delay_interval);
892 fields |= MQTT_FN_BIT_DELAY_INTERVAL;
893 break;
894
895 case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR:
896 if (fields & MQTT_FN_BIT_PAYLOAD_FORMAT_INDICATOR)
897 goto end;
898 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connect.payload.will_props.payload_format_indicator);
899 /* can have only 2 values: 0 or 1 */
900 if (mpkt->data.connect.payload.will_props.payload_format_indicator > 1)
901 goto end;
902 fields |= MQTT_FN_BIT_PAYLOAD_FORMAT_INDICATOR;
903 break;
904
905 case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL:
906 if (fields & MQTT_FN_BIT_MESSAGE_EXPIRY_INTERVAL)
907 goto end;
908 props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connect.payload.will_props.message_expiry_interval);
909 fields |= MQTT_FN_BIT_MESSAGE_EXPIRY_INTERVAL;
910 break;
911
912 case MQTT_PROP_CONTENT_TYPE:
913 if (fields & MQTT_FN_BIT_CONTENT_TYPE)
914 goto end;
915 props = mqtt_read_string(istnext(props), &mpkt->data.connect.payload.will_props.content_type);
916 fields |= MQTT_FN_BIT_CONTENT_TYPE;
917 break;
918
919 case MQTT_PROP_RESPONSE_TOPIC:
920 if (fields & MQTT_FN_BIT_RESPONSE_TOPIC)
921 goto end;
922 props = mqtt_read_string(istnext(props), &mpkt->data.connect.payload.will_props.response_topic);
923 fields |= MQTT_FN_BIT_RESPONSE_TOPIC;
924 break;
925
926 case MQTT_PROP_CORRELATION_DATA:
927 if (fields & MQTT_FN_BIT_CORRELATION_DATA)
928 goto end;
929 props = mqtt_read_string(istnext(props), &mpkt->data.connect.payload.will_props.correlation_data);
930 fields |= MQTT_FN_BIT_CORRELATION_DATA;
931 break;
932
933 case MQTT_PROP_USER_PROPERTIES:
934 /* if we reached MQTT_PROP_USER_PROPERTY_ENTRIES already, then
935 * we start writing over the first property */
936 if (user_prop_idx >= MQTT_PROP_USER_PROPERTY_ENTRIES)
937 user_prop_idx = 0;
938
939 /* read user property name and value */
940 props = mqtt_read_string(istnext(props), &mpkt->data.connect.payload.will_props.user_props[user_prop_idx].name);
941 if (!isttest(props))
942 goto end;
943 props = mqtt_read_string(props, &mpkt->data.connect.payload.will_props.user_props[user_prop_idx].value);
944 ++user_prop_idx;
945 break;
946
947 default:
948 goto end;
949 }
950
951 if (!isttest(props))
952 goto end;
953 }
954 }
955
956 /* read Will Topic and Will Message (MQTT 3.1.1) or Payload (MQTT 5.0) */
957 if (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL) {
958 parser = mqtt_read_string(parser, &mpkt->data.connect.payload.will_topic);
959 if (!isttest(parser))
960 goto end;
961 parser = mqtt_read_string(parser, &mpkt->data.connect.payload.will_payload);
962 if (!isttest(parser))
963 goto end;
964 }
965
966 /* read User Name */
967 if (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_USERNAME) {
968 parser = mqtt_read_string(parser, &mpkt->data.connect.payload.username);
969 if (!isttest(parser))
970 goto end;
971 }
972
973 /* read Password */
974 if (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_PASSWORD) {
975 parser = mqtt_read_string(parser, &mpkt->data.connect.payload.password);
976 if (!isttest(parser))
977 goto end;
978 }
979
980 if ((orig_len - istlen(parser)) == mpkt->fixed_hdr.remaining_length)
981 ret = MQTT_VALID_MESSAGE;
982
983 end:
984 return ret;
985}
986
987/* Parses a CONNACK packet :
988 * https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718033
989 * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901074
990 *
991 * <parser> should point right after the MQTT fixed header. The remaining length
992 * was already checked, thus missing data is an error. On success, the result of
993 * the parsing is stored in <mpkt>.
994 *
995 * Returns:
996 * MQTT_INVALID_MESSAGE if the CONNECT message is invalid
997 * MQTT_VALID_MESSAGE if the CONNECT message looks valid
998 */
999static int mqtt_parse_connack(struct ist parser, struct mqtt_pkt *mpkt)
1000{
1001 /* The parser length is stored to be sure exactly consumed the announced
1002 * remaining length. */
1003 size_t orig_len = istlen(parser);
1004 int ret = MQTT_INVALID_MESSAGE;
1005
1006 if (istlen(parser) < 2)
1007 goto end;
1008 else if (istlen(parser) == 2)
1009 mpkt->data.connack.var_hdr.protocol_version = MQTT_VERSION_3_1_1;
1010 else
1011 mpkt->data.connack.var_hdr.protocol_version = MQTT_VERSION_5_0;
1012
1013 /*
1014 * parsing variable header
1015 */
1016 /* read flags */
1017 /* bits 7 to 1 on flags are reserved and must be 0 */
1018 parser = mqtt_read_1byte_int(parser, &mpkt->data.connack.var_hdr.flags);
1019 if (!isttest(parser) || (mpkt->data.connack.var_hdr.flags & 0xFE))
1020 goto end;
1021
1022 /* read reason_code */
1023 parser = mqtt_read_1byte_int(parser, &mpkt->data.connack.var_hdr.reason_code);
1024 if (!isttest(parser))
1025 goto end;
1026
1027 /* we can leave here for MQTT 3.1.1 */
1028 if (mpkt->data.connack.var_hdr.protocol_version == MQTT_VERSION_3_1_1) {
1029 if ((orig_len - istlen(parser)) == mpkt->fixed_hdr.remaining_length)
1030 ret = MQTT_VALID_MESSAGE;
1031 goto end;
1032 }
1033
1034 /* read properties, only available in MQTT_VERSION_5_0 */
1035 if (mpkt->data.connack.var_hdr.protocol_version == MQTT_VERSION_5_0) {
1036 struct ist props;
1037 unsigned int user_prop_idx = 0;
1038 uint64_t fields = 0;
1039 uint32_t plen = 0;
1040
1041 parser = mqtt_read_varint(parser, &plen);
1042 if (!isttest(parser) || istlen(parser) < plen)
1043 goto end;
1044 props = ist2(istptr(parser), plen);
1045 parser = istadv(parser, props.len);
1046
1047 while (istlen(props) > 0) {
1048 switch (*istptr(props)) {
1049 case MQTT_PROP_SESSION_EXPIRY_INTERVAL:
1050 if (fields & MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL)
1051 goto end;
1052 props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.session_expiry_interval);
1053 fields |= MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL;
1054 break;
1055
1056 case MQTT_PROP_RECEIVE_MAXIMUM:
1057 if (fields & MQTT_FN_BIT_RECEIVE_MAXIMUM)
1058 goto end;
1059 props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.receive_maximum);
1060 /* cannot be 0 */
1061 if (!mpkt->data.connack.var_hdr.props.receive_maximum)
1062 goto end;
1063 fields |= MQTT_FN_BIT_RECEIVE_MAXIMUM;
1064 break;
1065
1066 case MQTT_PROP_MAXIMUM_QOS:
1067 if (fields & MQTT_FN_BIT_MAXIMUM_QOS)
1068 goto end;
1069 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.maximum_qos);
1070 /* can have only 2 values: 0 or 1 */
1071 if (mpkt->data.connack.var_hdr.props.maximum_qos > 1)
1072 goto end;
1073 fields |= MQTT_FN_BIT_MAXIMUM_QOS;
1074 break;
1075
1076 case MQTT_PROP_RETAIN_AVAILABLE:
1077 if (fields & MQTT_FN_BIT_RETAIN_AVAILABLE)
1078 goto end;
1079 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.retain_available);
1080 /* can have only 2 values: 0 or 1 */
1081 if (mpkt->data.connack.var_hdr.props.retain_available > 1)
1082 goto end;
1083 fields |= MQTT_FN_BIT_RETAIN_AVAILABLE;
1084 break;
1085
1086 case MQTT_PROP_MAXIMUM_PACKET_SIZE:
1087 if (fields & MQTT_FN_BIT_MAXIMUM_PACKET_SIZE)
1088 goto end;
1089 props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.maximum_packet_size);
1090 /* cannot be 0 */
1091 if (!mpkt->data.connack.var_hdr.props.maximum_packet_size)
1092 goto end;
1093 fields |= MQTT_FN_BIT_MAXIMUM_PACKET_SIZE;
1094 break;
1095
1096 case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER:
1097 if (fields & MQTT_FN_BIT_ASSIGNED_CLIENT_IDENTIFIER)
1098 goto end;
1099 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.assigned_client_identifier);
1100 if (!istlen(mpkt->data.connack.var_hdr.props.assigned_client_identifier))
1101 goto end;
1102 fields |= MQTT_FN_BIT_ASSIGNED_CLIENT_IDENTIFIER;
1103 break;
1104
1105 case MQTT_PROP_TOPIC_ALIAS_MAXIMUM:
1106 if (fields & MQTT_FN_BIT_TOPIC_ALIAS_MAXIMUM)
1107 goto end;
1108 props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.topic_alias_maximum);
1109 fields |= MQTT_FN_BIT_TOPIC_ALIAS_MAXIMUM;
1110 break;
1111
1112 case MQTT_PROP_REASON_STRING:
1113 if (fields & MQTT_FN_BIT_REASON_STRING)
1114 goto end;
1115 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.reason_string);
1116 fields |= MQTT_FN_BIT_REASON_STRING;
1117 break;
1118
1119 case MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE:
1120 if (fields & MQTT_FN_BIT_WILDCARD_SUBSCRIPTION_AVAILABLE)
1121 goto end;
1122 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.wildcard_subscription_available);
1123 /* can have only 2 values: 0 or 1 */
1124 if (mpkt->data.connack.var_hdr.props.wildcard_subscription_available > 1)
1125 goto end;
1126 fields |= MQTT_FN_BIT_WILDCARD_SUBSCRIPTION_AVAILABLE;
1127 break;
1128
1129 case MQTT_PROP_SUBSCRIPTION_IDENTIFIERS_AVAILABLE:
1130 if (fields & MQTT_FN_BIT_SUBSCRIPTION_IDENTIFIER)
1131 goto end;
1132 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.subscription_identifiers_available);
1133 /* can have only 2 values: 0 or 1 */
1134 if (mpkt->data.connack.var_hdr.props.subscription_identifiers_available > 1)
1135 goto end;
1136 fields |= MQTT_FN_BIT_SUBSCRIPTION_IDENTIFIER;
1137 break;
1138
1139 case MQTT_PROP_SHARED_SUBSRIPTION_AVAILABLE:
1140 if (fields & MQTT_FN_BIT_SHARED_SUBSCRIPTION_AVAILABLE)
1141 goto end;
1142 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.shared_subsription_available);
1143 /* can have only 2 values: 0 or 1 */
1144 if (mpkt->data.connack.var_hdr.props.shared_subsription_available > 1)
1145 goto end;
1146 fields |= MQTT_FN_BIT_SHARED_SUBSCRIPTION_AVAILABLE;
1147 break;
1148
1149 case MQTT_PROP_SERVER_KEEPALIVE:
1150 if (fields & MQTT_FN_BIT_SERVER_KEEPALIVE)
1151 goto end;
1152 props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.server_keepalive);
1153 fields |= MQTT_FN_BIT_SERVER_KEEPALIVE;
1154 break;
1155
1156 case MQTT_PROP_RESPONSE_INFORMATION:
1157 if (fields & MQTT_FN_BIT_RESPONSE_INFORMATION)
1158 goto end;
1159 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.response_information);
1160 fields |= MQTT_FN_BIT_RESPONSE_INFORMATION;
1161 break;
1162
1163 case MQTT_PROP_SERVER_REFERENCE:
1164 if (fields & MQTT_FN_BIT_SERVER_REFERENCE)
1165 goto end;
1166 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.server_reference);
1167 fields |= MQTT_FN_BIT_SERVER_REFERENCE;
1168 break;
1169
1170 case MQTT_PROP_USER_PROPERTIES:
1171 /* if we reached MQTT_PROP_USER_PROPERTY_ENTRIES already, then
1172 * we start writing over the first property */
1173 if (user_prop_idx >= MQTT_PROP_USER_PROPERTY_ENTRIES)
1174 user_prop_idx = 0;
1175
1176 /* read user property name and value */
1177 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.user_props[user_prop_idx].name);
1178 if (!isttest(props))
1179 goto end;
1180 props = mqtt_read_string(props, &mpkt->data.connack.var_hdr.props.user_props[user_prop_idx].value);
1181 ++user_prop_idx;
1182 break;
1183
1184 case MQTT_PROP_AUTHENTICATION_METHOD:
1185 if (fields & MQTT_FN_BIT_AUTHENTICATION_METHOD)
1186 goto end;
1187 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.authentication_method);
1188 fields |= MQTT_FN_BIT_AUTHENTICATION_METHOD;
1189 break;
1190
1191 case MQTT_PROP_AUTHENTICATION_DATA:
1192 if (fields & MQTT_FN_BIT_AUTHENTICATION_DATA)
1193 goto end;
1194 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.authentication_data);
1195 fields |= MQTT_FN_BIT_AUTHENTICATION_DATA;
1196 break;
1197
1198 default:
1199 return 0;
1200 }
1201
1202 if (!isttest(props))
1203 goto end;
1204 }
1205 }
1206
1207 if ((orig_len - istlen(parser)) == mpkt->fixed_hdr.remaining_length)
1208 ret = MQTT_VALID_MESSAGE;
1209 end:
1210 return ret;
1211}
1212
1213
1214/* Parses and validates a MQTT packet
1215 * https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718028
1216 *
1217 * For now, due to HAProxy limitation, only validation of CONNECT and CONNACK packets
1218 * are supported.
1219 *
1220 * - check FIXED_HDR
1221 * - check remaining length
1222 * - check variable headers and payload
1223 *
1224 * if <mpkt> is not NULL, then this structure will be filled up as well. An
1225 * unsupported packet type is considered as invalid. It is not a problem for now
1226 * because only the first packet on each side can be parsed (CONNECT for the
1227 * client and CONNACK for the server).
1228 *
1229 * Returns:
1230 * MQTT_INVALID_MESSAGE if the message is invalid
1231 * MQTT_NEED_MORE_DATA if we need more data to fully validate the message
1232 * MQTT_VALID_MESSAGE if the message looks valid
1233 */
1234int mqtt_validate_message(const struct ist msg, struct mqtt_pkt *mpkt)
1235{
1236 struct ist parser;
1237 struct mqtt_pkt tmp_mpkt;
1238 int ret = MQTT_INVALID_MESSAGE;
1239
1240 if (!mpkt)
1241 mpkt = &tmp_mpkt;
1242 memset(mpkt, 0, sizeof(*mpkt));
1243
1244 parser = msg;
1245 if (istlen(msg) < MQTT_MIN_PKT_SIZE) {
1246 ret = MQTT_NEED_MORE_DATA;
1247 goto end;
1248 }
1249
1250 /* parse the MQTT fixed header */
1251 parser = mqtt_read_fixed_hdr(parser, mpkt);
1252 if (!isttest(parser)) {
1253 ret = MQTT_INVALID_MESSAGE;
1254 goto end;
1255 }
1256
1257 /* Now parsing "remaining length" field */
1258 parser = mqtt_read_varint(parser, &mpkt->fixed_hdr.remaining_length);
1259 if (!isttest(parser)) {
1260 ret = MQTT_INVALID_MESSAGE;
1261 goto end;
1262 }
1263
1264 if (istlen(parser) < mpkt->fixed_hdr.remaining_length)
1265 return MQTT_NEED_MORE_DATA;
1266
1267 /* Now parsing the variable header and payload, which is based on the packet type */
1268 switch (mpkt->fixed_hdr.type) {
1269 case MQTT_CPT_CONNECT:
1270 ret = mqtt_parse_connect(parser, mpkt);
1271 break;
1272 case MQTT_CPT_CONNACK:
1273 ret = mqtt_parse_connack(parser, mpkt);
1274 break;
1275 default:
1276 break;
1277 }
1278
1279 end:
1280 return ret;
1281}