r12574: Bring tables.pl back to life and move registration of interfaces
[samba.git] / source4 / librpc / rpc / dcerpc.c
index ee7250a4f9cefbe4802da8cf54feb0129be2a244..37f7d82ef5c5da30381ef10d8cd6504480a7bae7 100644 (file)
@@ -3,8 +3,8 @@
    raw dcerpc operations
 
    Copyright (C) Tim Potter 2003
-   Copyright (C) Andrew Tridgell 2003
-   Copyright (C) Jelmer Vernooij 2004
+   Copyright (C) Andrew Tridgell 2003-2005
+   Copyright (C) Jelmer Vernooij 2004-2005
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
 #include "includes.h"
 #include "dlinklist.h"
+#include "lib/events/events.h"
 #include "librpc/gen_ndr/ndr_epmapper.h"
+#include "librpc/gen_ndr/ndr_dcerpc.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "libcli/composite/composite.h"
+#include "auth/gensec/gensec.h"
 
-struct dcerpc_interface_list *dcerpc_pipes = NULL;
+static void dcerpc_ship_next_request(struct dcerpc_connection *c);
 
-NTSTATUS librpc_register_interface(const struct dcerpc_interface_table *interface)
+/* destroy a dcerpc connection */
+static int dcerpc_connection_destructor(void *ptr)
 {
-       struct dcerpc_interface_list *l = talloc_p(talloc_autofree_context(),
-                                                  struct dcerpc_interface_list);
-               
-       if (idl_iface_by_name (interface->name) != NULL) {
-               DEBUG(0, ("Attempt to register interface %s twice\n", interface->name));
-               return NT_STATUS_OBJECT_NAME_COLLISION;
+       struct dcerpc_connection *c = ptr;
+       if (c->transport.shutdown_pipe) {
+               c->transport.shutdown_pipe(c);
        }
-       l->table = interface;
+       return 0;
+}
 
-       DLIST_ADD(dcerpc_pipes, l);
-       
-       return NT_STATUS_OK;
+
+/* initialise a dcerpc connection. 
+   the event context is optional
+*/
+struct dcerpc_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx, 
+                                                struct event_context *ev)
+{
+       struct dcerpc_connection *c;
+
+       c = talloc_zero(mem_ctx, struct dcerpc_connection);
+       if (!c) {
+               return NULL;
+       }
+
+       if (ev == NULL) {
+               ev = event_context_init(c);
+               if (ev == NULL) {
+                       talloc_free(c);
+                       return NULL;
+               }
+       }
+
+       c->event_ctx = ev;
+       c->call_id = 1;
+       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;
+       c->pending = NULL;
+
+       talloc_set_destructor(c, dcerpc_connection_destructor);
+
+       return c;
 }
 
 /* initialise a dcerpc pipe. */
-struct dcerpc_pipe *dcerpc_pipe_init(void)
+struct dcerpc_pipe *dcerpc_pipe_init(TALLOC_CTX *mem_ctx, struct event_context *ev)
 {
        struct dcerpc_pipe *p;
 
-       p = talloc_p(NULL, struct dcerpc_pipe);
+       p = talloc(mem_ctx, struct dcerpc_pipe);
        if (!p) {
                return NULL;
        }
 
-       p->reference_count = 0;
-       p->call_id = 1;
-       p->security_state.auth_info = NULL;
-       p->security_state.session_key = dcerpc_generic_session_key;
-       p->security_state.generic_state = NULL;
-       p->binding_string = NULL;
-       p->flags = 0;
-       p->srv_max_xmit_frag = 0;
-       p->srv_max_recv_frag = 0;
+       p->conn = dcerpc_connection_init(p, ev);
+       if (p->conn == NULL) {
+               talloc_free(p);
+               return NULL;
+       }
+
        p->last_fault_code = 0;
-       p->pending = NULL;
+       p->context_id = 0;
+       p->request_timeout = DCERPC_REQUEST_TIMEOUT;
+
+       ZERO_STRUCT(p->syntax);
+       ZERO_STRUCT(p->transfer_syntax);
 
        return p;
 }
 
+
 /* 
    choose the next call id to use
 */
