Coverage Report

Created: 2026-11-12 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-p4rpc.c
Line
Count
Source
1
/*
2
 * packet-p4rpc.c
3
 *
4
 * A Perforce P4RPC protocol packet dissector for Wireshark
5
 * Mark Wittenberg <mwittenberg@perforce.com>, <markw-perforce@wittenberg.us>
6
 * Copyright 2012-2022 Mark Wittenberg
7
 * Copyright 2023-2025 Perforce Software Inc.
8
 * Updates 2023-2024 by Tim Brazil and Jason Gibson
9
 *
10
 * It supports P4RPC over TLS as well as over TCP.
11
 *
12
 * Wireshark - Network traffic analyzer
13
 * By Gerald Combs <gerald@wireshark.org>
14
 * Copyright 1998 Gerald Combs
15
 *
16
 * SPDX-License-Identifier: GPL-2.0-or-later
17
 *
18
 * NB: C99 supports // comments, so I'm using them
19
 */
20
21
#include <wireshark.h>
22
#include <epan/packet.h>
23
#include <epan/prefs.h>
24
#include <epan/dissectors/packet-tcp.h>
25
26
// for TLS
27
#include <epan/tvbuff.h>
28
#include <epan/dissectors/packet-tls.h>
29
30
// for expert info
31
#include <epan/expert.h>
32
33
#include <ws_symbol_export.h>
34
#include <wsutil/plugins.h>
35
#include <wsutil/str_util.h>
36
#include <wsutil/nstime.h>
37
#include <wsutil/to_str.h>
38
#include <wsutil/strtoi.h>
39
#include <epan/proto.h>
40
41
// for prefs
42
#include <wsutil/report_message.h>
43
44
#include <locale.h>
45
46
#include "config.h"
47
48
/*
49
 * To decrypt TLS, don't set both ports to the same value;
50
 * that might prevent TLS from decrypting the packets
51
 * before P4RPC tries to dissect them.
52
 * Set the TLS port to the server port number;
53
 * TLS will automatically decrypt TLS packets
54
 * if the TCP port is not equal to the TLS port.
55
 * After decryption, if a packet's src or dst port
56
 * matches the "tls.port" then P4RPC will decrypt
57
 * the packet.
58
 *
59
 * These values are just the initial default value;
60
 * users can change the defaults via
61
 *     Preferences -> Protocols -> P4RPC
62
 * and changing either the TCP or TLS ports
63
 * in the "TCP port" or "TLS port" text fields.
64
 *
65
 * P4RPC preferences can be more easily reached by right-clicking
66
 * on a P4RPC packet (if there is one) and choosing
67
 *      Protocol Preferences -> P4RPC -> Open P4RPC Preferences
68
 *
69
 * Users can also right-click on an individual packet
70
 * and choose "Decode As..." but the same rules apply.
71
 *
72
 * If you don't care about supporting TLS then
73
 * set the TCP port to the desired port and set
74
 * the TLS port to anything else.
75
 */
76
#define P4RPC_TCP_PORT 1666     // Not IANA registered
77
#define P4RPC_TLS_PORT 1667     // Not IANA registered
78
79
/*
80
 * checksum(length)     1
81
 * length               4
82
 *
83
 * Every message starts with a header
84
 * and must contain at least one message.
85
 * The smallest message has:
86
 * - (1) empty name string + NUL terminator byte
87
 * - (4) val len = 0
88
 * - (1) trailing NUL byte
89
 * ie, 6 bytes.
90
 */
91
7.72k
#define MSG_HEADER_LEN 5
92
2.75k
#define MSG_MIN_PARAM_LEN 6
93
5.27k
#define MSG_MIN_LEN (MSG_HEADER_LEN + MSG_MIN_PARAM_LEN)
94
2.41k
#define MSG_MAX_LEN 0x1FFFFFFF
95
96
// for expert message
97
#define MSG_MIN_LEN_STR "11"
98
16
#define MSG_MAX_LEN_STR "536870911"
99
100
// Normally set by Wireshark builds
101
#ifndef VERSION
102
# define VERSION        "1.0.7"
103
#endif
104
105
/*
106
 * holds the total number of messages and pdus
107
 * in a reassembled packet
108
 */
109
typedef struct pdu_info {
110
    unsigned int num_msgs;
111
    unsigned int num_pdus;
112
} pdu_info;
113
114
static dissector_handle_t p4rpc_handle;
115
116
// for tls
117
static dissector_handle_t p4rpc_tls_handle;
118
119
/*
120
* If it is false then the parameters will be decoded
121
* only as a "name=value" string.
122
*
123
* If prefs_varval_tree is true then the name=val parameters
124
* in each message will be decoded as a tree (in addition
125
* to as a "name=value" string), with separate child nodes
126
* for each of the name, value length, and value.
127
*
128
* This options can be set in the P4RPC preferences dialog in
129
*     Preferences -> Protocols -> P4RPC
130
* in the "Show message parameters as a tree" checkbox.
131
*/
132
typedef struct {
133
    unsigned int prev_tcp_port;
134
    unsigned int cur_tcp_port;
135
    unsigned int prev_tls_port;
136
    unsigned int cur_tls_port;
137
    bool prev_heur_enable;
138
    bool cur_heur_enable;
139
    bool prefer_tls; // enable TLS
140
    bool varval_tree; // enable displaying var=val as a tree
141
    bool time_utc; // display time fields in UTC rather than local time zone
142
    bool clear_info; // clear the INFO col and append count, funcs; else append count
143
    bool show_nul; // show the terminating NUL of a variable
144
} p4prefs_t;
145
146
/*
147
 * Our global preferences
148
 */
149
static p4prefs_t p4prefs = {
150
    P4RPC_TCP_PORT, // prev tcp port
151
    P4RPC_TCP_PORT, // cur tcp port
152
    P4RPC_TLS_PORT, // prev tls port
153
    P4RPC_TLS_PORT, // cur tls port
154
    true, // prev heur enable
155
    true, // cur heur enable
156
    true, // prefer_tls
157
    true, // varval_tree
158
    true, // time_utc
159
    true, // clear_info
160
    false // show_nul
161
};
162
163
static char *decimal_point = ".";
164
165
static int proto_p4rpc;         // main tree proto
166
static int ett_p4rpc;           // first registered subtree
167
static int ett_argtree;         // second registered subtree
168
static int ett_haverec;         // third registered subtree
169
static int ett_sync_time;       // fourth registered subtree
170
171
// header indices
172
static int hf_p4rpc_checksum_len;
173
static int hf_p4rpc_len;
174
static int hf_p4rpc_varbytes;
175
176
// name=value subtree indices
177
static int hf_p4rpc_varname;
178
static int hf_p4rpc_varvallen;
179
static int hf_p4rpc_varval;
180
static int hf_p4rpc_sync_time;
181
static int hf_p4rpc_have_client_path;
182
static int hf_p4rpc_have_depot_path;
183
static int hf_p4rpc_have_file_rev;
184
static int hf_p4rpc_have_file_type;
185
static int hf_p4rpc_have_file_datetime;
186
static int hf_p4rpc_func; // func value
187
static int hf_p4rpc_handle; // handle value
188
static int hf_p4rpc_action; // action value
189
static int hf_p4rpc_confirm; // confirm value
190
static int hf_p4rpc_num_msgs; // num msgs in packet
191
static int hf_p4rpc_nul; // NUL terminator byte after param value
192
193
// expert fields (for packet warnings, errors, etc)
194
static expert_field ei_p4rpc_msg_short;
195
static expert_field ei_p4rpc_msg_len_cksum;
196
static expert_field ei_p4rpc_msg_len;
197
static expert_field ei_p4rpc_msg_val_len;
198
static expert_field ei_p4rpc_msg_val_nul;
199
static expert_field ei_p4rpc_timestr_non_numeric;
200
201
void proto_register_p4rpc(void);
202
void proto_reg_handoff_p4rpc(void);
203
204
/*
205
 * Time field number of displayed digits after decimal point (max 9)
206
 * All time fields are integers, so we set TIME_PREC to 0
207
 */
