2 * Routines for MQTT Protocol dissection
4 * MQTT v5.0 support sponsored by 1byt3 <customers at 1byt3.com>
6 * By Lakshmi Narayana Madala <madalanarayana@outlook.com>
7 * Stig Bjorlykke <stig@bjorlykke.org>
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
13 * SPDX-License-Identifier: GPL-2.0-or-later
17 * Protocol description:
19 * MQTT is a Client Server publish/subscribe messaging transport
20 * protocol. The protocol runs over TCP/IP, or over other network
21 * protocols that provide ordered, lossless, bi-directional
24 * MQTT v3.1 specification:
25 * http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html
27 * MQTT v3.1.1 specification:
28 * http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/
30 * MQTT v5.0 specification:
31 * http://docs.oasis-open.org/mqtt/mqtt/v5.0/
36 #include <epan/expert.h>
37 #include <epan/packet.h>
39 #include <wsutil/array.h>
41 #include "packet-tcp.h"
42 #include "packet-tls.h"
44 #define MQTT_DEFAULT_PORT 1883 /* IANA registered under service name as mqtt */
45 #define MQTT_SSL_DEFAULT_PORT 8883 /* IANA registered under service name secure-mqtt */
47 /* MQTT Protocol Versions */
48 #define MQTT_PROTO_V31 3
49 #define MQTT_PROTO_V311 4
50 #define MQTT_PROTO_V50 5
52 #define MQTT_HDR_SIZE_BEFORE_LEN 1
54 /* MQTT Message Types */
55 #define MQTT_RESERVED 0
56 #define MQTT_CONNECT 1
57 #define MQTT_CONNACK 2
58 #define MQTT_PUBLISH 3
62 #define MQTT_PUBCOMP 7
63 #define MQTT_SUBSCRIBE 8
65 #define MQTT_UNSUBSCRIBE 10
66 #define MQTT_UNSUBACK 11
67 #define MQTT_PINGREQ 12
68 #define MQTT_PINGRESP 13
69 #define MQTT_DISCONNECT 14
71 #define MQTT_RESERVED_16 16
73 /* Flag Values to extract fields */
74 #define MQTT_MASK_MSG_TYPE 0xF0
75 #define MQTT_MASK_HDR_RESERVED 0x0F
76 #define MQTT_MASK_HDR_DUP_RESERVED 0x07
77 #define MQTT_MASK_QOS 0x06
78 #define MQTT_MASK_DUP_FLAG 0x08
79 #define MQTT_MASK_RETAIN 0x01
81 /* MQTT v5.0 Flag Values for the Subscription Options @ Subscribe Packet */
82 #define MQTT_MASK_SUBS_QOS 0x03
83 #define MQTT_MASK_SUBS_NL 0x04
84 #define MQTT_MASK_SUBS_RAP 0x08
85 #define MQTT_MASK_SUBS_RETAIN 0x30
86 #define MQTT_MASK_SUBS_RESERVED 0xC0
88 void proto_register_mqtt(void);
89 void proto_reg_handoff_mqtt(void);
91 static dissector_table_t media_type_dissector_table
;
93 static const value_string mqtt_protocol_version_vals
[] = {
94 { MQTT_PROTO_V31
, "MQTT v3.1" },
95 { MQTT_PROTO_V311
, "MQTT v3.1.1" },
96 { MQTT_PROTO_V50
, "MQTT v5.0" },
100 static const enum_val_t mqtt_protocol_version_enumvals
[] = {
101 { "none", "None", 0 },
102 { "v31", "MQTT v3.1", MQTT_PROTO_V31
},
103 { "v311", "MQTT v3.1.1", MQTT_PROTO_V311
},
104 { "v50", "MQTT v5.0", MQTT_PROTO_V50
},
108 static const value_string mqtt_msgtype_vals
[] = {
109 { MQTT_RESERVED
, "Reserved" },
110 { MQTT_CONNECT
, "Connect Command" },
111 { MQTT_CONNACK
, "Connect Ack" },
112 { MQTT_PUBLISH
, "Publish Message" },
113 { MQTT_PUBACK
, "Publish Ack" },
114 { MQTT_PUBREC
, "Publish Received" },
115 { MQTT_PUBREL
, "Publish Release" },
116 { MQTT_PUBCOMP
, "Publish Complete" },
117 { MQTT_SUBSCRIBE
, "Subscribe Request" },
118 { MQTT_SUBACK
, "Subscribe Ack" },
119 { MQTT_UNSUBSCRIBE
, "Unsubscribe Request" },
120 { MQTT_UNSUBACK
, "Unsubscribe Ack" },
121 { MQTT_PINGREQ
, "Ping Request" },
122 { MQTT_PINGRESP
, "Ping Response" },
123 { MQTT_DISCONNECT
, "Disconnect Req" },
124 { MQTT_AUTH
, "Authentication Exchange" },
125 { MQTT_RESERVED_16
, "Reserved" },
128 static value_string_ext mqtt_msgtype_vals_ext
= VALUE_STRING_EXT_INIT(mqtt_msgtype_vals
);
130 #define MQTT_QOS_ATMOST_ONCE 0
131 #define MQTT_QOS_ATLEAST_ONCE 1
132 #define MQTT_QOS_EXACTLY_ONCE 2
133 #define MQTT_QOS_RESERVED 3
135 static const value_string mqtt_qos_vals
[] = {
136 { MQTT_QOS_ATMOST_ONCE
, "At most once delivery (Fire and Forget)" },
137 { MQTT_QOS_ATLEAST_ONCE
, "At least once delivery (Acknowledged deliver)" },
138 { MQTT_QOS_EXACTLY_ONCE
, "Exactly once delivery (Assured Delivery)" },
139 { MQTT_QOS_RESERVED
, "Reserved" },
143 #define MQTT_SUBACK_FAILURE 128
145 static const value_string mqtt_subqos_vals
[] = {
146 { MQTT_QOS_ATMOST_ONCE
, "At most once delivery (Fire and Forget)" },
147 { MQTT_QOS_ATLEAST_ONCE
, "At least once delivery (Acknowledged deliver)" },
148 { MQTT_QOS_EXACTLY_ONCE
, "Exactly once delivery (Assured Delivery)" },
149 { MQTT_QOS_RESERVED
, "Reserved" },
150 { MQTT_SUBACK_FAILURE
, "Failure" },
154 #define MQTT_CON_ACCEPTED 0
155 #define MQTT_CON_REFUSED_VERSION_MISMATCH 1
156 #define MQTT_CON_REFUSED_ID_REJECTED 2
157 #define MQTT_CON_REFUSED_SERVER_UNAVAILABLE 3
158 #define MQTT_CON_REFUSED_BAD_USER_PASSWD 4
159 #define MQTT_CON_REFUSED_UNAUTHORIZED 5
161 static const value_string mqtt_conack_vals
[] = {
162 { MQTT_CON_ACCEPTED
, "Connection Accepted" },
163 { MQTT_CON_REFUSED_VERSION_MISMATCH
, "Connection Refused: unacceptable protocol version" },
164 { MQTT_CON_REFUSED_ID_REJECTED
, "Connection Refused: identifier rejected" },
165 { MQTT_CON_REFUSED_SERVER_UNAVAILABLE
, "Connection Refused: server unavailable" },
166 { MQTT_CON_REFUSED_BAD_USER_PASSWD
, "Connection Refused: bad user name or password" },
167 { MQTT_CON_REFUSED_UNAUTHORIZED
, "Connection Refused: not authorized" },
171 #define MQTT_CONMASK_USER 0x80
172 #define MQTT_CONMASK_PASSWD 0x40
173 #define MQTT_CONMASK_RETAIN 0x20
174 #define MQTT_CONMASK_QOS 0x18
175 #define MQTT_CONMASK_WILLFLAG 0x04
176 #define MQTT_CONMASK_CLEANSESS 0x02
177 #define MQTT_CONMASK_RESERVED 0x01
179 #define MQTT_CONACKMASK_RESERVED 0xFE
180 #define MQTT_CONACKMASK_SP 0x01
182 /* The protocol version is present in the CONNECT message. */
184 uint8_t runtime_proto_version
;
185 wmem_map_t
*topic_alias_map
;
188 typedef struct _mqtt_message_decode_t
{
189 unsigned match_criteria
;
192 unsigned msg_decoding
;
193 char *payload_proto_name
;
194 dissector_handle_t payload_proto
;
195 } mqtt_message_decode_t
;
197 typedef struct _mqtt_properties_t
{
198 const uint8_t *content_type
;
199 uint32_t topic_alias
;
202 #define MATCH_CRITERIA_EQUAL 0
203 #define MATCH_CRITERIA_CONTAINS 1
204 #define MATCH_CRITERIA_STARTS_WITH 2
205 #define MATCH_CRITERIA_ENDS_WITH 3
206 #define MATCH_CRITERIA_REGEX 4
208 static const value_string match_criteria
[] = {
209 { MATCH_CRITERIA_EQUAL
, "Equal to" },
210 { MATCH_CRITERIA_CONTAINS
, "Contains" },
211 { MATCH_CRITERIA_STARTS_WITH
, "Starts with" },
212 { MATCH_CRITERIA_ENDS_WITH
, "Ends with" },
213 { MATCH_CRITERIA_REGEX
, "Regular Expression" },
217 #define MSG_DECODING_NONE 0
218 #define MSG_DECODING_COMPRESSED 1
220 static const value_string msg_decoding
[] = {
221 { MSG_DECODING_NONE
, "none" },
222 { MSG_DECODING_COMPRESSED
, "compressed" },
226 #define PROP_PAYLOAD_FORMAT_INDICATOR 0x01
227 #define PROP_PUBLICATION_EXPIRY_INTERVAL 0x02
228 #define PROP_CONTENT_TYPE 0x03
229 #define PROP_RESPONSE_TOPIC 0x08
230 #define PROP_CORRELATION_DATA 0x09
231 #define PROP_SUBSCRIPTION_IDENTIFIER 0x0B
232 #define PROP_SESSION_EXPIRY_INTERVAL 0x11
233 #define PROP_ASSIGNED_CLIENT_IDENTIFIER 0x12
234 #define PROP_SERVER_KEEP_ALIVE 0x13
235 #define PROP_AUTH_METHOD 0x15
236 #define PROP_AUTH_DATA 0x16
237 #define PROP_REQUEST_PROBLEM_INFORMATION 0x17
238 #define PROP_WILL_DELAY_INTERVAL 0x18
239 #define PROP_REQUEST_RESPONSE_INFORMATION 0x19
240 #define PROP_RESPONSE_INFORMATION 0x1A
241 #define PROP_SERVER_REFERENCE 0x1C
242 #define PROP_REASON_STRING 0x1F
243 #define PROP_RECEIVE_MAXIMUM 0x21
244 #define PROP_TOPIC_ALIAS_MAXIMUM 0x22
245 #define PROP_TOPIC_ALIAS 0x23
246 #define PROP_MAXIMUM_QOS 0x24
247 #define PROP_RETAIN_AVAILABLE 0x25
248 #define PROP_USER_PROPERTY 0x26
249 #define PROP_MAXIMUM_PACKET_SIZE 0x27
250 #define PROP_WILDCARD_SUBSCRIPTION_AVAILABLE 0x28
251 #define PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE 0x29
252 #define PROP_SHARED_SUBSCRIPTION_AVAILABLE 0x2A
254 static const value_string mqtt_property_vals
[] = {
255 { PROP_PAYLOAD_FORMAT_INDICATOR
, "Payload Format Indicator" },
256 { PROP_PUBLICATION_EXPIRY_INTERVAL
, "Publication Expiry Interval" },
257 { PROP_CONTENT_TYPE
, "Content Type" },
258 { PROP_RESPONSE_TOPIC
, "Response Topic" },
259 { PROP_CORRELATION_DATA
, "Correlation Data" },
260 { PROP_SUBSCRIPTION_IDENTIFIER
, "Subscription Identifier" },
261 { PROP_SESSION_EXPIRY_INTERVAL
, "Session Expiry Interval" },
262 { PROP_ASSIGNED_CLIENT_IDENTIFIER
, "Assigned Client Identifier" },
263 { PROP_SERVER_KEEP_ALIVE
, "Server Keep Alive" },
264 { PROP_AUTH_METHOD
, "Authentication Method" },
265 { PROP_AUTH_DATA
, "Authentication Data" },
266 { PROP_REQUEST_PROBLEM_INFORMATION
, "Request Problem Information" },
267 { PROP_WILL_DELAY_INTERVAL
, "Will Delay Interval" },
268 { PROP_REQUEST_RESPONSE_INFORMATION
, "Request Response Information" },
269 { PROP_RESPONSE_INFORMATION
, "Response Information" },
270 { PROP_SERVER_REFERENCE
, "Server Reference" },
271 { PROP_REASON_STRING
, "Reason String" },
272 { PROP_RECEIVE_MAXIMUM
, "Receive Maximum" },
273 { PROP_TOPIC_ALIAS_MAXIMUM
, "Topic Alias Maximum" },
274 { PROP_TOPIC_ALIAS
, "Topic Alias" },
275 { PROP_MAXIMUM_QOS
, "Maximum QoS" },
276 { PROP_RETAIN_AVAILABLE
, "Retain Available" },
277 { PROP_USER_PROPERTY
, "User Property" },
278 { PROP_MAXIMUM_PACKET_SIZE
, "Maximum Packet Size" },
279 { PROP_WILDCARD_SUBSCRIPTION_AVAILABLE
, "Wildcard Subscription Available" },
280 { PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE
, "Subscription Identifier Available" },
281 { PROP_SHARED_SUBSCRIPTION_AVAILABLE
, "Shared Subscription Available" },
285 /* MQTT v5.0 Subscription Options, Retain Handling option */
286 #define SUBSCRIPTION_RETAIN_SEND 0x00
287 #define SUBSCRIPTION_RETAIN_SEND_DONT_EXIST 0x01
288 #define SUBSCRIPTION_RETAIN_DONT_SEND 0x02
289 #define SUBSCRIPTION_RETAIN_RESERVED 0x03
291 static const value_string mqtt_subscription_retain_handling
[] = {
292 { SUBSCRIPTION_RETAIN_SEND
, "Send msgs at subscription time" },
293 { SUBSCRIPTION_RETAIN_SEND_DONT_EXIST
, "Send msgs if subscription does not exist" },
294 { SUBSCRIPTION_RETAIN_DONT_SEND
, "Do not send msgs at subscription time" },
295 { SUBSCRIPTION_RETAIN_RESERVED
, "Reserved" },
299 /* MQTT v5.0 Reason Codes */
300 #define RC_SUCCESS 0x00
301 #define RC_NORMAL_DISCONNECTION 0x00
302 #define RC_GRANTED_QOS0 0x00
303 #define RC_GRANTED_QOS1 0x01
304 #define RC_GRANTED_QOS2 0x02
305 #define RC_DISCONNECT_WILL 0x04
306 #define RC_NO_MATCHING_SUBSCRIBERS 0x10
307 #define RC_NO_SUBSCRIPTION_EXISTED 0x11
308 #define RC_CONTINUE_AUTHENTICATION 0x18
309 #define RC_RE_AUTHENTICATE 0x19
310 #define RC_UNSPECIFIED_ERROR 0x80
311 #define RC_MALFORMED_PACKET 0x81
312 #define RC_PROTOCOL_ERROR 0x82
313 #define RC_IMPLEMENTATION_SPECIFIC_ERROR 0x83
314 #define RC_UNSUPPORTED_PROTOCOL_VERSION 0x84
315 #define RC_CLIENT_IDENTIFIER_NOT_VALID 0x85
316 #define RC_BAD_USER_NAME_OR_PASSWORD 0x86
317 #define RC_NOT_AUTHORIZED 0x87
318 #define RC_SERVER_UNAVAILABLE 0x88
319 #define RC_SERVER_BUSY 0x89
320 #define RC_BANNED 0x8A
321 #define RC_SERVER_SHUTTING_DOWN 0x8B
322 #define RC_BAD_AUTHENTICATION_METHOD 0x8C
323 #define RC_KEEP_ALIVE_TIMEOUT 0x8D
324 #define RC_SESSION_TAKEN_OVER 0x8E
325 #define RC_TOPIC_FILTER_INVALID 0x8F
326 #define RC_TOPIC_NAME_INVALID 0x90
327 #define RC_PACKET_IDENTIFIER_IN_USE 0x91
328 #define RC_PACKET_IDENTIFIER_NOT_FOUND 0x92
329 #define RC_RECEIVE_MAXIMUM_EXCEEDED 0x93
330 #define RC_TOPIC_ALIAS_INVALID 0x94
331 #define RC_PACKET_TOO_LARGE 0x95
332 #define RC_MESSAGE_RATE_TOO_HIGH 0x96
333 #define RC_QUOTA_EXCEEDED 0x97
334 #define RC_ADMINISTRATIVE_ACTION 0x98
335 #define RC_PAYLOAD_FORMAT_INVALID 0x99
336 #define RC_RETAIN_NOT_SUPPORTED 0x9A
337 #define RC_QOS_NOT_SUPPORTED 0x9B
338 #define RC_USE_ANOTHER_SERVER 0x9C
339 #define RC_SERVER_MOVED 0x9D
340 #define RC_SHARED_SUBSCRIPTION_NOT_SUPPORTED 0x9E
341 #define RC_CONNECTION_RATE_EXCEEDED 0x9F
342 #define RC_MAXIMUM_CONNECT_TIME 0xA0
343 #define RC_SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED 0xA1
344 #define RC_WILDCARD_SUBSCRIPTION_NOT_SUPPORTED 0xA2
346 #define RC_SUCCESS_STR "Success"
347 #define RC_NORMAL_DISCONNECTION_STR "Normal disconnection"
348 #define RC_GRANTED_QOS0_STR "Granted QoS 0"
349 #define RC_GRANTED_QOS1_STR "Granted QoS 1"
350 #define RC_GRANTED_QOS2_STR "Granted QoS 2"
351 #define RC_DISCONNECT_WILL_STR "Disconnect with Will Message"
352 #define RC_NO_MATCHING_SUBSCRIBERS_STR "No matching subscribers"
353 #define RC_NO_SUBSCRIPTION_EXISTED_STR "No subscription existed"
354 #define RC_CONTINUE_AUTHENTICATION_STR "Continue authentication"
355 #define RC_RE_AUTHENTICATE_STR "Re-authenticate"
356 #define RC_UNSPECIFIED_ERROR_STR "Unspecified error"
357 #define RC_MALFORMED_PACKET_STR "Malformed Packet"
358 #define RC_PROTOCOL_ERROR_STR "Protocol Error"
359 #define RC_IMPLEMENTATION_SPECIFIC_ERROR_STR "Implementation specific error"
360 #define RC_UNSUPPORTED_PROTOCOL_VERSION_STR "Unsupported Protocol Version"
361 #define RC_CLIENT_IDENTIFIER_NOT_VALID_STR "Client Identifier not valid"
362 #define RC_BAD_USER_NAME_OR_PASSWORD_STR "Bad User Name or Password"
363 #define RC_NOT_AUTHORIZED_STR "Not authorized"
364 #define RC_SERVER_UNAVAILABLE_STR "Server unavailable"
365 #define RC_SERVER_BUSY_STR "Server busy"
366 #define RC_BANNED_STR "Banned"
367 #define RC_SERVER_SHUTTING_DOWN_STR "Server shutting down"
368 #define RC_BAD_AUTHENTICATION_METHOD_STR "Bad authentication method"
369 #define RC_KEEP_ALIVE_TIMEOUT_STR "Keep Alive timeout"
370 #define RC_SESSION_TAKEN_OVER_STR "Session taken over"
371 #define RC_TOPIC_FILTER_INVALID_STR "Topic Filter invalid"
372 #define RC_TOPIC_NAME_INVALID_STR "Topic Name invalid"
373 #define RC_PACKET_IDENTIFIER_IN_USE_STR "Packet Identifier in use"
374 #define RC_PACKET_IDENTIFIER_NOT_FOUND_STR "Packet Identifier not found"
375 #define RC_RECEIVE_MAXIMUM_EXCEEDED_STR "Receive Maximum exceeded"
376 #define RC_TOPIC_ALIAS_INVALID_STR "Topic Alias invalid"
377 #define RC_PACKET_TOO_LARGE_STR "Packet too large"
378 #define RC_MESSAGE_RATE_TOO_HIGH_STR "Message rate too high"
379 #define RC_QUOTA_EXCEEDED_STR "Quota exceeded"
380 #define RC_ADMINISTRATIVE_ACTION_STR "Administrative action"
381 #define RC_PAYLOAD_FORMAT_INVALID_STR "Payload format invalid"
382 #define RC_RETAIN_NOT_SUPPORTED_STR "Retain not supported"
383 #define RC_QOS_NOT_SUPPORTED_STR "QoS not supported"
384 #define RC_USE_ANOTHER_SERVER_STR "Use another server"
385 #define RC_SERVER_MOVED_STR "Server moved"
386 #define RC_SHARED_SUBSCRIPTION_NOT_SUPPORTED_STR "Shared Subscription not supported"
387 #define RC_CONNECTION_RATE_EXCEEDED_STR "Connection rate exceeded"
388 #define RC_MAXIMUM_CONNECT_TIME_STR "Maximum connect time"
389 #define RC_SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED_STR "Subscription Identifiers not supported"
390 #define RC_WILDCARD_SUBSCRIPTION_NOT_SUPPORTED_STR "Wildcard Subscription not supported"
392 static const value_string mqtt_reason_code_connack_vals
[] = {
393 { RC_SUCCESS
, RC_SUCCESS_STR
},
394 { RC_UNSPECIFIED_ERROR
, RC_UNSPECIFIED_ERROR_STR
},
395 { RC_MALFORMED_PACKET
, RC_MALFORMED_PACKET_STR
},
396 { RC_PROTOCOL_ERROR
, RC_PROTOCOL_ERROR_STR
},
397 { RC_IMPLEMENTATION_SPECIFIC_ERROR
, RC_IMPLEMENTATION_SPECIFIC_ERROR_STR
},
398 { RC_UNSUPPORTED_PROTOCOL_VERSION
, RC_UNSUPPORTED_PROTOCOL_VERSION_STR
},
399 { RC_CLIENT_IDENTIFIER_NOT_VALID
, RC_CLIENT_IDENTIFIER_NOT_VALID_STR
},
400 { RC_BAD_USER_NAME_OR_PASSWORD
, RC_BAD_USER_NAME_OR_PASSWORD_STR
},
401 { RC_NOT_AUTHORIZED
, RC_NOT_AUTHORIZED_STR
},
402 { RC_SERVER_UNAVAILABLE
, RC_SERVER_UNAVAILABLE_STR
},
403 { RC_SERVER_BUSY
, RC_SERVER_BUSY_STR
},
404 { RC_BANNED
, RC_BANNED_STR
},
405 { RC_BAD_AUTHENTICATION_METHOD
, RC_BAD_AUTHENTICATION_METHOD_STR
},
406 { RC_TOPIC_NAME_INVALID
, RC_TOPIC_NAME_INVALID_STR
},
407 { RC_PACKET_TOO_LARGE
, RC_PACKET_TOO_LARGE_STR
},
408 { RC_QUOTA_EXCEEDED
, RC_QUOTA_EXCEEDED_STR
},
409 { RC_RETAIN_NOT_SUPPORTED
, RC_RETAIN_NOT_SUPPORTED_STR
},
410 { RC_QOS_NOT_SUPPORTED
, RC_QOS_NOT_SUPPORTED_STR
},
411 { RC_USE_ANOTHER_SERVER
, RC_USE_ANOTHER_SERVER_STR
},
412 { RC_SERVER_MOVED
, RC_SERVER_MOVED_STR
},
413 { RC_CONNECTION_RATE_EXCEEDED
, RC_CONNECTION_RATE_EXCEEDED_STR
},
417 static const value_string mqtt_reason_code_puback_vals
[] = {
418 { RC_SUCCESS
, RC_SUCCESS_STR
},
419 { RC_NO_MATCHING_SUBSCRIBERS
, RC_NO_MATCHING_SUBSCRIBERS_STR
},
420 { RC_UNSPECIFIED_ERROR
, RC_UNSPECIFIED_ERROR_STR
},
421 { RC_IMPLEMENTATION_SPECIFIC_ERROR
, RC_IMPLEMENTATION_SPECIFIC_ERROR_STR
},
422 { RC_NOT_AUTHORIZED
, RC_NOT_AUTHORIZED_STR
},
423 { RC_TOPIC_NAME_INVALID
, RC_TOPIC_NAME_INVALID_STR
},
424 { RC_PACKET_IDENTIFIER_IN_USE
, RC_PACKET_IDENTIFIER_IN_USE_STR
},
425 { RC_QUOTA_EXCEEDED
, RC_QUOTA_EXCEEDED_STR
},
426 { RC_PAYLOAD_FORMAT_INVALID
, RC_PAYLOAD_FORMAT_INVALID_STR
},
430 static const value_string mqtt_reason_code_pubrel_vals
[] = {
431 { RC_SUCCESS
, RC_SUCCESS_STR
},
432 { RC_PACKET_IDENTIFIER_NOT_FOUND
, RC_PACKET_IDENTIFIER_NOT_FOUND_STR
},
436 static const value_string mqtt_reason_code_suback_vals
[] = {
437 { RC_GRANTED_QOS0
, RC_GRANTED_QOS0_STR
},
438 { RC_GRANTED_QOS1
, RC_GRANTED_QOS1_STR
},
439 { RC_GRANTED_QOS2
, RC_GRANTED_QOS2_STR
},
440 { RC_UNSPECIFIED_ERROR
, RC_UNSPECIFIED_ERROR_STR
},
441 { RC_IMPLEMENTATION_SPECIFIC_ERROR
, RC_IMPLEMENTATION_SPECIFIC_ERROR_STR
},
442 { RC_NOT_AUTHORIZED
, RC_NOT_AUTHORIZED_STR
},
443 { RC_TOPIC_FILTER_INVALID
, RC_TOPIC_FILTER_INVALID_STR
},
444 { RC_PACKET_IDENTIFIER_IN_USE
, RC_PACKET_IDENTIFIER_IN_USE_STR
},
445 { RC_QUOTA_EXCEEDED
, RC_QUOTA_EXCEEDED_STR
},
446 { RC_SHARED_SUBSCRIPTION_NOT_SUPPORTED
, RC_SHARED_SUBSCRIPTION_NOT_SUPPORTED_STR
},
447 { RC_SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED
, RC_SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED_STR
},
448 { RC_WILDCARD_SUBSCRIPTION_NOT_SUPPORTED
, RC_WILDCARD_SUBSCRIPTION_NOT_SUPPORTED_STR
},
452 static const value_string mqtt_reason_code_unsuback_vals
[] = {
453 { RC_SUCCESS
, RC_SUCCESS_STR
},
454 { RC_NO_SUBSCRIPTION_EXISTED
, RC_NO_SUBSCRIPTION_EXISTED_STR
},
455 { RC_IMPLEMENTATION_SPECIFIC_ERROR
, RC_IMPLEMENTATION_SPECIFIC_ERROR_STR
},
456 { RC_NOT_AUTHORIZED
, RC_NOT_AUTHORIZED_STR
},
457 { RC_TOPIC_FILTER_INVALID
, RC_TOPIC_FILTER_INVALID_STR
},
458 { RC_PACKET_IDENTIFIER_IN_USE
, RC_PACKET_IDENTIFIER_IN_USE_STR
},
462 static const value_string mqtt_reason_code_disconnect_vals
[] = {
463 { RC_NORMAL_DISCONNECTION
, RC_NORMAL_DISCONNECTION_STR
},
464 { RC_DISCONNECT_WILL
, RC_DISCONNECT_WILL_STR
},
465 { RC_UNSPECIFIED_ERROR
, RC_UNSPECIFIED_ERROR_STR
},
466 { RC_MALFORMED_PACKET
, RC_MALFORMED_PACKET_STR
},
467 { RC_PROTOCOL_ERROR
, RC_PROTOCOL_ERROR_STR
},
468 { RC_IMPLEMENTATION_SPECIFIC_ERROR
, RC_IMPLEMENTATION_SPECIFIC_ERROR_STR
},
469 { RC_NOT_AUTHORIZED
, RC_NOT_AUTHORIZED_STR
},
470 { RC_SERVER_BUSY
, RC_SERVER_BUSY_STR
},
471 { RC_SERVER_SHUTTING_DOWN
, RC_SERVER_SHUTTING_DOWN_STR
},
472 /* Bad authentication method: check Table 2.6 and Table 3.13 */
473 { RC_BAD_AUTHENTICATION_METHOD
, RC_BAD_AUTHENTICATION_METHOD_STR
},
474 { RC_KEEP_ALIVE_TIMEOUT
, RC_KEEP_ALIVE_TIMEOUT_STR
},
475 { RC_SESSION_TAKEN_OVER
, RC_SESSION_TAKEN_OVER_STR
},
476 { RC_TOPIC_FILTER_INVALID
, RC_TOPIC_FILTER_INVALID_STR
},
477 { RC_TOPIC_NAME_INVALID
, RC_TOPIC_NAME_INVALID_STR
},
478 { RC_RECEIVE_MAXIMUM_EXCEEDED
, RC_RECEIVE_MAXIMUM_EXCEEDED_STR
},
479 { RC_TOPIC_ALIAS_INVALID
, RC_TOPIC_ALIAS_INVALID_STR
},
480 { RC_PACKET_TOO_LARGE
, RC_PACKET_TOO_LARGE_STR
},
481 { RC_MESSAGE_RATE_TOO_HIGH
, RC_MESSAGE_RATE_TOO_HIGH_STR
},
482 { RC_QUOTA_EXCEEDED
, RC_QUOTA_EXCEEDED_STR
},
483 { RC_ADMINISTRATIVE_ACTION
, RC_ADMINISTRATIVE_ACTION_STR
},
484 { RC_PAYLOAD_FORMAT_INVALID
, RC_PAYLOAD_FORMAT_INVALID_STR
},
485 { RC_RETAIN_NOT_SUPPORTED
, RC_RETAIN_NOT_SUPPORTED_STR
},
486 { RC_QOS_NOT_SUPPORTED
, RC_QOS_NOT_SUPPORTED_STR
},
487 { RC_USE_ANOTHER_SERVER
, RC_USE_ANOTHER_SERVER_STR
},
488 { RC_SERVER_MOVED
, RC_SERVER_MOVED_STR
},
489 { RC_SHARED_SUBSCRIPTION_NOT_SUPPORTED
, RC_SHARED_SUBSCRIPTION_NOT_SUPPORTED_STR
},
490 { RC_CONNECTION_RATE_EXCEEDED
, RC_CONNECTION_RATE_EXCEEDED_STR
},
491 { RC_MAXIMUM_CONNECT_TIME
, RC_MAXIMUM_CONNECT_TIME_STR
},
492 { RC_SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED
, RC_SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED_STR
},
493 { RC_WILDCARD_SUBSCRIPTION_NOT_SUPPORTED
, RC_WILDCARD_SUBSCRIPTION_NOT_SUPPORTED_STR
},
497 static const value_string mqtt_reason_code_auth_vals
[] = {
498 { RC_SUCCESS
, RC_SUCCESS_STR
},
499 { RC_CONTINUE_AUTHENTICATION
, RC_CONTINUE_AUTHENTICATION_STR
},
500 { RC_RE_AUTHENTICATE
, RC_RE_AUTHENTICATE_STR
},
504 static mqtt_message_decode_t
*mqtt_message_decodes
;
505 static unsigned num_mqtt_message_decodes
;
506 static int default_protocol_version
;
508 static dissector_handle_t mqtt_handle
;
510 static heur_dissector_list_t mqtt_topic_subdissector
;
512 /* Initialize the protocol and registered fields */
513 static int proto_mqtt
;
516 static int hf_mqtt_hdrflags
;
517 static int hf_mqtt_msg_len
;
518 static int hf_mqtt_msg_type
;
519 static int hf_mqtt_reserved
;
520 static int hf_mqtt_dup_flag
;
521 static int hf_mqtt_qos_level
;
522 static int hf_mqtt_retain
;
523 static int hf_mqtt_retain_reserved
;
524 static int hf_mqtt_conack_reserved
;
525 static int hf_mqtt_conack_flags
;
526 static int hf_mqtt_conackflag_reserved
;
527 static int hf_mqtt_conackflag_sp
;
528 static int hf_mqtt_conack_code
;
529 static int hf_mqtt_msgid
;
530 static int hf_mqtt_sub_qos
;
531 static int hf_mqtt_suback_qos
;
532 static int hf_mqtt_topic_len
;
533 static int hf_mqtt_topic
;
534 static int hf_mqtt_will_topic_len
;
535 static int hf_mqtt_will_topic
;
536 static int hf_mqtt_will_msg_len
;
537 static int hf_mqtt_will_msg
;
538 static int hf_mqtt_will_msg_text
;
539 static int hf_mqtt_username_len
;
540 static int hf_mqtt_username
;
541 static int hf_mqtt_passwd_len
;
542 static int hf_mqtt_passwd
;
543 static int hf_mqtt_pubmsg
;
544 static int hf_mqtt_pubmsg_text
;
545 static int hf_mqtt_pubmsg_decoded
;
546 static int hf_mqtt_proto_len
;
547 static int hf_mqtt_proto_name
;
548 static int hf_mqtt_client_id_len
;
549 static int hf_mqtt_client_id
;
550 static int hf_mqtt_proto_ver
;
551 static int hf_mqtt_conflags
;
552 static int hf_mqtt_conflag_user
;
553 static int hf_mqtt_conflag_passwd
;
554 static int hf_mqtt_conflag_will_retain
;
555 static int hf_mqtt_conflag_will_qos
;
556 static int hf_mqtt_conflag_will_flag
;
557 static int hf_mqtt_conflag_clean_sess
;
558 static int hf_mqtt_conflag_reserved
;
559 static int hf_mqtt_keep_alive
;
560 static int hf_mqtt_subscription_options
;
562 /* MQTT v5.0 Reason Codes */
563 static int hf_mqtt_reason_code_connack
;
564 static int hf_mqtt_reason_code_puback
;
565 static int hf_mqtt_reason_code_pubrec
;
566 static int hf_mqtt_reason_code_pubrel
;
567 static int hf_mqtt_reason_code_pubcomp
;
568 static int hf_mqtt_reason_code_suback
;
569 static int hf_mqtt_reason_code_unsuback
;
570 static int hf_mqtt_reason_code_disconnect
;
571 static int hf_mqtt_reason_code_auth
;
573 /* MQTT v5.0 Subscribe Options */
574 static int hf_mqtt_subscription_qos
;
575 static int hf_mqtt_subscription_nl
;
576 static int hf_mqtt_subscription_rap
;
577 static int hf_mqtt_subscription_retain
;
578 static int hf_mqtt_subscription_reserved
;
580 /* MQTT v5.0 Properties */
581 static int hf_mqtt_property_len
;
582 static int hf_mqtt_property
;
583 static int hf_mqtt_will_property
;
584 static int hf_mqtt_property_id
;
585 static int hf_mqtt_prop_num
;
586 static int hf_mqtt_prop_content_type
;
587 static int hf_mqtt_prop_max_qos
;
588 static int hf_mqtt_prop_topic_alias
;
589 static int hf_mqtt_prop_unknown
;
590 static int hf_mqtt_prop_string_len
;
591 static int hf_mqtt_prop_string
;
592 static int hf_mqtt_prop_key_len
;
593 static int hf_mqtt_prop_key
;
594 static int hf_mqtt_prop_value_len
;
595 static int hf_mqtt_prop_value
;
597 /* Initialize the subtree pointers */
598 static int ett_mqtt_hdr
;
599 static int ett_mqtt_msg
;
600 static int ett_mqtt_hdr_flags
;
601 static int ett_mqtt_con_flags
;
602 static int ett_mqtt_conack_flags
;
603 static int ett_mqtt_property
;
604 static int ett_mqtt_subscription_flags
;
606 /* Initialize the expert fields */
607 static expert_field ei_illegal_length
;
608 static expert_field ei_unknown_version
;
609 static expert_field ei_unknown_topic_alias
;
611 /* Reassemble SMPP TCP segments */
612 static bool reassemble_mqtt_over_tcp
= true;
614 /* Show Publish Message as text */
615 static bool show_msg_as_text
;
617 static unsigned get_mqtt_pdu_len(packet_info
*pinfo _U_
, tvbuff_t
*tvb
,
618 int offset
, void *data _U_
)
623 len_offset
= tvb_get_varint(tvb
, (offset
+ MQTT_HDR_SIZE_BEFORE_LEN
), FT_VARINT_MAX_LEN
, &msg_len
, ENC_VARINT_PROTOBUF
);
625 /* Explicitly downcast the value, because the length can never be more than 4 bytes */
626 return (unsigned)(msg_len
+ len_offset
+ MQTT_HDR_SIZE_BEFORE_LEN
);
629 static void *mqtt_message_decode_copy_cb(void *dest
, const void *orig
, size_t len _U_
)
631 const mqtt_message_decode_t
*o
= (const mqtt_message_decode_t
*)orig
;
632 mqtt_message_decode_t
*d
= (mqtt_message_decode_t
*)dest
;
634 d
->match_criteria
= o
->match_criteria
;
635 d
->topic_pattern
= g_strdup(o
->topic_pattern
);
636 d
->msg_decoding
= o
->msg_decoding
;
637 d
->payload_proto_name
= g_strdup(o
->payload_proto_name
);
638 d
->payload_proto
= o
->payload_proto
;
643 static bool mqtt_message_decode_update_cb(void *record
, char **error
)
645 mqtt_message_decode_t
*u
= (mqtt_message_decode_t
*)record
;
647 if (u
->topic_pattern
== NULL
|| strlen(u
->topic_pattern
) == 0)
649 *error
= g_strdup("Missing topic pattern");
653 if (u
->payload_proto_name
== NULL
|| strlen(u
->payload_proto_name
) == 0)
655 *error
= g_strdup("Missing payload protocol");
659 if (u
->match_criteria
== MATCH_CRITERIA_REGEX
)
661 u
->topic_regex
= g_regex_new(u
->topic_pattern
, (GRegexCompileFlags
) G_REGEX_OPTIMIZE
, (GRegexMatchFlags
) 0, NULL
);
664 *error
= ws_strdup_printf("Invalid regex: %s", u
->topic_pattern
);
672 static void mqtt_message_decode_free_cb(void *record
)
674 mqtt_message_decode_t
*u
= (mqtt_message_decode_t
*)record
;
676 g_free(u
->topic_pattern
);
679 g_regex_unref(u
->topic_regex
);
681 g_free(u
->payload_proto_name
);
684 UAT_VS_DEF(message_decode
, match_criteria
, mqtt_message_decode_t
, unsigned, MATCH_CRITERIA_EQUAL
, "Equal to")
685 UAT_CSTRING_CB_DEF(message_decode
, topic_pattern
, mqtt_message_decode_t
)
686 UAT_VS_DEF(message_decode
, msg_decoding
, mqtt_message_decode_t
, unsigned, MSG_DECODING_NONE
, "none")
687 UAT_DISSECTOR_DEF(message_decode
, payload_proto
, payload_proto
, payload_proto_name
, mqtt_message_decode_t
)
689 static bool mqtt_user_decode_message(proto_tree
*tree
, proto_tree
*mqtt_tree
, packet_info
*pinfo
, const uint8_t *topic_str
, tvbuff_t
*msg_tvb
)
691 mqtt_message_decode_t
*message_decode_entry
= NULL
;
692 size_t topic_str_len
= strlen(topic_str
);
693 size_t topic_pattern_len
;
694 bool match_found
= false;
696 if (topic_str_len
== 0)
698 /* No topic to match */
702 for (unsigned i
= 0; i
< num_mqtt_message_decodes
&& !match_found
; i
++)
704 message_decode_entry
= &mqtt_message_decodes
[i
];
705 switch (message_decode_entry
->match_criteria
)
707 case MATCH_CRITERIA_EQUAL
:
708 match_found
= (strcmp(topic_str
, message_decode_entry
->topic_pattern
) == 0);
710 case MATCH_CRITERIA_CONTAINS
:
711 match_found
= (strstr(topic_str
, message_decode_entry
->topic_pattern
) != NULL
);
713 case MATCH_CRITERIA_STARTS_WITH
:
714 topic_pattern_len
= strlen(message_decode_entry
->topic_pattern
);
715 match_found
= ((topic_str_len
>= topic_pattern_len
) &&
716 (strncmp(topic_str
, message_decode_entry
->topic_pattern
, topic_pattern_len
) == 0));
718 case MATCH_CRITERIA_ENDS_WITH
:
719 topic_pattern_len
= strlen(message_decode_entry
->topic_pattern
);
720 match_found
= ((topic_str_len
>= topic_pattern_len
) &&
721 (strcmp(topic_str
+ (topic_str_len
- topic_pattern_len
), message_decode_entry
->topic_pattern
) == 0));
723 case MATCH_CRITERIA_REGEX
:
724 if (message_decode_entry
->topic_regex
)
726 GMatchInfo
*match_info
= NULL
;
727 /* DISSECTOR_ASSERT(g_utf8_validate(topic_str, -1, NULL)); */
728 g_regex_match(message_decode_entry
->topic_regex
, topic_str
, (GRegexMatchFlags
) 0, &match_info
);
729 match_found
= g_match_info_matches(match_info
);
730 g_match_info_free(match_info
);
734 /* Unknown match criteria */
741 if (message_decode_entry
->msg_decoding
== MSG_DECODING_COMPRESSED
)
743 msg_tvb
= tvb_child_uncompress_zlib(msg_tvb
, msg_tvb
, 0, tvb_reported_length(msg_tvb
));
746 add_new_data_source(pinfo
, msg_tvb
, "Uncompressed Message");
752 proto_item
*ti
= proto_tree_add_string(mqtt_tree
, hf_mqtt_pubmsg_decoded
, msg_tvb
, 0, -1,
753 message_decode_entry
->payload_proto_name
);
754 proto_item_set_generated(ti
);
756 call_dissector(message_decode_entry
->payload_proto
, msg_tvb
, pinfo
, tree
);
763 static unsigned dissect_string(tvbuff_t
*tvb
, proto_tree
*tree
, unsigned offset
, int hf_len
, int hf_value
)
767 proto_tree_add_item_ret_uint(tree
, hf_len
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &prop_len
);
768 proto_tree_add_item(tree
, hf_value
, tvb
, offset
+ 2, prop_len
, ENC_UTF_8
|ENC_NA
);
773 /* MQTT v5.0: Reason Codes */
774 static void dissect_mqtt_reason_code(proto_tree
*mqtt_tree
, tvbuff_t
*tvb
, unsigned offset
, uint8_t mqtt_msg_type
)
776 static int * const hf_rcode
[] = {
779 &hf_mqtt_reason_code_connack
,
781 &hf_mqtt_reason_code_puback
,
782 &hf_mqtt_reason_code_pubrec
,
783 &hf_mqtt_reason_code_pubrel
,
784 &hf_mqtt_reason_code_pubcomp
,
785 NULL
, /* SUBSCRIBE */
786 &hf_mqtt_reason_code_suback
,
787 NULL
, /* UNSUBSCRIBE */
788 &hf_mqtt_reason_code_unsuback
,
791 &hf_mqtt_reason_code_disconnect
,
792 &hf_mqtt_reason_code_auth
795 if (mqtt_msg_type
< array_length(hf_rcode
))
797 const int *hfindex
= hf_rcode
[mqtt_msg_type
];
800 proto_tree_add_item(mqtt_tree
, *hfindex
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
805 /* MQTT v5.0: dissect the MQTT properties */
806 static unsigned dissect_mqtt_properties(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*mqtt_tree
, unsigned offset
, int hf_property
, mqtt_properties_t
*mqtt_properties
)
808 proto_tree
*mqtt_prop_tree
;
812 const unsigned mqtt_prop_offset
= tvb_get_varint(tvb
, offset
, FT_VARINT_MAX_LEN
, &vbi
, ENC_VARINT_PROTOBUF
);
813 /* Property Length field can be stored in uint32 */
814 const unsigned mqtt_prop_len
= (int)vbi
;
816 /* Add the MQTT branch to the main tree */
817 /* hf_property is usually hf_mqtt_property, but can also be
818 * hf_mqtt_will_property when a Will is provided in a CONNECT packet */
819 ti
= proto_tree_add_item(mqtt_tree
, hf_property
, tvb
, offset
, mqtt_prop_offset
+ mqtt_prop_len
, ENC_NA
);
820 mqtt_prop_tree
= proto_item_add_subtree(ti
, ett_mqtt_property
);
822 proto_tree_add_item(mqtt_prop_tree
, hf_mqtt_property_len
, tvb
, offset
, mqtt_prop_offset
, ENC_BIG_ENDIAN
);
823 offset
+= mqtt_prop_offset
;
825 const unsigned bytes_to_read
= offset
+ mqtt_prop_len
;
826 while (offset
< bytes_to_read
)
829 proto_tree_add_item_ret_uint(mqtt_prop_tree
, hf_mqtt_property_id
, tvb
, offset
, 1, ENC_BIG_ENDIAN
, &prop_id
);
834 case PROP_PAYLOAD_FORMAT_INDICATOR
:
835 case PROP_REQUEST_PROBLEM_INFORMATION
:
836 case PROP_REQUEST_RESPONSE_INFORMATION
:
837 case PROP_RETAIN_AVAILABLE
:
838 case PROP_WILDCARD_SUBSCRIPTION_AVAILABLE
:
839 case PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE
:
840 case PROP_SHARED_SUBSCRIPTION_AVAILABLE
:
841 proto_tree_add_item(mqtt_prop_tree
, hf_mqtt_prop_num
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
845 case PROP_MAXIMUM_QOS
:
846 proto_tree_add_item(mqtt_prop_tree
, hf_mqtt_prop_max_qos
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
850 case PROP_TOPIC_ALIAS
:
851 proto_tree_add_item_ret_uint(mqtt_prop_tree
, hf_mqtt_prop_topic_alias
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &mqtt_properties
->topic_alias
);
855 case PROP_SERVER_KEEP_ALIVE
:
856 case PROP_RECEIVE_MAXIMUM
:
857 case PROP_TOPIC_ALIAS_MAXIMUM
:
858 proto_tree_add_item(mqtt_prop_tree
, hf_mqtt_prop_num
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
862 case PROP_PUBLICATION_EXPIRY_INTERVAL
:
863 case PROP_SESSION_EXPIRY_INTERVAL
:
864 case PROP_WILL_DELAY_INTERVAL
:
865 case PROP_MAXIMUM_PACKET_SIZE
:
866 proto_tree_add_item(mqtt_prop_tree
, hf_mqtt_prop_num
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
870 case PROP_SUBSCRIPTION_IDENTIFIER
:
873 proto_tree_add_item_ret_length(mqtt_prop_tree
, hf_mqtt_prop_num
, tvb
, offset
, -1, ENC_LITTLE_ENDIAN
|ENC_VARINT_PROTOBUF
, &vbi_len
);
878 case PROP_CONTENT_TYPE
:
881 proto_tree_add_item_ret_string_and_length(mqtt_prop_tree
, hf_mqtt_prop_content_type
, tvb
, offset
, 2, ENC_UTF_8
, pinfo
->pool
, &mqtt_properties
->content_type
, &length
);
886 case PROP_RESPONSE_TOPIC
:
887 case PROP_CORRELATION_DATA
:
888 case PROP_ASSIGNED_CLIENT_IDENTIFIER
:
889 case PROP_AUTH_METHOD
:
891 case PROP_RESPONSE_INFORMATION
:
892 case PROP_SERVER_REFERENCE
:
893 case PROP_REASON_STRING
:
894 offset
+= dissect_string(tvb
, mqtt_prop_tree
, offset
, hf_mqtt_prop_string_len
, hf_mqtt_prop_string
);
897 case PROP_USER_PROPERTY
:
898 offset
+= dissect_string(tvb
, mqtt_prop_tree
, offset
, hf_mqtt_prop_key_len
, hf_mqtt_prop_key
);
899 offset
+= dissect_string(tvb
, mqtt_prop_tree
, offset
, hf_mqtt_prop_value_len
, hf_mqtt_prop_value
);
903 proto_tree_add_item(mqtt_prop_tree
, hf_mqtt_prop_unknown
, tvb
, offset
, bytes_to_read
- offset
, ENC_UTF_8
);
904 offset
+= (bytes_to_read
- offset
);
909 return mqtt_prop_offset
+ mqtt_prop_len
;
912 /* Dissect the MQTT message */
913 static int dissect_mqtt(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
915 uint8_t mqtt_fixed_hdr
;
916 uint8_t mqtt_msg_type
;
918 const uint8_t *topic_str
= "";
920 proto_tree
*mqtt_tree
;
921 uint64_t mqtt_con_flags
;
922 uint64_t msg_len
= 0;
923 int mqtt_msg_len
= 0;
924 uint32_t mqtt_str_len
;
925 uint16_t mqtt_len_offset
;
926 int mqtt_payload_len
;
928 conversation_t
*conv
;
930 mqtt_properties_t mqtt_properties
= { 0 };
931 mqtt_properties_t mqtt_will_properties
= { 0 };
933 bool msg_handled
= false;
935 static int * const publish_fields
[] = {
943 static int * const v31_pubrel_sub_unsub_fields
[] = {
947 &hf_mqtt_retain_reserved
,
951 static int * const other_fields
[] = {
957 static int * const connect_flags
[] = {
958 &hf_mqtt_conflag_user
,
959 &hf_mqtt_conflag_passwd
,
960 &hf_mqtt_conflag_will_retain
,
961 &hf_mqtt_conflag_will_qos
,
962 &hf_mqtt_conflag_will_flag
,
963 &hf_mqtt_conflag_clean_sess
,
964 &hf_mqtt_conflag_reserved
,
968 static int * const connack_flags
[] = {
969 &hf_mqtt_conackflag_reserved
,
970 &hf_mqtt_conackflag_sp
,
974 static int * const v50_subscription_flags
[] = {
975 &hf_mqtt_subscription_reserved
,
976 &hf_mqtt_subscription_retain
,
977 &hf_mqtt_subscription_rap
,
978 &hf_mqtt_subscription_nl
,
979 &hf_mqtt_subscription_qos
,
983 /* Extract the message ID */
984 mqtt_fixed_hdr
= tvb_get_uint8(tvb
, offset
);
985 mqtt_msg_type
= mqtt_fixed_hdr
>> 4;
987 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "MQTT");
988 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ", ", val_to_str_ext(mqtt_msg_type
, &mqtt_msgtype_vals_ext
, "Unknown (0x%02x)"));
990 /* Add the MQTT branch to the main tree */
991 mqtt_ti
= proto_tree_add_item(tree
, proto_mqtt
, tvb
, 0, -1, ENC_NA
);
992 mqtt_tree
= proto_item_add_subtree(mqtt_ti
, ett_mqtt_hdr
);
994 conv
= find_or_create_conversation(pinfo
);
995 mqtt
= (mqtt_conv_t
*)conversation_get_proto_data(conv
, proto_mqtt
);
998 mqtt
= wmem_new0(wmem_file_scope(), mqtt_conv_t
);
999 mqtt
->runtime_proto_version
= default_protocol_version
;
1000 conversation_add_proto_data(conv
, proto_mqtt
, mqtt
);
1001 mqtt
->topic_alias_map
= wmem_map_new(wmem_file_scope(), g_direct_hash
, g_direct_equal
);
1004 mqtt_len_offset
= tvb_get_varint(tvb
, (offset
+ MQTT_HDR_SIZE_BEFORE_LEN
), FT_VARINT_MAX_LEN
, &msg_len
, ENC_VARINT_PROTOBUF
);
1006 /* Explicit downcast, typically maximum length of message could be 4 bytes */
1007 mqtt_msg_len
= (int) msg_len
;
1009 /* Add the type to the MQTT tree item */
1010 proto_item_append_text(mqtt_tree
, ", %s", val_to_str_ext(mqtt_msg_type
, &mqtt_msgtype_vals_ext
, "Unknown (0x%02x)"));
1012 if ((mqtt_msg_type
!= MQTT_CONNECT
) && (mqtt
->runtime_proto_version
== 0))
1014 expert_add_info(pinfo
, mqtt_ti
, &ei_unknown_version
);
1017 if (mqtt_msg_type
== MQTT_PUBLISH
)
1019 proto_tree_add_bitmask(mqtt_tree
, tvb
, offset
, hf_mqtt_hdrflags
, ett_mqtt_hdr_flags
, publish_fields
, ENC_BIG_ENDIAN
);
1021 else if (mqtt
->runtime_proto_version
== MQTT_PROTO_V31
&&
1022 (mqtt_msg_type
== MQTT_PUBREL
||
1023 mqtt_msg_type
== MQTT_SUBSCRIBE
||
1024 mqtt_msg_type
== MQTT_UNSUBSCRIBE
))
1026 proto_tree_add_bitmask(mqtt_tree
, tvb
, offset
, hf_mqtt_hdrflags
, ett_mqtt_hdr_flags
, v31_pubrel_sub_unsub_fields
, ENC_BIG_ENDIAN
);
1030 proto_tree_add_bitmask(mqtt_tree
, tvb
, offset
, hf_mqtt_hdrflags
, ett_mqtt_hdr_flags
, other_fields
, ENC_BIG_ENDIAN
);
1035 /* Add the MQTT message length */
1036 proto_tree_add_uint64(mqtt_tree
, hf_mqtt_msg_len
, tvb
, offset
, mqtt_len_offset
, msg_len
);
1037 offset
+= mqtt_len_offset
;
1039 switch (mqtt_msg_type
)
1042 proto_tree_add_item_ret_uint(mqtt_tree
, hf_mqtt_proto_len
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &mqtt_str_len
);
1045 proto_tree_add_item(mqtt_tree
, hf_mqtt_proto_name
, tvb
, offset
, mqtt_str_len
, ENC_UTF_8
);
1046 offset
+= mqtt_str_len
;
1048 mqtt
->runtime_proto_version
= tvb_get_uint8(tvb
, offset
);
1050 proto_tree_add_item(mqtt_tree
, hf_mqtt_proto_ver
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1053 proto_tree_add_bitmask_ret_uint64(mqtt_tree
, tvb
, offset
, hf_mqtt_conflags
,
1054 ett_mqtt_con_flags
, connect_flags
, ENC_BIG_ENDIAN
, &mqtt_con_flags
);
1057 proto_tree_add_item(mqtt_tree
, hf_mqtt_keep_alive
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
1060 if (mqtt
->runtime_proto_version
== MQTT_PROTO_V50
)
1062 offset
+= dissect_mqtt_properties(tvb
, pinfo
, mqtt_tree
, offset
, hf_mqtt_property
, &mqtt_properties
);
1065 proto_tree_add_item_ret_uint(mqtt_tree
, hf_mqtt_client_id_len
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &mqtt_str_len
);
1068 proto_tree_add_item(mqtt_tree
, hf_mqtt_client_id
, tvb
, offset
, mqtt_str_len
, ENC_UTF_8
);
1069 offset
+= mqtt_str_len
;
1071 if (mqtt_con_flags
& MQTT_CONMASK_WILLFLAG
)
1073 if (mqtt
->runtime_proto_version
== MQTT_PROTO_V50
)
1075 offset
+= dissect_mqtt_properties(tvb
, pinfo
, mqtt_tree
, offset
, hf_mqtt_will_property
, &mqtt_will_properties
);
1078 ti
= proto_tree_add_item_ret_uint(mqtt_tree
, hf_mqtt_will_topic_len
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &mqtt_str_len
);
1081 if (mqtt_str_len
> 0)
1083 proto_tree_add_item(mqtt_tree
, hf_mqtt_will_topic
, tvb
, offset
, mqtt_str_len
, ENC_UTF_8
);
1084 offset
+= mqtt_str_len
;
1088 expert_add_info(pinfo
, ti
, &ei_illegal_length
);
1091 proto_tree_add_item_ret_uint(mqtt_tree
, hf_mqtt_will_msg_len
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &mqtt_str_len
);
1094 if (show_msg_as_text
)
1096 proto_tree_add_item(mqtt_tree
, hf_mqtt_will_msg_text
, tvb
, offset
, mqtt_str_len
, ENC_UTF_8
);
1100 proto_tree_add_item(mqtt_tree
, hf_mqtt_will_msg
, tvb
, offset
, mqtt_str_len
, ENC_NA
);
1102 offset
+= mqtt_str_len
;
1105 if ((mqtt_con_flags
& MQTT_CONMASK_USER
) && (tvb_reported_length_remaining(tvb
, offset
) > 0))
1107 proto_tree_add_item_ret_uint(mqtt_tree
, hf_mqtt_username_len
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &mqtt_str_len
);
1110 proto_tree_add_item(mqtt_tree
, hf_mqtt_username
, tvb
, offset
, mqtt_str_len
, ENC_UTF_8
);
1111 offset
+= mqtt_str_len
;
1114 if ((mqtt_con_flags
& MQTT_CONMASK_PASSWD
) && (tvb_reported_length_remaining(tvb
, offset
) > 0))
1116 proto_tree_add_item_ret_uint(mqtt_tree
, hf_mqtt_passwd_len
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &mqtt_str_len
);
1119 proto_tree_add_item(mqtt_tree
, hf_mqtt_passwd
, tvb
, offset
, mqtt_str_len
, ENC_UTF_8
);
1124 if (mqtt
->runtime_proto_version
== MQTT_PROTO_V31
)
1126 /* v3.1 Connection Ack only contains a reserved byte and the Return Code. */
1127 proto_tree_add_item(mqtt_tree
, hf_mqtt_conack_reserved
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1131 /* v3.1.1 Conn Ack contains the Conn Ack Flags and the Return Code. */
1132 proto_tree_add_bitmask(mqtt_tree
, tvb
, offset
, hf_mqtt_conack_flags
,
1133 ett_mqtt_conack_flags
, connack_flags
, ENC_BIG_ENDIAN
);
1137 if ((mqtt
->runtime_proto_version
== MQTT_PROTO_V31
) ||
1138 (mqtt
->runtime_proto_version
== MQTT_PROTO_V311
))
1140 proto_tree_add_item(mqtt_tree
, hf_mqtt_conack_code
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1144 dissect_mqtt_reason_code(mqtt_tree
, tvb
, offset
, mqtt_msg_type
);
1148 if (mqtt
->runtime_proto_version
== MQTT_PROTO_V50
)
1150 offset
+= dissect_mqtt_properties(tvb
, pinfo
, mqtt_tree
, offset
, hf_mqtt_property
, &mqtt_properties
);
1155 /* TopicName|MsgID|Message| */
1156 ti
= proto_tree_add_item_ret_uint(mqtt_tree
, hf_mqtt_topic_len
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &mqtt_str_len
);
1159 if (mqtt_str_len
> 0)
1161 /* 'topic_regex' requires topic_str to be valid UTF-8. */
1162 proto_tree_add_item_ret_string(mqtt_tree
, hf_mqtt_topic
, tvb
, offset
, mqtt_str_len
, ENC_UTF_8
|ENC_NA
,
1163 pinfo
->pool
, &topic_str
);
1164 offset
+= mqtt_str_len
;
1167 /* Message ID is included only when QoS > 0 */
1168 if (mqtt_fixed_hdr
& MQTT_MASK_QOS
)
1170 proto_tree_add_item_ret_uint(mqtt_tree
, hf_mqtt_msgid
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &mqtt_msgid
);
1172 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " (id=%u)", mqtt_msgid
);
1175 if (mqtt
->runtime_proto_version
== MQTT_PROTO_V50
)
1177 offset
+= dissect_mqtt_properties(tvb
, pinfo
, mqtt_tree
, offset
, hf_mqtt_property
, &mqtt_properties
);
1179 if (mqtt_properties
.topic_alias
!= 0)
1181 if (!pinfo
->fd
->visited
&& mqtt_str_len
> 0)
1183 uint8_t *topic
= wmem_strdup(wmem_file_scope(), topic_str
);
1184 wmem_map_insert(mqtt
->topic_alias_map
, GUINT_TO_POINTER(mqtt_properties
.topic_alias
), topic
);
1188 uint8_t *topic
= (uint8_t *)wmem_map_lookup(mqtt
->topic_alias_map
, GUINT_TO_POINTER(mqtt_properties
.topic_alias
));
1194 ti
= proto_tree_add_string(mqtt_tree
, hf_mqtt_topic
, tvb
, offset
, 0, topic_str
);
1195 proto_item_set_generated(ti
);
1199 expert_add_info(pinfo
, ti
, &ei_unknown_topic_alias
);
1205 if ((mqtt_str_len
== 0) && (mqtt_properties
.topic_alias
== 0))
1207 expert_add_info(pinfo
, ti
, &ei_illegal_length
);
1210 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " [%s]", topic_str
);
1212 mqtt_payload_len
= tvb_reported_length(tvb
) - offset
;
1213 if (show_msg_as_text
)
1215 proto_tree_add_item(mqtt_tree
, hf_mqtt_pubmsg_text
, tvb
, offset
, mqtt_payload_len
, ENC_UTF_8
);
1219 proto_tree_add_item(mqtt_tree
, hf_mqtt_pubmsg
, tvb
, offset
, mqtt_payload_len
, ENC_NA
);
1222 if (num_mqtt_message_decodes
> 0)
1224 tvbuff_t
*msg_tvb
= tvb_new_subset_length(tvb
, offset
, mqtt_payload_len
);
1225 msg_handled
= mqtt_user_decode_message(tree
, mqtt_tree
, pinfo
, topic_str
, msg_tvb
);
1228 if (mqtt_properties
.content_type
)
1230 tvbuff_t
*msg_tvb
= tvb_new_subset_length(tvb
, offset
, mqtt_payload_len
);
1231 int bytes_read
= dissector_try_string_with_data(media_type_dissector_table
, mqtt_properties
.content_type
,
1232 msg_tvb
, pinfo
, tree
, true, NULL
);
1234 msg_handled
= msg_handled
| (bytes_read
!= 0);
1237 /* No UAT or content property match, try the heuristic dissectors, pass the topic string as data */
1239 heur_dtbl_entry_t
*hdtbl_entry
;
1240 tvbuff_t
*msg_tvb
= tvb_new_subset_length(tvb
, offset
, mqtt_payload_len
);
1241 char *sub_data
= wmem_strdup(pinfo
->pool
, (const char*)topic_str
);
1242 dissector_try_heuristic(mqtt_topic_subdissector
, msg_tvb
, pinfo
, tree
, &hdtbl_entry
, sub_data
);
1246 case MQTT_SUBSCRIBE
:
1247 /* After the Message Id field is found, the following fields must appear
1251 proto_tree_add_item_ret_uint(mqtt_tree
, hf_mqtt_msgid
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &mqtt_msgid
);
1253 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " (id=%u)", mqtt_msgid
);
1255 if (mqtt
->runtime_proto_version
== MQTT_PROTO_V50
)
1257 offset
+= dissect_mqtt_properties(tvb
, pinfo
, mqtt_tree
, offset
, hf_mqtt_property
, &mqtt_properties
);
1260 while (offset
< tvb_reported_length(tvb
))
1262 ti
= proto_tree_add_item_ret_uint(mqtt_tree
, hf_mqtt_topic_len
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &mqtt_str_len
);
1265 if (mqtt_str_len
> 0)
1267 proto_tree_add_item_ret_string(mqtt_tree
, hf_mqtt_topic
, tvb
, offset
, mqtt_str_len
, ENC_UTF_8
|ENC_NA
,
1268 wmem_epan_scope(), &topic_str
);
1269 offset
+= mqtt_str_len
;
1273 expert_add_info(pinfo
, ti
, &ei_illegal_length
);
1276 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " [%s]", topic_str
);
1278 if (mqtt
->runtime_proto_version
== MQTT_PROTO_V50
)
1280 proto_tree_add_bitmask(mqtt_tree
, tvb
, offset
, hf_mqtt_subscription_options
,
1281 ett_mqtt_subscription_flags
, v50_subscription_flags
, ENC_BIG_ENDIAN
);
1285 proto_tree_add_item(mqtt_tree
, hf_mqtt_sub_qos
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1291 case MQTT_UNSUBSCRIBE
:
1292 /* After the Message Id field is found, the following fields must appear
1296 proto_tree_add_item_ret_uint(mqtt_tree
, hf_mqtt_msgid
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &mqtt_msgid
);
1298 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " (id=%u)", mqtt_msgid
);
1300 if (mqtt
->runtime_proto_version
== MQTT_PROTO_V50
)
1302 offset
+= dissect_mqtt_properties(tvb
, pinfo
, mqtt_tree
, offset
, hf_mqtt_property
, &mqtt_properties
);
1305 while (offset
< tvb_reported_length(tvb
))
1307 ti
= proto_tree_add_item_ret_uint(mqtt_tree
, hf_mqtt_topic_len
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &mqtt_str_len
);
1310 if (mqtt_str_len
> 0)
1312 proto_tree_add_item(mqtt_tree
, hf_mqtt_topic
, tvb
, offset
, mqtt_str_len
, ENC_UTF_8
);
1313 offset
+= mqtt_str_len
;
1317 expert_add_info(pinfo
, ti
, &ei_illegal_length
);
1323 /* The SUBACK message contains a list of granted QoS levels that come
1324 * after the Message Id field. The size of each QoS entry is 1 byte.
1326 proto_tree_add_item_ret_uint(mqtt_tree
, hf_mqtt_msgid
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &mqtt_msgid
);
1328 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " (id=%u)", mqtt_msgid
);
1330 if (mqtt
->runtime_proto_version
== MQTT_PROTO_V50
)
1332 offset
+= dissect_mqtt_properties(tvb
, pinfo
, mqtt_tree
, offset
, hf_mqtt_property
, &mqtt_properties
);
1335 while (offset
< tvb_reported_length(tvb
))
1337 if ((mqtt
->runtime_proto_version
== MQTT_PROTO_V31
) ||
1338 (mqtt
->runtime_proto_version
== MQTT_PROTO_V311
))
1340 proto_tree_add_item(mqtt_tree
, hf_mqtt_suback_qos
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
1344 dissect_mqtt_reason_code(mqtt_tree
, tvb
, offset
, mqtt_msg_type
);
1354 proto_tree_add_item_ret_uint(mqtt_tree
, hf_mqtt_msgid
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &mqtt_msgid
);
1356 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " (id=%u)", mqtt_msgid
);
1358 /* MQTT v5.0: The Reason Code and Property Length can be omitted if the
1359 * Reason Code is 0x00 and there are no Properties.
1360 * In this case, the PUB* has a Remaining Length of 2.
1362 if (mqtt
->runtime_proto_version
== MQTT_PROTO_V50
&& mqtt_msg_len
> 2)
1364 dissect_mqtt_reason_code(mqtt_tree
, tvb
, offset
, mqtt_msg_type
);
1367 /* If the Remaining Length is less than 4, the Property Length is not
1368 * present and has a value of 0.
1370 if (mqtt_msg_len
> 3)
1372 offset
+= dissect_mqtt_properties(tvb
, pinfo
, mqtt_tree
, offset
, hf_mqtt_property
, &mqtt_properties
);
1378 proto_tree_add_item_ret_uint(mqtt_tree
, hf_mqtt_msgid
, tvb
, offset
, 2, ENC_BIG_ENDIAN
, &mqtt_msgid
);
1380 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " (id=%u)", mqtt_msgid
);
1382 if (mqtt
->runtime_proto_version
== MQTT_PROTO_V50
)
1384 offset
+= dissect_mqtt_properties(tvb
, pinfo
, mqtt_tree
, offset
, hf_mqtt_property
, &mqtt_properties
);
1386 while (offset
< tvb_reported_length(tvb
))
1388 dissect_mqtt_reason_code(mqtt_tree
, tvb
, offset
, mqtt_msg_type
);
1394 /* The following messages don't have variable header */
1399 case MQTT_DISCONNECT
:
1400 /* MQTT v5.0: Byte 1 in the Variable Header is the Disconnect Reason Code.
1401 * If the Remaining Length is less than 1 the value of 0x00
1402 * (Normal disconnection) is used.
1406 /* MQTT v5.0: The Reason Code and Property Length can be omitted if
1407 * the Reason Code is 0x00 (Success) and there are no Properties.
1408 * In this case the AUTH has a Remaining Length of 0.
1410 if (mqtt
->runtime_proto_version
== MQTT_PROTO_V50
&& mqtt_msg_len
> 0)
1412 dissect_mqtt_reason_code(mqtt_tree
, tvb
, offset
, mqtt_msg_type
);
1415 /* 3.14.2.2 DISCONNECT Properties:
1416 * If the Remaining Length is less than 2, a value of 0 is used.
1417 * Let's assume that it also applies to AUTH, why? DISCONNECT and AUTH
1418 * share the same structure with no payload.
1420 if (mqtt_msg_len
>= 2)
1422 offset
+= dissect_mqtt_properties(tvb
, pinfo
, mqtt_tree
, offset
, hf_mqtt_property
, &mqtt_properties
);
1432 "The minimum size of MQTT Packet is 2 bytes(Ping Req, Ping Rsp,
1433 Disconnect), and the maximum size is 256MB. Hence minimum fixed
1434 length should be 2 bytes for tcp_dissect_pdu.
1436 If the length field is spread across two TCP segments, then we have a
1437 problem, because exception will be raised. So long as MQTT length
1438 field(although spread over 4 bytes) is present within single TCP
1439 segment we shouldn't have any issue by calling tcp_dissect_pdu with
1440 minimum length set to 2."
1442 XXX: ToDo: Commit a fix for the case of the length field spread across TCP segments.
1445 static int dissect_mqtt_data(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
1447 col_clear(pinfo
->cinfo
, COL_INFO
);
1449 tcp_dissect_pdus(tvb
, pinfo
, tree
,
1450 reassemble_mqtt_over_tcp
,
1451 2, /* Length can be determined within 5 bytes */
1453 dissect_mqtt
, data
);
1455 return tvb_captured_length(tvb
);
1459 * Register the protocol with Wireshark
1461 void proto_register_mqtt(void)
1463 static hf_register_info hf_mqtt
[] = {
1465 { "Msg Len", "mqtt.len",
1466 FT_UINT64
, BASE_DEC
, NULL
, 0,
1468 { &hf_mqtt_hdrflags
,
1469 { "Header Flags", "mqtt.hdrflags",
1470 FT_UINT8
, BASE_HEX
, NULL
, 0,
1472 { &hf_mqtt_msg_type
,
1473 { "Message Type", "mqtt.msgtype",
1474 FT_UINT8
, BASE_DEC
| BASE_EXT_STRING
, &mqtt_msgtype_vals_ext
, MQTT_MASK_MSG_TYPE
,
1476 { &hf_mqtt_reserved
,
1477 { "Reserved", "mqtt.hdr_reserved",
1478 FT_UINT8
, BASE_DEC
, NULL
, MQTT_MASK_HDR_RESERVED
,
1479 "Fixed Header Reserved Field", HFILL
}},
1480 { &hf_mqtt_retain_reserved
,
1481 { "Reserved", "mqtt.retain_reserved",
1482 FT_UINT8
, BASE_DEC
, NULL
, MQTT_MASK_RETAIN
,
1483 "Fixed Header Reserved Field", HFILL
}},
1484 { &hf_mqtt_dup_flag
,
1485 { "DUP Flag", "mqtt.dupflag",
1486 FT_BOOLEAN
, 8, TFS(&tfs_set_notset
), MQTT_MASK_DUP_FLAG
,
1488 { &hf_mqtt_qos_level
,
1489 { "QoS Level", "mqtt.qos",
1490 FT_UINT8
, BASE_DEC
, VALS(mqtt_qos_vals
), MQTT_MASK_QOS
,
1493 { "Retain", "mqtt.retain",
1494 FT_BOOLEAN
, 8, TFS(&tfs_set_notset
), MQTT_MASK_RETAIN
,
1497 { &hf_mqtt_conack_reserved
,
1498 { "Reserved", "mqtt.conack.flags.reserved",
1499 FT_BOOLEAN
, BASE_NONE
, TFS(&tfs_set_notset
), 0,
1501 { &hf_mqtt_conack_flags
,
1502 { "Acknowledge Flags", "mqtt.conack.flags",
1503 FT_UINT8
, BASE_HEX
, NULL
, 0,
1505 { &hf_mqtt_conackflag_reserved
,
1506 { "Reserved", "mqtt.conack.flags.reserved",
1507 FT_BOOLEAN
, 8, TFS(&tfs_set_notset
), MQTT_CONACKMASK_RESERVED
,
1509 { &hf_mqtt_conackflag_sp
,
1510 { "Session Present", "mqtt.conack.flags.sp",
1511 FT_BOOLEAN
, 8, TFS(&tfs_set_notset
), MQTT_CONACKMASK_SP
,
1512 "Session Present (version 3.1.1)", HFILL
}},
1513 { &hf_mqtt_conack_code
,
1514 { "Return Code", "mqtt.conack.val",
1515 FT_UINT8
, BASE_DEC
, VALS(mqtt_conack_vals
), 0,
1517 /* Publish-Ack / Publish-Rec / Publish-Rel / Publish-Comp / Unsubscribe-Ack */
1519 { "Message Identifier", "mqtt.msgid",
1520 FT_UINT16
, BASE_DEC
, NULL
, 0,
1523 { "Requested QoS", "mqtt.sub.qos",
1524 FT_UINT8
, BASE_DEC
, VALS(mqtt_qos_vals
), 0,
1526 { &hf_mqtt_suback_qos
,
1527 { "Granted QoS", "mqtt.suback.qos",
1528 FT_UINT8
, BASE_DEC
, VALS(mqtt_subqos_vals
), 0,
1530 { &hf_mqtt_topic_len
,
1531 { "Topic Length", "mqtt.topic_len",
1532 FT_UINT16
, BASE_DEC
, NULL
, 0,
1535 { "Topic", "mqtt.topic",
1536 FT_STRING
, BASE_NONE
, NULL
, 0,
1538 { &hf_mqtt_will_topic_len
,
1539 { "Will Topic Length", "mqtt.willtopic_len",
1540 FT_UINT16
, BASE_DEC
, NULL
, 0,
1542 { &hf_mqtt_will_topic
,
1543 { "Will Topic", "mqtt.willtopic",
1544 FT_STRING
, BASE_NONE
, NULL
, 0,
1546 { &hf_mqtt_will_msg
,
1547 { "Will Message", "mqtt.willmsg",
1548 FT_BYTES
, BASE_NONE
, NULL
, 0,
1550 { &hf_mqtt_will_msg_text
,
1551 { "Will Message", "mqtt.willmsg_text",
1552 FT_STRING
, BASE_NONE
, NULL
, 0,
1554 { &hf_mqtt_will_msg_len
,
1555 { "Will Message Length", "mqtt.willmsg_len",
1556 FT_UINT16
, BASE_DEC
, NULL
, 0,
1558 { &hf_mqtt_username_len
,
1559 { "User Name Length", "mqtt.username_len",
1560 FT_UINT16
, BASE_DEC
, NULL
, 0,
1562 { &hf_mqtt_username
,
1563 { "User Name", "mqtt.username",
1564 FT_STRING
, BASE_NONE
, NULL
, 0,
1566 { &hf_mqtt_passwd_len
,
1567 { "Password Length", "mqtt.passwd_len",
1568 FT_UINT16
, BASE_DEC
, NULL
, 0,
1571 { "Password", "mqtt.passwd",
1572 FT_STRING
, BASE_NONE
, NULL
, 0,
1575 { "Message", "mqtt.msg",
1576 FT_BYTES
, BASE_NONE
, NULL
, 0,
1578 { &hf_mqtt_pubmsg_text
,
1579 { "Message", "mqtt.msg_text",
1580 FT_STRING
, BASE_NONE
, NULL
, 0,
1582 { &hf_mqtt_pubmsg_decoded
,
1583 { "Message decoded as", "mqtt.msg_decoded_as",
1584 FT_STRING
, BASE_NONE
, NULL
, 0,
1586 { &hf_mqtt_proto_len
,
1587 { "Protocol Name Length", "mqtt.proto_len",
1588 FT_UINT16
, BASE_DEC
, NULL
, 0,
1590 { &hf_mqtt_proto_name
,
1591 { "Protocol Name", "mqtt.protoname",
1592 FT_STRING
, BASE_NONE
, NULL
, 0,
1594 { &hf_mqtt_client_id_len
,
1595 { "Client ID Length", "mqtt.clientid_len",
1596 FT_UINT16
, BASE_DEC
, NULL
, 0,
1598 { &hf_mqtt_client_id
,
1599 { "Client ID", "mqtt.clientid",
1600 FT_STRING
, BASE_NONE
, NULL
, 0,
1602 { &hf_mqtt_proto_ver
,
1603 { "Version", "mqtt.ver",
1604 FT_UINT8
, BASE_DEC
, VALS(mqtt_protocol_version_vals
), 0,
1605 "MQTT version", HFILL
}},
1607 { &hf_mqtt_conflags
,
1608 { "Connect Flags", "mqtt.conflags",
1609 FT_UINT8
, BASE_HEX
, NULL
, 0,
1611 { &hf_mqtt_conflag_user
,
1612 { "User Name Flag", "mqtt.conflag.uname",
1613 FT_BOOLEAN
, 8, TFS(&tfs_set_notset
), MQTT_CONMASK_USER
,
1615 { &hf_mqtt_conflag_passwd
,
1616 { "Password Flag", "mqtt.conflag.passwd",
1617 FT_BOOLEAN
, 8, TFS(&tfs_set_notset
), MQTT_CONMASK_PASSWD
,
1619 { &hf_mqtt_conflag_will_retain
,
1620 { "Will Retain", "mqtt.conflag.retain",
1621 FT_BOOLEAN
, 8, TFS(&tfs_set_notset
), MQTT_CONMASK_RETAIN
,
1623 { &hf_mqtt_conflag_will_qos
,
1624 { "QoS Level", "mqtt.conflag.qos",
1625 FT_UINT8
, BASE_DEC
, VALS(mqtt_qos_vals
), MQTT_CONMASK_QOS
,
1627 { &hf_mqtt_conflag_will_flag
,
1628 { "Will Flag", "mqtt.conflag.willflag",
1629 FT_BOOLEAN
, 8, TFS(&tfs_set_notset
), MQTT_CONMASK_WILLFLAG
,
1631 { &hf_mqtt_conflag_clean_sess
,
1632 { "Clean Session Flag", "mqtt.conflag.cleansess",
1633 FT_BOOLEAN
, 8, TFS(&tfs_set_notset
), MQTT_CONMASK_CLEANSESS
,
1635 { &hf_mqtt_conflag_reserved
,
1636 { "(Reserved)", "mqtt.conflag.reserved",
1637 FT_BOOLEAN
, 8, TFS(&tfs_set_notset
), MQTT_CONMASK_RESERVED
,
1639 { &hf_mqtt_keep_alive
,
1640 { "Keep Alive", "mqtt.kalive",
1641 FT_UINT16
, BASE_DEC
, NULL
, 0,
1643 { &hf_mqtt_subscription_options
,
1644 { "Subscription Options", "mqtt.subscription_options",
1645 FT_UINT8
, BASE_HEX
, NULL
, 0,
1647 { &hf_mqtt_subscription_qos
,
1648 { "QoS", "mqtt.subscription_options_qos",
1649 FT_UINT8
, BASE_DEC
, VALS(mqtt_qos_vals
), MQTT_MASK_SUBS_QOS
,
1651 { &hf_mqtt_subscription_nl
,
1652 { "No Local", "mqtt.subscription_options_nl",
1653 FT_BOOLEAN
, 8, TFS(&tfs_set_notset
), MQTT_MASK_SUBS_NL
,
1655 { &hf_mqtt_subscription_rap
,
1656 { "Retain As Published", "mqtt.subscription_options_rap",
1657 FT_BOOLEAN
, 8, TFS(&tfs_set_notset
), MQTT_MASK_SUBS_RAP
,
1659 { &hf_mqtt_subscription_retain
,
1660 { "Retain Handling", "mqtt.subscription_options_retain",
1661 FT_UINT8
, BASE_DEC
, VALS(mqtt_subscription_retain_handling
), MQTT_MASK_SUBS_RETAIN
,
1663 { &hf_mqtt_subscription_reserved
,
1664 { "Reserved", "mqtt.subscription_options_reserved",
1665 FT_UINT8
, BASE_HEX
, NULL
, MQTT_MASK_SUBS_RESERVED
,
1668 /* v5.0 Reason Codes */
1669 { &hf_mqtt_reason_code_connack
,
1670 { "Reason Code", "mqtt.connack.reason_code",
1671 FT_UINT8
, BASE_DEC
, VALS(mqtt_reason_code_connack_vals
), 0,
1672 "MQTT Reason Code", HFILL
}},
1673 { &hf_mqtt_reason_code_puback
,
1674 { "Reason Code", "mqtt.puback.reason_code",
1675 FT_UINT8
, BASE_DEC
, VALS(mqtt_reason_code_puback_vals
), 0,
1676 "MQTT Reason Code", HFILL
}},
1677 { &hf_mqtt_reason_code_pubrec
,
1678 { "Reason Code", "mqtt.pubrec.reason_code",
1679 FT_UINT8
, BASE_DEC
, VALS(mqtt_reason_code_puback_vals
), 0,
1680 "MQTT Reason Code", HFILL
}},
1681 { &hf_mqtt_reason_code_pubrel
,
1682 { "Reason Code", "mqtt.pubrel.reason_code",
1683 FT_UINT8
, BASE_DEC
, VALS(mqtt_reason_code_pubrel_vals
), 0,
1684 "MQTT Reason Code", HFILL
}},
1685 { &hf_mqtt_reason_code_pubcomp
,
1686 { "Reason Code", "mqtt.pubcomp.reason_code",
1687 FT_UINT8
, BASE_DEC
, VALS(mqtt_reason_code_pubrel_vals
), 0,
1688 "MQTT Reason Code", HFILL
}},
1689 { &hf_mqtt_reason_code_suback
,
1690 { "Reason Code", "mqtt.suback.reason_code",
1691 FT_UINT8
, BASE_DEC
, VALS(mqtt_reason_code_suback_vals
), 0,
1692 "MQTT Reason Code", HFILL
}},
1693 { &hf_mqtt_reason_code_unsuback
,
1694 { "Reason Code", "mqtt.unsuback.reason_code",
1695 FT_UINT8
, BASE_DEC
, VALS(mqtt_reason_code_unsuback_vals
), 0,
1696 "MQTT Reason Code", HFILL
}},
1697 { &hf_mqtt_reason_code_disconnect
,
1698 { "Reason Code", "mqtt.disconnect.reason_code",
1699 FT_UINT8
, BASE_DEC
, VALS(mqtt_reason_code_disconnect_vals
), 0,
1700 "MQTT Reason Code", HFILL
}},
1701 { &hf_mqtt_reason_code_auth
,
1702 { "Reason Code", "mqtt.auth.reason_code",
1703 FT_UINT8
, BASE_DEC
, VALS(mqtt_reason_code_auth_vals
), 0,
1704 "MQTT Reason Code", HFILL
}},
1707 { &hf_mqtt_property
,
1708 { "Properties", "mqtt.properties",
1709 FT_NONE
, BASE_NONE
, NULL
, 0,
1711 { &hf_mqtt_will_property
,
1712 { "Will Properties", "mqtt.will_properties",
1713 FT_NONE
, BASE_NONE
, NULL
, 0,
1715 { &hf_mqtt_property_len
,
1716 { "Total Length", "mqtt.property_len",
1717 FT_UINT64
, BASE_DEC
, NULL
, 0,
1719 { &hf_mqtt_property_id
,
1720 { "ID", "mqtt.property_id",
1721 FT_UINT8
, BASE_HEX
, VALS(mqtt_property_vals
), 0,
1722 "Property Id", HFILL
}},
1723 { &hf_mqtt_prop_num
,
1724 { "Value", "mqtt.prop_number",
1725 FT_UINT32
, BASE_DEC
, NULL
, 0,
1727 { &hf_mqtt_prop_content_type
,
1728 { "Content Type", "mqtt.property.content_type",
1729 FT_UINT_STRING
, BASE_NONE
, NULL
, 0,
1731 { &hf_mqtt_prop_max_qos
,
1732 { "QoS", "mqtt.property.max_qos",
1733 FT_UINT8
, BASE_DEC
, VALS(mqtt_qos_vals
), 0,
1735 { &hf_mqtt_prop_topic_alias
,
1736 { "Topic Alias", "mqtt.property.topic_alias",
1737 FT_UINT16
, BASE_DEC
, NULL
, 0,
1739 { &hf_mqtt_prop_unknown
,
1740 { "Unknown Property", "mqtt.prop_unknown",
1741 FT_STRING
, BASE_NONE
, NULL
, 0,
1743 { &hf_mqtt_prop_string_len
,
1744 { "Length", "mqtt.prop_string_len",
1745 FT_UINT32
, BASE_DEC
, NULL
, 0,
1747 { &hf_mqtt_prop_string
,
1748 { "Value", "mqtt.prop_string",
1749 FT_STRING
, BASE_NONE
, NULL
, 0,
1751 { &hf_mqtt_prop_key_len
,
1752 { "Key Length", "mqtt.prop_key_len",
1753 FT_UINT32
, BASE_DEC
, NULL
, 0,
1755 { &hf_mqtt_prop_key
,
1756 { "Key", "mqtt.prop_key",
1757 FT_STRING
, BASE_NONE
, NULL
, 0,
1759 { &hf_mqtt_prop_value_len
,
1760 { "Value Length", "mqtt.prop_value_len",
1761 FT_UINT32
, BASE_DEC
, NULL
, 0,
1763 { &hf_mqtt_prop_value
,
1764 { "Value", "mqtt.prop_value",
1765 FT_STRING
, BASE_NONE
, NULL
, 0,
1769 /* Setup protocol subtree arrays */
1770 static int* ett_mqtt
[] = {
1773 &ett_mqtt_hdr_flags
,
1774 &ett_mqtt_con_flags
,
1775 &ett_mqtt_conack_flags
,
1777 &ett_mqtt_subscription_flags
,
1780 static ei_register_info ei
[] = {
1781 { &ei_illegal_length
,
1782 { "mqtt.illegal_topic_length", PI_PROTOCOL
, PI_WARN
, "Length cannot be 0", EXPFILL
} },
1783 { &ei_unknown_version
,
1784 { "mqtt.unknown_version", PI_PROTOCOL
, PI_NOTE
, "Unknown version (missing the CONNECT packet?)", EXPFILL
} },
1785 { &ei_unknown_topic_alias
,
1786 { "mqtt.unknown_topic_alias", PI_PROTOCOL
, PI_NOTE
, "Unknown topic alias", EXPFILL
} }
1789 static uat_field_t mqtt_message_decode_flds
[] = {
1790 UAT_FLD_VS(message_decode
, match_criteria
, "Match criteria", match_criteria
, "Match criteria"),
1791 UAT_FLD_CSTRING(message_decode
, topic_pattern
, "Topic pattern", "Pattern to match for the topic"),
1792 UAT_FLD_VS(message_decode
, msg_decoding
, "Decoding", msg_decoding
, "Decode message before dissecting as protocol"),
1793 UAT_FLD_DISSECTOR(message_decode
, payload_proto
, "Payload dissector",
1794 "Dissector to be used for the message part of the matching topic"),
1798 uat_t
*message_uat
= uat_new("Message Decoding",
1799 sizeof(mqtt_message_decode_t
),
1800 "mqtt_message_decoding",
1802 &mqtt_message_decodes
,
1803 &num_mqtt_message_decodes
,
1804 UAT_AFFECTS_DISSECTION
, /* affects dissection of packets, but not set of named fields */
1805 "ChMQTTMessageDecoding",
1806 mqtt_message_decode_copy_cb
,
1807 mqtt_message_decode_update_cb
,
1808 mqtt_message_decode_free_cb
,
1811 mqtt_message_decode_flds
);
1813 module_t
*mqtt_module
;
1814 expert_module_t
* expert_mqtt
;
1816 /* Register protocol names and descriptions */
1817 proto_mqtt
= proto_register_protocol("MQ Telemetry Transport Protocol", "MQTT", "mqtt");
1819 /* Register the dissector */
1820 mqtt_handle
= register_dissector("mqtt", dissect_mqtt_data
, proto_mqtt
);
1822 proto_register_field_array(proto_mqtt
, hf_mqtt
, array_length(hf_mqtt
));
1823 proto_register_subtree_array(ett_mqtt
, array_length(ett_mqtt
));
1825 mqtt_topic_subdissector
= register_heur_dissector_list_with_description("mqtt.topic", "MQTT message topic", proto_mqtt
);
1827 expert_mqtt
= expert_register_protocol(proto_mqtt
);
1828 expert_register_field_array(expert_mqtt
, ei
, array_length(ei
));
1830 mqtt_module
= prefs_register_protocol(proto_mqtt
, NULL
);
1832 prefs_register_uat_preference(mqtt_module
, "message_decode_table",
1834 "A table that enumerates custom message decodes to be used for a certain topic",
1837 prefs_register_enum_preference(mqtt_module
, "default_version",
1839 "Select the MQTT version to use as protocol version if the CONNECT packet is not captured",
1840 &default_protocol_version
, mqtt_protocol_version_enumvals
, false);
1842 prefs_register_bool_preference(mqtt_module
, "show_msg_as_text",
1843 "Show Message as text",
1844 "Show Publish Message as text",
1852 void proto_reg_handoff_mqtt(void)
1854 dissector_add_uint_with_preference("tcp.port", MQTT_DEFAULT_PORT
, mqtt_handle
);
1855 ssl_dissector_add(MQTT_SSL_DEFAULT_PORT
, mqtt_handle
);
1857 media_type_dissector_table
= find_dissector_table("media_type");
1858 dissector_add_string("quic.proto", "mqtt", mqtt_handle
);
1862 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1867 * indent-tabs-mode: nil
1870 * vi: set shiftwidth=2 tabstop=8 expandtab:
1871 * :indentSize=2:tabSize=8:noTabs=true: