sq librpc/rpc/binding_handle.c PIPE support
[metze/samba/wip.git] / librpc / rpc / binding_handle.c
index bb5245772e1ca75fc578bf5d573582a44a7287c0..0d3d36ac302d3eb707cb0c3da7ccedd10413be53 100644 (file)
@@ -167,6 +167,13 @@ struct tevent_req *dcerpc_binding_handle_raw_call_send(TALLOC_CTX *mem_ctx,
                object = h->object;
        }
 
+       if (state->ops->raw_call_in_send == NULL) {
+               if (in_flags & LIBNDR_FLAG_INCOMPLETE_BUFFER) {
+                       tevent_req_nterror(req, NT_STATUS_RPC_CANNOT_SUPPORT);
+                       return tevent_req_post(req, ev);
+               }
+       }
+
        state->subreq = state->ops->raw_call_send(state, ev, h,
                                                  object, opnum,
                                                  in_flags, in_data, in_length);
@@ -259,6 +266,11 @@ NTSTATUS dcerpc_binding_handle_raw_call(struct dcerpc_binding_handle *h,
                goto fail;
        }
 
+       if (in_flags & LIBNDR_FLAG_INCOMPLETE_BUFFER) {
+               talloc_free(frame);
+               return NT_STATUS_INVALID_PARAMETER_MIX;
+       }
+
        subreq = dcerpc_binding_handle_raw_call_send(frame, ev,
                                                     h, object, opnum,
                                                     in_flags,
@@ -282,6 +294,102 @@ fail:
        return status;
 }
 
