Coverage Report

Created: 2026-10-08 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ndpi/src/lib/protocols/mqtt.c
Line
Count
Source
1
/*
2
 * mqtt.c
3
 *
4
 * Copyright (C) 2016 Sorin Zamfir <sorin.zamfir@yahoo.com>
5
 *
6
 * This file is part of nDPI, an open source deep packet inspection
7
 * library based on the OpenDPI and PACE technology by ipoque GmbH
8
 *
9
 * nDPI is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Lesser General Public License as published by
11
 * the Free Software Foundation, either version 3 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * nDPI is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU Lesser General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public License
20
 * along with nDPI.  If not, see <http://www.gnu.org/licenses/>.
21
 *
22
 */
23
24
#include "ndpi_protocol_ids.h"
25
26
#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_MQTT
27
28
#include "ndpi_api.h"
29
#include "ndpi_private.h"
30
31
32
/**
33
 * The type of control messages in mqtt version 3.1.1
34
 * see http://docs.oasis-open.org/mqtt/mqtt/v3.1.1
35
 */
36
enum MQTT_PACKET_TYPES {
37
  CONNECT = 1,
38
  CONNACK = 2,
39
  PUBLISH = 3,
40
  PUBACK = 4,
41
  PUBREC = 5,
42
  PUBREL = 6,
43
  PUBCOMP = 7,
44
  SUBSCRIBE = 8,
45
  SUBACK = 9,
46
  UNSUBSCRIBE = 10,
47
  UNSUBACK = 11,
48
  PINGREQ = 12,
49
  PINGRESP = 13,
50
  DISCONNECT = 14
51
};
52
53
/**
54
 * Entry point when protocol is identified.
55
 */
56
static void ndpi_int_mqtt_add_connection (struct ndpi_detection_module_struct *ndpi_struct,
57
    struct ndpi_flow_struct *flow)
58
2.62k
{
59
2.62k
  ndpi_set_detected_protocol(ndpi_struct, &flow->core,
60
2.62k
           NDPI_PROTOCOL_MQTT,NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI);
61
2.62k
  NDPI_LOG_INFO(ndpi_struct, "found Mqtt\n");
62
2.62k
}
63
64
static int64_t get_var_int(const unsigned char *buf, int buf_len, u_int8_t *num_bytes)
65
2.79M
{
66
2.79M
  int i, multiplier = 1;
67
2.79M
  u_int32_t value = 0;
68
2.79M
  u_int8_t encodedByte;
69
70
2.79M
  *num_bytes= 0;
71
5.57M
  for (i = 0; i < 4; i++) {
72
4.99M
    if (i >= buf_len)
73
51.3k
      return -1;
74
4.94M
    (*num_bytes)++;
75
4.94M
    encodedByte = buf[i];
76
4.94M
    value += ((encodedByte & 127) * multiplier);
77
4.94M
    if ((encodedByte & 128) == 0)
78
2.16M
      break;
79
2.77M
    multiplier *= 128;
80
2.77M
  }
81
2.74M
  return value;
82
2.79M
}
83
84
/**
85
 * Dissector function that searches Mqtt headers
86
 */
87
static void ndpi_search_mqtt(struct ndpi_detection_module_struct *ndpi_struct,
88
           struct ndpi_flow_struct *flow)
