librpc/rpc/binding_handle.c cleanup
[metze/samba/wip.git] / librpc / rpc / binding_handle.c
index d96126478361b1fc1a182aaca0be425716555df9..d32a83081522026fd8faddcf6b5cad7f14943add 100644 (file)
@@ -453,6 +453,7 @@ NTSTATUS dcerpc_binding_handle_disconnect_recv(struct tevent_req *req)
 }
 
 struct dcerpc_binding_handle_call_params_state {
+       struct tevent_context *ev;
        struct dcerpc_binding_handle *h;
        const struct ndr_interface_call *call;
        struct dcerpc_binding_handle_call_params *params;
@@ -470,9 +471,11 @@ struct dcerpc_binding_handle_call_params_state {
        uint32_t out_pipe_idx;
 };
 
+static void dcerpc_binding_handle_call_params_cleanup(struct tevent_req *req);
 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_response(struct tevent_req *req);
 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);
 
@@ -504,6 +507,7 @@ struct tevent_req *dcerpc_binding_handle_call_params_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       state->ev = ev;
        state->h = h;
        state->call = &table->calls[opnum];
        state->params = params;
@@ -518,6 +522,8 @@ struct tevent_req *dcerpc_binding_handle_call_params_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       tevent_req_defer_callback(req, ev);
+
        /* setup for a ndr_push_* call */
        state->push = ndr_push_init_ctx(state);
        if (tevent_req_nomem(state->push, req)) {
@@ -665,6 +671,19 @@ struct tevent_req *dcerpc_binding_handle_call_params_send(TALLOC_CTX *mem_ctx,
        return req;
 }
 
+static void dcerpc_binding_handle_call_params_cleanup(struct tevent_req *req)
+{
+       struct dcerpc_binding_handle_call_params_state *state =
+               tevent_req_data(req,
+               struct dcerpc_binding_handle_call_params_state);
+
+       dcerpc_pipe_handle_connection_disconnect(state->pc);
+       state->pc = NULL;
+       state->call_pipe = NULL;
+
+       tevent_req_post(req, state->ev);
+}
+
 static void dcerpc_binding_handle_call_params_in_done(struct tevent_req *subreq)
 {
        struct tevent_req *req = tevent_req_callback_data(subreq,
@@ -674,6 +693,7 @@ static void dcerpc_binding_handle_call_params_in_done(struct tevent_req *subreq)
        error = dcerpc_binding_handle_raw_call_in_recv(subreq);
        TALLOC_FREE(subreq);
        if (tevent_req_nterror(req, error)) {
+               dcerpc_binding_handle_call_params_cleanup(req);
                return;
        }
 
@@ -706,6 +726,7 @@ static void dcerpc_binding_handle_call_params_next_pipe(struct tevent_req *req)
                                        state->ph);
                if (!ok) {
                        tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+                       dcerpc_binding_handle_call_params_cleanup(req);
                        return;
                }
 
@@ -725,12 +746,23 @@ static void dcerpc_binding_handle_call_params_next_pipe(struct tevent_req *req)
                                        state->ph);
                if (!ok) {
                        tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+                       dcerpc_binding_handle_call_params_cleanup(req);
                        return;
                }
 
                return;
        }
        state->out_pipe_idx = UINT32_MAX;
+
+       if (state->pull == NULL) {
+               return;
+       }
+
+       if (state->pull->flags & LIBNDR_FLAG_INCOMPLETE_BUFFER) {
+               return;
+       }
+
+       dcerpc_binding_handle_call_params_response(req);
 }
 
 static void dcerpc_binding_handle_call_params_done(struct tevent_req *subreq)
@@ -753,6 +785,7 @@ static void dcerpc_binding_handle_call_params_done(struct tevent_req *subreq)
        if (!NT_STATUS_IS_OK(error)) {
                TALLOC_FREE(state->subreq);
                tevent_req_nterror(req, error);
+               dcerpc_binding_handle_call_params_cleanup(req);
                return;
        }
 
