s4:librpc/rpc: allow a total reassembled response payload of 240 MBytes
[samba.git] / source4 / librpc / rpc / dcerpc.c
index 63d91948f9b69e9d49a341b3097cdf34c41ba9c6..4225e1d1157df83df98f531bc80a65bb08f9621f 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"
+
 
 enum rpc_request_state {
        RPC_REQUEST_QUEUED,
@@ -61,14 +64,9 @@ struct rpc_request {
        uint16_t opnum;
        DATA_BLOB request_data;
        bool ignore_timeout;
-
-       /* use by the ndr level async recv call */
-       struct {
-               const struct ndr_interface_table *table;
-               uint32_t opnum;
-               void *struct_ptr;
-               TALLOC_CTX *mem_ctx;
-       } ndr;
+       bool wait_for_sync;
+       bool verify_bitmask1;
+       bool verify_pcontext;
 
        struct {
                void (*callback)(struct rpc_request *);
@@ -82,9 +80,10 @@ _PUBLIC_ NTSTATUS dcerpc_init(void)
 }
 
 static void dcerpc_connection_dead(struct dcecli_connection *conn, NTSTATUS status);
-static void dcerpc_ship_next_request(struct dcecli_connection *c);
+static void dcerpc_schedule_io_trigger(struct dcecli_connection *c);
 
-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);
@@ -104,6 +103,10 @@ static NTSTATUS dcerpc_ndr_validate_out(struct dcecli_connection *c,
                                        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 dcecli_connection *conn)
@@ -138,15 +141,29 @@ static struct dcecli_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx,
        }
 
        c->call_id = 1;
-       c->security_state.auth_info = NULL;
+       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.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->max_total_response_size = DCERPC_NCACN_RESPONSE_DEFAULT_MAX_SIZE;
        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;
