Coverage Report

Created: 2025-12-27 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-nano.c
Line
Count
Source
1
/* packet-nano.c
2
 * Routines for Nano / RaiBlocks dissection
3
 * Copyright 2018, Roland Haenel <roland@haenel.me>
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
/*
13
 * For information about Nano / RaiBlocks, go to http://www.nano.org
14
 */
15
16
#include <config.h>
17
18
#include <conversation.h>
19
#include "packet-tcp.h"
20
#include <proto_data.h>
21
22
#include <epan/packet.h>
23
#include <epan/to_str.h>
24
#include <wsutil/str_util.h>
25
26
void proto_reg_handoff_nano(void);
27
void proto_register_nano(void);
28
29
static dissector_handle_t nano_handle, nano_tcp_handle;
30
31
static int proto_nano;
32
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;
42
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;
52
53
static int hf_nano_vote_account;
54
static int hf_nano_vote_signature;
55
static int hf_nano_vote_sequence;
56
57
static int hf_nano_bulk_pull_account;
58
static int hf_nano_bulk_pull_block_hash_end;
59
60
static int hf_nano_frontier_req_account;
61
static int hf_nano_frontier_req_age;
62
static int hf_nano_frontier_req_count;
63
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;
68
69
static int hf_nano_bulk_push_block_type;
70
71
static int hf_nano_bulk_pull_block_type;
72
73
static int hf_nano_frontier_account;
74
static int hf_nano_frontier_head_hash;
75
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;
87
88
2
#define NANO_PACKET_TYPE_INVALID 0
89
#define NANO_PACKET_TYPE_NOT_A_TYPE 1
90
7
#define NANO_PACKET_TYPE_KEEPALIVE 2
91
2
#define NANO_PACKET_TYPE_PUBLISH 3
92
2
#define NANO_PACKET_TYPE_CONFIRM_REQ 4
93
6
#define NANO_PACKET_TYPE_CONFIRM_ACK 5
94
10
#define NANO_PACKET_TYPE_BULK_PULL 6
95
9
#define NANO_PACKET_TYPE_BULK_PUSH 7
96
2
#define NANO_PACKET_TYPE_FRONTIER_REQ 8
97
2
#define NANO_PACKET_TYPE_BULK_PULL_BLOCKS 9
98
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 },
111
};
112
113
#define NANO_BLOCK_TYPE_INVALID 0
114
0
#define NANO_BLOCK_TYPE_NOT_A_BLOCK 1
115
0
#define NANO_BLOCK_TYPE_SEND 2
116
1
#define NANO_BLOCK_TYPE_RECEIVE 3
117
0
#define NANO_BLOCK_TYPE_OPEN 4
118
0
#define NANO_BLOCK_TYPE_CHANGE 5
119
0
#define NANO_BLOCK_TYPE_STATE 6
120
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 },
130
};
131
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 }
137
};
138
139
#define NANO_BULK_PULL_BLOCKS_MODE_LIST_BLOCKS 0
140
#define NANO_BULK_PULL_BLOCKS_MODE_CHECKSUM_BLOCKS 1
141
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 },
146
};
147
148
14
#define NANO_UDP_PORT 7075 /* Not IANA registered */
149
14
#define NANO_TCP_PORT 7075 /* Not IANA registered */
150
151
0
#define NANO_BLOCK_SIZE_SEND    (32+32+16+64+8)
152
1
#define NANO_BLOCK_SIZE_RECEIVE (32+32+64+8)
153
0
#define NANO_BLOCK_SIZE_OPEN    (32+32+32+64+8)
154
0
#define NANO_BLOCK_SIZE_CHANGE  (32+32+64+8)
155
0
#define NANO_BLOCK_SIZE_STATE   (32+32+32+16+32+64+8)
156
157
// Nano header length, and thus minimum length of any Nano UDP packet (or bootstrap request)
158
35
#define NANO_HEADER_LENGTH 8
159
160
// Nano bootstrap session state
161
struct nano_session_state {
162
    int client_packet_type;
163
    uint32_t server_port;
164
};
165
166
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)
169
7
{
170
7
    proto_item *ti;
171
7
    proto_tree *peer_tree, *peer_entry_tree;
172
7
    int i, peers;
173
7
    ws_in6_addr ip_addr;
174
7
    uint32_t port;
175
7
    char buf[100];
176
177
7
    peer_tree = proto_tree_add_subtree(nano_tree, tvb, offset, 8*(16+2), ett_nano_peers, NULL, "Peer List");
178
179
7
    peers = 0;
180
47
    for (i = 0; i < 8; i++) {
181
40
        peer_entry_tree = proto_tree_add_subtree(peer_tree, tvb, offset, 18, ett_nano_peer_details[i], &ti, "Peer");
182
183
40
        tvb_get_ipv6(tvb, offset, &ip_addr);
184
40
        proto_tree_add_item(peer_entry_tree, hf_nano_keepalive_peer_ip, tvb, offset, 16, ENC_NA);
185
40
        offset += 16;
186
187
40
        proto_tree_add_item_ret_uint(peer_entry_tree, hf_nano_keepalive_peer_port, tvb, offset, 2, ENC_LITTLE_ENDIAN, &port);
188
40
        offset += 2;
189
190
40
        if (!memcmp(&ip_addr, "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0", 16)) {
191
12
            proto_item_append_text(ti, ": (none)");
192
28
        } else if (!memcmp(&ip_addr, "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\xff\xff", 12)) {
193
0
            ip_addr_to_str_buf((ws_in4_addr *)((uint8_t *)&ip_addr + 12), buf, sizeof(buf));
194
0
            proto_item_append_text(ti, ": %s:%d", buf, port);
195
0
            peers++;
196
28
        } else {
197
28
            ip6_to_str_buf(&ip_addr, buf, sizeof(buf));
198
28
            proto_item_append_text(ti, ": [%s]:%d", buf, port);
199
28
            peers++;
200
28
        }
201
40
    }
202
203
7
    col_add_fstr(pinfo->cinfo, COL_INFO, "Keepalive (%d peer%s)", peers, plurality(peers, "", "s"));
204
205
7
    return offset;
206
7
}
207
208
// dissect a receive block
209
static int dissect_nano_receive_block(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
210
1
{
211
1
    proto_tree *block_tree;
212
213
1
    block_tree = proto_tree_add_subtree(nano_tree, tvb, offset, NANO_BLOCK_SIZE_RECEIVE, ett_nano_block, NULL, "Receive Block");
214
215
1
    proto_tree_add_item(block_tree, hf_nano_block_hash_previous, tvb, offset, 32, ENC_NA);
216
1
    offset += 32;
217
218
1
    proto_tree_add_item(block_tree, hf_nano_block_hash_source, tvb, offset, 32, ENC_NA);
219
1
    offset += 32;
220
221
1
    proto_tree_add_item(block_tree, hf_nano_block_signature, tvb, offset, 64, ENC_NA);
222
1
    offset += 64;
223
224
1
    proto_tree_add_item(block_tree, hf_nano_block_work, tvb, offset, 8, ENC_NA);
225
1
    offset += 8;
226
227
1
    return offset;
228
1
}
229
230
// dissect a send block
231
static int dissect_nano_send_block(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
232
0
{
233
0
    proto_tree *block_tree;
234
235
0
    block_tree = proto_tree_add_subtree(nano_tree, tvb, offset, NANO_BLOCK_SIZE_SEND, ett_nano_block, NULL, "Send Block");
236
237
0
    proto_tree_add_item(block_tree, hf_nano_block_hash_previous, tvb, offset, 32, ENC_NA);
238
0
    offset += 32;
239
240
0
    proto_tree_add_item(block_tree, hf_nano_block_destination_account, tvb, offset, 32, ENC_NA);
241
0
    offset += 32;
242
243
0
    proto_tree_add_item(block_tree, hf_nano_block_balance, tvb, offset, 16, ENC_NA);
244
0
    offset += 16;
245
246
0
    proto_tree_add_item(block_tree, hf_nano_block_signature, tvb, offset, 64, ENC_NA);
247
0
    offset += 64;
248
249
0
    proto_tree_add_item(block_tree, hf_nano_block_work, tvb, offset, 8, ENC_NA);
250
0
    offset += 8;
251
252
0
    return offset;
253
0
}
254
255
// dissect an open block
256
static int dissect_nano_open_block(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
257
0
{
258
0
    proto_tree *block_tree;
259
260
0
    block_tree = proto_tree_add_subtree(nano_tree, tvb, offset, NANO_BLOCK_SIZE_OPEN, ett_nano_block, NULL, "Open Block");
261
262
0
    proto_tree_add_item(block_tree, hf_nano_block_hash_source, tvb, offset, 32, ENC_NA);
263
0
    offset += 32;
264
265
0
    proto_tree_add_item(block_tree, hf_nano_block_representative_account, tvb, offset, 32, ENC_NA);
266
0
    offset += 32;
267
268
0
    proto_tree_add_item(block_tree, hf_nano_block_account, tvb, offset, 32, ENC_NA);
269
0
    offset += 32;
270
271
0
    proto_tree_add_item(block_tree, hf_nano_block_signature, tvb, offset, 64, ENC_NA);
272
0
    offset += 64;
273
274
0
    proto_tree_add_item(block_tree, hf_nano_block_work, tvb, offset, 8, ENC_NA);
275
0
    offset += 8;
276
277
0
    return offset;
278
0
}
279
280
// dissect an change block
281
static int dissect_nano_change_block(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
282
0
{
283
0
    proto_tree *block_tree;
284
285
0
    block_tree = proto_tree_add_subtree(nano_tree, tvb, offset, NANO_BLOCK_SIZE_CHANGE, ett_nano_block, NULL, "Change Block");
286
287
0
    proto_tree_add_item(block_tree, hf_nano_block_hash_previous, tvb, offset, 32, ENC_NA);
288
0
    offset += 32;
289
290
0
    proto_tree_add_item(block_tree, hf_nano_block_representative_account, tvb, offset, 32, ENC_NA);
291
0
    offset += 32;
292
293
0
    proto_tree_add_item(block_tree, hf_nano_block_signature, tvb, offset, 64, ENC_NA);
294
0
    offset += 64;
295
296
0
    proto_tree_add_item(block_tree, hf_nano_block_work, tvb, offset, 8, ENC_NA);
297
0
    offset += 8;
298
299
0
    return offset;
300
0
}
301
302
// dissect a state block
303
static int dissect_nano_state(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
304
0
{
305
0
    proto_tree *block_tree;
306
307
0
    block_tree = proto_tree_add_subtree(nano_tree, tvb, offset, NANO_BLOCK_SIZE_STATE, ett_nano_block, NULL, "State Block");
308
309
0
    proto_tree_add_item(block_tree, hf_nano_block_account, tvb, offset, 32, ENC_NA);
310
0
    offset += 32;
311
312
0
    proto_tree_add_item(block_tree, hf_nano_block_hash_previous, tvb, offset, 32, ENC_NA);
313
0
    offset += 32;
314
315
0
    proto_tree_add_item(block_tree, hf_nano_block_representative_account, tvb, offset, 32, ENC_NA);
316
0
    offset += 32;
317
318
0
    proto_tree_add_item(block_tree, hf_nano_block_balance, tvb, offset, 16, ENC_NA);
319
0
    offset += 16;
320
321
0
    proto_tree_add_item(block_tree, hf_nano_block_link, tvb, offset, 32, ENC_NA);
322
0
    offset += 32;
323
324
0
    proto_tree_add_item(block_tree, hf_nano_block_signature, tvb, offset, 64, ENC_NA);
325
0
    offset += 64;
326
327
0
    proto_tree_add_item(block_tree, hf_nano_block_work, tvb, offset, 8, ENC_NA);
328
0
    offset += 8;
329
330
0
    return offset;
331
0
}
332
333
// dissect a vote
334
static int dissect_nano_vote(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
335
1
{
336
1
    proto_tree *vote_tree;
337
338
1
    vote_tree = proto_tree_add_subtree(nano_tree, tvb, offset, 32+64+8, ett_nano_block, NULL, "Vote");
339
340
1
    proto_tree_add_item(vote_tree, hf_nano_vote_account, tvb, offset, 32, ENC_NA);
341
1
    offset += 32;
342
343
1
    proto_tree_add_item(vote_tree, hf_nano_vote_signature, tvb, offset, 64, ENC_NA);
344
1
    offset += 64;
345
346
1
    proto_tree_add_item(vote_tree, hf_nano_vote_sequence, tvb, offset, 8, ENC_LITTLE_ENDIAN);
347
1
    offset += 8;
348
349
1
    return offset;
350
1
}
351
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)
355
15
{
356
15
    proto_tree *header_tree;
357
15
    char *nano_magic_number;
358
15
    static int * const nano_extensions[] = {
359
15
        &hf_nano_extensions_block_type,
360
15
        NULL
361
15
    };
362
363
15
    header_tree = proto_tree_add_subtree(nano_tree, tvb, offset, NANO_HEADER_LENGTH, ett_nano_header, NULL, "Nano Protocol Header");
364
365
15
    nano_magic_number = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset, 2, ENC_ASCII);
366
15
    proto_tree_add_string_format_value(header_tree, hf_nano_magic_number, tvb, 0,
367
15
            2, nano_magic_number, "%s (%s)", str_to_str_wmem(pinfo->pool, nano_magic_number, nano_magic_numbers, "Unknown"), nano_magic_number);
368
15
    offset += 2;
369
370
15
    proto_tree_add_item(header_tree, hf_nano_version_max, tvb, offset, 1, ENC_NA);
371
15
    offset += 1;
372
373
15
    proto_tree_add_item(header_tree, hf_nano_version_using, tvb, offset, 1, ENC_NA);
374
15
    offset += 1;
375
376
15
    proto_tree_add_item(header_tree, hf_nano_version_min, tvb, offset, 1, ENC_NA);
377
15
    offset += 1;
378
379
15
    proto_tree_add_item_ret_uint(header_tree, hf_nano_packet_type, tvb, offset, 1, ENC_NA, nano_packet_type);
380
15
    offset += 1;
381
382
15
    proto_tree_add_bitmask_ret_uint64(header_tree, tvb, offset, hf_nano_extensions, ett_nano_extensions, nano_extensions, ENC_LITTLE_ENDIAN, extensions);
383
15
    offset += 2;
384
385
15
    return offset;
386
15
}
387
388
// dissect a Nano packet (UDP)
389
static int dissect_nano(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
390
12
{
391
12
    proto_item *ti;
392
12
    proto_tree *nano_tree;
393
12
    unsigned nano_packet_type, nano_block_type, offset;
394
12
    uint64_t extensions;
395
396
    /* Check that the packet is long enough for it to belong to us. */
397
12
    if (tvb_reported_length(tvb) < NANO_HEADER_LENGTH)
398
1
        return 0;
399
400
11
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "Nano");
401
11
    col_clear(pinfo->cinfo, COL_INFO);
402
403
11
    ti = proto_tree_add_item(tree, proto_nano, tvb, 0, -1, ENC_NA);
404
11
    nano_tree = proto_item_add_subtree(ti, ett_nano);
405
406
11
    offset = dissect_nano_header(tvb, pinfo, nano_tree, 0, &nano_packet_type, &extensions);
407
408
    // call specific dissectors for specific packet types
409
11
    switch (nano_packet_type) {
410
7
        case NANO_PACKET_TYPE_KEEPALIVE:
411
7
            return dissect_nano_keepalive(tvb, pinfo, nano_tree, offset);
412
413
2
        case NANO_PACKET_TYPE_PUBLISH:
414
2
        case NANO_PACKET_TYPE_CONFIRM_REQ:
415
3
        case NANO_PACKET_TYPE_CONFIRM_ACK:
416
417
            // set the INFO header with more information
418
3
            nano_block_type = (unsigned)((extensions >> 8) & 0xF);
419
3
            col_add_fstr(pinfo->cinfo, COL_INFO, "%s (%s)",
420
3
                    val_to_str_const(nano_packet_type, VALS(nano_packet_type_strings), " "),
421
3
                    val_to_str(pinfo->pool, nano_block_type, VALS(nano_block_type_strings), "Unknown (%d)"));
422
423
            // if it's a Confirm Ack packet, we first have a vote
424
3
            if (nano_packet_type == NANO_PACKET_TYPE_CONFIRM_ACK) {
425
1
                offset = dissect_nano_vote(tvb, nano_tree, offset);
426
1
            }
427
428
            // dissect the actual block
429
3
            switch (nano_block_type) {
430
1
                case NANO_BLOCK_TYPE_RECEIVE:
431
1
                    dissect_nano_receive_block(tvb, nano_tree, offset);
432
1
                    break;
433
0
                case NANO_BLOCK_TYPE_SEND:
434
0
                    dissect_nano_send_block(tvb, nano_tree, offset);
435
0
                    break;
436
0
                case NANO_BLOCK_TYPE_OPEN:
437
0
                    dissect_nano_open_block(tvb, nano_tree, offset);
438
0
                    break;
439
0
                case NANO_BLOCK_TYPE_CHANGE:
440
0
                    dissect_nano_change_block(tvb, nano_tree, offset);
441
0
                    break;
442
0
                case NANO_BLOCK_TYPE_STATE:
443
0
                    dissect_nano_state(tvb, nano_tree, offset);
444
0
                    break;
445
3
            }
446
1
            break;
447
448
1
        default:
449
1
            col_add_str(pinfo->cinfo, COL_INFO,
450
1
                    val_to_str(pinfo->pool, nano_packet_type, VALS(nano_packet_type_strings), "Unknown (%d)"));
451
11
    }
452
453
2
    return tvb_captured_length(tvb);
454
11
}
455
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_)
458
5
{
459
5
    int nano_packet_type, nano_block_type;
460
5
    struct nano_session_state *session_state;
461
462
5
    session_state = (struct nano_session_state *)data;
463
5
    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
465
466
0
        nano_block_type = tvb_get_uint8(tvb, offset);
467
0
        switch (nano_block_type) {
468
0
            case NANO_BLOCK_TYPE_NOT_A_BLOCK:
469
0
                return 1;
470
0
            case NANO_BLOCK_TYPE_SEND:
471
0
                return 1 + NANO_BLOCK_SIZE_SEND;
472
0
            case NANO_BLOCK_TYPE_RECEIVE:
473
0
                return 1 + NANO_BLOCK_SIZE_RECEIVE;
474
0
            case NANO_BLOCK_TYPE_OPEN:
475
0
                return 1 + NANO_BLOCK_SIZE_OPEN;
476
0
            case NANO_BLOCK_TYPE_CHANGE:
477
0
                return 1 + NANO_BLOCK_SIZE_CHANGE;
478
0
            case NANO_BLOCK_TYPE_STATE:
479
0
                return 1 + NANO_BLOCK_SIZE_STATE;
480
0
            default:
481
                // this is invalid
482
0
                return tvb_captured_length(tvb) - offset;
483
0
        }
484
0
    }
485
486
    // we expect a client command, this starts with a full Nano header
487
5
    if (tvb_captured_length(tvb) - offset < NANO_HEADER_LENGTH) {
488
1
        return 0;
489
1
    }
490
491
4
    nano_packet_type = tvb_get_uint8(tvb, offset + 5);
492
493
4
    switch (nano_packet_type) {
494
3
        case NANO_PACKET_TYPE_BULK_PULL:
495
3
            return NANO_HEADER_LENGTH + 32 + 32;
496
0
        case NANO_PACKET_TYPE_BULK_PUSH:
497
0
            return NANO_HEADER_LENGTH;
498
0
        case NANO_PACKET_TYPE_FRONTIER_REQ:
499
0
            return NANO_HEADER_LENGTH + 32 + 4 + 4;
500
0
        case NANO_PACKET_TYPE_BULK_PULL_BLOCKS:
501
0
            return NANO_HEADER_LENGTH + 32 + 32 + 1 + 4;
502
4
    }
503
504
1
    return tvb_captured_length(tvb) - offset;
505
4
}
506
507
// dissect a bulk pull request
508
static int dissect_nano_bulk_pull(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
509
3
{
510
3
    proto_tree *vote_tree;
511
512
3
    vote_tree = proto_tree_add_subtree(nano_tree, tvb, offset, 32+32, ett_nano_bulk_pull, NULL, "Bulk Pull");
513
514
3
    proto_tree_add_item(vote_tree, hf_nano_bulk_pull_account, tvb, offset, 32, ENC_NA);
515
3
    offset += 32;
516
517
3
    proto_tree_add_item(vote_tree, hf_nano_bulk_pull_block_hash_end, tvb, offset, 32, ENC_NA);
518
3
    offset += 32;
519
520
3
    return offset;
521
3
}
522
523
// dissect a frontier request
524
static int dissect_nano_frontier_req(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
525
0
{
526
0
    proto_tree *vote_tree;
527
528
0
    vote_tree = proto_tree_add_subtree(nano_tree, tvb, offset, 32+4+4, ett_nano_frontier_req, NULL, "Frontier Request");
529
530
0
    proto_tree_add_item(vote_tree, hf_nano_frontier_req_account, tvb, offset, 32, ENC_NA);
531
0
    offset += 32;
532
533
0
    proto_tree_add_item(vote_tree, hf_nano_frontier_req_age, tvb, offset, 4, ENC_LITTLE_ENDIAN);
534
0
    offset += 4;
535
536
0
    proto_tree_add_item(vote_tree, hf_nano_frontier_req_count, tvb, offset, 4, ENC_LITTLE_ENDIAN);
537
0
    offset += 4;
538
539
0
    return offset;
540
0
}
541
542
// dissect a bulk pull blocks request
543
static int dissect_nano_bulk_pull_blocks(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
544
0
{
545
0
    proto_tree *vote_tree;
546
547
0
    vote_tree = proto_tree_add_subtree(nano_tree, tvb, offset, 32+4+4, ett_nano_frontier_req, NULL, "Bulk Pull Blocks");
548
549
0
    proto_tree_add_item(vote_tree, hf_nano_bulk_pull_blocks_min_hash, tvb, offset, 32, ENC_NA);
550
0
    offset += 32;
551
552
0
    proto_tree_add_item(vote_tree, hf_nano_bulk_pull_blocks_max_hash, tvb, offset, 32, ENC_NA);
553
0
    offset += 32;
554
555
0
    proto_tree_add_item(nano_tree, hf_nano_bulk_pull_blocks_mode, tvb, offset, 1, ENC_NA);
556
0
    offset += 1;
557
558
0
    proto_tree_add_item(vote_tree, hf_nano_bulk_pull_blocks_max_count, tvb, offset, 4, ENC_LITTLE_ENDIAN);
559
0
    offset += 4;
560
561
0
    return offset;
562
0
}
563
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_)
566
4
{
567
4
    int offset;
568
4
    uint32_t nano_packet_type, nano_block_type;
569
4
    uint64_t extensions;
570
4
    struct nano_session_state *session_state;
571
572
4
    session_state = (struct nano_session_state *)data;
573
574
4
    if (session_state->client_packet_type == NANO_PACKET_TYPE_BULK_PUSH) {
575
        // we're within a bulk push
576
0
        col_set_str(pinfo->cinfo, COL_INFO, "Bulk Push ");
577
0
        proto_tree_add_item_ret_uint(tree, hf_nano_bulk_push_block_type, tvb, 0, 1, ENC_NA, &nano_block_type);
578
0
        switch (nano_block_type) {
579
0
            case NANO_BLOCK_TYPE_NOT_A_BLOCK:
580
0
                session_state->client_packet_type = NANO_PACKET_TYPE_INVALID;
581
0
                break;
582
0
            case NANO_BLOCK_TYPE_SEND:
583
0
                dissect_nano_send_block(tvb, tree, 1);
584
0
                break;
585
0
            case NANO_BLOCK_TYPE_RECEIVE:
586
0
                dissect_nano_receive_block(tvb, tree, 1);
587
0
                break;
588
0
            case NANO_BLOCK_TYPE_OPEN:
589
0
                dissect_nano_open_block(tvb, tree, 1);
590
0
                break;
591
0
            case NANO_BLOCK_TYPE_CHANGE:
592
0
                dissect_nano_change_block(tvb, tree, 1);
593
0
                break;
594
0
            case NANO_BLOCK_TYPE_STATE:
595
0
                dissect_nano_state(tvb, tree, 1);
596
0
                break;
597
0
        }
598
0
        return tvb_captured_length(tvb);
599
0
    }
600
601
    // a bootstrap client command starts with a Nano header
602
4
    offset = dissect_nano_header(tvb, pinfo, tree, 0, &nano_packet_type, &extensions);
603
4
    session_state->client_packet_type = nano_packet_type;
604
605
4
    switch (nano_packet_type) {
606
3
        case NANO_PACKET_TYPE_BULK_PULL:
607
3
            col_set_str(pinfo->cinfo, COL_INFO, "Bulk Pull Request ");
608
3
            dissect_nano_bulk_pull(tvb, tree, offset);
609
3
            break;
610
0
        case NANO_PACKET_TYPE_BULK_PUSH:
611
0
            col_set_str(pinfo->cinfo, COL_INFO, "Bulk Push Request ");
612
0
            break;
613
0
        case NANO_PACKET_TYPE_FRONTIER_REQ:
614
0
            col_set_str(pinfo->cinfo, COL_INFO, "Frontier Request ");
615
0
            dissect_nano_frontier_req(tvb, tree, offset);
616
0
            break;
617
0
        case NANO_PACKET_TYPE_BULK_PULL_BLOCKS:
618
0
            col_set_str(pinfo->cinfo, COL_INFO, "Bulk Pull Blocks Request ");
619
0
            dissect_nano_bulk_pull_blocks(tvb, tree, offset);
620
0
            break;
621
4
    }
622
623
4
    return tvb_captured_length(tvb);
624
4
}
625
626
// determine the length of a nano bootstrap message (server)
627
static unsigned get_nano_tcp_server_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
628
1
{
629
1
    int nano_block_type;
630
1
    struct nano_session_state *session_state;
631
632
1
    session_state = (struct nano_session_state *)data;
633
634
1
    if (session_state->client_packet_type == NANO_PACKET_TYPE_BULK_PULL ||
635
1
        session_state->client_packet_type == NANO_PACKET_TYPE_BULK_PULL_BLOCKS) {
636
        // we're in response to a bulk pull (blocks), so we expect a block type (uint8) and a block
637
638
0
        nano_block_type = tvb_get_uint8(tvb, offset);
639
0
        switch (nano_block_type) {
640
0
            case NANO_BLOCK_TYPE_NOT_A_BLOCK:
641
0
                return 1;
642
0
            case NANO_BLOCK_TYPE_SEND:
643
0
                return 1 + NANO_BLOCK_SIZE_SEND;
644
0
            case NANO_BLOCK_TYPE_RECEIVE:
645
0
                return 1 + NANO_BLOCK_SIZE_RECEIVE;
646
0
            case NANO_BLOCK_TYPE_OPEN:
647
0
                return 1 + NANO_BLOCK_SIZE_OPEN;
648
0
            case NANO_BLOCK_TYPE_CHANGE:
649
0
                return 1 + NANO_BLOCK_SIZE_CHANGE;
650
0
            case NANO_BLOCK_TYPE_STATE:
651
0
                return 1 + NANO_BLOCK_SIZE_STATE;
652
0
            default:
653
                // this is invalid
654
0
                return tvb_captured_length(tvb) - offset;
655
0
        }
656
0
    }
657
658
1
    if (session_state->client_packet_type == NANO_PACKET_TYPE_FRONTIER_REQ) {
659
0
        return 32 + 32;
660
0
    }
661
662
1
    return tvb_captured_length(tvb) - offset;
663
1
}
664
665
// dissect a frontier response entry
666
static int dissect_nano_frontier(tvbuff_t *tvb, proto_tree *nano_tree, int offset)
667
0
{
668
0
    proto_tree *frontier_tree;
669
670
0
    frontier_tree = proto_tree_add_subtree(nano_tree, tvb, offset, 32+32, ett_nano_frontier, NULL, "Frontier");
671
672
0
    proto_tree_add_item(frontier_tree, hf_nano_frontier_account, tvb, offset, 32, ENC_NA);
673
0
    offset += 32;
674
675
0
    proto_tree_add_item(frontier_tree, hf_nano_frontier_head_hash, tvb, offset, 32, ENC_NA);
676
0
    offset += 32;
677
678
0
    return offset;
679
0
}
680
681
// dissect a single nano bootstrap message (server)
682
static int dissect_nano_tcp_server_message(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_)
683
1
{
684
1
    uint32_t nano_block_type;
685
1
    struct nano_session_state *session_state;
686
687
1
    session_state = (struct nano_session_state *)data;
688
689
1
    if (session_state->client_packet_type == NANO_PACKET_TYPE_BULK_PULL ||
690
1
        session_state->client_packet_type == NANO_PACKET_TYPE_BULK_PULL_BLOCKS) {
691
692
        // we're within a bulk pull (blocks)
693
0
        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
695
0
        proto_tree_add_item_ret_uint(tree, hf_nano_bulk_pull_block_type, tvb, 0, 1, ENC_NA, &nano_block_type);
696
0
        switch (nano_block_type) {
697
0
            case NANO_BLOCK_TYPE_NOT_A_BLOCK:
698
0
                session_state->client_packet_type = NANO_PACKET_TYPE_INVALID;
699
0
                break;
700
0
            case NANO_BLOCK_TYPE_SEND:
701
0
                dissect_nano_send_block(tvb, tree, 1);
702
0
                break;
703
0
            case NANO_BLOCK_TYPE_RECEIVE:
704
0
                dissect_nano_receive_block(tvb, tree, 1);
705
0
                break;
706
0
            case NANO_BLOCK_TYPE_OPEN:
707
0
                dissect_nano_open_block(tvb, tree, 1);
708
0
                break;
709
0
            case NANO_BLOCK_TYPE_CHANGE:
710
0
                dissect_nano_change_block(tvb, tree, 1);
711
0
                break;
712
0
            case NANO_BLOCK_TYPE_STATE:
713
0
                dissect_nano_state(tvb, tree, 1);
714
0
                break;
715
0
        }
716
0
        return tvb_captured_length(tvb);
717
0
    }
718
719
1
    if (session_state->client_packet_type == NANO_PACKET_TYPE_FRONTIER_REQ) {
720
0
        col_set_str(pinfo->cinfo, COL_INFO, "Frontier Response ");
721
0
        dissect_nano_frontier(tvb, tree, 0);
722
0
    }
723
724
1
    return tvb_captured_length(tvb);
725
1
}
726
727
// dissect a Nano bootstrap packet (TCP)
728
static int dissect_nano_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
729
4
{
730
4
    int is_client;
731
4
    proto_item *ti;
732
4
    proto_tree *nano_tree;
733
4
    conversation_t *conversation;
734
4
    struct nano_session_state *session_state, *packet_session_state;
735
736
    // try to find this conversation
737
4
    if ((conversation = find_conversation_pinfo(pinfo, 0)) == NULL) {
738
        // create new conversation
739
0
        conversation = conversation_new(pinfo->num, &pinfo->src, &pinfo->dst, conversation_pt_to_conversation_type(pinfo->ptype),
740
0
                pinfo->srcport, pinfo->destport, 0);
741
0
    }
742
743
    // try to find session state
744
4
    session_state = (struct nano_session_state *)conversation_get_proto_data(conversation, proto_nano);
745
4
    if (!session_state) {
746
        // create new session state
747
2
        session_state = wmem_new0(wmem_file_scope(), struct nano_session_state);
748
2
        session_state->client_packet_type = NANO_PACKET_TYPE_INVALID;
749
2
        session_state->server_port = pinfo->match_uint;
750
2
        conversation_add_proto_data(conversation, proto_nano, session_state);
751
2
    }
752
753
    // check if we have a session state associated with the packet (start state for this packet)
754
4
    packet_session_state = (struct nano_session_state *)p_get_proto_data(wmem_file_scope(), pinfo, proto_nano, 0);
755
4
    if (!packet_session_state) {
756
        // this packet does not have a stored session state, get it from the conversation
757
4
        packet_session_state = wmem_new0(wmem_file_scope(), struct nano_session_state);
758
4
        memcpy(packet_session_state, session_state, sizeof(struct nano_session_state));
759
4
        p_add_proto_data(wmem_file_scope(), pinfo, proto_nano, 0, packet_session_state);
760
4
    } else {
761
        // this packet has a stored session state, take this as a starting point
762
0
        memcpy(session_state, packet_session_state, sizeof(struct nano_session_state));
763
0
    }
764
765
    // set some columns to meaningful defaults
766
4
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "Nano Bootstrap");
767
4
    col_clear(pinfo->cinfo, COL_INFO);
768
769
    // add Nano protocol tree
770
4
    ti = proto_tree_add_item(tree, proto_nano, tvb, 0, -1, ENC_NA);
771
4
    nano_tree = proto_item_add_subtree(ti, ett_nano);
772
773
    // is this a bootstrap client or server?
774
4
    is_client = pinfo->destport == session_state->server_port;
775
776
4
    if (is_client) {
777
        // Nano bootstrap client
778
3
        tcp_dissect_pdus(tvb, pinfo, nano_tree, true, 1, get_nano_tcp_client_message_len, dissect_nano_tcp_client_message, session_state);
779
780
3
    } else {
781
        // Nano bootstrap server
782
1
        tcp_dissect_pdus(tvb, pinfo, nano_tree, true, 1, get_nano_tcp_server_message_len, dissect_nano_tcp_server_message, session_state);
783
1
    }
784
785
4
    return tvb_captured_length(tvb);
786
4
}
787
788
/* Heuristics test */
789
static bool test_nano(packet_info *pinfo _U_, tvbuff_t *tvb, int offset _U_, void *data _U_)
790
0
{
791
    // if it's not a complete header length, it's not Nano.
792
0
    if (tvb_captured_length(tvb) < NANO_HEADER_LENGTH)
793
0
        return false;
794
795
    // first byte must be 'R', second byte 'A' or 'B' or 'C'
796
0
    if (tvb_get_uint8(tvb, 0) != (uint8_t) 'R')
797
0
        return false;
798
799
0
    char network = (char) tvb_get_uint8(tvb, 1);
800
0
    if (network != 'A' && network != 'B' && network != 'C')
801
0
        return false;
802
803
0
    uint8_t version_max = tvb_get_uint8(tvb, 2);
804
0
    uint8_t version_using = tvb_get_uint8(tvb, 3);
805
0
    uint8_t version_min = tvb_get_uint8(tvb, 4);
806
0
    if (version_max > 30 || version_max < version_using || version_using < version_min)
807
0
        return false;
808
809
0
    uint8_t ptype = tvb_get_uint8(tvb, 5);
810
0
    if (ptype > 15)
811
0
        return false;
812
813
0
    return true;
814
0
}
815
816
static bool dissect_nano_heur_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
817
0
{
818
0
    conversation_t *conversation;
819
0
    struct nano_session_state *session_state;
820
821
0
    if (!test_nano(pinfo, tvb, 0, data))
822
0
        return false;
823
824
0
    conversation = find_or_create_conversation(pinfo);
825
0
    conversation_set_dissector(conversation, nano_tcp_handle);
826
827
    // try to find session state
828
0
    session_state = (struct nano_session_state *)conversation_get_proto_data(conversation, proto_nano);
829
0
    if (!session_state) {
830
        // create new session state
831
0
        session_state = wmem_new0(wmem_file_scope(), struct nano_session_state);
832
0
        session_state->client_packet_type = NANO_PACKET_TYPE_INVALID;
833
0
        session_state->server_port = pinfo->destport;
834
0
        conversation_add_proto_data(conversation, proto_nano, session_state);
835
0
    }
836
837
0
    dissect_nano_tcp(tvb, pinfo, tree, data);
838
839
0
    return true;
840
0
}
841
842
static bool dissect_nano_heur_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
843
0
{
844
0
    conversation_t *conversation;
845
846
0
    if (!test_nano(pinfo, tvb, 0, data))
847
0
        return false;
848
849
0
    conversation = find_or_create_conversation(pinfo);
850
0
    conversation_set_dissector(conversation, nano_handle);
851
852
0
    dissect_nano(tvb, pinfo, tree, data);
853
854
0
    return true;
855
0
}
856
857
void proto_register_nano(void)
858
14
{
859
14
    static hf_register_info hf[] = {
860
14
        { &hf_nano_magic_number,
861
14
          { "Magic Number", "nano.magic_number",
862
14
            FT_STRING, BASE_NONE, NULL, 0x00,
863
14
            "Nano Protocol Magic Number", HFILL }
864
14
        },
865
14
        { &hf_nano_version_max,
866
14
          { "Maximum Version", "nano.version_max",
867
14
            FT_UINT8, BASE_DEC_HEX, NULL, 0x00,
868
14
            "Maximum Supported Protocol Version", HFILL }
869
14
        },
870
14
        { &hf_nano_version_using,
871
14
          { "Using Version", "nano.version_using",
872
14
            FT_UINT8, BASE_DEC_HEX, NULL, 0x00,
873
14
            "Used Protocol Version", HFILL }
874
14
        },
875
14
        { &hf_nano_version_min,
876
14
          { "Minimum Version", "nano.version_min",
877
14
            FT_UINT8, BASE_DEC_HEX, NULL, 0x00,
878
14
            "Minimum Supported Protocol Version", HFILL }
879
14
        },
880
14
        { &hf_nano_packet_type,
881
14
          { "Packet Type", "nano.packet_type",
882
14
            FT_UINT8, BASE_DEC_HEX, VALS(nano_packet_type_strings), 0x00,
883
14
            NULL, HFILL }
884
14
        },
885
14
        { &hf_nano_extensions,
886
14
          { "Extensions Field", "nano.extensions",
887
14
            FT_UINT16, BASE_HEX, NULL, 0x00,
888
14
            NULL, HFILL }
889
14
        },
890
14
        { &hf_nano_extensions_block_type,
891
14
          { "Block Type", "nano.extensions.block_type",
892
14
            FT_UINT16, BASE_HEX, VALS(nano_block_type_strings), 0x0f00,
893
14
            NULL, HFILL }
894
14
        },
895
14
        { &hf_nano_keepalive_peer_ip,
896
14
          { "Peer IP Address", "nano.keepalive.peer_ip",
897
14
            FT_IPv6, BASE_NONE, NULL, 0x00,
898
14
            NULL, HFILL }
899
14
        },
900
14
        { &hf_nano_keepalive_peer_port,
901
14
          { "Peer Port", "nano.keepalive.peer_port",
902
14
            FT_UINT16, BASE_DEC, NULL, 0x00,
903
14
            NULL, HFILL }
904
14
        },
905
14
        { &hf_nano_block_hash_previous,
906
14
          { "Previous Block Hash", "nano.block.hash_previous",
907
14
            FT_BYTES, BASE_NONE, NULL, 0x00,
908
14
            NULL, HFILL }
909
14
        },
910
14
        { &hf_nano_block_hash_source,
911
14
          { "Source Block Hash", "nano.block.hash_source",
912
14
            FT_BYTES, BASE_NONE, NULL, 0x00,
913
14
            NULL, HFILL }
914
14
        },
915
14
        { &hf_nano_block_signature,
916
14
          { "Signature", "nano.block.signature",
917
14
            FT_BYTES, BASE_NONE, NULL, 0x00,
918
14
            NULL, HFILL }
919
14
        },
920
14
        { &hf_nano_block_work,
921
14
          { "Work", "nano.block.work",
922
14
            FT_BYTES, BASE_NONE, NULL, 0x00,
923
14
            NULL, HFILL }
924
14
        },
925
14
        { &hf_nano_block_destination_account,
926
14
          { "Destination Account", "nano.block.destination_account",
927
14
            FT_BYTES, BASE_NONE, NULL, 0x00,
928
14
            NULL, HFILL }
929
14
        },
930
14
        { &hf_nano_block_balance,
931
14
          { "Balance", "nano.block.balance",
932
14
            FT_BYTES, BASE_NONE, NULL, 0x00,
933
14
            NULL, HFILL }
934
14
        },
935
14
        { &hf_nano_block_account,
936
14
          { "Account", "nano.block.account",
937
14
            FT_BYTES, BASE_NONE, NULL, 0x00,
938
14
            NULL, HFILL }
939
14
        },
940
14
        { &hf_nano_block_representative_account,
941
14
          { "Representative Account", "nano.block.representative_account",
942
14
            FT_BYTES, BASE_NONE, NULL, 0x00,
943
14
            NULL, HFILL }
944
14
        },
945
14
        { &hf_nano_block_link,
946
14
          { "Link", "nano.block.link",
947
14
            FT_BYTES, BASE_NONE, NULL, 0x00,
948
14
            NULL, HFILL }
949
14
        },
950
14
        { &hf_nano_vote_account,
951
14
          { "Account", "nano.vote.account",
952
14
            FT_BYTES, BASE_NONE, NULL, 0x00,
953
14
            NULL, HFILL }
954
14
        },
955
14
        { &hf_nano_vote_signature,
956
14
          { "Signature", "nano.vote.signature",
957
14
            FT_BYTES, BASE_NONE, NULL, 0x00,
958
14
            NULL, HFILL }
959
14
        },
960
14
        { &hf_nano_vote_sequence,
961
14
          { "Sequence", "nano.vote.sequence",
962
14
            FT_UINT64, BASE_DEC_HEX, NULL, 0x00,
963
14
            NULL, HFILL }
964
14
        },
965
14
        { &hf_nano_bulk_pull_account,
966
14
          { "Account", "nano.bulk_pull.account",
967
14
            FT_BYTES, BASE_NONE, NULL, 0x00,
968
14
            NULL, HFILL }
969
14
        },
970
14
        { &hf_nano_bulk_pull_block_hash_end,
971
14
          { "End Block Hash", "nano.bulk_pull_block.hash_end",
972
14
            FT_BYTES, BASE_NONE, NULL, 0x00,
973
14
            NULL, HFILL }
974
14
        },
975
14
        { &hf_nano_frontier_req_account,
976
14
          { "Account", "nano.frontier_req.account",
977
14
            FT_BYTES, BASE_NONE, NULL, 0x00,
978
14
            NULL, HFILL }
979
14
        },
980
14
        { &hf_nano_frontier_req_age,
981
14
          { "Age", "nano.frontier_req.age",
982
14
            FT_UINT32, BASE_HEX_DEC, NULL, 0x00,
983
14
            NULL, HFILL }
984
14
        },
985
14
        { &hf_nano_frontier_req_count,
986
14
          { "Count", "nano.frontier_req.count",
987
14
            FT_UINT32, BASE_HEX_DEC, NULL, 0x00,
988
14
            NULL, HFILL }
989
14
        },
990
14
        { &hf_nano_bulk_pull_blocks_min_hash,
991
14
          { "Min Block Hash", "nano.bulk_pull_blocks.min_hash",
992
14
            FT_BYTES, BASE_NONE, NULL, 0x00,
993
14
            NULL, HFILL }
994
14
        },
995
14
        { &hf_nano_bulk_pull_blocks_max_hash,
996
14
          { "Max Block Hash", "nano.bulk_pull_blocks.max_hash",
997
14
            FT_BYTES, BASE_NONE, NULL, 0x00,
998
14
            NULL, HFILL }
999
14
        },
1000
14
        { &hf_nano_bulk_pull_blocks_mode,
1001
14
          { "Mode", "nano.bulk_pull_blocks.mode",
1002
14
            FT_UINT8, BASE_DEC_HEX, VALS(nano_bulk_pull_blocks_mode_strings), 0x00,
1003
14
            NULL, HFILL }
1004
14
        },
1005
14
        { &hf_nano_bulk_pull_blocks_max_count,
1006
14
          { "Max Count", "nano.bulk_pull_blocks.max_count",
1007
14
            FT_UINT32, BASE_HEX_DEC, NULL, 0x00,
1008
14
            NULL, HFILL }
1009
14
        },
1010
14
        { &hf_nano_bulk_push_block_type,
1011
14
          { "Block Type", "nano.bulk_push.block_type",
1012
14
            FT_UINT8, BASE_HEX, VALS(nano_block_type_strings), 0x00,
1013
14
            NULL, HFILL }
1014
14
        },
1015
14
        { &hf_nano_bulk_pull_block_type,
1016
14
          { "Block Type", "nano.bulk_pull.block_type",
1017
14
            FT_UINT8, BASE_HEX, VALS(nano_block_type_strings), 0x00,
1018
14
            NULL, HFILL }
1019
14
        },
1020
14
        { &hf_nano_frontier_account,
1021
14
          { "Account", "nano.frontier.account",
1022
14
            FT_BYTES, BASE_NONE, NULL, 0x00,
1023
14
            NULL, HFILL }
1024
14
        },
1025
14
        { &hf_nano_frontier_head_hash,
1026
14
          { "Head Hash", "nano.frontier.head_hash",
1027
14
            FT_BYTES, BASE_NONE, NULL, 0x00,
1028
14
            NULL, HFILL }
1029
14
        }
1030
14
    };
1031
1032
14
    static int *ett[] = {
1033
14
        &ett_nano,
1034
14
        &ett_nano_header,
1035
14
        &ett_nano_extensions,
1036
14
        &ett_nano_peers,
1037
14
        &ett_nano_peer_details[0],
1038
14
        &ett_nano_peer_details[1],
1039
14
        &ett_nano_peer_details[2],
1040
14
        &ett_nano_peer_details[3],
1041
14
        &ett_nano_peer_details[4],
1042
14
        &ett_nano_peer_details[5],
1043
14
        &ett_nano_peer_details[6],
1044
14
        &ett_nano_peer_details[7],
1045
14
        &ett_nano_block,
1046
14
        &ett_nano_vote,
1047
14
        &ett_nano_bulk_pull,
1048
14
        &ett_nano_frontier_req,
1049
14
        &ett_nano_bulk_pull_blocks,
1050
14
        &ett_nano_frontier
1051
14
    };
1052
1053
14
    proto_nano = proto_register_protocol("Nano Cryptocurrency Protocol", "Nano", "nano");
1054
1055
14
    proto_register_field_array(proto_nano, hf, array_length(hf));
1056
14
    proto_register_subtree_array(ett, array_length(ett));
1057
14
}
1058
1059
void proto_reg_handoff_nano(void)
1060
14
{
1061
14
    nano_handle = register_dissector("nano", dissect_nano, proto_nano);
1062
14
    dissector_add_uint_with_preference("udp.port", NANO_UDP_PORT, nano_handle);
1063
14
    heur_dissector_add("udp", dissect_nano_heur_udp, "Nano UDP Heuristics", "nano-udp", proto_nano, HEURISTIC_DISABLE);
1064
1065
14
    nano_tcp_handle = register_dissector("nano-over-tcp", dissect_nano_tcp, proto_nano);
1066
14
    dissector_add_uint_with_preference("tcp.port", NANO_TCP_PORT, nano_tcp_handle);
1067
14
    heur_dissector_add("tcp", dissect_nano_heur_tcp, "Nano TCP Heuristics", "nano-tcp", proto_nano, HEURISTIC_DISABLE);
1068
14
}
1069
1070
/*
1071
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1072
 *
1073
 * Local variables:
1074
 * c-basic-offset: 4
1075
 * tab-width: 8
1076
 * indent-tabs-mode: nil
1077
 * End:
1078
 *
1079
 * vi: set shiftwidth=4 tabstop=8 expandtab:
1080
 * :indentSize=4:tabSize=8:noTabs=true:
1081
 */