208
0
#define TIME_PREC 0
209
210
// used for calculating the checksum of the message length field
211
static uint8_t
212
calc_checksum( uint32_t val )
213
503
{
214
503
    return ( (val & 0xFF) ^ ((val >> 8) & 0xFF) ^ ((val >> 16) & 0xFF) ^ ((val >> 24) & 0xFF) );
215
503
}
216
217
/*
218
 * Generate an ISO-8601 datetime string to append to the field representation.
219
 * - Fill "obuf" with the ISO-8601 datetime representation of "secs"
220
 *   with " (" prepended and ")" appended
221
 * - "secs" is the number of seconds since the epoch
222
 * - "secs" is an integer, so "obuf" will not contain a fractional part
223
 * - "len" must be the size of "obuf" and must be at least NSTIME_ISO8601_BUFSIZE+4
224
 */
225
static bool
226
p4rpc_secs_to_8601_str( char *obuf, unsigned int len, uint64_t secs, int precision )
227
0
{
228
0
    if( len < NSTIME_ISO8601_BUFSIZE+4 ) {
229
0
        obuf[0] = '\0'; // ensure it's terminated
230
231
0
        return false;
232
0
    }
233
234
0
    nstime_t when = { secs, 0 };
235
236
0
    obuf[0] = ' ';
237
0
    obuf[1] = '(';
238
239
0
    format_nstime_as_iso8601( obuf+2, NSTIME_ISO8601_BUFSIZE, &when,
240
0
        decimal_point, !p4prefs.time_utc, precision );
241
242
0
    (void) g_strlcat( obuf, ")", NSTIME_ISO8601_BUFSIZE+4 ); // close our parens
243
244
0
    return true;
245
0
}
246
247
/*
248
 * Generate an ISO-8601 datetime string to append to the field representation.
249
 * - Fill "obuf" with the ISO-8601 datetime representation of "secs"
250
 *   with " (" prepended and ")" appended
251
 * - "ibuf" is the ASCII string representation of the seconds since the epoch
252
 * - "obuf" will not contain a fractional part
253
 * - "len" must be the size of "obuf" and must be at least NSTIME_ISO8601_BUFSIZE+4
254
 * - "secs" will be set to the integer value of the "ibuf" string
255
 * - return true on success, false on error
256
 */
257
static bool
258
p4rpc_secs_str_to_8601_str( char *obuf, unsigned int len, const char *ibuf,
259
    bool *non_numeric, uint64_t *secs, int precision )
260
0
{
261
0
    obuf[0] = '\0'; // ensure it's terminated
262
0
    *secs = 0;
263
264
0
    nstime_t nst;
265
0
    const char *endptr = NULL;
266
267
0
    if( (endptr = unix_epoch_to_nstime(&nst, ibuf)) ) {
268
0
        if( non_numeric && *endptr )
269
0
            *non_numeric = true;
270
271
0
        *secs = nst.secs;
272
0
        return p4rpc_secs_to_8601_str( obuf, len, nst.secs, precision );
273
0
    }
274
275
0
    return false;
276
0
}
277
278
/*
279
 * Special handling for the "haveRec" field value
280
 * because it's a compound structure.
281
 *
282
 * Display a "haveRec" structure as a subtree with a subitem
283
 * for each field of the structure.
284
 *
285
 * Append ISO-8601 datetime strings to the numeric display.
286
 */
287
static void
288
p4rpc_decode_haverec( packet_info *pinfo, tvbuff_t *tvb, proto_item *parent_tree,
289
    uint32_t offset, uint32_t recsize )
290
0
{
291
0
    uint32_t cur_offset = offset;
292
293
    /*
294
     * Format of the haveRec field:
295
     *
296
     * struct haveRec {
297
     *     char clientPath[];
298
     *     char depotPath[];
299
     *     uint32_t rev;
300
     *     uint32_t type;
301
     *     uint32_t date; // sometimes a uint64_t
302
     * };
303
     */
304
305
    /*
306
     * add a new item item to our sub-subtree
307
     * and attach a new "value" sub-subtree to it
308
     */
309
0
    proto_item *have_tree = proto_item_add_subtree( parent_tree, ett_haverec );
310
311
    // add the client path to our sub-sub-subtree
312
0
    uint32_t client_len;
313
314
0
    tvb_get_stringz_enc( pinfo->pool, tvb, cur_offset, &client_len, ENC_UTF_8 );
315
0
    proto_tree_add_item( have_tree, hf_p4rpc_have_client_path, tvb, cur_offset,
316
0
        client_len, ENC_UTF_8 );
317
0
    cur_offset += client_len;
318
319
    // add the depot path to our sub-sub-subtree
320
0
    uint32_t depot_len;
321
322
0
    tvb_get_stringz_enc( pinfo->pool, tvb, cur_offset, &depot_len, ENC_UTF_8 );
323
0
    proto_tree_add_item( have_tree, hf_p4rpc_have_depot_path, tvb, cur_offset,
324
0
        depot_len, ENC_UTF_8 );
325
0
    cur_offset += depot_len;
326
327
    // add the file rev number to our sub-sub-subtree
328
0
    proto_tree_add_item( have_tree, hf_p4rpc_have_file_rev, tvb, cur_offset,
329
0
        4, ENC_LITTLE_ENDIAN );
330
0
    cur_offset += 4;
331
332
    // add the file type to our sub-sub-subtree
333
0
    proto_tree_add_item( have_tree, hf_p4rpc_have_file_type, tvb, cur_offset,
334
0
        4, ENC_LITTLE_ENDIAN );
335
0
    cur_offset += 4;
336
337
    /*
338
     * Add the file datetime to our sub-sub-subtree.
339
     * If there are 8 bytes remaining before the NUL terminator
340
     * then all 8 are the 64-bit datetime;
341
     * otherwise the next 4 bytes are the 32-bit datetime.
342
     *
343
     * Datetimes up to and including 03:14:07 UTC on 19 January 2038
344
     * will use 4 bytes, and datetimes after that will use 8 bytes.
345
     *
346
     * A datetime of 0 means that a datetime was not set.
347
     */
348
349
0
    unsigned int datetime_len = 4;
350
0
    uint64_t timestamp = 0;
351
352
0
    if( cur_offset - offset + 8 <= recsize ) {
353
        // 64-bit timestamp
354
0
        timestamp = tvb_get_letoh64( tvb, cur_offset );
355
0
        datetime_len = 8;
356
0
    } else {
357
        // 32-bit timestamp
358
0
        timestamp = tvb_get_letohl( tvb, cur_offset );
359
0
    }
360
361
0
    proto_item *date_item = proto_tree_add_item( have_tree,
362
0
        hf_p4rpc_have_file_datetime, tvb, cur_offset,
363
0
        datetime_len, ENC_LITTLE_ENDIAN );
364
365
0
    if( timestamp ) {
366
0
        char buf[NSTIME_ISO8601_BUFSIZE+4];
367
368
0
        p4rpc_secs_to_8601_str( buf, sizeof(buf), timestamp, TIME_PREC );
369
0
        proto_item_append_text( date_item, "%s", buf );
370
0
    } else {
371
        // Annotate that a datetime was not set
372
0
        proto_item_append_text( date_item, " (not set)" );
373
0
    }
374
0
}
375
376
/*
377
 * Display a time field (decimal string) as an ISO-8601 string.
378
 * - Special-case "syncTime" to allow filtering as an integer.
379
 * - Used only when "var=val" is a subtree, because for "syncTime"
380
 *   it will append text to that subtree.
381
 * - Used for time fields that are decimal strings rather than
382
 *   32- or 64-bit integer fields.
383
 */
384
static void
385
p4rpc_decode_time_str( tvbuff_t *tvb, proto_item *parent_tree,
386
    proto_item *item, const uint8_t *varname, const uint8_t *varval,
387
    bool *non_numeric, uint32_t vallen, uint32_t val_offset )