@@ -193,6 +210,25 @@ static uint32_t dcerpc_bh_set_timeout(struct dcerpc_binding_handle *h,
        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;
@@ -235,7 +271,8 @@ static struct tevent_req *dcerpc_bh_raw_call_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
-       subreq = dcerpc_request_send(hs->p,
+       subreq = dcerpc_request_send(state,
+                                    hs->p,
                                     object,
                                     opnum,
                                     &state->in_data);
@@ -544,6 +581,7 @@ 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,
@@ -667,7 +705,7 @@ static NTSTATUS ncacn_pull(struct dcecli_connection *c, DATA_BLOB *blob, TALLOC_
        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;
        }
@@ -676,11 +714,20 @@ static NTSTATUS ncacn_pull(struct dcecli_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;
 }
 
@@ -695,12 +742,16 @@ static NTSTATUS ncacn_pull_request_auth(struct dcecli_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;
+       status = dcerpc_verify_ncacn_packet_header(pkt, DCERPC_PKT_RESPONSE,
+                                       pkt->u.response.stub_and_verifier.length,
+                                       0, /* required_flags */
+                                       DCERPC_PFC_FLAG_FIRST |
+                                       DCERPC_PFC_FLAG_LAST);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
-       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;
@@ -720,6 +771,14 @@ static NTSTATUS ncacn_pull_request_auth(struct dcecli_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);
@@ -727,8 +786,20 @@ static NTSTATUS ncacn_pull_request_auth(struct dcecli_connection *c, TALLOC_CTX
 
        pkt->u.response.stub_and_verifier.length -= auth_length;
 
+       if (auth.auth_type != c->security_state.auth_type) {
+               return NT_STATUS_RPC_PROTOCOL_ERROR;
+       }
+
+       if (auth.auth_level != c->security_state.auth_level) {
+               return NT_STATUS_RPC_PROTOCOL_ERROR;
+       }
+
+       if (auth.auth_context_id != c->security_state.auth_context_id) {
+               return NT_STATUS_RPC_PROTOCOL_ERROR;
+       }
+
        /* 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, 
                                              raw_packet->data + DCERPC_REQUEST_LENGTH,
@@ -784,15 +855,18 @@ static NTSTATUS ncacn_push_request_sign(struct dcecli_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:
@@ -834,21 +908,18 @@ static NTSTATUS ncacn_push_request_sign(struct dcecli_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);
        }
@@ -866,7 +937,7 @@ static NTSTATUS ncacn_push_request_sign(struct dcecli_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, 
@@ -906,7 +977,7 @@ static NTSTATUS ncacn_push_request_sign(struct dcecli_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);
@@ -940,25 +1011,44 @@ static void init_ncacn_hdr(struct dcecli_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;
 }
 
 /*
@@ -989,11 +1079,10 @@ static void dcerpc_connection_dead(struct dcecli_connection *conn, NTSTATUS stat
 
        conn->dead = true;
 
-       conn->transport.recv_data = NULL;
+       TALLOC_FREE(conn->io_trigger);
+       conn->io_trigger_pending = false;
 
-       if (conn->transport.shutdown_pipe) {
-               conn->transport.shutdown_pipe(conn, status);
-       }
+       dcerpc_shutdown_pipe(conn, status);
 
        /* all pending requests get the error */
        while (conn->pending) {
@@ -1006,6 +1095,17 @@ static void dcerpc_connection_dead(struct dcecli_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);
@@ -1028,6 +1128,10 @@ static void dcerpc_recv_data(struct dcecli_connection *conn, DATA_BLOB *blob, NT
 {
        struct ncacn_packet pkt;
 
+       if (conn->dead) {
+               return;
+       }
+
        if (NT_STATUS_IS_OK(status) && blob->length == 0) {
                status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
        }
@@ -1045,68 +1149,10 @@ static void dcerpc_recv_data(struct dcecli_connection *conn, DATA_BLOB *blob, NT
        if (!NT_STATUS_IS_OK(status)) {
                data_blob_free(blob);
                dcerpc_connection_dead(conn, status);
-       }
-
-       dcerpc_request_recv_data(conn, blob, &pkt);
-}
-
-/*
-  Receive a bind reply from the transport
-*/
-static void dcerpc_bind_recv_handler(struct rpc_request *req, 
-                                    DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
-{
-       struct composite_context *c;
-       struct dcecli_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);
+       dcerpc_request_recv_data(conn, blob, &pkt);
 }
 
 /*
@@ -1130,27 +1176,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;
@@ -1158,20 +1221,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;
@@ -1179,47 +1244,202 @@ 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.tmp_auth_info.out);
+       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, struct rpc_request *);
+       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);
+       }
 
-       c->status = p->conn->transport.send_request(p->conn, &blob,
-                                                   true);
-       if (!composite_is_ok(c)) return c;
+       tevent_add_timer(ev, subreq,
+                        timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
+                        dcerpc_timeout_handler, subreq);
 
-       tevent_add_timer(c->event_ctx, req,
-                       timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
-                       dcerpc_timeout_handler, req);
+       return req;
+}
 
-       return c;
+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);
 }
 
-/*
-  recv side of async dcerpc bind request
-*/
-NTSTATUS dcerpc_bind_recv(struct composite_context *ctx)
+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 dcecli_security *sec = &conn->security_state;
+       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;
+       }
+
+       status = dcerpc_verify_ncacn_packet_header(pkt,
+                                       DCERPC_PKT_BIND_ACK,
+                                       pkt->u.bind_ack.auth_info.length,
+                                       DCERPC_PFC_FLAG_FIRST |
+                                       DCERPC_PFC_FLAG_LAST,
+                                       DCERPC_PFC_FLAG_CONC_MPX |
+                                       DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN);
+       if (!NT_STATUS_IS_OK(status)) {
+               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.num_results != 1) {
+               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.ctx_list[0].result != 0) {
+               status = dcerpc_map_ack_reason(&pkt->u.bind_ack.ctx_list[0]);
+               DEBUG(2,("dcerpc: bind_ack failed - reason %d - %s\n",
+                        pkt->u.bind_ack.ctx_list[0].reason.value,
+                        nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               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 (pkt->auth_length != 0 && sec->tmp_auth_info.in != NULL) {
+               uint32_t auth_length;
+
+               status = dcerpc_pull_auth_trailer(pkt, sec->tmp_auth_info.mem,
+                                                 &pkt->u.bind_ack.auth_info,
+                                                 sec->tmp_auth_info.in,
+                                                 &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);
 }
 
 /* 
@@ -1231,6 +1451,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);
 
@@ -1240,24 +1463,19 @@ 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,
-                                p->conn->security_state.auth_info);
+       status = ncacn_push_auth(&blob, mem_ctx, &pkt,
+                                p->conn->security_state.tmp_auth_info.out);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
 
        /* 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;
        }
@@ -1287,8 +1505,7 @@ static void dcerpc_request_recv_data(struct dcecli_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);
        }
 
@@ -1316,12 +1533,28 @@ static void dcerpc_request_recv_data(struct dcecli_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;
        }
 
        if (pkt->ptype == DCERPC_PKT_FAULT) {
+               status = dcerpc_fault_to_nt_status(pkt->u.fault.status);
                DEBUG(5,("rpc fault: %s\n", dcerpc_errstr(c, pkt->u.fault.status)));
+               if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTOCOL_ERROR)) {
+                       dcerpc_connection_dead(c, status);
+                       return;
+               }
+               if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) {
+                       dcerpc_connection_dead(c, status);
+                       return;
+               }
                req->fault_code = pkt->u.fault.status;
                req->status = NT_STATUS_NET_WRITE_FAULT;
                goto req_done;
@@ -1330,22 +1563,29 @@ static void dcerpc_request_recv_data(struct dcecli_connection *c,
        if (pkt->ptype != DCERPC_PKT_RESPONSE) {
                DEBUG(2,("Unexpected packet type %d in dcerpc response\n",
                         (int)pkt->ptype)); 
-               req->fault_code = DCERPC_FAULT_OTHER;
-               req->status = NT_STATUS_NET_WRITE_FAULT;
-               goto req_done;
+               dcerpc_connection_dead(c, NT_STATUS_RPC_PROTOCOL_ERROR);
+               return;
        }
 
        /* now check the status from the auth routines, and if it failed then fail
           this request accordingly */
        if (!NT_STATUS_IS_OK(status)) {
-               req->status = status;
-               goto req_done;
+               dcerpc_connection_dead(c, status);
+               return;
        }
 
        length = pkt->u.response.stub_and_verifier.length;
 
-       if (length > 0) {
-               req->payload.data = talloc_realloc(req, 
+       if (req->payload.length + length > c->max_total_response_size) {
+               DEBUG(2,("Unexpected total payload 0x%X > 0x%X dcerpc response\n",
+                        (unsigned)req->payload.length + length,
+                        (unsigned)c->max_total_response_size));
+               dcerpc_connection_dead(c, NT_STATUS_RPC_PROTOCOL_ERROR);
+               return;
+       }
+
+       if (length > 0) {
+               req->payload.data = talloc_realloc(req, 
                                                   req->payload.data, 
                                                   uint8_t,
                                                   req->payload.length + length);
@@ -1359,61 +1599,64 @@ static void dcerpc_request_recv_data(struct dcecli_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));
@@ -1421,24 +1664,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 *);
        talloc_set_destructor(req, dcerpc_req_dequeue);
 
-       dcerpc_ship_next_request(p->conn);
+       dcerpc_schedule_io_trigger(p->conn);
 
        if (p->request_timeout) {
-               tevent_add_timer(dcerpc_event_context(p), req,
+               tevent_add_timer(p->conn->event_ctx, req,
                                timeval_current_ofs(p->request_timeout, 0), 
                                dcerpc_timeout_handler, req);
        }
@@ -1446,6 +1690,120 @@ 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
 */
@@ -1461,6 +1819,7 @@ static void dcerpc_ship_next_request(struct dcecli_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) {
@@ -1474,6 +1833,16 @@ static void dcerpc_ship_next_request(struct dcecli_connection *c)
                need_async = true;
        }
 
+       if (c->security_state.auth_level >= DCERPC_AUTH_LEVEL_INTEGRITY) {
+               can_async = gensec_have_feature(c->security_state.generic_state,
+                                               GENSEC_FEATURE_ASYNC_REPLIES);
+       }
+
+       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;
@@ -1486,22 +1855,25 @@ static void dcerpc_ship_next_request(struct dcecli_connection *c)
           request header size */
        chunk_size = p->conn->srv_max_recv_frag;
        chunk_size -= DCERPC_REQUEST_LENGTH;
-       if (c->security_state.auth_info &&
-           c->security_state.generic_state) {
+       if (c->security_state.auth_level >= DCERPC_AUTH_LEVEL_INTEGRITY) {
+               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;
 
@@ -1528,6 +1900,7 @@ static void dcerpc_ship_next_request(struct dcecli_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;
@@ -1543,7 +1916,7 @@ static void dcerpc_ship_next_request(struct dcecli_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);
@@ -1551,7 +1924,7 @@ static void dcerpc_ship_next_request(struct dcecli_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);
@@ -1563,16 +1936,46 @@ static void dcerpc_ship_next_request(struct dcecli_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
@@ -1584,7 +1987,7 @@ static NTSTATUS dcerpc_request_recv(struct rpc_request *req,
        NTSTATUS status;
 
        while (req->state != RPC_REQUEST_DONE) {
-               struct tevent_context *ctx = dcerpc_event_context(req->p);
+               struct tevent_context *ctx = req->p->conn->event_ctx;
                if (tevent_loop_once(ctx) != 0) {
                        return NT_STATUS_CONNECTION_DISCONNECTED;
                }
@@ -1800,13 +2203,7 @@ static NTSTATUS dcerpc_ndr_validate_out(struct dcecli_connection *c,
 */
 _PUBLIC_ const char *dcerpc_server_name(struct dcerpc_pipe *p)
 {
-       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);
+       return p->conn ? p->conn->server_name : NULL;
 }
 
 
@@ -1829,80 +2226,44 @@ uint32_t dcerpc_auth_level(struct dcecli_connection *c)
        return auth_level;
 }
 
-/*
-  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)
-{
-       struct composite_context *c;
-       struct dcerpc_pipe *recv_pipe;
-
-       c = talloc_get_type(req->async.private_data, struct composite_context);
-       recv_pipe = talloc_get_type(c->private_data, struct dcerpc_pipe);
-
-       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;
-       }
-
-       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;
-       }
-
-       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;
-       }
-
-       /* 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 dcecli_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);
-               if (!NT_STATUS_IS_OK(status)) {
-                       composite_error(c, status);
-                       return;
-               }
-       }
+struct dcerpc_alter_context_state {
+       struct tevent_context *ev;
+       struct dcerpc_pipe *p;
+};
 
-       composite_done(c);
-}
+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);
 
-/* 
-   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 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 composite_context *c;
+       struct tevent_req *req;
+       struct dcerpc_alter_context_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_alter_context_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_ALTER;
@@ -1910,20 +2271,18 @@ struct composite_context *dcerpc_alter_context_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) {
-               pkt.pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
-       }
-
-       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.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(c, struct dcerpc_ctx_list, 1);
-       if (composite_nomem(pkt.u.alter.ctx_list, c)) return c;
+       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;
@@ -1931,43 +2290,166 @@ struct composite_context *dcerpc_alter_context_send(struct dcerpc_pipe *p,
        pkt.u.alter.auth_info = data_blob(NULL, 0);
 
        /* 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;
-
-       p->conn->transport.recv_data = dcerpc_recv_data;
+       status = ncacn_push_auth(&blob, state, &pkt,
+                                p->conn->security_state.tmp_auth_info.out);
+       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_alter_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_alter_context_fail_handler;
+       subreq->p = p;
+       subreq->recv_handler = dcerpc_alter_context_recv_handler;
+       DLIST_ADD_END(p->conn->pending, subreq, struct rpc_request *);
+       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);
+       }
 
-       c->status = p->conn->transport.send_request(p->conn, &blob, true);
-       if (!composite_is_ok(c)) return c;
+       tevent_add_timer(ev, subreq,
+                        timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
+                        dcerpc_timeout_handler, subreq);
 
-       tevent_add_timer(c->event_ctx, req,
-                       timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
-                       dcerpc_timeout_handler, req);
+       return req;
+}
 
-       return c;
+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);
 }
 
-NTSTATUS dcerpc_alter_context_recv(struct composite_context *ctx)
+static void dcerpc_alter_context_recv_handler(struct rpc_request *subreq,
+                                             DATA_BLOB *raw_packet,
+                                             struct ncacn_packet *pkt)
 {
-       NTSTATUS result = composite_wait(ctx);
-       talloc_free(ctx);
-       return result;
+       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;
+       struct dcecli_security *sec = &conn->security_state;
+       NTSTATUS status;
+
+       /*
+        * 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_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;
+       }
+
+       status = dcerpc_verify_ncacn_packet_header(pkt,
+                                       DCERPC_PKT_ALTER_RESP,
+                                       pkt->u.alter_resp.auth_info.length,
+                                       DCERPC_PFC_FLAG_FIRST |
+                                       DCERPC_PFC_FLAG_LAST,
+                                       DCERPC_PFC_FLAG_CONC_MPX |
+                                       DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN);
+       if (!NT_STATUS_IS_OK(status)) {
+               state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
+               tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
+               return;
+       }
+
+       if (pkt->u.alter_resp.num_results != 1) {
+               state->p->last_fault_code = DCERPC_NCA_S_PROTO_ERROR;
+               tevent_req_nterror(req, NT_STATUS_NET_WRITE_FAULT);
+               return;
+       }
+
+       if (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;
+       }
+
+       /* the alter_resp might contain a reply set of credentials */
+       if (pkt->auth_length != 0 && sec->tmp_auth_info.in != NULL) {
+               uint32_t auth_length;
+
+               status = dcerpc_pull_auth_trailer(pkt, sec->tmp_auth_info.mem,
+                                                 &pkt->u.alter_resp.auth_info,
+                                                 sec->tmp_auth_info.in,
+                                                 &auth_length, true);
+               if (tevent_req_nterror(req, status)) {
+                       return;
+               }
+       }
+
+       tevent_req_done(req);
+}
+
+NTSTATUS dcerpc_alter_context_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
 }
 
 /* 
@@ -1978,8 +2460,328 @@ _PUBLIC_ NTSTATUS dcerpc_alter_context(struct dcerpc_pipe *p,
                              const struct ndr_syntax_id *syntax,
                              const struct ndr_syntax_id *transfer_syntax)
 {
-       struct composite_context *creq;
-       creq = dcerpc_alter_context_send(p, mem_ctx, syntax, transfer_syntax);
-       return dcerpc_alter_context_recv(creq);
+       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;
+       }
+
+       return dcerpc_alter_context_recv(subreq);
+}
+
+static void dcerpc_transport_dead(struct dcecli_connection *c, NTSTATUS status)
+{
+       if (c->transport.stream == NULL) {
+               return;
+       }
+
+       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;
+       }
+
+       if (NT_STATUS_EQUAL(NT_STATUS_OK, status)) {
+               status = NT_STATUS_END_OF_FILE;
+       }
+
+       dcerpc_recv_data(c, NULL, status);
+}
+
+
+/*
+   shutdown SMB pipe connection
+*/
+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 dcerpc_shutdown_pipe_state *state;
+       struct tevent_req *subreq;
+
+       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;
+
+       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;
+}
+
+static void dcerpc_shutdown_pipe_done(struct tevent_req *subreq)
+{
+       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);
+}
+
+
+
+struct dcerpc_send_read_state {
+       struct dcecli_connection *p;
+};
+
+static int dcerpc_send_read_state_destructor(struct dcerpc_send_read_state *state)
+{
+       struct dcecli_connection *p = state->p;
+
+       p->transport.read_subreq = NULL;
+
+       return 0;
 }
 
+static void dcerpc_send_read_done(struct tevent_req *subreq);
+
+static NTSTATUS dcerpc_send_read(struct dcecli_connection *p)
+{
+       struct dcerpc_send_read_state *state;
+
+       if (p->transport.read_subreq != NULL) {
+               p->transport.pending_reads++;
+               return NT_STATUS_OK;
+       }
+
+       state = talloc_zero(p, struct dcerpc_send_read_state);
+       if (state == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       state->p = p;
+
+       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);
+
+       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;
+       }
+
+       /*
+        * 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)) {
+                       dcerpc_transport_dead(p, status);
+                       return;
+               }
+       }
+
+       dcerpc_recv_data(p, &blob, NT_STATUS_OK);
+}
+
+struct dcerpc_send_request_state {
+       struct dcecli_connection *p;
+       DATA_BLOB blob;
+       struct iovec iov;
+};
+
+static int dcerpc_send_request_state_destructor(struct dcerpc_send_request_state *state)
+{
+       struct dcecli_connection *p = state->p;
+
+       p->transport.read_subreq = NULL;
+
+       return 0;
+}
+
+static void dcerpc_send_request_wait_done(struct tevent_req *subreq);
+static void dcerpc_send_request_done(struct tevent_req *subreq);
+
+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->transport.stream == NULL) {
+               return NT_STATUS_CONNECTION_DISCONNECTED;
+       }
+
+       state = talloc_zero(p, struct dcerpc_send_request_state);
+       if (state == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       state->p = p;
+
+       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;
+
+       if (p->transport.read_subreq != NULL) {
+               use_trans = false;
+       }
+
+       if (!tstream_is_smbXcli_np(p->transport.stream)) {
+               use_trans = false;
+       }
+
+       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);
+
+               talloc_set_destructor(state, dcerpc_send_request_state_destructor);
+
+               trigger_read = false;
+       }
+
+       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);
+
+       if (trigger_read) {
+               dcerpc_send_read(p);
+       }
+
+       return NT_STATUS_OK;
+}
+
+static void dcerpc_send_request_wait_done(struct tevent_req *subreq)
+{
+       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);
+}
+
+static void dcerpc_send_request_done(struct tevent_req *subreq)
+{
+       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);
+}