+struct dcerpc_binding_handle_raw_call_in_state {
+       const struct dcerpc_binding_handle_ops *ops;
+       struct tevent_context *ev;
+       struct tevent_req *subreq;
+};
+
+static void dcerpc_binding_handle_raw_call_in_done(struct tevent_req *subreq);
+
+struct tevent_req *dcerpc_binding_handle_raw_call_in_send(TALLOC_CTX *mem_ctx,
+                                               struct tevent_context *ev,
+                                               struct tevent_req *raw_call_req,
+                                               uint32_t in_flags,
+                                               const uint8_t *in_data,
+                                               size_t in_length)
+{
+       struct dcerpc_binding_handle_raw_call_state *raw_call_state =
+               tevent_req_data(raw_call_req,
+               struct dcerpc_binding_handle_raw_call_state);
+       struct tevent_req *req;
+       struct dcerpc_binding_handle_raw_call_in_state *state;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct dcerpc_binding_handle_raw_call_in_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ops = raw_call_state->ops;
+       state->ev = ev;
+
+       if (state->ev != raw_call_state->ev) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+               return tevent_req_post(req, ev);
+       }
+
+       if (state->ops->raw_call_in_send == NULL) {
+               tevent_req_nterror(req, NT_STATUS_RPC_CANNOT_SUPPORT);
+               return tevent_req_post(req, ev);
+       }
+
+       state->subreq = state->ops->raw_call_in_send(state, ev,
+                                                    raw_call_state->subreq,
+                                                    in_flags,
+                                                    in_data,
+                                                    in_length);
+       if (tevent_req_nomem(state->subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       tevent_req_set_callback(state->subreq,
+                               dcerpc_binding_handle_raw_call_in_done,
+                               req);
+       return req;
+}
+
+static void dcerpc_binding_handle_raw_call_in_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+
+       if (tevent_req_is_in_progress(subreq)) {
+               tevent_req_notify_callback(req);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+NTSTATUS dcerpc_binding_handle_raw_call_in_recv(struct tevent_req *req)
+{
+       struct dcerpc_binding_handle_raw_call_in_state *state =
+               tevent_req_data(req,
+               struct dcerpc_binding_handle_raw_call_in_state);
+       NTSTATUS error;
+
+       if (!tevent_req_is_in_progress(req)) {
+               if (tevent_req_is_nterror(req, &error)) {
+                       tevent_req_received(req);
+                       return error;
+               }
+       }
+
+       error = state->ops->raw_call_in_recv(state->subreq);
+       if (!NT_STATUS_IS_OK(error)) {
+               tevent_req_received(req);
+               return error;
+       }
+
+       if (tevent_req_is_in_progress(state->subreq)) {
+               return NT_STATUS_OK;
+       }
+
+       tevent_req_received(req);
+       return error;
+}
+
 struct dcerpc_binding_handle_disconnect_state {
        const struct dcerpc_binding_handle_ops *ops;
 };
@@ -344,35 +452,44 @@ NTSTATUS dcerpc_binding_handle_disconnect_recv(struct tevent_req *req)
        return NT_STATUS_OK;
 }
 
-struct dcerpc_binding_handle_call_state {
+struct dcerpc_binding_handle_call_params_state {
        struct dcerpc_binding_handle *h;
        const struct ndr_interface_call *call;
-       TALLOC_CTX *r_mem;
-       void *r_ptr;
+       struct dcerpc_binding_handle_call_params *params;
+       uint32_t in_flags;
        struct ndr_push *push;
        DATA_BLOB request;
+       struct tevent_req *subreq;
        DATA_BLOB response;
        struct ndr_pull *pull;
-};
 
-static void dcerpc_binding_handle_call_done(struct tevent_req *subreq);
+       struct dcerpc_pipe_handle *ph;
+       struct dcerpc_pipe_handle_connection *pc;
+       const struct ndr_interface_call_pipe *call_pipe;
+       uint32_t in_pipe_idx;
+       uint32_t out_pipe_idx;
+};
 
-struct tevent_req *dcerpc_binding_handle_call_send(TALLOC_CTX *mem_ctx,
-                                       struct tevent_context *ev,
-                                       struct dcerpc_binding_handle *h,
-                                       const struct GUID *object,
-                                       const struct ndr_interface_table *table,
-                                       uint32_t opnum,
-                                       TALLOC_CTX *r_mem,
-                                       void *r_ptr)
+static void dcerpc_binding_handle_call_params_in_done(struct tevent_req *subreq);
+static void dcerpc_binding_handle_call_params_next_pipe(struct tevent_req *req);
+static void dcerpc_binding_handle_call_params_done(struct tevent_req *subreq);
+static void dcerpc_binding_handle_call_params_pipe_setup(struct tevent_req *call_req);
+static void dcerpc_binding_handle_call_params_pipe_notify(struct dcerpc_pipe_handle *p);
+
+struct tevent_req *dcerpc_binding_handle_call_params_send(TALLOC_CTX *mem_ctx,
+                               struct tevent_context *ev,
+                               struct dcerpc_binding_handle *h,
+                               const struct GUID *object,
+                               const struct ndr_interface_table *table,
+                               uint32_t opnum,
+                               struct dcerpc_binding_handle_call_params *params)
 {
        struct tevent_req *req;
-       struct dcerpc_binding_handle_call_state *state;
-       struct tevent_req *subreq;
+       struct dcerpc_binding_handle_call_params_state *state;
        enum ndr_err_code ndr_err;
 
        req = tevent_req_create(mem_ctx, &state,
-                               struct dcerpc_binding_handle_call_state);
+                               struct dcerpc_binding_handle_call_params_state);
        if (req == NULL) {
                return NULL;
        }
@@ -389,9 +506,17 @@ struct tevent_req *dcerpc_binding_handle_call_send(TALLOC_CTX *mem_ctx,
 
        state->h = h;
        state->call = &table->calls[opnum];
+       state->params = params;
 
-       state->r_mem = r_mem;
-       state->r_ptr = r_ptr;
+       if (params->in.num_pipes != state->call->in_pipes.num_pipes) {
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return tevent_req_post(req, ev);
+       }
+
+       if (params->out.num_pipes != state->call->out_pipes.num_pipes) {
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return tevent_req_post(req, ev);
+       }
 
        /* setup for a ndr_push_* call */
        state->push = ndr_push_init_ctx(state);
@@ -405,6 +530,7 @@ struct tevent_req *dcerpc_binding_handle_call_send(TALLOC_CTX *mem_ctx,
 
        if (h->ops->push_bigendian && h->ops->push_bigendian(h)) {
                state->push->flags |= LIBNDR_FLAG_BIGENDIAN;
+               state->in_flags |= LIBNDR_FLAG_BIGENDIAN;
        }
 
        if (h->ops->use_ndr64 && h->ops->use_ndr64(h)) {
@@ -413,17 +539,17 @@ struct tevent_req *dcerpc_binding_handle_call_send(TALLOC_CTX *mem_ctx,
 
        if (h->ops->do_ndr_print) {
                h->ops->do_ndr_print(h, NDR_IN | NDR_SET_VALUES,
-                                    state->r_ptr, state->call);
+                                    state->params->r_ptr, state->call);
        }
 
        /* push the structure into a blob */
-       ndr_err = state->call->ndr_push(state->push, NDR_IN, state->r_ptr);
+       ndr_err = state->call->ndr_push(state->push, NDR_IN, state->params->r_ptr);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                NTSTATUS error;
                error = ndr_map_error2ntstatus(ndr_err);
                if (h->ops->ndr_push_failed) {
                        h->ops->ndr_push_failed(h, error,
-                                               state->r_ptr,
+                                               state->params->r_ptr,
                                                state->call);
                }
                tevent_req_nterror(req, error);
@@ -444,26 +570,174 @@ struct tevent_req *dcerpc_binding_handle_call_send(TALLOC_CTX *mem_ctx,
                }
        }
 
-       subreq = dcerpc_binding_handle_raw_call_send(state, ev,
-                                                    h, object, opnum,
-                                                    state->push->flags,
-                                                    state->request.data,
-                                                    state->request.length);
-       if (tevent_req_nomem(subreq, req)) {
+       if (params->in.num_pipes != 0) {
+               state->in_flags |= LIBNDR_FLAG_INCOMPLETE_BUFFER;
+
+               /*
+                * push alignment for the next pipe chunk
+                */
+               ndr_err = ndr_push_align(state->push, 5);
+               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+                       NTSTATUS error;
+                       error = ndr_map_error2ntstatus(ndr_err);
+                       tevent_req_nterror(req, error);
+                       return tevent_req_post(req, ev);
+               }
+       }
+
+       if (params->out.num_pipes != 0) {
+               /*
+                * even if we only have output pipes we need to indicate that
+                * we want to get incomplete results
+                */
+               state->in_flags |= LIBNDR_FLAG_INCOMPLETE_BUFFER;
+       }
+
+       if (state->in_flags & LIBNDR_FLAG_INCOMPLETE_BUFFER) {
+               dcerpc_binding_handle_call_params_pipe_setup(req);
+               if (!tevent_req_is_in_progress(req)) {
+                       return tevent_req_post(req, ev);
+               }
+       }
+
+       /* retrieve the blob - including possible pipe chunk alignment */
+       state->request = ndr_push_blob(state->push);
+
+       state->subreq = dcerpc_binding_handle_raw_call_send(state, ev,
+                                               h, object, opnum,
+                                               state->in_flags,
+                                               state->request.data,
+                                               state->request.length);
+       if (tevent_req_nomem(state->subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(state->subreq,
+                               dcerpc_binding_handle_call_params_done,
+                               req);
+
+       if (!(state->in_flags & LIBNDR_FLAG_INCOMPLETE_BUFFER)) {
+               /*
+                * we didn't ask for any pipes
+                *
+                * Indicate that all pipes are done.
+                */
+               state->in_pipe_idx = UINT32_MAX;
+               state->out_pipe_idx = UINT32_MAX;
+               return req;
+       }
+
+       /*
+        * If the subreq is already finished, the backend
+        * may not support LIBNDR_FLAG_INCOMPLETE_BUFFER
+        */
+       if (!tevent_req_is_in_progress(state->subreq)) {
+               return req;
+       }
+
+       if (state->params->in.num_pipes == 0) {
+               struct tevent_req *subreq;
+
+               /*
+                * We have only out pipes,
+                * so indicate that we're done with sending in_data.
+                */
+               state->in_pipe_idx = UINT32_MAX;
+               state->in_flags &= ~LIBNDR_FLAG_INCOMPLETE_BUFFER;
+               subreq = dcerpc_binding_handle_raw_call_in_send(state, ev,
+                                                               state->subreq,
+                                                               state->in_flags,
+                                                               NULL, /* in_data */
+                                                               0); /* in_length */
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq,
+                                       dcerpc_binding_handle_call_params_in_done,
+                                       req);
+               return req;
+       }
+
+       dcerpc_binding_handle_call_params_next_pipe(req);
+       if (!tevent_req_is_in_progress(req)) {
                return tevent_req_post(req, ev);
        }
-       tevent_req_set_callback(subreq, dcerpc_binding_handle_call_done, req);
 
        return req;
 }
 
-static void dcerpc_binding_handle_call_done(struct tevent_req *subreq)
+static void dcerpc_binding_handle_call_params_in_done(struct tevent_req *subreq)
 {
        struct tevent_req *req = tevent_req_callback_data(subreq,
                                 struct tevent_req);
-       struct dcerpc_binding_handle_call_state *state =
+       NTSTATUS error;
+
+       error = dcerpc_binding_handle_raw_call_in_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, error)) {
+               return;
+       }
+
+       dcerpc_binding_handle_call_params_next_pipe(req);
+}
+
+static void dcerpc_binding_handle_call_params_next_pipe(struct tevent_req *req)
+{
+       struct dcerpc_binding_handle_call_params_state *state =
                tevent_req_data(req,
-               struct dcerpc_binding_handle_call_state);
+               struct dcerpc_binding_handle_call_params_state);
+       struct dcerpc_binding_handle_call_params *params = state->params;
+       bool ok;
+
+       dcerpc_pipe_handle_connection_disconnect(state->pc);
+       state->pc = NULL;
+       state->call_pipe = NULL;
+
+       if (state->in_pipe_idx < params->in.num_pipes) {
+               uint32_t idx = state->in_pipe_idx++;
+
+               state->pc = params->in.pipes[idx];
+               state->call_pipe = &state->call->in_pipes.pipes[idx];
+
+               ok = dcerpc_pipe_handle_connection_push_connect(state->pc,
+                                       state->call_pipe->chunk_struct_name,
+                                       state->call_pipe->chunk_struct_size,
+                                       state->ph);
+               if (!ok) {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+                       return;
+               }
+
+               return;
+       }
+       state->in_pipe_idx = UINT32_MAX;
+
+       if (state->out_pipe_idx < params->out.num_pipes) {
+               uint32_t idx = state->out_pipe_idx++;
+
+               state->pc = params->out.pipes[idx];
+               state->call_pipe = &state->call->out_pipes.pipes[idx];
+
+               ok = dcerpc_pipe_handle_connection_pull_connect(state->pc,
+                                       state->call_pipe->chunk_struct_name,
+                                       state->call_pipe->chunk_struct_size,
+                                       state->ph);
+               if (!ok) {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+                       return;
+               }
+
+               return;
+       }
+       state->out_pipe_idx = UINT32_MAX;
+}
+
+static void dcerpc_binding_handle_call_params_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(subreq,
+                                struct tevent_req);
+       struct dcerpc_binding_handle_call_params_state *state =
+               tevent_req_data(req,
+               struct dcerpc_binding_handle_call_params_state);
        struct dcerpc_binding_handle *h = state->h;
        NTSTATUS error;
        uint32_t out_flags = 0;