388
0
{
389
    /*
390
     * All params whose name ends in "Time" have values
391
     * that are a numeric string of seconds since the epoch.
392
     * Append the formatted datetime to the value field
393
     * if the value is set (ie, non-zero).
394
     */
395
0
    bool is_sync_time = strcmp((const char *)varname, "syncTime") == 0;
396
397
0
    char buf[NSTIME_ISO8601_BUFSIZE+4];
398
0
    uint64_t secs = 0;
399
400
0
    p4rpc_secs_str_to_8601_str( buf, sizeof(buf),
401
0
            (const char *)varval, non_numeric, &secs, TIME_PREC );
402
403
0
    if( secs ) {
404
0
        proto_item_append_text( item, "%s", buf );
405
0
        proto_item_append_text( parent_tree, "%s", buf );
406
0
    } else {
407
        // Annotate that a datetime wasn't set
408
0
        proto_item_append_text( item, " (not set)" );
409
0
        proto_item_append_text( parent_tree, " (not set)" );
410
0
    }
411
412
    /*
413
     * As a special case, allow filtering on "syncTime".
414
     *
415
     * We might want to consider allowing filtering
416
     * on other timestamp fields.
417
     *
418
     * These field values are decimal strings, so comparing
419
     * them except for something other than equality or inequality
420
     * requires creating a separate numeric field for each one,
421
     * as we have done here for "syncTime".
422
     */
423
0
    if( is_sync_time ) {
424
        // add the syncTime integer field
425
0
        proto_item *sync_item_val =
426
0
            proto_tree_add_uint64_format_value( parent_tree,
427
0
                hf_p4rpc_sync_time, tvb, val_offset, vallen,
428
0
                secs, "%" PRIu64, secs );
429
430
        /*
431
         * Hide the syncTime integer field because we already show
432
         * the time string and just want to allow filtering by its
433
         * value as an integer.
434
         */
435
0
        proto_item_set_hidden( sync_item_val );
436
0
    }
437
0
}
438
439
/*
440
 * Display a time field (decimal string) as an ISO-8601 string.
441
 * - Used only when "var=val" is a leaf node, not a subtree
442
 * - Used for time fields that are decimal strings rather than
443
 *   32- or 64-bit integer fields.
444
 * - Appends time string or "(not set)" to the leaf node.
445
 */
446
static void
447
item_decode_time_str( proto_item *tree, const uint8_t *varval, bool *non_numeric )
448
0
{
449
0
    char buf[NSTIME_ISO8601_BUFSIZE+4];
450
0
    uint64_t secs = 0;
451
452
0
    p4rpc_secs_str_to_8601_str( buf, sizeof(buf),
453
0
        (const char *)varval, non_numeric, &secs, TIME_PREC );
454
455
0
    if( secs ) {
456
0
        proto_item_append_text( tree, "%s", buf );
457
0
    } else {
458
        // Annotate a datetime that wasn't set
459
0
        proto_item_append_text( tree, " (not set)" );
460
0
    }
461
0
}
462
463
/*
464
 * dissect a single P4RPC message (request or response)
465
 *
466
 * Format of a P4RPC message:
467
 *      offset  len (always LE)         description
468
 *      ------  ---------------         -----------
469
 *      0       1 byte                  len XOR [see calc_checksum()]
470
 *      1       4 bytes                 len (uint32_t)
471
 *      5       N parameters
472
 *      Each param:
473
 *      *       M bytes                 ASCIIZ param name
474
 *              1 byte                  NUL byte (the Z in ASCIIZ)
475
 *              4 bytes                 length of the data (uint32_t)
476
 *              data_len bytes          data (value)
477
 *              1 byte                  NUL
478
 */
479
static uint32_t
480
dissect_one_p4rpc_message( tvbuff_t *tvb, uint32_t offset, uint32_t *seqno _U_,
481
    packet_info *pinfo, proto_tree *tree, unsigned int msg_idx, void *data _U_ )
