@EXPORT_OK = qw(Parse);
use Parse::Pidl qw(fatal warning error);
-use Parse::Pidl::Util qw(has_property ParseExpr);
+use Parse::Pidl::Util qw(has_property ParseExpr genpad);
use Parse::Pidl::NDR qw(ContainsPipe);
use Parse::Pidl::Typelist qw(mapTypeName);
use Parse::Pidl::Samba4 qw(choose_header is_intree DeclLong);
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($)
-{
- my ($s) = @_;
- my $nt = int((length($s)+1)/8);
- my $lt = ($nt*8)-1;
- my $ns = (length($s)-$lt);
- return "\t"x($nt)." "x($ns);
-}
-
sub new($)
{
my ($class) = shift;
my ($self, $if, $fn, $name) = @_;
my $uname = uc $name;
+ my $in_pipes = 0;
+ foreach my $e (@{$fn->{ELEMENTS}}) {
+ next unless grep(/in/, @{$e->{DIRECTION}});
+ next unless ContainsPipe($e, $e->{LEVELS}[0]);
+ $in_pipes++;
+ }
+ my $out_pipes = 0;
+ foreach my $e (@{$fn->{ELEMENTS}}) {
+ next unless grep(/out/, @{$e->{DIRECTION}});
+ next unless ContainsPipe($e, $e->{LEVELS}[0]);
+ $out_pipes++;
+ }
+
$self->pidl("struct dcerpc_$name\_r_state {");
$self->indent;
+ if ($in_pipes > 0 or $out_pipes > 0) {
+ $self->pidl("struct dcerpc_pipe_handle_connection *in_pipes[$in_pipes];") if ($in_pipes > 0);
+ $self->pidl("struct dcerpc_pipe_handle_connection *out_pipes[$out_pipes];") if ($out_pipes > 0);
+ $self->pidl("struct dcerpc_binding_handle_call_params params;");
+ }
$self->pidl("TALLOC_CTX *out_mem_ctx;");
$self->deindent;
$self->pidl("};");
}
$self->pidl("");
- $self->pidl("subreq = dcerpc_binding_handle_call_send(state, ev, h,");
- $self->pidl("\t\tNULL, &ndr_table_$if->{NAME},");
- $self->pidl("\t\tNDR_$uname, $submem, r);");
+ my $in_pipes = 0;
+ foreach my $e (@{$fn->{ELEMENTS}}) {
+ next unless grep(/in/, @{$e->{DIRECTION}});
+ next unless ContainsPipe($e, $e->{LEVELS}[0]);
+ $self->pidl("if (r->in.$e->{NAME} == NULL) {");
+ $self->indent;
+ $self->pidl("tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);");
+ $self->pidl("return tevent_req_post(req, ev);");
+ $self->deindent;
+ $self->pidl("}");
+ $self->pidl("state->in_pipes[$in_pipes] = r->in.".$e->{NAME}."->pc;");
+ $in_pipes++;
+ }
+ my $out_pipes = 0;
+ foreach my $e (@{$fn->{ELEMENTS}}) {
+ next unless grep(/out/, @{$e->{DIRECTION}});
+ next unless ContainsPipe($e, $e->{LEVELS}[0]);
+ $self->pidl("if (r->out.$e->{NAME} == NULL) {");
+ $self->indent;
+ $self->pidl("tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);");
+ $self->pidl("return tevent_req_post(req, ev);");
+ $self->deindent;
+ $self->pidl("}");
+ $self->pidl("state->out_pipes[$out_pipes] = r->out.".$e->{NAME}."->pc;");
+ $out_pipes++;
+ }
+
+ if ($in_pipes > 0 or $out_pipes > 0) {
+ $self->pidl("state->params.r_mem = $submem;");
+ $self->pidl("state->params.r_ptr = r;");
+ $self->pidl("state->params.in.num_pipes = $in_pipes;");
+ if ($in_pipes > 0) {
+ $self->pidl("state->params.in.pipes = state->in_pipes;");
+ } else {
+ $self->pidl("state->params.in.pipes = NULL;");
+ }
+ $self->pidl("state->params.out.num_pipes = $out_pipes;");
+ if ($out_pipes > 0) {
+ $self->pidl("state->params.out.pipes = state->out_pipes;");
+ } else {
+ $self->pidl("state->params.out.pipes = NULL;");
+ }
+ $self->pidl("");
+
+ $self->pidl("subreq = dcerpc_binding_handle_call_params_send(state, ev, h,");
+ $self->pidl("\t\tNULL, &ndr_table_$if->{NAME},");
+ $self->pidl("\t\tNDR_$uname, &state->params);");
+ } else {
+ $self->pidl("subreq = dcerpc_binding_handle_call_send(state, ev, h,");
+ $self->pidl("\t\tNULL, &ndr_table_$if->{NAME},");
+ $self->pidl("\t\tNDR_$uname, $submem, r);");
+ }
$self->pidl("if (tevent_req_nomem(subreq, req)) {");
$self->indent;
$self->pidl("return tevent_req_post(req, ev);");
my $proto = "static void dcerpc_$name\_r_done(struct tevent_req *subreq)";
+ my $in_pipes = 0;
+ foreach my $e (@{$fn->{ELEMENTS}}) {
+ next unless grep(/in/, @{$e->{DIRECTION}});
+ next unless ContainsPipe($e, $e->{LEVELS}[0]);
+ $in_pipes++;
+ }
+ my $out_pipes = 0;
+ foreach my $e (@{$fn->{ELEMENTS}}) {
+ next unless grep(/out/, @{$e->{DIRECTION}});
+ next unless ContainsPipe($e, $e->{LEVELS}[0]);
+ $out_pipes++;
+ }
+
$self->pidl("$proto");
$self->pidl("{");
$self->indent;
$self->pidl("NTSTATUS status;");
$self->pidl("");
- $self->pidl("status = dcerpc_binding_handle_call_recv(subreq);");
+ if ($in_pipes > 0 or $out_pipes > 0) {
+ $self->pidl("status = dcerpc_binding_handle_call_params_recv(subreq);");
+ } else {
+ $self->pidl("status = dcerpc_binding_handle_call_recv(subreq);");
+ }
$self->pidl("TALLOC_FREE(subreq);");
$self->pidl("if (tevent_req_nterror(req, status)) {");
$self->indent;
my($props,$ignores) = @_;
my $ret = "";
- foreach my $d (keys %{$props}) {
+ foreach my $d (sort(keys %{$props})) {
next if (grep(/^$d$/, @$ignores));
if($props->{$d} ne "1") {
$ret.= "$d($props->{$d}),";
if (defined($fn->{RETURN_TYPE})) {
$self->pidl("/* Result */");
- $self->pidl("ZERO_STRUCT(state->orig.out.result);");
+ $self->pidl("NDR_ZERO_STRUCT(state->orig.out.result);");
$self->pidl("");
}
}
$self->pidl("/* Reset temporary structure */");
- $self->pidl("ZERO_STRUCT(state->tmp);");
+ $self->pidl("NDR_ZERO_STRUCT(state->tmp);");
$self->pidl("");
$self->pidl("tevent_req_done(req);");
if (defined($fn->{RETURN_TYPE})) {
$self->pidl("/* Result */");
- $self->pidl("ZERO_STRUCT(r.out.result);");
+ $self->pidl("NDR_ZERO_STRUCT(r.out.result);");
$self->pidl("");
}
{
my ($self, $if, $fn) = @_;
- if ($self->ParseFunctionHasPipes($fn)) {
- $self->pidl_both("/*");
- $self->pidl_both(" * The following function is skipped because");
- $self->pidl_both(" * it uses pipes:");
- $self->pidl_both(" *");
- $self->pidl_both(" * dcerpc_$fn->{NAME}_r_send()");
- $self->pidl_both(" * dcerpc_$fn->{NAME}_r_recv()");
- $self->pidl_both(" * dcerpc_$fn->{NAME}_r()");
- $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("");
- warning($fn->{ORIGINAL}, "$fn->{NAME}: dcerpc client does not support pipe yet");
- return;
- }
-
$self->ParseFunction_r_State($if, $fn, $fn->{NAME});
$self->ParseFunction_r_Send($if, $fn, $fn->{NAME});
$self->ParseFunction_r_Done($if, $fn, $fn->{NAME});
$self->pidl_hdr("");
}
+sub ParsePipeCreate($$$)
+{
+ my ($self, $t, $cname) = @_;
+
+ $self->pidl_both("/* $t->{NAME} */");
+
+ $self->pidl_hdr("struct $t->{NAME};");
+ $self->pidl("struct $t->{NAME} {");
+ $self->indent;
+ $self->pidl("struct dcerpc_pipe_handle_connection *pc;");
+ $self->deindent;
+ $self->pidl("};");
+ $self->pidl("");
+
+ my $proto = "struct $t->{NAME} *dcerpc_$t->{NAME}\_create(TALLOC_CTX *mem_ctx)";
+
+ $self->pidl_hdr("$proto;");
+ $self->pidl("$proto");
+ $self->pidl("{");
+ $self->indent;
+
+ $self->pidl("struct $t->{NAME} *p;");
+ $self->pidl("");
+
+ $self->pidl("p = talloc_zero(mem_ctx, struct $t->{NAME});");
+ $self->pidl("if (p == NULL) {");
+ $self->indent;
+ $self->pidl("return NULL;");
+ $self->deindent;
+ $self->pidl("}");
+ $self->pidl("");
+
+ my $cname = "$t->{NAME}_chunk";
+
+ $self->pidl("/* the pipe is disconnected by default */");
+ $self->pidl("p->pc = dcerpc_pipe_handle_connection_create(p,");
+ $self->pidl("\t\t\t\"$cname\",");
+ $self->pidl("\t\t\tsizeof(struct $cname));");
+ $self->pidl("if (p->pc == NULL) {");
+ $self->indent;
+ $self->pidl("TALLOC_FREE(p);");
+ $self->pidl("return NULL;");
+ $self->deindent;
+ $self->pidl("}");
+ $self->pidl("");
+
+ $self->pidl("return p;");
+ $self->deindent;
+ $self->pidl("}");
+ $self->pidl("");
+}
+
+sub ParsePipeChunkPush_State($$$)
+{
+ my ($self, $t, $cname) = @_;
+
+ my $state_str = "struct dcerpc_$cname\_push_state";
+ my $done_fn = "dcerpc_$cname\_push_done";
+
+ $self->pidl("$state_str {");
+ $self->indent;
+ $self->pidl("uint8_t dummy;");
+ $self->deindent;
+ $self->pidl("};");
+ $self->pidl("");
+
+ $self->pidl("static void $done_fn(struct tevent_req *subreq);");
+ $self->pidl("");
+}
+
+sub ParsePipeChunkPush_Send($$$)
+{
+ my ($self, $t, $cname) = @_;
+
+ my $fn_args = "";
+ my $state_str = "struct dcerpc_$cname\_push_state";
+ my $done_fn = "dcerpc_$cname\_push_done";
+ my $fn_str = "struct tevent_req *dcerpc_$cname\_push_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 $t->{NAME} *p";
+ $fn_args .= ",\n" . $pad . "const struct $cname *chunk";
+
+ $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("");
+
+ $self->pidl("subreq = dcerpc_pipe_handle_push_send(state, ev,");
+ $self->pidl("\t\t\tp->pc, chunk);");
+ $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("");
+
+ $self->pidl("return req;");
+ $self->deindent;
+ $self->pidl("}");
+ $self->pidl("");
+}
+
+sub ParsePipeChunkPush_Done($$$)
+{
+ my ($self, $t, $cname) = @_;
+
+ my $fn_args = "";
+ my $state_str = "struct dcerpc_$cname\_push_state";
+ my $fn_str = "static void dcerpc_$cname\_push_done";
+ my $pad = genpad($fn_str);
+
+ $fn_args .= "struct tevent_req *subreq";
+
+ $self->pidl("$fn_str($fn_args)");
+ $self->pidl("{");
+ $self->indent;
+ $self->pidl("struct tevent_req *req =");
+ $self->pidl("\ttevent_req_callback_data(subreq,");
+ $self->pidl("\tstruct tevent_req);");
+ # unused
+ #$self->pidl("$state_str *state =");
+ #$self->pidl("\ttevent_req_data(req,");
+ #$self->pidl("\t$state_str);");
+ $self->pidl("NTSTATUS status;");
+ $self->pidl("");
+
+ $self->pidl("status = dcerpc_pipe_handle_push_recv(subreq);");
+ $self->pidl("TALLOC_FREE(subreq);");
+ $self->pidl("if (tevent_req_nterror(req, status)) {");
+ $self->indent;
+ $self->pidl("return;");
+ $self->deindent;
+ $self->pidl("}");
+ $self->pidl("");
+
+ $self->pidl("tevent_req_done(req);");
+ $self->deindent;
+ $self->pidl("}");
+ $self->pidl("");
+}
+
+sub ParsePipeChunkPush_Recv($$$)
+{
+ my ($self, $t, $cname) = @_;
+
+ my $fn_args = "";
+ my $state_str = "struct dcerpc_$cname\_push_state";
+ my $fn_str = "NTSTATUS dcerpc_$cname\_push_recv";
+ my $pad = genpad($fn_str);
+
+ $fn_args .= "struct tevent_req *req";
+
+ $self->fn_declare("$fn_str($fn_args)");
+ $self->pidl("{");
+ $self->indent;
+ $self->pidl("$state_str *state =");
+ $self->pidl("\ttevent_req_data(req,");
+ $self->pidl("\t$state_str);");
+ $self->pidl("NTSTATUS status;");
+ $self->pidl("");
+
+ $self->pidl("if (tevent_req_is_nterror(req, &status)) {");
+ $self->indent;
+ $self->pidl("/*");
+ $self->pidl(" * We want the tevent_req_data() protection,");
+ $self->pidl(" * but don't need 'state', so just pretend we use it");
+ $self->pidl(" * in order to avoid compiler warnings.");
+ $self->pidl(" */");
+ $self->pidl("state->dummy = 0;");
+ $self->pidl("tevent_req_received(req);");
+ $self->pidl("return status;");
+ $self->deindent;
+ $self->pidl("}");
+ $self->pidl("");
+
+ $self->pidl("tevent_req_received(req);");
+ $self->pidl("return NT_STATUS_OK;");
+ $self->deindent;
+ $self->pidl("}");
+ $self->pidl("");
+}
+
+sub ParsePipeChunkPull_State($$$)
+{
+ my ($self, $t, $cname) = @_;
+
+ my $state_str = "struct dcerpc_$cname\_pull_state";
+ my $done_fn = "dcerpc_$cname\_pull_done";
+
+ $self->pidl("$state_str {");
+ $self->indent;
+ $self->pidl("struct $cname *chunk;");
+ $self->deindent;
+ $self->pidl("};");
+ $self->pidl("");
+
+ $self->pidl("static void $done_fn(struct tevent_req *subreq);");
+ $self->pidl("");
+}
+
+sub ParsePipeChunkPull_Send($$$)
+{
+ my ($self, $t, $cname) = @_;
+
+ my $fn_args = "";
+ my $state_str = "struct dcerpc_$cname\_pull_state";
+ my $done_fn = "dcerpc_$cname\_pull_done";
+ my $fn_str = "struct tevent_req *dcerpc_$cname\_pull_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 $t->{NAME} *p";
+
+ $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->chunk = talloc_zero(state, struct $cname);");
+ $self->pidl("if (tevent_req_nomem(state->chunk, req)) {");
+ $self->indent;
+ $self->pidl("return tevent_req_post(req, ev);");
+ $self->deindent;
+ $self->pidl("}");
+ $self->pidl("");
+
+ $self->pidl("subreq = dcerpc_pipe_handle_pull_send(state, ev,");
+ $self->pidl("\t\t\tp->pc, state->chunk, state->chunk);");
+ $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("");
+
+ $self->pidl("return req;");
+ $self->deindent;
+ $self->pidl("}");
+ $self->pidl("");
+}
+
+sub ParsePipeChunkPull_Done($$$)
+{
+ my ($self, $t, $cname) = @_;
+
+ my $fn_args = "";
+ my $state_str = "struct dcerpc_$cname\_pull_state";
+ my $fn_str = "static void dcerpc_$cname\_pull_done";
+ my $pad = genpad($fn_str);
+
+ $fn_args .= "struct tevent_req *subreq";
+
+ $self->pidl("$fn_str($fn_args)");
+ $self->pidl("{");
+ $self->indent;
+ $self->pidl("struct tevent_req *req =");
+ $self->pidl("\ttevent_req_callback_data(subreq,");
+ $self->pidl("\tstruct tevent_req);");
+ # unused
+ #$self->pidl("$state_str *state =");
+ #$self->pidl("\ttevent_req_data(req,");
+ #$self->pidl("\t$state_str);");
+ $self->pidl("NTSTATUS status;");
+ $self->pidl("");
+
+ $self->pidl("status = dcerpc_pipe_handle_pull_recv(subreq);");
+ $self->pidl("TALLOC_FREE(subreq);");
+ $self->pidl("if (tevent_req_nterror(req, status)) {");
+ $self->indent;
+ $self->pidl("return;");
+ $self->deindent;
+ $self->pidl("}");
+ $self->pidl("");
+
+ $self->pidl("tevent_req_done(req);");
+ $self->deindent;
+ $self->pidl("}");
+ $self->pidl("");
+}
+
+sub ParsePipeChunkPull_Recv($$$)
+{
+ my ($self, $t, $cname) = @_;
+
+ my $fn_args = "";
+ my $state_str = "struct dcerpc_$cname\_pull_state";
+ my $fn_str = "NTSTATUS dcerpc_$cname\_pull_recv";
+ my $pad = genpad($fn_str);
+
+ $fn_args .= "struct tevent_req *req";
+ $fn_args .= ",\n" . $pad . "TALLOC_CTX *mem_ctx";
+ $fn_args .= ",\n" . $pad . "struct $cname **chunk";
+
+ $self->fn_declare("$fn_str($fn_args)");
+ $self->pidl("{");
+ $self->indent;
+ $self->pidl("$state_str *state =");
+ $self->pidl("\ttevent_req_data(req,");
+ $self->pidl("\t$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("*chunk = talloc_move(mem_ctx, &state->chunk);");
+ $self->pidl("");
+
+ $self->pidl("tevent_req_received(req);");
+ $self->pidl("return NT_STATUS_OK;");
+ $self->deindent;
+ $self->pidl("}");
+ $self->pidl("");
+}
+
+sub ParsePipe($$)
+{
+ my ($self, $t) = @_;
+
+ my $pipe = $t;
+ $pipe = $t->{DATA} if ($t->{TYPE} eq "TYPEDEF");
+ my $struct = $pipe->{DATA};
+
+ my $name = "$struct->{NAME}";
+
+ $self->ParsePipeCreate($t, $name);
+
+ $self->ParsePipeChunkPush_State($t, $name);
+ $self->ParsePipeChunkPush_Send($t, $name);
+ $self->ParsePipeChunkPush_Done($t, $name);
+ $self->ParsePipeChunkPush_Recv($t, $name);
+
+ $self->ParsePipeChunkPull_State($t, $name);
+ $self->ParsePipeChunkPull_Send($t, $name);
+ $self->ParsePipeChunkPull_Done($t, $name);
+ $self->ParsePipeChunkPull_Recv($t, $name);
+ $self->pidl_both("");
+}
+
my %done;
#####################################################################
$self->pidl("/* $if->{NAME} - client functions generated by pidl */");
$self->pidl("");
+ foreach my $t (reverse @{$if->{TYPES}}) {
+ next unless Parse::Pidl::Typelist::typeIs($t, "PIPE");
+
+ $self->ParsePipe($t);
+ $done{$t->{NAME}} = 1;
+ }
+
foreach my $fn (@{$if->{FUNCTIONS}}) {
next if defined($done{$fn->{NAME}});
next if has_property($fn, "noopnum");