@@ -473,27 +747,71 @@ static void dcerpc_binding_handle_call_done(struct tevent_req *subreq)
                                                    &state->response.data,
                                                    &state->response.length,
                                                    &out_flags);
-       TALLOC_FREE(subreq);
-       if (tevent_req_nterror(req, error)) {
+       subreq = NULL;
+       if (!NT_STATUS_IS_OK(error)) {
+               TALLOC_FREE(state->subreq);
+               tevent_req_nterror(req, error);
                return;
        }
 
-       state->pull = ndr_pull_init_blob(&state->response, state);
-       if (tevent_req_nomem(state->pull, req)) {
+       if (!(out_flags & LIBNDR_FLAG_INCOMPLETE_BUFFER)) {
+               TALLOC_FREE(state->subreq);
+       }
+
+       if (state->in_pipe_idx != UINT32_MAX) {
+               /*
+                * we haven't send all data yet,
+                * this is a protocol error
+                */
+               tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR);
                return;
        }
-       state->pull->flags = state->push->flags;
 
-       if (out_flags & LIBNDR_FLAG_BIGENDIAN) {
-               state->pull->flags |= LIBNDR_FLAG_BIGENDIAN;
+       if (state->pull == NULL) {
+               state->pull = ndr_pull_init_blob(&state->response, state);
+               if (tevent_req_nomem(state->pull, req)) {
+                       return;
+               }
+               state->pull->flags = state->push->flags;
+
+               if (out_flags & LIBNDR_FLAG_BIGENDIAN) {
+                       state->pull->flags |= LIBNDR_FLAG_BIGENDIAN;
+               } else {
+                       state->pull->flags &= ~LIBNDR_FLAG_BIGENDIAN;
+               }
+
+               if (out_flags & LIBNDR_FLAG_INCOMPLETE_BUFFER) {
+                       state->pull->flags |= LIBNDR_FLAG_INCOMPLETE_BUFFER;
+               }
        } else {
-               state->pull->flags &= ~LIBNDR_FLAG_BIGENDIAN;
+               if (!(out_flags & LIBNDR_FLAG_INCOMPLETE_BUFFER)) {
+                       state->pull->flags &= ~LIBNDR_FLAG_INCOMPLETE_BUFFER;
+               }
+
+               ndr_err = NDR_ERR_UNREAD_BYTES;// ndr_pull_append_blob(state->pull,
+               //                             &state->response);
+               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+                       error = ndr_map_error2ntstatus(ndr_err);
+                       if (h->ops->ndr_pull_failed) {
+                               h->ops->ndr_pull_failed(h, error,
+                                                       &state->response,
+                                                       state->call);
+                       }
+                       tevent_req_nterror(req, error);
+                       return;
+               }
+       }
+
+       if (state->out_pipe_idx != UINT32_MAX) {
+               dcerpc_binding_handle_call_params_pipe_notify(state->ph);
+               return;
        }
 
-       state->pull->current_mem_ctx = state->r_mem;
+       state->pull->current_mem_ctx = state->params->r_mem;
 
        /* pull the structure from the blob */
-       ndr_err = state->call->ndr_pull(state->pull, NDR_OUT, state->r_ptr);
+       ndr_err = state->call->ndr_pull(state->pull, NDR_OUT,
+                                       state->params->r_ptr);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                error = ndr_map_error2ntstatus(ndr_err);
                if (h->ops->ndr_pull_failed) {
@@ -507,13 +825,13 @@ static void dcerpc_binding_handle_call_done(struct tevent_req *subreq)
 
        if (h->ops->do_ndr_print) {
                h->ops->do_ndr_print(h, NDR_OUT,
-                                    state->r_ptr, state->call);
+                                    state->params->r_ptr, state->call);
        }
 
        if (h->ops->ndr_validate_out) {
                error = h->ops->ndr_validate_out(h,
                                                 state->pull,
-                                                state->r_ptr,
+                                                state->params->r_ptr,
                                                 state->call);
                if (!NT_STATUS_IS_OK(error)) {
                        tevent_req_nterror(req, error);
@@ -524,6 +842,433 @@ static void dcerpc_binding_handle_call_done(struct tevent_req *subreq)
        tevent_req_done(req);
 }
 
+NTSTATUS dcerpc_binding_handle_call_params_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct dcerpc_binding_handle_call_params_pipe {
+       struct tevent_req *call_req;
+       struct tevent_req *pull_req;
+};
+
+struct dcerpc_binding_handle_call_params_push_state {
+       struct tevent_context *ev;
+       struct dcerpc_pipe_handle *p;
+       struct ndr_push *push;
+       DATA_BLOB chunk_blob;
+       bool is_last_chunk;
+};
+
+static int dcerpc_binding_handle_call_params_push_state_destructor(
+       struct dcerpc_binding_handle_call_params_push_state *state)
+{
+       struct dcerpc_binding_handle_call_params_pipe *pp =
+               dcerpc_pipe_handle_data(state->p,
+               struct dcerpc_binding_handle_call_params_pipe);
+       struct dcerpc_binding_handle_call_params_state *call_state =
+               tevent_req_data(pp->call_req,
+               struct dcerpc_binding_handle_call_params_state);
+
+       dcerpc_pipe_handle_connection_disconnect(call_state->pc);
+       call_state->pc = NULL;
+       call_state->call_pipe = NULL;
+       return 0;
+}
+
+static void dcerpc_binding_handle_call_params_push_done(struct tevent_req *subreq);
+
+static struct tevent_req *dcerpc_binding_handle_call_params_push_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct dcerpc_pipe_handle *p,
+                                       const void *chunk_ptr)
+{
+       struct dcerpc_binding_handle_call_params_pipe *pp =
+               dcerpc_pipe_handle_data(p,
+               struct dcerpc_binding_handle_call_params_pipe);
+       struct dcerpc_binding_handle_call_params_state *call_state =
+               tevent_req_data(pp->call_req,
+               struct dcerpc_binding_handle_call_params_state);
+       struct tevent_req *req;
+       struct dcerpc_binding_handle_call_params_push_state *state;
+       struct tevent_req *subreq;
+       enum ndr_err_code ndr_err;
+       const uint32_t *count = NULL;
+       bool is_last_pipe = false;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct dcerpc_binding_handle_call_params_push_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->p = p;
+
+       talloc_set_destructor(state,
+                             dcerpc_binding_handle_call_params_push_state_destructor);
+
+       /* setup for a ndr_push_* call */
+       state->push = ndr_push_init_ctx(state);
+       if (tevent_req_nomem(state->push, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->push->flags = call_state->push->flags;
+
+       //if (h->ops->do_ndr_print) {
+       //      h->ops->do_ndr_print(h, NDR_IN | NDR_SET_VALUES,
+       //                           state->params->r_ptr, state->call);
+       //}
+
+       /* push the structure into a blob */
+       ndr_err = call_state->call_pipe->ndr_push(state->push,
+                                                 NDR_SCALARS|NDR_BUFFERS,
+                                                 chunk_ptr);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               NTSTATUS error;
+               error = ndr_map_error2ntstatus(ndr_err);
+               //if (h->ops->ndr_push_failed) {
+               //      h->ops->ndr_push_failed(h, error,
+               //                              state->params->r_ptr,
+               //                              state->call);
+               //}
+               tevent_req_nterror(req, error);
+               return tevent_req_post(req, ev);
+       }
+
+       /*
+        * Note: the first struct member is always
+        * 'uint32_t count;'
+        */
+       count = (const uint32_t *)chunk_ptr;
+
+       state->is_last_chunk = false;
+       if (*count == 0) {
+               state->is_last_chunk = true;
+       }
+
+       if (call_state->in_pipe_idx >= call_state->params->in.num_pipes) {
+               is_last_pipe = true;
+       }
+
+       if (is_last_pipe && state->is_last_chunk) {
+               call_state->in_flags &= ~LIBNDR_FLAG_INCOMPLETE_BUFFER;
+       } else {
+               /*
+                * push alignment for the next pipe chunk
+                */
+               ndr_err = ndr_push_align(state->push, 5);
+               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+                       NTSTATUS error;
+                       error = ndr_map_error2ntstatus(ndr_err);
+                       tevent_req_nterror(req, error);
+                       return tevent_req_post(req, ev);
+               }
+       }
+
+       /* retrieve the blob - including possible alignment for the next chunk */
+       state->chunk_blob = ndr_push_blob(state->push);
+
+       subreq = dcerpc_binding_handle_raw_call_in_send(state, ev,
+                                                       call_state->subreq,
+                                                       call_state->in_flags,
+                                                       state->chunk_blob.data,
+                                                       state->chunk_blob.length);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq,
+                               dcerpc_binding_handle_call_params_push_done,
+                               req);
+
+       return req;
+}
+
+static void dcerpc_binding_handle_call_params_push_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct dcerpc_binding_handle_call_params_push_state *state =
+               tevent_req_data(req,
+               struct dcerpc_binding_handle_call_params_push_state);
+       struct dcerpc_binding_handle_call_params_pipe *pp =
+               dcerpc_pipe_handle_data(state->p,
+               struct dcerpc_binding_handle_call_params_pipe);
+       NTSTATUS status;
+
+       status = dcerpc_binding_handle_raw_call_in_recv(subreq);
+       TALLOC_FREE(subreq);
+       TALLOC_FREE(state->push);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       talloc_set_destructor(state, NULL);
+
+       if (!state->is_last_chunk) {
+               tevent_req_done(req);
+               return;
+       }
+
+       tevent_req_defer_callback(req, state->ev);
+       tevent_req_done(req);
+       dcerpc_binding_handle_call_params_next_pipe(pp->call_req);
+}
+
+static NTSTATUS dcerpc_binding_handle_call_params_push_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct dcerpc_binding_handle_call_params_pull_state {
+       struct tevent_context *ev;
+       struct dcerpc_pipe_handle *p;
+       void *chunk_mem;
+       void *chunk_ptr;
+};
+
+static int dcerpc_binding_handle_call_params_pull_state_destructor(
+       struct dcerpc_binding_handle_call_params_pull_state *state)
+{
+       struct dcerpc_binding_handle_call_params_pipe *pp =
+               dcerpc_pipe_handle_data(state->p,
+               struct dcerpc_binding_handle_call_params_pipe);
+       struct dcerpc_binding_handle_call_params_state *call_state =
+               tevent_req_data(pp->call_req,
+               struct dcerpc_binding_handle_call_params_state);
+
+       pp->pull_req = NULL;
+
+       dcerpc_pipe_handle_connection_disconnect(call_state->pc);
+       call_state->pc = NULL;
+       call_state->call_pipe = NULL;
+       return 0;
+}
+
+static void dcerpc_binding_handle_call_params_pull_notify(struct tevent_req *req);
+
+static struct tevent_req *dcerpc_binding_handle_call_params_pull_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct dcerpc_pipe_handle *p,
+                                       void *chunk_mem,
+                                       void *chunk_ptr)
+{
+       struct dcerpc_binding_handle_call_params_pipe *pp =
+               dcerpc_pipe_handle_data(p,
+               struct dcerpc_binding_handle_call_params_pipe);
+       struct tevent_req *req;
+       struct dcerpc_binding_handle_call_params_pull_state *state;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct dcerpc_binding_handle_call_params_pull_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->p = p;
+       state->chunk_mem = chunk_mem;
+       state->chunk_ptr = chunk_ptr;
+
+       dcerpc_binding_handle_call_params_pull_notify(req);
+       if (!tevent_req_is_in_progress(req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       talloc_set_destructor(state,
+                             dcerpc_binding_handle_call_params_pull_state_destructor);
+       pp->pull_req = req;
+
+       return req;
+}
+
+static void dcerpc_binding_handle_call_params_pull_notify(struct tevent_req *req)
+{
+       struct dcerpc_binding_handle_call_params_pull_state *state =
+               tevent_req_data(req,
+               struct dcerpc_binding_handle_call_params_pull_state);
+       struct dcerpc_binding_handle_call_params_pipe *pp =
+               dcerpc_pipe_handle_data(state->p,
+               struct dcerpc_binding_handle_call_params_pipe);
+       struct dcerpc_binding_handle_call_params_state *call_state =
+               tevent_req_data(pp->call_req,
+               struct dcerpc_binding_handle_call_params_state);
+       enum ndr_err_code ndr_err;
+       const uint32_t *count = NULL;
+
+       if (call_state->pull == NULL) {
+               return;
+       }
+
+       if (call_state->pull->data_size == 0) {
+               return;
+       }
+
+       call_state->pull->current_mem_ctx = state->chunk_mem;
+
+       /* pull the structure from the blob */
+       ndr_err = call_state->call_pipe->ndr_pull(call_state->pull,
+                                                 NDR_SCALARS|NDR_BUFFERS,
+                                                 state->chunk_ptr);
+       if (ndr_err == NDR_ERR_INCOMPLETE_BUFFER) {
+               return;
+       }
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               NTSTATUS error;
+               error = ndr_map_error2ntstatus(ndr_err);
+               //if (h->ops->ndr_pull_failed) {
+               //      h->ops->ndr_pull_failed(h, error,
+               //                              &state->response,
+               //                              state->call);
+               //}
+               tevent_req_nterror(req, error);
+               return;
+       }
+
+       //if (h->ops->do_ndr_print) {
+       //      h->ops->do_ndr_print(h, NDR_OUT,
+       //                           state->params->r_ptr, state->call);
+       //}
+
+       //if (h->ops->ndr_validate_out) {
+       //      error = h->ops->ndr_validate_out(h,
+       //                                       state->pull,
+       //                                       state->params->r_ptr,
+       //                                       state->call);
+       //      if (!NT_STATUS_IS_OK(error)) {
+       //              tevent_req_nterror(req, error);
+       //              return;
+       //      }
+       //}
+
+       //ndr_err = ndr_pull_pop(call_state->pull);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               NTSTATUS error;
+               error = ndr_map_error2ntstatus(ndr_err);
+               tevent_req_nterror(req, error);
+               return;
+       }
+
+       pp->pull_req = NULL;
+       talloc_set_destructor(state, NULL);
+
+       /*
+        * Note: the first struct member is always
+        * 'uint32_t count;'
+        */
+       count = (const uint32_t *)state->chunk_ptr;
+
+       if (*count != 0) {
+               tevent_req_done(req);
+               return;
+       }
+
+       tevent_req_defer_callback(req, state->ev);
+       tevent_req_done(req);
+       dcerpc_binding_handle_call_params_next_pipe(pp->call_req);
+
+}
+
+static NTSTATUS dcerpc_binding_handle_call_params_pull_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+static struct dcerpc_pipe_handle_ops dcerpc_binding_handle_call_params_pipe_ops = {
+       .name = "dcerpc_binding_handle_call_params_pipe",
+
+       .chunk_push_send = dcerpc_binding_handle_call_params_push_send,
+       .chunk_push_recv = dcerpc_binding_handle_call_params_push_recv,
+
+       .chunk_pull_send = dcerpc_binding_handle_call_params_pull_send,
+       .chunk_pull_recv = dcerpc_binding_handle_call_params_pull_recv,
+};
+
+static void dcerpc_binding_handle_call_params_pipe_setup(struct tevent_req *call_req)
+{
+       struct dcerpc_binding_handle_call_params_state *call_state =
+               tevent_req_data(call_req,
+               struct dcerpc_binding_handle_call_params_state);
+       struct dcerpc_binding_handle_call_params_pipe *pp;
+
+       call_state->ph = dcerpc_pipe_handle_create(call_state,
+                               &dcerpc_binding_handle_call_params_pipe_ops,
+                               &pp,
+                               struct dcerpc_binding_handle_call_params_pipe);
+       if (tevent_req_nomem(call_state->ph, call_req)) {
+               return;
+       }
+
+       pp->call_req = call_req;
+};
+
+static void dcerpc_binding_handle_call_params_pipe_notify(struct dcerpc_pipe_handle *p)
+{
+       struct dcerpc_binding_handle_call_params_pipe *pp =
+               dcerpc_pipe_handle_data(p,
+               struct dcerpc_binding_handle_call_params_pipe);
+
+       if (pp->pull_req == NULL) {
+               return;
+       }
+
+       dcerpc_binding_handle_call_params_pull_notify(pp->pull_req);
+}
+
+struct dcerpc_binding_handle_call_state {
+       struct dcerpc_binding_handle_call_params params;
+};
+
+static void dcerpc_binding_handle_call_done(struct tevent_req *subreq);
+
+struct tevent_req *dcerpc_binding_handle_call_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct dcerpc_binding_handle *h,
+                                       const struct GUID *object,
+                                       const struct ndr_interface_table *table,
+                                       uint32_t opnum,
+                                       TALLOC_CTX *r_mem,
+                                       void *r_ptr)
+{
+       struct tevent_req *req;
+       struct dcerpc_binding_handle_call_state *state;
+       struct tevent_req *subreq;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct dcerpc_binding_handle_call_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->params.r_mem = r_mem;
+       state->params.r_ptr = r_ptr;
+
+       subreq = dcerpc_binding_handle_call_params_send(state, ev, h,
+                                                       object, table, opnum,
+                                                       &state->params);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, dcerpc_binding_handle_call_done, req);
+
+       return req;
+}
+
+static void dcerpc_binding_handle_call_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(subreq,
+                                struct tevent_req);
+       NTSTATUS error;
+
+       error = dcerpc_binding_handle_call_params_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, error)) {
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
 NTSTATUS dcerpc_binding_handle_call_recv(struct tevent_req *req)
 {
        return tevent_req_simple_recv_ntstatus(req);
@@ -539,6 +1284,7 @@ NTSTATUS dcerpc_binding_handle_call(struct dcerpc_binding_handle *h,
        TALLOC_CTX *frame = talloc_stackframe();
        struct tevent_context *ev;
        struct tevent_req *subreq;
+       struct dcerpc_binding_handle_call_params params;
        NTSTATUS status = NT_STATUS_NO_MEMORY;
 
        /*
@@ -554,9 +1300,13 @@ NTSTATUS dcerpc_binding_handle_call(struct dcerpc_binding_handle *h,
                goto fail;
        }
 
-       subreq = dcerpc_binding_handle_call_send(frame, ev,
-                                                h, object, table,
-                                                opnum, r_mem, r_ptr);
+       ZERO_STRUCT(params);
+       params.r_mem = r_mem;
+       params.r_ptr = r_ptr;
+
+       subreq = dcerpc_binding_handle_call_params_send(frame, ev, h,
+                                                       object, table, opnum,
+                                                       &params);
        if (subreq == NULL) {
                goto fail;
        }
@@ -565,7 +1315,7 @@ NTSTATUS dcerpc_binding_handle_call(struct dcerpc_binding_handle *h,
                goto fail;
        }
 
-       status = dcerpc_binding_handle_call_recv(subreq);
+       status = dcerpc_binding_handle_call_params_recv(subreq);
 fail:
        TALLOC_FREE(frame);
        return status;