#!/usr/bin/env perl # # Copyright 2013 Michael Mann (see AUTHORS file) # # A program to help convert the "old" expert_add_info_format API calls into filterable "items" that # use the other expert API calls. The program requires 2 passes. "Pass 1" (generate) collects # the eligible expert_add_info_format calls and outputs the necessary data into a delimited # file. "Pass 2" (fix-all) takes the data from the delimited file and replaces the # expert_add_info_format calls with filterable "expert info" calls as well as # generating a separate files for the ei variable declarations and array data. # The ei "file" can be copy/pasted into the dissector where appropriate # # Note that the output from "Pass 1" won't always be a perfect conversion for "Pass 2", so # "human interaction" is needed as an intermediary to verify and update the delimited file # before "Pass 2" is done. # # Delimited file field format: # <[GROUP]><[SEVERITY]><[FIELDNAME]><[EXPERTABBREV]> # # # convert proto_tree_add_text_call enumerations: # 1 - expert_add_info # 2 - expert_add_info_format # 3 - proto_tree_add_expert # 4 - proto_tree_add_expert_format # # Usage: convert_expert_add_info_format.pl action= # # Based off of convert_proto_tree_add_text.pl # # Wireshark - Network traffic analyzer # By Gerald Combs # Copyright 1998 Gerald Combs # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # use strict; use warnings; use Getopt::Long; my %EXPERT_SEVERITY = ('PI_COMMENT' => "PI_COMMENT", 'PI_CHAT' => "PI_CHAT", 'PI_NOTE' => "PI_NOTE", 'PI_WARN' => "PI_WARN", 'PI_ERROR' => "PI_ERROR"); my %EXPERT_GROUPS = ('PI_CHECKSUM' => "PI_CHECKSUM", 'PI_SEQUENCE' => "PI_SEQUENCE", 'PI_RESPONSE_CODE' => "PI_RESPONSE_CODE", 'PI_REQUEST_CODE' => "PI_REQUEST_CODE", 'PI_UNDECODED' => "PI_UNDECODED", 'PI_REASSEMBLE' => "PI_REASSEMBLE", 'PI_MALFORMED' => "PI_MALFORMED", 'PI_DEBUG' => "PI_DEBUG", 'PI_PROTOCOL' => "PI_PROTOCOL", 'PI_SECURITY' => "PI_SECURITY", 'PI_COMMENTS_GROUP' => "PI_COMMENTS_GROUP"); my @expert_list; my $protabbrev = ""; # Perl trim function to remove whitespace from the start and end of the string sub trim($) { my $string = shift; $string =~ s/^\s+//; $string =~ s/\s+$//; return $string; } # --------------------------------------------------------------------- # # MAIN # my $helpFlag = ''; my $action = 'generate'; my $register = ''; my $result = GetOptions( 'action=s' => \$action, 'register' => \$register, 'help|?' => \$helpFlag ); if (!$result || $helpFlag || !$ARGV[0]) { usage(); } sub usage { print "\nUsage: $0 [--action=generate|fix-all|find-all] FILENAME [...]\n\n"; print " --action = generate (default)\n"; print " generate - create a delimited file (FILENAME.expert_add_info_input) with\n"; print " expert_add_info_format fields in FILENAME(s)\n"; print " fix-all - Use delimited file (FILENAME.expert_add_info_input) to convert\n"; print " expert_add_info_format to \"filterable\" expert API\n"; print " Also generates FILENAME.ei to be copy/pasted into\n"; print " the dissector where appropriate\n\n"; print " --register = generate ei_register_info and expert register function calls\n\n"; exit(1); } # # XXX Outline general algorithm here # my $found_total = 0; my $protabbrev_index; my $line_number = 0; while (my $fileName = $ARGV[0]) { shift; my $fileContents = ''; die "No such file: \"$fileName\"\n" if (! -e $fileName); # delete leading './' $fileName =~ s{ ^ \. / } {}xo; #determine PROTABBREV for dissector based on file name format of (dirs)/packet-PROTABBREV.c $protabbrev_index = rindex($fileName, "packet-"); if ($protabbrev_index == -1) { print "$fileName doesn't fit format of packet-PROTABBREV.c\n"; next; } $protabbrev = substr($fileName, $protabbrev_index+length("packet-")); $protabbrev_index = rindex($protabbrev, "."); if ($protabbrev_index == -1) { print "$fileName doesn't fit format of packet-PROTABBREV.c\n"; next; } $protabbrev = lc(substr($protabbrev, 0, $protabbrev_index)); # Read in the file (ouch, but it's easier that way) open(FCI, "<", $fileName) || die("Couldn't open $fileName"); while () { $fileContents .= $_; } close(FCI); if ($action eq "generate") { generate_eis(\$fileContents, $fileName); } if ($action eq "fix-all") { # Read in the ei "input" file $line_number = 0; my $errors = 0; open(FCI, "<", $fileName . ".expert_add_info_input") || die("Couldn't open $fileName.expert_add_info_input"); while(my $line=){ my @expert_item = split(/;|\n/, $line); $line_number++; $errors += verify_line(@expert_item); push(@expert_list, \@expert_item); } close(FCI); if ($errors > 0) { print "Aborting conversion.\n"; exit(-1); } fix_expert_add_info_format(\$fileContents, $fileName); # Write out the ei data output_ei_data($fileName); # Write out the changed version to a file open(FCO, ">", $fileName . ".expert_add_info_format"); print FCO "$fileContents"; close(FCO); } } # while exit $found_total; # --------------------------------------------------------------------- # Sanity check the data in the .proto_tree_input file sub verify_line { my( @expert_item) = @_; my $errors = 0; #do some basic error checking of the file if (($expert_item[0] eq "1") || ($expert_item[0] eq "2") || ($expert_item[0] eq "3") || ($expert_item[0] eq "4")) { #expert info conversions if (!($expert_item[2] =~ /^ei_/)) { print "$line_number: Poorly formed ei_ variable ($expert_item[2])!\n"; $errors++; } } else { print "$line_number: Bad conversion value!\n"; $errors++; } if ($expert_item[1] eq "1") { if (!($expert_item[2] =~ /^ei_/)) { print "$line_number: Poorly formed ei_ variable ($expert_item[2])!\n"; $errors++; } if (!exists($EXPERT_SEVERITY{$expert_item[4]})) { print "$line_number: Expert severity value '$expert_item[5]' unknown!\n"; $errors++; } if (!exists($EXPERT_GROUPS{$expert_item[3]})) { print "$line_number: Expert group value '$expert_item[4]' unknown!\n"; $errors++; } } elsif ($expert_item[1] ne "0") { print "$line_number: Bad ei variable generation value!\n"; $errors++; } return $errors; } sub generate_eis { my( $fileContentsRef, $fileName) = @_; my @args; my $num_items = 0; my @temp; my $str_temp; my $pat; $pat = qr / ( (?:expert_add_info_format)\s* \( (([^[\,;])*\,){4,} [^;]* \s* \) \s* ; ) /xs; while ($$fileContentsRef =~ / $pat /xgso) { my @expert_item = (1, 1, "ei_name", "GROUP", "SEVERITY", "fieldfullname", "fieldabbrevname", "pinfo", "item", "tvb", "offset", "length", "params"); my $arg_loop = 5; my $str = "${1}\n"; $str =~ tr/\t\n\r/ /d; $str =~ s/ \s+ / /xg; #print "$fileName: $str\n"; @args = split(/,/, $str); #printf "ARGS(%d): %s\n", scalar @args, join("# ", @args); $args[0] =~ s/expert_add_info_format\s*\(\s*//; $expert_item[7] = $args[0]; #pinfo $expert_item[8] = trim($args[1]); #item $expert_item[3] = trim($args[2]); #GROUP $expert_item[4] = trim($args[3]); #SEVERITY $expert_item[5] = trim($args[4]); #fieldfullname $expert_item[5] =~ s/\"//; #XXX - conditional? $expert_item[5] =~ s/\"\s*\)\s*;$//; $expert_item[5] =~ s/\"$//; #params $expert_item[12] = ""; while ($arg_loop < scalar @args) { $expert_item[12] .= trim($args[$arg_loop]); if ($arg_loop+1 < scalar @args) { $expert_item[12] .= ", "; } $arg_loop += 1; } $expert_item[12] =~ s/\s*\)\s*;$//; #ei variable name $expert_item[2] = sprintf("ei_%s_%s", $protabbrev, lc($expert_item[5])); $expert_item[2] =~ s/\s+|-|:/_/g; #field abbreviated name $expert_item[6] = sprintf("%s.%s", $protabbrev, lc($expert_item[5])); $expert_item[6] =~ s/\s+|-|:/_/g; push(@expert_list, \@expert_item); $num_items += 1; } if ($num_items > 0) { open(FCO, ">", $fileName . ".expert_add_info_input"); for my $item (@expert_list) { print FCO join(";", @{$item}), "\n"; } close(FCO); } } # --------------------------------------------------------------------- # Find all expert_add_info_format calls and replace them with the data # found in expert_list sub fix_expert_add_info_format { my( $fileContentsRef, $fileName) = @_; my $found = 0; my $pat; $pat = qr / ( (?:expert_add_info_format)\s* \( (([^[\,;])*\,){4,} [^;]* \s* \) \s* ; ) /xs; $$fileContentsRef =~ s/ $pat /patsub($found, $1)/xges; } # --------------------------------------------------------------------- # Format expert info functions with expert_list data sub patsub { my $item_str; #print $expert_list[$_[0]][2] . " = "; #print $#{$expert_list[$_[0]]}+1; #print "\n"; if ($expert_list[$_[0]][0] eq "1") { $item_str = sprintf("expert_add_info(%s, %s, &%s);", $expert_list[$_[0]][7], $expert_list[$_[0]][8], $expert_list[$_[0]][2]); } elsif ($expert_list[$_[0]][0] eq "2") { $item_str = sprintf("expert_add_info_format(%s, %s, &%s, \"%s\"", $expert_list[$_[0]][7], $expert_list[$_[0]][8], $expert_list[$_[0]][2], $expert_list[$_[0]][5]); if (($#{$expert_list[$_[0]]}+1 > 12 ) && ($expert_list[$_[0]][12] ne "")) { $item_str .= ", $expert_list[$_[0]][12]"; } $item_str .= ");"; } elsif ($expert_list[$_[0]][0] eq "3") { $item_str = sprintf("proto_tree_add_expert(%s, %s, &%s, %s, %s, %s);", $expert_list[$_[0]][8], $expert_list[$_[0]][7], $expert_list[$_[0]][2], $expert_list[$_[0]][9], $expert_list[$_[0]][10], $expert_list[$_[0]][11]); } elsif ($expert_list[$_[0]][0] eq "4") { $item_str = sprintf("proto_tree_add_expert_format(%s, %s, &%s, %s, %s, %s, \"%s\"", $expert_list[$_[0]][8], $expert_list[$_[0]][7], $expert_list[$_[0]][2], $expert_list[$_[0]][9], $expert_list[$_[0]][10], $expert_list[$_[0]][11], $expert_list[$_[0]][5]); if (($#{$expert_list[$_[0]]}+1 > 12) && ($expert_list[$_[0]][12] ne "")) { $item_str .= ", $expert_list[$_[0]][12]"; } $item_str .= ");"; } $_[0] += 1; return $item_str; } # --------------------------------------------------------------------- # Output the ei variable declarations and expert array. For now, write them to a file. # XXX - Eventually find the right place to add it to the modified dissector file sub output_ei_data { my( $fileName) = @_; my %eis = (); my $index; my $key; #add ei to hash table to prevent against (accidental) duplicates for ($index=0;$index<@expert_list;$index++) { if ($expert_list[$index][1] eq "1") { $eis{$expert_list[$index][2]} = $expert_list[$index][2]; } } open(FCO, ">", $fileName . ".ei"); print FCO "/* Generated from convert_expert_add_info_format.pl */\n"; foreach $key (keys %eis) { print FCO "static expert_field $key = EI_INIT;\n"; } print FCO "\n\n"; if ($register ne "") { print FCO " static ei_register_info ei[] = {\n"; } %eis = (); for ($index=0;$index<@expert_list;$index++) { if ($expert_list[$index][1] eq "1") { if (exists($eis{$expert_list[$index][2]})) { print "duplicate ei entry '$expert_list[$index][2]' found! Aborting conversion.\n"; exit(-1); } $eis{$expert_list[$index][2]} = $expert_list[$index][2]; print FCO " { &$expert_list[$index][2], { \"$expert_list[$index][6]\", $expert_list[$index][3], "; print FCO "$expert_list[$index][4], \"$expert_list[$index][5]\", EXPFILL }},\r\n"; } } if ($register ne "") { print FCO " };\n\n\n"; print FCO " expert_module_t* expert_$protabbrev;\n\n"; print FCO " expert_$protabbrev = expert_register_protocol(proto_$protabbrev);\n"; print FCO " expert_register_field_array(expert_$protabbrev, ei, array_length(ei));\n\n"; } close(FCO); }