-static uint32_t next_call_id(struct dcerpc_pipe *p)
+static uint32_t next_call_id(struct dcerpc_connection *c)
 {
-       p->call_id++;
-       if (p->call_id == 0) {
-               p->call_id++;
-       }
-       return p->call_id;
-}
-
-/* close down a dcerpc over SMB pipe */
-void dcerpc_pipe_close(struct dcerpc_pipe *p)
-{
-       if (!p) return;
-       p->reference_count--;
-       if (p->reference_count <= 0) {
-               p->transport.shutdown_pipe(p);
-               talloc_free(p);
+       c->call_id++;
+       if (c->call_id == 0) {
+               c->call_id++;
        }
+       return c->call_id;
 }
 
 /* we need to be able to get/set the fragment length without doing a full
@@ -124,17 +152,18 @@ void dcerpc_set_auth_length(DATA_BLOB *blob, uint16_t v)
 /*
   setup for a ndr pull, also setting up any flags from the binding string
 */
-static struct ndr_pull *ndr_pull_init_flags(struct dcerpc_pipe *p, DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
+static struct ndr_pull *ndr_pull_init_flags(struct dcerpc_connection *c, 
+                                           DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
 {
        struct ndr_pull *ndr = ndr_pull_init_blob(blob, mem_ctx);
 
        if (ndr == NULL) return ndr;
 
-       if (p->flags & DCERPC_DEBUG_PAD_CHECK) {
+       if (c->flags & DCERPC_DEBUG_PAD_CHECK) {
                ndr->flags |= LIBNDR_FLAG_PAD_CHECK;
        }
 
-       if (p->flags & DCERPC_NDR_REF_ALLOC) {
+       if (c->flags & DCERPC_NDR_REF_ALLOC) {
                ndr->flags |= LIBNDR_FLAG_REF_ALLOC;
        }
 
@@ -142,15 +171,15 @@ static struct ndr_pull *ndr_pull_init_flags(struct dcerpc_pipe *p, DATA_BLOB *bl
 }
 
 /* 
-   parse a data blob into a dcerpc_packet structure. This handles both
+   parse a data blob into a ncacn_packet structure. This handles both
    input and output packets
 */
-static NTSTATUS dcerpc_pull(struct dcerpc_pipe *p, DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
-                           struct dcerpc_packet *pkt)
+static NTSTATUS ncacn_pull(struct dcerpc_connection *c, DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
+                           struct ncacn_packet *pkt)
 {
        struct ndr_pull *ndr;
 
-       ndr = ndr_pull_init_flags(p, blob, mem_ctx);
+       ndr = ndr_pull_init_flags(c, blob, mem_ctx);
        if (!ndr) {
                return NT_STATUS_NO_MEMORY;
        }
@@ -159,7 +188,7 @@ static NTSTATUS dcerpc_pull(struct dcerpc_pipe *p, DATA_BLOB *blob, TALLOC_CTX *
                ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
        }
 
-       return ndr_pull_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
+       return ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
 }
 
 /*
@@ -177,7 +206,7 @@ static NTSTATUS dcerpc_connect_verifier(TALLOC_CTX *mem_ctx, DATA_BLOB *blob)
 }
 
 /*
-  generate a CONNECT level verifier
+  check a CONNECT level verifier
 */
 static NTSTATUS dcerpc_check_connect_verifier(DATA_BLOB *blob)
 {
@@ -189,44 +218,19 @@ static NTSTATUS dcerpc_check_connect_verifier(DATA_BLOB *blob)
 }
 
 /* 
-   parse a possibly signed blob into a dcerpc request packet structure
+   parse the authentication information on a dcerpc response packet
 */
-static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p
-                                        DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
-                                        struct dcerpc_packet *pkt)
+static NTSTATUS ncacn_pull_request_auth(struct dcerpc_connection *c, TALLOC_CTX *mem_ctx
+                                       DATA_BLOB *raw_packet,
+                                       struct ncacn_packet *pkt)
 {
        struct ndr_pull *ndr;
        NTSTATUS status;
        struct dcerpc_auth auth;
        DATA_BLOB auth_blob;
 
-       /* non-signed packets are simpler */
-       if (!p->security_state.auth_info || 
-           !p->security_state.generic_state) {
-               return dcerpc_pull(p, blob, mem_ctx, pkt);
-       }
-
-       ndr = ndr_pull_init_flags(p, blob, mem_ctx);
-       if (!ndr) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       if (! (CVAL(blob->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
-               ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
-       }
-
-       /* pull the basic packet */
-       status = ndr_pull_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
-
-       if (pkt->ptype != DCERPC_PKT_RESPONSE) {
-               return status;
-       }
-
        if (pkt->auth_length == 0 &&
-           p->security_state.auth_info->auth_level == DCERPC_AUTH_LEVEL_CONNECT) {
+           c->security_state.auth_info->auth_level == DCERPC_AUTH_LEVEL_CONNECT) {
                return NT_STATUS_OK;
        }
 
@@ -243,12 +247,12 @@ static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p,
        pkt->u.response.stub_and_verifier.length -= auth_blob.length;
 
        /* pull the auth structure */
-       ndr = ndr_pull_init_flags(p, &auth_blob, mem_ctx);
+       ndr = ndr_pull_init_flags(c, &auth_blob, mem_ctx);
        if (!ndr) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       if (! (CVAL(blob->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
+       if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
                ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
        }
 
@@ -259,27 +263,27 @@ static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p,
        
        
        /* check signature or unseal the packet */
-       switch (p->security_state.auth_info->auth_level) {
+       switch (c->security_state.auth_info->auth_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
-               status = gensec_unseal_packet(p->security_state.generic_state, 
+               status = gensec_unseal_packet(c->security_state.generic_state, 
                                              mem_ctx, 
-                                             blob->data + DCERPC_REQUEST_LENGTH,
+                                             raw_packet->data + DCERPC_REQUEST_LENGTH,
                                              pkt->u.response.stub_and_verifier.length, 
-                                             blob->data,
-                                             blob->length - auth.credentials.length,
+                                             raw_packet->data,
+                                             raw_packet->length - auth.credentials.length,
                                              &auth.credentials);
                memcpy(pkt->u.response.stub_and_verifier.data,
-                      blob->data + DCERPC_REQUEST_LENGTH,
+                      raw_packet->data + DCERPC_REQUEST_LENGTH,
                       pkt->u.response.stub_and_verifier.length);
                break;
                
        case DCERPC_AUTH_LEVEL_INTEGRITY:
-               status = gensec_check_packet(p->security_state.generic_state, 
+               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, 
-                                            blob->data,
-                                            blob->length - auth.credentials.length,
+                                            raw_packet->data,
+                                            raw_packet->length - auth.credentials.length,
                                             &auth.credentials);
                break;
 
@@ -308,18 +312,19 @@ static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p,
 /* 
    push a dcerpc request packet into a blob, possibly signing it.
 */
-static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p
+static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c
                                         DATA_BLOB *blob, TALLOC_CTX *mem_ctx, 
-                                        struct dcerpc_packet *pkt)
+                                        struct ncacn_packet *pkt)
 {
        NTSTATUS status;
        struct ndr_push *ndr;
        DATA_BLOB creds2;
+       size_t payload_length;
 
        /* non-signed packets are simpler */
-       if (!p->security_state.auth_info || 
-           !p->security_state.generic_state) {
-               return dcerpc_push_auth(blob, mem_ctx, pkt, p->security_state.auth_info);
+       if (!c->security_state.auth_info || 
+           !c->security_state.generic_state) {
+               return ncacn_push_auth(blob, mem_ctx, pkt, c->security_state.auth_info);
        }
 
        ndr = ndr_push_init_ctx(mem_ctx);
@@ -327,7 +332,7 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p,
                return NT_STATUS_NO_MEMORY;
        }
 
-       if (p->flags & DCERPC_PUSH_BIGENDIAN) {
+       if (c->flags & DCERPC_PUSH_BIGENDIAN) {
                ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
        }
 
@@ -335,32 +340,36 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p,
                ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
        }
 
-       status = ndr_push_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
+       status = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
 
        /* pad to 16 byte multiple in the payload portion of the
           packet. This matches what w2k3 does */
-       p->security_state.auth_info->auth_pad_length = 
+       c->security_state.auth_info->auth_pad_length = 
                (16 - (pkt->u.request.stub_and_verifier.length & 15)) & 15;
-       ndr_push_zero(ndr, p->security_state.auth_info->auth_pad_length);
+       ndr_push_zero(ndr, c->security_state.auth_info->auth_pad_length);
+
+       payload_length = pkt->u.request.stub_and_verifier.length + 
+               c->security_state.auth_info->auth_pad_length;
 
        /* sign or seal the packet */
-       switch (p->security_state.auth_info->auth_level) {
+       switch (c->security_state.auth_info->auth_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
        case DCERPC_AUTH_LEVEL_INTEGRITY:
-               p->security_state.auth_info->credentials
-                       = data_blob_talloc(mem_ctx, NULL, gensec_sig_size(p->security_state.generic_state));
-               data_blob_clear(&p->security_state.auth_info->credentials);
+               c->security_state.auth_info->credentials
+                       = data_blob_talloc(mem_ctx, NULL, gensec_sig_size(c->security_state.generic_state, 
+                                                                         payload_length));
+               data_blob_clear(&c->security_state.auth_info->credentials);
                break;
 
        case DCERPC_AUTH_LEVEL_CONNECT:
-               status = dcerpc_connect_verifier(mem_ctx, &p->security_state.auth_info->credentials);
+               status = dcerpc_connect_verifier(mem_ctx, &c->security_state.auth_info->credentials);
                break;
                
        case DCERPC_AUTH_LEVEL_NONE:
-               p->security_state.auth_info->credentials = data_blob(NULL, 0);
+               c->security_state.auth_info->credentials = data_blob(NULL, 0);
                break;
                
        default:
@@ -373,7 +382,7 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p,
        }       
 
        /* add the auth verifier */
-       status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, p->security_state.auth_info);
+       status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, c->security_state.auth_info);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -385,18 +394,18 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p,
           in these earlier as we don't know the signature length (it
           could be variable length) */
        dcerpc_set_frag_length(blob, blob->length);
-       dcerpc_set_auth_length(blob, p->security_state.auth_info->credentials.length);
+       dcerpc_set_auth_length(blob, c->security_state.auth_info->credentials.length);
 
        /* sign or seal the packet */
-       switch (p->security_state.auth_info->auth_level) {
+       switch (c->security_state.auth_info->auth_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
-               status = gensec_seal_packet(p->security_state.generic_state, 
+               status = gensec_seal_packet(c->security_state.generic_state, 
                                            mem_ctx, 
                                            blob->data + DCERPC_REQUEST_LENGTH, 
-                                           pkt->u.request.stub_and_verifier.length+p->security_state.auth_info->auth_pad_length,
+                                           payload_length,
                                            blob->data,
                                            blob->length - 
-                                           p->security_state.auth_info->credentials.length,
+                                           c->security_state.auth_info->credentials.length,
                                            &creds2);
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
@@ -405,13 +414,13 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p,
                break;
 
        case DCERPC_AUTH_LEVEL_INTEGRITY:
-               status = gensec_sign_packet(p->security_state.generic_state, 
+               status = gensec_sign_packet(c->security_state.generic_state, 
                                            mem_ctx, 
                                            blob->data + DCERPC_REQUEST_LENGTH, 
-                                           pkt->u.request.stub_and_verifier.length+p->security_state.auth_info->auth_pad_length,
+                                           payload_length, 
                                            blob->data,
                                            blob->length - 
-                                           p->security_state.auth_info->credentials.length,
+                                           c->security_state.auth_info->credentials.length,
                                            &creds2);
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
@@ -423,7 +432,7 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p,
                break;
 
        case DCERPC_AUTH_LEVEL_NONE:
-               p->security_state.auth_info->credentials = data_blob(NULL, 0);
+               c->security_state.auth_info->credentials = data_blob(NULL, 0);
                break;
 
        default:
@@ -431,7 +440,7 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p,
                break;
        }
 
-       data_blob_free(&p->security_state.auth_info->credentials);
+       data_blob_free(&c->security_state.auth_info->credentials);
 
        return NT_STATUS_OK;
 }
@@ -440,11 +449,11 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p,
 /* 
    fill in the fixed values in a dcerpc header 
 */
-static void init_dcerpc_hdr(struct dcerpc_pipe *p, struct dcerpc_packet *pkt)
+static void init_ncacn_hdr(struct dcerpc_connection *c, struct ncacn_packet *pkt)
 {
        pkt->rpc_vers = 5;
        pkt->rpc_vers_minor = 0;
-       if (p->flags & DCERPC_PUSH_BIGENDIAN) {
+       if (c->flags & DCERPC_PUSH_BIGENDIAN) {
                pkt->drep[0] = 0;
        } else {
                pkt->drep[0] = DCERPC_DREP_LE;
@@ -455,247 +464,293 @@ static void init_dcerpc_hdr(struct dcerpc_pipe *p, struct dcerpc_packet *pkt)
 }
 
 /*
-  hold the state of pending full requests
+  map a bind nak reason to a NTSTATUS
 */
-struct full_request_state {
-       DATA_BLOB *reply_blob;
-       NTSTATUS status;
-};
+static NTSTATUS dcerpc_map_reason(uint16_t reason)
+{
+       switch (reason) {
+       case DCERPC_BIND_REASON_ASYNTAX:
+               return NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX;
+       }
+       return NT_STATUS_UNSUCCESSFUL;
+}
 
 /*
-  receive a reply to a full request
- */
-static void full_request_recv(struct dcerpc_pipe *p, DATA_BLOB *blob, 
-                             NTSTATUS status)
+  mark the dcerpc connection dead. All outstanding requests get an error
+*/
+static void dcerpc_connection_dead(struct dcerpc_connection *conn, NTSTATUS status)
 {
-       struct full_request_state *state = p->full_request_private;
+       /* all pending requests get the error */
+       while (conn->pending) {
+               struct rpc_request *req = conn->pending;
+               req->state = RPC_REQUEST_DONE;
+               req->status = status;
+               DLIST_REMOVE(conn->pending, req);
+               if (req->async.callback) {
+                       req->async.callback(req);
+               }
+       }       
 
-       if (!NT_STATUS_IS_OK(status)) {
-               state->status = status;
-               return;
+       if (conn->bind_private) {
+               /* a bind was in flight - fail it */
+               struct composite_context *c = talloc_get_type(conn->bind_private, struct composite_context);
+               composite_error(c, status);             
+       }
+
+       if (conn->alter_private) {
+               /* a alter context was in flight - fail it */
+               struct composite_context *c = talloc_get_type(conn->alter_private, struct composite_context);
+               composite_error(c, status);             
        }
-       state->reply_blob[0] = data_blob_talloc(state, blob->data, blob->length);
-       state->reply_blob = NULL;
 }
 
 /*
-  perform a single pdu synchronous request - used for the bind code
-  this cannot be mixed with normal async requests
+  forward declarations of the recv_data handlers for the 3 types of packets we need
+  to handle
 */
-static NTSTATUS full_request(struct dcerpc_pipe *p, 
-                            TALLOC_CTX *mem_ctx,
-                            DATA_BLOB *request_blob,
-                            DATA_BLOB *reply_blob)
+static void dcerpc_bind_recv_data(struct dcerpc_connection *conn, struct ncacn_packet *pkt);
+static void dcerpc_alter_recv_data(struct dcerpc_connection *conn, struct ncacn_packet *pkt);
+static void dcerpc_request_recv_data(struct dcerpc_connection *c, 
+                                    DATA_BLOB *raw_packet, struct ncacn_packet *pkt);
+
+/*
+  receive a dcerpc reply from the transport. Here we work out what
+  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)
 {
-       struct full_request_state *state = talloc_p(mem_ctx, struct full_request_state);
-       NTSTATUS status;
+       struct ncacn_packet pkt;
 
-       if (state == NULL) {
-               return NT_STATUS_NO_MEMORY;
+       if (NT_STATUS_IS_OK(status) && blob->length == 0) {
+               status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
        }
 
-       state->reply_blob = reply_blob;
-       state->status = NT_STATUS_OK;
-
-       p->transport.recv_data = full_request_recv;
-       p->full_request_private = state;
+       /* the transport may be telling us of a severe error, such as
+          a dropped socket */
+       if (!NT_STATUS_IS_OK(status)) {
+               data_blob_free(blob);
+               dcerpc_connection_dead(conn, status);
+               return;
+       }
 
-       status = p->transport.send_request(p, request_blob, True);
+       /* parse the basic packet to work out what type of response this is */
+       status = ncacn_pull(conn, blob, blob->data, &pkt);
        if (!NT_STATUS_IS_OK(status)) {
-               return status;
+               data_blob_free(blob);
+               dcerpc_connection_dead(conn, status);
        }
 
-       while (NT_STATUS_IS_OK(state->status) && state->reply_blob) {
-               struct event_context *ctx = p->transport.event_context(p);
-               if (event_loop_once(ctx) != 0) {
-                       return NT_STATUS_CONNECTION_DISCONNECTED;
+       switch (pkt.ptype) {
+       case DCERPC_PKT_BIND_NAK:
+       case DCERPC_PKT_BIND_ACK:
+               if (conn->bind_private) {
+                       talloc_steal(conn->bind_private, blob->data);
+                       dcerpc_bind_recv_data(conn, &pkt);
                }
+               break;
+
+       case DCERPC_PKT_ALTER_RESP:
+               if (conn->alter_private) {
+                       talloc_steal(conn->alter_private, blob->data);
+                       dcerpc_alter_recv_data(conn, &pkt);
+               }
+               break;
+
+       default:
+               /* assume its an ordinary request */
+               dcerpc_request_recv_data(conn, blob, &pkt);
+               break;
+       }
+}
+
+
+/*
+  Receive a bind reply from the transport
+*/
+static void dcerpc_bind_recv_data(struct dcerpc_connection *conn, struct ncacn_packet *pkt)
+{
+       struct composite_context *c;
+       struct dcerpc_pipe *pipe;
+
+       c = talloc_get_type(conn->bind_private, struct composite_context);
+       pipe = talloc_get_type(c->private_data, struct dcerpc_pipe);
+
+       /* mark the connection as not waiting for a bind reply */
+       conn->bind_private = NULL;
+
+       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;
        }
 
-       return state->status;
+       if ((pkt->ptype != DCERPC_PKT_BIND_ACK) ||
+           (pkt->u.bind_ack.num_results == 0) ||
+           (pkt->u.bind_ack.ctx_list[0].result != 0)) {
+               composite_error(c, NT_STATUS_UNSUCCESSFUL);
+               return;
+       }
+
+       conn->srv_max_xmit_frag = pkt->u.bind_ack.max_xmit_frag;
+       conn->srv_max_recv_frag = pkt->u.bind_ack.max_recv_frag;
+
+       /* the bind_ack might contain a reply set of credentials */
+       if (conn->security_state.auth_info &&
+           pkt->u.bind_ack.auth_info.length) {
+               c->status = ndr_pull_struct_blob(
+                       &pkt->u.bind_ack.auth_info, conn,
+                       conn->security_state.auth_info,
+                       (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
+               if (!composite_is_ok(c)) return;
+       }
+
+       composite_done(c);
 }
 
+/*
+  handle timeouts of dcerpc bind and alter context requests
+*/
+static void bind_timeout_handler(struct event_context *ev,
+                                struct timed_event *te, 
+                                struct timeval t, void *private)
+{
+       struct composite_context *ctx =
+               talloc_get_type(private, struct composite_context);
+       struct dcerpc_pipe *pipe = talloc_get_type(ctx->private_data, struct dcerpc_pipe);
 
-/* 
-   perform a bind using the given syntax 
+       SMB_ASSERT(pipe->conn->bind_private != NULL);
+       pipe->conn->bind_private = NULL;
+       composite_error(ctx, NT_STATUS_IO_TIMEOUT);
+}
 
-   the auth_info structure is updated with the reply authentication info
-   on success
+/*
+  send a async dcerpc bind request
 */
-NTSTATUS dcerpc_bind(struct dcerpc_pipe *p, 
-                    TALLOC_CTX *mem_ctx,
-                    const struct dcerpc_syntax_id *syntax,
-                    const struct dcerpc_syntax_id *transfer_syntax)
+struct composite_context *dcerpc_bind_send(struct dcerpc_pipe *p,
+                                          TALLOC_CTX *mem_ctx,
+                                          const struct dcerpc_syntax_id *syntax,
+                                          const struct dcerpc_syntax_id *transfer_syntax)
 {
-       struct dcerpc_packet pkt;
-       NTSTATUS status;
+       struct composite_context *c;
+       struct ncacn_packet pkt;
        DATA_BLOB blob;
 
+       c = talloc_zero(mem_ctx, struct composite_context);
+       if (c == NULL) return NULL;
+
+       c->state = COMPOSITE_STATE_IN_PROGRESS;
+       c->private_data = p;
+       c->event_ctx = p->conn->event_ctx;
+
        p->syntax = *syntax;
        p->transfer_syntax = *transfer_syntax;
 
-       init_dcerpc_hdr(p, &pkt);
+       init_ncacn_hdr(p->conn, &pkt);
 
        pkt.ptype = DCERPC_PKT_BIND;
        pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
-       pkt.call_id = p->call_id;
+       pkt.call_id = p->conn->call_id;
        pkt.auth_length = 0;
 
        pkt.u.bind.max_xmit_frag = 5840;
        pkt.u.bind.max_recv_frag = 5840;
        pkt.u.bind.assoc_group_id = 0;
        pkt.u.bind.num_contexts = 1;
-       pkt.u.bind.ctx_list = talloc_p(mem_ctx, struct dcerpc_ctx_list);
-       if (!pkt.u.bind.ctx_list) {
-               return NT_STATUS_NO_MEMORY;
+       pkt.u.bind.ctx_list =
+               talloc_array(mem_ctx, struct dcerpc_ctx_list, 1);
+       if (pkt.u.bind.ctx_list == NULL) {
+               c->status = NT_STATUS_NO_MEMORY;
+               goto failed;
        }
-       pkt.u.bind.ctx_list[0].context_id = 0;
+       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;
        pkt.u.bind.ctx_list[0].transfer_syntaxes = &p->transfer_syntax;
        pkt.u.bind.auth_info = data_blob(NULL, 0);
 
        /* construct the NDR form of the packet */
-       status = dcerpc_push_auth(&blob, mem_ctx, &pkt, p->security_state.auth_info);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       c->status = ncacn_push_auth(&blob, c, &pkt,
+                                   p->conn->security_state.auth_info);
+       if (!NT_STATUS_IS_OK(c->status)) {
+               goto failed;
        }
 
-       /* send it on its way */
-       status = full_request(p, mem_ctx, &blob, &blob);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
+       p->conn->transport.recv_data = dcerpc_recv_data;
+       p->conn->bind_private = c;
 
-       /* unmarshall the NDR */
-       status = dcerpc_pull(p, &blob, mem_ctx, &pkt);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       c->status = p->conn->transport.send_request(p->conn, &blob,
+                                                   True);
+       if (!NT_STATUS_IS_OK(c->status)) {
+               goto failed;
        }
 
-       if (pkt.ptype == DCERPC_PKT_BIND_NAK) {
-               DEBUG(2,("dcerpc: bind_nak reason %d\n", pkt.u.bind_nak.reject_reason));
-               return NT_STATUS_ACCESS_DENIED;
-       }
+       event_add_timed(c->event_ctx, c,
+                       timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
+                       bind_timeout_handler, c);
 
-       if ((pkt.ptype != DCERPC_PKT_BIND_ACK) ||
-           pkt.u.bind_ack.num_results == 0 ||
-           pkt.u.bind_ack.ctx_list[0].result != 0) {
-               return NT_STATUS_UNSUCCESSFUL;
-       }
+       return c;
 
-       if (pkt.ptype == DCERPC_PKT_BIND_ACK) {
-               p->srv_max_xmit_frag = pkt.u.bind_ack.max_xmit_frag;
-               p->srv_max_recv_frag = pkt.u.bind_ack.max_recv_frag;
-       }
-
-       /* the bind_ack might contain a reply set of credentials */
-       if (p->security_state.auth_info && pkt.u.bind_ack.auth_info.length) {
-               status = ndr_pull_struct_blob(&pkt.u.bind_ack.auth_info,
-                                             mem_ctx,
-                                             p->security_state.auth_info,
-                                             (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
-       }
+ failed:
+       composite_error(c, c->status);
+       return c;
+}
 
-       return status;  
+/*
+  recv side of async dcerpc bind request
+*/
+NTSTATUS dcerpc_bind_recv(struct composite_context *ctx)
+{
+       NTSTATUS result = composite_wait(ctx);
+       talloc_free(ctx);
+       return result;
 }
 
 /* 
-   perform a alter context using the given syntax 
+   perform a bind using the given syntax 
 
    the auth_info structure is updated with the reply authentication info
    on success
 */
-NTSTATUS dcerpc_alter(struct dcerpc_pipe *p, 
-                    TALLOC_CTX *mem_ctx)
+NTSTATUS dcerpc_bind(struct dcerpc_pipe *p, 
+                    TALLOC_CTX *mem_ctx,
+                    const struct dcerpc_syntax_id *syntax,
+                    const struct dcerpc_syntax_id *transfer_syntax)
 {
-       struct dcerpc_packet pkt;
-       NTSTATUS status;
-       DATA_BLOB blob;
-
-       init_dcerpc_hdr(p, &pkt);
-
-       pkt.ptype = DCERPC_PKT_ALTER;
-       pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
-       pkt.call_id = p->call_id;
-       pkt.auth_length = 0;
-
-       pkt.u.alter.max_xmit_frag = 0x2000;
-       pkt.u.alter.max_recv_frag = 0x2000;
-       pkt.u.alter.assoc_group_id = 0;
-       pkt.u.alter.num_contexts = 1;
-       pkt.u.alter.ctx_list = talloc_p(mem_ctx, struct dcerpc_ctx_list);
-       if (!pkt.u.alter.ctx_list) {
-               return NT_STATUS_NO_MEMORY;
-       }
-       pkt.u.alter.ctx_list[0].context_id = 0;
-       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 = dcerpc_push_auth(&blob, mem_ctx, &pkt, p->security_state.auth_info);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
-
-       /* send it on its way */
-       status = full_request(p, mem_ctx, &blob, &blob);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
-
-       /* unmarshall the NDR */
-       status = dcerpc_pull(p, &blob, mem_ctx, &pkt);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
-
-       if ((pkt.ptype != DCERPC_PKT_ALTER_ACK) ||
-           pkt.u.alter_ack.num_results == 0 ||
-           pkt.u.alter_ack.ctx_list[0].result != 0) {
-               status = NT_STATUS_UNSUCCESSFUL;
-       }
-
-       /* the bind_ack might contain a reply set of credentials */
-       if (p->security_state.auth_info && pkt.u.alter_ack.auth_info.length) {
-               status = ndr_pull_struct_blob(&pkt.u.alter_ack.auth_info,
-                                             mem_ctx,
-                                             p->security_state.auth_info,
-                                             (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
-       }
-
-       return status;  
+       struct composite_context *creq;
+       creq = dcerpc_bind_send(p, mem_ctx, syntax, transfer_syntax);
+       return dcerpc_bind_recv(creq);
 }
 
 /* 
    perform a continued bind (and auth3)
 */
-NTSTATUS dcerpc_auth3(struct dcerpc_pipe *p
+NTSTATUS dcerpc_auth3(struct dcerpc_connection *c
                      TALLOC_CTX *mem_ctx)
 {
-       struct dcerpc_packet pkt;
+       struct ncacn_packet pkt;
        NTSTATUS status;
        DATA_BLOB blob;
 
-       init_dcerpc_hdr(p, &pkt);
+       init_ncacn_hdr(c, &pkt);
 
        pkt.ptype = DCERPC_PKT_AUTH3;
        pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
-       pkt.call_id = next_call_id(p);
+       pkt.call_id = next_call_id(c);
        pkt.auth_length = 0;
        pkt.u.auth3._pad = 0;
        pkt.u.auth3.auth_info = data_blob(NULL, 0);
 
        /* construct the NDR form of the packet */
-       status = dcerpc_push_auth(&blob, mem_ctx, &pkt, p->security_state.auth_info);
+       status = ncacn_push_auth(&blob, mem_ctx, &pkt, c->security_state.auth_info);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
 
        /* send it on its way */
-       status = p->transport.send_request(p, &blob, False);
+       status = c->transport.send_request(c, &blob, False);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -704,109 +759,103 @@ NTSTATUS dcerpc_auth3(struct dcerpc_pipe *p,
 }
 
 
+/*
+  return the rpc syntax and transfer syntax given the pipe uuid and version
+*/
+NTSTATUS dcerpc_init_syntaxes(const struct dcerpc_interface_table *table,
+                             struct dcerpc_syntax_id *syntax,
+                             struct dcerpc_syntax_id *transfer_syntax)
+{
+       syntax->uuid = table->uuid;
+       syntax->if_version = table->if_version;
+
+       *transfer_syntax = ndr_transfer_syntax;
+
+       return NT_STATUS_OK;
+}
+
 /* perform a dcerpc bind, using the uuid as the key */
 NTSTATUS dcerpc_bind_byuuid(struct dcerpc_pipe *p, 
                            TALLOC_CTX *mem_ctx,
-                           const char *uuid, uint_t version)
+                           const struct dcerpc_interface_table *table)
 {
        struct dcerpc_syntax_id syntax;
        struct dcerpc_syntax_id transfer_syntax;
        NTSTATUS status;
 
-       status = GUID_from_string(uuid, &syntax.uuid);
+       status = dcerpc_init_syntaxes(table,
+                                     &syntax, &transfer_syntax);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(2,("Invalid uuid string in dcerpc_bind_byuuid\n"));
                return status;
        }
-       syntax.if_version = version;
-
-       status = GUID_from_string(NDR_GUID, &transfer_syntax.uuid);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
-       transfer_syntax.if_version = NDR_GUID_VERSION;
 
        return dcerpc_bind(p, mem_ctx, &syntax, &transfer_syntax);
 }
 
+
 /*
   process a fragment received from the transport layer during a
   request
+
+  This function frees the data 
 */
-static void dcerpc_request_recv_data(struct dcerpc_pipe *p, 
-                                    DATA_BLOB *data,
-                                    NTSTATUS status)
+static void dcerpc_request_recv_data(struct dcerpc_connection *c, 
+                                    DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
 {
-       struct dcerpc_packet pkt;
        struct rpc_request *req;
        uint_t length;
-       
-       if (!NT_STATUS_IS_OK(status)) {
-               /* all pending requests get the error */
-               while (p->pending) {
-                       req = p->pending;
-                       req->state = RPC_REQUEST_DONE;
-                       req->status = status;
-                       DLIST_REMOVE(p->pending, req);
-                       if (req->async.callback) {
-                               req->async.callback(req);
-                       }
-               }
-               return;
-       }
-
-       pkt.call_id = 0;
+       NTSTATUS status = NT_STATUS_OK;
 
-       status = dcerpc_pull_request_sign(p, data, (TALLOC_CTX *)data->data, &pkt);
+       /*
+         if this is an authenticated connection then parse and check
+         the auth info. We have to do this before finding the
+         matching packet, as the request structure might have been
+         removed due to a timeout, but if it has been we still need
+         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) {
+               status = ncacn_pull_request_auth(c, raw_packet->data, raw_packet, pkt);
+       }
 
-       /* find the matching request. Notice we match before we check
-          the status.  this is ok as a pending call_id can never be
-          zero */
-       for (req=p->pending;req;req=req->next) {
-               if (pkt.call_id == req->call_id) break;
+       /* find the matching request */
+       for (req=c->pending;req;req=req->next) {
+               if (pkt->call_id == req->call_id) break;
        }
 
        if (req == NULL) {
-               DEBUG(2,("dcerpc_request: unmatched call_id %u in response packet\n", pkt.call_id));
+               DEBUG(2,("dcerpc_request: unmatched call_id %u in response packet\n", pkt->call_id));
+               data_blob_free(raw_packet);
                return;
        }
 
-       if (!NT_STATUS_IS_OK(status)) {
-               req->status = status;
-               req->state = RPC_REQUEST_DONE;
-               DLIST_REMOVE(p->pending, req);
-               if (req->async.callback) {
-                       req->async.callback(req);
-               }
-               return;
-       }
+       talloc_steal(req, raw_packet->data);
 
-       if (pkt.ptype == DCERPC_PKT_FAULT) {
-               DEBUG(5,("rpc fault: %s\n", dcerpc_errstr(p, pkt.u.fault.status)));
-               req->fault_code = pkt.u.fault.status;
+       if (pkt->ptype == DCERPC_PKT_FAULT) {
+               DEBUG(5,("rpc fault: %s\n", dcerpc_errstr(c, pkt->u.fault.status)));
+               req->fault_code = pkt->u.fault.status;
                req->status = NT_STATUS_NET_WRITE_FAULT;
-               req->state = RPC_REQUEST_DONE;
-               DLIST_REMOVE(p->pending, req);
-               if (req->async.callback) {
-                       req->async.callback(req);
-               }
-               return;
+               goto req_done;
        }
 
-       if (pkt.ptype != DCERPC_PKT_RESPONSE) {
+       if (pkt->ptype != DCERPC_PKT_RESPONSE) {
                DEBUG(2,("Unexpected packet type %d in dcerpc response\n",
-                        (int)pkt.ptype)); 
+                        (int)pkt->ptype)); 
                req->fault_code = DCERPC_FAULT_OTHER;
                req->status = NT_STATUS_NET_WRITE_FAULT;
-               req->state = RPC_REQUEST_DONE;
-               DLIST_REMOVE(p->pending, req);
-               if (req->async.callback) {
-                       req->async.callback(req);
-               }
-               return;
+               goto req_done;
        }
 
-       length = pkt.u.response.stub_and_verifier.length;
+       /* 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;
+       }
+
+       length = pkt->u.response.stub_and_verifier.length;
 
        if (length > 0) {
                req->payload.data = talloc_realloc(req, 
@@ -815,33 +864,56 @@ static void dcerpc_request_recv_data(struct dcerpc_pipe *p,
                                                   req->payload.length + length);
                if (!req->payload.data) {
                        req->status = NT_STATUS_NO_MEMORY;
-                       req->state = RPC_REQUEST_DONE;
-                       DLIST_REMOVE(p->pending, req);
-                       if (req->async.callback) {
-                               req->async.callback(req);
-                       }
-                       return;
+                       goto req_done;
                }
                memcpy(req->payload.data+req->payload.length, 
-                      pkt.u.response.stub_and_verifier.data, length);
+                      pkt->u.response.stub_and_verifier.data, length);
                req->payload.length += length;
        }
 
-       if (!(pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
-               p->transport.send_read(p);
+       if (!(pkt->pfc_flags & DCERPC_PFC_FLAG_LAST)) {
+               c->transport.send_read(c);
                return;
        }
 
-       /* we've got the full payload */
-       req->state = RPC_REQUEST_DONE;
-       DLIST_REMOVE(p->pending, req);
-
-       if (!(pkt.drep[0] & DCERPC_DREP_LE)) {
+       if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
                req->flags |= DCERPC_PULL_BIGENDIAN;
        } else {
                req->flags &= ~DCERPC_PULL_BIGENDIAN;
        }
 
+
+req_done:
+       /* we've got the full payload */
+       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);
+       }
+
+       if (req->async.callback) {
+               req->async.callback(req);
+       }
+}
+
+/*
+  handle timeouts of individual dcerpc requests
+*/
+static void dcerpc_timeout_handler(struct event_context *ev, struct timed_event *te, 
+                                  struct timeval t, void *private)
+{
+       struct rpc_request *req = talloc_get_type(private, struct rpc_request);
+
+       if (req->state != RPC_REQUEST_PENDING) {
+               return;
+       }
+
+       req->status = NT_STATUS_IO_TIMEOUT;
+       req->state = RPC_REQUEST_DONE;
+       DLIST_REMOVE(req->p->conn->pending, req);
        if (req->async.callback) {
                req->async.callback(req);
        }
@@ -854,65 +926,120 @@ static void dcerpc_request_recv_data(struct dcerpc_pipe *p,
 static int dcerpc_req_destructor(void *ptr)
 {
        struct rpc_request *req = ptr;
-       DLIST_REMOVE(req->p->pending, req);
+       DLIST_REMOVE(req->p->conn->pending, req);
        return 0;
 }
 
 /*
-  perform the send size of a async dcerpc request
+  perform the send side of a async dcerpc request
 */
-struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p, 
-                                       const struct GUID *object,
-                                       uint16_t opnum,
-                                       TALLOC_CTX *mem_ctx,
-                                       DATA_BLOB *stub_data)
+static struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p, 
+                                              const struct GUID *object,
+                                              uint16_t opnum,
+                                              BOOL async,
+                                              DATA_BLOB *stub_data)
 {
        struct rpc_request *req;
-       struct dcerpc_packet pkt;
-       DATA_BLOB blob;
-       uint32_t remaining, chunk_size;
-       BOOL first_packet = True;
 
-       p->transport.recv_data = dcerpc_request_recv_data;
+       p->conn->transport.recv_data = dcerpc_recv_data;
 
-       req = talloc_p(mem_ctx, struct rpc_request);
+       req = talloc(p, struct rpc_request);
        if (req == NULL) {
                return NULL;
        }
 
        req->p = p;
-       req->call_id = next_call_id(p);
+       req->call_id = next_call_id(p->conn);
        req->status = NT_STATUS_OK;
        req->state = RPC_REQUEST_PENDING;
        req->payload = data_blob(NULL, 0);
        req->flags = 0;
        req->fault_code = 0;
+       req->async_call = async;
        req->async.callback = NULL;
 
-       init_dcerpc_hdr(p, &pkt);
+       if (object != NULL) {
+               req->object = talloc_memdup(req, object, sizeof(*object));
+               if (req->object == NULL) {
+                       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.data == NULL) {
+               return NULL;
+       }
+
+       DLIST_ADD_END(p->conn->request_queue, req, struct rpc_request *);
+
+       dcerpc_ship_next_request(p->conn);
+
+       if (p->request_timeout) {
+               event_add_timed(dcerpc_event_context(p), req, 
+                               timeval_current_ofs(p->request_timeout, 0), 
+                               dcerpc_timeout_handler, req);
+       }
+
+       talloc_set_destructor(req, dcerpc_req_destructor);
+       return req;
+}
+
+/*
+  Send a request using the transport
+*/
+
+static void dcerpc_ship_next_request(struct dcerpc_connection *c)
+{
+       struct rpc_request *req;
+       struct dcerpc_pipe *p;
+       DATA_BLOB *stub_data;
+       struct ncacn_packet pkt;
+       DATA_BLOB blob;
+       uint32_t remaining, chunk_size;
+       BOOL first_packet = True;
+
+       req = c->request_queue;
+       if (req == NULL) {
+               return;
+       }
+
+       p = req->p;
+       stub_data = &req->request_data;
+
+       if (!req->async_call && (c->pending != NULL)) {
+               return;
+       }
+
+       DLIST_REMOVE(c->request_queue, req);
+       DLIST_ADD(c->pending, req);
+
+       init_ncacn_hdr(p->conn, &pkt);
 
        remaining = stub_data->length;
 
        /* we can write a full max_recv_frag size, minus the dcerpc
           request header size */
-       chunk_size = p->srv_max_recv_frag - (DCERPC_MAX_SIGN_SIZE+DCERPC_REQUEST_LENGTH);
+       chunk_size = p->conn->srv_max_recv_frag - (DCERPC_MAX_SIGN_SIZE+DCERPC_REQUEST_LENGTH);
 
        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 = 0;
-       pkt.u.request.opnum = opnum;
+       pkt.u.request.context_id = p->context_id;
+       pkt.u.request.opnum = req->opnum;
 
-       if (object) {
-               pkt.u.request.object.object = *object;
+       if (req->object) {
+               pkt.u.request.object.object = *req->object;
                pkt.pfc_flags |= DCERPC_PFC_FLAG_ORPC;
-               chunk_size -= ndr_size_GUID(object,0);
+               chunk_size -= ndr_size_GUID(req->object,0);
        }
 
-       DLIST_ADD(p->pending, req);
-
        /* we send a series of pdus without waiting for a reply */
        while (remaining > 0 || first_packet) {
                uint32_t chunk = MIN(chunk_size, remaining);
@@ -933,26 +1060,22 @@ struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
                        (stub_data->length - remaining);
                pkt.u.request.stub_and_verifier.length = chunk;
 
-               req->status = dcerpc_push_request_sign(p, &blob, mem_ctx, &pkt);
+               req->status = ncacn_push_request_sign(p->conn, &blob, req, &pkt);
                if (!NT_STATUS_IS_OK(req->status)) {
                        req->state = RPC_REQUEST_DONE;
-                       DLIST_REMOVE(p->pending, req);
-                       return req;
+                       DLIST_REMOVE(p->conn->pending, req);
+                       return;
                }
                
-               req->status = p->transport.send_request(p, &blob, last_frag);
+               req->status = p->conn->transport.send_request(p->conn, &blob, last_frag);
                if (!NT_STATUS_IS_OK(req->status)) {
                        req->state = RPC_REQUEST_DONE;
-                       DLIST_REMOVE(p->pending, req);
-                       return req;
+                       DLIST_REMOVE(p->conn->pending, req);
+                       return;
                }               
 
                remaining -= chunk;
        }
-
-       talloc_set_destructor(req, dcerpc_req_destructor);
-
-       return req;
 }
 
 /*
@@ -961,7 +1084,7 @@ struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
 */
 struct event_context *dcerpc_event_context(struct dcerpc_pipe *p)
 {
-       return p->transport.event_context(p);
+       return p->conn->event_ctx;
 }
 
 
@@ -999,13 +1122,14 @@ NTSTATUS dcerpc_request_recv(struct rpc_request *req,
 NTSTATUS dcerpc_request(struct dcerpc_pipe *p, 
                        struct GUID *object,
                        uint16_t opnum,
+                       BOOL async,
                        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, mem_ctx, stub_data_in);
+       req = dcerpc_request_send(p, object, opnum, async, stub_data_in);
        if (req == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
@@ -1020,12 +1144,12 @@ NTSTATUS dcerpc_request(struct dcerpc_pipe *p,
   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_pipe *p
+static NTSTATUS dcerpc_ndr_validate_in(struct dcerpc_connection *c
                                       TALLOC_CTX *mem_ctx,
                                       DATA_BLOB blob,
                                       size_t struct_size,
-                                      NTSTATUS (*ndr_push)(struct ndr_push *, int, void *),
-                                      NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *))
+                                      ndr_push_flags_fn_t ndr_push,
+                                      ndr_pull_flags_fn_t ndr_pull)
 {
        void *st;
        struct ndr_pull *pull;
@@ -1038,10 +1162,11 @@ static NTSTATUS dcerpc_ndr_validate_in(struct dcerpc_pipe *p,
                return NT_STATUS_NO_MEMORY;
        }
 
-       pull = ndr_pull_init_flags(p, &blob, mem_ctx);
+       pull = ndr_pull_init_flags(c, &blob, mem_ctx);
        if (!pull) {
                return NT_STATUS_NO_MEMORY;
        }
+       pull->flags |= LIBNDR_FLAG_REF_ALLOC;
 
        status = ndr_pull(pull, NDR_IN, st);
        if (!NT_STATUS_IS_OK(status)) {
@@ -1084,12 +1209,12 @@ static NTSTATUS dcerpc_ndr_validate_in(struct dcerpc_pipe *p,
   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_pipe *p,
+static NTSTATUS dcerpc_ndr_validate_out(struct dcerpc_connection *c,
                                        TALLOC_CTX *mem_ctx,
                                        void *struct_ptr,
                                        size_t struct_size,
-                                       NTSTATUS (*ndr_push)(struct ndr_push *, int, void *),
-                                       NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *))
+                                       ndr_push_flags_fn_t ndr_push,
+                                       ndr_pull_flags_fn_t ndr_pull)
 {
        void *st;
        struct ndr_pull *pull;
@@ -1117,7 +1242,7 @@ static NTSTATUS dcerpc_ndr_validate_out(struct dcerpc_pipe *p,
 
        blob = ndr_push_blob(push);
 
-       pull = ndr_pull_init_flags(p, &blob, mem_ctx);
+       pull = ndr_pull_init_flags(c, &blob, mem_ctx);
        if (!pull) {
                return NT_STATUS_NO_MEMORY;
        }
@@ -1177,12 +1302,12 @@ struct rpc_request *dcerpc_ndr_request_send(struct dcerpc_pipe *p,
        call = &table->calls[opnum];
 
        /* setup for a ndr_push_* call */
-       push = ndr_push_init();
+       push = ndr_push_init_ctx(mem_ctx);
        if (!push) {
                return NULL;
        }
 
-       if (p->flags & DCERPC_PUSH_BIGENDIAN) {
+       if (p->conn->flags & DCERPC_PUSH_BIGENDIAN) {
                push->flags |= LIBNDR_FLAG_BIGENDIAN;
        }
 
@@ -1191,20 +1316,20 @@ struct rpc_request *dcerpc_ndr_request_send(struct dcerpc_pipe *p,
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(2,("Unable to ndr_push structure in dcerpc_ndr_request_send - %s\n",
                         nt_errstr(status)));
-               ndr_push_free(push);
+               talloc_free(push);
                return NULL;
        }
 
        /* retrieve the blob */
        request = ndr_push_blob(push);
 
-       if (p->flags & DCERPC_DEBUG_VALIDATE_IN) {
-               status = dcerpc_ndr_validate_in(p, mem_ctx, request, call->struct_size, 
+       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)));
-                       ndr_push_free(push);
+                       talloc_free(push);
                        return NULL;
                }
        }
@@ -1213,7 +1338,8 @@ struct rpc_request *dcerpc_ndr_request_send(struct dcerpc_pipe *p,
        dump_data(10, request.data, request.length);
 
        /* make the actual dcerpc request */
-       req = dcerpc_request_send(p, object, opnum, mem_ctx, &request);
+       req = dcerpc_request_send(p, object, opnum, table->calls[opnum].async,
+                                 &request);
 
        if (req != NULL) {
                req->ndr.table = table;
@@ -1222,8 +1348,8 @@ struct rpc_request *dcerpc_ndr_request_send(struct dcerpc_pipe *p,
                req->ndr.mem_ctx = mem_ctx;
        }
 
-       ndr_push_free(push);
-       
+       talloc_free(push);
+
        return req;
 }
 
@@ -1253,14 +1379,19 @@ NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
        }
 
        flags = req->flags;
-       talloc_free(req);
 
        /* prepare for ndr_pull_* */
-       pull = ndr_pull_init_flags(p, &response, mem_ctx);
+       pull = ndr_pull_init_flags(p->conn, &response, mem_ctx);
        if (!pull) {
+               talloc_free(req);
                return NT_STATUS_NO_MEMORY;
        }
 
+       if (pull->data) {
+               pull->data = talloc_steal(pull, pull->data);
+       }
+       talloc_free(req);
+
        if (flags & DCERPC_PULL_BIGENDIAN) {
                pull->flags |= LIBNDR_FLAG_BIGENDIAN;
        }
@@ -1276,8 +1407,8 @@ NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
                return status;
        }
 
-       if (p->flags & DCERPC_DEBUG_VALIDATE_OUT) {
-               status = dcerpc_ndr_validate_out(p, mem_ctx, r, call->struct_size, 
+       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);
                if (!NT_STATUS_IS_OK(status)) {
                        dcerpc_log_packet(table, opnum, NDR_OUT, 
@@ -1289,13 +1420,15 @@ NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
        if (pull->offset != pull->data_size) {
                DEBUG(0,("Warning! ignoring %d unread bytes in rpc packet!\n", 
                         pull->data_size - pull->offset));
-               /* we used return NT_STATUS_INFO_LENGTH_MISMATCH here,
+               /* 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 */
        }
 
+       /* TODO: make pull context independent from the output mem_ctx and free the pull context */
+
        return NT_STATUS_OK;
 }
 
@@ -1329,28 +1462,162 @@ NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
 */
 const char *dcerpc_server_name(struct dcerpc_pipe *p)
 {
-       if (!p->transport.peer_name) {
+       if (!p->conn->transport.peer_name) {
                return "";
        }
-       return p->transport.peer_name(p);
+       return p->conn->transport.peer_name(p->conn);
 }
 
+
 /*
-  a useful function to get the auth_level 
+  get the dcerpc auth_level for a open connection
 */
-
-uint32 dcerpc_auth_level(struct dcerpc_pipe *p) 
+uint32_t dcerpc_auth_level(struct dcerpc_connection *c) 
 {
        uint8_t auth_level;
 
-       if (p->flags & DCERPC_SEAL) {
+       if (c->flags & DCERPC_SEAL) {
                auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
-       } else if (p->flags & DCERPC_SIGN) {
+       } else if (c->flags & DCERPC_SIGN) {
                auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
-       } else if (p->flags & DCERPC_CONNECT) {
+       } else if (c->flags & DCERPC_CONNECT) {
                auth_level = DCERPC_AUTH_LEVEL_CONNECT;
        } else {
                auth_level = DCERPC_AUTH_LEVEL_NONE;
        }
        return auth_level;
 }
+
+/*
+  Receive an alter reply from the transport
+*/
+static void dcerpc_alter_recv_data(struct dcerpc_connection *conn, struct ncacn_packet *pkt)
+{
+       struct composite_context *c;
+       struct dcerpc_pipe *pipe;
+
+       c = talloc_get_type(conn->alter_private, struct composite_context);
+       pipe = talloc_get_type(c->private_data, struct dcerpc_pipe);
+
+       /* mark the connection as not waiting for a alter context reply */
+       conn->alter_private = NULL;
+
+       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_ALTER_RESP ||
+           pkt->u.alter_resp.num_results == 0 ||
+           pkt->u.alter_resp.ctx_list[0].result != 0) {
+               composite_error(c, NT_STATUS_UNSUCCESSFUL);
+               return;
+       }
+
+       /* the alter_resp might contain a reply set of credentials */
+       if (pipe->conn->security_state.auth_info &&
+           pkt->u.alter_resp.auth_info.length) {
+               c->status = ndr_pull_struct_blob(
+                       &pkt->u.alter_resp.auth_info, pipe,
+                       pipe->conn->security_state.auth_info,
+                       (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
+               if (!composite_is_ok(c)) return;
+       }
+
+       composite_done(c);
+}
+
+/* 
+   send a dcerpc alter_context request
+*/
+struct composite_context *dcerpc_alter_context_send(struct dcerpc_pipe *p, 
+                                                   TALLOC_CTX *mem_ctx,
+                                                   const struct dcerpc_syntax_id *syntax,
+                                                   const struct dcerpc_syntax_id *transfer_syntax)
+{
+       struct composite_context *c;
+       struct ncacn_packet pkt;
+       DATA_BLOB blob;
+
+       c = talloc_zero(mem_ctx, struct composite_context);
+       if (c == NULL) return NULL;
+
+       c->state = COMPOSITE_STATE_IN_PROGRESS;
+       c->private_data = p;
+       c->event_ctx = p->conn->event_ctx;
+
+       p->syntax = *syntax;
+       p->transfer_syntax = *transfer_syntax;
+
+       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;
+
+       pkt.u.alter.max_xmit_frag = 5840;
+       pkt.u.alter.max_recv_frag = 5840;
+       pkt.u.alter.assoc_group_id = 0;
+       pkt.u.alter.num_contexts = 1;
+       pkt.u.alter.ctx_list = talloc_array(mem_ctx,
+                                                  struct dcerpc_ctx_list, 1);
+       if (pkt.u.alter.ctx_list == NULL) {
+               c->status = NT_STATUS_NO_MEMORY;
+               goto failed;
+       }
+       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 */
+       c->status = ncacn_push_auth(&blob, mem_ctx, &pkt,
+                                   p->conn->security_state.auth_info);
+       if (!NT_STATUS_IS_OK(c->status)) {
+               goto failed;
+       }
+
+       p->conn->transport.recv_data = dcerpc_recv_data;
+       p->conn->alter_private = c;
+
+       c->status = p->conn->transport.send_request(p->conn, &blob, True);
+       if (!NT_STATUS_IS_OK(c->status)) {
+               goto failed;
+       }
+
+       event_add_timed(c->event_ctx, c,
+                       timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
+                       bind_timeout_handler, c);
+
+       return c;
+
+ failed:
+       composite_error(c, c->status);
+       return c;
+}
+
+NTSTATUS dcerpc_alter_context_recv(struct composite_context *ctx)
+{
+       NTSTATUS result = composite_wait(ctx);
+       talloc_free(ctx);
+       return result;
+}
+
+/* 
+   send a dcerpc alter_context request
+*/
+NTSTATUS dcerpc_alter_context(struct dcerpc_pipe *p, 
+                             TALLOC_CTX *mem_ctx,
+                             const struct dcerpc_syntax_id *syntax,
+                             const struct dcerpc_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);
+}