482
350
{
483
350
    uint32_t msg_start = offset;
484
485
    // add an item to the parent tree and attach a subtree to it
486
350
    proto_item *ti = proto_tree_add_item( tree, proto_p4rpc, tvb, 0, -1, ENC_NA );
487
350
    proto_item *ti_tree = proto_item_add_subtree( ti, ett_p4rpc );
488
489
    // we need at least MSG_HEADER_LEN bytes to find the message length
490
350
    if ( !tvb_bytes_exist(tvb, offset, MSG_HEADER_LEN) ) {
491
19
        proto_tree_add_expert_format( ti_tree, pinfo, &ei_p4rpc_msg_short, tvb,
492
19
            0, MSG_HEADER_LEN,
493
19
            "Invalid message: Fewer than %d bytes available", MSG_HEADER_LEN );
494
19
    }
495
496
    // add the checksum of the length field as an item to our subtree
497
350
    uint8_t cksum_of_len = tvb_get_uint8( tvb, offset );
498
350
    proto_tree_add_item( ti_tree, hf_p4rpc_checksum_len, tvb, offset, 1, ENC_NA );
499
350
    offset++;
500
501
    // add the length field as an item to our subtree
502
350
    uint32_t msg_len = tvb_get_letohl( tvb, offset );
503
350
    proto_tree_add_item( ti_tree, hf_p4rpc_len, tvb, offset, 4, ENC_LITTLE_ENDIAN );
504
350
    offset += 4;
505
506
    // warn if the length checksum doesn't match the actual checksum
507
350
    if( cksum_of_len != calc_checksum(msg_len) ) {
508
90
        proto_tree_add_expert_format( ti_tree, pinfo, &ei_p4rpc_msg_len_cksum, tvb, 0, MSG_HEADER_LEN,
509
90
            "checksum mismatch: expected 0x%0X, got 0x%0X for length %d (0x%0X)",
510
90
            cksum_of_len, calc_checksum(msg_len), msg_len, msg_len );
511
90
    }
512
513
    // warn if the message length is out of bounds
514
350
    if ( msg_len < MSG_MIN_LEN || msg_len > MSG_MAX_LEN ) {
515
231
        proto_tree_add_expert_format( ti_tree, pinfo, &ei_p4rpc_msg_len, tvb, 1, 4,
516
231
            "Message length %d (0x%0X) not in range [%d, %d]", msg_len, msg_len,
517
231
            MSG_MIN_LEN, MSG_MAX_LEN );
518
231
    }
519
520
    // there's something wrong if msg len exceeds the captured data
521
350
    uint32_t tvb_len = tvb_captured_length( tvb );
522
350
    if( msg_len > tvb_len - MSG_HEADER_LEN ) {
523
71
        proto_tree_add_expert_format( ti_tree, pinfo, &ei_p4rpc_msg_len, tvb, 1, 4,
524
71
            "Message length %d (0x%0X) exceeds tvb length (%d)",
525
71
            msg_len, msg_len, tvb_len - MSG_HEADER_LEN );
526
527
        // don't try to dissect past the end of the captured data
528
71
        msg_len = tvb_len - MSG_HEADER_LEN;
529
71
    }
530
531
    // process each param {name, value}
532
874
    while( offset - msg_start < msg_len )
533
524
    {
534
        // the offset to the start of the name of the parameter
535
524
        uint32_t name_offset = offset;
536
524
        uint32_t name_len;
537
538
        // get the param name
539
524
        const uint8_t *varname = tvb_get_stringz_enc( pinfo->pool, tvb, offset, &name_len, ENC_UTF_8 );
540
524
        offset += name_len; // NB: name_len counts the NUL terminator byte
541
542
524
        uint32_t vallen_offset = offset; // the offset to the start of the value length
543
544
        // get the param value length
545
524
        uint32_t vallen = tvb_get_letohl( tvb, offset );
546
524
        offset += 4;
547
548
        // get the param value
549
524
        uint32_t val_offset = offset; // the offset to the start of the value
550
524
        uint8_t *varval = tvb_get_string_enc( pinfo->pool, tvb, offset, vallen, ENC_UTF_8 );
551
524
        int32_t tot_var_len = name_len + vallen + 4;
552
553
        // get the (expected) NUL byte
554
524
        uint32_t nul_offset = offset + vallen;
555
524
        uint8_t zero = tvb_get_uint8( tvb, nul_offset );
556
557
1.58k
#define ARGBUF_SZ       128
558
        // make our "var=val" string
559
524
        char *argbuf = wmem_alloc( pinfo->pool, ARGBUF_SZ );
560
524
        int bytes_written;
561
        // Pass in bufsz-1 to save space for closing bracket.
562
524
        bytes_written = snprintf( argbuf, ARGBUF_SZ-1, "%s = {%s",
563
524
            (*varname ? varname : (const uint8_t *)"<none>"), varval );
564
565
        /*
566
         * Returns number of bytes that would have been written
567
         * (not including the null terminator) if not for the
568
         * buffer size limit.
569
         */
570
524
        if( bytes_written > ARGBUF_SZ - 2 ) { // Possibly truncated
571
11
            ws_utf8_truncate( argbuf, ARGBUF_SZ-2 ); // ensure no partial char at the end
572
11
        }
573
524
        (void) g_strlcat( argbuf, "}", ARGBUF_SZ ); // close our bracket
574
575
        // used to check if a value length goes past the end of the message
576
524
        uint32_t bytes_left = msg_len - (offset - msg_start) + MSG_HEADER_LEN;
577
578
        // is our "var=val" item a tree or just text?
579
524
        if( p4prefs.varval_tree )
580
408
        {
581
            // add a new item item to our subtree and attach a new "var=value" sub-subtree to it
582
408
            proto_item *ti_arg = proto_tree_add_item( ti_tree, proto_p4rpc, tvb, name_offset, tot_var_len, ENC_NA );
583
408
            proto_item *arg_tree = proto_item_add_subtree( ti_arg, ett_argtree );
584
408
            proto_item_set_text( arg_tree, "%s", argbuf );
585
586
            // add the param name to our sub-subtree
587
408
            proto_tree_add_item( arg_tree, hf_p4rpc_varname, tvb, name_offset, name_len, ENC_UTF_8 );
588
589
            // add the length of the param value to our sub-subtree
590
408
            proto_tree_add_item( arg_tree, hf_p4rpc_varvallen, tvb, vallen_offset, 4, ENC_LITTLE_ENDIAN );
591
592
            // add the param value to our sub-subtree
593
408
            proto_item *vv_rec = proto_tree_add_item( arg_tree, hf_p4rpc_varval, tvb, val_offset, vallen, ENC_NA );
594
595
            /*
596
             * Special handling for the compound structure fields:
597
             *   haveRec
598
             * TODO:
599
             * - Decode the other "*Rec" structure fields.
600
             *
601
             * Special handling for time fields -- append ISO-8601 datetime
602
             * strings to the numeric display:
603
             *   depotTime
604
             *   headModTime
605
             *   headTime
606
             *   maxLockTime
607
             *   maxPauseTime
608
             *   theirTime
609
             */
610
408
            if( strcmp((const char *)varname, "haveRec") == 0 ) {
611
0
                p4rpc_decode_haverec( pinfo, tvb, arg_tree, val_offset, vallen );
612
408
            } else if( g_str_has_suffix((const char *)varname, "Time") ) {
613
0
                bool non_numeric = false;
614
615
0
                p4rpc_decode_time_str( tvb, arg_tree, vv_rec, varname,
616
0
                    varval, &non_numeric, vallen, val_offset );
617
618
                // report non-numeric char in time string
619
0
                if( non_numeric )
620
0
                {
621
0
                    proto_tree_add_expert_format( arg_tree, pinfo,
622
0
                        &ei_p4rpc_timestr_non_numeric, tvb, val_offset, vallen,
623
0
                        "Time string contains non-numeric character" );
624
0
                }
625
0
            }
626
627
            // decode the nul terminator
628
408
            proto_item *nul_item =
629
408
                proto_tree_add_item( arg_tree, hf_p4rpc_nul, tvb, nul_offset, 1, ENC_NA );
630
408
            if( !p4prefs.show_nul )
631
408
                proto_item_set_hidden( nul_item );
632
633
408
            if( zero )
634
34
            {
635
                // add a new subtree to the value field for the expert info
636
34
                proto_item *expert_tree = proto_item_add_subtree( arg_tree, ett_argtree );
637
34
                proto_tree_add_expert_format( expert_tree, pinfo, &ei_p4rpc_msg_val_nul,
638
34
                    tvb, nul_offset, 1,
639
34
                    "Terminating NUL byte is 0x%0X instead of 0x0",
640
34
                    zero );
641
34
            }
642
643
            // does the value go past the end of the message?
644
408
            if( vallen > bytes_left )
645
0
            {
646
0
                proto_tree_add_expert_format( arg_tree, pinfo, &ei_p4rpc_msg_val_len,
647
0
                    tvb, vallen_offset, 4,
648
0
                    "Invalid value: length %d > %d bytes remaining in message",
649
0
                    vallen, bytes_left );
650
651
                // don't try to read past the end
652
0
                vallen = bytes_left;
653
0
            }
654
408
        } else {
655
            // add "var=value" to be a leaf item to our subtree
656
116
            proto_item *arg_tree =
657
116
                proto_tree_add_bytes_format( ti_tree, hf_p4rpc_varbytes, tvb,
658
116
                    name_offset, tot_var_len, NULL, "%s", argbuf );
659
660
            /*
661
             * All params whose name ends in "Time" have values
662
             * that are a numeric string of seconds since the epoch.
663
             * Append the formatted datetime to the value field
664
             * if the value is set (ie, non-zero).
665
             */
666
116
            if( g_str_has_suffix((const char *)varname, "Time") ) {
667
0
                bool non_numeric = false;
668
669
0
                item_decode_time_str( arg_tree, varval, &non_numeric );
670
671
                // report non-numeric char in time string
672
0
                if( non_numeric )
673
0
                {
674
0
                    proto_tree_add_expert_format( ti_tree, pinfo,
675
0
                        &ei_p4rpc_timestr_non_numeric, tvb, val_offset,
676
0
                        vallen,
677
0
                        "Time string contains non-numeric character" );
678
0
                }
679
0
            }
680
681
            // decode the nul terminator
682
116
            proto_item *nul_item =
683
116
                proto_tree_add_item( ti_tree, hf_p4rpc_nul, tvb, nul_offset, 1, ENC_NA );
684
116
            if( !p4prefs.show_nul )
685
0
                proto_item_set_hidden( nul_item );
686
687
            // report incorrect NUL terminator
688
116
            if( zero )
689
0
            {
690
0
                proto_tree_add_expert_format( ti_tree, pinfo, &ei_p4rpc_msg_val_nul,
691
0
                    tvb, nul_offset, 1,
692
0
                    "Invalid terminating NUL byte is 0x%0X instead of 0x0",
693
0
                    zero );
694
0
            }
695
696
            // does the value go past the end of the message?
697
116
            if( vallen > bytes_left )
698
0
            {
699
0
                proto_tree_add_expert_format( ti_tree, pinfo, &ei_p4rpc_msg_val_len,
700
0
                    tvb, vallen_offset, 4,
701
0
                    "Invalid value: var \"%s\" length %d > %d bytes remaining in message",
702
0
                    varname, vallen, bytes_left );
703
704
                // don't try to read past the end
705
0
                vallen = bytes_left;
706
0
            }
707
116
        } // var=val subtree
708
709
        /*
710
         * special "alias" handling for "func" and "handle" params, etc.
711
         */
712
713
524
        if( strcmp((char *)varname, "func") == 0 )
714
0
        {
715
0
            if( p4prefs.clear_info ) {
716
                // append the current func name to the INFO column
717
0
                col_append_fstr( pinfo->cinfo, COL_INFO,
718
0
                    (msg_idx == 0) ? "%s" : " | %s", varval );
719
0
            }
720
721
            // add the "func" name to the subtree (not sub-subtree) text
722
0
            proto_item_append_text( ti_tree, " : [%s] [len=%d]",
723
0
                varval, offset+vallen+1 );
724
725
            /*
726
             * Special case "func" and "handle" for convenience,
727
             * so that a filter can be written as:
728
             *  p4rpc.func == "sync"
729
             * rather than as:
730
             *  p4rpc.var.name == "func" && p4rpc.var.val == "sync"
731
             *
732
             * Allow filtering by the value of the "func" param.
733
             * Don't show it because the "func" is already shown.
734
             */
735
0
            proto_item *func_item = proto_tree_add_item(
736
0
                ti_tree, hf_p4rpc_func, tvb, val_offset, vallen, ENC_UTF_8);
737
0
            proto_item_set_hidden( func_item );
738
739
0
        }
740
741
524
        if( strcmp((char *)varname, "handle") == 0 )
742
0
        {
743
            /*
744
             * Special case "func" and "handle" for convenience,
745
             * so that a filter can be written as:
746
             *  p4rpc.handle == "sync"
747
             * rather than as:
748
             *  p4rpc.var.name == "handle" && p4rpc.var.val == "sync"
749
             * Allow filtering by the value of the "handle" param.
750
             * Don't show it because the "handle" is already shown.
751
             */
752
0
            proto_item *handle_item = proto_tree_add_item(
753
0
                ti_tree, hf_p4rpc_handle, tvb, val_offset, vallen, ENC_UTF_8 );
754
0
            proto_item_set_hidden( handle_item );
755
0
        }
756
757
524
        if( strcmp((char *)varname, "action") == 0 )
758
0
        {
759
0
            proto_item *handle_item = proto_tree_add_item(
760
0
                ti_tree, hf_p4rpc_action, tvb, val_offset, vallen, ENC_UTF_8 );
761
0
            proto_item_set_hidden( handle_item );
762
0
        }
763
764
524
        if( strcmp((char *)varname, "confirm") == 0 )
765
1
        {
766
1
            proto_item *handle_item = proto_tree_add_item(
767
1
                ti_tree, hf_p4rpc_confirm, tvb, val_offset, vallen, ENC_UTF_8 );
768
1
            proto_item_set_hidden( handle_item );
769
1
        }
770
771
524
        offset += vallen + 1;   // + 1 for the NUL data terminator
772
524
    }
773
774
350
    return offset;
775
350
}
776
777
/*
778
 * This method dissects a fully reassembled PDU
779
 * - it will contain one complete P4RPC message
780
 */
