Coverage Report

Created: 2026-10-16 06:09

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