diff options
-rw-r--r-- | pidl/lib/Parse/Pidl/Samba4/NDR/Client.pm | 462 |
1 files changed, 460 insertions, 2 deletions
diff --git a/pidl/lib/Parse/Pidl/Samba4/NDR/Client.pm b/pidl/lib/Parse/Pidl/Samba4/NDR/Client.pm index 55bc2fc75c..4805934346 100644 --- a/pidl/lib/Parse/Pidl/Samba4/NDR/Client.pm +++ b/pidl/lib/Parse/Pidl/Samba4/NDR/Client.pm @@ -10,8 +10,11 @@ use Exporter; @ISA = qw(Exporter); @EXPORT_OK = qw(Parse); -use Parse::Pidl::Samba4 qw(choose_header is_intree); -use Parse::Pidl::Util qw(has_property); +use Parse::Pidl qw(fatal warning error); +use Parse::Pidl::Util qw(has_property ParseExpr); +use Parse::Pidl::Typelist qw(mapTypeName); +use Parse::Pidl::Samba4 qw(choose_header is_intree DeclLong); +use Parse::Pidl::Samba4::Header qw(GenerateFunctionInEnv GenerateFunctionOutEnv); use vars qw($VERSION); $VERSION = '0.01'; @@ -22,6 +25,7 @@ sub indent($) { my ($self) = @_; $self->{tabs}.="\t"; } sub deindent($) { my ($self) = @_; $self->{tabs} = substr($self->{tabs}, 1); } sub pidl($$) { my ($self,$txt) = @_; $self->{res} .= $txt ? "$self->{tabs}$txt\n" : "\n"; } sub pidl_hdr($$) { my ($self, $txt) = @_; $self->{res_hdr} .= "$txt\n"; } +sub pidl_both($$) { my ($self, $txt) = @_; $self->{hdr} .= "$txt\n"; $self->{res_hdr} .= "$txt\n"; } sub fn_declare($$) { my ($self,$n) = @_; $self->pidl($n); $self->pidl_hdr("$n;"); } sub genpad($) @@ -218,6 +222,414 @@ sub ParseFunction_r_Sync($$$$) $self->pidl(""); } +sub ElementDirection($) +{ + my ($e) = @_; + + return "[in,out]" if (has_property($e, "in") and has_property($e, "out")); + return "[in]" if (has_property($e, "in")); + return "[out]" if (has_property($e, "out")); + return "[in,out]"; +} + +sub HeaderProperties($$) +{ + my($props,$ignores) = @_; + my $ret = ""; + + foreach my $d (keys %{$props}) { + next if (grep(/^$d$/, @$ignores)); + if($props->{$d} ne "1") { + $ret.= "$d($props->{$d}),"; + } else { + $ret.="$d,"; + } + } + + if ($ret) { + return "[" . substr($ret, 0, -1) . "]"; + } +} + +sub ParseCopyInArgument($$$$$$) +{ + my ($self, $fn, $e, $r, $i, $invalid_response_type) = @_; + my $l = $e->{LEVELS}[0]; + + if ($l->{TYPE} eq "ARRAY" and $l->{IS_FIXED} == 1) { + $self->pidl("memcpy(${r}in.$e->{NAME}, ${i}$e->{NAME}, sizeof(${r}in.$e->{NAME}));"); + } else { + $self->pidl("${r}in.$e->{NAME} = ${i}$e->{NAME};"); + } +} + +sub ParseInvalidResponse($$) +{ + my ($self, $type) = @_; + + if ($type eq "sync") { + $self->pidl("return NT_STATUS_INVALID_NETWORK_RESPONSE;"); + } elsif ($type eq "async") { + $self->pidl("tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);"); + $self->pidl("return;"); + } else { + die("ParseInvalidResponse($type)"); + } +} + +sub ParseOutputArgument($$$$$$) +{ + my ($self, $fn, $e, $r, $o, $invalid_response_type) = @_; + my $level = 0; + + if ($e->{LEVELS}[0]->{TYPE} ne "POINTER" and $e->{LEVELS}[0]->{TYPE} ne "ARRAY") { + fatal($e->{ORIGINAL}, "[out] argument is not a pointer or array"); + return; + } + + if ($e->{LEVELS}[0]->{TYPE} eq "POINTER") { + $level = 1; + if ($e->{LEVELS}[0]->{POINTER_TYPE} ne "ref") { + $self->pidl("if ($o$e->{NAME} && ${r}out.$e->{NAME}) {"); + $self->indent; + } + } + + if ($e->{LEVELS}[$level]->{TYPE} eq "ARRAY") { + # This is a call to GenerateFunctionInEnv intentionally. + # Since the data is being copied into a user-provided data + # structure, the user should be able to know the size beforehand + # to allocate a structure of the right size. + my $in_env = GenerateFunctionInEnv($fn, $r); + my $out_env = GenerateFunctionOutEnv($fn, $r); + my $l = $e->{LEVELS}[$level]; + unless (defined($l->{SIZE_IS})) { + fatal($e->{ORIGINAL}, "no size known for [out] array `$e->{NAME}'"); + } else { + my $in_size_is = ParseExpr($l->{SIZE_IS}, $in_env, $e->{ORIGINAL}); + my $out_size_is = ParseExpr($l->{SIZE_IS}, $out_env, $e->{ORIGINAL}); + my $out_length_is = $out_size_is; + if (defined($l->{LENGTH_IS})) { + $out_length_is = ParseExpr($l->{LENGTH_IS}, $out_env, $e->{ORIGINAL}); + } + if ($out_size_is ne $in_size_is) { + $self->pidl("if (($out_size_is) > ($in_size_is)) {"); + $self->indent; + $self->ParseInvalidResponse($invalid_response_type); + $self->deindent; + $self->pidl("}"); + } + if ($out_length_is ne $out_size_is) { + $self->pidl("if (($out_length_is) > ($out_size_is)) {"); + $self->indent; + $self->ParseInvalidResponse($invalid_response_type); + $self->deindent; + $self->pidl("}"); + } + if (has_property($e, "charset")) { + $self->pidl("memcpy(discard_const_p(uint8_t *, $o$e->{NAME}), ${r}out.$e->{NAME}, ($out_length_is) * sizeof(*$o$e->{NAME}));"); + } else { + $self->pidl("memcpy($o$e->{NAME}, ${r}out.$e->{NAME}, ($out_length_is) * sizeof(*$o$e->{NAME}));"); + } + } + } else { + $self->pidl("*$o$e->{NAME} = *${r}out.$e->{NAME};"); + } + + if ($e->{LEVELS}[0]->{TYPE} eq "POINTER") { + if ($e->{LEVELS}[0]->{POINTER_TYPE} ne "ref") { + $self->deindent; + $self->pidl("}"); + } + } +} + +sub ParseFunction_State($$$$) +{ + my ($self, $if, $fn, $name) = @_; + + my $state_str = "struct dcerpc_$name\_state"; + my $done_fn = "dcerpc_$name\_done"; + + $self->pidl("$state_str {"); + $self->indent; + $self->pidl("struct $name orig;"); + $self->pidl("struct $name tmp;"); + $self->pidl("TALLOC_CTX *out_mem_ctx;"); + $self->deindent; + $self->pidl("};"); + $self->pidl(""); + $self->pidl("static void $done_fn(struct tevent_req *subreq);"); + $self->pidl(""); +} + +sub ParseFunction_Send($$$$) +{ + my ($self, $if, $fn, $name) = @_; + + my $fn_args = ""; + my $state_str = "struct dcerpc_$name\_state"; + my $done_fn = "dcerpc_$name\_done"; + my $out_mem_ctx = "dcerpc_$name\_out_memory"; + my $fn_str = "struct tevent_req *dcerpc_$name\_send"; + my $pad = genpad($fn_str); + + $fn_args .= "TALLOC_CTX *mem_ctx"; + $fn_args .= ",\n" . $pad . "struct tevent_context *ev"; + $fn_args .= ",\n" . $pad . "struct dcerpc_binding_handle *h"; + + foreach (@{$fn->{ELEMENTS}}) { + my $dir = ElementDirection($_); + my $prop = HeaderProperties($_->{PROPERTIES}, ["in", "out"]); + $fn_args .= ",\n" . $pad . DeclLong($_, "_") . " /* $dir $prop */"; + } + + $self->fn_declare("$fn_str($fn_args)"); + $self->pidl("{"); + $self->indent; + $self->pidl("struct tevent_req *req;"); + $self->pidl("$state_str *state;"); + $self->pidl("struct tevent_req *subreq;"); + $self->pidl(""); + $self->pidl("req = tevent_req_create(mem_ctx, &state,"); + $self->pidl("\t\t\t$state_str);"); + $self->pidl("if (req == NULL) {"); + $self->indent; + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("state->out_mem_ctx = NULL;"); + $self->pidl(""); + + $self->pidl("/* In parameters */"); + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/in/, @{$e->{DIRECTION}})); + + $self->ParseCopyInArgument($fn, $e, "state->orig.", "_", "async"); + } + $self->pidl(""); + + my $out_params = 0; + $self->pidl("/* Out parameters */"); + foreach (@{$fn->{ELEMENTS}}) { + if (grep(/out/, @{$_->{DIRECTION}})) { + $self->pidl("state->orig.out.$_->{NAME} = _$_->{NAME};"); + $out_params++; + } + } + $self->pidl(""); + + if (defined($fn->{RETURN_TYPE})) { + $self->pidl("/* Result */"); + $self->pidl("ZERO_STRUCT(state->orig.out.result);"); + $self->pidl(""); + } + + if ($out_params > 0) { + $self->pidl("state->out_mem_ctx = talloc_named_const(state, 0,"); + $self->pidl("\t\t \"$out_mem_ctx\");"); + $self->pidl("if (tevent_req_nomem(state->out_mem_ctx, req)) {"); + $self->indent; + $self->pidl("return tevent_req_post(req, ev);"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + } + + $self->pidl("/* make a temporary copy, that we pass to the dispatch function */"); + $self->pidl("state->tmp = state->orig;"); + $self->pidl(""); + + $self->pidl("subreq = dcerpc_$name\_r_send(state, ev, h, &state->tmp);"); + $self->pidl("if (tevent_req_nomem(subreq, req)) {"); + $self->indent; + $self->pidl("return tevent_req_post(req, ev);"); + $self->deindent; + $self->pidl("}"); + $self->pidl("tevent_req_set_callback(subreq, $done_fn, req);"); + $self->pidl("return req;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub ParseFunction_Done($$$$) +{ + my ($self, $if, $fn, $name) = @_; + + my $state_str = "struct dcerpc_$name\_state"; + my $done_fn = "dcerpc_$name\_done"; + + $self->pidl("static void $done_fn(struct tevent_req *subreq)"); + $self->pidl("{"); + $self->indent; + $self->pidl("struct tevent_req *req = tevent_req_callback_data("); + $self->pidl("\tsubreq, struct tevent_req);"); + $self->pidl("$state_str *state = tevent_req_data("); + $self->pidl("\treq, $state_str);"); + $self->pidl("NTSTATUS status;"); + $self->pidl("TALLOC_CTX *mem_ctx;"); + $self->pidl(""); + + $self->pidl("if (state->out_mem_ctx) {"); + $self->indent; + $self->pidl("mem_ctx = state->out_mem_ctx;"); + $self->deindent; + $self->pidl("} else {"); + $self->indent; + $self->pidl("mem_ctx = state;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("status = dcerpc_$name\_r_recv(subreq, mem_ctx);"); + $self->pidl("TALLOC_FREE(subreq);"); + $self->pidl("if (!NT_STATUS_IS_OK(status)) {"); + $self->indent; + $self->pidl("tevent_req_nterror(req, status);"); + $self->pidl("return;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("/* Copy out parameters */"); + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/out/, @{$e->{DIRECTION}})); + + $self->ParseOutputArgument($fn, $e, + "state->tmp.", + "state->orig.out.", + "async"); + } + $self->pidl(""); + + if (defined($fn->{RETURN_TYPE})) { + $self->pidl("/* Copy result */"); + $self->pidl("state->orig.out.result = state->tmp.out.result;"); + $self->pidl(""); + } + + $self->pidl("/* Reset temporary structure */"); + $self->pidl("ZERO_STRUCT(state->tmp);"); + $self->pidl(""); + + $self->pidl("tevent_req_done(req);"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub ParseFunction_Recv($$$$) +{ + my ($self, $if, $fn, $name) = @_; + + my $fn_args = ""; + my $state_str = "struct dcerpc_$name\_state"; + my $fn_str = "NTSTATUS dcerpc_$name\_recv"; + my $pad = genpad($fn_str); + + $fn_args .= "struct tevent_req *req,\n" . $pad . "TALLOC_CTX *mem_ctx"; + + if (defined($fn->{RETURN_TYPE})) { + $fn_args .= ",\n" . $pad . mapTypeName($fn->{RETURN_TYPE}). " *result"; + } + + $self->fn_declare("$fn_str($fn_args)"); + $self->pidl("{"); + $self->indent; + $self->pidl("$state_str *state = tevent_req_data("); + $self->pidl("\treq, $state_str);"); + $self->pidl("NTSTATUS status;"); + $self->pidl(""); + $self->pidl("if (tevent_req_is_nterror(req, &status)) {"); + $self->indent; + $self->pidl("tevent_req_received(req);"); + $self->pidl("return status;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("/* Steal possbile out parameters to the callers context */"); + $self->pidl("talloc_steal(mem_ctx, state->out_mem_ctx);"); + $self->pidl(""); + + if (defined($fn->{RETURN_TYPE})) { + $self->pidl("/* Return result */"); + $self->pidl("*result = state->orig.out.result;"); + $self->pidl(""); + } + + $self->pidl("tevent_req_received(req);"); + $self->pidl("return NT_STATUS_OK;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub ParseFunction_Sync($$$$) +{ + my ($self, $if, $fn, $name) = @_; + + my $fn_args = ""; + my $fn_str = "NTSTATUS dcerpc_$name"; + my $pad = genpad($fn_str); + + $fn_args .= "struct dcerpc_binding_handle *h,\n" . $pad . "TALLOC_CTX *mem_ctx"; + + foreach (@{$fn->{ELEMENTS}}) { + my $dir = ElementDirection($_); + my $prop = HeaderProperties($_->{PROPERTIES}, ["in", "out"]); + $fn_args .= ",\n" . $pad . DeclLong($_, "_") . " /* $dir $prop */"; + } + + if (defined($fn->{RETURN_TYPE})) { + $fn_args .= ",\n" . $pad . mapTypeName($fn->{RETURN_TYPE}). " *result"; + } + + $self->fn_declare("$fn_str($fn_args)"); + $self->pidl("{"); + $self->indent; + $self->pidl("struct $name r;"); + $self->pidl("NTSTATUS status;"); + $self->pidl(""); + $self->pidl("/* In parameters */"); + + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/in/, @{$e->{DIRECTION}})); + + $self->ParseCopyInArgument($fn, $e, "r.", "_", "sync"); + } + + $self->pidl(""); + $self->pidl("status = dcerpc_$name\_r(h, mem_ctx, &r);"); + $self->pidl("if (!NT_STATUS_IS_OK(status)) {"); + $self->indent; + $self->pidl("return status;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("/* Return variables */"); + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/out/, @{$e->{DIRECTION}})); + + $self->ParseOutputArgument($fn, $e, "r.", "_", "sync"); + } + $self->pidl(""); + + $self->pidl("/* Return result */"); + if ($fn->{RETURN_TYPE}) { + $self->pidl("*result = r.out.result;"); + } + $self->pidl(""); + + $self->pidl("return NT_STATUS_OK;"); + + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + ##################################################################### # parse a function sub ParseFunction($$$) @@ -230,6 +642,51 @@ sub ParseFunction($$$) $self->ParseFunction_r_Recv($if, $fn, $fn->{NAME}); $self->ParseFunction_r_Sync($if, $fn, $fn->{NAME}); + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/out/, @{$e->{DIRECTION}})); + + my $reason = "is not a pointer or array"; + + # TODO: make this fatal at NDR level + if ($e->{LEVELS}[0]->{TYPE} eq "POINTER") { + if ($e->{LEVELS}[1]->{TYPE} eq "DATA" and + $e->{LEVELS}[1]->{DATA_TYPE} eq "string") { + $reason = "is a pointer to type 'string'"; + } elsif ($e->{LEVELS}[1]->{TYPE} eq "ARRAY" and + not defined($e->{LEVELS}[1]->{SIZE_IS})) { + $reason = "is a pointer to an unsized array"; + } else { + next; + } + } + if ($e->{LEVELS}[0]->{TYPE} eq "ARRAY") { + if (not defined($e->{LEVELS}[0]->{SIZE_IS})) { + $reason = "is a pointer to an unsized array"; + } else { + next; + } + } + + $self->pidl_both("/*"); + $self->pidl_both(" * The following functions are skipped because"); + $self->pidl_both(" * an [out] argument $e->{NAME} $reason:"); + $self->pidl_both(" *"); + $self->pidl_both(" * dcerpc_$fn->{NAME}_send()"); + $self->pidl_both(" * dcerpc_$fn->{NAME}_recv()"); + $self->pidl_both(" * dcerpc_$fn->{NAME}()"); + $self->pidl_both(" */"); + $self->pidl_both(""); + + error($e->{ORIGINAL}, "$fn->{NAME}: [out] argument '$e->{NAME}' $reason, skip client functions"); + return; + } + + $self->ParseFunction_State($if, $fn, $fn->{NAME}); + $self->ParseFunction_Send($if, $fn, $fn->{NAME}); + $self->ParseFunction_Done($if, $fn, $fn->{NAME}); + $self->ParseFunction_Recv($if, $fn, $fn->{NAME}); + $self->ParseFunction_Sync($if, $fn, $fn->{NAME}); + $self->pidl_hdr(""); } @@ -281,6 +738,7 @@ sub Parse($$$$$$) $self->pidl("#include <stdlib.h>"); $self->pidl("#include <stdint.h>"); $self->pidl("#include <stdarg.h>"); + $self->pidl("#include <string.h>"); $self->pidl("#include <core/ntstatus.h>"); } $self->pidl("#include <tevent.h>"); |