1################################################### 2# parse an Wireshark conformance file 3# Copyright jelmer@samba.org 2005 4# released under the GNU GPL 5 6=pod 7 8=head1 NAME 9 10Parse::Pidl::Wireshark::Conformance - Conformance file parser for Wireshark 11 12=head1 DESCRIPTION 13 14This module supports parsing Wireshark conformance files (*.cnf). 15 16=head1 FILE FORMAT 17 18Pidl needs additional data for Wireshark output. This data is read from 19so-called conformance files. This section describes the format of these 20files. 21 22Conformance files are simple text files with a single command on each line. 23Empty lines and lines starting with a '#' character are ignored. 24Arguments to commands are seperated by spaces. 25 26The following commands are currently supported: 27 28=over 4 29 30=item I<TYPE> name dissector ft_type base_type mask valsstring alignment 31 32Register new data type with specified name, what dissector function to call 33and what properties to give header fields for elements of this type. 34 35=item I<NOEMIT> type 36 37Suppress emitting a dissect_type function for the specified type 38 39=item I<PARAM_VALUE> type param 40 41Set parameter to specify to dissector function for given type. 42 43=item I<HF_FIELD> hf title filter ft_type base_type valsstring mask description 44 45Generate a custom header field with specified properties. 46 47=item I<HF_RENAME> old_hf_name new_hf_name 48 49Force the use of new_hf_name when the parser generator was going to 50use old_hf_name. 51 52This can be used in conjunction with HF_FIELD in order to make more than 53one element use the same filter name. 54 55=item I<ETT_FIELD> ett 56 57Register a custom ett field 58 59=item I<STRIP_PREFIX> prefix 60 61Remove the specified prefix from all function names (if present). 62 63=item I<PROTOCOL> longname shortname filtername 64 65Change the short-, long- and filter-name for the current interface in 66Wireshark. 67 68=item I<FIELD_DESCRIPTION> field desc 69 70Change description for the specified header field. `field' is the hf name of the field. 71 72=item I<IMPORT> dissector code... 73 74Code to insert when generating the specified dissector. @HF@ and 75@PARAM@ will be substituted. 76 77=item I<INCLUDE> filename 78 79Include conformance data from the specified filename in the dissector. 80 81=item I<TFS> hf_name "true string" "false string" 82 83Override the text shown when a bitmap boolean value is enabled or disabled. 84 85=item I<MANUAL> fn_name 86 87Force pidl to not generate a particular function but allow the user 88to write a function manually. This can be used to remove the function 89for only one level for a particular element rather than all the functions and 90ett/hf variables for a particular element as the NOEMIT command does. 91 92=back 93 94=head1 EXAMPLE 95 96 INFO_KEY OpenKey.Ke 97 98=cut 99 100package Parse::Pidl::Wireshark::Conformance; 101 102require Exporter; 103use vars qw($VERSION); 104$VERSION = '0.01'; 105 106@ISA = qw(Exporter); 107@EXPORT_OK = qw(ReadConformance ReadConformanceFH valid_ft_type valid_base_type); 108 109use strict; 110 111use Parse::Pidl qw(fatal warning error); 112use Parse::Pidl::Util qw(has_property); 113 114sub handle_type($$$$$$$$$$) 115{ 116 my ($pos,$data,$name,$dissectorname,$ft_type,$base_type,$mask,$valsstring,$alignment) = @_; 117 118 unless(defined($alignment)) { 119 error($pos, "incomplete TYPE command"); 120 return; 121 } 122 123 unless ($dissectorname =~ /.*dissect_.*/) { 124 warning($pos, "dissector name does not contain `dissect'"); 125 } 126 127 unless(valid_ft_type($ft_type)) { 128 warning($pos, "invalid FT_TYPE `$ft_type'"); 129 } 130 131 unless (valid_base_type($base_type)) { 132 warning($pos, "invalid BASE_TYPE `$base_type'"); 133 } 134 135 $dissectorname =~ s/^\"(.*)\"$/$1/g; 136 137 if (not ($dissectorname =~ /;$/)) { 138 warning($pos, "missing semicolon"); 139 } 140 141 $data->{types}->{$name} = { 142 NAME => $name, 143 POS => $pos, 144 USED => 0, 145 DISSECTOR_NAME => $dissectorname, 146 FT_TYPE => $ft_type, 147 BASE_TYPE => $base_type, 148 MASK => $mask, 149 VALSSTRING => $valsstring, 150 ALIGNMENT => $alignment 151 }; 152} 153 154sub handle_tfs($$$$$) 155{ 156 my ($pos,$data,$hf,$trues,$falses) = @_; 157 158 unless(defined($falses)) { 159 error($pos, "incomplete TFS command"); 160 return; 161 } 162 163 $data->{tfs}->{$hf} = { 164 TRUE_STRING => $trues, 165 FALSE_STRING => $falses 166 }; 167} 168 169sub handle_hf_rename($$$$) 170{ 171 my ($pos,$data,$old,$new) = @_; 172 173 unless(defined($new)) { 174 warning($pos, "incomplete HF_RENAME command"); 175 return; 176 } 177 178 $data->{hf_renames}->{$old} = { 179 OLDNAME => $old, 180 NEWNAME => $new, 181 POS => $pos, 182 USED => 0 183 }; 184} 185 186sub handle_param_value($$$$) 187{ 188 my ($pos,$data,$dissector_name,$value) = @_; 189 190 unless(defined($value)) { 191 error($pos, "incomplete PARAM_VALUE command"); 192 return; 193 } 194 195 $data->{dissectorparams}->{$dissector_name} = { 196 DISSECTOR => $dissector_name, 197 PARAM => $value, 198 POS => $pos, 199 USED => 0 200 }; 201} 202 203sub valid_base_type($) 204{ 205 my $t = shift; 206 return 0 unless($t =~ /^BASE_.*/); 207 return 1; 208} 209 210sub valid_ft_type($) 211{ 212 my $t = shift; 213 return 0 unless($t =~ /^FT_.*/); 214 return 1; 215} 216 217sub handle_hf_field($$$$$$$$$$) 218{ 219 my ($pos,$data,$index,$name,$filter,$ft_type,$base_type,$valsstring,$mask,$blurb) = @_; 220 221 unless(defined($blurb)) { 222 error($pos, "incomplete HF_FIELD command"); 223 return; 224 } 225 226 unless(valid_ft_type($ft_type)) { 227 warning($pos, "invalid FT_TYPE `$ft_type'"); 228 } 229 230 unless(valid_base_type($base_type)) { 231 warning($pos, "invalid BASE_TYPE `$base_type'"); 232 } 233 234 $data->{header_fields}->{$index} = { 235 INDEX => $index, 236 POS => $pos, 237 USED => 0, 238 NAME => $name, 239 FILTER => $filter, 240 FT_TYPE => $ft_type, 241 BASE_TYPE => $base_type, 242 VALSSTRING => $valsstring, 243 MASK => $mask, 244 BLURB => $blurb 245 }; 246} 247 248sub handle_strip_prefix($$$) 249{ 250 my ($pos,$data,$x) = @_; 251 252 push (@{$data->{strip_prefixes}}, $x); 253} 254 255sub handle_noemit($$$) 256{ 257 my ($pos,$data,$type) = @_; 258 259 if (defined($type)) { 260 $data->{noemit}->{$type} = 1; 261 } else { 262 $data->{noemit_dissector} = 1; 263 } 264} 265 266sub handle_manual($$$) 267{ 268 my ($pos,$data,$fn) = @_; 269 270 unless(defined($fn)) { 271 warning($pos, "incomplete MANUAL command"); 272 return; 273 } 274 275 $data->{manual}->{$fn} = 1; 276} 277 278sub handle_protocol($$$$$$) 279{ 280 my ($pos, $data, $name, $longname, $shortname, $filtername) = @_; 281 282 $data->{protocols}->{$name} = { 283 LONGNAME => $longname, 284 SHORTNAME => $shortname, 285 FILTERNAME => $filtername 286 }; 287} 288 289sub handle_fielddescription($$$$) 290{ 291 my ($pos,$data,$field,$desc) = @_; 292 293 unless(defined($desc)) { 294 warning($pos, "incomplete FIELD_DESCRIPTION command"); 295 return; 296 } 297 298 $data->{fielddescription}->{$field} = { 299 DESCRIPTION => $desc, 300 POS => $pos, 301 USED => 0 302 }; 303} 304 305sub handle_import 306{ 307 my $pos = shift @_; 308 my $data = shift @_; 309 my $dissectorname = shift @_; 310 311 unless(defined($dissectorname)) { 312 error($pos, "no dissectorname specified"); 313 return; 314 } 315 316 $data->{imports}->{$dissectorname} = { 317 NAME => $dissectorname, 318 DATA => join(' ', @_), 319 USED => 0, 320 POS => $pos 321 }; 322} 323 324sub handle_ett_field 325{ 326 my $pos = shift @_; 327 my $data = shift @_; 328 my $ett = shift @_; 329 330 unless(defined($ett)) { 331 error($pos, "incomplete ETT_FIELD command"); 332 return; 333 } 334 335 push (@{$data->{ett}}, $ett); 336} 337 338sub handle_include 339{ 340 my $pos = shift @_; 341 my $data = shift @_; 342 my $fn = shift @_; 343 344 unless(defined($fn)) { 345 error($pos, "incomplete INCLUDE command"); 346 return; 347 } 348 349 ReadConformance($fn, $data); 350} 351 352my %field_handlers = ( 353 TYPE => \&handle_type, 354 NOEMIT => \&handle_noemit, 355 MANUAL => \&handle_manual, 356 PARAM_VALUE => \&handle_param_value, 357 HF_FIELD => \&handle_hf_field, 358 HF_RENAME => \&handle_hf_rename, 359 ETT_FIELD => \&handle_ett_field, 360 TFS => \&handle_tfs, 361 STRIP_PREFIX => \&handle_strip_prefix, 362 PROTOCOL => \&handle_protocol, 363 FIELD_DESCRIPTION => \&handle_fielddescription, 364 IMPORT => \&handle_import, 365 INCLUDE => \&handle_include 366); 367 368sub ReadConformance($$) 369{ 370 my ($f,$data) = @_; 371 my $ret; 372 373 open(IN,"<$f") or return undef; 374 375 $ret = ReadConformanceFH(*IN, $data, $f); 376 377 close(IN); 378 379 return $ret; 380} 381 382sub ReadConformanceFH($$$) 383{ 384 my ($fh,$data,$f) = @_; 385 386 my $incodeblock = 0; 387 388 my $ln = 0; 389 390 foreach (<$fh>) { 391 $ln++; 392 next if (/^#.*$/); 393 next if (/^$/); 394 395 s/[\r\n]//g; 396 397 if ($_ eq "CODE START") { 398 $incodeblock = 1; 399 next; 400 } elsif ($incodeblock and $_ eq "CODE END") { 401 $incodeblock = 0; 402 next; 403 } elsif ($incodeblock) { 404 if (exists $data->{override}) { 405 $data->{override}.="$_\n"; 406 } else { 407 $data->{override} = "$_\n"; 408 } 409 next; 410 } 411 412 my @fields = /([^ "]+|"[^"]+")/g; 413 414 my $cmd = $fields[0]; 415 416 shift @fields; 417 418 my $pos = { FILE => $f, LINE => $ln }; 419 420 next unless(defined($cmd)); 421 422 if (not defined($field_handlers{$cmd})) { 423 warning($pos, "Unknown command `$cmd'"); 424 next; 425 } 426 427 $field_handlers{$cmd}($pos, $data, @fields); 428 } 429 430 if ($incodeblock) { 431 warning({ FILE => $f, LINE => $ln }, 432 "Expecting CODE END"); 433 return undef; 434 } 435 436 return 1; 437} 438 4391; 440