blob: 7679cbae6f56547939e8bd7719247c26bddde83b [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);
186 parser = istadv(parser, 1);
187 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{
291 uint16_t len;
292
293 /* read and compute the string length */
294 if (istlen(parser) <= 2)
295 goto error;
296
297 len = ((uint16_t)*istptr(parser) << 8) + (uint16_t)*(istptr(parser) + 1);
298 parser = istadv(parser, 2);
299 if (istlen(parser) < len)
300 goto error;
301
302 if (str) {
303 str->ptr = istptr(parser);
304 str->len = len;
305 }
306
307 return istadv(parser, len);
308
309 error:
310 return IST_NULL;
311}
312
313/* Helper function to convert a unsigned integer to a string. The result is
314 * written in <buf>. On success, the written size is returned, otherwise, on
315 * error, 0 is returned.
316 */
317static inline size_t mqtt_uint2str(struct buffer *buf, uint32_t i)
318{
319 char *end;
320
321 end = ultoa_o(i, buf->area, buf->size);
322 if (!end)
323 return 0;
324 buf->data = end - buf->area;
325 return buf->data;
326}
327
328/* Extracts the value of a <fieldname_id> of type <type> from a given MQTT
Ilya Shipitsinf38a0182020-12-21 01:16:17 +0500329 * message <msg>. IST_NULL is returned if an error occurred while parsing or if
Baptiste Assmanne279ca62020-10-27 18:10:06 +0100330 * the field could not be found. If more data are required, the message with a
331 * length set to 0 is returned. If the field is found, the response is returned
332 * as a struct ist.
333 */
334struct ist mqtt_field_value(struct ist msg, int type, int fieldname_id)
335{
336 struct buffer *trash = get_trash_chunk();
337 struct mqtt_pkt mpkt;
338 struct ist res;
339
340 switch (mqtt_validate_message(msg, &mpkt)) {
341 case MQTT_VALID_MESSAGE:
342 if (mpkt.fixed_hdr.type != type)
343 goto not_found_or_invalid;
344 break;
345 case MQTT_NEED_MORE_DATA:
346 goto need_more;
347 case MQTT_INVALID_MESSAGE:
348 goto not_found_or_invalid;
349 }
350
351 switch (type) {
352 case MQTT_CPT_CONNECT:
353 switch (fieldname_id) {
354 case MQTT_FN_FLAGS:
355 if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.flags))
356 goto not_found_or_invalid;
357 res = ist2(trash->area, trash->data);
358 goto end;
359
360 case MQTT_FN_PROTOCOL_NAME:
361 if (!istlen(mpkt.data.connect.var_hdr.protocol_name))
362 goto not_found_or_invalid;
363 res = mpkt.data.connect.var_hdr.protocol_name;
364 goto end;
365
366 case MQTT_FN_PROTOCOL_VERSION:
367 if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.protocol_version))
368 goto not_found_or_invalid;
369 res = ist2(trash->area, trash->data);
370 goto end;
371
372 case MQTT_FN_CLIENT_IDENTIFIER:
373 if (!istlen(mpkt.data.connect.payload.client_identifier))
374 goto not_found_or_invalid;
375 res = mpkt.data.connect.payload.client_identifier;
376 goto end;
377
378 case MQTT_FN_WILL_TOPIC:
379 if (!istlen(mpkt.data.connect.payload.will_topic))
380 goto not_found_or_invalid;
381 res = mpkt.data.connect.payload.will_topic;
382 goto end;
383
384 case MQTT_FN_WILL_PAYLOAD:
385 if (!istlen(mpkt.data.connect.payload.will_payload))
386 goto not_found_or_invalid;
387 res = mpkt.data.connect.payload.will_payload;
388 goto end;
389
390 case MQTT_FN_USERNAME:
391 if (!istlen(mpkt.data.connect.payload.username))
392 goto not_found_or_invalid;
393 res = mpkt.data.connect.payload.username;
394 goto end;
395
396 case MQTT_FN_PASSWORD:
397 if (!istlen(mpkt.data.connect.payload.password))
398 goto not_found_or_invalid;
399 res = mpkt.data.connect.payload.password;
400 goto end;
401
402 case MQTT_FN_KEEPALIVE:
403 if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.keepalive))
404 goto not_found_or_invalid;
405 res = ist2(trash->area, trash->data);
406 goto end;
407
408 case MQTT_FN_PAYLOAD_FORMAT_INDICATOR:
409 if ((mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) ||
410 !(mpkt.data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL))
411 goto not_found_or_invalid;
412 if (!mqtt_uint2str(trash, mpkt.data.connect.payload.will_props.payload_format_indicator))
413 goto not_found_or_invalid;
414 res = ist2(trash->area, trash->data);
415 goto end;
416
417 case MQTT_FN_MESSAGE_EXPIRY_INTERVAL:
418 if ((mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) ||
419 !(mpkt.data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL))
420 goto not_found_or_invalid;
421 if (!mqtt_uint2str(trash, mpkt.data.connect.payload.will_props.message_expiry_interval))
422 goto not_found_or_invalid;
423 res = ist2(trash->area, trash->data);
424 goto end;
425
426 case MQTT_FN_CONTENT_TYPE:
427 if ((mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) ||
428 !(mpkt.data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL))
429 goto not_found_or_invalid;
430 if (!istlen(mpkt.data.connect.payload.will_props.content_type))
431 goto not_found_or_invalid;
432 res = mpkt.data.connect.payload.will_props.content_type;
433 goto end;
434
435 case MQTT_FN_RESPONSE_TOPIC:
436 if ((mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) ||
437 !(mpkt.data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL))
438 goto not_found_or_invalid;
439 if (!istlen(mpkt.data.connect.payload.will_props.response_topic))
440 goto not_found_or_invalid;
441 res = mpkt.data.connect.payload.will_props.response_topic;
442 goto end;
443
444 case MQTT_FN_CORRELATION_DATA:
445 if ((mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) ||
446 !(mpkt.data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL))
447 goto not_found_or_invalid;
448 if (!istlen(mpkt.data.connect.payload.will_props.correlation_data))
449 goto not_found_or_invalid;
450 res = mpkt.data.connect.payload.will_props.correlation_data;
451 goto end;
452
453 case MQTT_FN_SESSION_EXPIRY_INTERVAL:
454 if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
455 goto not_found_or_invalid;
456 if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.props.session_expiry_interval))
457 goto not_found_or_invalid;
458 res = ist2(trash->area, trash->data);
459 goto end;
460
461 case MQTT_FN_AUTHENTICATION_METHOD:
462 if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
463 goto not_found_or_invalid;
464 if (!istlen(mpkt.data.connect.var_hdr.props.authentication_method))
465 goto not_found_or_invalid;
466 res = mpkt.data.connect.var_hdr.props.authentication_method;
467 goto end;
468
469 case MQTT_FN_AUTHENTICATION_DATA:
470 if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
471 goto not_found_or_invalid;
472 if (!istlen(mpkt.data.connect.var_hdr.props.authentication_data))
473 goto not_found_or_invalid;
474 res = mpkt.data.connect.var_hdr.props.authentication_data;
475 goto end;
476
477 case MQTT_FN_REQUEST_PROBLEM_INFORMATION:
478 if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
479 goto not_found_or_invalid;
480 if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.props.request_problem_information))
481 goto not_found_or_invalid;
482 res = ist2(trash->area, trash->data);
483 goto end;
484
485 case MQTT_FN_DELAY_INTERVAL:
486 if ((mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) ||
487 !(mpkt.data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL))
488 goto not_found_or_invalid;
489 if (!mqtt_uint2str(trash, mpkt.data.connect.payload.will_props.delay_interval))
490 goto not_found_or_invalid;
491 res = ist2(trash->area, trash->data);
492 goto end;
493
494 case MQTT_FN_REQUEST_RESPONSE_INFORMATION:
495 if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
496 goto not_found_or_invalid;
497 if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.props.request_response_information))
498 goto not_found_or_invalid;
499 res = ist2(trash->area, trash->data);
500 goto end;
501
502 case MQTT_FN_RECEIVE_MAXIMUM:
503 if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
504 goto not_found_or_invalid;
505 if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.props.receive_maximum))
506 goto not_found_or_invalid;
507 res = ist2(trash->area, trash->data);
508 goto end;
509
510 case MQTT_FN_TOPIC_ALIAS_MAXIMUM:
511 if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
512 goto not_found_or_invalid;
513 if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.props.topic_alias_maximum))
514 goto not_found_or_invalid;
515 res = ist2(trash->area, trash->data);
516 goto end;
517
518 case MQTT_FN_MAXIMUM_PACKET_SIZE:
519 if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
520 goto not_found_or_invalid;
521 if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.props.maximum_packet_size))
522 goto not_found_or_invalid;
523 res = ist2(trash->area, trash->data);
524 goto end;
525
526 default:
527 goto not_found_or_invalid;
528 }
529 break;
530
531 case MQTT_CPT_CONNACK:
532 switch (fieldname_id) {
533 case MQTT_FN_FLAGS:
534 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.flags))
535 goto not_found_or_invalid;
536 res = ist2(trash->area, trash->data);
537 goto end;
538
539 case MQTT_FN_REASON_CODE:
540 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.reason_code))
541 goto not_found_or_invalid;
542 res = ist2(trash->area, trash->data);
543 goto end;
544
545 case MQTT_FN_PROTOCOL_VERSION:
546 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.protocol_version))
547 goto not_found_or_invalid;
548 res = ist2(trash->area, trash->data);
549 goto end;
550
551 case MQTT_FN_SESSION_EXPIRY_INTERVAL:
552 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
553 goto not_found_or_invalid;
554 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.session_expiry_interval))
555 goto not_found_or_invalid;
556 res = ist2(trash->area, trash->data);
557 goto end;
558
559 case MQTT_FN_ASSIGNED_CLIENT_IDENTIFIER:
560 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
561 goto not_found_or_invalid;
562 if (!istlen(mpkt.data.connack.var_hdr.props.assigned_client_identifier))
563 goto not_found_or_invalid;
564 res = mpkt.data.connack.var_hdr.props.assigned_client_identifier;
565 goto end;
566
567 case MQTT_FN_SERVER_KEEPALIVE:
568 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
569 goto not_found_or_invalid;
570 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.server_keepalive))
571 goto not_found_or_invalid;
572 res = ist2(trash->area, trash->data);
573 goto end;
574
575 case MQTT_FN_AUTHENTICATION_METHOD:
576 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
577 goto not_found_or_invalid;
578 if (!istlen(mpkt.data.connack.var_hdr.props.authentication_method))
579 goto not_found_or_invalid;
580 res = mpkt.data.connack.var_hdr.props.authentication_method;
581 goto end;
582
583 case MQTT_FN_AUTHENTICATION_DATA:
584 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
585 goto not_found_or_invalid;
586 if (!istlen(mpkt.data.connack.var_hdr.props.authentication_data))
587 goto not_found_or_invalid;
588 res = mpkt.data.connack.var_hdr.props.authentication_data;
589 goto end;
590
591 case MQTT_FN_RESPONSE_INFORMATION:
592 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
593 goto not_found_or_invalid;
594 if (!istlen(mpkt.data.connack.var_hdr.props.response_information))
595 goto not_found_or_invalid;
596 res = mpkt.data.connack.var_hdr.props.response_information;
597 goto end;
598
599 case MQTT_FN_SERVER_REFERENCE:
600 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
601 goto not_found_or_invalid;
602 if (!istlen(mpkt.data.connack.var_hdr.props.server_reference))
603 goto not_found_or_invalid;
604 res = mpkt.data.connack.var_hdr.props.server_reference;
605 goto end;
606
607 case MQTT_FN_REASON_STRING:
608 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
609 goto not_found_or_invalid;
610 if (!istlen(mpkt.data.connack.var_hdr.props.reason_string))
611 goto not_found_or_invalid;
612 res = mpkt.data.connack.var_hdr.props.reason_string;
613 goto end;
614
615 case MQTT_FN_RECEIVE_MAXIMUM:
616 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
617 goto not_found_or_invalid;
618 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.receive_maximum))
619 goto not_found_or_invalid;
620 res = ist2(trash->area, trash->data);
621 goto end;
622
623 case MQTT_FN_TOPIC_ALIAS_MAXIMUM:
624 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
625 goto not_found_or_invalid;
626 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.topic_alias_maximum))
627 goto not_found_or_invalid;
628 res = ist2(trash->area, trash->data);
629 goto end;
630
631 case MQTT_FN_MAXIMUM_QOS:
632 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
633 goto not_found_or_invalid;
634 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.maximum_qos))
635 goto not_found_or_invalid;
636 res = ist2(trash->area, trash->data);
637 goto end;
638
639 case MQTT_FN_RETAIN_AVAILABLE:
640 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
641 goto not_found_or_invalid;
642 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.retain_available))
643 goto not_found_or_invalid;
644 res = ist2(trash->area, trash->data);
645 goto end;
646
647 case MQTT_FN_MAXIMUM_PACKET_SIZE:
648 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
649 goto not_found_or_invalid;
650 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.maximum_packet_size))
651 goto not_found_or_invalid;
652 res = ist2(trash->area, trash->data);
653 goto end;
654
655 case MQTT_FN_WILDCARD_SUBSCRIPTION_AVAILABLE:
656 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
657 goto not_found_or_invalid;
658 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.wildcard_subscription_available))
659 goto not_found_or_invalid;
660 res = ist2(trash->area, trash->data);
661 goto end;
662
663 case MQTT_FN_SUBSCRIPTION_IDENTIFIERS_AVAILABLE:
664 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
665 goto not_found_or_invalid;
666 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.subscription_identifiers_available))
667 goto not_found_or_invalid;
668 res = ist2(trash->area, trash->data);
669 goto end;
670
671 case MQTT_FN_SHARED_SUBSCRIPTION_AVAILABLE:
672 if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0)
673 goto not_found_or_invalid;
674 if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.shared_subsription_available))
675 goto not_found_or_invalid;
676 res = ist2(trash->area, trash->data);
677 goto end;
678
679 default:
680 goto not_found_or_invalid;
681 }
682 break;
683
684 default:
685 goto not_found_or_invalid;
686 }
687
688 end:
689 return res;
690
691 need_more:
692 return ist2(istptr(msg), 0);
693
694 not_found_or_invalid:
695 return IST_NULL;
696}
697
698/* Parses a CONNECT packet :
699 * 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);
722 if (!isttest(parser) || !isteqi(mpkt->data.connect.var_hdr.protocol_name, ist("MQTT")))
723 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;
729 if (mpkt->data.connect.var_hdr.protocol_version != MQTT_VERSION_3_1_1 &&
730 mpkt->data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0)
731 goto end;
732
733 /* read flags */
734 /* bit 1 is 'reserved' and must be set to 0 in CONNECT message flags */
735 parser = mqtt_read_1byte_int(parser, &mpkt->data.connect.var_hdr.flags);
736 if (!isttest(parser) || (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_RESERVED))
737 goto end;
738
739 /* if WILL flag must be set to have WILL_QOS flag or WILL_RETAIN set */
740 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)
741 goto end;
742
743 /* read keepalive */
744 parser = mqtt_read_2byte_int(parser, &mpkt->data.connect.var_hdr.keepalive);
745 if (!isttest(parser))
746 goto end;
747
748 /* read properties, only available in MQTT_VERSION_5_0 */
749 if (mpkt->data.connect.var_hdr.protocol_version == MQTT_VERSION_5_0) {
750 struct ist props;
751 unsigned int user_prop_idx = 0;
752 uint64_t fields = 0;
753 uint32_t plen = 0;
754
755 parser = mqtt_read_varint(parser, &plen);
756 if (!isttest(parser) || istlen(parser) < plen)
757 goto end;
758 props = ist2(istptr(parser), plen);
759 parser = istadv(parser, props.len);
760
761 while (istlen(props) > 0) {
762 switch (*istptr(props)) {
763 case MQTT_PROP_SESSION_EXPIRY_INTERVAL:
764 if (fields & MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL)
765 goto end;
766 props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.session_expiry_interval);
767 fields |= MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL;
768 break;
769
770 case MQTT_PROP_RECEIVE_MAXIMUM:
771 if (fields & MQTT_FN_BIT_RECEIVE_MAXIMUM)
772 goto end;
773 props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.receive_maximum);
774 /* cannot be 0 */
775 if (!mpkt->data.connect.var_hdr.props.receive_maximum)
776 goto end;
777 fields |= MQTT_FN_BIT_RECEIVE_MAXIMUM;
778 break;
779
780 case MQTT_PROP_MAXIMUM_PACKET_SIZE:
781 if (fields & MQTT_FN_BIT_MAXIMUM_PACKET_SIZE)
782 goto end;
783 props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.maximum_packet_size);
784 /* cannot be 0 */
785 if (!mpkt->data.connect.var_hdr.props.maximum_packet_size)
786 goto end;
787 fields |= MQTT_FN_BIT_MAXIMUM_PACKET_SIZE;
788 break;
789
790 case MQTT_PROP_TOPIC_ALIAS_MAXIMUM:
791 if (fields & MQTT_FN_BIT_TOPIC_ALIAS)
792 goto end;
793 props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.topic_alias_maximum);
794 fields |= MQTT_FN_BIT_TOPIC_ALIAS;
795 break;
796
797 case MQTT_PROP_REQUEST_RESPONSE_INFORMATION:
798 if (fields & MQTT_FN_BIT_REQUEST_RESPONSE_INFORMATION)
799 goto end;
800 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.request_response_information);
801 /* can have only 2 values: 0 or 1 */
802 if (mpkt->data.connect.var_hdr.props.request_response_information > 1)
803 goto end;
804 fields |= MQTT_FN_BIT_REQUEST_RESPONSE_INFORMATION;
805 break;
806
807 case MQTT_PROP_REQUEST_PROBLEM_INFORMATION:
808 if (fields & MQTT_FN_BIT_REQUEST_PROBLEM_INFORMATION)
809 goto end;
810 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.request_problem_information);
811 /* can have only 2 values: 0 or 1 */
812 if (mpkt->data.connect.var_hdr.props.request_problem_information > 1)
813 goto end;
814 fields |= MQTT_FN_BIT_REQUEST_PROBLEM_INFORMATION;
815 break;
816
817 case MQTT_PROP_USER_PROPERTIES:
818 /* if we reached MQTT_PROP_USER_PROPERTY_ENTRIES already, then
819 * we start writing over the first property */
820 if (user_prop_idx >= MQTT_PROP_USER_PROPERTY_ENTRIES)
821 user_prop_idx = 0;
822
823 /* read user property name and value */
824 props = mqtt_read_string(istnext(props), &mpkt->data.connect.var_hdr.props.user_props[user_prop_idx].name);
825 if (!isttest(props))
826 goto end;
827 props = mqtt_read_string(props, &mpkt->data.connect.var_hdr.props.user_props[user_prop_idx].value);
828 ++user_prop_idx;
829 break;
830
831 case MQTT_PROP_AUTHENTICATION_METHOD:
832 if (fields & MQTT_FN_BIT_AUTHENTICATION_METHOD)
833 goto end;
834 props = mqtt_read_string(istnext(props), &mpkt->data.connect.var_hdr.props.authentication_method);
835 fields |= MQTT_FN_BIT_AUTHENTICATION_METHOD;
836 break;
837
838 case MQTT_PROP_AUTHENTICATION_DATA:
839 if (fields & MQTT_FN_BIT_AUTHENTICATION_DATA)
840 goto end;
841 props = mqtt_read_string(istnext(props), &mpkt->data.connect.var_hdr.props.authentication_data);
842 fields |= MQTT_FN_BIT_AUTHENTICATION_DATA;
843 break;
844
845 default:
846 goto end;
847 }
848
849 if (!isttest(props))
850 goto end;
851 }
852 }
853
854 /* cannot have auth data without auth method */
855 if (!istlen(mpkt->data.connect.var_hdr.props.authentication_method) &&
856 istlen(mpkt->data.connect.var_hdr.props.authentication_data))
857 goto end;
858
859 /* parsing payload
860 *
Ilya Shipitsinf38a0182020-12-21 01:16:17 +0500861 * Content of payload is related to flags parsed above and the field order is pre-defined:
Baptiste Assmanne279ca62020-10-27 18:10:06 +0100862 * Client Identifier, Will Topic, Will Message, User Name, Password
863 */
864 /* read client identifier */
865 parser = mqtt_read_string(parser, &mpkt->data.connect.payload.client_identifier);
866 if (!isttest(parser) || !istlen(mpkt->data.connect.payload.client_identifier))
867 goto end;
868
869 /* read Will Properties, for MQTT v5 only
870 * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901060
871 */
872 if ((mpkt->data.connect.var_hdr.protocol_version == MQTT_VERSION_5_0) &&
873 (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL)) {
874 struct ist props;
875 unsigned int user_prop_idx = 0;
876 uint64_t fields = 0;
877 uint32_t plen = 0;
878
879 parser = mqtt_read_varint(parser, &plen);
880 if (!isttest(parser) || istlen(parser) < plen)
881 goto end;
882 props = ist2(istptr(parser), plen);
883 parser = istadv(parser, props.len);
884
885 while (istlen(props) > 0) {
886 switch (*istptr(props)) {
887 case MQTT_PROP_WILL_DELAY_INTERVAL:
888 if (fields & MQTT_FN_BIT_DELAY_INTERVAL)
889 goto end;
890 props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connect.payload.will_props.delay_interval);
891 fields |= MQTT_FN_BIT_DELAY_INTERVAL;
892 break;
893
894 case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR:
895 if (fields & MQTT_FN_BIT_PAYLOAD_FORMAT_INDICATOR)
896 goto end;
897 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connect.payload.will_props.payload_format_indicator);
898 /* can have only 2 values: 0 or 1 */
899 if (mpkt->data.connect.payload.will_props.payload_format_indicator > 1)
900 goto end;
901 fields |= MQTT_FN_BIT_PAYLOAD_FORMAT_INDICATOR;
902 break;
903
904 case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL:
905 if (fields & MQTT_FN_BIT_MESSAGE_EXPIRY_INTERVAL)
906 goto end;
907 props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connect.payload.will_props.message_expiry_interval);
908 fields |= MQTT_FN_BIT_MESSAGE_EXPIRY_INTERVAL;
909 break;
910
911 case MQTT_PROP_CONTENT_TYPE:
912 if (fields & MQTT_FN_BIT_CONTENT_TYPE)
913 goto end;
914 props = mqtt_read_string(istnext(props), &mpkt->data.connect.payload.will_props.content_type);
915 fields |= MQTT_FN_BIT_CONTENT_TYPE;
916 break;
917
918 case MQTT_PROP_RESPONSE_TOPIC:
919 if (fields & MQTT_FN_BIT_RESPONSE_TOPIC)
920 goto end;
921 props = mqtt_read_string(istnext(props), &mpkt->data.connect.payload.will_props.response_topic);
922 fields |= MQTT_FN_BIT_RESPONSE_TOPIC;
923 break;
924
925 case MQTT_PROP_CORRELATION_DATA:
926 if (fields & MQTT_FN_BIT_CORRELATION_DATA)
927 goto end;
928 props = mqtt_read_string(istnext(props), &mpkt->data.connect.payload.will_props.correlation_data);
929 fields |= MQTT_FN_BIT_CORRELATION_DATA;
930 break;
931
932 case MQTT_PROP_USER_PROPERTIES:
933 /* if we reached MQTT_PROP_USER_PROPERTY_ENTRIES already, then
934 * we start writing over the first property */
935 if (user_prop_idx >= MQTT_PROP_USER_PROPERTY_ENTRIES)
936 user_prop_idx = 0;
937
938 /* read user property name and value */
939 props = mqtt_read_string(istnext(props), &mpkt->data.connect.payload.will_props.user_props[user_prop_idx].name);
940 if (!isttest(props))
941 goto end;
942 props = mqtt_read_string(props, &mpkt->data.connect.payload.will_props.user_props[user_prop_idx].value);
943 ++user_prop_idx;
944 break;
945
946 default:
947 goto end;
948 }
949
950 if (!isttest(props))
951 goto end;
952 }
953 }
954
955 /* read Will Topic and Will Message (MQTT 3.1.1) or Payload (MQTT 5.0) */
956 if (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL) {
957 parser = mqtt_read_string(parser, &mpkt->data.connect.payload.will_topic);
958 if (!isttest(parser))
959 goto end;
960 parser = mqtt_read_string(parser, &mpkt->data.connect.payload.will_payload);
961 if (!isttest(parser))
962 goto end;
963 }
964
965 /* read User Name */
966 if (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_USERNAME) {
967 parser = mqtt_read_string(parser, &mpkt->data.connect.payload.username);
968 if (!isttest(parser))
969 goto end;
970 }
971
972 /* read Password */
973 if (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_PASSWORD) {
974 parser = mqtt_read_string(parser, &mpkt->data.connect.payload.password);
975 if (!isttest(parser))
976 goto end;
977 }
978
979 if ((orig_len - istlen(parser)) == mpkt->fixed_hdr.remaining_length)
980 ret = MQTT_VALID_MESSAGE;
981
982 end:
983 return ret;
984}
985
986/* Parses a CONNACK packet :
987 * https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718033
988 * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901074
989 *
990 * <parser> should point right after the MQTT fixed header. The remaining length
991 * was already checked, thus missing data is an error. On success, the result of
992 * the parsing is stored in <mpkt>.
993 *
994 * Returns:
995 * MQTT_INVALID_MESSAGE if the CONNECT message is invalid
996 * MQTT_VALID_MESSAGE if the CONNECT message looks valid
997 */
998static int mqtt_parse_connack(struct ist parser, struct mqtt_pkt *mpkt)
999{
1000 /* The parser length is stored to be sure exactly consumed the announced
1001 * remaining length. */
1002 size_t orig_len = istlen(parser);
1003 int ret = MQTT_INVALID_MESSAGE;
1004
1005 if (istlen(parser) < 2)
1006 goto end;
1007 else if (istlen(parser) == 2)
1008 mpkt->data.connack.var_hdr.protocol_version = MQTT_VERSION_3_1_1;
1009 else
1010 mpkt->data.connack.var_hdr.protocol_version = MQTT_VERSION_5_0;
1011
1012 /*
1013 * parsing variable header
1014 */
1015 /* read flags */
1016 /* bits 7 to 1 on flags are reserved and must be 0 */
1017 parser = mqtt_read_1byte_int(parser, &mpkt->data.connack.var_hdr.flags);
1018 if (!isttest(parser) || (mpkt->data.connack.var_hdr.flags & 0xFE))
1019 goto end;
1020
1021 /* read reason_code */
1022 parser = mqtt_read_1byte_int(parser, &mpkt->data.connack.var_hdr.reason_code);
1023 if (!isttest(parser))
1024 goto end;
1025
1026 /* we can leave here for MQTT 3.1.1 */
1027 if (mpkt->data.connack.var_hdr.protocol_version == MQTT_VERSION_3_1_1) {
1028 if ((orig_len - istlen(parser)) == mpkt->fixed_hdr.remaining_length)
1029 ret = MQTT_VALID_MESSAGE;
1030 goto end;
1031 }
1032
1033 /* read properties, only available in MQTT_VERSION_5_0 */
1034 if (mpkt->data.connack.var_hdr.protocol_version == MQTT_VERSION_5_0) {
1035 struct ist props;
1036 unsigned int user_prop_idx = 0;
1037 uint64_t fields = 0;
1038 uint32_t plen = 0;
1039
1040 parser = mqtt_read_varint(parser, &plen);
1041 if (!isttest(parser) || istlen(parser) < plen)
1042 goto end;
1043 props = ist2(istptr(parser), plen);
1044 parser = istadv(parser, props.len);
1045
1046 while (istlen(props) > 0) {
1047 switch (*istptr(props)) {
1048 case MQTT_PROP_SESSION_EXPIRY_INTERVAL:
1049 if (fields & MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL)
1050 goto end;
1051 props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.session_expiry_interval);
1052 fields |= MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL;
1053 break;
1054
1055 case MQTT_PROP_RECEIVE_MAXIMUM:
1056 if (fields & MQTT_FN_BIT_RECEIVE_MAXIMUM)
1057 goto end;
1058 props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.receive_maximum);
1059 /* cannot be 0 */
1060 if (!mpkt->data.connack.var_hdr.props.receive_maximum)
1061 goto end;
1062 fields |= MQTT_FN_BIT_RECEIVE_MAXIMUM;
1063 break;
1064
1065 case MQTT_PROP_MAXIMUM_QOS:
1066 if (fields & MQTT_FN_BIT_MAXIMUM_QOS)
1067 goto end;
1068 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.maximum_qos);
1069 /* can have only 2 values: 0 or 1 */
1070 if (mpkt->data.connack.var_hdr.props.maximum_qos > 1)
1071 goto end;
1072 fields |= MQTT_FN_BIT_MAXIMUM_QOS;
1073 break;
1074
1075 case MQTT_PROP_RETAIN_AVAILABLE:
1076 if (fields & MQTT_FN_BIT_RETAIN_AVAILABLE)
1077 goto end;
1078 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.retain_available);
1079 /* can have only 2 values: 0 or 1 */
1080 if (mpkt->data.connack.var_hdr.props.retain_available > 1)
1081 goto end;
1082 fields |= MQTT_FN_BIT_RETAIN_AVAILABLE;
1083 break;
1084
1085 case MQTT_PROP_MAXIMUM_PACKET_SIZE:
1086 if (fields & MQTT_FN_BIT_MAXIMUM_PACKET_SIZE)
1087 goto end;
1088 props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.maximum_packet_size);
1089 /* cannot be 0 */
1090 if (!mpkt->data.connack.var_hdr.props.maximum_packet_size)
1091 goto end;
1092 fields |= MQTT_FN_BIT_MAXIMUM_PACKET_SIZE;
1093 break;
1094
1095 case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER:
1096 if (fields & MQTT_FN_BIT_ASSIGNED_CLIENT_IDENTIFIER)
1097 goto end;
1098 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.assigned_client_identifier);
1099 if (!istlen(mpkt->data.connack.var_hdr.props.assigned_client_identifier))
1100 goto end;
1101 fields |= MQTT_FN_BIT_ASSIGNED_CLIENT_IDENTIFIER;
1102 break;
1103
1104 case MQTT_PROP_TOPIC_ALIAS_MAXIMUM:
1105 if (fields & MQTT_FN_BIT_TOPIC_ALIAS_MAXIMUM)
1106 goto end;
1107 props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.topic_alias_maximum);
1108 fields |= MQTT_FN_BIT_TOPIC_ALIAS_MAXIMUM;
1109 break;
1110
1111 case MQTT_PROP_REASON_STRING:
1112 if (fields & MQTT_FN_BIT_REASON_STRING)
1113 goto end;
1114 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.reason_string);
1115 fields |= MQTT_FN_BIT_REASON_STRING;
1116 break;
1117
1118 case MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE:
1119 if (fields & MQTT_FN_BIT_WILDCARD_SUBSCRIPTION_AVAILABLE)
1120 goto end;
1121 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.wildcard_subscription_available);
1122 /* can have only 2 values: 0 or 1 */
1123 if (mpkt->data.connack.var_hdr.props.wildcard_subscription_available > 1)
1124 goto end;
1125 fields |= MQTT_FN_BIT_WILDCARD_SUBSCRIPTION_AVAILABLE;
1126 break;
1127
1128 case MQTT_PROP_SUBSCRIPTION_IDENTIFIERS_AVAILABLE:
1129 if (fields & MQTT_FN_BIT_SUBSCRIPTION_IDENTIFIER)
1130 goto end;
1131 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.subscription_identifiers_available);
1132 /* can have only 2 values: 0 or 1 */
1133 if (mpkt->data.connack.var_hdr.props.subscription_identifiers_available > 1)
1134 goto end;
1135 fields |= MQTT_FN_BIT_SUBSCRIPTION_IDENTIFIER;
1136 break;
1137
1138 case MQTT_PROP_SHARED_SUBSRIPTION_AVAILABLE:
1139 if (fields & MQTT_FN_BIT_SHARED_SUBSCRIPTION_AVAILABLE)
1140 goto end;
1141 props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.shared_subsription_available);
1142 /* can have only 2 values: 0 or 1 */
1143 if (mpkt->data.connack.var_hdr.props.shared_subsription_available > 1)
1144 goto end;
1145 fields |= MQTT_FN_BIT_SHARED_SUBSCRIPTION_AVAILABLE;
1146 break;
1147
1148 case MQTT_PROP_SERVER_KEEPALIVE:
1149 if (fields & MQTT_FN_BIT_SERVER_KEEPALIVE)
1150 goto end;
1151 props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.server_keepalive);
1152 fields |= MQTT_FN_BIT_SERVER_KEEPALIVE;
1153 break;
1154
1155 case MQTT_PROP_RESPONSE_INFORMATION:
1156 if (fields & MQTT_FN_BIT_RESPONSE_INFORMATION)
1157 goto end;
1158 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.response_information);
1159 fields |= MQTT_FN_BIT_RESPONSE_INFORMATION;
1160 break;
1161
1162 case MQTT_PROP_SERVER_REFERENCE:
1163 if (fields & MQTT_FN_BIT_SERVER_REFERENCE)
1164 goto end;
1165 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.server_reference);
1166 fields |= MQTT_FN_BIT_SERVER_REFERENCE;
1167 break;
1168
1169 case MQTT_PROP_USER_PROPERTIES:
1170 /* if we reached MQTT_PROP_USER_PROPERTY_ENTRIES already, then
1171 * we start writing over the first property */
1172 if (user_prop_idx >= MQTT_PROP_USER_PROPERTY_ENTRIES)
1173 user_prop_idx = 0;
1174
1175 /* read user property name and value */
1176 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.user_props[user_prop_idx].name);
1177 if (!isttest(props))
1178 goto end;
1179 props = mqtt_read_string(props, &mpkt->data.connack.var_hdr.props.user_props[user_prop_idx].value);
1180 ++user_prop_idx;
1181 break;
1182
1183 case MQTT_PROP_AUTHENTICATION_METHOD:
1184 if (fields & MQTT_FN_BIT_AUTHENTICATION_METHOD)
1185 goto end;
1186 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.authentication_method);
1187 fields |= MQTT_FN_BIT_AUTHENTICATION_METHOD;
1188 break;
1189
1190 case MQTT_PROP_AUTHENTICATION_DATA:
1191 if (fields & MQTT_FN_BIT_AUTHENTICATION_DATA)
1192 goto end;
1193 props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.authentication_data);
1194 fields |= MQTT_FN_BIT_AUTHENTICATION_DATA;
1195 break;
1196
1197 default:
1198 return 0;
1199 }
1200
1201 if (!isttest(props))
1202 goto end;
1203 }
1204 }
1205
1206 if ((orig_len - istlen(parser)) == mpkt->fixed_hdr.remaining_length)
1207 ret = MQTT_VALID_MESSAGE;
1208 end:
1209 return ret;
1210}
1211
1212
1213/* Parses and validates a MQTT packet
1214 * https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718028
1215 *
1216 * For now, due to HAProxy limitation, only validation of CONNECT and CONNACK packets
1217 * are supported.
1218 *
1219 * - check FIXED_HDR
1220 * - check remaining length
1221 * - check variable headers and payload
1222 *
1223 * if <mpkt> is not NULL, then this structure will be filled up as well. An
1224 * unsupported packet type is considered as invalid. It is not a problem for now
1225 * because only the first packet on each side can be parsed (CONNECT for the
1226 * client and CONNACK for the server).
1227 *
1228 * Returns:
1229 * MQTT_INVALID_MESSAGE if the message is invalid
1230 * MQTT_NEED_MORE_DATA if we need more data to fully validate the message
1231 * MQTT_VALID_MESSAGE if the message looks valid
1232 */
1233int mqtt_validate_message(const struct ist msg, struct mqtt_pkt *mpkt)
1234{
1235 struct ist parser;
1236 struct mqtt_pkt tmp_mpkt;
1237 int ret = MQTT_INVALID_MESSAGE;
1238
1239 if (!mpkt)
1240 mpkt = &tmp_mpkt;
1241 memset(mpkt, 0, sizeof(*mpkt));
1242
1243 parser = msg;
1244 if (istlen(msg) < MQTT_MIN_PKT_SIZE) {
1245 ret = MQTT_NEED_MORE_DATA;
1246 goto end;
1247 }
1248
1249 /* parse the MQTT fixed header */
1250 parser = mqtt_read_fixed_hdr(parser, mpkt);
1251 if (!isttest(parser)) {
1252 ret = MQTT_INVALID_MESSAGE;
1253 goto end;
1254 }
1255
1256 /* Now parsing "remaining length" field */
1257 parser = mqtt_read_varint(parser, &mpkt->fixed_hdr.remaining_length);
1258 if (!isttest(parser)) {
1259 ret = MQTT_INVALID_MESSAGE;
1260 goto end;
1261 }
1262
1263 if (istlen(parser) < mpkt->fixed_hdr.remaining_length)
1264 return MQTT_NEED_MORE_DATA;
1265
1266 /* Now parsing the variable header and payload, which is based on the packet type */
1267 switch (mpkt->fixed_hdr.type) {
1268 case MQTT_CPT_CONNECT:
1269 ret = mqtt_parse_connect(parser, mpkt);
1270 break;
1271 case MQTT_CPT_CONNACK:
1272 ret = mqtt_parse_connack(parser, mpkt);
1273 break;
1274 default:
1275 break;
1276 }
1277
1278 end:
1279 return ret;
1280}