2 * ZeroMQ Message Transport Protocol as described at https://rfc.zeromq.org/spec/23/
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
12 /* N.B. this dissector aims to replace the popular lua dissector at
13 * https://github.com/whitequark/zmtp-wireshark
14 * Tries to support the same backward compatibility and
15 * dissector table (TCP port -> protocol) as the Lua dissector, but also has UAT that will override.
17 * TODO: would be nice if entries added in the tables were automatically added to TCP port range..
22 #include <epan/packet.h>
23 #include <epan/expert.h>
24 #include <epan/exceptions.h>
25 #include <epan/prefs.h>
26 #include <epan/proto_data.h>
29 #include <wsutil/array.h>
31 #include <ui/tap-credentials.h>
33 #include "packet-tcp.h"
35 static int credentials_tap
;
37 static int proto_zmtp
;
39 static int hf_zmtp_flags
;
40 static int hf_zmtp_flags_reserved
;
41 static int hf_zmtp_flags_command
;
42 static int hf_zmtp_flags_long
;
43 static int hf_zmtp_flags_more
;
44 static int hf_zmtp_length
;
45 static int hf_zmtp_data
;
46 static int hf_zmtp_data_text
;
47 static int hf_zmtp_signature
;
48 static int hf_zmtp_padding
;
49 static int hf_zmtp_version
;
50 static int hf_zmtp_version_major
;
51 static int hf_zmtp_version_minor
;
52 static int hf_zmtp_mechanism
;
53 static int hf_zmtp_as_server
;
54 static int hf_zmtp_filler
;
55 static int hf_zmtp_metadata_key
;
56 static int hf_zmtp_metadata_value
;
57 static int hf_zmtp_command_name_length
;
58 static int hf_zmtp_command_name
;
59 static int hf_zmtp_curvezmq_nonce
;
60 static int hf_zmtp_curvezmq_box
;
61 static int hf_zmtp_curvezmq_version
;
62 static int hf_zmtp_curvezmq_version_major
;
63 static int hf_zmtp_curvezmq_version_minor
;
64 static int hf_zmtp_curvezmq_publickey
;
65 static int hf_zmtp_curvezmq_signature
;
66 static int hf_zmtp_curvezmq_cookie
;
67 static int hf_zmtp_username
;
68 static int hf_zmtp_password
;
69 static int hf_zmtp_error_reason
;
70 static int hf_zmtp_ping_ttl
;
71 static int hf_zmtp_ping_context
;
75 static int ett_zmtp_flags
;
76 static int ett_zmtp_version
;
77 static int ett_zmtp_curvezmq_version
;
80 static expert_field ei_zmtp_unknown_flags_value
;
81 static expert_field ei_zmtp_unsupported_version
;
83 static dissector_handle_t zmtp_handle
;
85 /* Forward declarations */
86 void proto_register_zmtp(void);
87 void proto_reg_handoff_zmtp(void);
89 static dissector_table_t zmtp_port_dissector_table
;
91 /* User definable values */
92 static range_t
*global_zmtp_port_range
= NULL
;
95 /**************************************************************************/
96 /* Conversation state */
97 /**************************************************************************/
101 MECH_NULL
=0, /* assuming as default */
106 static const value_string mechanism_vals
[] =
108 { MECH_NULL
, "NULL" },
109 { MECH_PLAIN
, "PLAIN" },
110 { MECH_CURVE
, "CURVE" },
117 mechanism_type mechanism
;
118 uint32_t mechanism_frame
;
119 } zmtp_conversation_t
;
121 static const value_string flags_vals
[] =
123 { 0xff, "Greeting" },
134 /**************************************************************************/
135 /* Preferences state */
136 /**************************************************************************/
138 /* The data payload type of the data on certain TCP ports */
140 range_t
*tcp_port_range
; /* dissect data on these tcp ports as protocol */
141 char *protocol
; /* protocol of data on these tcp ports */
142 } zmtp_tcp_protocol_t
;
144 static zmtp_tcp_protocol_t
* zmtp_tcp_protocols
= NULL
;
145 static unsigned num_zmtp_tcp_protocols
= 0;
148 zmtp_tcp_protocols_copy_cb(void* n
, const void* o
, size_t siz _U_
)
150 zmtp_tcp_protocol_t
* new_rec
= (zmtp_tcp_protocol_t
*)n
;
151 const zmtp_tcp_protocol_t
* old_rec
= (const zmtp_tcp_protocol_t
*)o
;
153 /* Cpy interval values like int */
154 memcpy(new_rec
, old_rec
, sizeof(zmtp_tcp_protocol_t
));
156 if (old_rec
->tcp_port_range
) {
157 new_rec
->tcp_port_range
= range_copy(NULL
, old_rec
->tcp_port_range
);
159 if (old_rec
->protocol
) {
160 new_rec
->protocol
= g_strdup(old_rec
->protocol
);
167 zmtp_tcp_protocols_update_cb(void *r
, char **err
)
169 zmtp_tcp_protocol_t
* rec
= (zmtp_tcp_protocol_t
*)r
;
170 static range_t
*empty
;
172 empty
= range_empty(NULL
);
173 if (ranges_are_equal(rec
->tcp_port_range
, empty
)) {
174 *err
= g_strdup("Must specify TCP port(s) (like 8000 or 8000,8008-8088)");
175 wmem_free(NULL
, empty
);
179 wmem_free(NULL
, empty
);
184 zmtp_tcp_protocols_free_cb(void*r
)
186 zmtp_tcp_protocol_t
* rec
= (zmtp_tcp_protocol_t
*)r
;
188 wmem_free(NULL
, rec
->tcp_port_range
);
189 g_free(rec
->protocol
);
192 UAT_RANGE_CB_DEF(zmtp_tcp_protocols
, tcp_port_range
, zmtp_tcp_protocol_t
)
193 UAT_CSTRING_CB_DEF(zmtp_tcp_protocols
, protocol
, zmtp_tcp_protocol_t
)
195 /* Try to find matching data dissector name by TCP port */
197 find_data_dissector_by_tcp_port(packet_info
*pinfo
)
199 range_t
* tcp_port_range
;
200 const char* protocol
;
202 for (i
= 0; i
< num_zmtp_tcp_protocols
; ++i
) {
203 tcp_port_range
= zmtp_tcp_protocols
[i
].tcp_port_range
;
204 if (value_is_in_range(tcp_port_range
, pinfo
->srcport
) ||
205 value_is_in_range(tcp_port_range
, pinfo
->destport
)) {
207 protocol
= zmtp_tcp_protocols
[i
].protocol
;
208 if (protocol
&& strlen(protocol
) > 0) {
218 /* How long is this message (by checking flags+length). cb for tcp_dissect_pdus() */
220 get_zmtp_message_len(packet_info
*pinfo _U_
, tvbuff_t
*tvb
, int offset
, void *data _U_
)
222 uint8_t flags
= tvb_get_uint8(tvb
, offset
);
226 case 0xff: /* Greeting */
229 /* 1-byte length field */
230 case 0: /* data short (last) */
231 case 1: /* data short (and more) */
232 case 4: /* command (short) */
233 length
= tvb_get_uint8(tvb
, offset
+1);
234 return (unsigned)length
+ 2;
236 /* 8-byte length field */
237 case 2: /* data long (last) */
238 case 3: /* data long (and more) */
239 case 6: /* command (long) */
240 if (tvb_captured_length(tvb
) < 9) {
243 length
= tvb_get_ntoh64(tvb
, offset
+1);
244 return (unsigned)length
+ 9;
250 /* Dissect the payload of a data message */
251 static void dissect_zmtp_data(tvbuff_t
*tvb
, int offset
, packet_info
*pinfo
, proto_tree
*tree
, uint64_t length
,
252 zmtp_conversation_t
*p_conv_data
)
254 if (length
== 0 || !p_conv_data
) {
258 /* Show mechanism value */
259 proto_item
*mech_ti
= proto_tree_add_string(tree
, hf_zmtp_mechanism
, tvb
, 0, 0,
260 val_to_str_const(p_conv_data
->mechanism
, mechanism_vals
, "Unknown"));
261 proto_item_set_generated(mech_ti
);
263 /* Is data all text? */
264 bool all_text
= true;
265 for (uint64_t n
=offset
; n
< tvb_captured_length(tvb
); n
++) {
266 if (!g_ascii_isprint(tvb_get_uint8(tvb
, offset
))) {
272 /* Add data as raw bytes */
273 proto_item
*raw_data_ti
= proto_tree_add_item(tree
, hf_zmtp_data
, tvb
, offset
, -1, ENC_NA
);
274 /* If all text, prefer to show as text (bytes filter is still there) */
276 proto_item_set_hidden(raw_data_ti
);
277 proto_tree_add_item(tree
, hf_zmtp_data_text
, tvb
, offset
, -1, ENC_ASCII
);
280 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "(%" PRIu64
" bytes) ", length
);
282 /* Should only try to make any more sense of data if mechanism is not encrypted.. */
283 if (p_conv_data
->mechanism
== MECH_CURVE
) {
287 /* Get data tvb ready */
288 tvbuff_t
*data_tvb
= tvb_new_subset_remaining(tvb
, offset
);
290 /* Look up UAT for dissector to use */
291 const char *protocol
= find_data_dissector_by_tcp_port(pinfo
);
293 dissector_handle_t protocol_handle
= find_dissector(protocol
);
294 if (protocol_handle
) {
296 col_set_writable(pinfo
->cinfo
, COL_INFO
, false);
297 call_dissector_only(protocol_handle
, data_tvb
, pinfo
, tree
, NULL
);
298 col_set_writable(pinfo
->cinfo
, COL_INFO
, true);
308 /* Look up registered dissector table (try both ports) */
309 if (dissector_try_uint(zmtp_port_dissector_table
, pinfo
->destport
, data_tvb
, pinfo
, tree
)) {
312 if (dissector_try_uint(zmtp_port_dissector_table
, pinfo
->srcport
, data_tvb
, pinfo
, tree
)) {
316 /* Last resort (if wasn't text) - call data dissector */
318 proto_item_set_hidden(raw_data_ti
);
319 call_data_dissector(data_tvb
, pinfo
, tree
);
323 /* Dissect key=data pairs to end of frame */
324 static void dissect_zmtp_metadata(tvbuff_t
*tvb
, int offset
, packet_info
*pinfo
, proto_tree
*tree
)
327 const unsigned char *key
;
329 while (tvb_reported_length_remaining(tvb
, offset
)) {
331 length
= tvb_get_uint8(tvb
, offset
);
333 proto_tree_add_item_ret_string(tree
, hf_zmtp_metadata_key
, tvb
, offset
, length
, ENC_ASCII
, pinfo
->pool
, &key
);
337 length
= tvb_get_ntohl(tvb
, offset
);
340 const unsigned char *value
;
341 proto_tree_add_item_ret_string(tree
, hf_zmtp_metadata_value
, tvb
, offset
, length
, ENC_ASCII
, pinfo
->pool
, &value
);
343 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " %s=%s", key
, value
);
346 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " %s", key
);
351 /* These command details are largely taken from the Lua dissector */
352 static int dissect_zmtp_command(tvbuff_t
*tvb
, int offset
, packet_info
*pinfo _U_
, proto_tree
*tree
,
353 mechanism_type mechanism
)
355 proto_item
*len_ti
, *mech_ti
;
357 /* Show mechanism value */
358 mech_ti
= proto_tree_add_string(tree
, hf_zmtp_mechanism
, tvb
, 0, 0,
359 val_to_str_const(mechanism
, mechanism_vals
, "Unknown"));
360 proto_item_set_generated(mech_ti
);
362 /* command-name (len + bytes) */
363 uint32_t command_name_length
;
364 const unsigned char *command_name
;
365 len_ti
= proto_tree_add_item_ret_uint(tree
, hf_zmtp_command_name_length
, tvb
, offset
, 1, ENC_BIG_ENDIAN
, &command_name_length
);
366 proto_item_set_hidden(len_ti
);
368 proto_tree_add_item_ret_string(tree
, hf_zmtp_command_name
, tvb
, offset
, command_name_length
, ENC_ASCII
, pinfo
->pool
, &command_name
);
369 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "(%s) ", command_name
);
370 offset
+= command_name_length
;
372 /* What comes next depends upon the command and mechanism setting */
373 if (strcmp(command_name
, "READY") == 0) {
376 proto_tree_add_item(tree
, hf_zmtp_curvezmq_nonce
, tvb
, offset
, 8, ENC_ASCII
);
378 proto_tree_add_item(tree
, hf_zmtp_curvezmq_box
, tvb
, offset
, -1, ENC_ASCII
);
382 dissect_zmtp_metadata(tvb
, offset
, pinfo
, tree
);
386 else if (strcmp(command_name
, "HELLO") == 0) {
390 /* N.B., these may be empty. */
394 const unsigned char *username
;
395 len
= tvb_get_uint8(tvb
, offset
);
397 proto_item
*username_ti
= proto_tree_add_item_ret_string(tree
, hf_zmtp_username
, tvb
, offset
, len
, ENC_ASCII
, pinfo
->pool
, &username
);
400 proto_item_append_text(username_ti
, " (empty)");
404 const unsigned char *password
;
405 len
= tvb_get_uint8(tvb
, offset
);
407 proto_item
*password_ti
= proto_tree_add_item_ret_string(tree
, hf_zmtp_password
, tvb
, offset
, len
, ENC_ASCII
, pinfo
->pool
, &password
);
410 proto_item_append_text(password_ti
, " (empty)");
413 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "(username=%s, password=%s) ",
416 /* Also tap credentials */
417 tap_credential_t
* auth
= wmem_new0(wmem_packet_scope(), tap_credential_t
);
418 auth
->num
= pinfo
->num
;
419 auth
->proto
= "ZMTP";
420 auth
->password_hf_id
= hf_zmtp_password
;
421 auth
->username
= (char*)username
;
422 auth
->username_num
= pinfo
->num
;
423 auth
->info
= wmem_strdup_printf(wmem_packet_scope(), "PLAIN: username/password");
424 tap_queue_packet(credentials_tap
, pinfo
, auth
);
429 /* Version (in its own subtree) */
430 uint32_t major
, minor
;
431 proto_item
*version_ti
= proto_tree_add_string_format(tree
, hf_zmtp_curvezmq_version
, tvb
, offset
, 2, "", "Version");
432 proto_tree
*version_tree
= proto_item_add_subtree(version_ti
, ett_zmtp_curvezmq_version
);
435 proto_tree_add_item_ret_uint(version_tree
, hf_zmtp_curvezmq_version_major
, tvb
, offset
, 1, ENC_NA
, &major
);
438 proto_tree_add_item_ret_uint(version_tree
, hf_zmtp_curvezmq_version_minor
, tvb
, offset
, 1, ENC_NA
, &minor
);
440 proto_item_append_text(version_ti
, " (%u.%u)", major
, minor
);
443 if (major
==1 && minor
==0) {
444 /* 70 bytes padding */
445 proto_tree_add_item(tree
, hf_zmtp_padding
, tvb
, offset
, 70, ENC_NA
);
447 /* 32 bytes publickey */
448 proto_tree_add_item(tree
, hf_zmtp_curvezmq_publickey
, tvb
, offset
, 32, ENC_ASCII
);
451 proto_tree_add_item(tree
, hf_zmtp_curvezmq_nonce
, tvb
, offset
, 8, ENC_ASCII
);
453 /* 80 bytes signature */
454 proto_tree_add_item(tree
, hf_zmtp_curvezmq_signature
, tvb
, offset
, 80, ENC_ASCII
);
458 expert_add_info_format(pinfo
, version_ti
, &ei_zmtp_unsupported_version
,
459 "Unsupported version (%u.%u)", major
, minor
);
464 /* Unexpected mechanism for receiving "HELLO" */
468 else if (strcmp(command_name
, "WELCOME") == 0) {
471 /* Nonce (16 bytes) */
472 proto_tree_add_item(tree
, hf_zmtp_curvezmq_nonce
, tvb
, offset
, 16, ENC_ASCII
);
474 /* Box (128 bytes) */
475 proto_tree_add_item(tree
, hf_zmtp_curvezmq_box
, tvb
, offset
, 128, ENC_ASCII
);
479 /* Unexpected mechanism for receiving "WELCOME" */
484 else if (strcmp(command_name
, "INITIATE") == 0) {
488 dissect_zmtp_metadata(tvb
, offset
, pinfo
, tree
);
491 /* cookie (96 bytes) */
492 proto_tree_add_item(tree
, hf_zmtp_curvezmq_cookie
, tvb
, offset
, 96, ENC_ASCII
);
494 /* nonce (8 bytes) */
495 proto_tree_add_item(tree
, hf_zmtp_curvezmq_nonce
, tvb
, offset
, 8, ENC_ASCII
);
497 /* box (remainder) */
498 proto_tree_add_item(tree
, hf_zmtp_curvezmq_box
, tvb
, offset
, -1, ENC_ASCII
);
501 /* Unexpected mechanism for receiving "INITIATE" */
505 else if (strcmp(command_name
, "ERROR") == 0) {
506 /* 1 byte length, followed by reason */
507 uint8_t len
= tvb_get_uint8(tvb
, offset
);
509 const unsigned char *reason
;
510 proto_tree_add_item_ret_string(tree
, hf_zmtp_error_reason
, tvb
, offset
, len
, ENC_ASCII
, pinfo
->pool
, &reason
);
511 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " reason=%s", reason
);
514 else if (strcmp(command_name
, "PING") == 0) {
516 proto_tree_add_item(tree
, hf_zmtp_ping_ttl
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
518 /* Context (optional, remainder) */
519 if (tvb_captured_length_remaining(tvb
, offset
)) {
520 proto_tree_add_item(tree
, hf_zmtp_ping_context
, tvb
, offset
, -1, ENC_ASCII
);
523 else if (strcmp(command_name
, "PONG") == 0) {
524 proto_tree_add_item(tree
, hf_zmtp_ping_context
, tvb
, offset
, -1, ENC_ASCII
);
527 /* Extra separator in case data follows in same segment */
528 col_append_str(pinfo
->cinfo
, COL_INFO
, " ");
534 dissect_zmtp_message(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
536 proto_tree
*zmtp_tree
;
540 /* Protocol column */
541 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "zmtp");
542 col_clear(pinfo
->cinfo
, COL_INFO
);
545 root_ti
= proto_tree_add_item(tree
, proto_zmtp
, tvb
, offset
, -1, ENC_NA
);
546 zmtp_tree
= proto_item_add_subtree(root_ti
, ett_zmtp
);
548 /* Look up, or create, conversation */
549 zmtp_conversation_t
*p_conv_data
;
550 conversation_t
*p_conv
;
552 p_conv
= find_conversation(pinfo
->num
, &pinfo
->net_dst
, &pinfo
->net_src
,
553 conversation_pt_to_conversation_type(pinfo
->ptype
),
554 pinfo
->destport
, pinfo
->srcport
,
557 /* Look up data from conversation */
558 p_conv_data
= (zmtp_conversation_t
*)conversation_get_proto_data(p_conv
, proto_zmtp
);
560 /* Create new data for conversation data if not found */
561 if (!p_conv_data
&& !PINFO_FD_VISITED(pinfo
)) {
562 p_conv_data
= wmem_new(wmem_file_scope(), zmtp_conversation_t
);
564 /* Set initial values */
565 p_conv_data
->mechanism
= MECH_NULL
;
566 p_conv_data
->mechanism_frame
= 0;
568 /* Store in conversation */
569 conversation_add_proto_data(p_conv
, proto_zmtp
, p_conv_data
);
573 proto_item
*flags_ti
;
574 uint8_t flags
= tvb_get_uint8(tvb
, offset
);
576 /* Greeting value not broken down */
577 proto_tree_add_item(zmtp_tree
, hf_zmtp_flags
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
581 static int* const flags_fields
[] = { &hf_zmtp_flags_reserved
,
582 &hf_zmtp_flags_command
,
587 flags_ti
= proto_tree_add_bitmask(zmtp_tree
, tvb
, offset
, hf_zmtp_flags
,
588 ett_zmtp_flags
, flags_fields
, ENC_BIG_ENDIAN
);
591 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "%s ",
592 val_to_str(flags
, flags_vals
, "Unknown(%u)"));
593 proto_item_append_text(root_ti
, " (%s)", val_to_str(flags
, flags_vals
, "Unknown(%u)"));
598 case 0xff: /* Greeting */
600 /* signature = %xFF padding %x7F */
601 proto_tree_add_item(zmtp_tree
, hf_zmtp_signature
, tvb
, offset
-1, 10, ENC_NA
);
604 /* version = version-major version-minor */
605 uint32_t major
, minor
;
607 proto_item
*version_ti
= proto_tree_add_string_format(zmtp_tree
, hf_zmtp_version
, tvb
, offset
, 2, "", "Version");
608 proto_tree
*version_tree
= proto_item_add_subtree(version_ti
, ett_zmtp_version
);
610 proto_tree_add_item_ret_uint(version_tree
, hf_zmtp_version_major
, tvb
, offset
, 1, ENC_NA
, &major
);
613 proto_tree_add_item_ret_uint(version_tree
, hf_zmtp_version_minor
, tvb
, offset
, 1, ENC_NA
, &minor
);
615 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "(version=%u.%u", major
, minor
);
616 proto_item_append_text(version_ti
, " (%u.%u)", major
, minor
);
618 /* mechanism (20 bytes). N.B. *must* must match setting from peer */
619 const unsigned char *mechanism
;
620 unsigned mechanism_len
;
621 proto_tree_add_item_ret_string_and_length(zmtp_tree
, hf_zmtp_mechanism
, tvb
, offset
, 20, ENC_ASCII
,
622 pinfo
->pool
, &mechanism
, &mechanism_len
);
623 offset
+= mechanism_len
;
624 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " mechanism=%s", mechanism
);
626 /* Store in conversation data whether NULL, PLAIN or CURVE */
627 /* This affects what we expect to find in commands, and also whether can call dissectors to data payloads */
628 if (!PINFO_FD_VISITED(pinfo
)) {
629 if (strcmp(mechanism
, "NULL") == 0) {
630 p_conv_data
->mechanism
= MECH_NULL
;
632 else if (strcmp(mechanism
, "PLAIN") == 0) {
633 p_conv_data
->mechanism
= MECH_PLAIN
;
635 else if (strcmp(mechanism
, "CURVE") == 0) {
636 p_conv_data
->mechanism
= MECH_CURVE
;
639 p_conv_data
->mechanism_frame
= pinfo
->num
;
644 proto_tree_add_item_ret_boolean(zmtp_tree
, hf_zmtp_as_server
, tvb
, offset
, 1, ENC_NA
, &as_server
);
646 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " %s)", tfs_get_string(as_server
, &tfs_server_client
));
648 /* filler (31 octets) */
649 proto_tree_add_item(zmtp_tree
, hf_zmtp_filler
, tvb
, offset
, -1, ENC_NA
);
653 case 0x04: /* Command (short) */
655 proto_tree_add_item_ret_uint64(zmtp_tree
, hf_zmtp_length
, tvb
, offset
, 1, ENC_BIG_ENDIAN
, &length
);
658 dissect_zmtp_command(tvb
, offset
, pinfo
, zmtp_tree
, p_conv_data
->mechanism
);
661 case 0x06: /* Command (long) */
662 proto_tree_add_item_ret_uint64(zmtp_tree
, hf_zmtp_length
, tvb
, offset
, 8, ENC_BIG_ENDIAN
, &length
);
665 dissect_zmtp_command(tvb
, offset
, pinfo
, zmtp_tree
, p_conv_data
->mechanism
);
669 case 0x0: /* Data short (more) */
670 case 0x1: /* Data short (last) */
671 proto_tree_add_item_ret_uint64(zmtp_tree
, hf_zmtp_length
, tvb
, offset
, 1, ENC_BIG_ENDIAN
, &length
);
673 dissect_zmtp_data(tvb
, offset
, pinfo
, zmtp_tree
, length
, p_conv_data
);
676 case 0x2: /* Data long (last) */
677 case 0x3: /* Data long (more) */
678 proto_tree_add_item_ret_uint64(zmtp_tree
, hf_zmtp_length
, tvb
, offset
, 8, ENC_BIG_ENDIAN
, &length
);
680 dissect_zmtp_data(tvb
, offset
, pinfo
, zmtp_tree
, length
, p_conv_data
);
684 /* Expert info for unexpected flags value */
685 expert_add_info_format(pinfo
, flags_ti
, &ei_zmtp_unknown_flags_value
,
686 "Unexpected flags value %u", flags
);
690 col_set_fence(pinfo
->cinfo
, COL_INFO
);
692 /* Claim whole frame regardless */
693 return tvb_reported_length(tvb
);
696 /******************************/
697 /* Main dissection function. */
699 dissect_zmtp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
701 /* Frame starts off with no PDUs seen */
702 static bool false_value
= false;
703 p_add_proto_data(wmem_file_scope(), pinfo
, proto_zmtp
, 0, &false_value
);
705 /* Find whole PDUs and send them to dissect_zmtp_message() */
706 tcp_dissect_pdus(tvb
, pinfo
, tree
, true, /* desegment */
707 2, /* need flags bytes + long-size */
708 get_zmtp_message_len
,
709 dissect_zmtp_message
, data
);
710 return tvb_reported_length(tvb
);
715 proto_register_zmtp(void)
717 static hf_register_info hf
[] = {
719 { "Flags", "zmtp.flags", FT_UINT8
, BASE_HEX
,
720 VALS(flags_vals
), 0x0, NULL
, HFILL
}},
721 { &hf_zmtp_flags_reserved
,
722 { "Reserved", "zmtp.flags.reserved", FT_UINT8
, BASE_HEX
,
723 NULL
, 0xf8, NULL
, HFILL
}},
724 { &hf_zmtp_flags_command
,
725 { "Command", "zmtp.flags.command", FT_UINT8
, BASE_HEX
,
726 NULL
, 0x04, NULL
, HFILL
}},
727 { &hf_zmtp_flags_long
,
728 { "Long", "zmtp.flags.long", FT_UINT8
, BASE_HEX
,
729 NULL
, 0x02, NULL
, HFILL
}},
730 { &hf_zmtp_flags_more
,
731 { "More", "zmtp.flags.more", FT_UINT8
, BASE_HEX
,
732 NULL
, 0x01, NULL
, HFILL
}},
734 { "Length", "zmtp.length", FT_UINT64
, BASE_DEC
,
735 NULL
, 0x0, NULL
, HFILL
}},
737 { "Data", "zmtp.data", FT_BYTES
, BASE_NONE
,
738 NULL
, 0x0, NULL
, HFILL
}},
739 { &hf_zmtp_data_text
,
740 { "Text", "zmtp.data.text", FT_STRING
, BASE_NONE
,
741 NULL
, 0x0, NULL
, HFILL
}},
742 { &hf_zmtp_signature
,
743 { "Signature", "zmtp.signature", FT_BYTES
, BASE_NONE
,
744 NULL
, 0x0, NULL
, HFILL
}},
746 { "Padding", "zmtp.padding", FT_BYTES
, BASE_NONE
,
747 NULL
, 0x0, NULL
, HFILL
}},
749 { "Version", "zmtp.version", FT_STRING
, BASE_NONE
,
750 NULL
, 0x0, NULL
, HFILL
}},
751 { &hf_zmtp_version_major
,
752 { "Major version", "zmtp.version.major", FT_UINT8
, BASE_DEC
,
753 NULL
, 0x0, NULL
, HFILL
}},
754 { &hf_zmtp_version_minor
,
755 { "Minor version", "zmtp.version.minor", FT_UINT8
, BASE_DEC
,
756 NULL
, 0x0, NULL
, HFILL
}},
757 { &hf_zmtp_mechanism
,
758 { "Mechanism", "zmtp.mechanism", FT_STRINGZ
, BASE_NONE
,
759 NULL
, 0x0, NULL
, HFILL
}},
760 { &hf_zmtp_as_server
,
761 { "As-Server", "zmtp.as-server", FT_BOOLEAN
, BASE_NONE
,
762 TFS(&tfs_server_client
), 0x0, NULL
, HFILL
}},
764 { "Filler", "zmtp.filler", FT_BYTES
, BASE_NONE
,
765 NULL
, 0x0, NULL
, HFILL
}},
766 { &hf_zmtp_metadata_key
,
767 { "Metadata key", "zmtp.metadata.key", FT_STRING
, BASE_NONE
,
768 NULL
, 0x0, NULL
, HFILL
}},
769 { &hf_zmtp_metadata_value
,
770 { "Metadata value", "zmtp.metadata.value", FT_STRING
, BASE_NONE
,
771 NULL
, 0x0, NULL
, HFILL
}},
772 { &hf_zmtp_command_name_length
,
773 { "command-name length", "zmtp.command-name.length", FT_UINT8
, BASE_DEC
,
774 NULL
, 0x0, NULL
, HFILL
}},
775 { &hf_zmtp_command_name
,
776 { "command-name", "zmtp.command-name", FT_STRING
, BASE_NONE
,
777 NULL
, 0x0, NULL
, HFILL
}},
778 { &hf_zmtp_curvezmq_nonce
,
779 { "CurveZMQ nonce", "zmtp.curvezmq.nonce", FT_BYTES
, BASE_NONE
,
780 NULL
, 0x0, NULL
, HFILL
}},
781 { &hf_zmtp_curvezmq_box
,
782 { "CurveZMQ box", "zmtp.curvezmq.box", FT_BYTES
, BASE_NONE
,
783 NULL
, 0x0, NULL
, HFILL
}},
784 { &hf_zmtp_curvezmq_version
,
785 { "Version", "zmtp.curvezmq.version", FT_STRING
, BASE_NONE
,
786 NULL
, 0x0, NULL
, HFILL
}},
787 { &hf_zmtp_curvezmq_version_major
,
788 { "Major version", "zmtp.curvezmq.version.major", FT_UINT8
, BASE_DEC
,
789 NULL
, 0x0, NULL
, HFILL
}},
790 { &hf_zmtp_curvezmq_version_minor
,
791 { "Minor version", "zmtp.curvezmq.version.minor", FT_UINT8
, BASE_DEC
,
792 NULL
, 0x0, NULL
, HFILL
}},
793 { &hf_zmtp_curvezmq_publickey
,
794 { "PublicKey", "zmtp.curvezmq.publickey", FT_BYTES
, BASE_NONE
,
795 NULL
, 0x0, NULL
, HFILL
}},
796 { &hf_zmtp_curvezmq_signature
,
797 { "Signature", "zmtp.curvezmq.signature", FT_BYTES
, BASE_NONE
,
798 NULL
, 0x0, NULL
, HFILL
}},
799 { &hf_zmtp_curvezmq_cookie
,
800 { "Cookie", "zmtp.curvezmq.cookie", FT_BYTES
, BASE_NONE
,
801 NULL
, 0x0, NULL
, HFILL
}},
803 { "Username", "zmtp.username", FT_STRING
, BASE_NONE
,
804 NULL
, 0x0, NULL
, HFILL
}},
806 { "Password", "zmtp.password", FT_STRING
, BASE_NONE
,
807 NULL
, 0x0, NULL
, HFILL
}},
808 { &hf_zmtp_error_reason
,
809 { "Reason", "zmtp.reason", FT_STRING
, BASE_NONE
,
810 NULL
, 0x0, NULL
, HFILL
}},
812 { "TTL", "zmtp.ping.ttl", FT_UINT16
, BASE_DEC
,
813 NULL
, 0x0, NULL
, HFILL
}},
814 { &hf_zmtp_ping_context
,
815 { "Context", "zmtp.ping.context", FT_STRING
, BASE_NONE
,
816 NULL
, 0x0, NULL
, HFILL
}}
819 static int *ett
[] = {
823 &ett_zmtp_curvezmq_version
826 expert_module_t
* expert_zmtp
;
828 static ei_register_info ei
[] = {
829 { &ei_zmtp_unknown_flags_value
, { "zmtp.unknown_flags_value", PI_UNDECODED
, PI_WARN
, "Unsupported Flags value", EXPFILL
}},
830 { &ei_zmtp_unsupported_version
, { "zmtp.unsupported_version", PI_PROTOCOL
, PI_WARN
, "Unsupported Version", EXPFILL
}},
833 module_t
*zmtp_module
;
835 static uat_field_t zmtp_tcp_protocols_table_columns
[] = {
836 UAT_FLD_RANGE(zmtp_tcp_protocols
, tcp_port_range
, "TCP Ports", 0xFFFF, "TCP ports on which ZMTP data payloads will be dissected as protocol"),
837 UAT_FLD_CSTRING(zmtp_tcp_protocols
, protocol
, "Protocol", "Protocol for data on these TCP ports"),
840 uat_t
* zmtp_tcp_protocols_uat
;
842 proto_zmtp
= proto_register_protocol("ZeroMQ Message Transport Protocol", "ZMTP", "zmtp");
843 proto_register_field_array(proto_zmtp
, hf
, array_length(hf
));
844 proto_register_subtree_array(ett
, array_length(ett
));
846 zmtp_handle
= register_dissector("zmtp", dissect_zmtp
, proto_zmtp
);
848 expert_zmtp
= expert_register_protocol(proto_zmtp
);
849 expert_register_field_array(expert_zmtp
, ei
, array_length(ei
));
851 zmtp_module
= prefs_register_protocol(proto_zmtp
, proto_reg_handoff_zmtp
);
853 zmtp_tcp_protocols_uat
= uat_new("ZMTP TCP Protocols",
854 sizeof(zmtp_tcp_protocol_t
),
855 "zmtp_tcp_protocols",
858 &num_zmtp_tcp_protocols
,
859 UAT_AFFECTS_DISSECTION
| UAT_AFFECTS_FIELDS
,
860 NULL
, /* "ChZMTPTCPProtocols", */
861 zmtp_tcp_protocols_copy_cb
,
862 zmtp_tcp_protocols_update_cb
,
863 zmtp_tcp_protocols_free_cb
,
864 NULL
, /* post_update_cb */
866 zmtp_tcp_protocols_table_columns
868 prefs_register_uat_preference(zmtp_module
, "tcp_protocols", "ZMTP TCP protocols",
869 "Specify the protocol of data on certain TCP ports.",
870 zmtp_tcp_protocols_uat
);
872 zmtp_port_dissector_table
= register_dissector_table("zmtp.protocol",
873 "ZMTP Data Type", proto_zmtp
, FT_UINT16
, BASE_DEC
);
875 credentials_tap
= register_tap("credentials");
879 apply_zmtp_prefs(void)
881 global_zmtp_port_range
= prefs_get_range_value("zmtp", "tcp.port");
885 proto_reg_handoff_zmtp(void)
887 dissector_add_uint_range_with_preference("tcp.port", "", zmtp_handle
);
892 * Editor modelines - https://www.wireshark.org/tools/modelines.html
897 * indent-tabs-mode: nil
900 * vi: set shiftwidth=4 tabstop=8 expandtab:
901 * :indentSize=4:tabSize=8:noTabs=true: