Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-zmtp.c
blob1c347062f700e9bb1e6f799cafa6fba22a7044b4
1 /* packet-zmtp.c
2 * ZeroMQ Message Transport Protocol as described at https://rfc.zeromq.org/spec/23/
3 * Martin Mathieson
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..
20 #include "config.h"
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>
27 #include <epan/uat.h>
28 #include <epan/tfs.h>
29 #include <wsutil/array.h>
30 #include <tap.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;
73 /* Subtrees */
74 static int ett_zmtp;
75 static int ett_zmtp_flags;
76 static int ett_zmtp_version;
77 static int ett_zmtp_curvezmq_version;
79 /* Expert info */
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 /**************************************************************************/
99 typedef enum
101 MECH_NULL=0, /* assuming as default */
102 MECH_PLAIN,
103 MECH_CURVE
104 } mechanism_type;
106 static const value_string mechanism_vals[] =
108 { MECH_NULL, "NULL" },
109 { MECH_PLAIN, "PLAIN" },
110 { MECH_CURVE, "CURVE" },
111 { 0x0, NULL }
115 typedef struct
117 mechanism_type mechanism;
118 uint32_t mechanism_frame;
119 } zmtp_conversation_t;
121 static const value_string flags_vals[] =
123 { 0xff, "Greeting" },
124 { 0x00, "Data" },
125 { 0x01, "Data(+)" },
126 { 0x02, "Data" },
127 { 0x03, "Data(+)" },
128 { 0x04, "Command" },
129 { 0x06, "Command" },
130 { 0x0, NULL }
134 /**************************************************************************/
135 /* Preferences state */
136 /**************************************************************************/
138 /* The data payload type of the data on certain TCP ports */
139 typedef struct {
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;
147 static void *
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);
163 return new_rec;
166 static bool
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);
176 return false;
179 wmem_free(NULL, empty);
180 return true;
183 static void
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 */
196 static const char*
197 find_data_dissector_by_tcp_port(packet_info *pinfo)
199 range_t* tcp_port_range;
200 const char* protocol;
201 unsigned i;
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) {
209 return protocol;
213 return NULL;
218 /* How long is this message (by checking flags+length). cb for tcp_dissect_pdus() */
219 static unsigned
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);
223 uint64_t length;
225 switch (flags) {
226 case 0xff: /* Greeting */
227 return 64;
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) {
241 return 0;
243 length = tvb_get_ntoh64(tvb, offset+1);
244 return (unsigned)length + 9;
247 return 0;
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) {
255 return;
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))) {
267 all_text = false;
268 break;
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) */
275 if (all_text) {
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) {
284 return;
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);
292 if (protocol) {
293 dissector_handle_t protocol_handle = find_dissector(protocol);
294 if (protocol_handle) {
295 TRY {
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);
300 CATCH_ALL {
302 ENDTRY
304 return;
308 /* Look up registered dissector table (try both ports) */
309 if (dissector_try_uint(zmtp_port_dissector_table, pinfo->destport, data_tvb, pinfo, tree)) {
310 return;
312 if (dissector_try_uint(zmtp_port_dissector_table, pinfo->srcport, data_tvb, pinfo, tree)) {
313 return;
316 /* Last resort (if wasn't text) - call data dissector */
317 if (!all_text) {
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)
326 unsigned length;
327 const unsigned char *key;
329 while (tvb_reported_length_remaining(tvb, offset)) {
330 /* Key */
331 length = tvb_get_uint8(tvb, offset);
332 offset++;
333 proto_tree_add_item_ret_string(tree, hf_zmtp_metadata_key, tvb, offset, length, ENC_ASCII, pinfo->pool, &key);
334 offset += length;
336 /* Data */
337 length = tvb_get_ntohl(tvb, offset);
338 offset += 4;
339 if (length) {
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);
342 offset += length;
343 col_append_fstr(pinfo->cinfo, COL_INFO, " %s=%s", key, value);
345 else {
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);
367 offset++;
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) {
374 switch (mechanism) {
375 case MECH_CURVE:
376 proto_tree_add_item(tree, hf_zmtp_curvezmq_nonce, tvb, offset, 8, ENC_ASCII);
377 offset += 8;
378 proto_tree_add_item(tree, hf_zmtp_curvezmq_box, tvb, offset, -1, ENC_ASCII);
379 break;
380 default:
381 /* Metadata */
382 dissect_zmtp_metadata(tvb, offset, pinfo, tree);
383 break;
386 else if (strcmp(command_name, "HELLO") == 0) {
387 switch (mechanism) {
388 case MECH_PLAIN:
390 /* N.B., these may be empty. */
391 uint8_t len;
393 /* Username */
394 const unsigned char *username;
395 len = tvb_get_uint8(tvb, offset);
396 offset++;
397 proto_item *username_ti = proto_tree_add_item_ret_string(tree, hf_zmtp_username, tvb, offset, len, ENC_ASCII, pinfo->pool, &username);
398 offset += len;
399 if (len == 0) {
400 proto_item_append_text(username_ti, " (empty)");
403 /* Password */
404 const unsigned char *password;
405 len = tvb_get_uint8(tvb, offset);
406 offset++;
407 proto_item *password_ti = proto_tree_add_item_ret_string(tree, hf_zmtp_password, tvb, offset, len, ENC_ASCII, pinfo->pool, &password);
408 offset += len;
409 if (len == 0) {
410 proto_item_append_text(password_ti, " (empty)");
413 col_append_fstr(pinfo->cinfo, COL_INFO, "(username=%s, password=%s) ",
414 username, password);
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);
425 break;
427 case MECH_CURVE:
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);
434 /* major */
435 proto_tree_add_item_ret_uint(version_tree, hf_zmtp_curvezmq_version_major, tvb, offset, 1, ENC_NA, &major);
436 offset++;
437 /* minor */
438 proto_tree_add_item_ret_uint(version_tree, hf_zmtp_curvezmq_version_minor, tvb, offset, 1, ENC_NA, &minor);
439 offset++;
440 proto_item_append_text(version_ti, " (%u.%u)", major, minor);
442 /* If 1.0 */
443 if (major==1 && minor==0) {
444 /* 70 bytes padding */
445 proto_tree_add_item(tree, hf_zmtp_padding, tvb, offset, 70, ENC_NA);
446 offset += 70;
447 /* 32 bytes publickey */
448 proto_tree_add_item(tree, hf_zmtp_curvezmq_publickey, tvb, offset, 32, ENC_ASCII);
449 offset += 32;
450 /* 8 bytes nonce */
451 proto_tree_add_item(tree, hf_zmtp_curvezmq_nonce, tvb, offset, 8, ENC_ASCII);
452 offset += 8;
453 /* 80 bytes signature */
454 proto_tree_add_item(tree, hf_zmtp_curvezmq_signature, tvb, offset, 80, ENC_ASCII);
455 offset += 80;
457 else {
458 expert_add_info_format(pinfo, version_ti, &ei_zmtp_unsupported_version,
459 "Unsupported version (%u.%u)", major, minor);
461 break;
463 default:
464 /* Unexpected mechanism for receiving "HELLO" */
465 break;
468 else if (strcmp(command_name, "WELCOME") == 0) {
469 switch (mechanism) {
470 case MECH_CURVE:
471 /* Nonce (16 bytes) */
472 proto_tree_add_item(tree, hf_zmtp_curvezmq_nonce, tvb, offset, 16, ENC_ASCII);
473 offset += 16;
474 /* Box (128 bytes) */
475 proto_tree_add_item(tree, hf_zmtp_curvezmq_box, tvb, offset, 128, ENC_ASCII);
476 offset += 128;
477 break;
478 default:
479 /* Unexpected mechanism for receiving "WELCOME" */
480 break;
484 else if (strcmp(command_name, "INITIATE") == 0) {
485 switch (mechanism) {
486 case MECH_PLAIN:
487 /* Metadata */
488 dissect_zmtp_metadata(tvb, offset, pinfo, tree);
489 break;
490 case MECH_CURVE:
491 /* cookie (96 bytes) */
492 proto_tree_add_item(tree, hf_zmtp_curvezmq_cookie, tvb, offset, 96, ENC_ASCII);
493 offset += 96;
494 /* nonce (8 bytes) */
495 proto_tree_add_item(tree, hf_zmtp_curvezmq_nonce, tvb, offset, 8, ENC_ASCII);
496 offset += 8;
497 /* box (remainder) */
498 proto_tree_add_item(tree, hf_zmtp_curvezmq_box, tvb, offset, -1, ENC_ASCII);
499 break;
500 default:
501 /* Unexpected mechanism for receiving "INITIATE" */
502 break;
505 else if (strcmp(command_name, "ERROR") == 0) {
506 /* 1 byte length, followed by reason */
507 uint8_t len = tvb_get_uint8(tvb, offset);
508 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);
512 offset += len;
514 else if (strcmp(command_name, "PING") == 0) {
515 /* TTL (2 bytes) */
516 proto_tree_add_item(tree, hf_zmtp_ping_ttl, tvb, offset, 2, ENC_BIG_ENDIAN);
517 offset += 2;
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, " ");
530 return offset;
533 static int
534 dissect_zmtp_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
536 proto_tree *zmtp_tree;
537 proto_item *root_ti;
538 int offset = 0;
540 /* Protocol column */
541 col_set_str(pinfo->cinfo, COL_PROTOCOL, "zmtp");
542 col_clear(pinfo->cinfo, COL_INFO);
544 /* Protocol root */
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,
555 0 /* options */);
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);
572 /* Flags */
573 proto_item *flags_ti;
574 uint8_t flags = tvb_get_uint8(tvb, offset);
575 if (flags == 0xff) {
576 /* Greeting value not broken down */
577 proto_tree_add_item(zmtp_tree, hf_zmtp_flags, tvb, offset, 1, ENC_BIG_ENDIAN);
579 else {
580 /* Break it down */
581 static int* const flags_fields[] = { &hf_zmtp_flags_reserved,
582 &hf_zmtp_flags_command,
583 &hf_zmtp_flags_long,
584 &hf_zmtp_flags_more,
585 NULL
587 flags_ti = proto_tree_add_bitmask(zmtp_tree, tvb, offset, hf_zmtp_flags,
588 ett_zmtp_flags, flags_fields, ENC_BIG_ENDIAN);
590 offset += 1;
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)"));
595 uint64_t length;
597 switch (flags) {
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);
602 offset += 9;
604 /* version = version-major version-minor */
605 uint32_t major, minor;
606 /* subtree */
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);
609 /* major */
610 proto_tree_add_item_ret_uint(version_tree, hf_zmtp_version_major, tvb, offset, 1, ENC_NA, &major);
611 offset++;
612 /* minor */
613 proto_tree_add_item_ret_uint(version_tree, hf_zmtp_version_minor, tvb, offset, 1, ENC_NA, &minor);
614 offset++;
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;
642 /* as-server */
643 bool as_server;
644 proto_tree_add_item_ret_boolean(zmtp_tree, hf_zmtp_as_server, tvb, offset, 1, ENC_NA, &as_server);
645 offset++;
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);
650 break;
653 case 0x04: /* Command (short) */
654 /* Length */
655 proto_tree_add_item_ret_uint64(zmtp_tree, hf_zmtp_length, tvb, offset, 1, ENC_BIG_ENDIAN, &length);
656 offset++;
657 if (p_conv_data) {
658 dissect_zmtp_command(tvb, offset, pinfo, zmtp_tree, p_conv_data->mechanism);
660 break;
661 case 0x06: /* Command (long) */
662 proto_tree_add_item_ret_uint64(zmtp_tree, hf_zmtp_length, tvb, offset, 8, ENC_BIG_ENDIAN, &length);
663 offset += 8;
664 if (p_conv_data) {
665 dissect_zmtp_command(tvb, offset, pinfo, zmtp_tree, p_conv_data->mechanism);
667 break;
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);
672 offset++;
673 dissect_zmtp_data(tvb, offset, pinfo, zmtp_tree, length, p_conv_data);
674 break;
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);
679 offset += 8;
680 dissect_zmtp_data(tvb, offset, pinfo, zmtp_tree, length, p_conv_data);
681 break;
683 default:
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);
687 break;
690 col_set_fence(pinfo->cinfo, COL_INFO);
692 /* Claim whole frame regardless */
693 return tvb_reported_length(tvb);
696 /******************************/
697 /* Main dissection function. */
698 static int
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);
714 void
715 proto_register_zmtp(void)
717 static hf_register_info hf[] = {
718 { &hf_zmtp_flags,
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 }},
733 { &hf_zmtp_length,
734 { "Length", "zmtp.length", FT_UINT64, BASE_DEC,
735 NULL, 0x0, NULL, HFILL }},
736 { &hf_zmtp_data,
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 }},
745 { &hf_zmtp_padding,
746 { "Padding", "zmtp.padding", FT_BYTES, BASE_NONE,
747 NULL, 0x0, NULL, HFILL }},
748 { &hf_zmtp_version,
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 }},
763 { &hf_zmtp_filler,
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 }},
802 { &hf_zmtp_username,
803 { "Username", "zmtp.username", FT_STRING, BASE_NONE,
804 NULL, 0x0, NULL, HFILL }},
805 { &hf_zmtp_password,
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 }},
811 { &hf_zmtp_ping_ttl,
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[] = {
820 &ett_zmtp,
821 &ett_zmtp_flags,
822 &ett_zmtp_version,
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"),
838 UAT_END_FIELDS
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",
856 true,
857 &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 */
865 NULL, /* reset_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");
878 static void
879 apply_zmtp_prefs(void)
881 global_zmtp_port_range = prefs_get_range_value("zmtp", "tcp.port");
884 void
885 proto_reg_handoff_zmtp(void)
887 dissector_add_uint_range_with_preference("tcp.port", "", zmtp_handle);
888 apply_zmtp_prefs();
892 * Editor modelines - https://www.wireshark.org/tools/modelines.html
894 * Local variables:
895 * c-basic-offset: 4
896 * tab-width: 8
897 * indent-tabs-mode: nil
898 * End:
900 * vi: set shiftwidth=4 tabstop=8 expandtab:
901 * :indentSize=4:tabSize=8:noTabs=true: