CVE-2015-5370: s4:librpc/rpc: avoid dereferencing sec->auth_info in dcerpc_request_pr...
[samba.git] / source4 / librpc / rpc / dcerpc.c
index 3d5080734aba21c8f325e0e87836f8b4da6b2773..319741f06c5dd94eda27d65164f14dac63d260a3 100644 (file)
 */
 
 #include "includes.h"
+#include "system/filesys.h"
 #include "../lib/util/dlinklist.h"
 #include "lib/events/events.h"
 #include "librpc/rpc/dcerpc.h"
 #include "librpc/rpc/dcerpc_proto.h"
 #include "librpc/gen_ndr/ndr_misc.h"
 #include "librpc/gen_ndr/ndr_dcerpc.h"
-#include "libcli/composite/composite.h"
 #include "auth/gensec/gensec.h"
 #include "param/param.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "librpc/rpc/rpc_common.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/smb/tstream_smbXcli_np.h"
 
-_PUBLIC_ NTSTATUS dcerpc_init(struct loadparm_context *lp_ctx)
+
+enum rpc_request_state {
+       RPC_REQUEST_QUEUED,
+       RPC_REQUEST_PENDING,
+       RPC_REQUEST_DONE
+};
+
+/*
+  handle for an async dcerpc request
+*/
+struct rpc_request {
+       struct rpc_request *next, *prev;
+       struct dcerpc_pipe *p;
+       NTSTATUS status;
+       uint32_t call_id;
+       enum rpc_request_state state;
+       DATA_BLOB payload;
+       uint32_t flags;
+       uint32_t fault_code;
+
+       /* this is used to distinguish bind and alter_context requests
+          from normal requests */
+       void (*recv_handler)(struct rpc_request *conn, 
+                            DATA_BLOB *blob, struct ncacn_packet *pkt);
+
+       const struct GUID *object;
+       uint16_t opnum;
+       DATA_BLOB request_data;
+       bool ignore_timeout;
+       bool wait_for_sync;
+       bool verify_bitmask1;
+       bool verify_pcontext;
+
+       struct {
+               void (*callback)(struct rpc_request *);
+               void *private_data;
+       } async;
+};
+
+_PUBLIC_ NTSTATUS dcerpc_init(void)
 {
-       return gensec_init(lp_ctx);
+       return gensec_init();
 }
 
-static void dcerpc_connection_dead(struct dcerpc_connection *conn, NTSTATUS status);
-static void dcerpc_ship_next_request(struct dcerpc_connection *c);
+static void dcerpc_connection_dead(struct dcecli_connection *conn, NTSTATUS status);
+static void dcerpc_schedule_io_trigger(struct dcecli_connection *c);
+
+static struct rpc_request *dcerpc_request_send(TALLOC_CTX *mem_ctx,
+                                              struct dcerpc_pipe *p,
+                                              const struct GUID *object,
+                                              uint16_t opnum,
+                                              DATA_BLOB *stub_data);
+static NTSTATUS dcerpc_request_recv(struct rpc_request *req,
+                                   TALLOC_CTX *mem_ctx,
+                                   DATA_BLOB *stub_data);
+static NTSTATUS dcerpc_ndr_validate_in(struct dcecli_connection *c,
+                                      TALLOC_CTX *mem_ctx,
+                                      DATA_BLOB blob,
+                                      size_t struct_size,
+                                      ndr_push_flags_fn_t ndr_push,
+                                      ndr_pull_flags_fn_t ndr_pull);
+static NTSTATUS dcerpc_ndr_validate_out(struct dcecli_connection *c,
+                                       struct ndr_pull *pull_in,
+                                       void *struct_ptr,
+                                       size_t struct_size,
+                                       ndr_push_flags_fn_t ndr_push,
+                                       ndr_pull_flags_fn_t ndr_pull,
+                                       ndr_print_function_t ndr_print);
+static NTSTATUS dcerpc_shutdown_pipe(struct dcecli_connection *p, NTSTATUS status);
+static NTSTATUS dcerpc_send_request(struct dcecli_connection *p, DATA_BLOB *data,
+                            bool trigger_read);
+static NTSTATUS dcerpc_send_read(struct dcecli_connection *p);
 
 /* destroy a dcerpc connection */
-static int dcerpc_connection_destructor(struct dcerpc_connection *conn)
+static int dcerpc_connection_destructor(struct dcecli_connection *conn)
 {
        if (conn->dead) {
                conn->free_skipped = true;
@@ -54,12 +123,12 @@ static int dcerpc_connection_destructor(struct dcerpc_connection *conn)
 /* initialise a dcerpc connection. 
    the event context is optional
 */
-static struct dcerpc_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx, 
+static struct dcecli_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx, 
                                                 struct tevent_context *ev)
 {
-       struct dcerpc_connection *c;
+       struct dcecli_connection *c;
 
-       c = talloc_zero(mem_ctx, struct dcerpc_connection);
+       c = talloc_zero(mem_ctx, struct dcecli_connection);
        if (!c) {
                return NULL;
        }
@@ -72,26 +141,493 @@ static struct dcerpc_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx,
        }
 
        c->call_id = 1;
+       c->security_state.auth_type = DCERPC_AUTH_TYPE_NONE;
+       c->security_state.auth_level = DCERPC_AUTH_LEVEL_NONE;
+       c->security_state.auth_context_id = 0;
        c->security_state.auth_info = NULL;
        c->security_state.session_key = dcerpc_generic_session_key;
        c->security_state.generic_state = NULL;
-       c->binding_string = NULL;
        c->flags = 0;
-       c->srv_max_xmit_frag = 0;
-       c->srv_max_recv_frag = 0;
+       /*
+        * Windows uses 5840 for ncacn_ip_tcp,
+        * so we also use it (for every transport)
+        * by default. But we give the transport
+        * the chance to overwrite it.
+        */
+       c->srv_max_xmit_frag = 5840;
+       c->srv_max_recv_frag = 5840;
        c->pending = NULL;
 
+       c->io_trigger = tevent_create_immediate(c);
+       if (c->io_trigger == NULL) {
+               talloc_free(c);
+               return NULL;
+       }
+
        talloc_set_destructor(c, dcerpc_connection_destructor);
 
        return c;
 }
 
+struct dcerpc_bh_state {
+       struct dcerpc_pipe *p;
+};
+
+static bool dcerpc_bh_is_connected(struct dcerpc_binding_handle *h)
+{
+       struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+                                    struct dcerpc_bh_state);
+
+       if (!hs->p) {
+               return false;
+       }
+
+       if (!hs->p->conn) {
+               return false;
+       }
+
+       if (hs->p->conn->dead) {
+               return false;
+       }
+
+       return true;
+}
+
+static uint32_t dcerpc_bh_set_timeout(struct dcerpc_binding_handle *h,
+                                     uint32_t timeout)
+{
+       struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+                                    struct dcerpc_bh_state);
+       uint32_t old;
+
+       if (!hs->p) {
+               return DCERPC_REQUEST_TIMEOUT;
+       }
+
+       old = hs->p->request_timeout;
+       hs->p->request_timeout = timeout;
+
+       return old;
+}
+
+static void dcerpc_bh_auth_info(struct dcerpc_binding_handle *h,
+                               enum dcerpc_AuthType *auth_type,
+                               enum dcerpc_AuthLevel *auth_level)
+{
+       struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+                                    struct dcerpc_bh_state);
+
+       if (hs->p == NULL) {
+               return;
+       }
+
+       if (hs->p->conn == NULL) {
+               return;
+       }
+
+       *auth_type = hs->p->conn->security_state.auth_type;
+       *auth_level = hs->p->conn->security_state.auth_level;
+}
+
+struct dcerpc_bh_raw_call_state {
+       struct tevent_context *ev;
+       struct dcerpc_binding_handle *h;
+       DATA_BLOB in_data;
+       DATA_BLOB out_data;
+       uint32_t out_flags;
+};
+
+static void dcerpc_bh_raw_call_done(struct rpc_request *subreq);
+
+static struct tevent_req *dcerpc_bh_raw_call_send(TALLOC_CTX *mem_ctx,
+                                                 struct tevent_context *ev,
+                                                 struct dcerpc_binding_handle *h,
+                                                 const struct GUID *object,
+                                                 uint32_t opnum,
+                                                 uint32_t in_flags,
+                                                 const uint8_t *in_data,
+                                                 size_t in_length)
+{
+       struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+                                    struct dcerpc_bh_state);
+       struct tevent_req *req;
+       struct dcerpc_bh_raw_call_state *state;
+       bool ok;
+       struct rpc_request *subreq;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct dcerpc_bh_raw_call_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->h = h;
+       state->in_data.data = discard_const_p(uint8_t, in_data);
+       state->in_data.length = in_length;
+
+       ok = dcerpc_bh_is_connected(h);
+       if (!ok) {
+               tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
+               return tevent_req_post(req, ev);
+       }
+
+       subreq = dcerpc_request_send(state,
+                                    hs->p,
+                                    object,
+                                    opnum,
+                                    &state->in_data);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       subreq->async.callback = dcerpc_bh_raw_call_done;
+       subreq->async.private_data = req;
+
+       return req;
+}
+
+static void dcerpc_bh_raw_call_done(struct rpc_request *subreq)
+{
+       struct tevent_req *req =
+               talloc_get_type_abort(subreq->async.private_data,
+               struct tevent_req);
+       struct dcerpc_bh_raw_call_state *state =
+               tevent_req_data(req,
+               struct dcerpc_bh_raw_call_state);
+       NTSTATUS status;
+       uint32_t fault_code;
+
+       state->out_flags = 0;
+       if (subreq->flags & DCERPC_PULL_BIGENDIAN) {
+               state->out_flags |= LIBNDR_FLAG_BIGENDIAN;
+       }
+
+       fault_code = subreq->fault_code;
+
+       status = dcerpc_request_recv(subreq, state, &state->out_data);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+               status = dcerpc_fault_to_nt_status(fault_code);
+       }
+
+       /*
+        * We trigger the callback in the next event run
+        * because the code in this file might trigger
+        * multiple request callbacks from within a single
+        * while loop.
+        *
+        * In order to avoid segfaults from within
+        * dcerpc_connection_dead() we call
+        * tevent_req_defer_callback().
+        */
+       tevent_req_defer_callback(req, state->ev);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+static NTSTATUS dcerpc_bh_raw_call_recv(struct tevent_req *req,
+                                       TALLOC_CTX *mem_ctx,
+                                       uint8_t **out_data,
+                                       size_t *out_length,
+                                       uint32_t *out_flags)
+{
+       struct dcerpc_bh_raw_call_state *state =
+               tevent_req_data(req,
+               struct dcerpc_bh_raw_call_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       *out_data = talloc_move(mem_ctx, &state->out_data.data);
+       *out_length = state->out_data.length;
+       *out_flags = state->out_flags;
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+struct dcerpc_bh_disconnect_state {
+       uint8_t _dummy;
+};
+
+static struct tevent_req *dcerpc_bh_disconnect_send(TALLOC_CTX *mem_ctx,
+                                               struct tevent_context *ev,
+                                               struct dcerpc_binding_handle *h)
+{
+       struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+                                    struct dcerpc_bh_state);
+       struct tevent_req *req;
+       struct dcerpc_bh_disconnect_state *state;
+       bool ok;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct dcerpc_bh_disconnect_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       ok = dcerpc_bh_is_connected(h);
+       if (!ok) {
+               tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
+               return tevent_req_post(req, ev);
+       }
+
+       /* TODO: do a real disconnect ... */
+       hs->p = NULL;
+
+       tevent_req_done(req);
+       return tevent_req_post(req, ev);
+}
+
+static NTSTATUS dcerpc_bh_disconnect_recv(struct tevent_req *req)
+{
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+static bool dcerpc_bh_push_bigendian(struct dcerpc_binding_handle *h)
+{
+       struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+                                    struct dcerpc_bh_state);
+
+       if (hs->p->conn->flags & DCERPC_PUSH_BIGENDIAN) {
+               return true;
+       }
+
+       return false;
+}
+
+static bool dcerpc_bh_ref_alloc(struct dcerpc_binding_handle *h)
+{
+       struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+                                    struct dcerpc_bh_state);
+
+       if (hs->p->conn->flags & DCERPC_NDR_REF_ALLOC) {
+               return true;
+       }
+
+       return false;
+}
+
+static bool dcerpc_bh_use_ndr64(struct dcerpc_binding_handle *h)
+{
+       struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+                                    struct dcerpc_bh_state);
+
+       if (hs->p->conn->flags & DCERPC_NDR64) {
+               return true;
+       }
+
+       return false;
+}
+
+static void dcerpc_bh_do_ndr_print(struct dcerpc_binding_handle *h,
+                                  int ndr_flags,
+                                  const void *_struct_ptr,
+                                  const struct ndr_interface_call *call)
+{
+       struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+                                    struct dcerpc_bh_state);
+       void *struct_ptr = discard_const(_struct_ptr);
+
+       if (ndr_flags & NDR_IN) {
+               if (hs->p->conn->flags & DCERPC_DEBUG_PRINT_IN) {
+                       ndr_print_function_debug(call->ndr_print,
+                                                call->name,
+                                                ndr_flags,
+                                                struct_ptr);
+               }
+       }
+       if (ndr_flags & NDR_OUT) {
+               if (hs->p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
+                       ndr_print_function_debug(call->ndr_print,
+                                                call->name,
+                                                ndr_flags,
+                                                struct_ptr);
+               }
+       }
+}
+
+static void dcerpc_bh_ndr_push_failed(struct dcerpc_binding_handle *h,
+                                     NTSTATUS error,
+                                     const void *struct_ptr,
+                                     const struct ndr_interface_call *call)
+{
+       DEBUG(2,("Unable to ndr_push structure for %s - %s\n",
+                call->name, nt_errstr(error)));
+}
+
+static void dcerpc_bh_ndr_pull_failed(struct dcerpc_binding_handle *h,
+                                     NTSTATUS error,
+                                     const DATA_BLOB *blob,
+                                     const struct ndr_interface_call *call)
+{
+       struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+                                    struct dcerpc_bh_state);
+       const uint32_t num_examples = 20;
+       uint32_t i;
+
+       DEBUG(2,("Unable to ndr_pull structure for %s - %s\n",
+                call->name, nt_errstr(error)));
+
+       if (hs->p->conn->packet_log_dir == NULL) return;
+
+       for (i=0;i<num_examples;i++) {
+               char *name=NULL;
+               int ret;
+
+               ret = asprintf(&name, "%s/rpclog/%s-out.%d",
+                              hs->p->conn->packet_log_dir,
+                              call->name, i);
+               if (ret == -1) {
+                       return;
+               }
+               if (!file_exist(name)) {
+                       if (file_save(name, blob->data, blob->length)) {
+                               DEBUG(10,("Logged rpc packet to %s\n", name));
+                       }
+                       free(name);
+                       break;
+               }
+               free(name);
+       }
+}
+
+static NTSTATUS dcerpc_bh_ndr_validate_in(struct dcerpc_binding_handle *h,
+                                         TALLOC_CTX *mem_ctx,
+                                         const DATA_BLOB *blob,
+                                         const struct ndr_interface_call *call)
+{
+       struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+                                    struct dcerpc_bh_state);
+
+       if (hs->p->conn->flags & DCERPC_DEBUG_VALIDATE_IN) {
+               NTSTATUS status;
+
+               status = dcerpc_ndr_validate_in(hs->p->conn,
+                                               mem_ctx,
+                                               *blob,
+                                               call->struct_size,
+                                               call->ndr_push,
+                                               call->ndr_pull);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0,("Validation [in] failed for %s - %s\n",
+                                call->name, nt_errstr(status)));
+                       return status;
+               }
+       }
+
+       DEBUG(10,("rpc request data:\n"));
+       dump_data(10, blob->data, blob->length);
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS dcerpc_bh_ndr_validate_out(struct dcerpc_binding_handle *h,
+                                          struct ndr_pull *pull_in,
+                                          const void *_struct_ptr,
+                                          const struct ndr_interface_call *call)
+{
+       struct dcerpc_bh_state *hs = dcerpc_binding_handle_data(h,
+                                    struct dcerpc_bh_state);
+       void *struct_ptr = discard_const(_struct_ptr);
+
+       DEBUG(10,("rpc reply data:\n"));
+       dump_data(10, pull_in->data, pull_in->data_size);
+
+       if (pull_in->offset != pull_in->data_size) {
+               DEBUG(0,("Warning! ignoring %u unread bytes at ofs:%u (0x%08X) for %s!\n",
+                        pull_in->data_size - pull_in->offset,
+                        pull_in->offset, pull_in->offset,
+                        call->name));
+               /* we used to return NT_STATUS_INFO_LENGTH_MISMATCH here,
+                  but it turns out that early versions of NT
+                  (specifically NT3.1) add junk onto the end of rpc
+                  packets, so if we want to interoperate at all with
+                  those versions then we need to ignore this error */
+       }
+
+       if (hs->p->conn->flags & DCERPC_DEBUG_VALIDATE_OUT) {
+               NTSTATUS status;
+
+               status = dcerpc_ndr_validate_out(hs->p->conn,
+                                                pull_in,
+                                                struct_ptr,
+                                                call->struct_size,
+                                                call->ndr_push,
+                                                call->ndr_pull,
+                                                call->ndr_print);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(2,("Validation [out] failed for %s - %s\n",
+                                call->name, nt_errstr(status)));
+                       return status;
+               }
+       }
+
+       return NT_STATUS_OK;
+}
+
+static const struct dcerpc_binding_handle_ops dcerpc_bh_ops = {
+       .name                   = "dcerpc",
+       .is_connected           = dcerpc_bh_is_connected,
+       .set_timeout            = dcerpc_bh_set_timeout,
+       .auth_info              = dcerpc_bh_auth_info,
+       .raw_call_send          = dcerpc_bh_raw_call_send,
+       .raw_call_recv          = dcerpc_bh_raw_call_recv,
+       .disconnect_send        = dcerpc_bh_disconnect_send,
+       .disconnect_recv        = dcerpc_bh_disconnect_recv,
+
+       .push_bigendian         = dcerpc_bh_push_bigendian,
+       .ref_alloc              = dcerpc_bh_ref_alloc,
+       .use_ndr64              = dcerpc_bh_use_ndr64,
+       .do_ndr_print           = dcerpc_bh_do_ndr_print,
+       .ndr_push_failed        = dcerpc_bh_ndr_push_failed,
+       .ndr_pull_failed        = dcerpc_bh_ndr_pull_failed,
+       .ndr_validate_in        = dcerpc_bh_ndr_validate_in,
+       .ndr_validate_out       = dcerpc_bh_ndr_validate_out,
+};
+
+/* initialise a dcerpc pipe. */
+struct dcerpc_binding_handle *dcerpc_pipe_binding_handle(struct dcerpc_pipe *p)
+{
+       struct dcerpc_binding_handle *h;
+       struct dcerpc_bh_state *hs;
+
+       h = dcerpc_binding_handle_create(p,
+                                        &dcerpc_bh_ops,
+                                        NULL,
+                                        NULL, /* TODO */
+                                        &hs,
+                                        struct dcerpc_bh_state,
+                                        __location__);
+       if (h == NULL) {
+               return NULL;
+       }
+       hs->p = p;
+
+       dcerpc_binding_handle_set_sync_ev(h, p->conn->event_ctx);
+
+       return h;
+}
+
 /* initialise a dcerpc pipe. */
 _PUBLIC_ struct dcerpc_pipe *dcerpc_pipe_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev)
 {
        struct dcerpc_pipe *p;
 
-       p = talloc(mem_ctx, struct dcerpc_pipe);
+       p = talloc_zero(mem_ctx, struct dcerpc_pipe);
        if (!p) {
                return NULL;
        }
@@ -114,12 +650,11 @@ _PUBLIC_ struct dcerpc_pipe *dcerpc_pipe_init(TALLOC_CTX *mem_ctx, struct tevent
                p->conn->flags |= DCERPC_DEBUG_PRINT_BOTH;
        }
 
-       p->binding_handle = talloc(p, struct dcerpc_binding_handle);
+       p->binding_handle = dcerpc_pipe_binding_handle(p);
        if (p->binding_handle == NULL) {
                talloc_free(p);
                return NULL;
        }
-       p->binding_handle->private_data = p;
 
        return p;
 }
