TODO librpc/rpc: add DCERPC pipe support to dcerpc_binding_handle_call_params_send...
authorStefan Metzmacher <metze@samba.org>
Sat, 31 Aug 2013 08:16:11 +0000 (10:16 +0200)
committerStefan Metzmacher <metze@samba.org>
Tue, 4 Jun 2019 11:14:55 +0000 (13:14 +0200)
librpc/rpc/binding_handle.c

index 42347f0470a547112f4fb5477bd75fd708af60dd..fd342cfa7628fe3362adf6493d219940caf3361b 100644 (file)
@@ -456,13 +456,25 @@ struct dcerpc_binding_handle_call_params_state {
        struct dcerpc_binding_handle *h;
        const struct ndr_interface_call *call;
        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;
+
+       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;
 };
 
+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,
@@ -474,7 +486,6 @@ struct tevent_req *dcerpc_binding_handle_call_params_send(TALLOC_CTX *mem_ctx,
 {
        struct tevent_req *req;
        struct dcerpc_binding_handle_call_params_state *state;
-       struct tevent_req *subreq;
        enum ndr_err_code ndr_err;
 
        req = tevent_req_create(mem_ctx, &state,
@@ -507,16 +518,6 @@ struct tevent_req *dcerpc_binding_handle_call_params_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
-       if (params->in.num_pipes != 0) {
-               tevent_req_nterror(req, NT_STATUS_RPC_CANNOT_SUPPORT);
-               return tevent_req_post(req, ev);
-       }
-
-       if (params->out.num_pipes != 0) {
-               tevent_req_nterror(req, NT_STATUS_RPC_CANNOT_SUPPORT);
-               return tevent_req_post(req, ev);
-       }
-
        /* setup for a ndr_push_* call */
        state->push = ndr_push_init_ctx(state);
        if (tevent_req_nomem(state->push, req)) {
@@ -529,6 +530,7 @@ struct tevent_req *dcerpc_binding_handle_call_params_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)) {
@@ -568,19 +570,167 @@ struct tevent_req *dcerpc_binding_handle_call_params_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(state->subreq)) {
+                       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_params_done, req);
 
        return req;
 }
 
+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);
+       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_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,
@@ -597,21 +747,64 @@ static void dcerpc_binding_handle_call_params_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_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->params->r_mem;
@@ -654,6 +847,374 @@ 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;
 };