781
static int
782
dissect_p4rpc_pdu( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data )
783
325
{
784
325
    uint32_t offset = 0;
785
325
    uint32_t tvb_len = tvb_captured_length( tvb );
786
325
    uint32_t seqno = 0xFFFF;
787
325
    pdu_info *info = (pdu_info *) data;
788
789
325
    ++info->num_pdus; // we get called once per pdu
790
791
    /*
792
     * Process every message in this PDU
793
     * (but a PDU is exactly one message
794
     * so we pass through the loop exactly once).
795
     */
796
675
    while( offset < tvb_len )
797
350
    {
798
350
        offset = dissect_one_p4rpc_message( tvb, offset, &seqno, pinfo, tree, info->num_msgs++, data );
799
350
    }
800
801
325
    return offset;
802
325
}
803
804
/*
805
 * determine PDU length of protocol p4rpc
806
 */
807
static unsigned int
808
get_p4rpc_pdu_len( packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_ )
809
326
{
810
326
    return tvb_get_letohl(tvb, offset+1) + MSG_HEADER_LEN;
811
326
}
812
813
/*
814
 * The main dissecting routine:
815
 * - A P4RPC Protocol Data Unit (PDU) consists of one P4RPC message.
816
 * - A single (large) PDU may be split into multiple packets.
817
 *   In this case Wireshark will dissect all but the last
818
 *   of the component packets as TCP, adding "[TCP PDU reassembled in NN]"
819
 *   to the INFO field of each packet (where NN is the number of the final
820
 *   packet in the PDU), and dissect the final packet of the PDU
821
 *   as the concatenation of all of the component packets.
822
 *
823
 * - "data" is a pointer to a pdu_info that is a count
824
 *   of the numbers of messages and PDUs that have been dissected.
825
 *   Because there is exactly one message in a PDU, these counts
826
 *   are always equal, but if the protocol ever allows bundling
827
 *   multiple messages in a PDU then the counts can differ.
828
 */
829
static int
830
dissect_p4rpc( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_ )
831
66
{
832
66
    pdu_info    info = { 0, 0 };
833
834
66
    col_set_str( pinfo->cinfo, COL_PROTOCOL, "P4RPC" );
835
836
    // clear the INFO column if the user requested it
837
66
    if( p4prefs.clear_info )
838
66
        col_clear( pinfo->cinfo, COL_INFO );
839
840
    // group all of the messages in this set of frames
841
66
    col_append_str( pinfo->cinfo, COL_INFO, " {" );
842
    // reassemble TCP packets and call dissect_p4rpc_pdu on each pdu
843
66
    tcp_dissect_pdus( tvb, pinfo, tree, true, MSG_HEADER_LEN,
844
66
                     get_p4rpc_pdu_len, dissect_p4rpc_pdu, &info );
845
66
    col_append_str( pinfo->cinfo, COL_INFO, "}" );
846
847
    /*
848
     * Prefix {msgs=NN} to the INFO of the packet tree where NN
849
     * is the number of messages in the packet.
850
     * Note: If there are multiple sets of messages then the message
851
     * counts will be in reverse order.
852
     */
853
66
    col_prepend_fstr( pinfo->cinfo, COL_INFO,
854
66
        (p4prefs.clear_info ? "[msgs=%d]" : "[msgs=%d] "), info.num_msgs );
855
856
    /*
857
     * Similar to "func" and "hidden", allow filtering by number
858
     * of messages in a packet.
859
     * Hidden because we already show it in COL_INFO.
860
     */
861
66
    proto_item *num_msgs_item = proto_tree_add_uint(
862
66
        tree, hf_p4rpc_num_msgs, tvb, 0, 0, info.num_msgs );
863
66
    proto_item_set_hidden( num_msgs_item );
864
865
66
    return tvb_captured_length( tvb );
866
66
}
867
868
/*
869
 * Called to dissect a decrypted tvb, so just call
870
 * our p4rpc dissector.
871
 */
872
static int
873
dissect_tls_p4rpc( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data )
874
0
{
875
0
    return dissect_p4rpc( tvb, pinfo, tree, data );
876
0
}
877
878
/*
879
 * Return true if this is a P4RPC packet
880
 * else return false so that another dissector can try.
881
 * - if the checksum matches and length is reasonable
882
 *   then assume that it is but that's not very definitive.
883
 * Don't enable USE_DEBUG unless you want to see a lot of error messages,
884
 * because non-P4RPC packets are very likely going to emit one.
885
 */
886
static bool
887
p4rpc_check_heur( packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_ )
888
2.33k
{
889
2.33k
    uint8_t cksum_of_len = 0;
890
2.33k
    uint32_t msg_len = 0;
891
892
    // if we don't have at least our header, it isn't P4RPC
893
2.33k
    if( !tvb_bytes_exist(tvb, offset, MSG_HEADER_LEN) ) {
894
157
        return false;
895
157
    }
896
897
    // fetch the checksum from the tvb
898
2.17k
    cksum_of_len = tvb_get_uint8(tvb, offset);
899
900
    // not P4RPC if the message is too small or too big
901
2.17k
    msg_len = tvb_get_letohl(tvb, offset+1);
902
903
2.17k
    if( msg_len < MSG_MIN_LEN || msg_len > MSG_MAX_LEN)  {
904
1.42k
        return false;
905
1.42k
    }
906
907
    // not P4RPC if the message length exceeds the captured data
908
752
    uint32_t tvb_len = tvb_captured_length( tvb );
909
752
    if( msg_len > tvb_len - MSG_HEADER_LEN ) {
910
670
        return false;
911
670
    }
912
913
    // not P4RPC if the checksum is wrong
914
82
    if( cksum_of_len != calc_checksum(msg_len) ) {
915
36
        return false;
916
36
    }
917
918
46
    return true;
919
82
}
920
921
/*
922
 * Check to see if this packet is P4RPC and dissect it if so,
923
 * else return false and let another dissector try.
924
 */