89
3.17M
{
90
3.17M
  u_int8_t pt,flags, rl_len;
91
3.17M
  int64_t rl;
92
93
3.17M
  NDPI_LOG_DBG(ndpi_struct, "search Mqtt\n");
94
3.17M
  struct ndpi_packet_struct *packet = &ndpi_struct->packet;
95
3.17M
  if (flow->core.packet_counter > 10) {
96
93
    NDPI_LOG_DBG(ndpi_struct, "Excluding Mqtt .. mandatory header not found!\n");
97
93
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
98
93
    return;
99
93
  }
100
101
3.17M
  NDPI_LOG_DBG2(ndpi_struct, "====>>>> Mqtt header: %4x%4x%4x%4x [len: %u]\n",
102
3.17M
          packet->payload_packet_len > 0 ? packet->payload[0] : '.',
103
3.17M
          packet->payload_packet_len > 1 ? packet->payload[1] : '.',
104
3.17M
          packet->payload_packet_len > 2 ? packet->payload[2] : '.',
105
3.17M
          packet->payload_packet_len > 3 ? packet->payload[3] : '.',
106
3.17M
          packet->payload_packet_len);
107
3.17M
  if (packet->payload_packet_len < 2) {
108
371k
    NDPI_LOG_DBG(ndpi_struct, "Excluding Mqtt .. mandatory header not found!\n");
109
371k
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
110
371k
    return;
111
371k
  }
112
  // we extract the remaining length
113
2.79M
  rl = get_var_int(&packet->payload[1], packet->payload_packet_len - 1, &rl_len);
114
2.79M
  if (rl < 0) {
115
51.3k
    NDPI_LOG_DBG(ndpi_struct, "Excluding Mqtt .. invalid length!\n");
116
51.3k
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
117
51.3k
    return;
118
51.3k
  }
119
2.74M
  NDPI_LOG_DBG(ndpi_struct, "Mqtt: msg_len %d\n", (unsigned long long)rl);
120
2.74M
  if (packet->payload_packet_len != rl + 1 + rl_len) {
121
2.70M
    NDPI_LOG_DBG(ndpi_struct, "Excluding Mqtt .. maximum packet size exceeded!\n");
122
2.70M
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
123
2.70M
    return;
124
2.70M
  }
125
  // we extract the packet type
126
42.3k
  pt = (u_int8_t) ((packet->payload[0] & 0xF0) >> 4);
127
42.3k
  NDPI_LOG_DBG2(ndpi_struct,"====>>>> Mqtt packet type: [%d]\n",pt);
128
42.3k
  if ((pt == 0) || (pt == 15)) {
129
13.0k
    NDPI_LOG_DBG(ndpi_struct, "Excluding Mqtt .. invalid packet type!\n");
130
13.0k
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
131
13.0k
    return;
132
13.0k
  }
133
  // we extract the flags
134
29.3k
  flags = (u_int8_t) (packet->payload[0] & 0x0F);
135
29.3k
  NDPI_LOG_DBG2(ndpi_struct,"====>>>> Mqtt flags type: [%d]\n",flags);
136
  // first stage verification
137
29.3k
  if (((pt == CONNECT) || (pt == CONNACK) || (pt == PUBACK) || (pt == PUBREC) ||
138
19.3k
          (pt == PUBCOMP) || (pt == SUBACK) || (pt == UNSUBACK) || (pt == PINGREQ) ||
139
17.3k
          (pt == PINGRESP) || (pt == DISCONNECT)) && (flags > 0)) {
140
14.2k
    NDPI_LOG_DBG(ndpi_struct, "Excluding Mqtt invalid Packet-Flag combination flag!=0\n");
141
14.2k
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
142
14.2k
    return;
143
14.2k
  }
144
15.0k
  if (((pt == PUBREL) || (pt == SUBSCRIBE) || (pt == UNSUBSCRIBE)) && (flags != 2)) {
145
5.99k
    NDPI_LOG_DBG(ndpi_struct, "Excluding Mqtt invalid Packet-Flag combination flag!=2\n");
146
5.99k
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
147
5.99k
    return;
148
5.99k
  }
149
9.10k
  NDPI_LOG_DBG2(ndpi_struct,"====>>>> Passed first stage of identification\n");
150
  // second stage verification (no payload, just variable headers)
151
9.10k
  if ((pt == CONNACK) || (pt == PUBACK) || (pt == PUBREL) ||
152
7.90k
      (pt == PUBREC) || (pt == PUBCOMP) || (pt == UNSUBACK)) {
153
2.09k
    if (packet->payload_packet_len != 4) { // these packets are always 4 bytes long
154
1.64k
      NDPI_LOG_DBG(ndpi_struct, "Excluding Mqtt invalid Packet-Length < 4 \n");
155
1.64k
      NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
156
1.64k
      return;
157
1.64k
    } else {
158
455
      NDPI_LOG_INFO(ndpi_struct, "found Mqtt CONNACK/PUBACK/PUBREL/PUBREC/PUBCOMP/UNSUBACK\n");
159
455
      ndpi_int_mqtt_add_connection(ndpi_struct,flow);
160
455
      return;
161
455
    }
162
2.09k
  }
163
7.00k
  if ((pt == PINGREQ) || (pt == PINGRESP) || (pt == DISCONNECT)) {
164
726
    if (packet->payload_packet_len != 2) { // these packets are always 2 bytes long
165
344
      NDPI_LOG_DBG(ndpi_struct, "Excluding Mqtt invalid Packet-Length <2 \n");
166
344
      NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
167
344
      return;
168
382
    } else {
169
382
      NDPI_LOG_INFO(ndpi_struct, "found Mqtt PING/PINGRESP/DISCONNECT\n");
170
382
      ndpi_int_mqtt_add_connection(ndpi_struct,flow);
171
382
      return;
172
382
    }
173
726
  }
174
6.27k
  NDPI_LOG_DBG2(ndpi_struct,"====>>>> Passed second stage of identification\n");
175
  // third stage verification (payload)
176
6.27k
  if (pt == CONNECT) {
177
266
    NDPI_LOG_DBG(ndpi_struct, "found Mqtt CONNECT\n");
178
266
    ndpi_int_mqtt_add_connection(ndpi_struct,flow);
179
266
    return;
180
266
  }
181
6.01k
  if (pt == PUBLISH) {
182
    // payload CAN be zero bytes length (section 3.3.3 of MQTT standard)
183
4.83k
    u_int8_t qos = (u_int8_t) (flags & 0x06) >> 1;
184
4.83k
    u_int8_t dup = (u_int8_t) (flags & 0x08) >> 3;
185
4.83k
    if (qos > 2) { // qos values possible are 0,1,2
186
798
      NDPI_LOG_DBG(ndpi_struct, "Excluding Mqtt invalid PUBLISH qos\n");
187
798
      NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
188
798
      return;
189
798
    }
190
4.03k
    if (qos == 0) {
191
1.45k
      if (dup != 0) {
192
545
        NDPI_LOG_DBG(ndpi_struct, "Excluding Mqtt invalid PUBLISH qos0 and dup combination\n");
193
545
        NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
194
545
        return;
195
545
      }
196
911
      if (packet->payload_packet_len < 5) { // at least topic (3Bytes + 2Bytes fixed header)
197
544
        NDPI_LOG_DBG(ndpi_struct, "Excluding Mqtt invalid PUBLISH qos0 size\n");
198
544
        NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
199
544
        return;
200
544
      }
201
911
    }
202
2.94k
    if ((qos == 1) || (qos == 2)) {
203
2.58k
      if (packet->payload_packet_len < 7 ) { // at least topic + pkt identifier (3Bytes + 2Bytes + 2Bytes fixed header)
204
1.85k
        NDPI_LOG_DBG(ndpi_struct, "Excluding Mqtt invalid PUBLISH qos1&2\n");
205
1.85k
        NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
206
1.85k
        return;
207
1.85k
      }
208
2.58k
    }
209
1.09k
    NDPI_LOG_INFO(ndpi_struct, "found Mqtt PUBLISH\n");
210
1.09k
    ndpi_int_mqtt_add_connection(ndpi_struct,flow);
211
1.09k
    return;
212
2.94k
  }
213
1.17k
  if (pt == SUBSCRIBE) {
214
412
    if (packet->payload_packet_len < 8) { // at least one topic+filter is required in the payload
215
239
      NDPI_LOG_DBG(ndpi_struct, "Excluding Mqtt invalid SUBSCRIBE\n");
216
239
      NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
217
239
      return;
218
239
    } else {
219
173
      NDPI_LOG_INFO(ndpi_struct, "found Mqtt SUBSCRIBE\n");
220
173
      ndpi_int_mqtt_add_connection(ndpi_struct,flow);
221
173
      return;
222
173
    }
223
412
  }
224
764
  if (pt == SUBACK ) {
225
311
    if (packet->payload_packet_len <5 ) { // must have at least a response code
226
196
      NDPI_LOG_DBG(ndpi_struct, "Excluding Mqtt invalid SUBACK\n");
227
196
      NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
228
196
      return;
229
196
    } else {
230
115
      NDPI_LOG_INFO(ndpi_struct, "found Mqtt SUBACK\n");
231
115
      ndpi_int_mqtt_add_connection(ndpi_struct,flow);
232
115
      return;
233
115
    }
234
311
  }
235
453
  if (pt == UNSUBSCRIBE) {
236
453
    if (packet->payload_packet_len < 7) { // at least a topic
237
310
      NDPI_LOG_DBG(ndpi_struct, "Excluding Mqtt invalid UNSUBSCRIBE\n");
238
310
      NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
239
310
      return;
240
310
    } else {
241
143
      NDPI_LOG_INFO(ndpi_struct, "found Mqtt UNSUBSCRIBE\n");
242
143
      ndpi_int_mqtt_add_connection(ndpi_struct,flow);
243
143
      return;
244
143
    }
245
453
  }
246
  /* We already checked every possible values of pt: we are never here */
247
453
}
248
/**
249
 * Entry point for the ndpi library
250
 */
251
void init_mqtt_dissector (struct ndpi_detection_module_struct *ndpi_struct)
252
15.4k
{
253
15.4k
  ndpi_register_dissector("MQTT", ndpi_struct,
254
15.4k
                     ndpi_search_mqtt,
255
15.4k
                     NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION,
256
15.4k
                      DISSECTOR_LICENSE_LGPL,
257
15.4k
                      1, NDPI_PROTOCOL_MQTT);
258
15.4k
}
259
260