@@ -128,7 +663,7 @@ _PUBLIC_ struct dcerpc_pipe *dcerpc_pipe_init(TALLOC_CTX *mem_ctx, struct tevent
 /* 
    choose the next call id to use
 */
-static uint32_t next_call_id(struct dcerpc_connection *c)
+static uint32_t next_call_id(struct dcecli_connection *c)
 {
        c->call_id++;
        if (c->call_id == 0) {
@@ -140,7 +675,7 @@ static uint32_t next_call_id(struct dcerpc_connection *c)
 /**
   setup for a ndr pull, also setting up any flags from the binding string
 */
-static struct ndr_pull *ndr_pull_init_flags(struct dcerpc_connection *c, 
+static struct ndr_pull *ndr_pull_init_flags(struct dcecli_connection *c, 
                                            DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
 {
        struct ndr_pull *ndr = ndr_pull_init_blob(blob, mem_ctx);
@@ -166,13 +701,13 @@ static struct ndr_pull *ndr_pull_init_flags(struct dcerpc_connection *c,
    parse a data blob into a ncacn_packet structure. This handles both
    input and output packets
 */
-static NTSTATUS ncacn_pull(struct dcerpc_connection *c, DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
+static NTSTATUS ncacn_pull(struct dcecli_connection *c, DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
                            struct ncacn_packet *pkt)
 {
        struct ndr_pull *ndr;
        enum ndr_err_code ndr_err;
 
-       ndr = ndr_pull_init_flags(c, blob, mem_ctx);
+       ndr = ndr_pull_init_blob(blob, mem_ctx);
        if (!ndr) {
                return NT_STATUS_NO_MEMORY;
        }
@@ -181,18 +716,27 @@ static NTSTATUS ncacn_pull(struct dcerpc_connection *c, DATA_BLOB *blob, TALLOC_
                ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
        }
 
+       if (CVAL(blob->data, DCERPC_PFC_OFFSET) & DCERPC_PFC_FLAG_OBJECT_UUID) {
+               ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
+       }
+
        ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
+       TALLOC_FREE(ndr);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                return ndr_map_error2ntstatus(ndr_err);
        }
 
+       if (pkt->frag_length != blob->length) {
+               return NT_STATUS_RPC_PROTOCOL_ERROR;
+       }
+
        return NT_STATUS_OK;
 }
 
 /* 
    parse the authentication information on a dcerpc response packet
 */
-static NTSTATUS ncacn_pull_request_auth(struct dcerpc_connection *c, TALLOC_CTX *mem_ctx, 
+static NTSTATUS ncacn_pull_request_auth(struct dcecli_connection *c, TALLOC_CTX *mem_ctx, 
                                        DATA_BLOB *raw_packet,
                                        struct ncacn_packet *pkt)
 {
@@ -200,12 +744,7 @@ static NTSTATUS ncacn_pull_request_auth(struct dcerpc_connection *c, TALLOC_CTX
        struct dcerpc_auth auth;
        uint32_t auth_length;
 
-       if (!c->security_state.auth_info ||
-           !c->security_state.generic_state) {
-               return NT_STATUS_OK;
-       }
-
-       switch (c->security_state.auth_info->auth_level) {
+       switch (c->security_state.auth_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
        case DCERPC_AUTH_LEVEL_INTEGRITY:
                break;
@@ -225,6 +764,14 @@ static NTSTATUS ncacn_pull_request_auth(struct dcerpc_connection *c, TALLOC_CTX
                return NT_STATUS_INVALID_LEVEL;
        }
 
+       if (pkt->auth_length == 0) {
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       if (c->security_state.generic_state == NULL) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
        status = dcerpc_pull_auth_trailer(pkt, mem_ctx,
                                          &pkt->u.response.stub_and_verifier,
                                          &auth, &auth_length, false);
@@ -233,10 +780,9 @@ static NTSTATUS ncacn_pull_request_auth(struct dcerpc_connection *c, TALLOC_CTX
        pkt->u.response.stub_and_verifier.length -= auth_length;
 
        /* check signature or unseal the packet */
-       switch (c->security_state.auth_info->auth_level) {
+       switch (c->security_state.auth_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
                status = gensec_unseal_packet(c->security_state.generic_state, 
-                                             mem_ctx, 
                                              raw_packet->data + DCERPC_REQUEST_LENGTH,
                                              pkt->u.response.stub_and_verifier.length, 
                                              raw_packet->data,
@@ -249,7 +795,6 @@ static NTSTATUS ncacn_pull_request_auth(struct dcerpc_connection *c, TALLOC_CTX
                
        case DCERPC_AUTH_LEVEL_INTEGRITY:
                status = gensec_check_packet(c->security_state.generic_state, 
-                                            mem_ctx, 
                                             pkt->u.response.stub_and_verifier.data, 
                                             pkt->u.response.stub_and_verifier.length, 
                                             raw_packet->data,
@@ -280,7 +825,7 @@ static NTSTATUS ncacn_pull_request_auth(struct dcerpc_connection *c, TALLOC_CTX
 /* 
    push a dcerpc request packet into a blob, possibly signing it.
 */
-static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c, 
+static NTSTATUS ncacn_push_request_sign(struct dcecli_connection *c, 
                                         DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
                                         size_t sig_size,
                                         struct ncacn_packet *pkt)
@@ -291,15 +836,18 @@ static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c,
        size_t payload_length;
        enum ndr_err_code ndr_err;
        size_t hdr_size = DCERPC_REQUEST_LENGTH;
+       struct dcerpc_auth auth_info = {
+               .auth_type = c->security_state.auth_type,
+               .auth_level = c->security_state.auth_level,
+               .auth_context_id = c->security_state.auth_context_id,
+       };
 
-       /* non-signed packets are simpler */
-       if (sig_size == 0) {
-               return ncacn_push_auth(blob, mem_ctx, pkt, NULL);
-       }
-
-       switch (c->security_state.auth_info->auth_level) {
+       switch (c->security_state.auth_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
        case DCERPC_AUTH_LEVEL_INTEGRITY:
+               if (sig_size == 0) {
+                       return NT_STATUS_INTERNAL_ERROR;
+               }
                break;
 
        case DCERPC_AUTH_LEVEL_CONNECT:
@@ -341,21 +889,18 @@ static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c,
           ndr_push_align() as that is relative to the start of the
           whole packet, whereas w2k8 wants it relative to the start
           of the stub */
-       c->security_state.auth_info->auth_pad_length =
-               (16 - (pkt->u.request.stub_and_verifier.length & 15)) & 15;
-       ndr_err = ndr_push_zero(ndr, c->security_state.auth_info->auth_pad_length);
+       auth_info.auth_pad_length =
+               DCERPC_AUTH_PAD_LENGTH(pkt->u.request.stub_and_verifier.length);
+       ndr_err = ndr_push_zero(ndr, auth_info.auth_pad_length);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                return ndr_map_error2ntstatus(ndr_err);
        }
 
        payload_length = pkt->u.request.stub_and_verifier.length + 
-               c->security_state.auth_info->auth_pad_length;
-
-       /* we start without signature, it will appended later */
-       c->security_state.auth_info->credentials = data_blob(NULL,0);
+               auth_info.auth_pad_length;
 
        /* add the auth verifier */
-       ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, c->security_state.auth_info);
+       ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth_info);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                return ndr_map_error2ntstatus(ndr_err);
        }
@@ -373,7 +918,7 @@ static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c,
        dcerpc_set_auth_length(blob, sig_size);
 
        /* sign or seal the packet */
-       switch (c->security_state.auth_info->auth_level) {
+       switch (c->security_state.auth_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
                status = gensec_seal_packet(c->security_state.generic_state, 
                                            mem_ctx, 
@@ -413,7 +958,7 @@ static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c,
                DEBUG(3,("ncacn_push_request_sign: creds2.length[%u] != sig_size[%u] pad[%u] stub[%u]\n",
                        (unsigned) creds2.length,
                        (unsigned) sig_size,
-                       (unsigned) c->security_state.auth_info->auth_pad_length,
+                       (unsigned) auth_info.auth_pad_length,
                        (unsigned) pkt->u.request.stub_and_verifier.length));
                dcerpc_set_frag_length(blob, blob->length + creds2.length);
                dcerpc_set_auth_length(blob, creds2.length);
@@ -430,7 +975,7 @@ static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c,
 /* 
    fill in the fixed values in a dcerpc header 
 */
-static void init_ncacn_hdr(struct dcerpc_connection *c, struct ncacn_packet *pkt)
+static void init_ncacn_hdr(struct dcecli_connection *c, struct ncacn_packet *pkt)
 {
        pkt->rpc_vers = 5;
        pkt->rpc_vers_minor = 0;
@@ -447,25 +992,44 @@ static void init_ncacn_hdr(struct dcerpc_connection *c, struct ncacn_packet *pkt
 /*
   map a bind nak reason to a NTSTATUS
 */
-static NTSTATUS dcerpc_map_reason(uint16_t reason)
+static NTSTATUS dcerpc_map_nak_reason(enum dcerpc_bind_nak_reason reason)
 {
        switch (reason) {
-       case DCERPC_BIND_REASON_ASYNTAX:
-               return NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX;
-       case DCERPC_BIND_REASON_INVALID_AUTH_TYPE:
+       case DCERPC_BIND_NAK_REASON_PROTOCOL_VERSION_NOT_SUPPORTED:
+               return NT_STATUS_REVISION_MISMATCH;
+       case DCERPC_BIND_NAK_REASON_INVALID_AUTH_TYPE:
                return NT_STATUS_INVALID_PARAMETER;
+       default:
+               break;
        }
        return NT_STATUS_UNSUCCESSFUL;
 }
 
-/*
-  a bind or alter context has failed
-*/
-static void dcerpc_composite_fail(struct rpc_request *req)
+static NTSTATUS dcerpc_map_ack_reason(const struct dcerpc_ack_ctx *ack)
 {
-       struct composite_context *c = talloc_get_type(req->async.private_data, 
-                                                     struct composite_context);
-       composite_error(c, req->status);
+       if (ack == NULL) {
+               return NT_STATUS_RPC_PROTOCOL_ERROR;
+       }
+
+       switch (ack->result) {
+       case DCERPC_BIND_ACK_RESULT_NEGOTIATE_ACK:
+               /*
+                * We have not asked for this...
+                */
+               return NT_STATUS_RPC_PROTOCOL_ERROR;
+       default:
+               break;
+       }
+
+       switch (ack->reason.value) {
+       case DCERPC_BIND_ACK_REASON_ABSTRACT_SYNTAX_NOT_SUPPORTED:
+               return NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX;
+       case DCERPC_BIND_ACK_REASON_TRANSFER_SYNTAXES_NOT_SUPPORTED:
+               return NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX;
+       default:
+               break;
+       }
+       return NT_STATUS_UNSUCCESSFUL;
 }
 
 /*
@@ -490,15 +1054,16 @@ static int dcerpc_req_dequeue(struct rpc_request *req)
 /*
   mark the dcerpc connection dead. All outstanding requests get an error
 */
-static void dcerpc_connection_dead(struct dcerpc_connection *conn, NTSTATUS status)
+static void dcerpc_connection_dead(struct dcecli_connection *conn, NTSTATUS status)
 {
        if (conn->dead) return;
 
        conn->dead = true;
 
-       if (conn->transport.shutdown_pipe) {
-               conn->transport.shutdown_pipe(conn, status);
-       }
+       TALLOC_FREE(conn->io_trigger);
+       conn->io_trigger_pending = false;
+
+       dcerpc_shutdown_pipe(conn, status);
 
        /* all pending requests get the error */
        while (conn->pending) {
@@ -511,6 +1076,17 @@ static void dcerpc_connection_dead(struct dcerpc_connection *conn, NTSTATUS stat
                }
        }       
 
+       /* all requests, which are not shipped */
+       while (conn->request_queue) {
+               struct rpc_request *req = conn->request_queue;
+               dcerpc_req_dequeue(req);
+               req->state = RPC_REQUEST_DONE;
+               req->status = status;
+               if (req->async.callback) {
+                       req->async.callback(req);
+               }
+       }
+
        talloc_set_destructor(conn, NULL);
        if (conn->free_skipped) {
                talloc_free(conn);
@@ -521,7 +1097,7 @@ static void dcerpc_connection_dead(struct dcerpc_connection *conn, NTSTATUS stat
   forward declarations of the recv_data handlers for the types of
   packets we need to handle
 */
-static void dcerpc_request_recv_data(struct dcerpc_connection *c, 
+static void dcerpc_request_recv_data(struct dcecli_connection *c, 
                                     DATA_BLOB *raw_packet, struct ncacn_packet *pkt);
 
 /*
@@ -529,10 +1105,14 @@ static void dcerpc_request_recv_data(struct dcerpc_connection *c,
   type of reply it is (normal request, bind or alter context) and
   dispatch to the appropriate handler
 */
-static void dcerpc_recv_data(struct dcerpc_connection *conn, DATA_BLOB *blob, NTSTATUS status)
+static void dcerpc_recv_data(struct dcecli_connection *conn, DATA_BLOB *blob, NTSTATUS status)
 {
        struct ncacn_packet pkt;
 
+       if (conn->dead) {
+               return;
+       }
+
        if (NT_STATUS_IS_OK(status) && blob->length == 0) {
                status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
        }
@@ -550,77 +1130,19 @@ static void dcerpc_recv_data(struct dcerpc_connection *conn, DATA_BLOB *blob, NT
        if (!NT_STATUS_IS_OK(status)) {
                data_blob_free(blob);
                dcerpc_connection_dead(conn, status);
+               return;
        }
 
        dcerpc_request_recv_data(conn, blob, &pkt);
 }
 
 /*
-  Receive a bind reply from the transport
+  handle timeouts of individual dcerpc requests
 */
-static void dcerpc_bind_recv_handler(struct rpc_request *req
-                                    DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
+static void dcerpc_timeout_handler(struct tevent_context *ev, struct tevent_timer *te
+                                  struct timeval t, void *private_data)
 {
-       struct composite_context *c;
-       struct dcerpc_connection *conn;
-
-       c = talloc_get_type(req->async.private_data, struct composite_context);
-
-       if (pkt->ptype == DCERPC_PKT_BIND_NAK) {
-               DEBUG(2,("dcerpc: bind_nak reason %d\n",
-                        pkt->u.bind_nak.reject_reason));
-               composite_error(c, dcerpc_map_reason(pkt->u.bind_nak.
-                                                    reject_reason));
-               return;
-       }
-
-       if ((pkt->ptype != DCERPC_PKT_BIND_ACK) ||
-           (pkt->u.bind_ack.num_results == 0) ||
-           (pkt->u.bind_ack.ctx_list[0].result != 0)) {
-               req->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
-               composite_error(c, NT_STATUS_NET_WRITE_FAULT);
-               return;
-       }
-
-       conn = req->p->conn;
-
-       conn->srv_max_xmit_frag = pkt->u.bind_ack.max_xmit_frag;
-       conn->srv_max_recv_frag = pkt->u.bind_ack.max_recv_frag;
-
-       if ((req->p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) &&
-           (pkt->pfc_flags & DCERPC_PFC_FLAG_CONC_MPX)) {
-               conn->flags |= DCERPC_CONCURRENT_MULTIPLEX;
-       }
-
-       if ((req->p->binding->flags & DCERPC_HEADER_SIGNING) &&
-           (pkt->pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN)) {
-               conn->flags |= DCERPC_HEADER_SIGNING;
-       }
-
-       /* the bind_ack might contain a reply set of credentials */
-       if (conn->security_state.auth_info && pkt->u.bind_ack.auth_info.length) {
-               NTSTATUS status;
-               uint32_t auth_length;
-               status = dcerpc_pull_auth_trailer(pkt, conn, &pkt->u.bind_ack.auth_info,
-                                                 conn->security_state.auth_info, &auth_length, true);
-               if (!NT_STATUS_IS_OK(status)) {
-                       composite_error(c, status);
-                       return;
-               }
-       }
-
-       req->p->assoc_group_id = pkt->u.bind_ack.assoc_group_id;
-
-       composite_done(c);
-}
-
-/*
-  handle timeouts of individual dcerpc requests
-*/
-static void dcerpc_timeout_handler(struct tevent_context *ev, struct tevent_timer *te, 
-                                  struct timeval t, void *private_data)
-{
-       struct rpc_request *req = talloc_get_type(private_data, struct rpc_request);
+       struct rpc_request *req = talloc_get_type(private_data, struct rpc_request);
 
        if (req->ignore_timeout) {
                dcerpc_req_dequeue(req);
@@ -635,27 +1157,44 @@ static void dcerpc_timeout_handler(struct tevent_context *ev, struct tevent_time
        dcerpc_connection_dead(req->p->conn, NT_STATUS_IO_TIMEOUT);
 }
 
-/*
-  send a async dcerpc bind request
-*/
-struct composite_context *dcerpc_bind_send(struct dcerpc_pipe *p,
-                                          TALLOC_CTX *mem_ctx,
-                                          const struct ndr_syntax_id *syntax,
-                                          const struct ndr_syntax_id *transfer_syntax)
+struct dcerpc_bind_state {
+       struct tevent_context *ev;
+       struct dcerpc_pipe *p;
+};
+
+static void dcerpc_bind_fail_handler(struct rpc_request *subreq);
+static void dcerpc_bind_recv_handler(struct rpc_request *subreq,
+                                    DATA_BLOB *raw_packet,
+                                    struct ncacn_packet *pkt);
+
+struct tevent_req *dcerpc_bind_send(TALLOC_CTX *mem_ctx,
+                                   struct tevent_context *ev,
+                                   struct dcerpc_pipe *p,
+                                   const struct ndr_syntax_id *syntax,
+                                   const struct ndr_syntax_id *transfer_syntax)
 {
-       struct composite_context *c;
+       struct tevent_req *req;
+       struct dcerpc_bind_state *state;
        struct ncacn_packet pkt;
        DATA_BLOB blob;
-       struct rpc_request *req;
+       NTSTATUS status;
+       struct rpc_request *subreq;
+       uint32_t flags;
 
-       c = composite_create(mem_ctx,p->conn->event_ctx);
-       if (c == NULL) return NULL;
+       req = tevent_req_create(mem_ctx, &state,
+                               struct dcerpc_bind_state);
+       if (req == NULL) {
+               return NULL;
+       }
 
-       c->private_data = p;
+       state->ev = ev;
+       state->p = p;
 
        p->syntax = *syntax;
        p->transfer_syntax = *transfer_syntax;
 
+       flags = dcerpc_binding_get_flags(p->binding);
+
        init_ncacn_hdr(p->conn, &pkt);
 
        pkt.ptype = DCERPC_PKT_BIND;
@@ -663,20 +1202,22 @@ struct composite_context *dcerpc_bind_send(struct dcerpc_pipe *p,
        pkt.call_id = p->conn->call_id;
        pkt.auth_length = 0;
 
-       if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
+       if (flags & DCERPC_CONCURRENT_MULTIPLEX) {
                pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
        }
 
-       if (p->binding->flags & DCERPC_HEADER_SIGNING) {
+       if (p->conn->flags & DCERPC_PROPOSE_HEADER_SIGNING) {
                pkt.pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
        }
 
-       pkt.u.bind.max_xmit_frag = 5840;
-       pkt.u.bind.max_recv_frag = 5840;
-       pkt.u.bind.assoc_group_id = p->binding->assoc_group_id;
+       pkt.u.bind.max_xmit_frag = p->conn->srv_max_xmit_frag;
+       pkt.u.bind.max_recv_frag = p->conn->srv_max_recv_frag;
+       pkt.u.bind.assoc_group_id = dcerpc_binding_get_assoc_group_id(p->binding);
        pkt.u.bind.num_contexts = 1;
        pkt.u.bind.ctx_list = talloc_array(mem_ctx, struct dcerpc_ctx_list, 1);
-       if (composite_nomem(pkt.u.bind.ctx_list, c)) return c;
+       if (tevent_req_nomem(pkt.u.bind.ctx_list, req)) {
+               return tevent_req_post(req, ev);
+       }
        pkt.u.bind.ctx_list[0].context_id = p->context_id;
        pkt.u.bind.ctx_list[0].num_transfer_syntaxes = 1;
        pkt.u.bind.ctx_list[0].abstract_syntax = p->syntax;
@@ -684,47 +1225,179 @@ struct composite_context *dcerpc_bind_send(struct dcerpc_pipe *p,
        pkt.u.bind.auth_info = data_blob(NULL, 0);
 
        /* construct the NDR form of the packet */
-       c->status = ncacn_push_auth(&blob, c, &pkt,
-                                   p->conn->security_state.auth_info);
-       if (!composite_is_ok(c)) return c;
-
-       p->conn->transport.recv_data = dcerpc_recv_data;
+       status = ncacn_push_auth(&blob, state, &pkt,
+                                p->conn->security_state.auth_info);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
 
        /*
         * we allocate a dcerpc_request so we can be in the same
         * request queue as normal requests
         */
-       req = talloc_zero(c, struct rpc_request);
-       if (composite_nomem(req, c)) return c;
+       subreq = talloc_zero(state, struct rpc_request);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
 
-       req->state = RPC_REQUEST_PENDING;
-       req->call_id = pkt.call_id;
-       req->async.private_data = c;
-       req->async.callback = dcerpc_composite_fail;
-       req->p = p;
-       req->recv_handler = dcerpc_bind_recv_handler;
-       DLIST_ADD_END(p->conn->pending, req, struct rpc_request *);
-       talloc_set_destructor(req, dcerpc_req_dequeue);
+       subreq->state = RPC_REQUEST_PENDING;
+       subreq->call_id = pkt.call_id;
+       subreq->async.private_data = req;
+       subreq->async.callback = dcerpc_bind_fail_handler;
+       subreq->p = p;
+       subreq->recv_handler = dcerpc_bind_recv_handler;
+       DLIST_ADD_END(p->conn->pending, subreq);
+       talloc_set_destructor(subreq, dcerpc_req_dequeue);
 
-       c->status = p->conn->transport.send_request(p->conn, &blob,
-                                                   true);
-       if (!composite_is_ok(c)) return c;
+       status = dcerpc_send_request(p->conn, &blob, true);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
 
-       event_add_timed(c->event_ctx, req,
-                       timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
-                       dcerpc_timeout_handler, req);
+       tevent_add_timer(ev, subreq,
+                        timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
+                        dcerpc_timeout_handler, subreq);
 
-       return c;
+       return req;
 }
 
-/*
-  recv side of async dcerpc bind request
-*/
-NTSTATUS dcerpc_bind_recv(struct composite_context *ctx)
+static void dcerpc_bind_fail_handler(struct rpc_request *subreq)
+{
+       struct tevent_req *req =
+               talloc_get_type_abort(subreq->async.private_data,
+               struct tevent_req);
+       struct dcerpc_bind_state *state =
+               tevent_req_data(req,
+               struct dcerpc_bind_state);
+       NTSTATUS status = subreq->status;
+
+       TALLOC_FREE(subreq);
+
+       /*
+        * We trigger the callback in the next event run
+        * because the code in this file might trigger
+        * multiple request callbacks from within a single
+        * while loop.
+        *
+        * In order to avoid segfaults from within
+        * dcerpc_connection_dead() we call
+        * tevent_req_defer_callback().
+        */
+       tevent_req_defer_callback(req, state->ev);
+
+       tevent_req_nterror(req, status);
+}
+
+static void dcerpc_bind_recv_handler(struct rpc_request *subreq,
+                                    DATA_BLOB *raw_packet,
+                                    struct ncacn_packet *pkt)
+{
+       struct tevent_req *req =
+               talloc_get_type_abort(subreq->async.private_data,
+               struct tevent_req);
+       struct dcerpc_bind_state *state =
+               tevent_req_data(req,
+               struct dcerpc_bind_state);
+       struct dcecli_connection *conn = state->p->conn;
+       struct dcerpc_binding *b = NULL;
+       NTSTATUS status;
+       uint32_t flags;
+
+       /*
+        * Note that pkt is allocated under raw_packet->data,
+        * while raw_packet->data is a child of subreq.
+        */
+       talloc_steal(state, raw_packet->data);
+       TALLOC_FREE(subreq);
+
+       /*
+        * We trigger the callback in the next event run
+        * because the code in this file might trigger
+        * multiple request callbacks from within a single
+        * while loop.
+        *
+        * In order to avoid segfaults from within
+        * dcerpc_connection_dead() we call
+        * tevent_req_defer_callback().
+        */
+       tevent_req_defer_callback(req, state->ev);
+
+       if (pkt->ptype == DCERPC_PKT_BIND_NAK) {
+               status = dcerpc_map_nak_reason(pkt->u.bind_nak.reject_reason);
+
+               DEBUG(2,("dcerpc: bind_nak reason %d - %s\n",
+                        pkt->u.bind_nak.reject_reason, nt_errstr(status)));
+
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       if ((pkt->ptype != DCERPC_PKT_BIND_ACK) ||
+           (pkt->u.bind_ack.num_results == 0) ||
+           (pkt->u.bind_ack.ctx_list[0].result != 0)) {
+               state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
+               tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
+               return;
+       }
+
+       /*
+        * DCE-RPC 1.1 (c706) specifies
+        * CONST_MUST_RCV_FRAG_SIZE as 1432
+        */
+       if (pkt->u.bind_ack.max_xmit_frag < 1432) {
+               state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
+               tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
+               return;
+       }
+       if (pkt->u.bind_ack.max_recv_frag < 1432) {
+               state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
+               tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
+               return;
+       }
+       conn->srv_max_xmit_frag = MIN(conn->srv_max_xmit_frag,
+                                     pkt->u.bind_ack.max_xmit_frag);
+       conn->srv_max_recv_frag = MIN(conn->srv_max_recv_frag,
+                                     pkt->u.bind_ack.max_recv_frag);
+
+       flags = dcerpc_binding_get_flags(state->p->binding);
+
+       if ((flags & DCERPC_CONCURRENT_MULTIPLEX) &&
+           (pkt->pfc_flags & DCERPC_PFC_FLAG_CONC_MPX)) {
+               conn->flags |= DCERPC_CONCURRENT_MULTIPLEX;
+       }
+
+       if ((conn->flags & DCERPC_PROPOSE_HEADER_SIGNING) &&
+           (pkt->pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN)) {
+               conn->flags |= DCERPC_HEADER_SIGNING;
+       }
+
+       /* the bind_ack might contain a reply set of credentials */
+       if (conn->security_state.auth_info && pkt->u.bind_ack.auth_info.length) {
+               uint32_t auth_length;
+
+               status = dcerpc_pull_auth_trailer(pkt, conn, &pkt->u.bind_ack.auth_info,
+                                                 conn->security_state.auth_info, &auth_length, true);
+               if (tevent_req_nterror(req, status)) {
+                       return;
+               }
+       }
+
+       /*
+        * We're the owner of the binding, so we're allowed to modify it.
+        */
+       b = discard_const_p(struct dcerpc_binding, state->p->binding);
+       status = dcerpc_binding_set_assoc_group_id(b,
+                                                  pkt->u.bind_ack.assoc_group_id);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+NTSTATUS dcerpc_bind_recv(struct tevent_req *req)
 {
-       NTSTATUS result = composite_wait(ctx);
-       talloc_free(ctx);
-       return result;
+       return tevent_req_simple_recv_ntstatus(req);
 }
 
 /* 
@@ -736,6 +1409,9 @@ NTSTATUS dcerpc_auth3(struct dcerpc_pipe *p,
        struct ncacn_packet pkt;
        NTSTATUS status;
        DATA_BLOB blob;
+       uint32_t flags;
+
+       flags = dcerpc_binding_get_flags(p->binding);
 
        init_ncacn_hdr(p->conn, &pkt);
 
@@ -745,14 +1421,10 @@ NTSTATUS dcerpc_auth3(struct dcerpc_pipe *p,
        pkt.auth_length = 0;
        pkt.u.auth3.auth_info = data_blob(NULL, 0);
 
-       if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
+       if (flags & DCERPC_CONCURRENT_MULTIPLEX) {
                pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
        }
 
-       if (p->binding->flags & DCERPC_HEADER_SIGNING) {
-               pkt.pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
-       }
-
        /* construct the NDR form of the packet */
        status = ncacn_push_auth(&blob, mem_ctx,
                                 &pkt,
@@ -762,7 +1434,7 @@ NTSTATUS dcerpc_auth3(struct dcerpc_pipe *p,
        }
 
        /* send it on its way */
-       status = p->conn->transport.send_request(p->conn, &blob, false);
+       status = dcerpc_send_request(p->conn, &blob, false);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -777,7 +1449,7 @@ NTSTATUS dcerpc_auth3(struct dcerpc_pipe *p,
 
   This function frees the data 
 */
-static void dcerpc_request_recv_data(struct dcerpc_connection *c, 
+static void dcerpc_request_recv_data(struct dcecli_connection *c, 
                                     DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
 {
        struct rpc_request *req;
@@ -792,8 +1464,7 @@ static void dcerpc_request_recv_data(struct dcerpc_connection *c,
          to run the auth routines so that we don't get the sign/seal
          info out of step with the server
        */
-       if (c->security_state.auth_info && c->security_state.generic_state &&
-           pkt->ptype == DCERPC_PKT_RESPONSE) {
+       if (pkt->ptype == DCERPC_PKT_RESPONSE) {
                status = ncacn_pull_request_auth(c, raw_packet->data, raw_packet, pkt);
        }
 
@@ -821,6 +1492,13 @@ static void dcerpc_request_recv_data(struct dcerpc_connection *c,
        if (req->recv_handler != NULL) {
                dcerpc_req_dequeue(req);
                req->state = RPC_REQUEST_DONE;
+
+               /*
+                * We have to look at shipping further requests before calling
+                * the async function, that one might close the pipe
+                */
+               dcerpc_schedule_io_trigger(c);
+
                req->recv_handler(req, raw_packet, pkt);
                return;
        }
@@ -864,61 +1542,64 @@ static void dcerpc_request_recv_data(struct dcerpc_connection *c,
        }
 
        if (!(pkt->pfc_flags & DCERPC_PFC_FLAG_LAST)) {
-               c->transport.send_read(c);
+               data_blob_free(raw_packet);
+               dcerpc_send_read(c);
                return;
        }
 
+       if (req->verify_bitmask1) {
+               req->p->conn->security_state.verified_bitmask1 = true;
+       }
+       if (req->verify_pcontext) {
+               req->p->verified_pcontext = true;
+       }
+
        if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
                req->flags |= DCERPC_PULL_BIGENDIAN;
        } else {
                req->flags &= ~DCERPC_PULL_BIGENDIAN;
        }
 
-
 req_done:
+       data_blob_free(raw_packet);
+
        /* we've got the full payload */
+       dcerpc_req_dequeue(req);
        req->state = RPC_REQUEST_DONE;
-       DLIST_REMOVE(c->pending, req);
 
-       if (c->request_queue != NULL) {
-               /* We have to look at shipping further requests before calling
-                * the async function, that one might close the pipe */
-               dcerpc_ship_next_request(c);
-       }
+       /*
+        * We have to look at shipping further requests before calling
+        * the async function, that one might close the pipe
+        */
+       dcerpc_schedule_io_trigger(c);
 
        if (req->async.callback) {
                req->async.callback(req);
        }
 }
 
+static NTSTATUS dcerpc_request_prepare_vt(struct rpc_request *req);
+
 /*
   perform the send side of a async dcerpc request
 */
-static struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p, 
+static struct rpc_request *dcerpc_request_send(TALLOC_CTX *mem_ctx,
+                                              struct dcerpc_pipe *p,
                                               const struct GUID *object,
                                               uint16_t opnum,
                                               DATA_BLOB *stub_data)
 {
        struct rpc_request *req;
+       NTSTATUS status;
 
-       p->conn->transport.recv_data = dcerpc_recv_data;
-
-       req = talloc(p, struct rpc_request);
+       req = talloc_zero(mem_ctx, struct rpc_request);
        if (req == NULL) {
                return NULL;
        }
 
        req->p = p;
        req->call_id = next_call_id(p->conn);
-       req->status = NT_STATUS_OK;
        req->state = RPC_REQUEST_QUEUED;
-       req->payload = data_blob(NULL, 0);
-       req->flags = 0;
-       req->fault_code = 0;
-       req->ignore_timeout = false;
-       req->async.callback = NULL;
-       req->async.private_data = NULL;
-       req->recv_handler = NULL;
 
        if (object != NULL) {
                req->object = (struct GUID *)talloc_memdup(req, (const void *)object, sizeof(*object));
@@ -926,24 +1607,25 @@ static struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
                        talloc_free(req);
                        return NULL;
                }
-       } else {
-               req->object = NULL;
        }
 
        req->opnum = opnum;
        req->request_data.length = stub_data->length;
-       req->request_data.data = talloc_reference(req, stub_data->data);
-       if (req->request_data.length && req->request_data.data == NULL) {
+       req->request_data.data = stub_data->data;
+
+       status = dcerpc_request_prepare_vt(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(req);
                return NULL;
        }
 
-       DLIST_ADD_END(p->conn->request_queue, req, struct rpc_request *);
+       DLIST_ADD_END(p->conn->request_queue, req);
        talloc_set_destructor(req, dcerpc_req_dequeue);
 
-       dcerpc_ship_next_request(p->conn);
+       dcerpc_schedule_io_trigger(p->conn);
 
        if (p->request_timeout) {
-               event_add_timed(dcerpc_event_context(p), req, 
+               tevent_add_timer(p->conn->event_ctx, req,
                                timeval_current_ofs(p->request_timeout, 0), 
                                dcerpc_timeout_handler, req);
        }
@@ -951,11 +1633,125 @@ static struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
        return req;
 }
 
+static NTSTATUS dcerpc_request_prepare_vt(struct rpc_request *req)
+{
+       struct dcecli_security *sec = &req->p->conn->security_state;
+       struct dcerpc_sec_verification_trailer *t;
+       struct dcerpc_sec_vt *c = NULL;
+       struct ndr_push *ndr = NULL;
+       enum ndr_err_code ndr_err;
+
+       if (sec->auth_level < DCERPC_AUTH_LEVEL_INTEGRITY) {
+               return NT_STATUS_OK;
+       }
+
+       t = talloc_zero(req, struct dcerpc_sec_verification_trailer);
+       if (t == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!sec->verified_bitmask1) {
+               t->commands = talloc_realloc(t, t->commands,
+                                            struct dcerpc_sec_vt,
+                                            t->count.count + 1);
+               if (t->commands == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               c = &t->commands[t->count.count++];
+               ZERO_STRUCTP(c);
+
+               c->command = DCERPC_SEC_VT_COMMAND_BITMASK1;
+               if (req->p->conn->flags & DCERPC_PROPOSE_HEADER_SIGNING) {
+                       c->u.bitmask1 = DCERPC_SEC_VT_CLIENT_SUPPORTS_HEADER_SIGNING;
+               }
+               req->verify_bitmask1 = true;
+       }
+
+       if (!req->p->verified_pcontext) {
+               t->commands = talloc_realloc(t, t->commands,
+                                            struct dcerpc_sec_vt,
+                                            t->count.count + 1);
+               if (t->commands == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               c = &t->commands[t->count.count++];
+               ZERO_STRUCTP(c);
+
+               c->command = DCERPC_SEC_VT_COMMAND_PCONTEXT;
+               c->u.pcontext.abstract_syntax = req->p->syntax;
+               c->u.pcontext.transfer_syntax = req->p->transfer_syntax;
+
+               req->verify_pcontext = true;
+       }
+
+       if (!(req->p->conn->flags & DCERPC_HEADER_SIGNING)) {
+               t->commands = talloc_realloc(t, t->commands,
+                                            struct dcerpc_sec_vt,
+                                            t->count.count + 1);
+               if (t->commands == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               c = &t->commands[t->count.count++];
+               ZERO_STRUCTP(c);
+
+               c->command = DCERPC_SEC_VT_COMMAND_HEADER2;
+               c->u.header2.ptype = DCERPC_PKT_REQUEST;
+               if (req->p->conn->flags & DCERPC_PUSH_BIGENDIAN) {
+                       c->u.header2.drep[0] = 0;
+               } else {
+                       c->u.header2.drep[0] = DCERPC_DREP_LE;
+               }
+               c->u.header2.drep[1] = 0;
+               c->u.header2.drep[2] = 0;
+               c->u.header2.drep[3] = 0;
+               c->u.header2.call_id = req->call_id;
+               c->u.header2.context_id = req->p->context_id;
+               c->u.header2.opnum = req->opnum;
+       }
+
+       if (t->count.count == 0) {
+               TALLOC_FREE(t);
+               return NT_STATUS_OK;
+       }
+
+       c = &t->commands[t->count.count - 1];
+       c->command |= DCERPC_SEC_VT_COMMAND_END;
+
+       if (DEBUGLEVEL >= 10) {
+               NDR_PRINT_DEBUG(dcerpc_sec_verification_trailer, t);
+       }
+
+       ndr = ndr_push_init_ctx(req);
+       if (ndr == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /*
+        * for now we just copy and append
+        */
+
+       ndr_err = ndr_push_bytes(ndr, req->request_data.data,
+                                req->request_data.length);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               return ndr_map_error2ntstatus(ndr_err);
+       }
+
+       ndr_err = ndr_push_dcerpc_sec_verification_trailer(ndr,
+                                               NDR_SCALARS | NDR_BUFFERS,
+                                               t);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               return ndr_map_error2ntstatus(ndr_err);
+       }
+       req->request_data = ndr_push_blob(ndr);
+
+       return NT_STATUS_OK;
+}
+
 /*
   Send a request using the transport
 */
 
-static void dcerpc_ship_next_request(struct dcerpc_connection *c)
+static void dcerpc_ship_next_request(struct dcecli_connection *c)
 {
        struct rpc_request *req;
        struct dcerpc_pipe *p;
@@ -966,6 +1762,7 @@ static void dcerpc_ship_next_request(struct dcerpc_connection *c)
        bool first_packet = true;
        size_t sig_size = 0;
        bool need_async = false;
+       bool can_async = true;
 
        req = c->request_queue;
        if (req == NULL) {
@@ -979,6 +1776,32 @@ static void dcerpc_ship_next_request(struct dcerpc_connection *c)
                need_async = true;
        }
 
+       if (c->security_state.auth_info &&
+           c->security_state.generic_state)
+       {
+               struct gensec_security *gensec = c->security_state.generic_state;
+
+               switch (c->security_state.auth_info->auth_level) {
+               case DCERPC_AUTH_LEVEL_PRIVACY:
+               case DCERPC_AUTH_LEVEL_INTEGRITY:
+                       can_async = gensec_have_feature(gensec,
+                                               GENSEC_FEATURE_ASYNC_REPLIES);
+                       break;
+               case DCERPC_AUTH_LEVEL_CONNECT:
+               case DCERPC_AUTH_LEVEL_NONE:
+                       can_async = true;
+                       break;
+               default:
+                       can_async = false;
+                       break;
+               }
+       }
+
+       if (need_async && !can_async) {
+               req->wait_for_sync = true;
+               return;
+       }
+
        DLIST_REMOVE(c->request_queue, req);
        DLIST_ADD(c->pending, req);
        req->state = RPC_REQUEST_PENDING;
@@ -993,20 +1816,24 @@ static void dcerpc_ship_next_request(struct dcerpc_connection *c)
        chunk_size -= DCERPC_REQUEST_LENGTH;
        if (c->security_state.auth_info &&
            c->security_state.generic_state) {
+               size_t max_payload = chunk_size;
+
+               max_payload -= DCERPC_AUTH_TRAILER_LENGTH;
+               max_payload -= (max_payload % DCERPC_AUTH_PAD_ALIGNMENT);
+
                sig_size = gensec_sig_size(c->security_state.generic_state,
-                                          p->conn->srv_max_recv_frag);
+                                          max_payload);
                if (sig_size) {
                        chunk_size -= DCERPC_AUTH_TRAILER_LENGTH;
                        chunk_size -= sig_size;
                }
        }
-       chunk_size -= (chunk_size % 16);
+       chunk_size -= (chunk_size % DCERPC_AUTH_PAD_ALIGNMENT);
 
        pkt.ptype = DCERPC_PKT_REQUEST;
        pkt.call_id = req->call_id;
        pkt.auth_length = 0;
        pkt.pfc_flags = 0;
-       pkt.u.request.alloc_hint = remaining;
        pkt.u.request.context_id = p->context_id;
        pkt.u.request.opnum = req->opnum;
 
@@ -1033,6 +1860,7 @@ static void dcerpc_ship_next_request(struct dcerpc_connection *c)
                        last_frag = true;
                }
 
+               pkt.u.request.alloc_hint = remaining;
                pkt.u.request.stub_and_verifier.data = stub_data->data + 
                        (stub_data->length - remaining);
                pkt.u.request.stub_and_verifier.length = chunk;
@@ -1048,7 +1876,7 @@ static void dcerpc_ship_next_request(struct dcerpc_connection *c)
                        do_trans = true;
                }
 
-               req->status = p->conn->transport.send_request(p->conn, &blob, do_trans);
+               req->status = dcerpc_send_request(p->conn, &blob, do_trans);
                if (!NT_STATUS_IS_OK(req->status)) {
                        req->state = RPC_REQUEST_DONE;
                        DLIST_REMOVE(p->conn->pending, req);
@@ -1056,7 +1884,7 @@ static void dcerpc_ship_next_request(struct dcerpc_connection *c)
                }               
 
                if (last_frag && !do_trans) {
-                       req->status = p->conn->transport.send_read(p->conn);
+                       req->status = dcerpc_send_read(p->conn);
                        if (!NT_STATUS_IS_OK(req->status)) {
                                req->state = RPC_REQUEST_DONE;
                                DLIST_REMOVE(p->conn->pending, req);
@@ -1068,29 +1896,59 @@ static void dcerpc_ship_next_request(struct dcerpc_connection *c)
        }
 }
 
-/*
-  return the event context for a dcerpc pipe
-  used by callers who wish to operate asynchronously
-*/
-_PUBLIC_ struct tevent_context *dcerpc_event_context(struct dcerpc_pipe *p)
+static void dcerpc_io_trigger(struct tevent_context *ctx,
+                             struct tevent_immediate *im,
+                             void *private_data)
 {
-       return p->conn->event_ctx;
+       struct dcecli_connection *c =
+               talloc_get_type_abort(private_data,
+               struct dcecli_connection);
+
+       c->io_trigger_pending = false;
+
+       dcerpc_schedule_io_trigger(c);
+
+       dcerpc_ship_next_request(c);
 }
 
+static void dcerpc_schedule_io_trigger(struct dcecli_connection *c)
+{
+       if (c->dead) {
+               return;
+       }
+
+       if (c->request_queue == NULL) {
+               return;
+       }
+
+       if (c->request_queue->wait_for_sync && c->pending) {
+               return;
+       }
+
+       if (c->io_trigger_pending) {
+               return;
+       }
+
+       c->io_trigger_pending = true;
 
+       tevent_schedule_immediate(c->io_trigger,
+                                 c->event_ctx,
+                                 dcerpc_io_trigger,
+                                 c);
+}
 
 /*
   perform the receive side of a async dcerpc request
 */
-NTSTATUS dcerpc_request_recv(struct rpc_request *req,
-                            TALLOC_CTX *mem_ctx,
-                            DATA_BLOB *stub_data)
+static NTSTATUS dcerpc_request_recv(struct rpc_request *req,
+                                   TALLOC_CTX *mem_ctx,
+                                   DATA_BLOB *stub_data)
 {
        NTSTATUS status;
 
        while (req->state != RPC_REQUEST_DONE) {
-               struct tevent_context *ctx = dcerpc_event_context(req->p);
-               if (event_loop_once(ctx) != 0) {
+               struct tevent_context *ctx = req->p->conn->event_ctx;
+               if (tevent_loop_once(ctx) != 0) {
                        return NT_STATUS_CONNECTION_DISCONNECTED;
                }
        }
@@ -1106,34 +1964,13 @@ NTSTATUS dcerpc_request_recv(struct rpc_request *req,
        return status;
 }
 
-/*
-  perform a full request/response pair on a dcerpc pipe
-*/
-NTSTATUS dcerpc_request(struct dcerpc_pipe *p, 
-                       struct GUID *object,
-                       uint16_t opnum,
-                       TALLOC_CTX *mem_ctx,
-                       DATA_BLOB *stub_data_in,
-                       DATA_BLOB *stub_data_out)
-{
-       struct rpc_request *req;
-
-       req = dcerpc_request_send(p, object, opnum, stub_data_in);
-       if (req == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       return dcerpc_request_recv(req, mem_ctx, stub_data_out);
-}
-
-
 /*
   this is a paranoid NDR validator. For every packet we push onto the wire
   we pull it back again, then push it again. Then we compare the raw NDR data
   for that to the NDR we initially generated. If they don't match then we know
   we must have a bug in either the pull or push side of our code
 */
-static NTSTATUS dcerpc_ndr_validate_in(struct dcerpc_connection *c, 
+static NTSTATUS dcerpc_ndr_validate_in(struct dcecli_connection *c, 
                                       TALLOC_CTX *mem_ctx,
                                       DATA_BLOB blob,
                                       size_t struct_size,
@@ -1218,7 +2055,7 @@ static NTSTATUS dcerpc_ndr_validate_in(struct dcerpc_connection *c,
   initially generated. If they don't match then we know we must have a
   bug in either the pull or push side of our code
 */
-static NTSTATUS dcerpc_ndr_validate_out(struct dcerpc_connection *c,
+static NTSTATUS dcerpc_ndr_validate_out(struct dcecli_connection *c,
                                        struct ndr_pull *pull_in,
                                        void *struct_ptr,
                                        size_t struct_size,
@@ -1321,380 +2158,579 @@ static NTSTATUS dcerpc_ndr_validate_out(struct dcerpc_connection *c,
        return NT_STATUS_OK;
 }
 
+/*
+  a useful function for retrieving the server name we connected to
+*/
+_PUBLIC_ const char *dcerpc_server_name(struct dcerpc_pipe *p)
+{
+       return p->conn ? p->conn->server_name : NULL;
+}
 
-/**
- send a rpc request given a dcerpc_call structure 
- */
-struct rpc_request *dcerpc_ndr_request_send(struct dcerpc_pipe *p,
-                                           const struct GUID *object,
-                                           const struct ndr_interface_table *table,
-                                           uint32_t opnum,
-                                           bool async,
-                                           TALLOC_CTX *mem_ctx,
-                                           void *r)
-{
-       const struct ndr_interface_call *call;
-       struct ndr_push *push;
-       NTSTATUS status;
-       DATA_BLOB request;
-       struct rpc_request *req;
-       enum ndr_err_code ndr_err;
-
-       call = &table->calls[opnum];
 
-       /* setup for a ndr_push_* call */
-       push = ndr_push_init_ctx(mem_ctx);
-       if (!push) {
-               return NULL;
-       }
+/*
+  get the dcerpc auth_level for a open connection
+*/
+uint32_t dcerpc_auth_level(struct dcecli_connection *c) 
+{
+       uint8_t auth_level;
 
-       if (p->conn->flags & DCERPC_PUSH_BIGENDIAN) {
-               push->flags |= LIBNDR_FLAG_BIGENDIAN;
+       if (c->flags & DCERPC_SEAL) {
+               auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
+       } else if (c->flags & DCERPC_SIGN) {
+               auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
+       } else if (c->flags & DCERPC_CONNECT) {
+               auth_level = DCERPC_AUTH_LEVEL_CONNECT;
+       } else {
+               auth_level = DCERPC_AUTH_LEVEL_NONE;
        }
+       return auth_level;
+}
 
-       if (p->conn->flags & DCERPC_NDR64) {
-               push->flags |= LIBNDR_FLAG_NDR64;
-       }
+struct dcerpc_alter_context_state {
+       struct tevent_context *ev;
+       struct dcerpc_pipe *p;
+};
+
+static void dcerpc_alter_context_fail_handler(struct rpc_request *subreq);
+static void dcerpc_alter_context_recv_handler(struct rpc_request *req,
+                                             DATA_BLOB *raw_packet,
+                                             struct ncacn_packet *pkt);
+
+struct tevent_req *dcerpc_alter_context_send(TALLOC_CTX *mem_ctx,
+                                            struct tevent_context *ev,
+                                            struct dcerpc_pipe *p,
+                                            const struct ndr_syntax_id *syntax,
+                                            const struct ndr_syntax_id *transfer_syntax)
+{
+       struct tevent_req *req;
+       struct dcerpc_alter_context_state *state;
+       struct ncacn_packet pkt;
+       DATA_BLOB blob;
+       NTSTATUS status;
+       struct rpc_request *subreq;
+       uint32_t flags;
 
-       /* push the structure into a blob */
-       ndr_err = call->ndr_push(push, NDR_IN, r);
-       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-               status = ndr_map_error2ntstatus(ndr_err);
-               DEBUG(2,("Unable to ndr_push structure in dcerpc_ndr_request_send - %s\n",
-                        nt_errstr(status)));
-               talloc_free(push);
+       req = tevent_req_create(mem_ctx, &state,
+                               struct dcerpc_alter_context_state);
+       if (req == NULL) {
                return NULL;
        }
 
-       /* retrieve the blob */
-       request = ndr_push_blob(push);
+       state->ev = ev;
+       state->p = p;
 
-       if (p->conn->flags & DCERPC_DEBUG_VALIDATE_IN) {
-               status = dcerpc_ndr_validate_in(p->conn, push, request, call->struct_size, 
-                                               call->ndr_push, call->ndr_pull);
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(2,("Validation failed in dcerpc_ndr_request_send - %s\n",
-                                nt_errstr(status)));
-                       talloc_free(push);
-                       return NULL;
-               }
+       p->syntax = *syntax;
+       p->transfer_syntax = *transfer_syntax;
+
+       flags = dcerpc_binding_get_flags(p->binding);
+
+       init_ncacn_hdr(p->conn, &pkt);
+
+       pkt.ptype = DCERPC_PKT_ALTER;
+       pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+       pkt.call_id = p->conn->call_id;
+       pkt.auth_length = 0;
+
+       if (flags & DCERPC_CONCURRENT_MULTIPLEX) {
+               pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
        }
 
-       DEBUG(10,("rpc request data:\n"));
-       dump_data(10, request.data, request.length);
+       pkt.u.alter.max_xmit_frag = p->conn->srv_max_xmit_frag;
+       pkt.u.alter.max_recv_frag = p->conn->srv_max_recv_frag;
+       pkt.u.alter.assoc_group_id = dcerpc_binding_get_assoc_group_id(p->binding);
+       pkt.u.alter.num_contexts = 1;
+       pkt.u.alter.ctx_list = talloc_array(state, struct dcerpc_ctx_list, 1);
+       if (tevent_req_nomem(pkt.u.alter.ctx_list, req)) {
+               return tevent_req_post(req, ev);
+       }
+       pkt.u.alter.ctx_list[0].context_id = p->context_id;
+       pkt.u.alter.ctx_list[0].num_transfer_syntaxes = 1;
+       pkt.u.alter.ctx_list[0].abstract_syntax = p->syntax;
+       pkt.u.alter.ctx_list[0].transfer_syntaxes = &p->transfer_syntax;
+       pkt.u.alter.auth_info = data_blob(NULL, 0);
+
+       /* construct the NDR form of the packet */
+       status = ncacn_push_auth(&blob, state, &pkt,
+                                p->conn->security_state.auth_info);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
 
-       /* make the actual dcerpc request */
-       req = dcerpc_request_send(p, object, opnum, &request);
+       /*
+        * we allocate a dcerpc_request so we can be in the same
+        * request queue as normal requests
+        */
+       subreq = talloc_zero(state, struct rpc_request);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
 
-       if (req != NULL) {
-               req->ndr.table = table;
-               req->ndr.opnum = opnum;
-               req->ndr.struct_ptr = r;
-               req->ndr.mem_ctx = mem_ctx;
+       subreq->state = RPC_REQUEST_PENDING;
+       subreq->call_id = pkt.call_id;
+       subreq->async.private_data = req;
+       subreq->async.callback = dcerpc_alter_context_fail_handler;
+       subreq->p = p;
+       subreq->recv_handler = dcerpc_alter_context_recv_handler;
+       DLIST_ADD_END(p->conn->pending, subreq);
+       talloc_set_destructor(subreq, dcerpc_req_dequeue);
+
+       status = dcerpc_send_request(p->conn, &blob, true);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
        }
 
-       talloc_free(push);
+       tevent_add_timer(ev, subreq,
+                        timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
+                        dcerpc_timeout_handler, subreq);
 
        return req;
 }
 
-/*
-  receive the answer from a dcerpc_ndr_request_send()
-*/
-_PUBLIC_ NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
+static void dcerpc_alter_context_fail_handler(struct rpc_request *subreq)
+{
+       struct tevent_req *req =
+               talloc_get_type_abort(subreq->async.private_data,
+               struct tevent_req);
+       struct dcerpc_alter_context_state *state =
+               tevent_req_data(req,
+               struct dcerpc_alter_context_state);
+       NTSTATUS status = subreq->status;
+
+       TALLOC_FREE(subreq);
+
+       /*
+        * We trigger the callback in the next event run
+        * because the code in this file might trigger
+        * multiple request callbacks from within a single
+        * while loop.
+        *
+        * In order to avoid segfaults from within
+        * dcerpc_connection_dead() we call
+        * tevent_req_defer_callback().
+        */
+       tevent_req_defer_callback(req, state->ev);
+
+       tevent_req_nterror(req, status);
+}
+
+static void dcerpc_alter_context_recv_handler(struct rpc_request *subreq,
+                                             DATA_BLOB *raw_packet,
+                                             struct ncacn_packet *pkt)
 {
-       struct dcerpc_pipe *p = req->p;
+       struct tevent_req *req =
+               talloc_get_type_abort(subreq->async.private_data,
+               struct tevent_req);
+       struct dcerpc_alter_context_state *state =
+               tevent_req_data(req,
+               struct dcerpc_alter_context_state);
+       struct dcecli_connection *conn = state->p->conn;
        NTSTATUS status;
-       DATA_BLOB response;
-       struct ndr_pull *pull;
-       unsigned int flags;
-       TALLOC_CTX *mem_ctx = req->ndr.mem_ctx;
-       void *r = req->ndr.struct_ptr;
-       uint32_t opnum = req->ndr.opnum;
-       const struct ndr_interface_table *table = req->ndr.table;
-       const struct ndr_interface_call *call = &table->calls[opnum];
-       enum ndr_err_code ndr_err;
 
-       /* make sure the recv code doesn't free the request, as we
-          need to grab the flags element before it is freed */
-       if (talloc_reference(p, req) == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
+       /*
+        * Note that pkt is allocated under raw_packet->data,
+        * while raw_packet->data is a child of subreq.
+        */
+       talloc_steal(state, raw_packet->data);
+       TALLOC_FREE(subreq);
 
-       status = dcerpc_request_recv(req, mem_ctx, &response);
-       if (!NT_STATUS_IS_OK(status)) {
-               talloc_unlink(p, req);
-               return status;
-       }
+       /*
+        * We trigger the callback in the next event run
+        * because the code in this file might trigger
+        * multiple request callbacks from within a single
+        * while loop.
+        *
+        * In order to avoid segfaults from within
+        * dcerpc_connection_dead() we call
+        * tevent_req_defer_callback().
+        */
+       tevent_req_defer_callback(req, state->ev);
 
-       flags = req->flags;
+       if (pkt->ptype == DCERPC_PKT_ALTER_RESP &&
+           pkt->u.alter_resp.num_results == 1 &&
+           pkt->u.alter_resp.ctx_list[0].result != 0) {
+               status = dcerpc_map_ack_reason(&pkt->u.alter_resp.ctx_list[0]);
+               DEBUG(2,("dcerpc: alter_resp failed - reason %d - %s\n",
+                        pkt->u.alter_resp.ctx_list[0].reason.value,
+                        nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
 
-       /* prepare for ndr_pull_* */
-       pull = ndr_pull_init_flags(p->conn, &response, mem_ctx);
-       if (!pull) {
-               talloc_unlink(p, req);
-               return NT_STATUS_NO_MEMORY;
+       if (pkt->ptype == DCERPC_PKT_FAULT) {
+               DEBUG(5,("dcerpc: alter_resp - rpc fault: %s\n",
+                        dcerpc_errstr(state, pkt->u.fault.status)));
+               if (pkt->u.fault.status == DCERPC_FAULT_ACCESS_DENIED) {
+                       state->p->last_fault_code = pkt->u.fault.status;
+                       tevent_req_nterror(req, NT_STATUS_LOGON_FAILURE);
+               } else if (pkt->u.fault.status == DCERPC_FAULT_SEC_PKG_ERROR) {
+                       state->p->last_fault_code = pkt->u.fault.status;
+                       tevent_req_nterror(req, NT_STATUS_LOGON_FAILURE);
+               } else {
+                       state->p->last_fault_code = pkt->u.fault.status;
+                       status = dcerpc_fault_to_nt_status(pkt->u.fault.status);
+                       tevent_req_nterror(req, status);
+               }
+               return;
        }
 
-       if (pull->data) {
-               pull->data = talloc_steal(pull, pull->data);
+       if (pkt->ptype != DCERPC_PKT_ALTER_RESP ||
+           pkt->u.alter_resp.num_results == 0 ||
+           pkt->u.alter_resp.ctx_list[0].result != 0) {
+               state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
+               tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
+               return;
        }
-       talloc_unlink(p, req);
 
-       if (flags & DCERPC_PULL_BIGENDIAN) {
-               pull->flags |= LIBNDR_FLAG_BIGENDIAN;
+       /* the alter_resp might contain a reply set of credentials */
+       if (conn->security_state.auth_info &&
+           pkt->u.alter_resp.auth_info.length) {
+               uint32_t auth_length;
+
+               status = dcerpc_pull_auth_trailer(pkt, conn, &pkt->u.alter_resp.auth_info,
+                                                 conn->security_state.auth_info, &auth_length, true);
+               if (tevent_req_nterror(req, status)) {
+                       return;
+               }
        }
 
-       DEBUG(10,("rpc reply data:\n"));
-       dump_data(10, pull->data, pull->data_size);
+       tevent_req_done(req);
+}
 
-       /* pull the structure from the blob */
-       ndr_err = call->ndr_pull(pull, NDR_OUT, r);
-       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-               status = ndr_map_error2ntstatus(ndr_err);
-               dcerpc_log_packet(p->conn->packet_log_dir,
-                                                 table, opnum, NDR_OUT, 
-                                                 &response);
+NTSTATUS dcerpc_alter_context_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+/* 
+   send a dcerpc alter_context request
+*/
+_PUBLIC_ NTSTATUS dcerpc_alter_context(struct dcerpc_pipe *p, 
+                             TALLOC_CTX *mem_ctx,
+                             const struct ndr_syntax_id *syntax,
+                             const struct ndr_syntax_id *transfer_syntax)
+{
+       struct tevent_req *subreq;
+       struct tevent_context *ev = p->conn->event_ctx;
+       bool ok;
+
+       /* TODO: create a new event context here */
+
+       subreq = dcerpc_alter_context_send(mem_ctx, ev,
+                                          p, syntax, transfer_syntax);
+       if (subreq == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ok = tevent_req_poll(subreq, ev);
+       if (!ok) {
+               NTSTATUS status;
+               status = map_nt_error_from_unix_common(errno);
                return status;
        }
 
-       if (p->conn->flags & DCERPC_DEBUG_VALIDATE_OUT) {
-               status = dcerpc_ndr_validate_out(p->conn, pull, r, call->struct_size, 
-                                                call->ndr_push, call->ndr_pull, 
-                                                call->ndr_print);
-               if (!NT_STATUS_IS_OK(status)) {
-                       dcerpc_log_packet(p->conn->packet_log_dir, 
-                                                         table, opnum, NDR_OUT, 
-                                 &response);
-                       return status;
-               }
+       return dcerpc_alter_context_recv(subreq);
+}
+
+static void dcerpc_transport_dead(struct dcecli_connection *c, NTSTATUS status)
+{
+       if (c->transport.stream == NULL) {
+               return;
        }
 
-       if (pull->offset != pull->data_size) {
-               DEBUG(0,("Warning! ignoring %d unread bytes in rpc packet!\n", 
-                        pull->data_size - pull->offset));
-               /* we used to return NT_STATUS_INFO_LENGTH_MISMATCH here,
-                  but it turns out that early versions of NT
-                  (specifically NT3.1) add junk onto the end of rpc
-                  packets, so if we want to interoperate at all with
-                  those versions then we need to ignore this error */
+       tevent_queue_stop(c->transport.write_queue);
+       TALLOC_FREE(c->transport.read_subreq);
+       TALLOC_FREE(c->transport.stream);
+
+       if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
+               status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
        }
 
-       /* TODO: make pull context independent from the output mem_ctx and free the pull context */
+       if (NT_STATUS_EQUAL(NT_STATUS_OK, status)) {
+               status = NT_STATUS_END_OF_FILE;
+       }
 
-       return NT_STATUS_OK;
+       dcerpc_recv_data(c, NULL, status);
 }
 
 
 /*
-  a useful helper function for synchronous rpc requests 
-
-  this can be used when you have ndr push/pull functions in the
-  standard format
+   shutdown SMB pipe connection
 */
-_PUBLIC_ NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
-                           const struct GUID *object,
-                           const struct ndr_interface_table *table,
-                           uint32_t opnum, 
-                           TALLOC_CTX *mem_ctx, 
-                           void *r)
+struct dcerpc_shutdown_pipe_state {
+       struct dcecli_connection *c;
+       NTSTATUS status;
+};
+
+static void dcerpc_shutdown_pipe_done(struct tevent_req *subreq);
+
+static NTSTATUS dcerpc_shutdown_pipe(struct dcecli_connection *c, NTSTATUS status)
 {
-       struct rpc_request *req;
+       struct dcerpc_shutdown_pipe_state *state;
+       struct tevent_req *subreq;
 
-       req = dcerpc_ndr_request_send(p, object, table, opnum, false, mem_ctx, r);
-       if (req == NULL) {
+       if (c->transport.stream == NULL) {
+               return NT_STATUS_OK;
+       }
+
+       state = talloc_zero(c, struct dcerpc_shutdown_pipe_state);
+       if (state == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
+       state->c = c;
+       state->status = status;
 
-       return dcerpc_ndr_request_recv(req);
-}
+       subreq = tstream_disconnect_send(state, c->event_ctx, c->transport.stream);
+       if (subreq == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       tevent_req_set_callback(subreq, dcerpc_shutdown_pipe_done, state);
 
+       return status;
+}
 
-/*
-  a useful function for retrieving the server name we connected to
-*/
-_PUBLIC_ const char *dcerpc_server_name(struct dcerpc_pipe *p)
+static void dcerpc_shutdown_pipe_done(struct tevent_req *subreq)
 {
-       if (!p->conn->transport.target_hostname) {
-               if (!p->conn->transport.peer_name) {
-                       return "";
-               }
-               return p->conn->transport.peer_name(p->conn);
-       }
-       return p->conn->transport.target_hostname(p->conn);
+       struct dcerpc_shutdown_pipe_state *state =
+               tevent_req_callback_data(subreq, struct dcerpc_shutdown_pipe_state);
+       struct dcecli_connection *c = state->c;
+       NTSTATUS status = state->status;
+       int error;
+
+       /*
+        * here we ignore the return values...
+        */
+       tstream_disconnect_recv(subreq, &error);
+       TALLOC_FREE(subreq);
+
+       TALLOC_FREE(state);
+
+       dcerpc_transport_dead(c, status);
 }
 
 
-/*
-  get the dcerpc auth_level for a open connection
-*/
-uint32_t dcerpc_auth_level(struct dcerpc_connection *c) 
+
+struct dcerpc_send_read_state {
+       struct dcecli_connection *p;
+};
+
+static int dcerpc_send_read_state_destructor(struct dcerpc_send_read_state *state)
 {
-       uint8_t auth_level;
+       struct dcecli_connection *p = state->p;
 
-       if (c->flags & DCERPC_SEAL) {
-               auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
-       } else if (c->flags & DCERPC_SIGN) {
-               auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
-       } else if (c->flags & DCERPC_CONNECT) {
-               auth_level = DCERPC_AUTH_LEVEL_CONNECT;
-       } else {
-               auth_level = DCERPC_AUTH_LEVEL_NONE;
-       }
-       return auth_level;
+       p->transport.read_subreq = NULL;
+
+       return 0;
 }
 
-/*
-  Receive an alter reply from the transport
-*/
-static void dcerpc_alter_recv_handler(struct rpc_request *req,
-                                     DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
+static void dcerpc_send_read_done(struct tevent_req *subreq);
+
+static NTSTATUS dcerpc_send_read(struct dcecli_connection *p)
 {
-       struct composite_context *c;
-       struct dcerpc_pipe *recv_pipe;
+       struct dcerpc_send_read_state *state;
 
-       c = talloc_get_type(req->async.private_data, struct composite_context);
-       recv_pipe = talloc_get_type(c->private_data, struct dcerpc_pipe);
+       if (p->transport.read_subreq != NULL) {
+               p->transport.pending_reads++;
+               return NT_STATUS_OK;
+       }
 
-       if (pkt->ptype == DCERPC_PKT_ALTER_RESP &&
-           pkt->u.alter_resp.num_results == 1 &&
-           pkt->u.alter_resp.ctx_list[0].result != 0) {
-               DEBUG(2,("dcerpc: alter_resp failed - reason %d\n", 
-                        pkt->u.alter_resp.ctx_list[0].reason));
-               composite_error(c, dcerpc_map_reason(pkt->u.alter_resp.ctx_list[0].reason));
-               return;
+       state = talloc_zero(p, struct dcerpc_send_read_state);
+       if (state == NULL) {
+               return NT_STATUS_NO_MEMORY;
        }
+       state->p = p;
 
-       if (pkt->ptype == DCERPC_PKT_FAULT) {
-               DEBUG(5,("rpc fault: %s\n", dcerpc_errstr(c, pkt->u.fault.status)));
-               recv_pipe->last_fault_code = pkt->u.fault.status;
-               composite_error(c, NT_STATUS_NET_WRITE_FAULT);
-               return;
+       talloc_set_destructor(state, dcerpc_send_read_state_destructor);
+
+       p->transport.read_subreq = dcerpc_read_ncacn_packet_send(state,
+                                                         p->event_ctx,
+                                                         p->transport.stream);
+       if (p->transport.read_subreq == NULL) {
+               return NT_STATUS_NO_MEMORY;
        }
+       tevent_req_set_callback(p->transport.read_subreq, dcerpc_send_read_done, state);
 
-       if (pkt->ptype != DCERPC_PKT_ALTER_RESP ||
-           pkt->u.alter_resp.num_results == 0 ||
-           pkt->u.alter_resp.ctx_list[0].result != 0) {
-               recv_pipe->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
-               composite_error(c, NT_STATUS_NET_WRITE_FAULT);
+       return NT_STATUS_OK;
+}
+
+static void dcerpc_send_read_done(struct tevent_req *subreq)
+{
+       struct dcerpc_send_read_state *state =
+               tevent_req_callback_data(subreq,
+                                        struct dcerpc_send_read_state);
+       struct dcecli_connection *p = state->p;
+       NTSTATUS status;
+       struct ncacn_packet *pkt;
+       DATA_BLOB blob;
+
+       status = dcerpc_read_ncacn_packet_recv(subreq, state,
+                                              &pkt, &blob);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(state);
+               dcerpc_transport_dead(p, status);
                return;
        }
 
-       /* the alter_resp might contain a reply set of credentials */
-       if (recv_pipe->conn->security_state.auth_info &&
-           pkt->u.alter_resp.auth_info.length) {
-               struct dcerpc_connection *conn = recv_pipe->conn;
-               NTSTATUS status;
-               uint32_t auth_length;
-               status = dcerpc_pull_auth_trailer(pkt, conn, &pkt->u.alter_resp.auth_info,
-                                                 conn->security_state.auth_info, &auth_length, true);
+       /*
+        * here we steal into thet connection context,
+        * but p->transport.recv_data() will steal or free it again
+        */
+       talloc_steal(p, blob.data);
+       TALLOC_FREE(state);
+
+       if (p->transport.pending_reads > 0) {
+               p->transport.pending_reads--;
+
+               status = dcerpc_send_read(p);
                if (!NT_STATUS_IS_OK(status)) {
-                       composite_error(c, status);
+                       dcerpc_transport_dead(p, status);
                        return;
                }
        }
 
-       composite_done(c);
+       dcerpc_recv_data(p, &blob, NT_STATUS_OK);
 }
 
-/* 
-   send a dcerpc alter_context request
-*/
-struct composite_context *dcerpc_alter_context_send(struct dcerpc_pipe *p, 
-                                                   TALLOC_CTX *mem_ctx,
-                                                   const struct ndr_syntax_id *syntax,
-                                                   const struct ndr_syntax_id *transfer_syntax)
-{
-       struct composite_context *c;
-       struct ncacn_packet pkt;
+struct dcerpc_send_request_state {
+       struct dcecli_connection *p;
        DATA_BLOB blob;
-       struct rpc_request *req;
+       struct iovec iov;
+};
 
-       c = composite_create(mem_ctx, p->conn->event_ctx);
-       if (c == NULL) return NULL;
+static int dcerpc_send_request_state_destructor(struct dcerpc_send_request_state *state)
+{
+       struct dcecli_connection *p = state->p;
 
-       c->private_data = p;
+       p->transport.read_subreq = NULL;
 
-       p->syntax = *syntax;
-       p->transfer_syntax = *transfer_syntax;
+       return 0;
+}
 
-       init_ncacn_hdr(p->conn, &pkt);
+static void dcerpc_send_request_wait_done(struct tevent_req *subreq);
+static void dcerpc_send_request_done(struct tevent_req *subreq);
 
-       pkt.ptype = DCERPC_PKT_ALTER;
-       pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
-       pkt.call_id = p->conn->call_id;
-       pkt.auth_length = 0;
+static NTSTATUS dcerpc_send_request(struct dcecli_connection *p, DATA_BLOB *data,
+                                   bool trigger_read)
+{
+       struct dcerpc_send_request_state *state;
+       struct tevent_req *subreq;
+       bool use_trans = trigger_read;
 
-       if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
-               pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
+       if (p->transport.stream == NULL) {
+               return NT_STATUS_CONNECTION_DISCONNECTED;
        }
 
-       if (p->binding->flags & DCERPC_HEADER_SIGNING) {
-               pkt.pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
+       state = talloc_zero(p, struct dcerpc_send_request_state);
+       if (state == NULL) {
+               return NT_STATUS_NO_MEMORY;
        }
+       state->p = p;
 
-       pkt.u.alter.max_xmit_frag = 5840;
-       pkt.u.alter.max_recv_frag = 5840;
-       pkt.u.alter.assoc_group_id = p->binding->assoc_group_id;
-       pkt.u.alter.num_contexts = 1;
-       pkt.u.alter.ctx_list = talloc_array(c, struct dcerpc_ctx_list, 1);
-       if (composite_nomem(pkt.u.alter.ctx_list, c)) return c;
-       pkt.u.alter.ctx_list[0].context_id = p->context_id;
-       pkt.u.alter.ctx_list[0].num_transfer_syntaxes = 1;
-       pkt.u.alter.ctx_list[0].abstract_syntax = p->syntax;
-       pkt.u.alter.ctx_list[0].transfer_syntaxes = &p->transfer_syntax;
-       pkt.u.alter.auth_info = data_blob(NULL, 0);
+       state->blob = data_blob_talloc(state, data->data, data->length);
+       if (state->blob.data == NULL) {
+               TALLOC_FREE(state);
+               return NT_STATUS_NO_MEMORY;
+       }
+       state->iov.iov_base = (void *)state->blob.data;
+       state->iov.iov_len = state->blob.length;
 
-       /* construct the NDR form of the packet */
-       c->status = ncacn_push_auth(&blob, mem_ctx, &pkt,
-                                   p->conn->security_state.auth_info);
-       if (!composite_is_ok(c)) return c;
+       if (p->transport.read_subreq != NULL) {
+               use_trans = false;
+       }
 
-       p->conn->transport.recv_data = dcerpc_recv_data;
+       if (!tstream_is_smbXcli_np(p->transport.stream)) {
+               use_trans = false;
+       }
 
-       /*
-        * we allocate a dcerpc_request so we can be in the same
-        * request queue as normal requests
-        */
-       req = talloc_zero(c, struct rpc_request);
-       if (composite_nomem(req, c)) return c;
+       if (use_trans) {
+               /*
+                * we need to block reads until our write is
+                * the next in the write queue.
+                */
+               p->transport.read_subreq = tevent_queue_wait_send(state, p->event_ctx,
+                                                            p->transport.write_queue);
+               if (p->transport.read_subreq == NULL) {
+                       TALLOC_FREE(state);
+                       return NT_STATUS_NO_MEMORY;
+               }
+               tevent_req_set_callback(p->transport.read_subreq,
+                                       dcerpc_send_request_wait_done,
+                                       state);
 
-       req->state = RPC_REQUEST_PENDING;
-       req->call_id = pkt.call_id;
-       req->async.private_data = c;
-       req->async.callback = dcerpc_composite_fail;
-       req->p = p;
-       req->recv_handler = dcerpc_alter_recv_handler;
-       DLIST_ADD_END(p->conn->pending, req, struct rpc_request *);
-       talloc_set_destructor(req, dcerpc_req_dequeue);
+               talloc_set_destructor(state, dcerpc_send_request_state_destructor);
 
-       c->status = p->conn->transport.send_request(p->conn, &blob, true);
-       if (!composite_is_ok(c)) return c;
+               trigger_read = false;
+       }
 
-       event_add_timed(c->event_ctx, req,
-                       timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
-                       dcerpc_timeout_handler, req);
+       subreq = tstream_writev_queue_send(state, p->event_ctx,
+                                          p->transport.stream,
+                                          p->transport.write_queue,
+                                          &state->iov, 1);
+       if (subreq == NULL) {
+               TALLOC_FREE(state);
+               return NT_STATUS_NO_MEMORY;
+       }
+       tevent_req_set_callback(subreq, dcerpc_send_request_done, state);
 
-       return c;
+       if (trigger_read) {
+               dcerpc_send_read(p);
+       }
+
+       return NT_STATUS_OK;
 }
 
-NTSTATUS dcerpc_alter_context_recv(struct composite_context *ctx)
+static void dcerpc_send_request_wait_done(struct tevent_req *subreq)
 {
-       NTSTATUS result = composite_wait(ctx);
-       talloc_free(ctx);
-       return result;
+       struct dcerpc_send_request_state *state =
+               tevent_req_callback_data(subreq,
+               struct dcerpc_send_request_state);
+       struct dcecli_connection *p = state->p;
+       NTSTATUS status;
+       bool ok;
+
+       p->transport.read_subreq = NULL;
+       talloc_set_destructor(state, NULL);
+
+       ok = tevent_queue_wait_recv(subreq);
+       if (!ok) {
+               TALLOC_FREE(state);
+               dcerpc_transport_dead(p, NT_STATUS_NO_MEMORY);
+               return;
+       }
+
+       if (tevent_queue_length(p->transport.write_queue) <= 2) {
+               status = tstream_smbXcli_np_use_trans(p->transport.stream);
+               if (!NT_STATUS_IS_OK(status)) {
+                       TALLOC_FREE(state);
+                       dcerpc_transport_dead(p, status);
+                       return;
+               }
+       }
+
+       /* we free subreq after tstream_cli_np_use_trans */
+       TALLOC_FREE(subreq);
+
+       dcerpc_send_read(p);
 }
 
-/* 
-   send a dcerpc alter_context request
-*/
-_PUBLIC_ NTSTATUS dcerpc_alter_context(struct dcerpc_pipe *p, 
-                             TALLOC_CTX *mem_ctx,
-                             const struct ndr_syntax_id *syntax,
-                             const struct ndr_syntax_id *transfer_syntax)
+static void dcerpc_send_request_done(struct tevent_req *subreq)
 {
-       struct composite_context *creq;
-       creq = dcerpc_alter_context_send(p, mem_ctx, syntax, transfer_syntax);
-       return dcerpc_alter_context_recv(creq);
-}
+       struct dcerpc_send_request_state *state =
+               tevent_req_callback_data(subreq,
+               struct dcerpc_send_request_state);
+       int ret;
+       int error;
+
+       ret = tstream_writev_queue_recv(subreq, &error);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               struct dcecli_connection *p = state->p;
+               NTSTATUS status = map_nt_error_from_unix_common(error);
+
+               TALLOC_FREE(state);
+               dcerpc_transport_dead(p, status);
+               return;
+       }
 
+       TALLOC_FREE(state);
+}