################################################## # 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 pidl::typelist; use pidl::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 $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"); $needed->{"decl_$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->{"decl_$e->{TYPE}"} = $needed->{"pull_$e->{TYPE}"} = 1; } $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 (typelist::scalar_is_reference($l->{DATA_TYPE})) { 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); } ##################################################################### # 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 util::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 util::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 = util::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; # # ALAND! for packet-dcerpc-lsa.c, uncommenting this code # produces constructs like &(&r->string), to pass to another # function, which gives compiler errors. # if (typelist::scalar_is_reference($l->{DATA_TYPE})) { $var_name = get_pointer_to($var_name); } $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 = util::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 = util::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 = util::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 = util::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->{"decl_$d->{NAME}"}) && DeclTypedef($d, $needed); } } sub DeclTypedef($$) { my $e = shift; my $needed = shift; if (defined($typefamily{$e->{DATA}->{TYPE}}->{DECL})) { $typefamily{$e->{DATA}->{TYPE}}->{DECL}->($e->{DATA}, $e->{NAME}); # Make sure we don't generate a function twice... $needed->{"decl_$e->{NAME}"} = 0; } } 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_$x->{NAME}_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;