925
static bool
926
dissect_p4rpc_heur( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data )
927
2.33k
{
928
2.33k
    if( !p4rpc_check_heur(pinfo, tvb, 0, data) )
929
2.28k
        return false;
930
931
    /*
932
     * Assume that this is a P4RPC packet and set p4rpc as the dissector
933
     * for the rest of the packets in this conversation.
934
     */
935
46
    conversation_t *conversation = find_or_create_conversation( pinfo );
936
46
    if( conversation )
937
46
        conversation_set_dissector( conversation, p4rpc_handle );
938
939
    // go ahead and dissect it
940
46
    int rslt = dissect_p4rpc( tvb, pinfo, tree, NULL );
941
942
46
    return rslt > 0 ? true : false;
943
2.33k
}
944
945
/*
946
 * proto_reg_handoff_p4rpc() calls this with isInit == true
947
 * - set up initially, ignoring the p4prefs.pref_* members
948
 * prefs_cb() calls this with isInit == false
949
 * - change only the ports that have changes
950
 */
951
static void
952
p4rpc_setup_ports( bool isInit )
953
16
{
954
16
    bool tcp_port_changed = p4prefs.cur_tcp_port != p4prefs.prev_tcp_port;
955
16
    bool tls_port_changed = p4prefs.cur_tls_port != p4prefs.prev_tls_port;
956
16
    bool heur_changed = p4prefs.cur_heur_enable != p4prefs.prev_heur_enable;
957
958
    /*
959
     * Claim tcp.port for P4RPC if:
960
     * - we don't prefer TLS, and
961
     *   we're initializing or the tls port has changes
962
     */
963
16
    if( !p4prefs.prefer_tls && (isInit || tcp_port_changed) )
964
0
    {
965
0
        if( !isInit )
966
0
            dissector_delete_uint( "tcp.port", p4prefs.prev_tcp_port, p4rpc_handle );
967
0
        dissector_add_uint( "tcp.port", p4prefs.cur_tcp_port, p4rpc_handle );
968
0
    }
969
970
    /*
971
     * Allow P4RPC to grab our TLS port (after decryption) by default
972
     * - if we prefer TLS, and
973
     *   we're initializing or the tls port has changes
974
     */
975
16
    if( p4prefs.prefer_tls && (isInit || tls_port_changed) )
976
16
    {
977
16
        if( !isInit )
978
0
            dissector_delete_uint( "tls.port", p4prefs.prev_tls_port, p4rpc_handle );
979
16
        dissector_add_uint( "tls.port", p4prefs.cur_tls_port, p4rpc_handle );
980
16
    }
981
982
    /*
983
     * Add or delete our TCP heuristic packet finder if TLS is lower priority.
984
     */
985
16
    if( isInit || heur_changed )
986
16
    {
987
        /*
988
         * We don't delete the heuristic dissector during initialization
989
         * because none could have been registered already.
990
         *
991
         * After initialization it's still possible that no heuristic
992
         * dissector was registered.
993
         * Rather than track whether or not one has already been registered
994
         * we always delete it, because heur_dissector_delete() is a no-op
995
         * if the dissector isn't registered.
996
         */
997
16
        if( !isInit )
998
0
            heur_dissector_delete( "tcp", dissect_p4rpc_heur, proto_p4rpc );
999
1000
16
        if( p4prefs.cur_heur_enable )
1001
16
        {
1002
16
            heur_dissector_add( "tcp", dissect_p4rpc_heur, "P4RPC over TCP", "p4rpc_tcp", proto_p4rpc, HEURISTIC_ENABLE );
1003
16
        }
1004
16
    }
1005
1006
    /*
1007
     * remember our current settings
1008
     */
1009
1010
16
    if( tcp_port_changed )
1011
0
        p4prefs.prev_tcp_port = p4prefs.cur_tcp_port;
1012
1013
16
    if( tls_port_changed )
1014
0
        p4prefs.prev_tls_port = p4prefs.cur_tls_port;
1015
1016
16
    if( heur_changed )
1017
0
        p4prefs.prev_heur_enable = p4prefs.cur_heur_enable;
1018
16
}
1019
1020
/*
1021
 * Called after the user has changed preferences.
1022
 * We validate the settings, alert the user if appropriate,
1023
 * and apply those that have changed.
1024
 */
1025
static void
1026
prefs_cb(void)
1027
0
{
1028
    /*
1029
     * Warn against making both ports the same
1030
     * because the P4RPC dissector may take
1031
     * precedence over the TLS dissector
1032
     */
1033
0
    if( p4prefs.prefer_tls && (p4prefs.cur_tcp_port == p4prefs.cur_tls_port) )
1034
0
    {
1035
0
        report_warning( "You have set both the P4RPC TCP and TLS ports to %d.\n"
1036
0
            "If this prevents Wireshark from decoding both the TLS and"
1037
0
            " the P4RPC layers then set the TLS port to the server port"
1038
0
            " and set the TCP port to any other port.\n"
1039
0
            "\nDisabling \"Decrypt TLS before P4RPC\" might help if TLS"
1040
0
            " grabs the port first and blocks P4RPC from decoding packets.",
1041
0
            p4prefs.cur_tcp_port );
1042
0
    }
1043
1044
0
    p4rpc_setup_ports( false );
1045
0
}
1046
1047
/*
1048
 * Define our preferences pane and hook in our callback routine (above).
1049
 */
1050
static void
1051
register_prefs(void)
1052
16
{
1053
    // register our callback func
1054
16
    module_t *prefs_mod = prefs_register_protocol( proto_p4rpc, prefs_cb );
1055
1056
    // tcp.port
1057
16
    prefs_register_uint_preference( prefs_mod, "tcp.port",
1058
16
        "TCP port",
1059
16
        "Default P4RPC-over-TCP port.",
1060
16
        10, // decimal
1061
16
        &p4prefs.cur_tcp_port );
1062
1063
    // tls.port
1064
16
    prefs_register_uint_preference( prefs_mod, "tls.port",
1065
16
        "TLS port",
1066
16
        "Default P4RPC-over-TLS port (should not be same as \"TCP port\" if \"TLS priority\" is disabled).",
1067
16
        10, // decimal
1068
16
        &p4prefs.cur_tls_port );
1069
1070
    // tls preference
1071
16
    prefs_register_bool_preference( prefs_mod, "prefer_tls",
1072
16
        "Decrypt TLS before P4RPC",
1073
16
        "Always decrypt packets before P4RPC tries to dissect them.\n\n"
1074
16
        "- Enabled: If traffic is TLS then then it will be decrypted and then decoded as P4RPC.\n"
1075
16
        "- Disabled: Traffic on \"TCP port\" will not be decrypted even if it is TLS.\n"
1076
16
        "\nRelevant only when \"TCP port\" is the same as \"TLS port\", "
1077
16
        "but normally should be enabled.",
1078
16
        &p4prefs.prefer_tls );
1079
1080
    // tcp heuristics preference
1081
16
    prefs_register_bool_preference( prefs_mod, "tcp_heur",
1082
16
        "Enable P4RPC/TCP heuristic detection",
1083
16
        "Enable heuristic detection of P4RPC over TCP.\n"
1084
16
        "\nTry to guess at P4RPC packets on ports other than \"TCP port\".\n"
1085
16
        "\nEnable this if you frequently dissect P4RPC traffic on different ports,"
1086
16
        " so that you won't need to use \"Decode As...\" often.\n"
1087
16
        "\nP4RPC over TLS is always auto-detected when \"Decrypt TLS before P4RPC\" is enabled,"
1088
16
        " whether or not this option is enabled.",
1089
16
        &p4prefs.cur_heur_enable );
1090
1091
    // var=val tree preference
1092
16
    prefs_register_bool_preference( prefs_mod, "message_params_as_tree",
1093
16
        "Show message parameters as a \"var=value\" tree",
1094
16
        "Show message parameters as a \"var=value\" subtree with \"var\", "
1095
16
        " \"length\", and \"val\" children.\n"
1096
16
        "\nIt is highly recommended to enable this, because this is required"
1097
16
        " to enable filtering by parameter names and values (eg, p4rpc.var.*, etc)",
1098
16
        &p4prefs.varval_tree );
1099
1100
    // UTC vs local time field preference
1101
16
    prefs_register_bool_preference( prefs_mod, "time_fields_in_utc",
1102
16
        "Display time fields in UTC rather than in the local time zone",
1103
16
        "If set, display time fields in UTC rather than in the local time zone",
1104
16
        &p4prefs.time_utc );
1105
1106
    // Clear INFO field preference
1107
16
    prefs_register_bool_preference( prefs_mod, "clear_info",
1108
16
        "Clear the INFO column and show the message counts and the request names",
1109
16
        "If not set then just prepend the message counts",
1110
16
        &p4prefs.clear_info );
1111
1112
    // show_nul preference
1113
16
    prefs_register_bool_preference( prefs_mod, "show_nul",
1114
16
        "Show the NUL byte of var=val params in the protocol tree",
1115
16
        "If set, add a subtree item for the ending NUL byte of a message parameter",
1116
16
        &p4prefs.show_nul );
1117
1118
    /*
1119
     * Not real preferences, just some static text to display.
1120
     * Added a dummy one to separate the version string a little.
1121
     */
1122
16
    prefs_register_static_text_preference( prefs_mod, "", "", "" );
1123
16
    prefs_register_static_text_preference( prefs_mod, "version",
1124
16
        "P4RPC dissector version: " VERSION,
1125
16
        VERSION ); // this last arg doesn't seem to get displayed
1126
16
}
1127
1128
/*
1129
 * Register P4RPC dissector with its tree items and filter variables.
1130
 */