@@ -766,12 +799,14 @@ static void dcerpc_binding_handle_call_params_done(struct tevent_req *subreq)
                 * this is a protocol error
                 */
                tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR);
+               dcerpc_binding_handle_call_params_cleanup(req);
                return;
        }
 
        if (state->pull == NULL) {
                state->pull = ndr_pull_init_blob(&state->response, state);
                if (tevent_req_nomem(state->pull, req)) {
+                       dcerpc_binding_handle_call_params_cleanup(req);
                        return;
                }
                state->pull->flags = state->push->flags;
@@ -790,16 +825,12 @@ static void dcerpc_binding_handle_call_params_done(struct tevent_req *subreq)
                        state->pull->flags &= ~LIBNDR_FLAG_INCOMPLETE_BUFFER;
                }
 
-               ndr_err = NDR_ERR_UNREAD_BYTES;// ndr_pull_append_blob(state->pull,
-               //                             &state->response);
+               ndr_err = ndr_pull_append(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);
+                       dcerpc_binding_handle_call_params_cleanup(req);
                        return;
                }
        }
@@ -809,6 +840,18 @@ static void dcerpc_binding_handle_call_params_done(struct tevent_req *subreq)
                return;
        }
 
+       dcerpc_binding_handle_call_params_response(req);
+}
+
+static void dcerpc_binding_handle_call_params_response(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 *h = state->h;
+       NTSTATUS error;
+       enum ndr_err_code ndr_err;
+
        state->pull->current_mem_ctx = state->params->r_mem;
 
        /* pull the structure from the blob */
@@ -822,6 +865,7 @@ static void dcerpc_binding_handle_call_params_done(struct tevent_req *subreq)
                                                state->call);
                }
                tevent_req_nterror(req, error);
+               dcerpc_binding_handle_call_params_cleanup(req);
                return;
        }
 
@@ -837,6 +881,7 @@ static void dcerpc_binding_handle_call_params_done(struct tevent_req *subreq)
                                                 state->call);
                if (!NT_STATUS_IS_OK(error)) {
                        tevent_req_nterror(req, error);
+                       dcerpc_binding_handle_call_params_cleanup(req);
                        return;
                }
        }
@@ -868,13 +913,12 @@ static int dcerpc_binding_handle_call_params_push_state_destructor(
        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;
+       if (!state->is_last_chunk) {
+               return 0;
+       }
+
+       dcerpc_binding_handle_call_params_next_pipe(pp->call_req);
        return 0;
 }
 
@@ -905,6 +949,9 @@ static struct tevent_req *dcerpc_binding_handle_call_params_push_send(TALLOC_CTX
        }
        state->ev = ev;
        state->p = p;
+       state->is_last_chunk = true;
+
+       tevent_req_defer_callback(req, state->ev);
 
        talloc_set_destructor(state,
                              dcerpc_binding_handle_call_params_push_state_destructor);
@@ -917,11 +964,6 @@ static struct tevent_req *dcerpc_binding_handle_call_params_push_send(TALLOC_CTX
 
        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,
@@ -929,11 +971,6 @@ static struct tevent_req *dcerpc_binding_handle_call_params_push_send(TALLOC_CTX
        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);
        }
@@ -994,9 +1031,6 @@ static void dcerpc_binding_handle_call_params_push_done(struct tevent_req *subre
        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);
@@ -1006,16 +1040,7 @@ static void dcerpc_binding_handle_call_params_push_done(struct tevent_req *subre
                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)
