3 # Copyright 2011, William Meier <wmeier[AT]newsguy.com>
5 # A program to fix encoding args for certain Wireshark API function calls
6 # from TRUE/FALSE to ENC_?? as appropriate (and possible)
7 # - proto_tree_add_item
8 # - proto_tree_add_bits_item
9 # - proto_tree_add_bits_ret_val
10 # - proto_tree_add_bitmask
11 # - proto_tree_add_bitmask_text !! ToDo: encoding arg not last arg
18 # - ptvcursor_add_no_advance
19 # - ptvcursor_add_with_subtree !! ToDo: encoding arg not last arg
21 # ToDo: Rework program so that it can better be used to *validate* encoding-args
23 # Wireshark - Network traffic analyzer
24 # By Gerald Combs <gerald@wireshark.org>
25 # Copyright 1998 Gerald Combs
27 # SPDX-License-Identifier: GPL-2.0-or-later
35 # Conversion "Requests"
37 # Standard conversions
38 my $searchReplaceFalseTrueHRef =
40 "FALSE" => "ENC_BIG_ENDIAN",
41 "0" => "ENC_BIG_ENDIAN",
42 "TRUE" => "ENC_LITTLE_ENDIAN",
43 "1" => "ENC_LITTLE_ENDIAN"
46 my $searchReplaceEncNAHRef =
52 "ENC_LITTLE_ENDIAN" => "ENC_NA",
53 "ENC_BIG_ENDIAN" => "ENC_NA",
54 "ENC_ASCII|ENC_NA" => "ENC_NA",
55 "ENC_ASCII | ENC_NA" => "ENC_NA"
58 # ---------------------------------------------------------------------
59 # Conversion "request" structure
61 # [ <list of field types for which this conversion request applies> ],
62 # { <hash of desired encoding arg conversions> }
67 [ qw (FT_NONE FT_BYTES FT_ETHER FT_IPv6 FT_IPXNET FT_OID FT_REL_OID)],
68 $searchReplaceEncNAHRef
73 [ qw (FT_UINT8 FT_UINT16 FT_UINT24 FT_UINT32 FT_UINT64 FT_INT8
74 FT_INT16 FT_INT24 FT_INT32 FT_INT64 FT_FLOAT FT_DOUBLE)],
75 $searchReplaceFalseTrueHRef
80 [ qw (FT_BOOLEAN FT_IPv4 FT_GUID FT_EUI64)],
81 $searchReplaceFalseTrueHRef
86 [qw (FT_STRING FT_STRINGZ)],
88 "FALSE" => "ENC_ASCII|ENC_NA",
89 "0" => "ENC_ASCII|ENC_NA",
90 "TRUE" => "ENC_ASCII|ENC_NA",
91 "1" => "ENC_ASCII|ENC_NA",
92 "ENC_LITTLE_ENDIAN" => "ENC_ASCII|ENC_NA",
93 "ENC_BIG_ENDIAN" => "ENC_ASCII|ENC_NA",
94 "ENC_NA" => "ENC_ASCII|ENC_NA",
96 "ENC_ASCII" => "ENC_ASCII|ENC_NA",
97 "ENC_ASCII|ENC_LITTLE_ENDIAN" => "ENC_ASCII|ENC_NA",
98 "ENC_ASCII|ENC_BIG_ENDIAN" => "ENC_ASCII|ENC_NA",
100 "ENC_UTF_8" => "ENC_UTF_8|ENC_NA",
101 "ENC_UTF_8|ENC_LITTLE_ENDIAN" => "ENC_UTF_8|ENC_NA",
102 "ENC_UTF_8|ENC_BIG_ENDIAN" => "ENC_UTF_8|ENC_NA",
104 "ENC_EBCDIC" => "ENC_EBCDIC|ENC_NA",
105 "ENC_EBCDIC|ENC_LITTLE_ENDIAN" => "ENC_EBCDIC|ENC_NA",
106 "ENC_EBCDIC|ENC_BIG_ENDIAN" => "ENC_EBCDIC|ENC_NA",
110 my @types_UINT_STRING =
112 [qw (FT_UINT_STRING)],
114 "FALSE" => "ENC_ASCII|ENC_BIG_ENDIAN",
115 "0" => "ENC_ASCII|ENC_BIG_ENDIAN",
116 "TRUE" => "ENC_ASCII|ENC_LITTLE_ENDIAN",
117 "1" => "ENC_ASCII|ENC_LITTLE_ENDIAN",
118 "ENC_BIG_ENDIAN" => "ENC_ASCII|ENC_BIG_ENDIAN",
119 "ENC_LITTLE_ENDIAN" => "ENC_ASCII|ENC_LITTLE_ENDIAN",
120 "ENC_ASCII|ENC_NA" => "ENC_ASCII|ENC_BIG_ENDIAN",
121 "ENC_ASCII" => "ENC_ASCII|ENC_BIG_ENDIAN",
122 "ENC_NA" => "ENC_ASCII|ENC_BIG_ENDIAN"
126 my @types_REG_PROTO =
129 $searchReplaceEncNAHRef
132 # ---------------------------------------------------------------------
133 # For searching (and doing no substitutions) (obsolete ?)
136 [qw (FT_ABSOLUTE_TIME FT_RELATIVE_TIME)],
176 {# valid encoding args
178 "b"=>"ENC_LITTLE_ENDIAN",
179 "c"=>"ENC_BIG_ENDIAN",
181 "d"=>"ENC_ASCII|ENC_NA",
182 "e"=>"ENC_ASCII|ENC_LITTLE_ENDIAN",
183 "f"=>"ENC_ASCII|ENC_BIG_ENDIAN",
185 "g"=>"ENC_UTF_8|ENC_NA",
186 "h"=>"ENC_UTF_8|ENC_LITTLE_ENDIAN",
187 "i"=>"ENC_UTF_8|ENC_BIG_ENDIAN",
189 "j"=>"ENC_EBCDIC|ENC_NA",
190 "k"=>"ENC_EBCDIC|ENC_LITTLE_ENDIAN",
191 "l"=>"ENC_EBCDIC|ENC_BIG_ENDIAN",
195 # ---------------------------------------------------------------------
197 my @findAllFunctionList =
198 ## proto_tree_add_bitmask_text !! ToDo: encoding arg not last arg
199 ## ptvcursor_add_with_subtree !! ToDo: encoding Arg not last arg
202 proto_tree_add_bits_item
203 proto_tree_add_bits_ret_val
204 proto_tree_add_bitmask
205 proto_tree_add_bitmask_with_flags
212 ptvcursor_add_no_advance
215 # ---------------------------------------------------------------------
221 my $action = 'fix-all';
223 my $result = GetOptions(
224 'action=s' => \$action,
225 'write' => \$writeFlag,
226 'help|?' => \$helpFlag
229 if (!$result || $helpFlag || !$ARGV[0]) {
233 if (($action ne 'fix-all') && ($action ne 'find-all')) {
238 print "\nUsage: $0 [--action=fix-all|find-all] [--write] FILENAME [...]\n\n";
239 print " --action = fix-all (default)\n";
240 print " Fix <certain-fcn-names>() encoding arg when possible in FILENAME(s)\n";
241 print " Fixes (if any) are listed on stdout)\n\n";
242 print " --write create FILENAME.encoding-arg-fixes (original file with fixes)\n";
243 print " (effective only for fix-all)\n";
245 print " --action = find-all\n";
246 print " Find all occurrences of <certain-fcn-names>() statements)\n";
247 print " highlighting the 'encoding' arg\n";
251 # Read through the files; fix up encoding parameter of proto_tree_add_item() calls
254 # . Create a hash of the hf_index_names & associated field types from the entries in hf[]
255 # . For each requested "conversion request" {
256 # . . For each hf[] entry hf_index_name with a field type in a set of specified field types {
257 # . . . For each proto_tree_add_item() statement
258 # . . . . - replace encoding arg in proto_tree_add_item(..., hf_index_name, ..., 'encoding-arg')
259 # specific values ith new values
260 # . . . . - print the statement showing the change
264 # . If requested and if replacements done: write new file "orig-filename.encoding-arg-fixes"
267 # Note: The proto_tree_add_item() encoding arg will be converted only if
268 # the hf_index_name referenced is in one of the entries in hf[] in the same file
272 while (my $fileName = $ARGV[0]) {
274 my $fileContents = '';
276 die "No such file: \"$fileName\"\n" if (! -e $fileName);
278 # delete leading './'
279 $fileName =~ s{ ^ \. / } {}xo;
280 ##print "$fileName\n";
282 # Read in the file (ouch, but it's easier that way)
283 open(FCI, "<", $fileName) || die("Couldn't open $fileName");
289 # Create a hash of the hf[] entries (name_index_name=>field_type)
290 my $hfArrayEntryFieldTypeHRef = find_hf_array_entries(\$fileContents, $fileName);
292 if ($action eq "fix-all") {
294 # Find and replace: <fcn_name_pattern>() encoding arg in $fileContents for:
295 # - hf[] entries with specified field types;
296 # - 'proto' as returned from proto_register_protocol()
297 my $fcn_name = "(?:proto_tree_add_item|ptvcursor_add(?:_no_advance)?)";
299 $found += fix_encoding_args_by_hf_type(1, \@types_NA, $fcn_name, \$fileContents, $hfArrayEntryFieldTypeHRef, $fileName);
300 $found += fix_encoding_args_by_hf_type(1, \@types_INT, $fcn_name, \$fileContents, $hfArrayEntryFieldTypeHRef, $fileName);
301 $found += fix_encoding_args_by_hf_type(1, \@types_MISC, $fcn_name, \$fileContents, $hfArrayEntryFieldTypeHRef, $fileName);
302 $found += fix_encoding_args_by_hf_type(1, \@types_STRING, $fcn_name, \$fileContents, $hfArrayEntryFieldTypeHRef, $fileName);
303 $found += fix_encoding_args_by_hf_type(1, \@types_UINT_STRING, $fcn_name, \$fileContents, $hfArrayEntryFieldTypeHRef, $fileName);
304 $found += fix_encoding_args_by_hf_type(1, \@types_REG_PROTO, $fcn_name, \$fileContents, $hfArrayEntryFieldTypeHRef, $fileName);
306 # Find and replace: alters <fcn_name>() encoding arg in $fileContents
307 $found += fix_encoding_args(1, $searchReplaceFalseTrueHRef, "proto_tree_add_bits_(?:item|ret_val)", \$fileContents, $fileName);
308 $found += fix_encoding_args(1, $searchReplaceFalseTrueHRef, "proto_tree_add_bitmask", \$fileContents, $fileName);
309 $found += fix_encoding_args(1, $searchReplaceFalseTrueHRef, "proto_tree_add_bitmask_with_flags", \$fileContents, $fileName);
310 $found += fix_encoding_args(1, $searchReplaceFalseTrueHRef, "tvb_get_bits(?:16|24|32|64)?", \$fileContents, $fileName);
311 $found += fix_encoding_args(1, $searchReplaceFalseTrueHRef, "tvb_get_(?:ephemeral_)?unicode_string[z]?", \$fileContents, $fileName);
313 # If desired and if any changes, write out the changed version to a file
314 if (($writeFlag) && ($found > 0)) {
315 open(FCO, ">", $fileName . ".encoding-arg-fixes");
316 # open(FCO, ">", $fileName );
317 print FCO "$fileContents";
320 $found_total += $found;
323 if ($action eq "find-all") {
324 # Find all proto_tree_add_item() statements
325 # and output same highlighting the encoding arg
326 $found_total += find_all(\@findAllFunctionList, \$fileContents, $fileName);
329 # Optional searches: (kind of obsolete ?)
330 # search for (and output) proto_tree_add_item() statements with invalid encoding arg for specified field types
331 # $fcn_name = "proto_tree_add_item";
332 # fix_encoding_args(2, \@types_NA, $fcn_name, \$fileContents, $hfArrayEntryFieldTypeHRef, $fileName);
333 # fix_encoding_args(2, \@types_INT, $fcn_name, \$fileContents, $hfArrayEntryFieldTypeHRef, $fileName);
334 # fix_encoding_args(2, \@types_MISC, $fcn_name, \$fileContents, $hfArrayEntryFieldTypeHRef, $fileName);
335 # fix_encoding_args(2, \@types_STRING, $fcn_name, \$fileContents, $hfArrayEntryFieldTypeHRef, $fileName);
336 # fix_encoding_args(2, \@types_UINT_STRING, $fcn_name, \$fileContents, $hfArrayEntryFieldTypeHRef, $fileName);
337 # fix_encoding_args(2, \@types_ALL, $fcn_name, \$fileContents, $hfArrayEntryFieldTypeHRef, $fileName);
338 # search for (and output) proto_tree_add_item()$fcn_name, statements with any encoding arg for specified field types
339 # fix_encoding_args(3, \@types_TIME, $fcn_name, \$fileContents, $hfArrayEntryFieldTypeHRef, $fileName);
346 # ---------------------------------------------------------------------
347 # Create a hash containing an entry (hf_index_name => field_type) for each hf[]entry.
348 # also: create an entry in the hash for the 'protocol name' variable (proto... => FT_PROTOCOL)
349 # returns: ref to the hash
351 sub find_hf_array_entries {
352 my ($fileContentsRef, $fileName) = @_;
354 # The below Regexp is based on one from:
355 # http://aspn.activestate.com/ASPN/Cookbook/Rx/Recipe/59811
356 # It is in the public domain.
357 # A complicated regex which matches C-style comments.
358 my $CCommentRegEx = qr{ / [*] [^*]* [*]+ (?: [^/*] [^*]* [*]+ )* / }xo;
360 # hf[] entry regex (to extract an hf_index_name and associated field type)
361 my $hfArrayFieldTypeRegEx = qr {
364 &\s*([A-Z0-9_\[\]-]+) # &hf
367 .+? # (a bit dangerous)
369 (FT_[A-Z0-9_]+) # field type
376 # create a copy of $fileContents with comments removed
377 my $fileContentsWithoutComments = $$fileContentsRef;
378 $fileContentsWithoutComments =~ s {$CCommentRegEx} []xg;
380 # find all the hf[] entries (searching $fileContentsWithoutComments).
381 # Create a hash keyed by the hf_index_name with the associated value being the field_type
382 my %hfArrayEntryFieldType;
383 while ($fileContentsWithoutComments =~ m{ $hfArrayFieldTypeRegEx }xgis) {
385 if (exists $hfArrayEntryFieldType{$1}) {
386 printf "%-35.35s: ? duplicate hf[] entry: no fixes done for: $1; manual action may be req'd\n", $fileName;
387 $hfArrayEntryFieldType{$1} = "???"; # prevent any substitutions for this hf_index_name
389 $hfArrayEntryFieldType{$1} = $2;
393 # RegEx to get "proto" variable name
394 my $protoRegEx = qr /
395 ^ \s* # note m modifier below
402 proto_register_protocol
407 # Find all registered protocols
408 while ($fileContentsWithoutComments =~ m { $protoRegEx }xgioms ) {
410 if (exists $hfArrayEntryFieldType{$1}) {
411 printf "%-35.35s: ? duplicate 'proto': no fixes done for: $1; manual action may be req'd\n", $fileName;
412 $hfArrayEntryFieldType{$1} = "???"; # prevent any substitutions for this protocol
414 $hfArrayEntryFieldType{$1} = "REG_PROTO";
418 return \%hfArrayEntryFieldType;
421 # ---------------------------------------------------------------------
423 # Substitute new values for the specified <fcn_name>() encoding arg values
424 # when the encoding arg is the *last* arg of the call to fcn_name
426 # substitute_flag: 1: replace specified encoding arg values by a new value (keys/values in search hash);
427 # ref to hash containing search (keys) and replacement (values) for encoding arg
429 # ref to string containing file contents
436 my $searchReplaceHRef;
439 sub fix_encoding_args {
440 (my $subFlag, $searchReplaceHRef, my $fcn_name, my $fileContentsRef, $fileName) = @_;
445 # just match for <fcn_name>() statements which have an encoding arg matching one of the
446 # keys in the searchReplace hash.
447 # Escape any "|" characters in the keys
448 # and then create "alternatives" string containing all the resulting key strings. Ex: "(A|B|C\|D|..."
449 $encArgPat = join "|", map { my $copy = $_; $copy =~ s{ ( \| ) }{\\$1}gx; $copy } keys %$searchReplaceHRef;
450 } elsif ($subFlag == 3) {
451 # match for <fcn_name>() statements for any value of the encoding parameter
452 # IOW: find all the <fcn_name> statements
453 $encArgPat = qr / [^,)]+? /x;
456 # build the complete pattern
460 (?:^|=) # don't try to handle fcn_name call when arg of another fcn call
463 [^;]+? # a bit dangerous
468 # exact match of pattern (including spaces)
476 /xms; # m for ^ above
478 ##print "$patRegEx\n";
480 ## Match and substitute as specified
483 $$fileContentsRef =~ s/ $patRegEx /patsubx($1,$2,$3)/xges;
488 # Called from fix_encoding_args to determine replacement string when a regex match is encountered
490 # $_[1]: part 2: encoding arg
492 # lookup the desired replacement value for the encoding arg
493 # print match string showing and highlighting the encoding arg replacement
494 # return "replacement" string
497 my $substr = exists $$searchReplaceHRef{$_[1]} ? $$searchReplaceHRef{$_[1]} : "???";
498 my $str = sprintf("%s[[%s]-->[%s]]%s", $_[0], $_[1], $substr, $_[2]);
499 $str =~ tr/\t\n\r/ /d;
500 printf "%s: $str\n", $fileName;
501 return $_[0] . $substr . $_[2];
505 # ---------------------------------------------------------------------
506 # fix_encoding_args_by_hf_type
508 # Substitute new values for certain proto_tree_add_item() encoding arg
509 # values (for specified hf field types)
510 # Variants: search for and display for "exceptions" to allowed encoding arg values;
511 # search for and display all encoding arg values
513 # substitute_flag: 1: replace specified encoding arg values by a new value (keys/values in search hash);
514 # 2: search for "exceptions" to allowed encoding arg values (values in search hash);
515 # 3: search for all encoding arg values
516 # ref to array containing two elements:
517 # - ref to array containing hf[] types to be processed (FT_STRING, etc)
518 # - ref to hash containing search (keys) and replacement (values) for encoding arg
520 # ref to hfArrayEntries hash (key: hf name; value: field type)
521 # ref to string containing file contents
528 my $searchReplaceHRef;
532 sub fix_encoding_args_by_hf_type {
534 (my $subFlag, my $mapArg, my $fcn_name, my $fileContentsRef, my $hfArrayEntryFieldTypeHRef, $fileName) = @_;
540 $hfTypesARef = $$mapArg[0];
541 $searchReplaceHRef = $$mapArg[1];
544 @hfTypes{@$hfTypesARef}=();
546 # set up the encoding arg match pattern
548 # just match for <fcn_name>() statements which have an encoding arg matching one of the
549 # keys in the searchReplace hash.
550 # Escape any "|" characters in the keys
551 # and then create "alternatives" string containing all the resulting key strings. Ex: "A|B|C\|D|..."
552 $encArgPat = join "|", map { my $copy = $_; $copy =~ s{ ( \| ) }{\\$1}gx; $copy } keys %$searchReplaceHRef;
553 } elsif ($subFlag == 2) {
554 # Find all the <fcn_name>() statements wherein the encoding arg is a value other than
555 # one of the "replace" values.
556 # Uses zero-length negative-lookahead to find <fcn_name>() statements for which the encoding
557 # arg is something other than one of the the provided replace values.
558 # Escape any "|" characters in the values to be matched
559 # and then create "alternatives" string containing all the value strings. Ex: "A|B|C\|D|..."
560 my $match_str = join "|", map { my $copy = $_; $copy =~ s{ ( \| ) }{\\$1}gx; $copy } values %$searchReplaceHRef;
562 (?! # negative zero-length look-ahead
564 (?: $match_str ) # alternatives we don't want to match
567 [^,)]+? # OK: enoding arg is other than one of the alternatives:
568 # match to end of the arg
570 } elsif ($subFlag == 3) {
571 # match for <fcn_name>() statements for any value of the encoding parameter
572 # IOW: find all the proto_tree_add_item statements with an hf entry of the desired types
573 $encArgPat = qr / [^,)]+? /x;
576 # For each hf[] entry which matches a type in %hfTypes do replacements
578 foreach my $key (keys %$hfArrayEntryFieldTypeHRef) {
579 $hf_index_name = $key;
580 $hf_index_name =~ s{ ( \[ | \] ) }{\\$1}xg; # escape any "[" or "]" characters
581 $hf_field_type = $$hfArrayEntryFieldTypeHRef{$key};
582 ##printf "--> %-35.35s: %s\n", $hf_index_name, $hf_field_type;
584 next unless exists $hfTypes{$hf_field_type}; # Do we want to process for this hf[] entry type ?
586 # build the complete pattern
600 # exact match of pattern (including spaces)
610 ##print "\n$hf_index_name $hf_field_type\n";
611 ##print "\n$patRegEx\n";
613 ## Match and substitute as specified
614 $$fileContentsRef =~ s/ $patRegEx /patsub($1,$2,$3)/xges;
621 # Called from fix_encoding_args to determine replacement string when a regex match is encountered
623 # $_[1]: part 2: encoding arg
625 # lookup the desired replacement value for the encoding arg
626 # print match string showing and highlighting the encoding arg replacement
627 # return "replacement" string
630 my $substr = exists $$searchReplaceHRef{$_[1]} ? $$searchReplaceHRef{$_[1]} : "???";
631 my $str = sprintf("%s[[%s]-->[%s]]%s", $_[0], $_[1], $substr, $_[2]);
632 $str =~ tr/\t\n\r/ /d;
633 printf "%s: %-17.17s $str\n", $fileName, $hf_field_type . ":";
634 return $_[0] . $substr . $_[2];
638 # ---------------------------------------------------------------------
639 # Find all <fcnList> statements
640 # and output same highlighting the encoding arg
641 # Currently: encoding arg is matched as the *last* arg of the function call
644 my( $fcnListARef, $fileContentsRef, $fileName) = @_;
647 my $fcnListPat = join "|", @$fcnListARef;
650 (?:$fcnListPat) \s* \(
663 while ($$fileContentsRef =~ / $pat /xgso) {
664 my $str = "${1}[[${2}]]${3}\n";
665 $str =~ tr/\t\n\r/ /d;
666 $str =~ s/ \s+ / /xg;
667 print "$fileName: $str\n";