Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-nano.c
blob78656a5812785729daddd00db171f97cffb01b5d
1 /* packet-nano.c
2 * Routines for Nano / RaiBlocks dissection
3 * Copyright 2018, Roland Haenel <roland@haenel.me>
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
13 * For information about Nano / RaiBlocks, go to http://www.nano.org
16 #include <config.h>
18 #include <conversation.h>
19 #include "packet-tcp.h"
20 #include <proto_data.h>
22 #include <epan/packet.h>
23 #include <epan/to_str.h>
24 #include <wsutil/str_util.h>
26 void proto_reg_handoff_nano(void);
27 void proto_register_nano(void);
29 static dissector_handle_t nano_handle, nano_tcp_handle;
31 static int proto_nano;
33 static int hf_nano_magic_number;
34 static int hf_nano_version_max;
35 static int hf_nano_version_using;
36 static int hf_nano_version_min;
37 static int hf_nano_packet_type;
38 static int hf_nano_extensions;
39 static int hf_nano_extensions_block_type;
40 static int hf_nano_keepalive_peer_ip;
41 static int hf_nano_keepalive_peer_port;
43 static int hf_nano_block_hash_previous;
44 static int hf_nano_block_hash_source;
45 static int hf_nano_block_signature;
46 static int hf_nano_block_work;
47 static int hf_nano_block_destination_account;
48 static int hf_nano_block_balance;
49 static int hf_nano_block_account;
50 static int hf_nano_block_representative_account;
51 static int hf_nano_block_link;
53 static int hf_nano_vote_account;
54 static int hf_nano_vote_signature;
55 static int hf_nano_vote_sequence;
57 static int hf_nano_bulk_pull_account;
58 static int hf_nano_bulk_pull_block_hash_end;
60 static int hf_nano_frontier_req_account;
61 static int hf_nano_frontier_req_age;
62 static int hf_nano_frontier_req_count;
64 static int hf_nano_bulk_pull_blocks_min_hash;
65 static int hf_nano_bulk_pull_blocks_max_hash;
66 static int hf_nano_bulk_pull_blocks_mode;
67 static int hf_nano_bulk_pull_blocks_max_count;
69 static int hf_nano_bulk_push_block_type;
71 static int hf_nano_bulk_pull_block_type;
73 static int hf_nano_frontier_account;
74 static int hf_nano_frontier_head_hash;
76 static int ett_nano;
77 static int ett_nano_header;
78 static int ett_nano_extensions;
79 static int ett_nano_peers;
80 static int ett_nano_peer_details[8];
81 static int ett_nano_block;
82 static int ett_nano_vote;
83 static int ett_nano_bulk_pull;
84 static int ett_nano_frontier_req;
85 static int ett_nano_bulk_pull_blocks;
86 static int ett_nano_frontier;
88 #define NANO_PACKET_TYPE_INVALID 0
89 #define NANO_PACKET_TYPE_NOT_A_TYPE 1
90 #define NANO_PACKET_TYPE_KEEPALIVE 2
91 #define NANO_PACKET_TYPE_PUBLISH 3
92 #define NANO_PACKET_TYPE_CONFIRM_REQ 4
93 #define NANO_PACKET_TYPE_CONFIRM_ACK 5
94 #define NANO_PACKET_TYPE_BULK_PULL 6
95 #define NANO_PACKET_TYPE_BULK_PUSH 7
96 #define NANO_PACKET_TYPE_FRONTIER_REQ 8
97 #define NANO_PACKET_TYPE_BULK_PULL_BLOCKS 9
99 static const value_string nano_packet_type_strings[] = {
100 { NANO_PACKET_TYPE_INVALID, "Invalid" },
101 { NANO_PACKET_TYPE_NOT_A_TYPE, "Not A Type" },
102 { NANO_PACKET_TYPE_KEEPALIVE, "Keepalive" },
103 { NANO_PACKET_TYPE_PUBLISH, "Publish" },
104 { NANO_PACKET_TYPE_CONFIRM_REQ, "Confirm Req" },
105 { NANO_PACKET_TYPE_CONFIRM_ACK, "Confirm Ack" },
106 { NANO_PACKET_TYPE_BULK_PULL, "Bulk Pull" },
107 { NANO_PACKET_TYPE_BULK_PUSH, "Bulk Push" },
108 { NANO_PACKET_TYPE_FRONTIER_REQ, "Frontier Req" },
109 { NANO_PACKET_TYPE_BULK_PULL_BLOCKS, "Bulk Pull Blocks" },
110 { 0, NULL },
113 #define NANO_BLOCK_TYPE_INVALID 0
114 #define NANO_BLOCK_TYPE_NOT_A_BLOCK 1
115 #define NANO_BLOCK_TYPE_SEND 2
116 #define NANO_BLOCK_TYPE_RECEIVE 3
117 #define NANO_BLOCK_TYPE_OPEN 4
118 #define NANO_BLOCK_TYPE_CHANGE 5
119 #define NANO_BLOCK_TYPE_STATE 6
121 static const value_string nano_block_type_strings[] = {
122 { NANO_BLOCK_TYPE_INVALID, "Invalid" },
123 { NANO_BLOCK_TYPE_NOT_A_BLOCK, "Not A Block" },
124 { NANO_BLOCK_TYPE_SEND, "Send" },
125 { NANO_BLOCK_TYPE_RECEIVE, "Receive" },
126 { NANO_BLOCK_TYPE_OPEN, "Open" },
127 { NANO_BLOCK_TYPE_CHANGE, "Change" },
128 { NANO_BLOCK_TYPE_STATE, "State" },
129 { 0, NULL },
132 static const string_string nano_magic_numbers[] = {
133 { "RA", "Nano Test Network" },
134 { "RB", "Nano Beta Network" },
135 { "RC", "Nano Production Network" },
136 { NULL, NULL }
139 #define NANO_BULK_PULL_BLOCKS_MODE_LIST_BLOCKS 0
140 #define NANO_BULK_PULL_BLOCKS_MODE_CHECKSUM_BLOCKS 1
142 static const value_string nano_bulk_pull_blocks_mode_strings[] = {
143 { NANO_BULK_PULL_BLOCKS_MODE_LIST_BLOCKS, "List Blocks" },
144 { NANO_BULK_PULL_BLOCKS_MODE_CHECKSUM_BLOCKS, "Checksum Blocks" },
145 { 0, NULL },
148 #define NANO_UDP_PORT 7075 /* Not IANA registered */
149 #define NANO_TCP_PORT 7075 /* Not IANA registered */
151 #define NANO_BLOCK_SIZE_SEND (32+32+16+64+8)
152 #define NANO_BLOCK_SIZE_RECEIVE (32+32+64+8)
153 #define NANO_BLOCK_SIZE_OPEN (32+32+32+64+8)
154 #define NANO_BLOCK_SIZE_CHANGE (32+32+64+8)
155 #define NANO_BLOCK_SIZE_STATE (32+32+32+16+32+64+8)
157 // Nano header length, and thus minimum length of any Nano UDP packet (or bootstrap request)
158 #define NANO_HEADER_LENGTH 8
160 // Nano bootstrap session state
161 struct nano_session_state {
162 int client_packet_type;
163 uint32_t server_port;
167 // dissect the inside of a keepalive packet (that is, the neighbor nodes)
168 static int dissect_nano_keepalive(tvbuff_t *tvb, packet_info *pinfo, proto_tree *nano_tree, int offset)
170 proto_item *ti;
171 proto_tree *peer_tree, *peer_entry_tree;
172 int i, peers;
173 ws_in6_addr ip_addr;
174 uint32_t port;
175 char buf[100];
177 peer_tree = proto_tree_add_subtree(nano_tree, tvb, offset, 8*(16+2), ett_nano_peers, NULL, "Peer List");
179 peers = 0;
180 for (i = 0; i < 8; i++) {
181 peer_entry_tree = proto_tree_add_subtree(peer_tree, tvb, offset, 18, ett_nano_peer_details[i], &ti, "Peer");
183 tvb_get_ipv6(tvb, offset, &ip_addr);
184 proto_tree_add_item(peer_entry_tree, hf_nano_keepalive_peer_ip, tvb, offset, 16, ENC_NA);
185 offset += 16;
187 proto_tree_add_item_ret_uint(peer_entry_tree, hf_nano_keepalive_peer_port, tvb, offset, 2, ENC_LITTLE_ENDIAN, &port);
188 offset += 2;
190 if (!memcmp(&ip_addr, "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0", 16)) {
191 proto_item_append_text(ti, ": (none)");
192 } else if (!memcmp(&ip_addr, "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\xff\xff", 12)) {
193 ip_addr_to_str_buf((ws_in4_addr *)((uint8_t *)&ip_addr + 12), buf, sizeof(buf));
194 proto_item_append_text(ti, ": %s:%d", buf, port);
195 peers++;
196 } else {
197 ip6_to_str_buf(&ip_addr, buf, sizeof(buf));
198 proto_item_append_text(ti, ": [%s]:%d", buf, port);
199 peers++;
203 col_add_fstr(pinfo->cinfo, COL_INFO, "Keepalive (%d peer%s)", peers, plurality(peers, "", "s"));
205 return offset;
208 // dissect a receive block
209 static int dissect_nano_receive_block(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
211 proto_tree *block_tree;
213 block_tree = proto_tree_add_subtree(nano_tree, tvb, offset, NANO_BLOCK_SIZE_RECEIVE, ett_nano_block, NULL, "Receive Block");
215 proto_tree_add_item(block_tree, hf_nano_block_hash_previous, tvb, offset, 32, ENC_NA);
216 offset += 32;
218 proto_tree_add_item(block_tree, hf_nano_block_hash_source, tvb, offset, 32, ENC_NA);
219 offset += 32;
221 proto_tree_add_item(block_tree, hf_nano_block_signature, tvb, offset, 64, ENC_NA);
222 offset += 64;
224 proto_tree_add_item(block_tree, hf_nano_block_work, tvb, offset, 8, ENC_NA);
225 offset += 8;
227 return offset;
230 // dissect a send block
231 static int dissect_nano_send_block(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
233 proto_tree *block_tree;
235 block_tree = proto_tree_add_subtree(nano_tree, tvb, offset, NANO_BLOCK_SIZE_SEND, ett_nano_block, NULL, "Send Block");
237 proto_tree_add_item(block_tree, hf_nano_block_hash_previous, tvb, offset, 32, ENC_NA);
238 offset += 32;
240 proto_tree_add_item(block_tree, hf_nano_block_destination_account, tvb, offset, 32, ENC_NA);
241 offset += 32;
243 proto_tree_add_item(block_tree, hf_nano_block_balance, tvb, offset, 16, ENC_NA);
244 offset += 16;
246 proto_tree_add_item(block_tree, hf_nano_block_signature, tvb, offset, 64, ENC_NA);
247 offset += 64;
249 proto_tree_add_item(block_tree, hf_nano_block_work, tvb, offset, 8, ENC_NA);
250 offset += 8;
252 return offset;
255 // dissect an open block
256 static int dissect_nano_open_block(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
258 proto_tree *block_tree;
260 block_tree = proto_tree_add_subtree(nano_tree, tvb, offset, NANO_BLOCK_SIZE_OPEN, ett_nano_block, NULL, "Open Block");
262 proto_tree_add_item(block_tree, hf_nano_block_hash_source, tvb, offset, 32, ENC_NA);
263 offset += 32;
265 proto_tree_add_item(block_tree, hf_nano_block_representative_account, tvb, offset, 32, ENC_NA);
266 offset += 32;
268 proto_tree_add_item(block_tree, hf_nano_block_account, tvb, offset, 32, ENC_NA);
269 offset += 32;
271 proto_tree_add_item(block_tree, hf_nano_block_signature, tvb, offset, 64, ENC_NA);
272 offset += 64;
274 proto_tree_add_item(block_tree, hf_nano_block_work, tvb, offset, 8, ENC_NA);
275 offset += 8;
277 return offset;
280 // dissect an change block
281 static int dissect_nano_change_block(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
283 proto_tree *block_tree;
285 block_tree = proto_tree_add_subtree(nano_tree, tvb, offset, NANO_BLOCK_SIZE_CHANGE, ett_nano_block, NULL, "Change Block");
287 proto_tree_add_item(block_tree, hf_nano_block_hash_previous, tvb, offset, 32, ENC_NA);
288 offset += 32;
290 proto_tree_add_item(block_tree, hf_nano_block_representative_account, tvb, offset, 32, ENC_NA);
291 offset += 32;
293 proto_tree_add_item(block_tree, hf_nano_block_signature, tvb, offset, 64, ENC_NA);
294 offset += 64;
296 proto_tree_add_item(block_tree, hf_nano_block_work, tvb, offset, 8, ENC_NA);
297 offset += 8;
299 return offset;
302 // dissect a state block
303 static int dissect_nano_state(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
305 proto_tree *block_tree;
307 block_tree = proto_tree_add_subtree(nano_tree, tvb, offset, NANO_BLOCK_SIZE_STATE, ett_nano_block, NULL, "State Block");
309 proto_tree_add_item(block_tree, hf_nano_block_account, tvb, offset, 32, ENC_NA);
310 offset += 32;
312 proto_tree_add_item(block_tree, hf_nano_block_hash_previous, tvb, offset, 32, ENC_NA);
313 offset += 32;
315 proto_tree_add_item(block_tree, hf_nano_block_representative_account, tvb, offset, 32, ENC_NA);
316 offset += 32;
318 proto_tree_add_item(block_tree, hf_nano_block_balance, tvb, offset, 16, ENC_NA);
319 offset += 16;
321 proto_tree_add_item(block_tree, hf_nano_block_link, tvb, offset, 32, ENC_NA);
322 offset += 32;
324 proto_tree_add_item(block_tree, hf_nano_block_signature, tvb, offset, 64, ENC_NA);
325 offset += 64;
327 proto_tree_add_item(block_tree, hf_nano_block_work, tvb, offset, 8, ENC_NA);
328 offset += 8;
330 return offset;
333 // dissect a vote
334 static int dissect_nano_vote(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
336 proto_tree *vote_tree;
338 vote_tree = proto_tree_add_subtree(nano_tree, tvb, offset, 32+64+8, ett_nano_block, NULL, "Vote");
340 proto_tree_add_item(vote_tree, hf_nano_vote_account, tvb, offset, 32, ENC_NA);
341 offset += 32;
343 proto_tree_add_item(vote_tree, hf_nano_vote_signature, tvb, offset, 64, ENC_NA);
344 offset += 64;
346 proto_tree_add_item(vote_tree, hf_nano_vote_sequence, tvb, offset, 8, ENC_LITTLE_ENDIAN);
347 offset += 8;
349 return offset;
352 // dissect a Nano protocol header, fills in the values
353 // for nano_packet_type, nano_block_type
354 static int dissect_nano_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *nano_tree, int offset, unsigned *nano_packet_type, uint64_t *extensions)
356 proto_tree *header_tree;
357 char *nano_magic_number;
358 static int * const nano_extensions[] = {
359 &hf_nano_extensions_block_type,
360 NULL
363 header_tree = proto_tree_add_subtree(nano_tree, tvb, offset, NANO_HEADER_LENGTH, ett_nano_header, NULL, "Nano Protocol Header");
365 nano_magic_number = tvb_get_string_enc(pinfo->pool, tvb, offset, 2, ENC_ASCII);
366 proto_tree_add_string_format_value(header_tree, hf_nano_magic_number, tvb, 0,
367 2, nano_magic_number, "%s (%s)", str_to_str(nano_magic_number, nano_magic_numbers, "Unknown"), nano_magic_number);
368 offset += 2;
370 proto_tree_add_item(header_tree, hf_nano_version_max, tvb, offset, 1, ENC_NA);
371 offset += 1;
373 proto_tree_add_item(header_tree, hf_nano_version_using, tvb, offset, 1, ENC_NA);
374 offset += 1;
376 proto_tree_add_item(header_tree, hf_nano_version_min, tvb, offset, 1, ENC_NA);
377 offset += 1;
379 proto_tree_add_item_ret_uint(header_tree, hf_nano_packet_type, tvb, offset, 1, ENC_NA, nano_packet_type);
380 offset += 1;
382 proto_tree_add_bitmask_ret_uint64(header_tree, tvb, offset, hf_nano_extensions, ett_nano_extensions, nano_extensions, ENC_LITTLE_ENDIAN, extensions);
383 offset += 2;
385 return offset;
388 // dissect a Nano packet (UDP)
389 static int dissect_nano(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
391 proto_item *ti;
392 proto_tree *nano_tree;
393 unsigned nano_packet_type, nano_block_type, offset;
394 uint64_t extensions;
396 /* Check that the packet is long enough for it to belong to us. */
397 if (tvb_reported_length(tvb) < NANO_HEADER_LENGTH)
398 return 0;
400 col_set_str(pinfo->cinfo, COL_PROTOCOL, "Nano");
401 col_clear(pinfo->cinfo, COL_INFO);
403 ti = proto_tree_add_item(tree, proto_nano, tvb, 0, -1, ENC_NA);
404 nano_tree = proto_item_add_subtree(ti, ett_nano);
406 offset = dissect_nano_header(tvb, pinfo, nano_tree, 0, &nano_packet_type, &extensions);
408 // call specific dissectors for specific packet types
409 switch (nano_packet_type) {
410 case NANO_PACKET_TYPE_KEEPALIVE:
411 return dissect_nano_keepalive(tvb, pinfo, nano_tree, offset);
413 case NANO_PACKET_TYPE_PUBLISH:
414 case NANO_PACKET_TYPE_CONFIRM_REQ:
415 case NANO_PACKET_TYPE_CONFIRM_ACK:
417 // set the INFO header with more information
418 nano_block_type = (unsigned)((extensions >> 8) & 0xF);
419 col_add_fstr(pinfo->cinfo, COL_INFO, "%s (%s)",
420 val_to_str_const(nano_packet_type, VALS(nano_packet_type_strings), " "),
421 val_to_str(nano_block_type, VALS(nano_block_type_strings), "Unknown (%d)"));
423 // if it's a Confirm Ack packet, we first have a vote
424 if (nano_packet_type == NANO_PACKET_TYPE_CONFIRM_ACK) {
425 offset = dissect_nano_vote(tvb, nano_tree, offset);
428 // dissect the actual block
429 switch (nano_block_type) {
430 case NANO_BLOCK_TYPE_RECEIVE:
431 dissect_nano_receive_block(tvb, nano_tree, offset);
432 break;
433 case NANO_BLOCK_TYPE_SEND:
434 dissect_nano_send_block(tvb, nano_tree, offset);
435 break;
436 case NANO_BLOCK_TYPE_OPEN:
437 dissect_nano_open_block(tvb, nano_tree, offset);
438 break;
439 case NANO_BLOCK_TYPE_CHANGE:
440 dissect_nano_change_block(tvb, nano_tree, offset);
441 break;
442 case NANO_BLOCK_TYPE_STATE:
443 dissect_nano_state(tvb, nano_tree, offset);
444 break;
446 break;
448 default:
449 col_add_str(pinfo->cinfo, COL_INFO,
450 val_to_str(nano_packet_type, VALS(nano_packet_type_strings), "Unknown (%d)"));
453 return tvb_captured_length(tvb);
456 // determine the length of a nano bootstrap message (client)
457 static unsigned get_nano_tcp_client_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
459 int nano_packet_type, nano_block_type;
460 struct nano_session_state *session_state;
462 session_state = (struct nano_session_state *)data;
463 if (session_state->client_packet_type == NANO_PACKET_TYPE_BULK_PUSH) {
464 // we're in the middle of a bulk push, so we expect a block type (uint8) and a block
466 nano_block_type = tvb_get_uint8(tvb, offset);
467 switch (nano_block_type) {
468 case NANO_BLOCK_TYPE_NOT_A_BLOCK:
469 return 1;
470 case NANO_BLOCK_TYPE_SEND:
471 return 1 + NANO_BLOCK_SIZE_SEND;
472 case NANO_BLOCK_TYPE_RECEIVE:
473 return 1 + NANO_BLOCK_SIZE_RECEIVE;
474 case NANO_BLOCK_TYPE_OPEN:
475 return 1 + NANO_BLOCK_SIZE_OPEN;
476 case NANO_BLOCK_TYPE_CHANGE:
477 return 1 + NANO_BLOCK_SIZE_CHANGE;
478 case NANO_BLOCK_TYPE_STATE:
479 return 1 + NANO_BLOCK_SIZE_STATE;
480 default:
481 // this is invalid
482 return tvb_captured_length(tvb) - offset;
486 // we expect a client command, this starts with a full Nano header
487 if (tvb_captured_length(tvb) - offset < NANO_HEADER_LENGTH) {
488 return 0;
491 nano_packet_type = tvb_get_uint8(tvb, offset + 5);
493 switch (nano_packet_type) {
494 case NANO_PACKET_TYPE_BULK_PULL:
495 return NANO_HEADER_LENGTH + 32 + 32;
496 case NANO_PACKET_TYPE_BULK_PUSH:
497 return NANO_HEADER_LENGTH;
498 case NANO_PACKET_TYPE_FRONTIER_REQ:
499 return NANO_HEADER_LENGTH + 32 + 4 + 4;
500 case NANO_PACKET_TYPE_BULK_PULL_BLOCKS:
501 return NANO_HEADER_LENGTH + 32 + 32 + 1 + 4;
504 return tvb_captured_length(tvb) - offset;
507 // dissect a bulk pull request
508 static int dissect_nano_bulk_pull(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
510 proto_tree *vote_tree;
512 vote_tree = proto_tree_add_subtree(nano_tree, tvb, offset, 32+32, ett_nano_bulk_pull, NULL, "Bulk Pull");
514 proto_tree_add_item(vote_tree, hf_nano_bulk_pull_account, tvb, offset, 32, ENC_NA);
515 offset += 32;
517 proto_tree_add_item(vote_tree, hf_nano_bulk_pull_block_hash_end, tvb, offset, 32, ENC_NA);
518 offset += 32;
520 return offset;
523 // dissect a frontier request
524 static int dissect_nano_frontier_req(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
526 proto_tree *vote_tree;
528 vote_tree = proto_tree_add_subtree(nano_tree, tvb, offset, 32+4+4, ett_nano_frontier_req, NULL, "Frontier Request");
530 proto_tree_add_item(vote_tree, hf_nano_frontier_req_account, tvb, offset, 32, ENC_NA);
531 offset += 32;
533 proto_tree_add_item(vote_tree, hf_nano_frontier_req_age, tvb, offset, 4, ENC_LITTLE_ENDIAN);
534 offset += 4;
536 proto_tree_add_item(vote_tree, hf_nano_frontier_req_count, tvb, offset, 4, ENC_LITTLE_ENDIAN);
537 offset += 4;
539 return offset;
542 // dissect a bulk pull blocks request
543 static int dissect_nano_bulk_pull_blocks(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
545 proto_tree *vote_tree;
547 vote_tree = proto_tree_add_subtree(nano_tree, tvb, offset, 32+4+4, ett_nano_frontier_req, NULL, "Bulk Pull Blocks");
549 proto_tree_add_item(vote_tree, hf_nano_bulk_pull_blocks_min_hash, tvb, offset, 32, ENC_NA);
550 offset += 32;
552 proto_tree_add_item(vote_tree, hf_nano_bulk_pull_blocks_max_hash, tvb, offset, 32, ENC_NA);
553 offset += 32;
555 proto_tree_add_item(nano_tree, hf_nano_bulk_pull_blocks_mode, tvb, offset, 1, ENC_NA);
556 offset += 1;
558 proto_tree_add_item(vote_tree, hf_nano_bulk_pull_blocks_max_count, tvb, offset, 4, ENC_LITTLE_ENDIAN);
559 offset += 4;
561 return offset;
564 // dissect a single nano bootstrap message (client)
565 static int dissect_nano_tcp_client_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void *data _U_)
567 int offset, nano_packet_type, nano_block_type;
568 uint64_t extensions;
569 struct nano_session_state *session_state;
571 session_state = (struct nano_session_state *)data;
573 if (session_state->client_packet_type == NANO_PACKET_TYPE_BULK_PUSH) {
574 // we're within a bulk push
575 col_set_str(pinfo->cinfo, COL_INFO, "Bulk Push ");
576 proto_tree_add_item_ret_uint(tree, hf_nano_bulk_push_block_type, tvb, 0, 1, ENC_NA, &nano_block_type);
577 switch (nano_block_type) {
578 case NANO_BLOCK_TYPE_NOT_A_BLOCK:
579 session_state->client_packet_type = NANO_PACKET_TYPE_INVALID;
580 break;
581 case NANO_BLOCK_TYPE_SEND:
582 dissect_nano_send_block(tvb, tree, 1);
583 break;
584 case NANO_BLOCK_TYPE_RECEIVE:
585 dissect_nano_receive_block(tvb, tree, 1);
586 break;
587 case NANO_BLOCK_TYPE_OPEN:
588 dissect_nano_open_block(tvb, tree, 1);
589 break;
590 case NANO_BLOCK_TYPE_CHANGE:
591 dissect_nano_change_block(tvb, tree, 1);
592 break;
593 case NANO_BLOCK_TYPE_STATE:
594 dissect_nano_state(tvb, tree, 1);
595 break;
597 return tvb_captured_length(tvb);
600 // a bootstrap client command starts with a Nano header
601 offset = dissect_nano_header(tvb, pinfo, tree, 0, &nano_packet_type, &extensions);
602 session_state->client_packet_type = nano_packet_type;
604 switch (nano_packet_type) {
605 case NANO_PACKET_TYPE_BULK_PULL:
606 col_set_str(pinfo->cinfo, COL_INFO, "Bulk Pull Request ");
607 dissect_nano_bulk_pull(tvb, tree, offset);
608 break;
609 case NANO_PACKET_TYPE_BULK_PUSH:
610 col_set_str(pinfo->cinfo, COL_INFO, "Bulk Push Request ");
611 break;
612 case NANO_PACKET_TYPE_FRONTIER_REQ:
613 col_set_str(pinfo->cinfo, COL_INFO, "Frontier Request ");
614 dissect_nano_frontier_req(tvb, tree, offset);
615 break;
616 case NANO_PACKET_TYPE_BULK_PULL_BLOCKS:
617 col_set_str(pinfo->cinfo, COL_INFO, "Bulk Pull Blocks Request ");
618 dissect_nano_bulk_pull_blocks(tvb, tree, offset);
619 break;
622 return tvb_captured_length(tvb);
625 // determine the length of a nano bootstrap message (server)
626 static unsigned get_nano_tcp_server_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
628 int nano_block_type;
629 struct nano_session_state *session_state;
631 session_state = (struct nano_session_state *)data;
633 if (session_state->client_packet_type == NANO_PACKET_TYPE_BULK_PULL ||
634 session_state->client_packet_type == NANO_PACKET_TYPE_BULK_PULL_BLOCKS) {
635 // we're in response to a bulk pull (blocks), so we expect a block type (uint8) and a block
637 nano_block_type = tvb_get_uint8(tvb, offset);
638 switch (nano_block_type) {
639 case NANO_BLOCK_TYPE_NOT_A_BLOCK:
640 return 1;
641 case NANO_BLOCK_TYPE_SEND:
642 return 1 + NANO_BLOCK_SIZE_SEND;
643 case NANO_BLOCK_TYPE_RECEIVE:
644 return 1 + NANO_BLOCK_SIZE_RECEIVE;
645 case NANO_BLOCK_TYPE_OPEN:
646 return 1 + NANO_BLOCK_SIZE_OPEN;
647 case NANO_BLOCK_TYPE_CHANGE:
648 return 1 + NANO_BLOCK_SIZE_CHANGE;
649 case NANO_BLOCK_TYPE_STATE:
650 return 1 + NANO_BLOCK_SIZE_STATE;
651 default:
652 // this is invalid
653 return tvb_captured_length(tvb) - offset;
657 if (session_state->client_packet_type == NANO_PACKET_TYPE_FRONTIER_REQ) {
658 return 32 + 32;
661 return tvb_captured_length(tvb) - offset;
664 // dissect a frontier response entry
665 static int dissect_nano_frontier(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
667 proto_tree *frontier_tree;
669 frontier_tree = proto_tree_add_subtree(nano_tree, tvb, offset, 32+32, ett_nano_frontier, NULL, "Frontier");
671 proto_tree_add_item(frontier_tree, hf_nano_frontier_account, tvb, offset, 32, ENC_NA);
672 offset += 32;
674 proto_tree_add_item(frontier_tree, hf_nano_frontier_head_hash, tvb, offset, 32, ENC_NA);
675 offset += 32;
677 return offset;
680 // dissect a single nano bootstrap message (server)
681 static int dissect_nano_tcp_server_message(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_)
683 int nano_block_type;
684 struct nano_session_state *session_state;
686 session_state = (struct nano_session_state *)data;
688 if (session_state->client_packet_type == NANO_PACKET_TYPE_BULK_PULL ||
689 session_state->client_packet_type == NANO_PACKET_TYPE_BULK_PULL_BLOCKS) {
691 // we're within a bulk pull (blocks)
692 col_set_str(pinfo->cinfo, COL_INFO, session_state->client_packet_type == NANO_PACKET_TYPE_BULK_PULL ? "Bulk Pull Response " : "Bulk Pull Blocks Response ");
694 proto_tree_add_item_ret_uint(tree, hf_nano_bulk_pull_block_type, tvb, 0, 1, ENC_NA, &nano_block_type);
695 switch (nano_block_type) {
696 case NANO_BLOCK_TYPE_NOT_A_BLOCK:
697 session_state->client_packet_type = NANO_PACKET_TYPE_INVALID;
698 break;
699 case NANO_BLOCK_TYPE_SEND:
700 dissect_nano_send_block(tvb, tree, 1);
701 break;
702 case NANO_BLOCK_TYPE_RECEIVE:
703 dissect_nano_receive_block(tvb, tree, 1);
704 break;
705 case NANO_BLOCK_TYPE_OPEN:
706 dissect_nano_open_block(tvb, tree, 1);
707 break;
708 case NANO_BLOCK_TYPE_CHANGE:
709 dissect_nano_change_block(tvb, tree, 1);
710 break;
711 case NANO_BLOCK_TYPE_STATE:
712 dissect_nano_state(tvb, tree, 1);
713 break;
715 return tvb_captured_length(tvb);
718 if (session_state->client_packet_type == NANO_PACKET_TYPE_FRONTIER_REQ) {
719 col_set_str(pinfo->cinfo, COL_INFO, "Frontier Response ");
720 dissect_nano_frontier(tvb, tree, 0);
723 return tvb_captured_length(tvb);
726 // dissect a Nano bootstrap packet (TCP)
727 static int dissect_nano_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
729 int is_client;
730 proto_item *ti;
731 proto_tree *nano_tree;
732 conversation_t *conversation;
733 struct nano_session_state *session_state, *packet_session_state;
735 // try to find this conversation
736 if ((conversation = find_conversation_pinfo(pinfo, 0)) == NULL) {
737 // create new conversation
738 conversation = conversation_new(pinfo->num, &pinfo->src, &pinfo->dst, conversation_pt_to_conversation_type(pinfo->ptype),
739 pinfo->srcport, pinfo->destport, 0);
742 // try to find session state
743 session_state = (struct nano_session_state *)conversation_get_proto_data(conversation, proto_nano);
744 if (!session_state) {
745 // create new session state
746 session_state = wmem_new0(wmem_file_scope(), struct nano_session_state);
747 session_state->client_packet_type = NANO_PACKET_TYPE_INVALID;
748 session_state->server_port = pinfo->match_uint;
749 conversation_add_proto_data(conversation, proto_nano, session_state);
752 // check if we have a session state associated with the packet (start state for this packet)
753 packet_session_state = (struct nano_session_state *)p_get_proto_data(wmem_file_scope(), pinfo, proto_nano, 0);
754 if (!packet_session_state) {
755 // this packet does not have a stored session state, get it from the conversation
756 packet_session_state = wmem_new0(wmem_file_scope(), struct nano_session_state);
757 memcpy(packet_session_state, session_state, sizeof(struct nano_session_state));
758 p_add_proto_data(wmem_file_scope(), pinfo, proto_nano, 0, packet_session_state);
759 } else {
760 // this packet has a stored session state, take this as a starting point
761 memcpy(session_state, packet_session_state, sizeof(struct nano_session_state));
764 // set some columns to meaningful defaults
765 col_set_str(pinfo->cinfo, COL_PROTOCOL, "Nano Bootstrap");
766 col_clear(pinfo->cinfo, COL_INFO);
768 // add Nano protocol tree
769 ti = proto_tree_add_item(tree, proto_nano, tvb, 0, -1, ENC_NA);
770 nano_tree = proto_item_add_subtree(ti, ett_nano);
772 // is this a bootstrap client or server?
773 is_client = pinfo->destport == session_state->server_port;
775 if (is_client) {
776 // Nano bootstrap client
777 tcp_dissect_pdus(tvb, pinfo, nano_tree, true, 1, get_nano_tcp_client_message_len, dissect_nano_tcp_client_message, session_state);
779 } else {
780 // Nano bootstrap server
781 tcp_dissect_pdus(tvb, pinfo, nano_tree, true, 1, get_nano_tcp_server_message_len, dissect_nano_tcp_server_message, session_state);
784 return tvb_captured_length(tvb);
787 /* Heuristics test */
788 static bool test_nano(packet_info *pinfo _U_, tvbuff_t *tvb, int offset _U_, void *data _U_)
790 // if it's not a complete header length, it's not Nano.
791 if (tvb_captured_length(tvb) < NANO_HEADER_LENGTH)
792 return false;
794 // first byte must be 'R', second byte 'A' or 'B' or 'C'
795 if (tvb_get_uint8(tvb, 0) != (uint8_t) 'R')
796 return false;
798 char network = (char) tvb_get_uint8(tvb, 1);
799 if (network != 'A' && network != 'B' && network != 'C')
800 return false;
802 uint8_t version_max = tvb_get_uint8(tvb, 2);
803 uint8_t version_using = tvb_get_uint8(tvb, 3);
804 uint8_t version_min = tvb_get_uint8(tvb, 4);
805 if (version_max > 30 || version_max < version_using || version_using < version_min)
806 return false;
808 uint8_t ptype = tvb_get_uint8(tvb, 5);
809 if (ptype > 15)
810 return false;
812 return true;
815 static bool dissect_nano_heur_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
817 conversation_t *conversation;
818 struct nano_session_state *session_state;
820 if (!test_nano(pinfo, tvb, 0, data))
821 return false;
823 conversation = find_or_create_conversation(pinfo);
824 conversation_set_dissector(conversation, nano_tcp_handle);
826 // try to find session state
827 session_state = (struct nano_session_state *)conversation_get_proto_data(conversation, proto_nano);
828 if (!session_state) {
829 // create new session state
830 session_state = wmem_new0(wmem_file_scope(), struct nano_session_state);
831 session_state->client_packet_type = NANO_PACKET_TYPE_INVALID;
832 session_state->server_port = pinfo->destport;
833 conversation_add_proto_data(conversation, proto_nano, session_state);
836 dissect_nano_tcp(tvb, pinfo, tree, data);
838 return true;
841 static bool dissect_nano_heur_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
843 conversation_t *conversation;
845 if (!test_nano(pinfo, tvb, 0, data))
846 return false;
848 conversation = find_or_create_conversation(pinfo);
849 conversation_set_dissector(conversation, nano_handle);
851 dissect_nano(tvb, pinfo, tree, data);
853 return true;
856 void proto_register_nano(void)
858 static hf_register_info hf[] = {
859 { &hf_nano_magic_number,
860 { "Magic Number", "nano.magic_number",
861 FT_STRING, BASE_NONE, NULL, 0x00,
862 "Nano Protocol Magic Number", HFILL }
864 { &hf_nano_version_max,
865 { "Maximum Version", "nano.version_max",
866 FT_UINT8, BASE_DEC_HEX, NULL, 0x00,
867 "Maximum Supported Protocol Version", HFILL }
869 { &hf_nano_version_using,
870 { "Using Version", "nano.version_using",
871 FT_UINT8, BASE_DEC_HEX, NULL, 0x00,
872 "Used Protocol Version", HFILL }
874 { &hf_nano_version_min,
875 { "Minimum Version", "nano.version_min",
876 FT_UINT8, BASE_DEC_HEX, NULL, 0x00,
877 "Minimum Supported Protocol Version", HFILL }
879 { &hf_nano_packet_type,
880 { "Packet Type", "nano.packet_type",
881 FT_UINT8, BASE_DEC_HEX, VALS(nano_packet_type_strings), 0x00,
882 NULL, HFILL }
884 { &hf_nano_extensions,
885 { "Extensions Field", "nano.extensions",
886 FT_UINT16, BASE_HEX, NULL, 0x00,
887 NULL, HFILL }
889 { &hf_nano_extensions_block_type,
890 { "Block Type", "nano.extensions.block_type",
891 FT_UINT16, BASE_HEX, VALS(nano_block_type_strings), 0x0f00,
892 NULL, HFILL }
894 { &hf_nano_keepalive_peer_ip,
895 { "Peer IP Address", "nano.keepalive.peer_ip",
896 FT_IPv6, BASE_NONE, NULL, 0x00,
897 NULL, HFILL }
899 { &hf_nano_keepalive_peer_port,
900 { "Peer Port", "nano.keepalive.peer_port",
901 FT_UINT16, BASE_DEC, NULL, 0x00,
902 NULL, HFILL }
904 { &hf_nano_block_hash_previous,
905 { "Previous Block Hash", "nano.block.hash_previous",
906 FT_BYTES, BASE_NONE, NULL, 0x00,
907 NULL, HFILL }
909 { &hf_nano_block_hash_source,
910 { "Source Block Hash", "nano.block.hash_source",
911 FT_BYTES, BASE_NONE, NULL, 0x00,
912 NULL, HFILL }
914 { &hf_nano_block_signature,
915 { "Signature", "nano.block.signature",
916 FT_BYTES, BASE_NONE, NULL, 0x00,
917 NULL, HFILL }
919 { &hf_nano_block_work,
920 { "Work", "nano.block.work",
921 FT_BYTES, BASE_NONE, NULL, 0x00,
922 NULL, HFILL }
924 { &hf_nano_block_destination_account,
925 { "Destination Account", "nano.block.destination_account",
926 FT_BYTES, BASE_NONE, NULL, 0x00,
927 NULL, HFILL }
929 { &hf_nano_block_balance,
930 { "Balance", "nano.block.balance",
931 FT_BYTES, BASE_NONE, NULL, 0x00,
932 NULL, HFILL }
934 { &hf_nano_block_account,
935 { "Account", "nano.block.account",
936 FT_BYTES, BASE_NONE, NULL, 0x00,
937 NULL, HFILL }
939 { &hf_nano_block_representative_account,
940 { "Representative Account", "nano.block.representative_account",
941 FT_BYTES, BASE_NONE, NULL, 0x00,
942 NULL, HFILL }
944 { &hf_nano_block_link,
945 { "Link", "nano.block.link",
946 FT_BYTES, BASE_NONE, NULL, 0x00,
947 NULL, HFILL }
949 { &hf_nano_vote_account,
950 { "Account", "nano.vote.account",
951 FT_BYTES, BASE_NONE, NULL, 0x00,
952 NULL, HFILL }
954 { &hf_nano_vote_signature,
955 { "Signature", "nano.vote.signature",
956 FT_BYTES, BASE_NONE, NULL, 0x00,
957 NULL, HFILL }
959 { &hf_nano_vote_sequence,
960 { "Sequence", "nano.vote.sequence",
961 FT_UINT64, BASE_DEC_HEX, NULL, 0x00,
962 NULL, HFILL }
964 { &hf_nano_bulk_pull_account,
965 { "Account", "nano.bulk_pull.account",
966 FT_BYTES, BASE_NONE, NULL, 0x00,
967 NULL, HFILL }
969 { &hf_nano_bulk_pull_block_hash_end,
970 { "End Block Hash", "nano.bulk_pull_block.hash_end",
971 FT_BYTES, BASE_NONE, NULL, 0x00,
972 NULL, HFILL }
974 { &hf_nano_frontier_req_account,
975 { "Account", "nano.frontier_req.account",
976 FT_BYTES, BASE_NONE, NULL, 0x00,
977 NULL, HFILL }
979 { &hf_nano_frontier_req_age,
980 { "Age", "nano.frontier_req.age",
981 FT_UINT32, BASE_HEX_DEC, NULL, 0x00,
982 NULL, HFILL }
984 { &hf_nano_frontier_req_count,
985 { "Count", "nano.frontier_req.count",
986 FT_UINT32, BASE_HEX_DEC, NULL, 0x00,
987 NULL, HFILL }
989 { &hf_nano_bulk_pull_blocks_min_hash,
990 { "Min Block Hash", "nano.bulk_pull_blocks.min_hash",
991 FT_BYTES, BASE_NONE, NULL, 0x00,
992 NULL, HFILL }
994 { &hf_nano_bulk_pull_blocks_max_hash,
995 { "Max Block Hash", "nano.bulk_pull_blocks.max_hash",
996 FT_BYTES, BASE_NONE, NULL, 0x00,
997 NULL, HFILL }
999 { &hf_nano_bulk_pull_blocks_mode,
1000 { "Mode", "nano.bulk_pull_blocks.mode",
1001 FT_UINT8, BASE_DEC_HEX, VALS(nano_bulk_pull_blocks_mode_strings), 0x00,
1002 NULL, HFILL }
1004 { &hf_nano_bulk_pull_blocks_max_count,
1005 { "Max Count", "nano.bulk_pull_blocks.max_count",
1006 FT_UINT32, BASE_HEX_DEC, NULL, 0x00,
1007 NULL, HFILL }
1009 { &hf_nano_bulk_push_block_type,
1010 { "Block Type", "nano.bulk_push.block_type",
1011 FT_UINT8, BASE_HEX, VALS(nano_block_type_strings), 0x00,
1012 NULL, HFILL }
1014 { &hf_nano_bulk_pull_block_type,
1015 { "Block Type", "nano.bulk_pull.block_type",
1016 FT_UINT8, BASE_HEX, VALS(nano_block_type_strings), 0x00,
1017 NULL, HFILL }
1019 { &hf_nano_frontier_account,
1020 { "Account", "nano.frontier.account",
1021 FT_BYTES, BASE_NONE, NULL, 0x00,
1022 NULL, HFILL }
1024 { &hf_nano_frontier_head_hash,
1025 { "Head Hash", "nano.frontier.head_hash",
1026 FT_BYTES, BASE_NONE, NULL, 0x00,
1027 NULL, HFILL }
1031 static int *ett[] = {
1032 &ett_nano,
1033 &ett_nano_header,
1034 &ett_nano_extensions,
1035 &ett_nano_peers,
1036 &ett_nano_peer_details[0],
1037 &ett_nano_peer_details[1],
1038 &ett_nano_peer_details[2],
1039 &ett_nano_peer_details[3],
1040 &ett_nano_peer_details[4],
1041 &ett_nano_peer_details[5],
1042 &ett_nano_peer_details[6],
1043 &ett_nano_peer_details[7],
1044 &ett_nano_block,
1045 &ett_nano_vote,
1046 &ett_nano_bulk_pull,
1047 &ett_nano_frontier_req,
1048 &ett_nano_bulk_pull_blocks,
1049 &ett_nano_frontier
1052 proto_nano = proto_register_protocol("Nano Cryptocurrency Protocol", "Nano", "nano");
1054 proto_register_field_array(proto_nano, hf, array_length(hf));
1055 proto_register_subtree_array(ett, array_length(ett));
1058 void proto_reg_handoff_nano(void)
1060 nano_handle = register_dissector("nano", dissect_nano, proto_nano);
1061 dissector_add_uint_with_preference("udp.port", NANO_UDP_PORT, nano_handle);
1062 heur_dissector_add("udp", dissect_nano_heur_udp, "Nano UDP Heuristics", "nano-udp", proto_nano, HEURISTIC_DISABLE);
1064 nano_tcp_handle = register_dissector("nano-over-tcp", dissect_nano_tcp, proto_nano);
1065 dissector_add_uint_with_preference("tcp.port", NANO_TCP_PORT, nano_tcp_handle);
1066 heur_dissector_add("tcp", dissect_nano_heur_tcp, "Nano TCP Heuristics", "nano-tcp", proto_nano, HEURISTIC_DISABLE);
1070 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1072 * Local variables:
1073 * c-basic-offset: 4
1074 * tab-width: 8
1075 * indent-tabs-mode: nil
1076 * End:
1078 * vi: set shiftwidth=4 tabstop=8 expandtab:
1079 * :indentSize=4:tabSize=8:noTabs=true: