diff options
author | Jelmer Vernooij <jelmer@samba.org> | 2005-05-25 13:50:27 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 13:17:01 -0500 |
commit | e427f58622e3d88c59953d6c1fb583acfb046213 (patch) | |
tree | 4fe708ec07cdcb85dd3af028e158505e065ca59e /source4/build/pidl/eth_parser.pm | |
parent | e7b3f91678a27d85791f7a62fc418988edc92214 (diff) | |
download | samba-e427f58622e3d88c59953d6c1fb583acfb046213.tar.gz samba-e427f58622e3d88c59953d6c1fb583acfb046213.tar.bz2 samba-e427f58622e3d88c59953d6c1fb583acfb046213.zip |
r6973: Merge new version of pidl into the main SAMBA_4_0 branch.
The main difference in this new version is the extra data structure generated
between the IDL data structure and the NDR parser:
IDL -> NDR -> { ndr_parser, ndr_header, eparser, etc }
This makes the ndr_parser.pm internals much more sane.
Other changes include:
- Remove unnecessary calls with NDR_BUFFERS (for example, GUID doesn't have any buffers, just scalars) as well as some (unnecessary) nested setting of flags.
- Parse array loops in the C code rather then calling ndr_pull_array(). This allows us to have, for example, arrays of pointers or arrays of pointers to arrays, etc..
- Use if() {} rather then if () goto foo; everywhere
- NDR_IN no longer implies LIBNDR_FLAG_REF_ALLOC
- By default, top level pointers are now "ref" (as is the default in
most other IDL compilers). This can be overridden using the
default_pointer_top() property.
- initial work on new ethereal parser generators by Alan DeKok and me
- pidl now writes errors in the standard format used by compilers, which
is parsable by most editors
- ability to warn about the fact that pidl extension(s) have been used,
useful for making sure IDL files work with other IDL compilers.
oh, and there's probably some other things I can't think of right now..
(This used to be commit 13cf227615f6b9e0e5fa62e59197024410254f01)
Diffstat (limited to 'source4/build/pidl/eth_parser.pm')
-rw-r--r-- | source4/build/pidl/eth_parser.pm | 1410 |
1 files changed, 1410 insertions, 0 deletions
diff --git a/source4/build/pidl/eth_parser.pm b/source4/build/pidl/eth_parser.pm new file mode 100644 index 0000000000..a2e1bf051c --- /dev/null +++ b/source4/build/pidl/eth_parser.pm @@ -0,0 +1,1410 @@ +################################################## +# Samba4 NDR parser generator for IDL structures +# Copyright tridge@samba.org 2000-2003 +# Copyright tpot@samba.org 2001 +# Copyright jelmer@samba.org 2004-2005 +# released under the GNU GPL + +package EthParser; + +use strict; +use typelist; +use ndr; + +# the list of needed functions + +# list of known types +my %typefamily; + + +sub NeededFunction($$) +{ + my $fn = shift; + my $needed = shift; + $needed->{"pull_$fn->{NAME}"} = 1; + foreach my $e (@{$fn->{ELEMENTS}}) { + $e->{PARENT} = $fn; + unless(defined($needed->{"pull_$e->{TYPE}"})) { + $needed->{"pull_$e->{TYPE}"} = 1; + } + + # for Ethereal + if (Ndr::is_scalar_type($e->{TYPE})) { + + if (defined($e->{ARRAY_LEN}) or + util::has_property($e, "size_is")) { + + # Array of scalar types + + $needed->{"hf_$fn->{NAME}_$e->{NAME}_array"} = { + 'name' => field2name($e->{NAME}), + 'type' => $e->{TYPE}, + 'ft' => "FT_BYTES", + 'base' => elementbase($e) + }; + + } else { + $needed->{"hf_$fn->{NAME}_$e->{NAME}"} = { + 'name' => field2name($e->{NAME}), + 'type' => $e->{TYPE}, + 'ft' => type2ft($e->{TYPE}), + 'base' => elementbase($e) + }; + } + } else { + $needed->{"hf_$fn->{NAME}_$e->{NAME}"} = { + 'name' => field2name($e->{NAME}), + 'type' => $e->{TYPE}, + 'ft' => type2ft($e->{TYPE}), + 'base' => elementbase($e) + }; + $needed->{"hf_$e->{TYPE}"} = { + 'name' => field2name($e->{NAME}), + 'type' => $e->{TYPE}, + 'ft' => type2ft($e->{TYPE}), + 'base' => elementbase($e) + }; + $needed->{"ett_$e->{TYPE}"} = 1; + } + } + + # Add entry for return value + if (defined($fn->{RETURN_TYPE})) { + $needed->{"hf_$fn->{NAME}_result"} = { + 'name' => field2name('result'), + 'type' => $fn->{RETURN_TYPE}, + 'ft' => type2ft($fn->{RETURN_TYPE}), + 'base' => elementbase($fn) + }; + } +} + +sub NeededTypedef($$) +{ + my $t = shift; + my $needed = shift; + + if (util::has_property($t, "public")) { + $needed->{"pull_$t->{NAME}"} = not util::has_property($t, "nopull"); + } + + if ($t->{DATA}->{TYPE} eq "STRUCT" or $t->{DATA}->{TYPE} eq "UNION") { + + for my $e (@{$t->{DATA}->{ELEMENTS}}) { + $e->{PARENT} = $t->{DATA}; + if ($needed->{"pull_$t->{NAME}"} and + not defined($needed->{"pull_$e->{TYPE}"})) { + $needed->{"pull_$e->{TYPE}"} = 1; + } + + if (Ndr::is_scalar_type($e->{TYPE})) { + if (defined($e->{ARRAY_LEN}) or + util::has_property($e, "size_is")) { + + # Arrays of scalar types are FT_BYTES + + $needed->{"hf_$t->{NAME}_$e->{NAME}_array"} = { + 'name' => field2name($e->{NAME}), + 'type' => $e->{TYPE}, + 'ft' => "FT_BYTES", + 'base' => elementbase($e) + }; + + } else { + $needed->{"hf_$t->{NAME}_$e->{NAME}"} = { + 'name' => field2name($e->{NAME}), + 'type' => $e->{TYPE}, + 'ft' => type2ft($e->{TYPE}), + 'base' => elementbase($e) + }; + } + + $e->{PARENT} = $t->{DATA}; + + if ($needed->{"pull_$t->{NAME}"}) { + $needed->{"pull_$e->{TYPE}"} = 1; + } + + } else { + $needed->{"hf_$t->{NAME}_$e->{NAME}"} = { + 'name' => field2name($e->{NAME}), + 'type' => $e->{TYPE}, + 'ft' => type2ft($e->{TYPE}), + 'base' => elementbase($e) + }; + $needed->{"ett_$e->{TYPE}"} = 1; + } + } + } + + if ($t->{DATA}->{TYPE} eq "ENUM") { + + $needed->{"hf_$t->{NAME}"} = { + 'name' => field2name($t->{NAME}), + 'ft' => 'FT_UINT16', + 'base' => 'BASE_DEC', + 'strings' => "VALS($t->{NAME}_vals)" + }; + $needed->{"ett_$t->{NAME}"} = 1; + } + + if ($t->{DATA}->{TYPE} eq "BITMAP") { + $needed->{BITMAPS}->{$t->{NAME}} = $t; + + foreach my $e (@{$t->{DATA}{ELEMENTS}}) { + $e =~ /^(.*?) \( (.*?) \)$/; + $needed->{"hf_$t->{NAME}_$1"} = { + 'name' => "$1", + 'ft' => "FT_BOOLEAN", + 'base' => bitmapbase($t), + 'bitmask' => "$2" + }; + } + $needed->{"ett_$t->{NAME}"} = 1; + } +} + +##################################################################### +# work out what parse functions are needed +sub NeededInterface($) +{ + my($interface) = shift; + my %needed = (); + + $needed{"hf_$interface->{NAME}_opnum"} = { + 'name' => "Operation", + 'ft' => "FT_UINT16", + 'base' => "BASE_DEC" + }; + + $needed{"ett_dcerpc_$interface->{NAME}"} = 1; + + foreach my $d (@{$interface->{FUNCTIONS}}) { + NeededFunction($d, \%needed); + } + foreach my $d (reverse @{$interface->{TYPEDEFS}}) { + NeededTypedef($d, \%needed); + } + + return \%needed; +} + +sub type2ft($) +{ + my($t) = shift; + + return "FT_UINT$1" if $t =~ /uint(8|16|32|64)/; + return "FT_INT$1" if $t =~ /int(8|16|32|64)/; + return "FT_UINT64", if $t eq "HYPER_T" or $t eq "NTTIME" + or $t eq "NTTIME_1sec" or $t eq "NTTIME_hyper" or $t eq "hyper"; + + # Type is an enum + + return "FT_UINT16"; +} + +# Determine the display base for an element + +sub elementbase($) +{ + my($e) = shift; + + if (my $base = util::has_property($e, "display")) { + return "BASE_" . uc($base); + } + + return "BASE_DEC", if $e->{TYPE} eq "ENUM"; + return "BASE_DEC", if $e->{TYPE} =~ /u?int(8|16|32|64)/; + return "BASE_DEC", if $e->{TYPE} eq "NTTIME" or $e->{TYPE} eq "HYPER_T"; + + # Probably an enum + + return "BASE_DEC"; +} + +# Convert a IDL structure field name (e.g access_mask) to a prettier +# string like 'Access Mask'. + +sub field2name($) +{ + my($field) = shift; + + $field =~ s/_/ /g; # Replace underscores with spaces + $field =~ s/(\w+)/\u\L$1/g; # Capitalise each word + + return $field; +} + +sub bitmapbase($) +{ + my $e = shift; + + return "16", if util::has_property($e->{DATA}, "bitmap16bit"); + return "8", if util::has_property($e->{DATA}, "bitmap8bit"); + + return "32"; +} + +sub get_typefamily($) +{ + my $n = shift; + return $typefamily{$n}; +} + +sub append_prefix($$) +{ + my $e = shift; + my $var_name = shift; + my $pointers = 0; + + foreach my $l (@{$e->{LEVELS}}) { + if ($l->{TYPE} eq "POINTER") { + $pointers++; + } elsif ($l->{TYPE} eq "ARRAY") { + if (($pointers == 0) and + (not $l->{IS_FIXED}) and + (not $l->{IS_INLINE})) { + return get_value_of($var_name) + } + } elsif ($l->{TYPE} eq "DATA") { + if ($l->{DATA_TYPE} eq "string" or + $l->{DATA_TYPE} eq "nbt_string") { + return get_value_of($var_name) unless ($pointers); + } + } + } + + return $var_name; +} + +# see if a variable needs to be allocated by the NDR subsystem on pull +sub need_alloc($) +{ + my $e = shift; + + return 0; +} + +sub get_pointer_to($) +{ + my $var_name = shift; + + if ($var_name =~ /^\*(.*)$/) { + return $1; + } elsif ($var_name =~ /^\&(.*)$/) { + return $var_name; +# return "&($var_name)"; + } else { + return "&$var_name"; + } +} + +sub get_value_of($) +{ + my $var_name = shift; + + if ($var_name =~ /^\&(.*)$/) { + return $1; + } else { + return "*$var_name"; + } +} + +my $res; +my $tabs = ""; +sub pidl($) +{ + my $d = shift; + if ($d) { + $res .= $tabs; + $res .= $d; + } + $res .="\n"; +} + +sub indent() +{ + $tabs .= "\t"; +} + +sub deindent() +{ + $tabs = substr($tabs, 0, -1); +} + +#################################################################### +# work out the name of a size_is() variable +sub ParseExpr($$) +{ + my($orig_expr) = shift; + my $varlist = shift; + + die("Undefined value in ParseExpr") if not defined($orig_expr); + + my $expr = $orig_expr; + + return $expr if (util::is_constant($expr)); + + my $prefix = ""; + my $postfix = ""; + + if ($expr =~ /\*(.*)/) { + $expr = $1; + $prefix = "*"; + } + + if ($expr =~ /^(.*)([\&\|\/+])(.*)$/) { + $postfix = $2.$3; + $expr = $1; + } + + if (defined($varlist->{$expr})) { + return $prefix.$varlist->{$expr}.$postfix; + } + + return $prefix.$expr.$postfix; +} + +##################################################################### +# check that a variable we get from ParseExpr isn't a null pointer +sub check_null_pointer($) +{ + my $size = shift; + if ($size =~ /^\*/) { + my $size2 = substr($size, 1); + pidl "if ($size2 == NULL) return NT_STATUS_INVALID_PARAMETER_MIX;"; + } +} + +##################################################################### +# check that a variable we get from ParseExpr isn't a null pointer +# void return varient +sub check_null_pointer_void($) +{ + my $size = shift; + if ($size =~ /^\*/) { + my $size2 = substr($size, 1); + pidl "if ($size2 == NULL) return;"; + } +} + +##################################################################### +# work out is a parse function should be declared static or not +sub fn_prefix($) +{ + my $fn = shift; + + return "" if (util::has_property($fn, "public")); + return "static "; +} + +################################################################### +# setup any special flags for an element or structure +sub start_flags($) +{ + my $e = shift; + my $flags = util::has_property($e, "flag"); + if (defined $flags) { + pidl "{ uint32_t _flags_save_$e->{TYPE} = ndr->flags;"; + pidl "ndr_set_flags(&ndr->flags, $flags);"; + indent; + } +} + +################################################################### +# end any special flags for an element or structure +sub end_flags($) +{ + my $e = shift; + my $flags = util::has_property($e, "flag"); + if (defined $flags) { + pidl "ndr->flags = _flags_save_$e->{TYPE};\n\t}"; + deindent; + } +} + +sub GenerateStructEnv($) +{ + my $x = shift; + my %env; + + foreach my $e (@{$x->{ELEMENTS}}) { + $env{$e->{NAME}} = "r->$e->{NAME}"; + } + + $env{"this"} = "r"; + + return \%env; +} + +sub GenerateFunctionEnv($) +{ + my $fn = shift; + my %env; + + foreach my $e (@{$fn->{ELEMENTS}}) { + if (grep (/out/, @{$e->{DIRECTION}})) { + $env{$e->{NAME}} = "r->out.$e->{NAME}"; + } + if (grep (/in/, @{$e->{DIRECTION}})) { + $env{$e->{NAME}} = "r->in.$e->{NAME}"; + } + } + + return \%env; +} + +##################################################################### +sub ParseArrayPreceding($$$) +{ + my $e = shift; + my $l = shift; + my $var_name = shift; + + return if ($l->{NO_METADATA}); + + # non fixed arrays encode the size just before the array + pidl "ndr_pull_array_size(ndr, tree, " . get_pointer_to($var_name) . ");"; +} + +sub compression_alg($$) +{ + my $e = shift; + my $l = shift; + my $compression = $l->{COMPRESSION}; + my ($alg, $clen, $dlen) = split(/ /, $compression); + + return $alg; +} + +sub compression_clen($$$) +{ + my $e = shift; + my $l = shift; + my $env = shift; + my $compression = $l->{COMPRESSION}; + my ($alg, $clen, $dlen) = split(/ /, $compression); + + return ParseExpr($clen, $env); +} + +sub compression_dlen($$$) +{ + my $e = shift; + my $l = shift; + my $env = shift; + my $compression = $l->{COMPRESSION}; + my ($alg, $clen, $dlen) = split(/ /, $compression); + + return ParseExpr($dlen, $env); +} + +sub ParseCompressionStart($$$$) +{ + my $e = shift; + my $l = shift; + my $subndr = shift; + my $env = shift; + my $comndr = $subndr."_compressed"; + my $alg = compression_alg($e, $l); + my $dlen = compression_dlen($e, $l, $env); + + pidl "{"; + indent; + pidl "struct pidl_pull *$comndr;"; + pidl "NDR_ALLOC($subndr, $comndr);"; + pidl "ndr_pull_compression($subndr, $comndr, $alg, $dlen);"; + + return $comndr; +} + +sub ParseCompressionEnd($$$) +{ + my $e = shift; + my $l = shift; + my $subndr = shift; + my $comndr = $subndr."_compressed"; + + deindent; + pidl "}"; +} + +sub ParseObfuscationStart($$) +{ + my $e = shift; + my $ndr = shift; + my $obfuscation = util::has_property($e, "obfuscation"); + + pidl "ndr_pull_obfuscation($ndr, $obfuscation);"; + + return $ndr; +} + +sub ParseObfuscationEnd($$) +{ + my $e = shift; + my $ndr = shift; + + # nothing to do here +} + +sub ParseSubcontextStart($$$$$$) +{ + my $e = shift; + my $l = shift; + my $ndr = shift; + my $var_name = shift; + my $ndr_flags = shift; + my $env = shift; + my $retndr = "_ndr_$e->{NAME}"; + + pidl "/* NDR_FLAGS $ndr_flags */"; + pidl "if ((ndr_flags) & NDR_SCALARS) {"; + indent; + pidl "struct pidl_pull *$retndr;"; + pidl "NDR_ALLOC(ndr, $retndr);"; + pidl "ndr_pull_subcontext_header($ndr, $l->{HEADER_SIZE}, $l->{SUBCONTEXT_SIZE}, $retndr);"; + + if (defined $l->{COMPRESSION}) { + $retndr = ParseCompressionStart($e, $l, $retndr, $env); + } + + if (defined $l->{OBFUSCATION}) { + $retndr = ParseObfuscationStart($e, $retndr); + } + + return ($retndr,$var_name); +} + +sub ParseSubcontextEnd($$) +{ + my $e = shift; + my $l = shift; + my $ndr = "_ndr_$e->{NAME}"; + + if (defined $l->{COMPRESSION}) { + ParseCompressionEnd($e, $l, $ndr); + } + + if (defined $l->{OBFUSCATION}) { + ParseObfuscationEnd($e, $ndr); + } + + my $advance; + if (defined($l->{SUBCONTEXT_SIZE}) and ($l->{SUBCONTEXT_SIZE} ne "-1")) { + $advance = $l->{SUBCONTEXT_SIZE}; + } elsif ($l->{HEADER_SIZE}) { + $advance = "$ndr->data_size"; + } else { + $advance = "$ndr->offset"; + } + pidl "ndr_pull_advance(ndr, $advance);"; + deindent; + pidl "}"; +} + +##################################################################### +# parse scalars in a structure element - pull size +sub ParseSwitch($$$$$$) +{ + my($e) = shift; + my $l = shift; + my $ndr = shift; + my($var_name) = shift; + my($ndr_flags) = shift; + my $env = shift; + my $switch_var = ParseExpr($l->{SWITCH_IS}, $env); + + check_null_pointer($switch_var); + + $var_name = get_pointer_to($var_name); + pidl "ndr_pull_set_switch_value($ndr, $var_name, $switch_var);"; + +} + +sub ParseData($$$$$) +{ + my $e = shift; + my $l = shift; + my $ndr = shift; + my $var_name = shift; + my $ndr_flags = shift; + + $var_name = get_pointer_to($var_name); + + # + # ALAND! for packet-dcerpc-lsa.c, uncommenting this code + # produces constructs like &(&r->string), to pass to another + # function, which gives compiler errors. + # + if ($l->{DATA_TYPE} eq "string" or + $l->{DATA_TYPE} eq "nbt_string") { + $var_name = get_pointer_to($var_name); + } + + pidl "offset += dissect_$l->{DATA_TYPE}(tvb, offset, pinfo, tree, drep, hf_FIXME, NULL);"; + + if (my $range = util::has_property($e, "range")) { + $var_name = get_value_of($var_name); + my ($low, $high) = split(/ /, $range, 2); + if (($l->{DATA_TYPE} =~ /^uint/) and ($low eq "0")) { + pidl "if ($var_name > $high) {"; + } else { + pidl "if ($var_name < $low || $var_name > $high) {"; + } + pidl "\treturn NT_STATUS_OK;"; + pidl "}"; + } +} + +sub CalcNdrFlags($$$) +{ + my $l = shift; + my $primitives = shift; + my $deferred = shift; + + my $scalars = 0; + my $buffers = 0; + + # Add NDR_SCALARS if this one is deferred + # and deferreds may be pushed + $scalars = 1 if ($l->{IS_DEFERRED} and $deferred); + + # Add NDR_SCALARS if this one is not deferred and + # primitives may be pushed + $scalars = 1 if (!$l->{IS_DEFERRED} and $primitives); + + # Add NDR_BUFFERS if this one contains deferred stuff + # and deferreds may be pushed + $buffers = 1 if ($l->{CONTAINS_DEFERRED} and $deferred); + + return "NDR_SCALARS|NDR_BUFFERS" if ($scalars and $buffers); + return "NDR_SCALARS" if ($scalars); + return "NDR_BUFFERS" if ($buffers); + return undef; +} + + +sub ParseElementLevel +{ + my($e) = shift; + my $l = shift; + my $ndr = shift; + my($var_name) = shift; + my $env = shift; + my $primitives = shift; + my $deferred = shift; + + my $ndr_flags = CalcNdrFlags($l, $primitives, $deferred); + + # Only pull something if there's actually something to be pulled + if (defined($ndr_flags)) { + if ($l->{TYPE} eq "SUBCONTEXT") { + ($ndr,$var_name) = ParseSubcontextStart($e, $l, $ndr, $var_name, $ndr_flags, $env); + ParseElementLevel($e,Ndr::GetNextLevel($e,$l), $ndr, $var_name, $env, $primitives, $deferred); + ParseSubcontextEnd($e, $l); + } elsif ($l->{TYPE} eq "ARRAY") { + my $length = ParseArrayHeader($e, $l, $ndr, $var_name, $env); + } elsif ($l->{TYPE} eq "POINTER") { + ParsePtr($e, $l, $ndr, $var_name); + } elsif ($l->{TYPE} eq "SWITCH") { + ParseSwitch($e, $l, $ndr, $var_name, $ndr_flags, $env); + } elsif ($l->{TYPE} eq "DATA") { + ParseData($e, $l, $ndr, $var_name, $ndr_flags); + } + } + + # add additional constructions + if ($l->{TYPE} eq "POINTER" and $deferred) { + if ($l->{POINTER_TYPE} ne "ref") { + pidl "if ($var_name) {"; + indent; + + if ($l->{POINTER_TYPE} eq "relative") { + pidl "struct ndr_pull_save _relative_save;"; + pidl "ndr_pull_save(ndr, &_relative_save);"; + pidl "NDR_CHECK(ndr_pull_relative_ptr2(ndr, $var_name));"; + } + } + + $var_name = get_value_of($var_name); + ParseElementLevel($e,Ndr::GetNextLevel($e,$l), $ndr, $var_name, $env, $primitives, $deferred); + + if ($l->{POINTER_TYPE} ne "ref") { + if ($l->{POINTER_TYPE} eq "relative") { + pidl "ndr_pull_restore(ndr, &_relative_save);"; + } + deindent; + pidl "}"; + } + } elsif ($l->{TYPE} eq "ARRAY") { + my $length = ParseExpr($l->{LENGTH_IS}, $env); + my $counter = "cntr_$e->{NAME}_$l->{LEVEL_INDEX}"; + + $var_name = $var_name . "[$counter]"; + unless ($l->{NO_METADATA}) { + $var_name = get_pointer_to($var_name); + } + + if (($primitives and not $l->{IS_DEFERRED}) or ($deferred and $l->{IS_DEFERRED})) { + pidl "for ($counter = 0; $counter < $length; $counter++) {"; + indent; + ParseElementLevel($e,Ndr::GetNextLevel($e,$l), $ndr, $var_name, $env, 1, 0); + deindent; + pidl "}"; + } + + if ($deferred and Ndr::ContainsDeferred($e, $l)) { + pidl "for ($counter = 0; $counter < $length; $counter++) {"; + indent; + ParseElementLevel($e,Ndr::GetNextLevel($e,$l), $ndr, $var_name, $env, 0, 1); + deindent; + pidl "}"; + } + } elsif ($l->{TYPE} eq "SWITCH") { + ParseElementLevel($e,Ndr::GetNextLevel($e,$l), $ndr, $var_name, $env, $primitives, $deferred); + } +} + +##################################################################### +# parse scalars in a structure element - pull size +sub ParseElement($$$$$$) +{ + my($e) = shift; + my $ndr = shift; + my($var_prefix) = shift; + my $env = shift; + my $primitives = shift; + my $deferred = shift; + + my $var_name = $var_prefix.$e->{NAME}; + + $var_name = append_prefix($e, $var_name); + + return unless $primitives or ($deferred and Ndr::ContainsDeferred($e, $e->{LEVELS}[0])); + + start_flags($e); + + ParseElementLevel($e,$e->{LEVELS}[0],$ndr,$var_name,$env,$primitives,$deferred); + + end_flags($e); +} + +##################################################################### +# parse a pointer in a struct element or function +sub ParsePtr($$$$) +{ + my($e) = shift; + my $l = shift; + my $ndr = shift; + my($var_name) = shift; + + my $nl = Ndr::GetNextLevel($e, $l); + my $next_is_array = ($nl->{TYPE} eq "ARRAY"); + + if ($l->{LEVEL} eq "EMBEDDED") { + pidl "dissect_ndr_embedded_pointer(FIXME);"; + } elsif ($l->{POINTER_TYPE} ne "ref") { + pidl "dissect_ndr_toplevel_pointer(FIXME);"; + } + + #pidl "memset($var_name, 0, sizeof($var_name));"; + if ($l->{POINTER_TYPE} eq "relative") { + pidl "ndr_pull_relative_ptr1($ndr, $var_name, _ptr_$e->{NAME});"; + } +} + +$typefamily{ENUM} = { + DECL => \&DeclEnum, +}; + +##################################################################### +# generate a pull function for an bitmap +sub ParseBitmap($$) +{ + my($bitmap) = shift; + my $name = shift; + my $type_fn = $bitmap->{BASE_TYPE}; + my($type_decl) = typelist::mapType($bitmap->{BASE_TYPE}); + + pidl "$type_decl v_bitmap;"; + start_flags($bitmap); + pidl "dissect_$type_fn(ndr, tree, hf, &v_bitmap);"; + + pidl "{\n\tproto_tree *subtree = NULL;"; + pidl ""; + pidl "\tif (tree->proto_tree)\n\t\tsubtree = proto_item_add_subtree(tree->proto_tree->last_child, ett_$name);"; + pidl ""; + foreach my $e (@{$bitmap->{DATA}{ELEMENTS}}) { + $e =~ /^(.*?) \( (.*?) \)$/; + pidl "\tproto_tree_add_boolean(subtree, hf_${name}_$1, ndr->tvb, ndr->offset - sizeof(v_bitmap), sizeof(v_bitmap), v_bitmap);"; + } + pidl "}"; + + pidl "*r = v_bitmap;"; + + end_flags($bitmap); +} + +$typefamily{BITMAP} = { + FN_BODY => \&ParseBitmap, +}; + +##################################################################### +# parse a struct - pull side +sub ParseStruct($$) +{ + my($struct) = shift; + my $name = shift; + my $conform_e; + + return unless defined $struct->{ELEMENTS}; + + my $env = GenerateStructEnv($struct); + + # see if the structure contains a conformant array. If it + # does, then it must be the last element of the structure, and + # we need to pull the conformant length early, as it fits on + # the wire before the structure (and even before the structure + # alignment) + $conform_e = $struct->{SURROUNDING_ELEMENT}; + + # declare any internal pointers we need + foreach my $e (@{$struct->{ELEMENTS}}) { + foreach my $l (@{$e->{LEVELS}}) { + if ($l->{TYPE} eq "POINTER" and not ($l->{POINTER_TYPE} eq "ref" and $l->{LEVEL} eq "TOP")) { + pidl "uint32_t _ptr_$e->{NAME};"; + last; + } + } + } + + start_flags($struct); + + pidl "if (ndr_flags & NDR_SCALARS) {"; + indent; + + if (defined $conform_e) { + ParseArrayPreceding($conform_e, $conform_e->{LEVELS}[0], "r->$conform_e->{NAME}"); + } + + pidl "ndr_pull_align(ndr, $struct->{ALIGN});"; + + foreach my $e (@{$struct->{ELEMENTS}}) { + ParseElement($e, "ndr", "r->", $env, 1, 0); + } + deindent; + pidl "}"; + + pidl "if (ndr_flags & NDR_BUFFERS) {"; + indent; + foreach my $e (@{$struct->{ELEMENTS}}) { + ParseElement($e, "ndr", "r->", $env, 0, 0); + } + + pidl "proto_item_set_end(tree->proto_tree, ndr->tvb, ndr->offset);"; + deindent; + pidl "}"; + + end_flags($struct); +} + +$typefamily{STRUCT} = { + FN_BODY => \&ParseStruct, +}; + +##################################################################### +# parse a union - pull side +sub ParseUnion($$$) +{ + my $e = shift; + my $name = shift; + my $have_default = 0; + my $switch_type = $e->{SWITCH_TYPE}; + + pidl "int level;"; + if (defined($switch_type)) { + if (typelist::typeIs($switch_type, "ENUM")) { + $switch_type = typelist::enum_type_fn(typelist::getType($switch_type)); + } + pidl typelist::mapType($switch_type) . " _level;"; + } + + start_flags($e); + + pidl "level = ndr_pull_get_switch_value(ndr, r);"; + + pidl "if (ndr_flags & NDR_SCALARS) {"; + indent; + + if (defined($switch_type)) { + pidl "ndr_pull_$switch_type(ndr, tree, hf_${name}, &_level);"; + pidl "if (_level != level) {"; + pidl "\treturn NT_STATUS_OK;"; + pidl "}"; + } + +# my $align = union_alignment($e); +# pidl "\tndr_pull_align(ndr, $align);\n"; + + pidl "switch (level) {"; + indent; + foreach my $el (@{$e->{ELEMENTS}}) { + if ($el->{CASE} eq "default") { + $have_default = 1; + } + pidl "$el->{CASE}: {"; + + if ($el->{TYPE} ne "EMPTY") { + indent; + foreach my $l (@{$el->{LEVELS}}) { + if ($l->{TYPE} eq "POINTER" and not ($l->{POINTER_TYPE} eq "ref" and $l->{LEVEL} eq "TOP")) { + pidl "uint32_t _ptr_$el->{NAME};"; + last; + } + } + ParseElement($el, "ndr", "r->", {}, 1, 0); + deindent; + } + pidl "break; }"; + pidl ""; + } + if (! $have_default) { + pidl "default:"; + pidl "\treturn NT_STATUS_OK;"; + } + deindent; + pidl "}"; + deindent; + pidl "}"; + pidl "if (ndr_flags & NDR_BUFFERS) {"; + indent; + pidl "switch (level) {"; + indent; + foreach my $el (@{$e->{ELEMENTS}}) { + pidl "$el->{CASE}:"; + if ($el->{TYPE} ne "EMPTY") { + indent; + ParseElement($el, "ndr", "r->", {}, 0, 1); + deindent; + } + pidl "break;"; + pidl ""; + } + if (! $have_default) { + pidl "default:"; + pidl "\treturn NT_STATUS_OK;"; + } + deindent; + pidl "}"; + pidl "proto_item_set_end(tree->proto_tree, ndr->tvb, ndr->offset);"; + deindent; + pidl "}"; + end_flags($e); +} + +$typefamily{UNION} = { + FN_BODY => \&ParseUnion, +}; + +##################################################################### +# parse an array +sub ParseArrayHeader($$$$$) +{ + my $e = shift; + my $l = shift; + my $ndr = shift; + my $var_name = shift; + my $env = shift; + + unless ($l->{NO_METADATA}) { + $var_name = get_pointer_to($var_name); + } + + # $var_name contains the name of the first argument here + + my $length = ParseExpr($l->{SIZE_IS}, $env); + my $size = $length; + + if ($l->{IS_CONFORMANT}) { + $length = $size = "ndr_get_array_size($ndr, " . get_pointer_to($var_name) . ")"; + } + + # if this is a conformant array then we use that size to allocate, and make sure + # we allocate enough to pull the elements + if (!$l->{IS_SURROUNDING}) { + ParseArrayPreceding($e, $l, $var_name); + } + + + if ($l->{IS_VARYING}) { + pidl "NDR_CHECK(ndr_pull_array_length($ndr, " . get_pointer_to($var_name) . "));"; + $length = "ndr_get_array_length($ndr, " . get_pointer_to($var_name) .")"; + } + + check_null_pointer($length); + + if ($length ne $size) { + pidl "if ($length > $size) {"; + indent; + pidl "return ndr_pull_error($ndr, NDR_ERR_ARRAY_SIZE, \"Bad array size %u should exceed array length %u\", $size, $length);"; + deindent; + pidl "}"; + } + + if ($l->{IS_CONFORMANT}) { + my $size = ParseExpr($l->{SIZE_IS}, $env); + check_null_pointer($size); + pidl "NDR_CHECK(ndr_check_array_size(ndr, (void*)" . get_pointer_to($var_name) . ", $size));"; + } + + if ($l->{IS_VARYING}) { + my $length = ParseExpr($l->{LENGTH_IS}, $env); + check_null_pointer($length); + pidl "NDR_CHECK(ndr_check_array_length(ndr, (void*)" . get_pointer_to($var_name) . ", $length));"; + } + + return $length; +} + +##################################################################### +# parse a typedef - pull side +sub ParseTypedef($) +{ + my($e) = shift; + + return unless (defined ($typefamily{$e->{DATA}->{TYPE}})); + + pidl fn_prefix($e) . "int dissect_$e->{NAME}(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, int hf_index, guint32 param)"; + + pidl "{"; + indent; + $typefamily{$e->{DATA}->{TYPE}}->{FN_BODY}->($e->{DATA}, $e->{NAME}); + pidl "return NT_STATUS_OK;"; + deindent; + pidl "}"; + pidl ""; +} + +##################################################################### +# parse a function +sub ParseFunctionRqst($) +{ + my($fn) = shift; + + my $env = GenerateFunctionEnv($fn); + + # request function + pidl "static int dissect_$fn->{NAME}_rqst(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, guint8 *drep)"; + pidl "{"; + indent; + + pidl "struct pidl_pull *ndr = pidl_pull_init(tvb, offset, pinfo, drep);"; + + # declare any internal pointers we need + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep (/in/, @{$e->{DIRECTIONS}})); + foreach my $l (@{$e->{LEVELS}}) { + if ($l->{TYPE} eq "POINTER" and + not ($l->{POINTER_TYPE} eq "ref" and + $l->{LEVEL} eq "TOP")) { + pidl "uint32_t _ptr_$e->{NAME};"; + last; + } + } + } + + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/in/, @{$e->{DIRECTION}})); + ParseElement($e, "ndr", "r->in.", $env, 1, 1); + } + + pidl "return offset;"; + deindent; + pidl "}"; + pidl ""; +} + +sub ParseFunctionResp($) +{ + my($fn) = shift; + + my $env = GenerateFunctionEnv($fn); + + # response function + pidl "static int dissect_$fn->{NAME}_resp(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, guint8 *drep)"; + pidl "{"; + indent; + + pidl "struct pidl_pull *ndr = pidl_pull_init(tvb, offset, pinfo, drep);"; + + # declare any internal pointers we need + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep (/out/, @{$e->{DIRECTIONS}})); + foreach my $l (@{$e->{LEVELS}}) { + if ($l->{TYPE} eq "POINTER" and + not ($l->{POINTER_TYPE} eq "ref" and + $l->{LEVEL} eq "TOP")) { + pidl "uint32_t _ptr_$e->{NAME};"; + last; + } + } + } + + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless grep(/out/, @{$e->{DIRECTION}}); + ParseElement($e, "ndr", "r->out.", $env, 1, 1); + } + + if ($fn->{RETURN_TYPE}) { + pidl "dissect_$fn->{RETURN_TYPE}(ndr, tree, hf_$fn->{NAME}_result, drep);"; + } + + pidl "return offset;"; + deindent; + pidl "}"; + pidl ""; +} + +##################################################################### +# produce a function call table +sub FunctionTable($) +{ + my($interface) = shift; + + pidl "static dcerpc_sub_dissector dcerpc_dissectors[] = {"; + my $num = 0; + foreach my $d (@{$interface->{FUNCTIONS}}) { + # Strip interface name from function name, if present + my($n) = $d->{NAME}; + $n = substr($d->{NAME}, length($interface->{NAME}) + 1), + if $interface->{NAME} eq substr($d->{NAME}, 0, length($interface->{NAME})); + pidl "\t{ $num, \"$n\","; + pidl "\t\tdissect_$d->{NAME}_rqst,"; + pidl "\t\tdissect_$d->{NAME}_resp },"; + $num++; + } + pidl "};\n"; +} + +##################################################################### +# parse the interface definitions +sub ParseInterface($$) +{ + my($interface) = shift; + my $needed = shift; + + # Typedefs + foreach my $d (@{$interface->{TYPEDEFS}}) { + ($needed->{"pull_$d->{NAME}"}) && ParseTypedef($d); + # Make sure we don't generate a function twice... + $needed->{"pull_$d->{NAME}"} = 0; + } + + # Functions + foreach my $d (@{$interface->{FUNCTIONS}}) { + if ($needed->{"pull_$d->{NAME}"}) { + ParseFunctionRqst($d); + ParseFunctionResp($d); + } + + # Make sure we don't generate a function twice... + $needed->{"pull_$d->{NAME}"} = 0; + } +} + +##################################################################### +# generate code to parse an enum +sub DeclEnum($$) +{ + my ($e) = shift; + my $n = shift; + + pidl "static const value_string $n\_vals[] ="; + pidl "{"; + + foreach my $x (@{$e->{ELEMENTS}}) { + $x =~ /([^=]*)=(.*)/; + pidl "\t{ $1, \"$1\" },"; + } + + pidl "};\n"; +} + +sub DeclInterface($$) +{ + my($interface) = shift; + my $needed = shift; + + # Typedefs + foreach my $d (@{$interface->{TYPEDEFS}}) { + ($needed->{"pull_$d->{NAME}"}) && DeclTypedef($d); + + # Make sure we don't generate a function twice... + $needed->{"pull_$d->{NAME}"} = 0; + } +} + +sub DeclTypedef($) +{ + my $e = shift; + + if (defined($typefamily{$e->{DATA}->{TYPE}}->{DECL})) { + $typefamily{$e->{DATA}->{TYPE}}->{DECL}->($e->{DATA}, $e->{NAME}); + } +} + +sub RegisterInterface($$) +{ + my $x = shift; + my $needed = shift; + + pidl "void proto_register_dcerpc_pidl_$x->{NAME}(void)"; + pidl "{"; + indent; + + pidl "static hf_register_info hf[] = {"; + pidl "{ &hf_ptr, { \"Pointer\", \"$x->{NAME}.ptr\", FT_UINT32, BASE_HEX, NULL, 0x0, \"Pointer\", HFILL }},"; + + foreach my $x (sort keys(%{$needed})) { + next, if !($x =~ /^hf_/); + pidl "{ &$x,"; + $needed->{$x}->{strings} = "NULL", if !defined($needed->{$x}->{strings}); + $needed->{$x}->{bitmask} = "0", if !defined($needed->{$x}->{bitmask}); + pidl " { \"$needed->{$x}->{name}\", \"$x\", $needed->{$x}->{ft}, $needed->{$x}->{base}, $needed->{$x}->{strings}, $needed->{$x}->{bitmask}, \"$x\", HFILL }},"; + } + + pidl "};\n"; + + pidl "static gint *ett[] = {"; + indent; + foreach my $x (sort keys(%{$needed})) { + pidl "&$x,", if $x =~ /^ett_/; + } + deindent; + + pidl "};\n"; + + if (defined($x->{UUID})) { + # These can be changed to non-pidl names if the old dissectors + # in epan/dissctors are deleted. + + my $name = "\"" . uc($x->{NAME}) . " (pidl)\""; + if (util::has_property($x, "helpstring")) { + $name = $x->{PROPERTIES}->{helpstring}; + } + my $short_name = "pidl_$x->{NAME}"; + my $filter_name = "pidl_$x->{NAME}"; + + pidl "proto_dcerpc_pidl_$x->{NAME} = proto_register_protocol($name, \"$short_name\", \"$filter_name\");"; + + pidl "proto_register_field_array(proto_dcerpc_pidl_$x->{NAME}, hf, array_length (hf));"; + pidl "proto_register_subtree_array(ett, array_length(ett));"; + } else { + pidl "int proto_dcerpc;"; + pidl "proto_dcerpc = proto_get_id_by_filter_name(\"dcerpc\");"; + pidl "proto_register_field_array(proto_dcerpc, hf, array_length(hf));"; + pidl "proto_register_subtree_array(ett, array_length(ett));"; + } + + deindent; + pidl "}\n"; +} + +sub RegisterInterfaceHandoff($) +{ + my $x = shift; + pidl "void proto_reg_handoff_dcerpc_pidl_$x->{NAME}(void)"; + pidl "{"; + indent; + pidl "dcerpc_init_uuid(proto_dcerpc_pidl_$x->{NAME}, ett_dcerpc_$x->{NAME},"; + pidl "\t&uuid_dcerpc_$x->{NAME}, ver_dcerpc_$x->{NAME},"; + pidl "\tdcerpc_dissectors, hf_opnum);"; + deindent; + pidl "}"; +} + +sub ProcessInterface($) +{ + my $x = shift; + my $needed = NeededInterface($x); + + # Required global declarations + DeclInterface($x, $needed); + + foreach my $y (sort keys(%{$needed})) { + pidl "static int $y = -1;", if $y =~ /^hf_/; + } + pidl ""; + + foreach my $y (sort keys(%{$needed})) { + pidl "static gint $y = -1;", if $y =~ /^ett_/; + } + pidl ""; + + pidl "int proto_dcerpc_pidl_$x->{NAME} = -1;\n"; + + if (defined($x->{UUID})) { + my $if_uuid = $x->{UUID}; + + pidl "static e_uuid_t uuid_dcerpc_$x->{NAME} = {"; + pidl "\t0x" . substr($if_uuid, 1, 8) + . ", 0x" . substr($if_uuid, 10, 4) + . ", 0x" . substr($if_uuid, 15, 4) . ","; + pidl "\t{ 0x" . substr($if_uuid, 20, 2) + . ", 0x" . substr($if_uuid, 22, 2) + . ", 0x" . substr($if_uuid, 25, 2) + . ", 0x" . substr($if_uuid, 27, 2) + . ", 0x" . substr($if_uuid, 29, 2) + . ", 0x" . substr($if_uuid, 31, 2) + . ", 0x" . substr($if_uuid, 33, 2) + . ", 0x" . substr($if_uuid, 35, 2) . " }"; + pidl "};\n"; + + pidl "static guint16 ver_dcerpc_$x->{NAME} = $x->{VERSION};"; + } + + # dissect_* functions + ParseInterface($x, $needed); + + # Function call tables + FunctionTable($x); + + RegisterInterface($x, $needed); + RegisterInterfaceHandoff($x); +} + +##################################################################### +# parse a parsed IDL structure back into an IDL file +sub Parse($$$) +{ + my($ndr) = shift; + my $module = shift; + my($filename) = shift; + + $tabs = ""; + my $h_filename = $filename; + $res = ""; + + if ($h_filename =~ /(.*)\.c/) { + $h_filename = "$1.h"; + } + + pidl "/* parser auto-generated by pidl */"; + pidl "#include \"packet-dcerpc.h\""; + pidl "#include \"$h_filename\""; + pidl ""; + pidl "static int hf_ptr = -1;"; + pidl "static int hf_array_size = -1;"; + pidl ""; + +# print keys %{$needed->{hf_atsvc_JobGetInfo_result}}, "\n"; + + # Ethereal protocol registration + + ProcessInterface($_) foreach (@$ndr); + + return $res; +} + +1; |