################################################### # Samba4 NDR parser generator for IDL structures # Copyright tridge@samba.org 2000-2003 # Copyright tpot@samba.org 2001 # Copyright jelmer@samba.org 2004-2006 # released under the GNU GPL package Parse::Pidl::Samba4::NDR::Parser; require Exporter; @ISA = qw(Exporter); @EXPORT_OK = qw(check_null_pointer GenerateFunctionInEnv GenerateFunctionOutEnv EnvSubstituteValue GenerateStructEnv NeededFunction NeededElement NeededType $res NeededInterface TypeFunctionName ParseElementPrint); use strict; use Parse::Pidl::Typelist qw(hasType getType mapTypeName); use Parse::Pidl::Util qw(has_property ParseExpr ParseExprExt print_uuid); use Parse::Pidl::NDR qw(GetPrevLevel GetNextLevel ContainsDeferred); use Parse::Pidl::Samba4 qw(is_intree choose_header); use Parse::Pidl qw(warning); use vars qw($VERSION); $VERSION = '0.01'; # list of known types my %typefamily; sub new($$) { my ($class) = @_; my $self = { res => "", res_hdr => "", deferred => [], tabs => "", defer_tabs => "" }; bless($self, $class); } sub get_typefamily($) { my $n = shift; return $typefamily{$n}; } sub append_prefix($$) { my ($e, $var_name) = @_; 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 (Parse::Pidl::Typelist::scalar_is_reference($l->{DATA_TYPE})) { return get_value_of($var_name) unless ($pointers); } } } return $var_name; } sub has_fast_array($$) { my ($e,$l) = @_; return 0 if ($l->{TYPE} ne "ARRAY"); my $nl = GetNextLevel($e,$l); return 0 unless ($nl->{TYPE} eq "DATA"); return 0 unless (hasType($nl->{DATA_TYPE})); my $t = getType($nl->{DATA_TYPE}); # Only uint8 and string have fast array functions at the moment return ($t->{NAME} eq "uint8") or ($t->{NAME} eq "string"); } sub is_charset_array($$) { my ($e,$l) = @_; return 0 if ($l->{TYPE} ne "ARRAY"); my $nl = GetNextLevel($e,$l); return 0 unless ($nl->{TYPE} eq "DATA"); return has_property($e, "charset"); } sub get_pointer_to($) { my $var_name = shift; if ($var_name =~ /^\*(.*)$/) { return $1; } elsif ($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"; } } #################################### # pidl() is our basic output routine sub pidl($$) { my ($self, $d) = @_; if ($d) { $self->{res} .= $self->{tabs}; $self->{res} .= $d; } $self->{res} .="\n"; } sub pidl_hdr($$) { my ($self, $d) = @_; $self->{res_hdr} .= "$d\n"; } #################################### # defer() is like pidl(), but adds to # a deferred buffer which is then added to the # output buffer at the end of the structure/union/function # This is needed to cope with code that must be pushed back # to the end of a block of elements sub defer_indent($) { my ($self) = @_; $self->{defer_tabs}.="\t"; } sub defer_deindent($) { my ($self) = @_; $self->{defer_tabs}=substr($self->{defer_tabs}, 0, -1); } sub defer($$) { my ($self, $d) = @_; if ($d) { push(@{$self->{deferred}}, $self->{defer_tabs}.$d); } } ######################################## # add the deferred content to the current # output sub add_deferred($) { my ($self) = @_; $self->pidl($_) foreach (@{$self->{deferred}}); $self->{deferred} = []; $self->{defer_tabs} = ""; } sub indent($) { my ($self) = @_; $self->{tabs} .= "\t"; } sub deindent($) { my ($self) = @_; $self->{tabs} = substr($self->{tabs}, 0, -1); } ##################################################################### # declare a function public or static, depending on its attributes sub fn_declare($$$$) { my ($self,$type,$fn,$decl) = @_; if (has_property($fn, "no$type")) { $self->pidl_hdr("$decl;"); return 0; } if (has_property($fn, "public")) { $self->pidl_hdr("$decl;"); $self->pidl("_PUBLIC_ $decl"); } else { $self->pidl("static $decl"); } return 1; } ################################################################### # setup any special flags for an element or structure sub start_flags($$) { my ($self, $e) = @_; my $flags = has_property($e, "flag"); if (defined $flags) { $self->pidl("{"); $self->indent; $self->pidl("uint32_t _flags_save_$e->{TYPE} = ndr->flags;"); $self->pidl("ndr_set_flags(&ndr->flags, $flags);"); } } ################################################################### # end any special flags for an element or structure sub end_flags($$) { my ($self, $e) = @_; my $flags = has_property($e, "flag"); if (defined $flags) { $self->pidl("ndr->flags = _flags_save_$e->{TYPE};"); $self->deindent; $self->pidl("}"); } } sub GenerateStructEnv($$) { my ($x, $v) = @_; my %env; foreach my $e (@{$x->{ELEMENTS}}) { $env{$e->{NAME}} = "$v->$e->{NAME}"; } $env{"this"} = $v; return \%env; } sub EnvSubstituteValue($$) { my ($env,$s) = @_; # Substitute the value() values in the env foreach my $e (@{$s->{ELEMENTS}}) { next unless (defined(my $v = has_property($e, "value"))); $env->{$e->{NAME}} = ParseExpr($v, $env, $e); } return $env; } sub GenerateFunctionInEnv($;$) { my ($fn, $base) = @_; my %env; $base = "r->" unless defined($base); foreach my $e (@{$fn->{ELEMENTS}}) { if (grep (/in/, @{$e->{DIRECTION}})) { $env{$e->{NAME}} = $base."in.$e->{NAME}"; } } return \%env; } sub GenerateFunctionOutEnv($;$) { my ($fn, $base) = @_; my %env; $base = "r->" unless defined($base); foreach my $e (@{$fn->{ELEMENTS}}) { if (grep (/out/, @{$e->{DIRECTION}})) { $env{$e->{NAME}} = $base."out.$e->{NAME}"; } elsif (grep (/in/, @{$e->{DIRECTION}})) { $env{$e->{NAME}} = $base."in.$e->{NAME}"; } } return \%env; } ##################################################################### # parse the data of an array - push side sub ParseArrayPushHeader($$$$$$) { my ($self,$e,$l,$ndr,$var_name,$env) = @_; my $size; my $length; if ($l->{IS_ZERO_TERMINATED}) { if (has_property($e, "charset")) { $size = $length = "ndr_charset_length($var_name, CH_$e->{PROPERTIES}->{charset})"; } else { $size = $length = "ndr_string_length($var_name, sizeof(*$var_name))"; } } else { $size = ParseExpr($l->{SIZE_IS}, $env, $e); $length = ParseExpr($l->{LENGTH_IS}, $env, $e); } if ((!$l->{IS_SURROUNDING}) and $l->{IS_CONFORMANT}) { $self->pidl("NDR_CHECK(ndr_push_uint32($ndr, NDR_SCALARS, $size));"); } if ($l->{IS_VARYING}) { $self->pidl("NDR_CHECK(ndr_push_uint32($ndr, NDR_SCALARS, 0));"); # array offset $self->pidl("NDR_CHECK(ndr_push_uint32($ndr, NDR_SCALARS, $length));"); } return $length; } sub check_fully_dereferenced($$) { my ($element, $env) = @_; return sub ($) { my $origvar = shift; my $check = 0; # Figure out the number of pointers in $ptr my $expandedvar = $origvar; $expandedvar =~ s/^(\**)//; my $ptr = $1; my $var = undef; foreach (keys %$env) { if ($env->{$_} eq $expandedvar) { $var = $_; last; } } return($origvar) unless (defined($var)); my $e; foreach (@{$element->{PARENT}->{ELEMENTS}}) { if ($_->{NAME} eq $var) { $e = $_; last; } } $e or die("Environment doesn't match siblings"); # See if pointer at pointer level $level # needs to be checked. my $nump = 0; foreach (@{$e->{LEVELS}}) { if ($_->{TYPE} eq "POINTER") { $nump = $_->{POINTER_INDEX}+1; } } warning($element->{ORIGINAL}, "Got pointer for `$e->{NAME}', expected fully derefenced variable") if ($nump > length($ptr)); return ($origvar); } } sub check_null_pointer($$$$) { my ($element, $env, $print_fn, $return) = @_; return sub ($) { my $expandedvar = shift; my $check = 0; # Figure out the number of pointers in $ptr $expandedvar =~ s/^(\**)//; my $ptr = $1; my $var = undef; foreach (keys %$env) { if ($env->{$_} eq $expandedvar) { $var = $_; last; } } if (defined($var)) { my $e; # lookup ptr in $e foreach (@{$element->{PARENT}->{ELEMENTS}}) { if ($_->{NAME} eq $var) { $e = $_; last; } } $e or die("Environment doesn't match siblings"); # See if pointer at pointer level $level # needs to be checked. foreach my $l (@{$e->{LEVELS}}) { if ($l->{TYPE} eq "POINTER" and $l->{POINTER_INDEX} == length($ptr)) { # No need to check ref pointers $check = ($l->{POINTER_TYPE} ne "ref"); last; } if ($l->{TYPE} eq "DATA") { warning($element, "too much dereferences for `$var'"); } } } else { warning($element, "unknown dereferenced expression `$expandedvar'"); $check = 1; } $print_fn->("if ($ptr$expandedvar == NULL) $return") if $check; } } ##################################################################### # parse an array - pull side sub ParseArrayPullHeader($$$$$$) { my ($self,$e,$l,$ndr,$var_name,$env) = @_; my $length; my $size; if ($l->{IS_CONFORMANT}) { $length = $size = "ndr_get_array_size($ndr, " . get_pointer_to($var_name) . ")"; } elsif ($l->{IS_ZERO_TERMINATED}) { # Noheader arrays $length = $size = "ndr_get_string_size($ndr, sizeof(*$var_name))"; } else { $length = $size = ParseExprExt($l->{SIZE_IS}, $env, $e->{ORIGINAL}, check_null_pointer($e, $env, sub { $self->pidl(shift); }, "return NT_STATUS_INVALID_PARAMETER_MIX;"), check_fully_dereferenced($e, $env)); } if ((!$l->{IS_SURROUNDING}) and $l->{IS_CONFORMANT}) { $self->pidl("NDR_CHECK(ndr_pull_array_size(ndr, " . get_pointer_to($var_name) . "));"); } if ($l->{IS_VARYING}) { $self->pidl("NDR_CHECK(ndr_pull_array_length($ndr, " . get_pointer_to($var_name) . "));"); $length = "ndr_get_array_length($ndr, " . get_pointer_to($var_name) .")"; } if ($length ne $size) { $self->pidl("if ($length > $size) {"); $self->indent; $self->pidl("return ndr_pull_error($ndr, NDR_ERR_ARRAY_SIZE, \"Bad array size %u should exceed array length %u\", $size, $length);"); $self->deindent; $self->pidl("}"); } if ($l->{IS_CONFORMANT} and not $l->{IS_ZERO_TERMINATED}) { $self->defer("if ($var_name) {"); $self->defer_indent; my $size = ParseExprExt($l->{SIZE_IS}, $env, $e->{ORIGINAL}, check_null_pointer($e, $env, sub { $self->defer(shift); }, "return NT_STATUS_INVALID_PARAMETER_MIX;"), check_fully_dereferenced($e, $env)); $self->defer("NDR_CHECK(ndr_check_array_size(ndr, (void*)" . get_pointer_to($var_name) . ", $size));"); $self->defer_deindent; $self->defer("}"); } if ($l->{IS_VARYING} and not $l->{IS_ZERO_TERMINATED}) { $self->defer("if ($var_name) {"); $self->defer_indent; my $length = ParseExprExt($l->{LENGTH_IS}, $env, $e->{ORIGINAL}, check_null_pointer($e, $env, sub { $self->defer(shift); }, "return NT_STATUS_INVALID_PARAMETER_MIX;"), check_fully_dereferenced($e, $env)); $self->defer("NDR_CHECK(ndr_check_array_length(ndr, (void*)" . get_pointer_to($var_name) . ", $length));"); $self->defer_deindent; $self->defer("}"); } if (not $l->{IS_FIXED} and not is_charset_array($e, $l)) { $self->AllocateArrayLevel($e,$l,$ndr,$env,$size); } return $length; } sub compression_alg($$) { my ($e, $l) = @_; my ($alg, $clen, $dlen) = split(/ /, $l->{COMPRESSION}); return $alg; } sub compression_clen($$$) { my ($e, $l, $env) = @_; my ($alg, $clen, $dlen) = split(/ /, $l->{COMPRESSION}); return ParseExpr($clen, $env, $e->{ORIGINAL}); } sub compression_dlen($$$) { my ($e,$l,$env) = @_; my ($alg, $clen, $dlen) = split(/ /, $l->{COMPRESSION}); return ParseExpr($dlen, $env, $e->{ORIGINAL}); } sub ParseCompressionPushStart($$$$$) { my ($self,$e,$l,$ndr,$env) = @_; my $comndr = "$ndr\_compressed"; my $alg = compression_alg($e, $l); my $dlen = compression_dlen($e, $l, $env); $self->pidl("{"); $self->indent; $self->pidl("struct ndr_push *$comndr;"); $self->pidl("NDR_CHECK(ndr_push_compression_start($ndr, &$comndr, $alg, $dlen));"); return $comndr; } sub ParseCompressionPushEnd($$$$$) { my ($self,$e,$l,$ndr,$env) = @_; my $comndr = "$ndr\_compressed"; my $alg = compression_alg($e, $l); my $dlen = compression_dlen($e, $l, $env); $self->pidl("NDR_CHECK(ndr_push_compression_end($ndr, $comndr, $alg, $dlen));"); $self->deindent; $self->pidl("}"); } sub ParseCompressionPullStart($$$$$) { my ($self,$e,$l,$ndr,$env) = @_; my $comndr = "$ndr\_compressed"; my $alg = compression_alg($e, $l); my $dlen = compression_dlen($e, $l, $env); $self->pidl("{"); $self->indent; $self->pidl("struct ndr_pull *$comndr;"); $self->pidl("NDR_CHECK(ndr_pull_compression_start($ndr, &$comndr, $alg, $dlen));"); return $comndr; } sub ParseCompressionPullEnd($$$$$) { my ($self,$e,$l,$ndr,$env) = @_; my $comndr = "$ndr\_compressed"; my $alg = compression_alg($e, $l); my $dlen = compression_dlen($e, $l, $env); $self->pidl("NDR_CHECK(ndr_pull_compression_end($ndr, $comndr, $alg, $dlen));"); $self->deindent; $self->pidl("}"); } sub ParseSubcontextPushStart($$$$$) { my ($self,$e,$l,$ndr,$env) = @_; my $subndr = "_ndr_$e->{NAME}"; my $subcontext_size = ParseExpr($l->{SUBCONTEXT_SIZE}, $env, $e->{ORIGINAL}); $self->pidl("{"); $self->indent; $self->pidl("struct ndr_push *$subndr;"); $self->pidl("NDR_CHECK(ndr_push_subcontext_start($ndr, &$subndr, $l->{HEADER_SIZE}, $subcontext_size));"); if (defined $l->{COMPRESSION}) { $subndr = $self->ParseCompressionPushStart($e, $l, $subndr, $env); } return $subndr; } sub ParseSubcontextPushEnd($$$$$) { my ($self,$e,$l,$ndr,$env) = @_; my $subndr = "_ndr_$e->{NAME}"; my $subcontext_size = ParseExpr($l->{SUBCONTEXT_SIZE}, $env, $e->{ORIGINAL}); if (defined $l->{COMPRESSION}) { $self->ParseCompressionPushEnd($e, $l, $subndr, $env); } $self->pidl("NDR_CHECK(ndr_push_subcontext_end($ndr, $subndr, $l->{HEADER_SIZE}, $subcontext_size));"); $self->deindent; $self->pidl("}"); } sub ParseSubcontextPullStart($$$$$) { my ($self,$e,$l,$ndr,$env) = @_; my $subndr = "_ndr_$e->{NAME}"; my $subcontext_size = ParseExpr($l->{SUBCONTEXT_SIZE}, $env, $e->{ORIGINAL}); $self->pidl("{"); $self->indent; $self->pidl("struct ndr_pull *$subndr;"); $self->pidl("NDR_CHECK(ndr_pull_subcontext_start($ndr, &$subndr, $l->{HEADER_SIZE}, $subcontext_size));"); if (defined $l->{COMPRESSION}) { $subndr = $self->ParseCompressionPullStart($e, $l, $subndr, $env); } return $subndr; } sub ParseSubcontextPullEnd($$$$$) { my ($self,$e,$l,$ndr,$env) = @_; my $subndr = "_ndr_$e->{NAME}"; my $subcontext_size = ParseExpr($l->{SUBCONTEXT_SIZE}, $env, $e->{ORIGINAL}); if (defined $l->{COMPRESSION}) { $self->ParseCompressionPullEnd($e, $l, $subndr, $env); } $self->pidl("NDR_CHECK(ndr_pull_subcontext_end($ndr, $subndr, $l->{HEADER_SIZE}, $subcontext_size));"); $self->deindent; $self->pidl("}"); } sub ParseElementPushLevel { my ($self,$e,$l,$ndr,$var_name,$env,$primitives,$deferred) = @_; my $ndr_flags = CalcNdrFlags($l, $primitives, $deferred); if ($l->{TYPE} eq "ARRAY" and ($l->{IS_CONFORMANT} or $l->{IS_VARYING})) { $var_name = get_pointer_to($var_name); } if (defined($ndr_flags)) { if ($l->{TYPE} eq "SUBCONTEXT") { my $subndr = $self->ParseSubcontextPushStart($e, $l, $ndr, $env); $self->ParseElementPushLevel($e, GetNextLevel($e, $l), $subndr, $var_name, $env, 1, 1); $self->ParseSubcontextPushEnd($e, $l, $ndr, $env); } elsif ($l->{TYPE} eq "POINTER") { $self->ParsePtrPush($e, $l, $var_name); } elsif ($l->{TYPE} eq "ARRAY") { my $length = $self->ParseArrayPushHeader($e, $l, $ndr, $var_name, $env); my $nl = GetNextLevel($e, $l); # Allow speedups for arrays of scalar types if (is_charset_array($e,$l)) { $self->pidl("NDR_CHECK(ndr_push_charset($ndr, $ndr_flags, $var_name, $length, sizeof(" . mapTypeName($nl->{DATA_TYPE}) . "), CH_$e->{PROPERTIES}->{charset}));"); return; } elsif (has_fast_array($e,$l)) { $self->pidl("NDR_CHECK(ndr_push_array_$nl->{DATA_TYPE}($ndr, $ndr_flags, $var_name, $length));"); return; } } elsif ($l->{TYPE} eq "SWITCH") { $self->ParseSwitchPush($e, $l, $ndr, $var_name, $env); } elsif ($l->{TYPE} eq "DATA") { $self->ParseDataPush($e, $l, $ndr, $var_name, $primitives, $deferred); } } if ($l->{TYPE} eq "POINTER" and $deferred) { if ($l->{POINTER_TYPE} ne "ref") { $self->pidl("if ($var_name) {"); $self->indent; if ($l->{POINTER_TYPE} eq "relative") { $self->pidl("NDR_CHECK(ndr_push_relative_ptr2(ndr, $var_name));"); } } $var_name = get_value_of($var_name); $self->ParseElementPushLevel($e, GetNextLevel($e, $l), $ndr, $var_name, $env, 1, 1); if ($l->{POINTER_TYPE} ne "ref") { $self->deindent; $self->pidl("}"); } } elsif ($l->{TYPE} eq "ARRAY" and not has_fast_array($e,$l) and not is_charset_array($e, $l)) { my $length = ParseExpr($l->{LENGTH_IS}, $env, $e->{ORIGINAL}); my $counter = "cntr_$e->{NAME}_$l->{LEVEL_INDEX}"; $var_name = $var_name . "[$counter]"; if (($primitives and not $l->{IS_DEFERRED}) or ($deferred and $l->{IS_DEFERRED})) { $self->pidl("for ($counter = 0; $counter < $length; $counter++) {"); $self->indent; $self->ParseElementPushLevel($e, GetNextLevel($e, $l), $ndr, $var_name, $env, 1, 0); $self->deindent; $self->pidl("}"); } if ($deferred and ContainsDeferred($e, $l)) { $self->pidl("for ($counter = 0; $counter < $length; $counter++) {"); $self->indent; $self->ParseElementPushLevel($e, GetNextLevel($e, $l), $ndr, $var_name, $env, 0, 1); $self->deindent; $self->pidl("}"); } } elsif ($l->{TYPE} eq "SWITCH") { $self->ParseElementPushLevel($e, GetNextLevel($e, $l), $ndr, $var_name, $env, $primitives, $deferred); } } ##################################################################### # parse scalars in a structure element sub ParseElementPush($$$$$$) { my ($self,$e,$ndr,$env,$primitives,$deferred) = @_; my $subndr = undef; my $var_name = $env->{$e->{NAME}}; return unless $primitives or ($deferred and ContainsDeferred($e, $e->{LEVELS}[0])); # Representation type is different from transmit_as if ($e->{REPRESENTATION_TYPE} ne $e->{TYPE}) { $self->pidl("{"); $self->indent; my $transmit_name = "_transmit_$e->{NAME}"; $self->pidl(mapTypeName($e->{TYPE}) ." $transmit_name;"); $self->pidl("NDR_CHECK(ndr_$e->{REPRESENTATION_TYPE}_to_$e->{TYPE}($var_name, " . get_pointer_to($transmit_name) . "));"); $var_name = $transmit_name; } $var_name = append_prefix($e, $var_name); $self->start_flags($e); if (defined(my $value = has_property($e, "value"))) { $var_name = ParseExpr($value, $env, $e->{ORIGINAL}); } $self->ParseElementPushLevel($e, $e->{LEVELS}[0], $ndr, $var_name, $env, $primitives, $deferred); $self->end_flags($e); if ($e->{REPRESENTATION_TYPE} ne $e->{TYPE}) { $self->deindent; $self->pidl("}"); } } ##################################################################### # parse a pointer in a struct element or function sub ParsePtrPush($$$$) { my ($self,$e,$l,$var_name) = @_; if ($l->{POINTER_TYPE} eq "ref") { $self->pidl("if ($var_name == NULL) return NT_STATUS_INVALID_PARAMETER_MIX;"); if ($l->{LEVEL} eq "EMBEDDED") { $self->pidl("NDR_CHECK(ndr_push_ref_ptr(ndr));"); } } elsif ($l->{POINTER_TYPE} eq "relative") { $self->pidl("NDR_CHECK(ndr_push_relative_ptr1(ndr, $var_name));"); } elsif ($l->{POINTER_TYPE} eq "unique") { $self->pidl("NDR_CHECK(ndr_push_unique_ptr(ndr, $var_name));"); } elsif ($l->{POINTER_TYPE} eq "full") { $self->pidl("NDR_CHECK(ndr_push_full_ptr(ndr, $var_name));"); } else { die("Unhandled pointer type $l->{POINTER_TYPE}"); } } sub ParseDataPrint($$$$) { my ($self, $e, $l, $var_name) = @_; if (not ref($l->{DATA_TYPE}) or defined($l->{DATA_TYPE}->{NAME})) { my $t; if (ref($l->{DATA_TYPE})) { $t = "$l->{DATA_TYPE}->{TYPE}_$l->{DATA_TYPE}->{NAME}"; } else { $t = $l->{DATA_TYPE}; } if (not Parse::Pidl::Typelist::is_scalar($t) or Parse::Pidl::Typelist::scalar_is_reference($t)) { $var_name = get_pointer_to($var_name); } $self->pidl("ndr_print_$t(ndr, \"$e->{NAME}\", $var_name);"); } else { $self->ParseTypePrint($l->{DATA_TYPE}, $var_name); } } ##################################################################### # print scalars in a structure element sub ParseElementPrint($$$$) { my($self, $e, $var_name, $env) = @_; return if (has_property($e, "noprint")); if ($e->{REPRESENTATION_TYPE} ne $e->{TYPE}) { $self->pidl("ndr_print_$e->{REPRESENTATION_TYPE}(ndr, \"$e->{NAME}\", $var_name);"); return; } $var_name = append_prefix($e, $var_name); if (defined(my $value = has_property($e, "value"))) { $var_name = "(ndr->flags & LIBNDR_PRINT_SET_VALUES)?" . ParseExpr($value,$env, $e->{ORIGINAL}) . ":$var_name"; } foreach my $l (@{$e->{LEVELS}}) { if ($l->{TYPE} eq "POINTER") { $self->pidl("ndr_print_ptr(ndr, \"$e->{NAME}\", $var_name);"); $self->pidl("ndr->depth++;"); if ($l->{POINTER_TYPE} ne "ref") { $self->pidl("if ($var_name) {"); $self->indent; } $var_name = get_value_of($var_name); } elsif ($l->{TYPE} eq "ARRAY") { my $length; if ($l->{IS_CONFORMANT} or $l->{IS_VARYING}) { $var_name = get_pointer_to($var_name); } if ($l->{IS_ZERO_TERMINATED}) { $length = "ndr_string_length($var_name, sizeof(*$var_name))"; } else { $length = ParseExprExt($l->{LENGTH_IS}, $env, $e->{ORIGINAL}, check_null_pointer($e, $env, sub { $self->pidl(shift); }, "return;"), check_fully_dereferenced($e, $env)); } if (is_charset_array($e,$l)) { $self->pidl("ndr_print_string(ndr, \"$e->{NAME}\", $var_name);"); last; } elsif (has_fast_array($e, $l)) { my $nl = GetNextLevel($e, $l); $self->pidl("ndr_print_array_$nl->{DATA_TYPE}(ndr, \"$e->{NAME}\", $var_name, $length);"); last; } else { my $counter = "cntr_$e->{NAME}_$l->{LEVEL_INDEX}"; $self->pidl("ndr->print(ndr, \"\%s: ARRAY(\%d)\", \"$e->{NAME}\", $length);"); $self->pidl("ndr->depth++;"); $self->pidl("for ($counter=0;$counter<$length;$counter++) {"); $self->indent; $self->pidl("char *idx_$l->{LEVEL_INDEX}=NULL;"); $self->pidl("asprintf(&idx_$l->{LEVEL_INDEX}, \"[\%d]\", $counter);"); $self->pidl("if (idx_$l->{LEVEL_INDEX}) {"); $self->indent; $var_name = $var_name . "[$counter]"; } } elsif ($l->{TYPE} eq "DATA") { $self->ParseDataPrint($e, $l, $var_name); } elsif ($l->{TYPE} eq "SWITCH") { my $switch_var = ParseExprExt($l->{SWITCH_IS}, $env, $e->{ORIGINAL}, check_null_pointer($e, $env, sub { $self->pidl(shift); }, "return;"), check_fully_dereferenced($e, $env)); $self->pidl("ndr_print_set_switch_value(ndr, " . get_pointer_to($var_name) . ", $switch_var);"); } } foreach my $l (reverse @{$e->{LEVELS}}) { if ($l->{TYPE} eq "POINTER") { if ($l->{POINTER_TYPE} ne "ref") { $self->deindent; $self->pidl("}"); } $self->pidl("ndr->depth--;"); } elsif (($l->{TYPE} eq "ARRAY") and not is_charset_array($e,$l) and not has_fast_array($e,$l)) { $self->pidl("free(idx_$l->{LEVEL_INDEX});"); $self->deindent; $self->pidl("}"); $self->deindent; $self->pidl("}"); $self->pidl("ndr->depth--;"); } } } ##################################################################### # parse scalars in a structure element - pull size sub ParseSwitchPull($$$$$$) { my($self,$e,$l,$ndr,$var_name,$env) = @_; my $switch_var = ParseExprExt($l->{SWITCH_IS}, $env, $e->{ORIGINAL}, check_null_pointer($e, $env, sub { $self->pidl(shift); }, "return NT_STATUS_INVALID_PARAMETER_MIX;"), check_fully_dereferenced($e, $env)); $var_name = get_pointer_to($var_name); $self->pidl("NDR_CHECK(ndr_pull_set_switch_value($ndr, $var_name, $switch_var));"); } ##################################################################### # push switch element sub ParseSwitchPush($$$$$$) { my($self,$e,$l,$ndr,$var_name,$env) = @_; my $switch_var = ParseExprExt($l->{SWITCH_IS}, $env, $e->{ORIGINAL}, check_null_pointer($e, $env, sub { $self->pidl(shift); }, "return NT_STATUS_INVALID_PARAMETER_MIX;"), check_fully_dereferenced($e, $env)); $var_name = get_pointer_to($var_name); $self->pidl("NDR_CHECK(ndr_push_set_switch_value($ndr, $var_name, $switch_var));"); } sub ParseDataPull($$$$$$$) { my ($self,$e,$l,$ndr,$var_name,$primitives,$deferred) = @_; if (not ref($l->{DATA_TYPE}) or defined($l->{DATA_TYPE}->{NAME})) { my $ndr_flags = CalcNdrFlags($l, $primitives, $deferred); if (Parse::Pidl::Typelist::scalar_is_reference($l->{DATA_TYPE})) { $var_name = get_pointer_to($var_name); } $var_name = get_pointer_to($var_name); $self->pidl("NDR_CHECK(".TypeFunctionName("ndr_pull", $l->{DATA_TYPE})."($ndr, $ndr_flags, $var_name));"); if (my $range = has_property($e, "range")) { $var_name = get_value_of($var_name); my ($low, $high) = split(/ /, $range, 2); $self->pidl("if ($var_name < $low || $var_name > $high) {"); $self->pidl("\treturn ndr_pull_error($ndr, NDR_ERR_RANGE, \"value out of range\");"); $self->pidl("}"); } } else { $self->ParseTypePull($l->{DATA_TYPE}, $var_name, $primitives, $deferred); } } sub ParseDataPush($$$$$$$) { my ($self,$e,$l,$ndr,$var_name,$primitives,$deferred) = @_; if (not ref($l->{DATA_TYPE}) or defined($l->{DATA_TYPE}->{NAME})) { my $t; if (ref($l->{DATA_TYPE}) eq "HASH") { $t = "$l->{DATA_TYPE}->{TYPE}_$l->{DATA_TYPE}->{NAME}"; } else { $t = $l->{DATA_TYPE}; } # strings are passed by value rather than reference if (not Parse::Pidl::Typelist::is_scalar($t) or Parse::Pidl::Typelist::scalar_is_reference($t)) { $var_name = get_pointer_to($var_name); } my $ndr_flags = CalcNdrFlags($l, $primitives, $deferred); $self->pidl("NDR_CHECK(ndr_push_$t($ndr, $ndr_flags, $var_name));"); } else { $self->ParseTypePush($l->{DATA_TYPE}, $var_name, $primitives, $deferred); } } sub CalcNdrFlags($$$) { my ($l,$primitives,$deferred) = @_; 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 ParseMemCtxPullStart($$$$) { my ($self, $e, $l, $ptr_name) = @_; my $mem_r_ctx = "_mem_save_$e->{NAME}_$l->{LEVEL_INDEX}"; my $mem_c_ctx = $ptr_name; my $mem_c_flags = "0"; return if ($l->{TYPE} eq "ARRAY" and $l->{IS_FIXED}); if (($l->{TYPE} eq "POINTER") and ($l->{POINTER_TYPE} eq "ref")) { my $nl = GetNextLevel($e, $l); my $next_is_array = ($nl->{TYPE} eq "ARRAY"); my $next_is_string = (($nl->{TYPE} eq "DATA") and ($nl->{DATA_TYPE} eq "string")); if ($next_is_array or $next_is_string) { return; } else { $mem_c_flags = "LIBNDR_FLAG_REF_ALLOC"; } } $self->pidl("$mem_r_ctx = NDR_PULL_GET_MEM_CTX(ndr);"); $self->pidl("NDR_PULL_SET_MEM_CTX(ndr, $mem_c_ctx, $mem_c_flags);"); } sub ParseMemCtxPullEnd($$$) { my ($self, $e, $l) = @_; my $mem_r_ctx = "_mem_save_$e->{NAME}_$l->{LEVEL_INDEX}"; my $mem_r_flags = "0"; return if ($l->{TYPE} eq "ARRAY" and $l->{IS_FIXED}); if (($l->{TYPE} eq "POINTER") and ($l->{POINTER_TYPE} eq "ref")) { my $nl = GetNextLevel($e, $l); my $next_is_array = ($nl->{TYPE} eq "ARRAY"); my $next_is_string = (($nl->{TYPE} eq "DATA") and ($nl->{DATA_TYPE} eq "string")); if ($next_is_array or $next_is_string) { return; } else { $mem_r_flags = "LIBNDR_FLAG_REF_ALLOC"; } } $self->pidl("NDR_PULL_SET_MEM_CTX(ndr, $mem_r_ctx, $mem_r_flags);"); } sub CheckStringTerminator($$$$$) { my ($self,$ndr,$e,$l,$length) = @_; my $nl = GetNextLevel($e, $l); # Make sure last element is zero! $self->pidl("NDR_CHECK(ndr_check_string_terminator($ndr, $length, sizeof($nl->{DATA_TYPE}_t)));"); } sub ParseElementPullLevel { my($self,$e,$l,$ndr,$var_name,$env,$primitives,$deferred) = @_; my $ndr_flags = CalcNdrFlags($l, $primitives, $deferred); if ($l->{TYPE} eq "ARRAY" and ($l->{IS_VARYING} or $l->{IS_CONFORMANT})) { $var_name = get_pointer_to($var_name); } # Only pull something if there's actually something to be pulled if (defined($ndr_flags)) { if ($l->{TYPE} eq "SUBCONTEXT") { my $subndr = $self->ParseSubcontextPullStart($e, $l, $ndr, $env); $self->ParseElementPullLevel($e, GetNextLevel($e,$l), $subndr, $var_name, $env, 1, 1); $self->ParseSubcontextPullEnd($e, $l, $ndr, $env); } elsif ($l->{TYPE} eq "ARRAY") { my $length = $self->ParseArrayPullHeader($e, $l, $ndr, $var_name, $env); my $nl = GetNextLevel($e, $l); if (is_charset_array($e,$l)) { if ($l->{IS_ZERO_TERMINATED}) { $self->CheckStringTerminator($ndr, $e, $l, $length); } $self->pidl("NDR_CHECK(ndr_pull_charset($ndr, $ndr_flags, ".get_pointer_to($var_name).", $length, sizeof(" . mapTypeName($nl->{DATA_TYPE}) . "), CH_$e->{PROPERTIES}->{charset}));"); return; } elsif (has_fast_array($e, $l)) { if ($l->{IS_ZERO_TERMINATED}) { $self->CheckStringTerminator($ndr,$e,$l,$length); } $self->pidl("NDR_CHECK(ndr_pull_array_$nl->{DATA_TYPE}($ndr, $ndr_flags, $var_name, $length));"); return; } } elsif ($l->{TYPE} eq "POINTER") { $self->ParsePtrPull($e, $l, $ndr, $var_name); } elsif ($l->{TYPE} eq "SWITCH") { $self->ParseSwitchPull($e, $l, $ndr, $var_name, $env); } elsif ($l->{TYPE} eq "DATA") { $self->ParseDataPull($e, $l, $ndr, $var_name, $primitives, $deferred); } } # add additional constructions if ($l->{TYPE} eq "POINTER" and $deferred) { if ($l->{POINTER_TYPE} ne "ref") { $self->pidl("if ($var_name) {"); $self->indent; if ($l->{POINTER_TYPE} eq "relative") { $self->pidl("struct ndr_pull_save _relative_save;"); $self->pidl("ndr_pull_save(ndr, &_relative_save);"); $self->pidl("NDR_CHECK(ndr_pull_relative_ptr2(ndr, $var_name));"); } } $self->ParseMemCtxPullStart($e, $l, $var_name); $var_name = get_value_of($var_name); $self->ParseElementPullLevel($e, GetNextLevel($e,$l), $ndr, $var_name, $env, 1, 1); $self->ParseMemCtxPullEnd($e,$l); if ($l->{POINTER_TYPE} ne "ref") { if ($l->{POINTER_TYPE} eq "relative") { $self->pidl("ndr_pull_restore(ndr, &_relative_save);"); } $self->deindent; $self->pidl("}"); } } elsif ($l->{TYPE} eq "ARRAY" and not has_fast_array($e,$l) and not is_charset_array($e, $l)) { my $length = ParseExpr($l->{LENGTH_IS}, $env, $e->{ORIGINAL}); my $counter = "cntr_$e->{NAME}_$l->{LEVEL_INDEX}"; my $array_name = $var_name; $var_name = $var_name . "[$counter]"; $self->ParseMemCtxPullStart($e, $l, $array_name); if (($primitives and not $l->{IS_DEFERRED}) or ($deferred and $l->{IS_DEFERRED})) { my $nl = GetNextLevel($e,$l); if ($l->{IS_ZERO_TERMINATED}) { $self->CheckStringTerminator($ndr,$e,$l,$length); } $self->pidl("for ($counter = 0; $counter < $length; $counter++) {"); $self->indent; $self->ParseElementPullLevel($e, $nl, $ndr, $var_name, $env, 1, 0); $self->deindent; $self->pidl("}"); } if ($deferred and ContainsDeferred($e, $l)) { $self->pidl("for ($counter = 0; $counter < $length; $counter++) {"); $self->indent; $self->ParseElementPullLevel($e,GetNextLevel($e,$l), $ndr, $var_name, $env, 0, 1); $self->deindent; $self->pidl("}"); } $self->ParseMemCtxPullEnd($e, $l); } elsif ($l->{TYPE} eq "SWITCH") { $self->ParseElementPullLevel($e, GetNextLevel($e,$l), $ndr, $var_name, $env, $primitives, $deferred); } } ##################################################################### # parse scalars in a structure element - pull size sub ParseElementPull($$$$$$) { my($self,$e,$ndr,$env,$primitives,$deferred) = @_; my $var_name = $env->{$e->{NAME}}; my $represent_name; my $transmit_name; return unless $primitives or ($deferred and ContainsDeferred($e, $e->{LEVELS}[0])); if ($e->{REPRESENTATION_TYPE} ne $e->{TYPE}) { $self->pidl("{"); $self->indent; $represent_name = $var_name; $transmit_name = "_transmit_$e->{NAME}"; $var_name = $transmit_name; $self->pidl(mapTypeName($e->{TYPE})." $var_name;"); } $var_name = append_prefix($e, $var_name); $self->start_flags($e); $self->ParseElementPullLevel($e,$e->{LEVELS}[0],$ndr,$var_name,$env,$primitives,$deferred); $self->end_flags($e); # Representation type is different from transmit_as if ($e->{REPRESENTATION_TYPE} ne $e->{TYPE}) { $self->pidl("NDR_CHECK(ndr_$e->{TYPE}_to_$e->{REPRESENTATION_TYPE}($transmit_name, ".get_pointer_to($represent_name)."));"); $self->deindent; $self->pidl("}"); } } ##################################################################### # parse a pointer in a struct element or function sub ParsePtrPull($$$$$) { my($self, $e,$l,$ndr,$var_name) = @_; my $nl = GetNextLevel($e, $l); my $next_is_array = ($nl->{TYPE} eq "ARRAY"); my $next_is_string = (($nl->{TYPE} eq "DATA") and ($nl->{DATA_TYPE} eq "string")); if ($l->{POINTER_TYPE} eq "ref") { if ($l->{LEVEL} eq "EMBEDDED") { $self->pidl("NDR_CHECK(ndr_pull_ref_ptr($ndr, &_ptr_$e->{NAME}));"); } if (!$next_is_array and !$next_is_string) { $self->pidl("if (ndr->flags & LIBNDR_FLAG_REF_ALLOC) {"); $self->pidl("\tNDR_PULL_ALLOC($ndr, $var_name);"); $self->pidl("}"); } return; } elsif (($l->{POINTER_TYPE} eq "unique") or ($l->{POINTER_TYPE} eq "relative") or ($l->{POINTER_TYPE} eq "full")) { $self->pidl("NDR_CHECK(ndr_pull_generic_ptr($ndr, &_ptr_$e->{NAME}));"); $self->pidl("if (_ptr_$e->{NAME}) {"); $self->indent; } else { die("Unhandled pointer type $l->{POINTER_TYPE}"); } # Don't do this for arrays, they're allocated at the actual level # of the array unless ($next_is_array or $next_is_string) { $self->pidl("NDR_PULL_ALLOC($ndr, $var_name);"); } else { # FIXME: Yes, this is nasty. # We allocate an array twice # - once just to indicate that it's there, # - then the real allocation... $self->pidl("NDR_PULL_ALLOC($ndr, $var_name);"); } #$self->pidl("memset($var_name, 0, sizeof($var_name));"); if ($l->{POINTER_TYPE} eq "relative") { $self->pidl("NDR_CHECK(ndr_pull_relative_ptr1($ndr, $var_name, _ptr_$e->{NAME}));"); } $self->deindent; $self->pidl("} else {"); $self->pidl("\t$var_name = NULL;"); $self->pidl("}"); } sub ParseStructPushPrimitives($$$$) { my ($self, $struct, $varname, $env) = @_; # 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 push the conformant length early, as it fits on # the wire before the structure (and even before the structure # alignment) if (defined($struct->{SURROUNDING_ELEMENT})) { my $e = $struct->{SURROUNDING_ELEMENT}; if (defined($e->{LEVELS}[0]) and $e->{LEVELS}[0]->{TYPE} eq "ARRAY") { my $size; if ($e->{LEVELS}[0]->{IS_ZERO_TERMINATED}) { if (has_property($e, "charset")) { $size = "ndr_charset_length($varname->$e->{NAME}, CH_$e->{PROPERTIES}->{charset})"; } else { $size = "ndr_string_length($varname->$e->{NAME}, sizeof(*$varname->$e->{NAME}))"; } } else { $size = ParseExpr($e->{LEVELS}[0]->{SIZE_IS}, $env, $e->{ORIGINAL}); } $self->pidl("NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, $size));"); } else { $self->pidl("NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, ndr_string_array_size(ndr, $varname->$e->{NAME})));"); } } $self->pidl("NDR_CHECK(ndr_push_align(ndr, $struct->{ALIGN}));"); if (defined($struct->{PROPERTIES}{relative_base})) { # set the current offset as base for relative pointers # and store it based on the toplevel struct/union $self->pidl("NDR_CHECK(ndr_push_setup_relative_base_offset1(ndr, $varname, ndr->offset));"); } $self->ParseElementPush($_, "ndr", $env, 1, 0) foreach (@{$struct->{ELEMENTS}}); } sub ParseStructPushDeferred($$$) { my ($self, $struct, $varname, $env) = @_; if (defined($struct->{PROPERTIES}{relative_base})) { # retrieve the current offset as base for relative pointers # based on the toplevel struct/union $self->pidl("NDR_CHECK(ndr_push_setup_relative_base_offset2(ndr, $varname));"); } $self->ParseElementPush($_, "ndr", $env, 0, 1) foreach (@{$struct->{ELEMENTS}}); } ##################################################################### # parse a struct sub ParseStructPush($$$) { my ($self, $struct, $varname) = @_; return unless defined($struct->{ELEMENTS}); my $env = GenerateStructEnv($struct, $varname); EnvSubstituteValue($env, $struct); $self->DeclareArrayVariables($_) foreach (@{$struct->{ELEMENTS}}); $self->start_flags($struct); $self->pidl("if (ndr_flags & NDR_SCALARS) {"); $self->indent; $self->ParseStructPushPrimitives($struct, $varname, $env); $self->deindent; $self->pidl("}"); $self->pidl("if (ndr_flags & NDR_BUFFERS) {"); $self->indent; $self->ParseStructPushDeferred($struct, $varname, $env); $self->deindent; $self->pidl("}"); $self->end_flags($struct); } ##################################################################### # generate a push function for an enum sub ParseEnumPush($$$) { my($self,$enum,$varname) = @_; my($type_fn) = $enum->{BASE_TYPE}; $self->start_flags($enum); $self->pidl("NDR_CHECK(ndr_push_$type_fn(ndr, NDR_SCALARS, $varname));"); $self->end_flags($enum); } ##################################################################### # generate a pull function for an enum sub ParseEnumPull($$$) { my($self,$enum,$varname) = @_; my($type_fn) = $enum->{BASE_TYPE}; my($type_v_decl) = mapTypeName($type_fn); $self->pidl("$type_v_decl v;"); $self->start_flags($enum); $self->pidl("NDR_CHECK(ndr_pull_$type_fn(ndr, NDR_SCALARS, &v));"); $self->pidl("*$varname = v;"); $self->end_flags($enum); } ##################################################################### # generate a print function for an enum sub ParseEnumPrint($$$$) { my($self,$enum,$name,$varname) = @_; $self->pidl("const char *val = NULL;"); $self->pidl(""); $self->start_flags($enum); $self->pidl("switch ($varname) {"); $self->indent; my $els = \@{$enum->{ELEMENTS}}; foreach my $i (0 .. $#{$els}) { my $e = ${$els}[$i]; chomp $e; if ($e =~ /^(.*)=/) { $e = $1; } $self->pidl("case $e: val = \"$e\"; break;"); } $self->deindent; $self->pidl("}"); $self->pidl("ndr_print_enum(ndr, name, \"$enum->{TYPE}\", val, $varname);"); $self->end_flags($enum); } sub DeclEnum($$$$) { my ($e,$t,$name,$varname) = @_; return "enum $name " . ($t eq "pull"?"*":"") . $varname; } $typefamily{ENUM} = { DECL => \&DeclEnum, PUSH_FN_BODY => \&ParseEnumPush, PULL_FN_BODY => \&ParseEnumPull, PRINT_FN_BODY => \&ParseEnumPrint, }; ##################################################################### # generate a push function for a bitmap sub ParseBitmapPush($$$) { my($self,$bitmap,$varname) = @_; my($type_fn) = $bitmap->{BASE_TYPE}; $self->start_flags($bitmap); $self->pidl("NDR_CHECK(ndr_push_$type_fn(ndr, NDR_SCALARS, $varname));"); $self->end_flags($bitmap); } ##################################################################### # generate a pull function for an bitmap sub ParseBitmapPull($$$) { my($self,$bitmap,$varname) = @_; my $type_fn = $bitmap->{BASE_TYPE}; my($type_decl) = mapTypeName($bitmap->{BASE_TYPE}); $self->pidl("$type_decl v;"); $self->start_flags($bitmap); $self->pidl("NDR_CHECK(ndr_pull_$type_fn(ndr, NDR_SCALARS, &v));"); $self->pidl("*$varname = v;"); $self->end_flags($bitmap); } ##################################################################### # generate a print function for an bitmap sub ParseBitmapPrintElement($$$$$) { my($self,$e,$bitmap,$name,$varname) = @_; my($type_decl) = mapTypeName($bitmap->{BASE_TYPE}); my($type_fn) = $bitmap->{BASE_TYPE}; my($flag); if ($e =~ /^(\w+) .*$/) { $flag = "$1"; } else { die "Bitmap: \"$name\" invalid Flag: \"$e\""; } $self->pidl("ndr_print_bitmap_flag(ndr, sizeof($type_decl), \"$flag\", $flag, $varname);"); } ##################################################################### # generate a print function for an bitmap sub ParseBitmapPrint($$$$) { my($self,$bitmap,$name,$varname) = @_; my($type_decl) = mapTypeName($bitmap->{TYPE}); my($type_fn) = $bitmap->{BASE_TYPE}; $self->start_flags($bitmap); $self->pidl("ndr_print_$type_fn(ndr, name, $varname);"); $self->pidl("ndr->depth++;"); foreach my $e (@{$bitmap->{ELEMENTS}}) { $self->ParseBitmapPrintElement($e, $bitmap, $name, $varname); } $self->pidl("ndr->depth--;"); $self->end_flags($bitmap); } sub DeclBitmap($$$$) { my ($e,$t,$name,$varname) = @_; return mapTypeName(Parse::Pidl::Typelist::bitmap_type_fn($e)) . ($t eq "pull"?" *":" ") . $varname; } $typefamily{BITMAP} = { DECL => \&DeclBitmap, PUSH_FN_BODY => \&ParseBitmapPush, PULL_FN_BODY => \&ParseBitmapPull, PRINT_FN_BODY => \&ParseBitmapPrint, }; ##################################################################### # generate a struct print function sub ParseStructPrint($$$$) { my($self,$struct,$name,$varname) = @_; return unless defined $struct->{ELEMENTS}; my $env = GenerateStructEnv($struct, $varname); $self->DeclareArrayVariables($_) foreach (@{$struct->{ELEMENTS}}); $self->pidl("ndr_print_struct(ndr, name, \"$name\");"); $self->start_flags($struct); $self->pidl("ndr->depth++;"); $self->ParseElementPrint($_, $env->{$_->{NAME}}, $env) foreach (@{$struct->{ELEMENTS}}); $self->pidl("ndr->depth--;"); $self->end_flags($struct); } sub DeclarePtrVariables($$) { my ($self,$e) = @_; foreach my $l (@{$e->{LEVELS}}) { if ($l->{TYPE} eq "POINTER" and not ($l->{POINTER_TYPE} eq "ref" and $l->{LEVEL} eq "TOP")) { $self->pidl("uint32_t _ptr_$e->{NAME};"); last; } } } sub DeclareArrayVariables($$) { my ($self,$e) = @_; foreach my $l (@{$e->{LEVELS}}) { next if has_fast_array($e,$l); next if is_charset_array($e,$l); if ($l->{TYPE} eq "ARRAY") { $self->pidl("uint32_t cntr_$e->{NAME}_$l->{LEVEL_INDEX};"); } } } sub need_decl_mem_ctx($$) { my ($e,$l) = @_; return 0 if has_fast_array($e,$l); return 0 if is_charset_array($e,$l); return 1 if (($l->{TYPE} eq "ARRAY") and not $l->{IS_FIXED}); if (($l->{TYPE} eq "POINTER") and ($l->{POINTER_TYPE} eq "ref")) { my $nl = GetNextLevel($e, $l); my $next_is_array = ($nl->{TYPE} eq "ARRAY"); my $next_is_string = (($nl->{TYPE} eq "DATA") and ($nl->{DATA_TYPE} eq "string")); return 0 if ($next_is_array or $next_is_string); } return 1 if ($l->{TYPE} eq "POINTER"); return 0; } sub DeclareMemCtxVariables($$) { my ($self,$e) = @_; foreach my $l (@{$e->{LEVELS}}) { if (need_decl_mem_ctx($e, $l)) { $self->pidl("TALLOC_CTX *_mem_save_$e->{NAME}_$l->{LEVEL_INDEX};"); } } } sub ParseStructPullPrimitives($$$$) { my($self,$struct,$varname,$env) = @_; if (defined $struct->{SURROUNDING_ELEMENT}) { $self->pidl("NDR_CHECK(ndr_pull_array_size(ndr, &$varname->$struct->{SURROUNDING_ELEMENT}->{NAME}));"); } $self->pidl("NDR_CHECK(ndr_pull_align(ndr, $struct->{ALIGN}));"); if (defined($struct->{PROPERTIES}{relative_base})) { # set the current offset as base for relative pointers # and store it based on the toplevel struct/union $self->pidl("NDR_CHECK(ndr_pull_setup_relative_base_offset1(ndr, $varname, ndr->offset));"); } $self->ParseElementPull($_, "ndr", $env, 1, 0) foreach (@{$struct->{ELEMENTS}}); $self->add_deferred(); } sub ParseStructPullDeferred($$$$) { my ($self,$struct,$varname,$env) = @_; if (defined($struct->{PROPERTIES}{relative_base})) { # retrieve the current offset as base for relative pointers # based on the toplevel struct/union $self->pidl("NDR_CHECK(ndr_pull_setup_relative_base_offset2(ndr, $varname));"); } foreach my $e (@{$struct->{ELEMENTS}}) { $self->ParseElementPull($e, "ndr", $env, 0, 1); } $self->add_deferred(); } ##################################################################### # parse a struct - pull side sub ParseStructPull($$$) { my($self,$struct,$varname) = @_; return unless defined $struct->{ELEMENTS}; # declare any internal pointers we need foreach my $e (@{$struct->{ELEMENTS}}) { $self->DeclarePtrVariables($e); $self->DeclareArrayVariables($e); $self->DeclareMemCtxVariables($e); } $self->start_flags($struct); my $env = GenerateStructEnv($struct, $varname); $self->pidl("if (ndr_flags & NDR_SCALARS) {"); $self->indent; $self->ParseStructPullPrimitives($struct,$varname,$env); $self->deindent; $self->pidl("}"); $self->pidl("if (ndr_flags & NDR_BUFFERS) {"); $self->indent; $self->ParseStructPullDeferred($struct,$varname,$env); $self->deindent; $self->pidl("}"); $self->end_flags($struct); } ##################################################################### # calculate size of ndr struct sub ParseStructNdrSize($$$$) { my ($self,$t, $name, $varname) = @_; my $sizevar; if (my $flags = has_property($t, "flag")) { $self->pidl("flags |= $flags;"); } $self->pidl("return ndr_size_struct($varname, flags, (ndr_push_flags_fn_t)ndr_push_$name);"); } sub DeclStruct($$$$) { my ($e,$t,$name,$varname) = @_; return ($t ne "pull"?"const ":"") . "struct $name *$varname"; } sub ArgsStructNdrSize($$$) { my ($d, $name, $varname) = @_; return "const struct $name *$varname, int flags"; } $typefamily{STRUCT} = { PUSH_FN_BODY => \&ParseStructPush, DECL => \&DeclStruct, PULL_FN_BODY => \&ParseStructPull, PRINT_FN_BODY => \&ParseStructPrint, SIZE_FN_BODY => \&ParseStructNdrSize, SIZE_FN_ARGS => \&ArgsStructNdrSize, }; ##################################################################### # calculate size of ndr struct sub ParseUnionNdrSize($$$) { my ($self, $t, $name, $varname) = @_; my $sizevar; if (my $flags = has_property($t, "flag")) { $self->pidl("flags |= $flags;"); } $self->pidl("return ndr_size_union($varname, flags, level, (ndr_push_flags_fn_t)ndr_push_$name);"); } sub ParseUnionPushPrimitives($$$) { my ($self, $e, $varname) = @_; my $have_default = 0; $self->pidl("int level = ndr_push_get_switch_value(ndr, $varname);"); if (defined($e->{SWITCH_TYPE})) { $self->pidl("NDR_CHECK(ndr_push_$e->{SWITCH_TYPE}(ndr, NDR_SCALARS, level));"); } $self->pidl("switch (level) {"); $self->indent; foreach my $el (@{$e->{ELEMENTS}}) { if ($el->{CASE} eq "default") { $have_default = 1; } $self->pidl("$el->{CASE}:"); if ($el->{TYPE} ne "EMPTY") { $self->indent; if (defined($e->{PROPERTIES}{relative_base})) { $self->pidl("NDR_CHECK(ndr_push_align(ndr, $el->{ALIGN}));"); # set the current offset as base for relative pointers # and store it based on the toplevel struct/union $self->pidl("NDR_CHECK(ndr_push_setup_relative_base_offset1(ndr, $varname, ndr->offset));"); } $self->DeclareArrayVariables($el); $self->ParseElementPush($el, "ndr", {$el->{NAME} => "$varname->$el->{NAME}"}, 1, 0); $self->deindent; } $self->pidl("break;"); $self->pidl(""); } if (! $have_default) { $self->pidl("default:"); $self->pidl("\treturn ndr_push_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", level);"); } $self->deindent; $self->pidl("}"); } sub ParseUnionPushDeferred($$$) { my ($self,$e,$varname) = @_; my $have_default = 0; $self->pidl("int level = ndr_push_get_switch_value(ndr, $varname);"); if (defined($e->{PROPERTIES}{relative_base})) { # retrieve the current offset as base for relative pointers # based on the toplevel struct/union $self->pidl("NDR_CHECK(ndr_push_setup_relative_base_offset2(ndr, $varname));"); } $self->pidl("switch (level) {"); $self->indent; foreach my $el (@{$e->{ELEMENTS}}) { if ($el->{CASE} eq "default") { $have_default = 1; } $self->pidl("$el->{CASE}:"); if ($el->{TYPE} ne "EMPTY") { $self->indent; $self->ParseElementPush($el, "ndr", {$el->{NAME} => "$varname->$el->{NAME}"}, 0, 1); $self->deindent; } $self->pidl("break;"); $self->pidl(""); } if (! $have_default) { $self->pidl("default:"); $self->pidl("\treturn ndr_push_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", level);"); } $self->deindent; $self->pidl("}"); } ##################################################################### # parse a union - push side sub ParseUnionPush($$$) { my ($self,$e,$varname) = @_; my $have_default = 0; $self->start_flags($e); $self->pidl("if (ndr_flags & NDR_SCALARS) {"); $self->indent; $self->ParseUnionPushPrimitives($e, $varname); $self->deindent; $self->pidl("}"); $self->pidl("if (ndr_flags & NDR_BUFFERS) {"); $self->indent; $self->ParseUnionPushDeferred($e, $varname); $self->deindent; $self->pidl("}"); $self->end_flags($e); } ##################################################################### # print a union sub ParseUnionPrint($$$$) { my ($self,$e,$name,$varname) = @_; my $have_default = 0; $self->pidl("int level;"); foreach my $el (@{$e->{ELEMENTS}}) { $self->DeclareArrayVariables($el); } $self->start_flags($e); $self->pidl("level = ndr_print_get_switch_value(ndr, $varname);"); $self->pidl("ndr_print_union(ndr, name, level, \"$name\");"); $self->pidl("switch (level) {"); $self->indent; foreach my $el (@{$e->{ELEMENTS}}) { if ($el->{CASE} eq "default") { $have_default = 1; } $self->pidl("$el->{CASE}:"); if ($el->{TYPE} ne "EMPTY") { $self->indent; $self->ParseElementPrint($el, "$varname->$el->{NAME}", {}); $self->deindent; } $self->pidl("break;"); $self->pidl(""); } if (! $have_default) { $self->pidl("default:"); $self->pidl("\tndr_print_bad_level(ndr, name, level);"); } $self->deindent; $self->pidl("}"); $self->end_flags($e); } sub ParseUnionPullPrimitives($$$$) { my ($self,$e,$varname,$switch_type) = @_; my $have_default = 0; if (defined($switch_type)) { $self->pidl("NDR_CHECK(ndr_pull_$switch_type(ndr, NDR_SCALARS, &_level));"); $self->pidl("if (_level != level) {"); $self->pidl("\treturn ndr_pull_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value %u for $varname\", _level);"); $self->pidl("}"); } $self->pidl("switch (level) {"); $self->indent; foreach my $el (@{$e->{ELEMENTS}}) { if ($el->{CASE} eq "default") { $have_default = 1; } $self->pidl("$el->{CASE}: {"); if ($el->{TYPE} ne "EMPTY") { $self->indent; $self->DeclarePtrVariables($el); $self->DeclareArrayVariables($el); if (defined($e->{PROPERTIES}{relative_base})) { $self->pidl("NDR_CHECK(ndr_pull_align(ndr, $el->{ALIGN}));"); # set the current offset as base for relative pointers # and store it based on the toplevel struct/union $self->pidl("NDR_CHECK(ndr_pull_setup_relative_base_offset1(ndr, $varname, ndr->offset));"); } $self->ParseElementPull($el, "ndr", {$el->{NAME} => "$varname->$el->{NAME}"}, 1, 0); $self->deindent; } $self->pidl("break; }"); $self->pidl(""); } if (! $have_default) { $self->pidl("default:"); $self->pidl("\treturn ndr_pull_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", level);"); } $self->deindent; $self->pidl("}"); } sub ParseUnionPullDeferred($$$) { my ($self,$e,$varname) = @_; my $have_default = 0; if (defined($e->{PROPERTIES}{relative_base})) { # retrieve the current offset as base for relative pointers # based on the toplevel struct/union $self->pidl("NDR_CHECK(ndr_pull_setup_relative_base_offset2(ndr, $varname));"); } $self->pidl("switch (level) {"); $self->indent; foreach my $el (@{$e->{ELEMENTS}}) { if ($el->{CASE} eq "default") { $have_default = 1; } $self->pidl("$el->{CASE}:"); if ($el->{TYPE} ne "EMPTY") { $self->indent; $self->ParseElementPull($el, "ndr", {$el->{NAME} => "$varname->$el->{NAME}"}, 0, 1); $self->deindent; } $self->pidl("break;"); $self->pidl(""); } if (! $have_default) { $self->pidl("default:"); $self->pidl("\treturn ndr_pull_error(ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value \%u\", level);"); } $self->deindent; $self->pidl("}"); } ##################################################################### # parse a union - pull side sub ParseUnionPull($$$) { my ($self,$e,$varname) = @_; my $switch_type = $e->{SWITCH_TYPE}; $self->pidl("int level;"); if (defined($switch_type)) { if (Parse::Pidl::Typelist::typeIs($switch_type, "ENUM")) { $switch_type = Parse::Pidl::Typelist::enum_type_fn(getType($switch_type)->{DATA}); } $self->pidl(mapTypeName($switch_type) . " _level;"); } my %double_cases = (); foreach my $el (@{$e->{ELEMENTS}}) { next if ($el->{TYPE} eq "EMPTY"); next if ($double_cases{"$el->{NAME}"}); $self->DeclareMemCtxVariables($el); $double_cases{"$el->{NAME}"} = 1; } $self->start_flags($e); $self->pidl("level = ndr_pull_get_switch_value(ndr, $varname);"); $self->pidl("if (ndr_flags & NDR_SCALARS) {"); $self->indent; $self->ParseUnionPullPrimitives($e,$varname,$switch_type); $self->deindent; $self->pidl("}"); $self->pidl("if (ndr_flags & NDR_BUFFERS) {"); $self->indent; $self->ParseUnionPullDeferred($e,$varname); $self->deindent; $self->pidl("}"); $self->add_deferred(); $self->end_flags($e); } sub DeclUnion($$$$) { my ($e,$t,$name,$varname) = @_; return ($t ne "pull"?"const ":"") . "union $name *$varname"; } sub ArgsUnionNdrSize($$) { my ($d,$name) = @_; return "const union $name *r, uint32_t level, int flags"; } $typefamily{UNION} = { PUSH_FN_BODY => \&ParseUnionPush, DECL => \&DeclUnion, PULL_FN_BODY => \&ParseUnionPull, PRINT_FN_BODY => \&ParseUnionPrint, SIZE_FN_ARGS => \&ArgsUnionNdrSize, SIZE_FN_BODY => \&ParseUnionNdrSize, }; ##################################################################### # parse a typedef - push side sub ParseTypedefPush($$$) { my($self,$e,$varname) = @_; $typefamily{$e->{DATA}->{TYPE}}->{PUSH_FN_BODY}->($self, $e->{DATA}, $varname); } ##################################################################### # parse a typedef - pull side sub ParseTypedefPull($$$) { my($self,$e,$varname) = @_; $typefamily{$e->{DATA}->{TYPE}}->{PULL_FN_BODY}->($self, $e->{DATA}, $varname); } ##################################################################### # parse a typedef - print side sub ParseTypedefPrint($$$$) { my($self,$e,$name,$varname) = @_; $typefamily{$e->{DATA}->{TYPE}}->{PRINT_FN_BODY}->($self, $e->{DATA}, $name, $varname); } ##################################################################### ## calculate the size of a structure sub ParseTypedefNdrSize($$$$) { my($self,$t,$name,$varname) = @_; $typefamily{$t->{DATA}->{TYPE}}->{SIZE_FN_BODY}->($self, $t->{DATA}, $name, $varname); } sub DeclTypedef($$$$) { my ($e, $t, $name, $varname) = @_; return $typefamily{$e->{DATA}->{TYPE}}->{DECL}->($e->{DATA}, $t, $name, $varname); } sub ArgsTypedefNdrSize($$$) { my ($d, $name, $varname) = @_; return $typefamily{$d->{DATA}->{TYPE}}->{SIZE_FN_ARGS}->($d->{DATA}, $name, $varname); } $typefamily{TYPEDEF} = { PUSH_FN_BODY => \&ParseTypedefPush, DECL => \&DeclTypedef, PULL_FN_BODY => \&ParseTypedefPull, PRINT_FN_BODY => \&ParseTypedefPrint, SIZE_FN_ARGS => \&ArgsTypedefNdrSize, SIZE_FN_BODY => \&ParseTypedefNdrSize, }; ##################################################################### # parse a function - print side sub ParseFunctionPrint($$) { my($self, $fn) = @_; $self->pidl_hdr("void ndr_print_$fn->{NAME}(struct ndr_print *ndr, const char *name, int flags, const struct $fn->{NAME} *r);"); return if has_property($fn, "noprint"); $self->pidl("_PUBLIC_ void ndr_print_$fn->{NAME}(struct ndr_print *ndr, const char *name, int flags, const struct $fn->{NAME} *r)"); $self->pidl("{"); $self->indent; foreach my $e (@{$fn->{ELEMENTS}}) { $self->DeclareArrayVariables($e); } $self->pidl("ndr_print_struct(ndr, name, \"$fn->{NAME}\");"); $self->pidl("ndr->depth++;"); $self->pidl("if (flags & NDR_SET_VALUES) {"); $self->pidl("\tndr->flags |= LIBNDR_PRINT_SET_VALUES;"); $self->pidl("}"); $self->pidl("if (flags & NDR_IN) {"); $self->indent; $self->pidl("ndr_print_struct(ndr, \"in\", \"$fn->{NAME}\");"); $self->pidl("ndr->depth++;"); my $env = GenerateFunctionInEnv($fn); foreach my $e (@{$fn->{ELEMENTS}}) { if (grep(/in/,@{$e->{DIRECTION}})) { $self->ParseElementPrint($e, $env->{$e->{NAME}}, $env); } } $self->pidl("ndr->depth--;"); $self->deindent; $self->pidl("}"); $self->pidl("if (flags & NDR_OUT) {"); $self->indent; $self->pidl("ndr_print_struct(ndr, \"out\", \"$fn->{NAME}\");"); $self->pidl("ndr->depth++;"); $env = GenerateFunctionOutEnv($fn); foreach my $e (@{$fn->{ELEMENTS}}) { if (grep(/out/,@{$e->{DIRECTION}})) { $self->ParseElementPrint($e, $env->{$e->{NAME}}, $env); } } if ($fn->{RETURN_TYPE}) { $self->pidl("ndr_print_$fn->{RETURN_TYPE}(ndr, \"result\", r->out.result);"); } $self->pidl("ndr->depth--;"); $self->deindent; $self->pidl("}"); $self->pidl("ndr->depth--;"); $self->deindent; $self->pidl("}"); $self->pidl(""); } ##################################################################### # parse a function sub ParseFunctionPush($$) { my($self, $fn) = @_; $self->fn_declare("push", $fn, "NTSTATUS ndr_push_$fn->{NAME}(struct ndr_push *ndr, int flags, const struct $fn->{NAME} *r)") or return; return if has_property($fn, "nopush"); $self->pidl("{"); $self->indent; foreach my $e (@{$fn->{ELEMENTS}}) { $self->DeclareArrayVariables($e); } $self->pidl("if (flags & NDR_IN) {"); $self->indent; my $env = GenerateFunctionInEnv($fn); EnvSubstituteValue($env, $fn); foreach my $e (@{$fn->{ELEMENTS}}) { if (grep(/in/,@{$e->{DIRECTION}})) { $self->ParseElementPush($e, "ndr", $env, 1, 1); } } $self->deindent; $self->pidl("}"); $self->pidl("if (flags & NDR_OUT) {"); $self->indent; $env = GenerateFunctionOutEnv($fn); foreach my $e (@{$fn->{ELEMENTS}}) { if (grep(/out/,@{$e->{DIRECTION}})) { $self->ParseElementPush($e, "ndr", $env, 1, 1); } } if ($fn->{RETURN_TYPE}) { $self->pidl("NDR_CHECK(ndr_push_$fn->{RETURN_TYPE}(ndr, NDR_SCALARS, r->out.result));"); } $self->deindent; $self->pidl("}"); $self->pidl("return NT_STATUS_OK;"); $self->deindent; $self->pidl("}"); $self->pidl(""); } sub AllocateArrayLevel($$$$$$) { my ($self,$e,$l,$ndr,$env,$size) = @_; my $var = ParseExpr($e->{NAME}, $env, $e->{ORIGINAL}); my $pl = GetPrevLevel($e, $l); if (defined($pl) and $pl->{TYPE} eq "POINTER" and $pl->{POINTER_TYPE} eq "ref" and not $l->{IS_ZERO_TERMINATED}) { $self->pidl("if (ndr->flags & LIBNDR_FLAG_REF_ALLOC) {"); $self->pidl("\tNDR_PULL_ALLOC_N($ndr, $var, $size);"); $self->pidl("}"); if (grep(/in/,@{$e->{DIRECTION}}) and grep(/out/,@{$e->{DIRECTION}})) { $self->pidl("memcpy(r->out.$e->{NAME}, r->in.$e->{NAME}, $size * sizeof(*r->in.$e->{NAME}));"); } return; } $self->pidl("NDR_PULL_ALLOC_N($ndr, $var, $size);"); } ##################################################################### # parse a function sub ParseFunctionPull($$) { my($self,$fn) = @_; # pull function args $self->fn_declare("pull", $fn, "NTSTATUS ndr_pull_$fn->{NAME}(struct ndr_pull *ndr, int flags, struct $fn->{NAME} *r)") or return; $self->pidl("{"); $self->indent; # declare any internal pointers we need foreach my $e (@{$fn->{ELEMENTS}}) { $self->DeclarePtrVariables($e); $self->DeclareArrayVariables($e); } my %double_cases = (); foreach my $e (@{$fn->{ELEMENTS}}) { next if ($e->{TYPE} eq "EMPTY"); next if ($double_cases{"$e->{NAME}"}); $self->DeclareMemCtxVariables($e); $double_cases{"$e->{NAME}"} = 1; } $self->pidl("if (flags & NDR_IN) {"); $self->indent; # auto-init the out section of a structure. I originally argued that # this was a bad idea as it hides bugs, but coping correctly # with initialisation and not wiping ref vars is turning # out to be too tricky (tridge) foreach my $e (@{$fn->{ELEMENTS}}) { next unless grep(/out/, @{$e->{DIRECTION}}); $self->pidl("ZERO_STRUCT(r->out);"); $self->pidl(""); last; } my $env = GenerateFunctionInEnv($fn); foreach my $e (@{$fn->{ELEMENTS}}) { next unless (grep(/in/, @{$e->{DIRECTION}})); $self->ParseElementPull($e, "ndr", $env, 1, 1); } # allocate the "simple" out ref variables. FIXME: Shouldn't this have it's # own flag rather than be in NDR_IN ? foreach my $e (@{$fn->{ELEMENTS}}) { next unless (grep(/out/, @{$e->{DIRECTION}})); next unless ($e->{LEVELS}[0]->{TYPE} eq "POINTER" and $e->{LEVELS}[0]->{POINTER_TYPE} eq "ref"); next if (($e->{LEVELS}[1]->{TYPE} eq "DATA") and ($e->{LEVELS}[1]->{DATA_TYPE} eq "string")); next if (($e->{LEVELS}[1]->{TYPE} eq "ARRAY") and $e->{LEVELS}[1]->{IS_ZERO_TERMINATED}); if ($e->{LEVELS}[1]->{TYPE} eq "ARRAY") { my $size = ParseExprExt($e->{LEVELS}[1]->{SIZE_IS}, $env, $e->{ORIGINAL}, check_null_pointer($e, $env, sub { $self->pidl(shift); }, "return NT_STATUS_INVALID_PARAMETER_MIX;"), check_fully_dereferenced($e, $env)); $self->pidl("NDR_PULL_ALLOC_N(ndr, r->out.$e->{NAME}, $size);"); if (grep(/in/, @{$e->{DIRECTION}})) { $self->pidl("memcpy(r->out.$e->{NAME}, r->in.$e->{NAME}, $size * sizeof(*r->in.$e->{NAME}));"); } else { $self->pidl("memset(r->out.$e->{NAME}, 0, $size * sizeof(*r->out.$e->{NAME}));"); } } else { $self->pidl("NDR_PULL_ALLOC(ndr, r->out.$e->{NAME});"); if (grep(/in/, @{$e->{DIRECTION}})) { $self->pidl("*r->out.$e->{NAME} = *r->in.$e->{NAME};"); } else { $self->pidl("ZERO_STRUCTP(r->out.$e->{NAME});"); } } } $self->add_deferred(); $self->deindent; $self->pidl("}"); $self->pidl("if (flags & NDR_OUT) {"); $self->indent; $env = GenerateFunctionOutEnv($fn); foreach my $e (@{$fn->{ELEMENTS}}) { next unless grep(/out/, @{$e->{DIRECTION}}); $self->ParseElementPull($e, "ndr", $env, 1, 1); } if ($fn->{RETURN_TYPE}) { $self->pidl("NDR_CHECK(ndr_pull_$fn->{RETURN_TYPE}(ndr, NDR_SCALARS, &r->out.result));"); } $self->add_deferred(); $self->deindent; $self->pidl("}"); $self->pidl("return NT_STATUS_OK;"); $self->deindent; $self->pidl("}"); $self->pidl(""); } ##################################################################### # produce a function call table sub FunctionTable($$) { my($self,$interface) = @_; my $count = 0; my $uname = uc $interface->{NAME}; return if ($#{$interface->{FUNCTIONS}}+1 == 0); return unless defined ($interface->{PROPERTIES}->{uuid}); $self->pidl("static const struct dcerpc_interface_call $interface->{NAME}\_calls[] = {"); foreach my $d (@{$interface->{FUNCTIONS}}) { next if not defined($d->{OPNUM}); $self->pidl("\t{"); $self->pidl("\t\t\"$d->{NAME}\","); $self->pidl("\t\tsizeof(struct $d->{NAME}),"); $self->pidl("\t\t(ndr_push_flags_fn_t) ndr_push_$d->{NAME},"); $self->pidl("\t\t(ndr_pull_flags_fn_t) ndr_pull_$d->{NAME},"); $self->pidl("\t\t(ndr_print_function_t) ndr_print_$d->{NAME},"); $self->pidl("\t\t".($d->{ASYNC}?"True":"False").","); $self->pidl("\t},"); $count++; } $self->pidl("\t{ NULL, 0, NULL, NULL, NULL, False }"); $self->pidl("};"); $self->pidl(""); $self->pidl("static const char * const $interface->{NAME}\_endpoint_strings[] = {"); foreach my $ep (@{$interface->{ENDPOINTS}}) { $self->pidl("\t$ep, "); } my $endpoint_count = $#{$interface->{ENDPOINTS}}+1; $self->pidl("};"); $self->pidl(""); $self->pidl("static const struct dcerpc_endpoint_list $interface->{NAME}\_endpoints = {"); $self->pidl("\t.count\t= $endpoint_count,"); $self->pidl("\t.names\t= $interface->{NAME}\_endpoint_strings"); $self->pidl("};"); $self->pidl(""); if (! defined $interface->{PROPERTIES}->{authservice}) { $interface->{PROPERTIES}->{authservice} = "\"host\""; } my @a = split / /, $interface->{PROPERTIES}->{authservice}; my $authservice_count = $#a + 1; $self->pidl("static const char * const $interface->{NAME}\_authservice_strings[] = {"); foreach my $ap (@a) { $self->pidl("\t$ap, "); } $self->pidl("};"); $self->pidl(""); $self->pidl("static const struct dcerpc_authservice_list $interface->{NAME}\_authservices = {"); $self->pidl("\t.count\t= $endpoint_count,"); $self->pidl("\t.names\t= $interface->{NAME}\_authservice_strings"); $self->pidl("};"); $self->pidl(""); $self->pidl("\nconst struct dcerpc_interface_table dcerpc_table_$interface->{NAME} = {"); $self->pidl("\t.name\t\t= \"$interface->{NAME}\","); $self->pidl("\t.syntax_id\t= {"); $self->pidl("\t\t" . print_uuid($interface->{UUID}) .","); $self->pidl("\t\tDCERPC_$uname\_VERSION"); $self->pidl("\t},"); $self->pidl("\t.helpstring\t= DCERPC_$uname\_HELPSTRING,"); $self->pidl("\t.num_calls\t= $count,"); $self->pidl("\t.calls\t\t= $interface->{NAME}\_calls,"); $self->pidl("\t.endpoints\t= &$interface->{NAME}\_endpoints,"); $self->pidl("\t.authservices\t= &$interface->{NAME}\_authservices"); $self->pidl("};"); $self->pidl(""); } ##################################################################### # generate include statements for imported idl files sub HeaderImport { my $self = shift; my @imports = @_; foreach (@imports) { s/\.idl\"$//; s/^\"//; $self->pidl(choose_header("librpc/gen_ndr/ndr_$_\.h", "gen_ndr/ndr_$_.h")); } } ##################################################################### # generate include statements for included header files sub HeaderInclude { my $self = shift; my @includes = @_; foreach (@includes) { $self->pidl_hdr("#include $_"); } } ##################################################################### # generate prototypes and defines for the interface definitions # FIXME: these prototypes are for the DCE/RPC client functions, not the # NDR parser and so do not belong here, technically speaking sub HeaderInterface($$) { my($self,$interface) = @_; my $count = 0; $self->pidl_hdr(choose_header("librpc/ndr/libndr.h", "ndr.h")); if (has_property($interface, "object")) { $self->pidl(choose_header("librpc/gen_ndr/ndr_orpc.h", "ndr/orpc.h")); } if (defined $interface->{PROPERTIES}->{helper}) { $self->HeaderInclude(split / /, $interface->{PROPERTIES}->{helper}); } if (defined $interface->{PROPERTIES}->{uuid}) { my $name = uc $interface->{NAME}; $self->pidl_hdr("#define DCERPC_$name\_UUID " . Parse::Pidl::Util::make_str(lc($interface->{PROPERTIES}->{uuid}))); if(!defined $interface->{PROPERTIES}->{version}) { $interface->{PROPERTIES}->{version} = "0.0"; } $self->pidl_hdr("#define DCERPC_$name\_VERSION $interface->{PROPERTIES}->{version}"); $self->pidl_hdr("#define DCERPC_$name\_NAME \"$interface->{NAME}\""); if(!defined $interface->{PROPERTIES}->{helpstring}) { $interface->{PROPERTIES}->{helpstring} = "NULL"; } $self->pidl_hdr("#define DCERPC_$name\_HELPSTRING $interface->{PROPERTIES}->{helpstring}"); $self->pidl_hdr("extern const struct dcerpc_interface_table dcerpc_table_$interface->{NAME};"); $self->pidl_hdr("NTSTATUS dcerpc_server_$interface->{NAME}_init(void);"); } foreach (@{$interface->{FUNCTIONS}}) { next if has_property($_, "noopnum"); next if grep(/$_->{NAME}/,@{$interface->{INHERITED_FUNCTIONS}}); my $u_name = uc $_->{NAME}; my $val = sprintf("0x%02x", $count); if (defined($interface->{BASE})) { $val .= " + DCERPC_" . uc $interface->{BASE} . "_CALL_COUNT"; } $self->pidl_hdr("#define DCERPC_$u_name ($val)"); $self->pidl_hdr(""); $count++; } my $val = $count; if (defined($interface->{BASE})) { $val .= " + DCERPC_" . uc $interface->{BASE} . "_CALL_COUNT"; } $self->pidl_hdr("#define DCERPC_" . uc $interface->{NAME} . "_CALL_COUNT ($val)"); } sub ParseTypePush($$$$$) { my ($self,$e,$varname, $primitives, $deferred) = @_; # save the old relative_base_offset $self->pidl("uint32_t _save_relative_base_offset = ndr_push_get_relative_base_offset(ndr);") if defined(has_property($e, "relative_base")); $typefamily{$e->{TYPE}}->{PUSH_FN_BODY}->($self, $e, $varname); # restore the old relative_base_offset $self->pidl("ndr_push_restore_relative_base_offset(ndr, _save_relative_base_offset);") if defined(has_property($e, "relative_base")); } sub ParseTypePushFunction($$$) { my ($self, $e, $varname) = @_; my $args = $typefamily{$e->{TYPE}}->{DECL}->($e, "push", $e->{NAME}, $varname); $self->fn_declare("push", $e, "NTSTATUS ".TypeFunctionName("ndr_push", $e)."(struct ndr_push *ndr, int ndr_flags, $args)") or return; $self->pidl("{"); $self->indent; $self->ParseTypePush($e, $varname, 1, 1); $self->pidl("return NT_STATUS_OK;"); $self->deindent; $self->pidl("}"); $self->pidl("");; } sub ParseTypePull($$$$$) { my ($self, $e, $varname, $primitives, $deferred) = @_; # save the old relative_base_offset $self->pidl("uint32_t _save_relative_base_offset = ndr_pull_get_relative_base_offset(ndr);") if defined(has_property($e, "relative_base")); $typefamily{$e->{TYPE}}->{PULL_FN_BODY}->($self, $e, $varname); # restore the old relative_base_offset $self->pidl("ndr_pull_restore_relative_base_offset(ndr, _save_relative_base_offset);") if defined(has_property($e, "relative_base")); } sub ParseTypePullFunction($$) { my ($self, $e, $varname) = @_; my $args = $typefamily{$e->{TYPE}}->{DECL}->($e, "pull", $e->{NAME}, $varname); $self->fn_declare("pull", $e, "NTSTATUS ".TypeFunctionName("ndr_pull", $e)."(struct ndr_pull *ndr, int ndr_flags, $args)") or return; $self->pidl("{"); $self->indent; $self->ParseTypePull($e, $varname, 1, 1); $self->pidl("return NT_STATUS_OK;"); $self->deindent; $self->pidl("}"); $self->pidl(""); } sub ParseTypePrint($$$) { my ($self, $e, $varname) = @_; $typefamily{$e->{TYPE}}->{PRINT_FN_BODY}->($self, $e, $e->{NAME}, $varname); } sub ParseTypePrintFunction($$$) { my ($self, $e, $varname) = @_; my $args = $typefamily{$e->{TYPE}}->{DECL}->($e, "print", $e->{NAME}, $varname); $self->pidl_hdr("void ".TypeFunctionName("ndr_print", $e)."(struct ndr_print *ndr, const char *name, $args);"); return if (has_property($e, "noprint")); $self->pidl("_PUBLIC_ void ".TypeFunctionName("ndr_print", $e)."(struct ndr_print *ndr, const char *name, $args)"); $self->pidl("{"); $self->indent; $self->ParseTypePrint($e, $varname); $self->deindent; $self->pidl("}"); $self->pidl(""); } sub ParseTypeNdrSize($$) { my ($self,$t) = @_; my $varname = "r"; my $tf = $typefamily{$t->{TYPE}}; my $args = $tf->{SIZE_FN_ARGS}->($t, $t->{NAME}, $varname); $self->fn_declare("size", $t, "size_t ndr_size_$t->{NAME}($args)") or return; $self->pidl("{"); $self->indent; $typefamily{$t->{TYPE}}->{SIZE_FN_BODY}->($self,$t, $t->{NAME}, $varname); $self->deindent; $self->pidl("}"); $self->pidl(""); } ##################################################################### # parse the interface definitions sub ParseInterface($$$) { my($self,$interface,$needed) = @_; $self->pidl_hdr("#ifndef _HEADER_NDR_$interface->{NAME}"); $self->pidl_hdr("#define _HEADER_NDR_$interface->{NAME}"); $self->pidl_hdr(""); if ($needed->{"compression"}) { $self->pidl(choose_header("librpc/ndr/ndr_compression.h", "ndr/compression.h")); } $self->HeaderInterface($interface); # Typedefs foreach my $d (@{$interface->{TYPES}}) { ($needed->{TypeFunctionName("ndr_push", $d)}) && $self->ParseTypePushFunction($d, "r"); ($needed->{TypeFunctionName("ndr_pull", $d)}) && $self->ParseTypePullFunction($d, "r"); ($needed->{TypeFunctionName("ndr_print", $d)}) && $self->ParseTypePrintFunction($d, "r"); # Make sure we don't generate a function twice... $needed->{TypeFunctionName("ndr_push", $d)} = $needed->{TypeFunctionName("ndr_pull", $d)} = $needed->{TypeFunctionName("ndr_print", $d)} = 0; ($needed->{"ndr_size_$d->{NAME}"}) && $self->ParseTypeNdrSize($d); } # Functions foreach my $d (@{$interface->{FUNCTIONS}}) { ($needed->{"ndr_push_$d->{NAME}"}) && $self->ParseFunctionPush($d); ($needed->{"ndr_pull_$d->{NAME}"}) && $self->ParseFunctionPull($d); ($needed->{"ndr_print_$d->{NAME}"}) && $self->ParseFunctionPrint($d); # Make sure we don't generate a function twice... $needed->{"ndr_push_$d->{NAME}"} = $needed->{"ndr_pull_$d->{NAME}"} = $needed->{"ndr_print_$d->{NAME}"} = 0; } $self->FunctionTable($interface); $self->pidl_hdr("#endif /* _HEADER_NDR_$interface->{NAME} */"); } sub GenerateIncludes($) { my ($self) = @_; if (is_intree()) { $self->pidl("#include \"includes.h\""); } else { $self->pidl("#define _GNU_SOURCE"); $self->pidl("#include "); $self->pidl("#include "); $self->pidl("#include "); $self->pidl("#include "); $self->pidl("#include "); $self->pidl("#include "); } # Samba3 has everything in include/includes.h if (is_intree() != 3) { $self->pidl(choose_header("libcli/util/nterr.h", "core/nterr.h")); $self->pidl(choose_header("librpc/gen_ndr/ndr_misc.h", "gen_ndr/ndr_misc.h")); $self->pidl(choose_header("librpc/gen_ndr/ndr_dcerpc.h", "gen_ndr/ndr_dcerpc.h")); $self->pidl(choose_header("librpc/rpc/dcerpc.h", "dcerpc.h")); #FIXME: This shouldn't be here! } } ##################################################################### # parse a parsed IDL structure back into an IDL file sub Parse($$$$) { my($self, $ndr,$gen_header,$ndr_header) = @_; $self->pidl_hdr("/* header auto-generated by pidl */"); $self->pidl_hdr(""); $self->pidl_hdr("#include \"$gen_header\"") if ($gen_header); $self->pidl_hdr(""); $self->pidl("/* parser auto-generated by pidl */"); $self->pidl(""); $self->GenerateIncludes(); $self->pidl("#include \"$ndr_header\"") if ($ndr_header); $self->pidl(""); my %needed = (); foreach (@{$ndr}) { ($_->{TYPE} eq "INTERFACE") && NeededInterface($_, \%needed); } foreach (@{$ndr}) { ($_->{TYPE} eq "INTERFACE") && $self->ParseInterface($_, \%needed); ($_->{TYPE} eq "IMPORT") && $self->HeaderImport(@{$_->{PATHS}}); ($_->{TYPE} eq "INCLUDE") && $self->HeaderInclude(@{$_->{PATHS}}); } return ($self->{res_hdr}, $self->{res}); } sub NeededElement($$$) { my ($e, $dir, $needed) = @_; return if ($e->{TYPE} eq "EMPTY"); return if (ref($e->{TYPE}) eq "HASH" and not defined($e->{TYPE}->{NAME})); my ($t, $rt); if (ref($e->{TYPE}) eq "HASH") { $t = $e->{TYPE}->{TYPE}."_".$e->{TYPE}->{NAME}; } else { $t = $e->{TYPE}; } if (ref($e->{REPRESENTATION_TYPE}) eq "HASH") { $rt = $e->{REPRESENTATION_TYPE}->{TYPE}."_".$e->{REPRESENTATION_TYPE}->{NAME}; } else { $rt = $e->{REPRESENTATION_TYPE}; } die ("$e->{NAME} $t, $rt FOO") unless ($rt ne ""); my @fn = (); if ($dir eq "print") { push(@fn, TypeFunctionName("ndr_print", $e->{REPRESENTATION_TYPE})); } elsif ($dir eq "pull") { push (@fn, TypeFunctionName("ndr_pull", $e->{TYPE})); push (@fn, "ndr_$t\_to_$rt") if ($rt ne $t); } elsif ($dir eq "push") { push (@fn, TypeFunctionName("ndr_push", $e->{TYPE})); push (@fn, "ndr_$rt\_to_$t") if ($rt ne $t); } else { die("invalid direction `$dir'"); } foreach (@fn) { unless (defined($needed->{$_})) { $needed->{$_} = 1; } } } sub NeededFunction($$) { my ($fn,$needed) = @_; $needed->{"ndr_pull_$fn->{NAME}"} = 1; $needed->{"ndr_push_$fn->{NAME}"} = 1; $needed->{"ndr_print_$fn->{NAME}"} = 1; foreach my $e (@{$fn->{ELEMENTS}}) { $e->{PARENT} = $fn; NeededElement($e, $_, $needed) foreach ("pull", "push", "print"); } } sub NeededType($$$) { sub NeededType($$$); my ($t,$needed,$req) = @_; NeededType($t->{DATA}, $needed, $req) if ($t->{TYPE} eq "TYPEDEF"); if ($t->{TYPE} eq "STRUCT" or $t->{TYPE} eq "UNION") { for my $e (@{$t->{ELEMENTS}}) { $e->{PARENT} = $t; if (has_property($e, "compression")) { $needed->{"compression"} = 1; } NeededElement($e, $req, $needed); NeededType($e->{TYPE}, $needed, $req) if (ref($e->{TYPE}) eq "HASH"); } } } ##################################################################### # work out what parse functions are needed sub NeededInterface($$) { my ($interface,$needed) = @_; NeededFunction($_, $needed) foreach (@{$interface->{FUNCTIONS}}); foreach (reverse @{$interface->{TYPES}}) { if (has_property($_, "public")) { $needed->{TypeFunctionName("ndr_pull", $_)} = $needed->{TypeFunctionName("ndr_push", $_)} = $needed->{TypeFunctionName("ndr_print", $_)} = 1; } NeededType($_, $needed, "pull") if ($needed->{TypeFunctionName("ndr_pull", $_)}); NeededType($_, $needed, "push") if ($needed->{TypeFunctionName("ndr_push", $_)}); NeededType($_, $needed, "print") if ($needed->{TypeFunctionName("ndr_print", $_)}); if (has_property($_, "gensize")) { $needed->{"ndr_size_$_->{NAME}"} = 1; } } } sub TypeFunctionName($$) { my ($prefix, $t) = @_; return "$prefix\_$t->{NAME}" if (ref($t) eq "HASH" and ($t->{TYPE} eq "TYPEDEF" or $t->{TYPE} eq "DECLARE")); return "$prefix\_$t->{TYPE}_$t->{NAME}" if (ref($t) eq "HASH"); return "$prefix\_$t"; } 1;