1131
void
1132
proto_register_p4rpc(void)
1133
16
{
1134
16
    static hf_register_info hf[] = {
1135
16
        {
1136
            // basic protocol fields
1137
16
            &hf_p4rpc_checksum_len,
1138
16
            {
1139
16
                "message cksum of len",
1140
16
                "p4rpc.len.cksum",
1141
16
                FT_UINT8,
1142
16
                BASE_HEX,
1143
16
                NULL,
1144
16
                0x0,
1145
16
                "Checksum of p4rpc.msg.len (XOR of the 4 length bytes)",
1146
16
                HFILL
1147
16
            }
1148
16
        },
1149
16
        {
1150
16
            &hf_p4rpc_len,
1151
16
            {
1152
16
                "message len",
1153
16
                "p4rpc.msg.len",
1154
16
                FT_UINT32,
1155
16
                BASE_DEC,
1156
16
                NULL,
1157
16
                0x0,
1158
16
                "Total number of bytes in this message",
1159
16
                HFILL
1160
16
            }
1161
16
        },
1162
16
        {
1163
            // comprises p4rpc.var.name, p4rpc.var.val.len, p4rpc.var.val
1164
16
            &hf_p4rpc_varbytes,
1165
16
            {
1166
16
                "param name=val", // placeholder
1167
16
                "p4rpc.var.name.val",
1168
16
                FT_BYTES,
1169
16
                BASE_NONE,
1170
16
                NULL,
1171
16
                0x0,
1172
16
                "Combined var name and value",
1173
16
                HFILL
1174
16
            },
1175
16
        },
1176
16
        {
1177
16
            &hf_p4rpc_nul, // p_id (int)
1178
16
            {                       // hfinfo (header_field_info)
1179
16
                "param NUL terminator", // name (const char *)
1180
16
                "p4rpc.var.nul",        // abbrev (const char *)
1181
16
                FT_UINT8,               // type (enum ftenum)
1182
16
                BASE_HEX,               // display (int)
1183
16
                NULL,                   // strings (const void *)
1184
16
                0x0,                    // bitmask (uint64_t)
1185
16
                "Parameter value NUL terminator byte", // blurb (const char *)
1186
16
                HFILL
1187
16
            }
1188
16
        },
1189
16
        {
1190
16
            &hf_p4rpc_func,
1191
16
            {
1192
16
                "param func",
1193
16
                "p4rpc.func",
1194
16
                FT_STRINGZ,
1195
16
                BASE_NONE,
1196
16
                NULL,
1197
16
                0x0,
1198
16
                "Name of the requested function [required]",
1199
16
                HFILL
1200
16
            }
1201
16
        },
1202
16
        {
1203
16
            &hf_p4rpc_handle, // p_id (int)
1204
16
            {                       // hfinfo (header_field_info)
1205
16
                "param handle",         // name (const char *)
1206
16
                "p4rpc.handle",         // abbrev (const char *)
1207
16
                FT_STRINGZ,             // type (enum ftenum)
1208
16
                BASE_NONE,              // display (int)
1209
16
                NULL,                   // strings (const void *)
1210
16
                0x0,                    // bitmask (uint64_t)
1211
16
                "Name of response function [optional]", // blurb (const char *)
1212
16
                HFILL
1213
16
            }
1214
16
        },
1215
16
        {
1216
16
            &hf_p4rpc_action, // p_id (int)
1217
16
            {                       // hfinfo (header_field_info)
1218
16
                "param action",         // name (const char *)
1219
16
                "p4rpc.action",         // abbrev (const char *)
1220
16
                FT_STRINGZ,             // type (enum ftenum)
1221
16
                BASE_NONE,              // display (int)
1222
16
                NULL,                   // strings (const void *)
1223
16
                0x0,                    // bitmask (uint64_t)
1224
16
                "Name of action function [optional]", // blurb (const char *)
1225
16
                HFILL
1226
16
            }
1227
16
        },
1228
16
        {
1229
16
            &hf_p4rpc_confirm, // p_id (int)
1230
16
            {                       // hfinfo (header_field_info)
1231
16
                "param confirm",        // name (const char *)
1232
16
                "p4rpc.confirm",        // abbrev (const char *)
1233
16
                FT_STRINGZ,             // type (enum ftenum)
1234
16
                BASE_NONE,              // display (int)
1235
16
                NULL,                   // strings (const void *)
1236
16
                0x0,                    // bitmask (uint64_t)
1237
16
                "Name of confirm function [optional]", // blurb (const char *)
1238
16
                HFILL
1239
16
            }
1240
16
        },
1241
16
        {
1242
16
            &hf_p4rpc_num_msgs, // p_id (int)
1243
16
            {                       // hfinfo (header_field_info)
1244
16
                "number of messages",   // name (const char *)
1245
16
                "p4rpc.num.msgs",       // abbrev (const char *)
1246
16
                FT_UINT32,              // type (enum ftenum)
1247
16
                BASE_DEC,               // display (int)
1248
16
                NULL,                   // strings (const void *)
1249
16
                0x0,                    // bitmask (uint64_t)
1250
16
                "Number of messages in this (reassembled) packet", // blurb (const char *)
1251
16
                HFILL
1252
16
            }
1253
16
        },
1254
1255
        // name=value subtree
1256
16
        {
1257
16
            &hf_p4rpc_varname,
1258
16
            {
1259
16
                "param name",
1260
16
                "p4rpc.var.name",
1261
16
                FT_STRINGZ,
1262
16
                BASE_NONE,
1263
16
                NULL,
1264
16
                0x0,
1265
16
                "Name of this parameter",
1266
16
                HFILL
1267
16
            }
1268
16
        },
1269
16
        {
1270
16
            &hf_p4rpc_varvallen,
1271
16
            {
1272
16
                "param value len",
1273
16
                "p4rpc.var.val.len",
1274
16
                FT_UINT32,
1275
16
                BASE_DEC,
1276
16
                NULL,
1277
16
                0x0,
1278
16
                "Length of the value of this parameter",
1279
16
                HFILL
1280
16
            }
1281
16
        },
1282
16
        {
1283
16
            &hf_p4rpc_varval,
1284
16
            {
1285
16
                "param value",
1286
16
                "p4rpc.var.val",
1287
16
                FT_STRINGZTRUNC, // really FT_BYTES, but starts with a FT_STRINGZ
1288
16
                BASE_NONE,
1289
16
                NULL,
1290
16
                0x0,
1291
16
                "Value of this parameter",
1292
16
                HFILL
1293
16
            }
1294
16
        },
1295
1296
        // syncTime item
1297
16
        {
1298
16
            &hf_p4rpc_sync_time,
1299
16
            {
1300
16
                "sync time",
1301
16
                "p4rpc.synctime",
1302
16
                FT_UINT64,
1303
16
                BASE_DEC,
1304
16
                NULL,
1305
16
                0x0,
1306
16
                "Integer value of syncTime param",
1307
16
                HFILL
1308
16
            }
1309
16
        },
1310
1311
        // haveRec subtree
1312
16
        {
1313
16
            &hf_p4rpc_have_client_path,
1314
16
            {
1315
16
                "client path",
1316
16
                "p4rpc.have.client.path",
1317
16
                FT_STRINGZ,
1318
16
                BASE_NONE,
1319
16
                NULL,
1320
16
                0x0,
1321
16
                "File path in client syntax",
1322
16
                HFILL
1323
16
            }
1324
16
        },
1325
16
        {
1326
16
            &hf_p4rpc_have_depot_path,
1327
16
            {
1328
16
                "depot path",
1329
16
                "p4rpc.have.depot.path",
1330
16
                FT_STRINGZ,
1331
16
                BASE_NONE,
1332
16
                NULL,
1333
16
                0x0,
1334
16
                "File path in depot syntax",
1335
16
                HFILL
1336
16
            }
1337
16
        },
1338
16
        {
1339
16
            &hf_p4rpc_have_file_rev,
1340
16
            {
1341
16
                "file rev",
1342
16
                "p4rpc.have.file.rev",
1343
16
                FT_INT32,
1344
16
                BASE_DEC,
1345
16
                NULL,
1346
16
                0x0,
1347
16
                "File revision number",
1348
16
                HFILL
1349
16
            }
1350
16
        },
1351
16
        {
1352
16
            &hf_p4rpc_have_file_type,
1353
16
            {
1354
16
                "file type",
1355
16
                "p4rpc.have.file.type",
1356
16
                FT_INT32,
1357
16
                BASE_DEC,
1358
16
                NULL,
1359
16
                0x0,
1360
16
                "Internal code for the file type",
1361
16
                HFILL
1362
16
            }
1363
16
        },
1364
16
        {
1365
16
            &hf_p4rpc_have_file_datetime,
1366
16
            {
1367
16
                "file datetime",
1368
16
                "p4rpc.have.file.datetime",
1369
                /*
1370
                 * Use FT_INT64 rather than FT_ABSOLUTE_TIME
1371
                 * so that we can display in ISO-8601 format,
1372
                 * in either UTC or local timezone.
1373
                 */
1374
16
                FT_INT64, // often only 32 bits
1375
16
                BASE_DEC,
1376
16
                NULL,
1377
16
                0x0,
1378
16
                "Timestamp of this file",
1379
16
                HFILL
1380
16
            }
1381
16
        }
1382
16
    };
1383
1384
    /*
1385
     * Register our expert info variables
1386
     * (message warnings, errors, corruption, etc)
1387
     */
1388
16
    static ei_register_info ei[] = {
1389
16
        {
1390
16
            &ei_p4rpc_msg_short,
1391
16
            {
1392
16
                "p4rpc.msg.short",
1393
16
                PI_MALFORMED,
1394
16
                PI_ERROR,
1395
16
                "available data < 5 bytes",
1396
16
                EXPFILL
1397
16
            }
1398
16
        },
1399
16
        {
1400
16
            &ei_p4rpc_msg_len_cksum,
1401
16
            {
1402
16
                "p4rpc.msg.len_cksum",
1403
16
                PI_CHECKSUM,
1404
16
                PI_WARN,
1405
16
                "message length checksum mismatch",
1406
16
                EXPFILL
1407
16
            }
1408
16
        },
1409
16
        {
1410
16
            &ei_p4rpc_msg_len,
1411
16
            {
1412
16
                "p4rpc.msg.msglen",
1413
16
                PI_MALFORMED,
1414
16
                PI_ERROR,
1415
16
                "message length < " MSG_MIN_LEN_STR " or > " MSG_MAX_LEN_STR,
1416
16
                EXPFILL
1417
16
            }
1418
16
        },
1419
16
        {
1420
16
            &ei_p4rpc_msg_val_len,
1421
16
            {
1422
16
                "p4rpc.msg.val.len",
1423
16
                PI_MALFORMED,
1424
16
                PI_ERROR,
1425
16
                "value length extends past end of message",
1426
16
                EXPFILL
1427
16
            }
1428
16
        },
1429
16
        {
1430
16
            &ei_p4rpc_msg_val_nul,
1431
16
            {
1432
16
                "p4rpc.msg.val.nul",
1433
16
                PI_MALFORMED,
1434
16
                PI_WARN,
1435
16
                "expected NUL terminator is not zero",
1436
16
                EXPFILL
1437
16
            }
1438
16
        },
1439
16
        {
1440
16
            &ei_p4rpc_timestr_non_numeric,
1441
16
            {
1442
16
                "p4rpc.timestr.inval",
1443
16
                PI_MALFORMED,
1444
16
                PI_WARN,
1445
16
                "time string contains non-numeric character",
1446
16
                EXPFILL
1447
16
            }
1448
16
        }
1449
16
    };
1450
1451
    // setup subtree arrays
1452
16
    static int *ett_arrays[] = {
1453
16
        &ett_p4rpc, // protocol subtree array
1454
16
        &ett_argtree, // protocol subtree name=value array
1455
16
        &ett_sync_time, // protocol subtree datetime array
1456
16
        &ett_haverec // protocol subtree haveRec value array
1457
16
    };
1458
1459
    // Finally, register our protocol
1460
16
    proto_p4rpc = proto_register_protocol ("P4RPC (Perforce Protocol)", "P4RPC", "p4rpc");
1461
1462
    // Register our protocol variables
1463
16
    proto_register_field_array( proto_p4rpc, hf, array_length(hf) );
1464
16
    proto_register_subtree_array( ett_arrays, array_length(ett_arrays) );
1465
1466
    // register expert variables
1467
16
    expert_module_t *expert_p4rpc = NULL;
1468
1469
16
    expert_p4rpc = expert_register_protocol( proto_p4rpc );
1470
16
    expert_register_field_array( expert_p4rpc, ei, array_length(ei) );
1471
1472
    // register P4RPC as a TCP dissector
1473
16
    p4rpc_handle = register_dissector_with_description(
1474
16
        "p4rpc",           // dissector name
1475
16
        "P4RPC",           // dissector description
1476
16
        dissect_p4rpc,     // dissector function
1477
16
        proto_p4rpc        // protocol being dissected
1478
16
    );
1479
1480
    /*
1481
     * register P4RPC as a TLS dissector
1482
     *
1483
     * We probably could just register dissect_p4rpc
1484
     * as the TLS dissector as well.
1485
     */
1486
16
    p4rpc_tls_handle = register_dissector_with_description(
1487
16
        "p4rpc.tls",       // dissector name
1488
16
        "P4RPC over TLS",  // dissector description
1489
16
        dissect_tls_p4rpc, // dissector function
1490
16
        proto_p4rpc        // protocol being dissected
1491
16
    );
1492
1493
16
    register_prefs();
1494
1495
16
    decimal_point = localeconv()->decimal_point;
1496
16
} // proto_register_p4rpc
1497
1498
/*
1499
 * Associate our dissector with ports.
1500
 */
1501
void
1502
proto_reg_handoff_p4rpc(void)
1503
16
{
1504
    /*
1505
     * Allow "Decode As" with any TCP packet.
1506
     * - need to do it only once
1507
     */
1508
16
    dissector_add_for_decode_as( "tcp.port", p4rpc_handle );
1509
1510
    /*
1511
     * Allow "Decode As" with any packet on our TLS port.
1512
     * - need to do it only once
1513
     */
1514
16
    dissector_add_for_decode_as( "tls.port", p4rpc_handle );
1515
1516
    // all the setup that needs to be redone when prefs are changed
1517
    p4rpc_setup_ports( true );
1518
16
}
1519
1520
/*
1521
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1522
 *
1523
 * Local variables:
1524
 * c-basic-offset: 4
1525
 * tab-width: 8
1526
 * indent-tabs-mode: nil
1527
 * End:
1528
 *
1529
 * vi: set shiftwidth=4 tabstop=8 expandtab:
1530
 * :indentSize=4:tabSize=8:noTabs=true:
1531
 */