@@ -1028,6 +1053,7 @@ struct dcerpc_binding_handle_call_params_pull_state {
        struct dcerpc_pipe_handle *p;
        void *chunk_mem;
        void *chunk_ptr;
+       bool is_last_chunk;
 };
 
 static int dcerpc_binding_handle_call_params_pull_state_destructor(
@@ -1036,15 +1062,14 @@ static int dcerpc_binding_handle_call_params_pull_state_destructor(
        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;
+       if (!state->is_last_chunk) {
+               return 0;
+       }
+
+       dcerpc_binding_handle_call_params_next_pipe(pp->call_req);
        return 0;
 }
 
@@ -1071,16 +1096,32 @@ static struct tevent_req *dcerpc_binding_handle_call_params_pull_send(TALLOC_CTX
        state->p = p;
        state->chunk_mem = chunk_mem;
        state->chunk_ptr = chunk_ptr;
+       state->is_last_chunk = true;
 
-       dcerpc_binding_handle_call_params_pull_notify(req);
-       if (!tevent_req_is_in_progress(req)) {
+       if (talloc_total_blocks(chunk_mem) != 1) {
+               /*
+                * As we typically only have autogenerated callers
+                * of this function, we can enforce that chunk_mem
+                * doesn't have any talloc children yet.
+                *
+                * This makes the error handling in the
+                * NDR_ERR_INCOMPLETE_BUFFER case simpler.
+                */
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
                return tevent_req_post(req, ev);
        }
 
+       tevent_req_defer_callback(req, state->ev);
+
        talloc_set_destructor(state,
                              dcerpc_binding_handle_call_params_pull_state_destructor);
        pp->pull_req = req;
 
+       dcerpc_binding_handle_call_params_pull_notify(req);
+       if (!tevent_req_is_in_progress(req)) {
+               return tevent_req_post(req, ev);
+       }
+
        return req;
 }
 
@@ -1096,6 +1137,7 @@ static void dcerpc_binding_handle_call_params_pull_notify(struct tevent_req *req
                tevent_req_data(pp->call_req,
                struct dcerpc_binding_handle_call_params_state);
        enum ndr_err_code ndr_err;
+       struct ndr_pull *pipe_pull = NULL;
        const uint32_t *count = NULL;
 
        if (call_state->pull == NULL) {
@@ -1106,53 +1148,52 @@ static void dcerpc_binding_handle_call_params_pull_notify(struct tevent_req *req
                return;
        }
 
-       call_state->pull->current_mem_ctx = state->chunk_mem;
+       /*
+        * setup a shallow copy subcontext, which we might destroy
+        */
+       ndr_err = ndr_pull_subcontext_start(call_state->pull, &pipe_pull,
+                                           0xFFFFFFFF, 0);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               NTSTATUS error;
+               error = ndr_map_error2ntstatus(ndr_err);
+               tevent_req_nterror(req, error);
+               return;
+       }
 
-       /* pull the structure from the blob */
-       ndr_err = call_state->call_pipe->ndr_pull(call_state->pull,
+       /* pull the structure from the subcontext */
+       pipe_pull->current_mem_ctx = state->chunk_mem;
+       ndr_err = call_state->call_pipe->ndr_pull(pipe_pull,
                                                  NDR_SCALARS|NDR_BUFFERS,
                                                  state->chunk_ptr);
        if (ndr_err == NDR_ERR_INCOMPLETE_BUFFER) {
+               TALLOC_FREE(pipe_pull);
+               talloc_free_children(state->chunk_mem);
                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);
+       ndr_err = ndr_pull_subcontext_end(call_state->pull, pipe_pull,
+                                         0xFFFFFFFF, 0);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                NTSTATUS error;
                error = ndr_map_error2ntstatus(ndr_err);
                tevent_req_nterror(req, error);
                return;
        }
+       TALLOC_FREE(pipe_pull);
 
-       pp->pull_req = NULL;
-       talloc_set_destructor(state, NULL);
+       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;
+       }
 
        /*
         * Note: the first struct member is always
@@ -1161,13 +1202,13 @@ static void dcerpc_binding_handle_call_params_pull_notify(struct tevent_req *req
        count = (const uint32_t *)state->chunk_ptr;
 
        if (*count != 0) {
+               state->is_last_chunk = false;
                tevent_req_done(req);
                return;
        }
 
-       tevent_req_defer_callback(req, state->ev);
+       state->is_last_chunk = true;
        tevent_req_done(req);
-       dcerpc_binding_handle_call_params_next_pipe(pp->call_req);
 
 }