summaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-zmtp.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-09-19 04:14:53 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-09-19 04:14:53 +0000
commita86c5f7cae7ec9a3398300555a0b644689d946a1 (patch)
tree39fe4b107c71174fd1e8a8ceb9a4d2aa14116248 /epan/dissectors/packet-zmtp.c
parentReleasing progress-linux version 4.2.6-1~progress7.99u1. (diff)
downloadwireshark-a86c5f7cae7ec9a3398300555a0b644689d946a1.tar.xz
wireshark-a86c5f7cae7ec9a3398300555a0b644689d946a1.zip
Merging upstream version 4.4.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'epan/dissectors/packet-zmtp.c')
-rw-r--r--epan/dissectors/packet-zmtp.c900
1 files changed, 900 insertions, 0 deletions
diff --git a/epan/dissectors/packet-zmtp.c b/epan/dissectors/packet-zmtp.c
new file mode 100644
index 00000000..5ddec75f
--- /dev/null
+++ b/epan/dissectors/packet-zmtp.c
@@ -0,0 +1,900 @@
+/* packet-zmtp.c
+ * ZeroMQ Message Transport Protocol as described at https://rfc.zeromq.org/spec/23/
+ * Martin Mathieson
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* N.B. this dissector aims to replace the popular lua dissector at
+ * https://github.com/whitequark/zmtp-wireshark
+ * Tries to support the same backward compatibility and
+ * dissector table (TCP port -> protocol) as the Lua dissector, but also has UAT that will override.
+ *
+ * TODO: would be nice if entries added in the tables were automatically added to TCP port range..
+ */
+
+#include "config.h"
+
+#include <epan/packet.h>
+#include <epan/expert.h>
+#include <epan/exceptions.h>
+#include <epan/prefs.h>
+#include <epan/proto_data.h>
+#include <epan/uat.h>
+#include <tap.h>
+#include <ui/tap-credentials.h>
+
+#include "packet-tcp.h"
+
+static int credentials_tap;
+
+static int proto_zmtp;
+
+static int hf_zmtp_flags;
+static int hf_zmtp_flags_reserved;
+static int hf_zmtp_flags_command;
+static int hf_zmtp_flags_long;
+static int hf_zmtp_flags_more;
+static int hf_zmtp_length;
+static int hf_zmtp_data;
+static int hf_zmtp_data_text;
+static int hf_zmtp_signature;
+static int hf_zmtp_padding;
+static int hf_zmtp_version;
+static int hf_zmtp_version_major;
+static int hf_zmtp_version_minor;
+static int hf_zmtp_mechanism;
+static int hf_zmtp_as_server;
+static int hf_zmtp_filler;
+static int hf_zmtp_metadata_key;
+static int hf_zmtp_metadata_value;
+static int hf_zmtp_command_name_length;
+static int hf_zmtp_command_name;
+static int hf_zmtp_curvezmq_nonce;
+static int hf_zmtp_curvezmq_box;
+static int hf_zmtp_curvezmq_version;
+static int hf_zmtp_curvezmq_version_major;
+static int hf_zmtp_curvezmq_version_minor;
+static int hf_zmtp_curvezmq_publickey;
+static int hf_zmtp_curvezmq_signature;
+static int hf_zmtp_curvezmq_cookie;
+static int hf_zmtp_username;
+static int hf_zmtp_password;
+static int hf_zmtp_error_reason;
+static int hf_zmtp_ping_ttl;
+static int hf_zmtp_ping_context;
+
+/* Subtrees */
+static int ett_zmtp;
+static int ett_zmtp_flags;
+static int ett_zmtp_version;
+static int ett_zmtp_curvezmq_version;
+
+/* Expert info */
+static expert_field ei_zmtp_unknown_flags_value;
+static expert_field ei_zmtp_unsupported_version;
+
+static dissector_handle_t zmtp_handle;
+
+/* Forward declarations */
+void proto_register_zmtp(void);
+void proto_reg_handoff_zmtp(void);
+
+static dissector_table_t zmtp_port_dissector_table;
+
+/* User definable values */
+static range_t *global_zmtp_port_range = NULL;
+
+
+/**************************************************************************/
+/* Conversation state */
+/**************************************************************************/
+
+typedef enum
+{
+ MECH_NULL=0, /* assuming as default */
+ MECH_PLAIN,
+ MECH_CURVE
+} mechanism_type;
+
+static const value_string mechanism_vals[] =
+{
+ { MECH_NULL, "NULL" },
+ { MECH_PLAIN, "PLAIN" },
+ { MECH_CURVE, "CURVE" },
+ { 0x0, NULL }
+};
+
+
+typedef struct
+{
+ mechanism_type mechanism;
+ uint32_t mechanism_frame;
+} zmtp_conversation_t;
+
+static const value_string flags_vals[] =
+{
+ { 0xff, "Greeting" },
+ { 0x00, "Data" },
+ { 0x01, "Data(+)" },
+ { 0x02, "Data" },
+ { 0x03, "Data(+)" },
+ { 0x04, "Command" },
+ { 0x06, "Command" },
+ { 0x0, NULL }
+};
+
+
+/**************************************************************************/
+/* Preferences state */
+/**************************************************************************/
+
+/* The data payload type of the data on certain TCP ports */
+typedef struct {
+ range_t *tcp_port_range; /* dissect data on these tcp ports as protocol */
+ char *protocol; /* protocol of data on these tcp ports */
+} zmtp_tcp_protocol_t;
+
+static zmtp_tcp_protocol_t* zmtp_tcp_protocols = NULL;
+static unsigned num_zmtp_tcp_protocols = 0;
+
+static void *
+zmtp_tcp_protocols_copy_cb(void* n, const void* o, size_t siz _U_)
+{
+ zmtp_tcp_protocol_t* new_rec = (zmtp_tcp_protocol_t*)n;
+ const zmtp_tcp_protocol_t* old_rec = (const zmtp_tcp_protocol_t*)o;
+
+ /* Cpy interval values like int */
+ memcpy(new_rec, old_rec, sizeof(zmtp_tcp_protocol_t));
+
+ if (old_rec->tcp_port_range) {
+ new_rec->tcp_port_range = range_copy(NULL, old_rec->tcp_port_range);
+ }
+ if (old_rec->protocol) {
+ new_rec->protocol = g_strdup(old_rec->protocol);
+ }
+
+ return new_rec;
+}
+
+static bool
+zmtp_tcp_protocols_update_cb(void *r, char **err)
+{
+ zmtp_tcp_protocol_t* rec = (zmtp_tcp_protocol_t*)r;
+ static range_t *empty;
+
+ empty = range_empty(NULL);
+ if (ranges_are_equal(rec->tcp_port_range, empty)) {
+ *err = g_strdup("Must specify TCP port(s) (like 8000 or 8000,8008-8088)");
+ wmem_free(NULL, empty);
+ return false;
+ }
+
+ wmem_free(NULL, empty);
+ return true;
+}
+
+static void
+zmtp_tcp_protocols_free_cb(void*r)
+{
+ zmtp_tcp_protocol_t* rec = (zmtp_tcp_protocol_t*)r;
+
+ wmem_free(NULL, rec->tcp_port_range);
+ g_free(rec->protocol);
+}
+
+UAT_RANGE_CB_DEF(zmtp_tcp_protocols, tcp_port_range, zmtp_tcp_protocol_t)
+UAT_CSTRING_CB_DEF(zmtp_tcp_protocols, protocol, zmtp_tcp_protocol_t)
+
+/* Try to find matching data dissector name by TCP port */
+static const char*
+find_data_dissector_by_tcp_port(packet_info *pinfo)
+{
+ range_t* tcp_port_range;
+ const char* protocol;
+ unsigned i;
+ for (i = 0; i < num_zmtp_tcp_protocols; ++i) {
+ tcp_port_range = zmtp_tcp_protocols[i].tcp_port_range;
+ if (value_is_in_range(tcp_port_range, pinfo->srcport) ||
+ value_is_in_range(tcp_port_range, pinfo->destport)) {
+
+ protocol = zmtp_tcp_protocols[i].protocol;
+ if (protocol && strlen(protocol) > 0) {
+ return protocol;
+ }
+ }
+ }
+ return NULL;
+}
+
+
+
+/* How long is this message (by checking flags+length). cb for tcp_dissect_pdus() */
+static unsigned
+get_zmtp_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
+{
+ uint8_t flags = tvb_get_uint8(tvb, offset);
+ uint64_t length;
+
+ switch (flags) {
+ case 0xff: /* Greeting */
+ return 64;
+
+ /* 1-byte length field */
+ case 0: /* data short (last) */
+ case 1: /* data short (and more) */
+ case 4: /* command (short) */
+ length = tvb_get_uint8(tvb, offset+1);
+ return (unsigned)length + 2;
+
+ /* 8-byte length field */
+ case 2: /* data long (last) */
+ case 3: /* data long (and more) */
+ case 6: /* command (long) */
+ if (tvb_captured_length(tvb) < 9) {
+ return 0;
+ }
+ length = tvb_get_ntoh64(tvb, offset+1);
+ return (unsigned)length + 9;
+ }
+
+ return 0;
+}
+
+/* Dissect the payload of a data message */
+static void dissect_zmtp_data(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, uint64_t length,
+ zmtp_conversation_t *p_conv_data)
+{
+ if (length == 0 || !p_conv_data) {
+ return;
+ }
+
+ /* Show mechanism value */
+ proto_item *mech_ti = proto_tree_add_string(tree, hf_zmtp_mechanism, tvb, 0, 0,
+ val_to_str_const(p_conv_data->mechanism, mechanism_vals, "Unknown"));
+ proto_item_set_generated(mech_ti);
+
+ /* Is data all text? */
+ bool all_text = true;
+ for (uint64_t n=offset; n < tvb_captured_length(tvb); n++) {
+ if (!g_ascii_isprint(tvb_get_uint8(tvb, offset))) {
+ all_text = false;
+ break;
+ }
+ }
+
+ /* Add data as raw bytes */
+ proto_item *raw_data_ti = proto_tree_add_item(tree, hf_zmtp_data, tvb, offset, -1, ENC_NA);
+ /* If all text, prefer to show as text (bytes filter is still there) */
+ if (all_text) {
+ proto_item_set_hidden(raw_data_ti);
+ proto_tree_add_item(tree, hf_zmtp_data_text, tvb, offset, -1, ENC_ASCII);
+ }
+
+ col_append_fstr(pinfo->cinfo, COL_INFO, "(%" PRIu64 " bytes) ", length);
+
+ /* Should only try to make any more sense of data if mechanism is not encrypted.. */
+ if (p_conv_data->mechanism == MECH_CURVE) {
+ return;
+ }
+
+ /* Get data tvb ready */
+ tvbuff_t *data_tvb = tvb_new_subset_remaining(tvb, offset);
+
+ /* Look up UAT for dissector to use */
+ const char *protocol = find_data_dissector_by_tcp_port(pinfo);
+ if (protocol) {
+ dissector_handle_t protocol_handle = find_dissector(protocol);
+ if (protocol_handle) {
+ TRY {
+ col_set_writable(pinfo->cinfo, COL_INFO, false);
+ call_dissector_only(protocol_handle, data_tvb, pinfo, tree, NULL);
+ col_set_writable(pinfo->cinfo, COL_INFO, true);
+ }
+ CATCH_ALL {
+ }
+ ENDTRY
+
+ return;
+ }
+ }
+
+ /* Look up registered dissector table (try both ports) */
+ if (dissector_try_uint(zmtp_port_dissector_table, pinfo->destport, data_tvb, pinfo, tree)) {
+ return;
+ }
+ if (dissector_try_uint(zmtp_port_dissector_table, pinfo->srcport, data_tvb, pinfo, tree)) {
+ return;
+ }
+
+ /* Last resort (if wasn't text) - call data dissector */
+ if (!all_text) {
+ proto_item_set_hidden(raw_data_ti);
+ call_data_dissector(data_tvb, pinfo, tree);
+ }
+}
+
+/* Dissect key=data pairs to end of frame */
+static void dissect_zmtp_metadata(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
+{
+ unsigned length;
+ const unsigned char *key;
+
+ while (tvb_reported_length_remaining(tvb, offset)) {
+ /* Key */
+ length = tvb_get_uint8(tvb, offset);
+ offset++;
+ proto_tree_add_item_ret_string(tree, hf_zmtp_metadata_key, tvb, offset, length, ENC_ASCII, pinfo->pool, &key);
+ offset += length;
+
+ /* Data */
+ length = tvb_get_ntohl(tvb, offset);
+ offset += 4;
+ if (length) {
+ const unsigned char *value;
+ proto_tree_add_item_ret_string(tree, hf_zmtp_metadata_value, tvb, offset, length, ENC_ASCII, pinfo->pool, &value);
+ offset += length;
+ col_append_fstr(pinfo->cinfo, COL_INFO, " %s=%s", key, value);
+ }
+ else {
+ col_append_fstr(pinfo->cinfo, COL_INFO, " %s", key);
+ }
+ }
+}
+
+/* These command details are largely taken from the Lua dissector */
+static int dissect_zmtp_command(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree,
+ mechanism_type mechanism)
+{
+ proto_item *len_ti, *mech_ti;
+
+ /* Show mechanism value */
+ mech_ti = proto_tree_add_string(tree, hf_zmtp_mechanism, tvb, 0, 0,
+ val_to_str_const(mechanism, mechanism_vals, "Unknown"));
+ proto_item_set_generated(mech_ti);
+
+ /* command-name (len + bytes) */
+ uint32_t command_name_length;
+ const unsigned char *command_name;
+ len_ti = proto_tree_add_item_ret_uint(tree, hf_zmtp_command_name_length, tvb, offset, 1, ENC_BIG_ENDIAN, &command_name_length);
+ proto_item_set_hidden(len_ti);
+ offset++;
+ proto_tree_add_item_ret_string(tree, hf_zmtp_command_name, tvb, offset, command_name_length, ENC_ASCII, pinfo->pool, &command_name);
+ col_append_fstr(pinfo->cinfo, COL_INFO, "(%s) ", command_name);
+ offset += command_name_length;
+
+ /* What comes next depends upon the command and mechanism setting */
+ if (strcmp(command_name, "READY") == 0) {
+ switch (mechanism) {
+ case MECH_CURVE:
+ proto_tree_add_item(tree, hf_zmtp_curvezmq_nonce, tvb, offset, 8, ENC_ASCII);
+ offset += 8;
+ proto_tree_add_item(tree, hf_zmtp_curvezmq_box, tvb, offset, -1, ENC_ASCII);
+ break;
+ default:
+ /* Metadata */
+ dissect_zmtp_metadata(tvb, offset, pinfo, tree);
+ break;
+ }
+ }
+ else if (strcmp(command_name, "HELLO") == 0) {
+ switch (mechanism) {
+ case MECH_PLAIN:
+ {
+ /* N.B., these may be empty. */
+ uint8_t len;
+
+ /* Username */
+ const unsigned char *username;
+ len = tvb_get_uint8(tvb, offset);
+ offset++;
+ proto_item *username_ti = proto_tree_add_item_ret_string(tree, hf_zmtp_username, tvb, offset, len, ENC_ASCII, pinfo->pool, &username);
+ offset += len;
+ if (len == 0) {
+ proto_item_append_text(username_ti, " (empty)");
+ }
+
+ /* Password */
+ const unsigned char *password;
+ len = tvb_get_uint8(tvb, offset);
+ offset++;
+ proto_item *password_ti = proto_tree_add_item_ret_string(tree, hf_zmtp_password, tvb, offset, len, ENC_ASCII, pinfo->pool, &password);
+ offset += len;
+ if (len == 0) {
+ proto_item_append_text(password_ti, " (empty)");
+ }
+
+ col_append_fstr(pinfo->cinfo, COL_INFO, "(username=%s, password=%s) ",
+ username, password);
+
+ /* Also tap credentials */
+ tap_credential_t* auth = wmem_new0(wmem_packet_scope(), tap_credential_t);
+ auth->num = pinfo->num;
+ auth->proto = "ZMTP";
+ auth->password_hf_id = hf_zmtp_password;
+ auth->username = (char*)username;
+ auth->username_num = pinfo->num;
+ auth->info = wmem_strdup_printf(wmem_packet_scope(), "PLAIN: username/password");
+ tap_queue_packet(credentials_tap, pinfo, auth);
+ break;
+ }
+ case MECH_CURVE:
+ {
+ /* Version (in its own subtree) */
+ uint32_t major, minor;
+ proto_item *version_ti = proto_tree_add_string_format(tree, hf_zmtp_curvezmq_version, tvb, offset, 2, "", "Version");
+ proto_tree *version_tree = proto_item_add_subtree(version_ti, ett_zmtp_curvezmq_version);
+
+ /* major */
+ proto_tree_add_item_ret_uint(version_tree, hf_zmtp_curvezmq_version_major, tvb, offset, 1, ENC_NA, &major);
+ offset++;
+ /* minor */
+ proto_tree_add_item_ret_uint(version_tree, hf_zmtp_curvezmq_version_minor, tvb, offset, 1, ENC_NA, &minor);
+ offset++;
+ proto_item_append_text(version_ti, " (%u.%u)", major, minor);
+
+ /* If 1.0 */
+ if (major==1 && minor==0) {
+ /* 70 bytes padding */
+ proto_tree_add_item(tree, hf_zmtp_padding, tvb, offset, 70, ENC_NA);
+ offset += 70;
+ /* 32 bytes publickey */
+ proto_tree_add_item(tree, hf_zmtp_curvezmq_publickey, tvb, offset, 32, ENC_ASCII);
+ offset += 32;
+ /* 8 bytes nonce */
+ proto_tree_add_item(tree, hf_zmtp_curvezmq_nonce, tvb, offset, 8, ENC_ASCII);
+ offset += 8;
+ /* 80 bytes signature */
+ proto_tree_add_item(tree, hf_zmtp_curvezmq_signature, tvb, offset, 80, ENC_ASCII);
+ offset += 80;
+ }
+ else {
+ expert_add_info_format(pinfo, version_ti, &ei_zmtp_unsupported_version,
+ "Unsupported version (%u.%u)", major, minor);
+ }
+ break;
+ }
+ default:
+ /* Unexpected mechanism for receiving "HELLO" */
+ break;
+ }
+ }
+ else if (strcmp(command_name, "WELCOME") == 0) {
+ switch (mechanism) {
+ case MECH_CURVE:
+ /* Nonce (16 bytes) */
+ proto_tree_add_item(tree, hf_zmtp_curvezmq_nonce, tvb, offset, 16, ENC_ASCII);
+ offset += 16;
+ /* Box (128 bytes) */
+ proto_tree_add_item(tree, hf_zmtp_curvezmq_box, tvb, offset, 128, ENC_ASCII);
+ offset += 128;
+ break;
+ default:
+ /* Unexpected mechanism for receiving "WELCOME" */
+ break;
+ }
+
+ }
+ else if (strcmp(command_name, "INITIATE") == 0) {
+ switch (mechanism) {
+ case MECH_PLAIN:
+ /* Metadata */
+ dissect_zmtp_metadata(tvb, offset, pinfo, tree);
+ break;
+ case MECH_CURVE:
+ /* cookie (96 bytes) */
+ proto_tree_add_item(tree, hf_zmtp_curvezmq_cookie, tvb, offset, 96, ENC_ASCII);
+ offset += 96;
+ /* nonce (8 bytes) */
+ proto_tree_add_item(tree, hf_zmtp_curvezmq_nonce, tvb, offset, 8, ENC_ASCII);
+ offset += 8;
+ /* box (remainder) */
+ proto_tree_add_item(tree, hf_zmtp_curvezmq_box, tvb, offset, -1, ENC_ASCII);
+ break;
+ default:
+ /* Unexpected mechanism for receiving "INITIATE" */
+ break;
+ }
+ }
+ else if (strcmp(command_name, "ERROR") == 0) {
+ /* 1 byte length, followed by reason */
+ uint8_t len = tvb_get_uint8(tvb, offset);
+ offset++;
+ const unsigned char *reason;
+ proto_tree_add_item_ret_string(tree, hf_zmtp_error_reason, tvb, offset, len, ENC_ASCII, pinfo->pool, &reason);
+ col_append_fstr(pinfo->cinfo, COL_INFO, " reason=%s", reason);
+ offset += len;
+ }
+ else if (strcmp(command_name, "PING") == 0) {
+ /* TTL (2 bytes) */
+ proto_tree_add_item(tree, hf_zmtp_ping_ttl, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset += 2;
+ /* Context (optional, remainder) */
+ if (tvb_captured_length_remaining(tvb, offset)) {
+ proto_tree_add_item(tree, hf_zmtp_ping_context, tvb, offset, -1, ENC_ASCII);
+ }
+ }
+ else if (strcmp(command_name, "PONG") == 0) {
+ proto_tree_add_item(tree, hf_zmtp_ping_context, tvb, offset, -1, ENC_ASCII);
+ }
+
+ /* Extra separator in case data follows in same segment */
+ col_append_str(pinfo->cinfo, COL_INFO, " ");
+
+ return offset;
+}
+
+static int
+dissect_zmtp_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
+{
+ proto_tree *zmtp_tree;
+ proto_item *root_ti;
+ int offset = 0;
+
+ /* Protocol column */
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "zmtp");
+ col_clear(pinfo->cinfo, COL_INFO);
+
+ /* Protocol root */
+ root_ti = proto_tree_add_item(tree, proto_zmtp, tvb, offset, -1, ENC_NA);
+ zmtp_tree = proto_item_add_subtree(root_ti, ett_zmtp);
+
+ /* Look up, or create, conversation */
+ zmtp_conversation_t *p_conv_data;
+ conversation_t *p_conv;
+
+ p_conv = find_conversation(pinfo->num, &pinfo->net_dst, &pinfo->net_src,
+ conversation_pt_to_conversation_type(pinfo->ptype),
+ pinfo->destport, pinfo->srcport,
+ 0 /* options */);
+
+ /* Look up data from conversation */
+ p_conv_data = (zmtp_conversation_t *)conversation_get_proto_data(p_conv, proto_zmtp);
+
+ /* Create new data for conversation data if not found */
+ if (!p_conv_data && !PINFO_FD_VISITED(pinfo)) {
+ p_conv_data = wmem_new(wmem_file_scope(), zmtp_conversation_t);
+
+ /* Set initial values */
+ p_conv_data->mechanism = MECH_NULL;
+ p_conv_data->mechanism_frame = 0;
+
+ /* Store in conversation */
+ conversation_add_proto_data(p_conv, proto_zmtp, p_conv_data);
+ }
+
+ /* Flags */
+ proto_item *flags_ti;
+ uint8_t flags = tvb_get_uint8(tvb, offset);
+ if (flags == 0xff) {
+ /* Greeting value not broken down */
+ proto_tree_add_item(zmtp_tree, hf_zmtp_flags, tvb, offset, 1, ENC_BIG_ENDIAN);
+ }
+ else {
+ /* Break it down */
+ static int* const flags_fields[] = { &hf_zmtp_flags_reserved,
+ &hf_zmtp_flags_command,
+ &hf_zmtp_flags_long,
+ &hf_zmtp_flags_more,
+ NULL
+ };
+ flags_ti = proto_tree_add_bitmask(zmtp_tree, tvb, offset, hf_zmtp_flags,
+ ett_zmtp_flags, flags_fields, ENC_BIG_ENDIAN);
+ }
+ offset += 1;
+ col_append_fstr(pinfo->cinfo, COL_INFO, "%s ",
+ val_to_str(flags, flags_vals, "Unknown(%u)"));
+ proto_item_append_text(root_ti, " (%s)", val_to_str(flags, flags_vals, "Unknown(%u)"));
+
+ uint64_t length;
+
+ switch (flags) {
+ case 0xff: /* Greeting */
+ {
+ /* signature = %xFF padding %x7F */
+ proto_tree_add_item(zmtp_tree, hf_zmtp_signature, tvb, offset-1, 10, ENC_NA);
+ offset += 9;
+
+ /* version = version-major version-minor */
+ uint32_t major, minor;
+ /* subtree */
+ proto_item *version_ti = proto_tree_add_string_format(zmtp_tree, hf_zmtp_version, tvb, offset, 2, "", "Version");
+ proto_tree *version_tree = proto_item_add_subtree(version_ti, ett_zmtp_version);
+ /* major */
+ proto_tree_add_item_ret_uint(version_tree, hf_zmtp_version_major, tvb, offset, 1, ENC_NA, &major);
+ offset++;
+ /* minor */
+ proto_tree_add_item_ret_uint(version_tree, hf_zmtp_version_minor, tvb, offset, 1, ENC_NA, &minor);
+ offset++;
+ col_append_fstr(pinfo->cinfo, COL_INFO, "(version=%u.%u", major, minor);
+ proto_item_append_text(version_ti, " (%u.%u)", major, minor);
+
+ /* mechanism (20 bytes). N.B. *must* must match setting from peer */
+ const unsigned char *mechanism;
+ unsigned mechanism_len;
+ proto_tree_add_item_ret_string_and_length(zmtp_tree, hf_zmtp_mechanism, tvb, offset, 20, ENC_ASCII,
+ pinfo->pool, &mechanism, &mechanism_len);
+ offset += mechanism_len;
+ col_append_fstr(pinfo->cinfo, COL_INFO, " mechanism=%s", mechanism);
+
+ /* Store in conversation data whether NULL, PLAIN or CURVE */
+ /* This affects what we expect to find in commands, and also whether can call dissectors to data payloads */
+ if (!PINFO_FD_VISITED(pinfo)) {
+ if (strcmp(mechanism, "NULL") == 0) {
+ p_conv_data->mechanism = MECH_NULL;
+ }
+ else if (strcmp(mechanism, "PLAIN") == 0) {
+ p_conv_data->mechanism = MECH_PLAIN;
+ }
+ else if (strcmp(mechanism, "CURVE") == 0) {
+ p_conv_data->mechanism = MECH_CURVE;
+ }
+
+ p_conv_data->mechanism_frame = pinfo->num;
+ }
+
+ /* as-server */
+ bool as_server;
+ proto_tree_add_item_ret_boolean(zmtp_tree, hf_zmtp_as_server, tvb, offset, 1, ENC_NA, &as_server);
+ offset++;
+ col_append_fstr(pinfo->cinfo, COL_INFO, " %s)", tfs_get_string(as_server, &tfs_server_client));
+
+ /* filler (31 octets) */
+ proto_tree_add_item(zmtp_tree, hf_zmtp_filler, tvb, offset, -1, ENC_NA);
+ break;
+ }
+
+ case 0x04: /* Command (short) */
+ /* Length */
+ proto_tree_add_item_ret_uint64(zmtp_tree, hf_zmtp_length, tvb, offset, 1, ENC_BIG_ENDIAN, &length);
+ offset++;
+ if (p_conv_data) {
+ dissect_zmtp_command(tvb, offset, pinfo, zmtp_tree, p_conv_data->mechanism);
+ }
+ break;
+ case 0x06: /* Command (long) */
+ proto_tree_add_item_ret_uint64(zmtp_tree, hf_zmtp_length, tvb, offset, 8, ENC_BIG_ENDIAN, &length);
+ offset += 8;
+ if (p_conv_data) {
+ dissect_zmtp_command(tvb, offset, pinfo, zmtp_tree, p_conv_data->mechanism);
+ }
+ break;
+
+ case 0x0: /* Data short (more) */
+ case 0x1: /* Data short (last) */
+ proto_tree_add_item_ret_uint64(zmtp_tree, hf_zmtp_length, tvb, offset, 1, ENC_BIG_ENDIAN, &length);
+ offset++;
+ dissect_zmtp_data(tvb, offset, pinfo, zmtp_tree, length, p_conv_data);
+ break;
+
+ case 0x2: /* Data long (last) */
+ case 0x3: /* Data long (more) */
+ proto_tree_add_item_ret_uint64(zmtp_tree, hf_zmtp_length, tvb, offset, 8, ENC_BIG_ENDIAN, &length);
+ offset += 8;
+ dissect_zmtp_data(tvb, offset, pinfo, zmtp_tree, length, p_conv_data);
+ break;
+
+ default:
+ /* Expert info for unexpected flags value */
+ expert_add_info_format(pinfo, flags_ti, &ei_zmtp_unknown_flags_value,
+ "Unexpected flags value %u", flags);
+ break;
+ }
+
+ col_set_fence(pinfo->cinfo, COL_INFO);
+
+ /* Claim whole frame regardless */
+ return tvb_reported_length(tvb);
+}
+
+/******************************/
+/* Main dissection function. */
+static int
+dissect_zmtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
+{
+ /* Frame starts off with no PDUs seen */
+ static bool false_value = false;
+ p_add_proto_data(wmem_file_scope(), pinfo, proto_zmtp, 0, &false_value);
+
+ /* Find whole PDUs and send them to dissect_zmtp_message() */
+ tcp_dissect_pdus(tvb, pinfo, tree, true, /* desegment */
+ 2, /* need flags bytes + long-size */
+ get_zmtp_message_len,
+ dissect_zmtp_message, data);
+ return tvb_reported_length(tvb);
+}
+
+
+void
+proto_register_zmtp(void)
+{
+ static hf_register_info hf[] = {
+ { &hf_zmtp_flags,
+ { "Flags", "zmtp.flags", FT_UINT8, BASE_HEX,
+ VALS(flags_vals), 0x0, NULL, HFILL }},
+ { &hf_zmtp_flags_reserved,
+ { "Reserved", "zmtp.flags.reserved", FT_UINT8, BASE_HEX,
+ NULL, 0xf8, NULL, HFILL }},
+ { &hf_zmtp_flags_command,
+ { "Command", "zmtp.flags.command", FT_UINT8, BASE_HEX,
+ NULL, 0x04, NULL, HFILL }},
+ { &hf_zmtp_flags_long,
+ { "Long", "zmtp.flags.long", FT_UINT8, BASE_HEX,
+ NULL, 0x02, NULL, HFILL }},
+ { &hf_zmtp_flags_more,
+ { "More", "zmtp.flags.more", FT_UINT8, BASE_HEX,
+ NULL, 0x01, NULL, HFILL }},
+ { &hf_zmtp_length,
+ { "Length", "zmtp.length", FT_UINT64, BASE_DEC,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_data,
+ { "Data", "zmtp.data", FT_BYTES, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_data_text,
+ { "Text", "zmtp.data.text", FT_STRING, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_signature,
+ { "Signature", "zmtp.signature", FT_BYTES, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_padding,
+ { "Padding", "zmtp.padding", FT_BYTES, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_version,
+ { "Version", "zmtp.version", FT_STRING, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_version_major,
+ { "Major version", "zmtp.version.major", FT_UINT8, BASE_DEC,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_version_minor,
+ { "Minor version", "zmtp.version.minor", FT_UINT8, BASE_DEC,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_mechanism,
+ { "Mechanism", "zmtp.mechanism", FT_STRINGZ, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_as_server,
+ { "As-Server", "zmtp.as-server", FT_BOOLEAN, BASE_NONE,
+ TFS(&tfs_server_client), 0x0, NULL, HFILL }},
+ { &hf_zmtp_filler,
+ { "Filler", "zmtp.filler", FT_BYTES, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_metadata_key,
+ { "Metadata key", "zmtp.metadata.key", FT_STRING, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_metadata_value,
+ { "Metadata value", "zmtp.metadata.value", FT_STRING, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_command_name_length,
+ { "command-name length", "zmtp.command-name.length", FT_UINT8, BASE_DEC,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_command_name,
+ { "command-name", "zmtp.command-name", FT_STRING, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_curvezmq_nonce,
+ { "CurveZMQ nonce", "zmtp.curvezmq.nonce", FT_BYTES, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_curvezmq_box,
+ { "CurveZMQ box", "zmtp.curvezmq.box", FT_BYTES, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_curvezmq_version,
+ { "Version", "zmtp.curvezmq.version", FT_STRING, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_curvezmq_version_major,
+ { "Major version", "zmtp.curvezmq.version.major", FT_UINT8, BASE_DEC,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_curvezmq_version_minor,
+ { "Minor version", "zmtp.curvezmq.version.minor", FT_UINT8, BASE_DEC,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_curvezmq_publickey,
+ { "PublicKey", "zmtp.curvezmq.publickey", FT_BYTES, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_curvezmq_signature,
+ { "Signature", "zmtp.curvezmq.signature", FT_BYTES, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_curvezmq_cookie,
+ { "Cookie", "zmtp.curvezmq.cookie", FT_BYTES, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_username,
+ { "Username", "zmtp.username", FT_STRING, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_password,
+ { "Password", "zmtp.password", FT_STRING, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_error_reason,
+ { "Reason", "zmtp.reason", FT_STRING, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_ping_ttl,
+ { "TTL", "zmtp.ping.ttl", FT_UINT16, BASE_DEC,
+ NULL, 0x0, NULL, HFILL }},
+ { &hf_zmtp_ping_context,
+ { "Context", "zmtp.ping.context", FT_STRING, BASE_NONE,
+ NULL, 0x0, NULL, HFILL }}
+ };
+
+ static int *ett[] = {
+ &ett_zmtp,
+ &ett_zmtp_flags,
+ &ett_zmtp_version,
+ &ett_zmtp_curvezmq_version
+ };
+
+ expert_module_t* expert_zmtp;
+
+ static ei_register_info ei[] = {
+ { &ei_zmtp_unknown_flags_value, { "zmtp.unknown_flags_value", PI_UNDECODED, PI_WARN, "Unsupported Flags value", EXPFILL }},
+ { &ei_zmtp_unsupported_version, { "zmtp.unsupported_version", PI_PROTOCOL, PI_WARN, "Unsupported Version", EXPFILL }},
+ };
+
+ module_t *zmtp_module;
+
+ static uat_field_t zmtp_tcp_protocols_table_columns[] = {
+ UAT_FLD_RANGE(zmtp_tcp_protocols, tcp_port_range, "TCP Ports", 0xFFFF, "TCP ports on which ZMTP data payloads will be dissected as protocol"),
+ UAT_FLD_CSTRING(zmtp_tcp_protocols, protocol, "Protocol", "Protocol for data on these TCP ports"),
+ UAT_END_FIELDS
+ };
+ uat_t* zmtp_tcp_protocols_uat;
+
+ proto_zmtp = proto_register_protocol("ZeroMQ Message Transport Protocol", "ZMTP", "zmtp");
+ proto_register_field_array(proto_zmtp, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+
+ zmtp_handle = register_dissector("zmtp", dissect_zmtp, proto_zmtp);
+
+ expert_zmtp = expert_register_protocol(proto_zmtp);
+ expert_register_field_array(expert_zmtp, ei, array_length(ei));
+
+ zmtp_module = prefs_register_protocol(proto_zmtp, proto_reg_handoff_zmtp);
+
+ zmtp_tcp_protocols_uat = uat_new("ZMTP TCP Protocols",
+ sizeof(zmtp_tcp_protocol_t),
+ "zmtp_tcp_protocols",
+ true,
+ &zmtp_tcp_protocols,
+ &num_zmtp_tcp_protocols,
+ UAT_AFFECTS_DISSECTION | UAT_AFFECTS_FIELDS,
+ NULL, /* "ChZMTPTCPProtocols", */
+ zmtp_tcp_protocols_copy_cb,
+ zmtp_tcp_protocols_update_cb,
+ zmtp_tcp_protocols_free_cb,
+ NULL, /* post_update_cb */
+ NULL, /* reset_cb */
+ zmtp_tcp_protocols_table_columns
+ );
+ prefs_register_uat_preference(zmtp_module, "tcp_protocols", "ZMTP TCP protocols",
+ "Specify the protocol of data on certain TCP ports.",
+ zmtp_tcp_protocols_uat);
+
+ zmtp_port_dissector_table = register_dissector_table("zmtp.protocol",
+ "ZMTP Data Type", proto_zmtp, FT_UINT16, BASE_DEC);
+
+ credentials_tap = register_tap("credentials");
+}
+
+static void
+apply_zmtp_prefs(void)
+{
+ global_zmtp_port_range = prefs_get_range_value("zmtp", "tcp.port");
+}
+
+void
+proto_reg_handoff_zmtp(void)
+{
+ dissector_add_uint_range_with_preference("tcp.port", "", zmtp_handle);
+ apply_zmtp_prefs();
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */