r24780: More work allowing libutil to be used by external users.
[kai/samba.git] / source4 / librpc / rpc / dcerpc.c
index cd33d3d14b1c16a4901b44060f19522fde69925a..984cc20fbc7dd80f4c4be34540854732959856d2 100644 (file)
@@ -8,7 +8,7 @@
    
    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
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
-#include "dlinklist.h"
+#include "lib/util/dlinklist.h"
 #include "lib/events/events.h"
 #include "librpc/rpc/dcerpc.h"
 #include "librpc/gen_ndr/ndr_misc.h"
@@ -37,15 +36,17 @@ NTSTATUS dcerpc_init(void)
        return NT_STATUS_OK;
 }
 
+static void dcerpc_connection_dead(struct dcerpc_connection *conn, NTSTATUS status);
 static void dcerpc_ship_next_request(struct dcerpc_connection *c);
 
 /* destroy a dcerpc connection */
-static int dcerpc_connection_destructor(void *ptr)
+static int dcerpc_connection_destructor(struct dcerpc_connection *conn)
 {
-       struct dcerpc_connection *c = ptr;
-       if (c->transport.shutdown_pipe) {
-               c->transport.shutdown_pipe(c);
+       if (conn->dead) {
+               conn->free_skipped = True;
+               return -1;
        }
+       dcerpc_connection_dead(conn, NT_STATUS_LOCAL_DISCONNECT);
        return 0;
 }
 
@@ -53,7 +54,7 @@ static int dcerpc_connection_destructor(void *ptr)
 /* initialise a dcerpc connection. 
    the event context is optional
 */
-struct dcerpc_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx, 
+static struct dcerpc_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx, 
                                                 struct event_context *ev)
 {
        struct dcerpc_connection *c;
@@ -111,6 +112,7 @@ struct dcerpc_pipe *dcerpc_pipe_init(TALLOC_CTX *mem_ctx, struct event_context *
        p->last_fault_code = 0;
        p->context_id = 0;
        p->request_timeout = DCERPC_REQUEST_TIMEOUT;
+       p->binding = NULL;
 
        ZERO_STRUCT(p->syntax);
        ZERO_STRUCT(p->transfer_syntax);
@@ -273,7 +275,6 @@ static NTSTATUS ncacn_pull_request_auth(struct dcerpc_connection *c, TALLOC_CTX
                return status;
        }
        
-       
        /* check signature or unseal the packet */
        switch (c->security_state.auth_info->auth_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
@@ -348,7 +349,7 @@ static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c,
                ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
        }
 
-       if (pkt->pfc_flags & DCERPC_PFC_FLAG_ORPC) {
+       if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
                ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
        }
 
@@ -370,6 +371,9 @@ static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c,
        switch (c->security_state.auth_info->auth_level) {
        case DCERPC_AUTH_LEVEL_PRIVACY:
        case DCERPC_AUTH_LEVEL_INTEGRITY:
+               /* We hope this length is accruate.  If must be if the
+                * GENSEC mech does AEAD signing of the packet
+                * headers */
                c->security_state.auth_info->credentials
                        = data_blob_talloc(mem_ctx, NULL, gensec_sig_size(c->security_state.generic_state, 
                                                                          payload_length));
@@ -406,6 +410,8 @@ static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c,
           in these earlier as we don't know the signature length (it
           could be variable length) */
        dcerpc_set_frag_length(blob, blob->length);
+       /* We hope this value is accruate.  If must be if the GENSEC
+        * mech does AEAD signing of the packet headers */
        dcerpc_set_auth_length(blob, c->security_state.auth_info->credentials.length);
 
        /* sign or seal the packet */
@@ -422,7 +428,19 @@ static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c,
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
                }
-               memcpy(blob->data + blob->length - creds2.length, creds2.data, creds2.length);
+               blob->length -= c->security_state.auth_info->credentials.length;
+               if (!data_blob_append(mem_ctx, blob,
+                                         creds2.data, creds2.length)) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               dcerpc_set_auth_length(blob, creds2.length);
+               if (c->security_state.auth_info->credentials.length == 0) {
+                       /* this is needed for krb5 only, to correct the total packet
+                          length */
+                       dcerpc_set_frag_length(blob, 
+                                              dcerpc_get_frag_length(blob)
+                                              +creds2.length);
+               }
                break;
 
        case DCERPC_AUTH_LEVEL_INTEGRITY:
@@ -437,7 +455,19 @@ static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c,
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
                }
-               memcpy(blob->data + blob->length - creds2.length, creds2.data, creds2.length);
+               blob->length -= c->security_state.auth_info->credentials.length;
+               if (!data_blob_append(mem_ctx, blob,
+                                         creds2.data, creds2.length)) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               dcerpc_set_auth_length(blob, creds2.length);
+               if (c->security_state.auth_info->credentials.length == 0) {
+                       /* this is needed for krb5 only, to correct the total packet
+                          length */
+                       dcerpc_set_frag_length(blob, 
+                                              dcerpc_get_frag_length(blob)
+                                              +creds2.length);
+               }
                break;
 
        case DCERPC_AUTH_LEVEL_CONNECT:
@@ -489,41 +519,69 @@ static NTSTATUS dcerpc_map_reason(uint16_t reason)
        return NT_STATUS_UNSUCCESSFUL;
 }
 
+/*
+  a bind or alter context has failed
+*/
+static void dcerpc_composite_fail(struct rpc_request *req)
+{
+       struct composite_context *c = talloc_get_type(req->async.private_data, 
+                                                     struct composite_context);
+       composite_error(c, req->status);
+}
+
+/*
+  remove requests from the pending or queued queues
+ */
+static int dcerpc_req_dequeue(struct rpc_request *req)
+{
+       switch (req->state) {
+       case RPC_REQUEST_QUEUED:
+               DLIST_REMOVE(req->p->conn->request_queue, req);
+               break;
+       case RPC_REQUEST_PENDING:
+               DLIST_REMOVE(req->p->conn->pending, req);
+               break;
+       case RPC_REQUEST_DONE:
+               break;
+       }
+       return 0;
+}
+
+
 /*
   mark the dcerpc connection dead. All outstanding requests get an error
 */
 static void dcerpc_connection_dead(struct dcerpc_connection *conn, NTSTATUS status)
 {
+       if (conn->dead) return;
+
+       conn->dead = true;
+
+       if (conn->transport.shutdown_pipe) {
+               conn->transport.shutdown_pipe(conn, status);
+       }
+
        /* all pending requests get the error */
        while (conn->pending) {
                struct rpc_request *req = conn->pending;
+               dcerpc_req_dequeue(req);
                req->state = RPC_REQUEST_DONE;
                req->status = status;
-               DLIST_REMOVE(conn->pending, req);
                if (req->async.callback) {
                        req->async.callback(req);
                }
        }       
 
-       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);             
+       talloc_set_destructor(conn, NULL);
+       if (conn->free_skipped) {
+               talloc_free(conn);
        }
 }
 
 /*
-  forward declarations of the recv_data handlers for the 3 types of packets we need
-  to handle
+  forward declarations of the recv_data handlers for the types of
+  packets we need to handle
 */
-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);
 
@@ -555,41 +613,20 @@ static void dcerpc_recv_data(struct dcerpc_connection *conn, DATA_BLOB *blob, NT
                dcerpc_connection_dead(conn, status);
        }
 
-       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;
-       }
+       dcerpc_request_recv_data(conn, blob, &pkt);
 }
 
 
 /*
   Receive a bind reply from the transport
 */
-static void dcerpc_bind_recv_data(struct dcerpc_connection *conn, struct ncacn_packet *pkt)
+static void dcerpc_bind_recv_handler(struct rpc_request *req, 
+                                    DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
 {
        struct composite_context *c;
+       struct dcerpc_connection *conn;
 
-       c = talloc_get_type(conn->bind_private, struct composite_context);
-
-       /* mark the connection as not waiting for a bind reply */
-       conn->bind_private = NULL;
+       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",
@@ -602,10 +639,12 @@ static void dcerpc_bind_recv_data(struct dcerpc_connection *conn, struct ncacn_p
        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);
+               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;
 
@@ -619,23 +658,30 @@ static void dcerpc_bind_recv_data(struct dcerpc_connection *conn, struct ncacn_p
                if (!composite_is_ok(c)) return;
        }
 
+       req->p->assoc_group_id = pkt->u.bind_ack.assoc_group_id;
+
        composite_done(c);
 }
 
 /*
-  handle timeouts of dcerpc bind and alter context requests
+  handle timeouts of individual dcerpc requests
 */
-static void bind_timeout_handler(struct event_context *ev,
-                                struct timed_event *te, 
-                                struct timeval t, void *private)
+static void dcerpc_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 *timeout_pipe = talloc_get_type(ctx->private_data, struct dcerpc_pipe);
+       struct rpc_request *req = talloc_get_type(private, struct rpc_request);
 
-       SMB_ASSERT(timeout_pipe->conn->bind_private != NULL);
-       timeout_pipe->conn->bind_private = NULL;
-       composite_error(ctx, NT_STATUS_IO_TIMEOUT);
+       if (req->ignore_timeout) {
+               dcerpc_req_dequeue(req);
+               req->state = RPC_REQUEST_DONE;
+               req->status = NT_STATUS_IO_TIMEOUT;
+               if (req->async.callback) {
+                       req->async.callback(req);
+               }
+               return;
+       }
+
+       dcerpc_connection_dead(req->p->conn, NT_STATUS_IO_TIMEOUT);
 }
 
 /*
@@ -643,19 +689,18 @@ static void bind_timeout_handler(struct event_context *ev,
 */
 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)
+                                          const struct ndr_syntax_id *syntax,
+                                          const struct ndr_syntax_id *transfer_syntax)
 {
        struct composite_context *c;
        struct ncacn_packet pkt;
        DATA_BLOB blob;
+       struct rpc_request *req;
 
-       c = talloc_zero(mem_ctx, struct composite_context);
+       c = composite_create(mem_ctx,p->conn->event_ctx);
        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;
@@ -667,16 +712,16 @@ 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) {
+               pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
+       }
+
        pkt.u.bind.max_xmit_frag = 5840;
        pkt.u.bind.max_recv_frag = 5840;
-       pkt.u.bind.assoc_group_id = 0;
+       pkt.u.bind.assoc_group_id = p->binding->assoc_group_id;
        pkt.u.bind.num_contexts = 1;
-       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 = talloc_array(mem_ctx, struct dcerpc_ctx_list, 1);
+       if (composite_nomem(pkt.u.bind.ctx_list, c)) return c;
        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;
@@ -686,27 +731,34 @@ struct composite_context *dcerpc_bind_send(struct dcerpc_pipe *p,
        /* construct the NDR form of the packet */
        c->status = ncacn_push_auth(&blob, c, &pkt,
                                    p->conn->security_state.auth_info);
-       if (!NT_STATUS_IS_OK(c->status)) {
-               goto failed;
-       }
+       if (!composite_is_ok(c)) return c;
 
        p->conn->transport.recv_data = dcerpc_recv_data;
-       p->conn->bind_private = c;
+
+       /*
+        * 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;
+
+       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);
 
        c->status = p->conn->transport.send_request(p->conn, &blob,
                                                    True);
-       if (!NT_STATUS_IS_OK(c->status)) {
-               goto failed;
-       }
+       if (!composite_is_ok(c)) return c;
 
-       event_add_timed(c->event_ctx, c,
+       event_add_timed(c->event_ctx, req,
                        timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
-                       bind_timeout_handler, c);
-
-       return c;
+                       dcerpc_timeout_handler, req);
 
- failed:
-       composite_error(c, c->status);
        return c;
 }
 
@@ -720,22 +772,6 @@ NTSTATUS dcerpc_bind_recv(struct composite_context *ctx)
        return result;
 }
 
-/* 
-   perform a bind using the given syntax 
-
-   the auth_info structure is updated with the reply authentication info
-   on success
-*/
-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 *creq;
-       creq = dcerpc_bind_send(p, mem_ctx, syntax, transfer_syntax);
-       return dcerpc_bind_recv(creq);
-}
-
 /* 
    perform a continued bind (and auth3)
 */
@@ -771,41 +807,6 @@ NTSTATUS dcerpc_auth3(struct dcerpc_connection *c,
 }
 
 
-/*
-  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->syntax_id.uuid;
-       syntax->if_version = table->syntax_id.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 struct dcerpc_interface_table *table)
-{
-       struct dcerpc_syntax_id syntax;
-       struct dcerpc_syntax_id transfer_syntax;
-       NTSTATUS status;
-
-       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;
-       }
-
-       return dcerpc_bind(p, mem_ctx, &syntax, &transfer_syntax);
-}
-
-
 /*
   process a fragment received from the transport layer during a
   request
@@ -837,6 +838,14 @@ static void dcerpc_request_recv_data(struct dcerpc_connection *c,
                if (pkt->call_id == req->call_id) break;
        }
 
+#if 0
+       /* useful for testing certain vendors RPC servers */
+       if (req == NULL && c->pending && pkt->call_id == 0) {
+               DEBUG(0,("HACK FOR INCORRECT CALL ID\n"));
+               req = c->pending;
+       }
+#endif
+
        if (req == NULL) {
                DEBUG(2,("dcerpc_request: unmatched call_id %u in response packet\n", pkt->call_id));
                data_blob_free(raw_packet);
@@ -845,6 +854,13 @@ static void dcerpc_request_recv_data(struct dcerpc_connection *c,
 
        talloc_steal(req, raw_packet->data);
 
+       if (req->recv_handler != NULL) {
+               dcerpc_req_dequeue(req);
+               req->state = RPC_REQUEST_DONE;
+               req->recv_handler(req, raw_packet, pkt);
+               return;
+       }
+
        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;
@@ -911,37 +927,6 @@ req_done:
        }
 }
 
-/*
-  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);
-       }
-}
-
-
-/*
-  make sure requests are cleaned up 
- */
-static int dcerpc_req_destructor(void *ptr)
-{
-       struct rpc_request *req = ptr;
-       DLIST_REMOVE(req->p->conn->pending, req);
-       return 0;
-}
-
 /*
   perform the send side of a async dcerpc request
 */
@@ -963,12 +948,15 @@ static struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
        req->p = p;
        req->call_id = next_call_id(p->conn);
        req->status = NT_STATUS_OK;
-       req->state = RPC_REQUEST_PENDING;
+       req->state = RPC_REQUEST_QUEUED;
        req->payload = data_blob(NULL, 0);
        req->flags = 0;
        req->fault_code = 0;
        req->async_call = async;
+       req->ignore_timeout = False;
        req->async.callback = NULL;
+       req->async.private_data = NULL;
+       req->recv_handler = NULL;
 
        if (object != NULL) {
                req->object = talloc_memdup(req, object, sizeof(*object));
@@ -983,11 +971,12 @@ static struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
        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) {
+       if (req->request_data.length && req->request_data.data == NULL) {
                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);
 
@@ -997,7 +986,6 @@ static struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
                                dcerpc_timeout_handler, req);
        }
 
-       talloc_set_destructor(req, dcerpc_req_destructor);
        return req;
 }
 
@@ -1029,6 +1017,7 @@ static void dcerpc_ship_next_request(struct dcerpc_connection *c)
 
        DLIST_REMOVE(c->request_queue, req);
        DLIST_ADD(c->pending, req);
+       req->state = RPC_REQUEST_PENDING;
 
        init_ncacn_hdr(p->conn, &pkt);
 
@@ -1048,7 +1037,7 @@ static void dcerpc_ship_next_request(struct dcerpc_connection *c)
 
        if (req->object) {
                pkt.u.request.object.object = *req->object;
-               pkt.pfc_flags |= DCERPC_PFC_FLAG_ORPC;
+               pkt.pfc_flags |= DCERPC_PFC_FLAG_OBJECT_UUID;
                chunk_size -= ndr_size_GUID(req->object,0);
        }
 
@@ -1110,7 +1099,7 @@ NTSTATUS dcerpc_request_recv(struct rpc_request *req,
 {
        NTSTATUS status;
 
-       while (req->state == RPC_REQUEST_PENDING) {
+       while (req->state != RPC_REQUEST_DONE) {
                struct event_context *ctx = dcerpc_event_context(req->p);
                if (event_loop_once(ctx) != 0) {
                        return NT_STATUS_CONNECTION_DISCONNECTED;
@@ -1201,7 +1190,7 @@ static NTSTATUS dcerpc_ndr_validate_in(struct dcerpc_connection *c,
 
        blob2 = ndr_push_blob(push);
 
-       if (!data_blob_equal(&blob, &blob2)) {
+       if (data_blob_cmp(&blob, &blob2) != 0) {
                DEBUG(3,("original:\n"));
                dump_data(3, blob.data, blob.length);
                DEBUG(3,("secondary:\n"));
@@ -1284,7 +1273,7 @@ static NTSTATUS dcerpc_ndr_validate_out(struct dcerpc_connection *c,
 
        blob2 = ndr_push_blob(push);
 
-       if (!data_blob_equal(&blob, &blob2)) {
+       if (data_blob_cmp(&blob, &blob2) != 0) {
                DEBUG(3,("original:\n"));
                dump_data(3, blob.data, blob.length);
                DEBUG(3,("secondary:\n"));
@@ -1301,7 +1290,15 @@ static NTSTATUS dcerpc_ndr_validate_out(struct dcerpc_connection *c,
        s2 = ndr_print_function_string(mem_ctx, ndr_print, "VALIDATE", 
                                       NDR_OUT, st);
        if (strcmp(s1, s2) != 0) {
+#if 1
                printf("VALIDATE ERROR:\nWIRE:\n%s\n GEN:\n%s\n", s1, s2);
+#else
+               /* this is sometimes useful */
+               printf("VALIDATE ERROR\n");
+               file_save("wire.dat", s1, strlen(s1));
+               file_save("gen.dat", s2, strlen(s2));
+               system("diff -u wire.dat gen.dat");
+#endif
        }
 
        return NT_STATUS_OK;
@@ -1313,12 +1310,12 @@ static NTSTATUS dcerpc_ndr_validate_out(struct dcerpc_connection *c,
  */
 struct rpc_request *dcerpc_ndr_request_send(struct dcerpc_pipe *p,
                                                const struct GUID *object,
-                                               const struct dcerpc_interface_table *table,
+                                               const struct ndr_interface_table *table,
                                                uint32_t opnum, 
                                                TALLOC_CTX *mem_ctx, 
                                                void *r)
 {
-       const struct dcerpc_interface_call *call;
+       const struct ndr_interface_call *call;
        struct ndr_push *push;
        NTSTATUS status;
        DATA_BLOB request;
@@ -1381,7 +1378,7 @@ struct rpc_request *dcerpc_ndr_request_send(struct dcerpc_pipe *p,
 /*
   receive the answer from a dcerpc_ndr_request_send()
 */
-NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
+_PUBLIC_ NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
 {
        struct dcerpc_pipe *p = req->p;
        NTSTATUS status;
@@ -1391,15 +1388,18 @@ NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
        TALLOC_CTX *mem_ctx = req->ndr.mem_ctx;
        void *r = req->ndr.struct_ptr;
        uint32_t opnum = req->ndr.opnum;
-       const struct dcerpc_interface_table *table = req->ndr.table;
-       const struct dcerpc_interface_call *call = &table->calls[opnum];
+       const struct ndr_interface_table *table = req->ndr.table;
+       const struct ndr_interface_call *call = &table->calls[opnum];
 
        /* make sure the recv code doesn't free the request, as we
           need to grab the flags element before it is freed */
-       talloc_increase_ref_count(req);
+       if (talloc_reference(p, req) == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
 
        status = dcerpc_request_recv(req, mem_ctx, &response);
        if (!NT_STATUS_IS_OK(status)) {
+               talloc_unlink(p, req);
                return status;
        }
 
@@ -1408,14 +1408,14 @@ NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
        /* prepare for ndr_pull_* */
        pull = ndr_pull_init_flags(p->conn, &response, mem_ctx);
        if (!pull) {
-               talloc_free(req);
+               talloc_unlink(p, req);
                return NT_STATUS_NO_MEMORY;
        }
 
        if (pull->data) {
                pull->data = talloc_steal(pull, pull->data);
        }
-       talloc_free(req);
+       talloc_unlink(p, req);
 
        if (flags & DCERPC_PULL_BIGENDIAN) {
                pull->flags |= LIBNDR_FLAG_BIGENDIAN;
@@ -1467,7 +1467,7 @@ NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
 */
 NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
                            const struct GUID *object,
-                           const struct dcerpc_interface_table *table,
+                           const struct ndr_interface_table *table,
                            uint32_t opnum, 
                            TALLOC_CTX *mem_ctx, 
                            void *r)
@@ -1517,17 +1517,15 @@ uint32_t dcerpc_auth_level(struct dcerpc_connection *c)
 /*
   Receive an alter reply from the transport
 */
-static void dcerpc_alter_recv_data(struct dcerpc_connection *conn, struct ncacn_packet *pkt)
+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(conn->alter_private, struct composite_context);
+       c = talloc_get_type(req->async.private_data, struct composite_context);
        recv_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) {
@@ -1540,7 +1538,7 @@ static void dcerpc_alter_recv_data(struct dcerpc_connection *conn, struct ncacn_
        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);
+               composite_error(c, NT_STATUS_NET_WRITE_FAULT);
                return;
        }
 
@@ -1562,19 +1560,18 @@ static void dcerpc_alter_recv_data(struct dcerpc_connection *conn, struct ncacn_
 */
 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)
+                                                   const struct ndr_syntax_id *syntax,
+                                                   const struct ndr_syntax_id *transfer_syntax)
 {
        struct composite_context *c;
        struct ncacn_packet pkt;
        DATA_BLOB blob;
+       struct rpc_request *req;
 
-       c = talloc_zero(mem_ctx, struct composite_context);
+       c = composite_create(mem_ctx, p->conn->event_ctx);
        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;
@@ -1586,16 +1583,16 @@ 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) {
+               pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
+       }
+
        pkt.u.alter.max_xmit_frag = 5840;
        pkt.u.alter.max_recv_frag = 5840;
-       pkt.u.alter.assoc_group_id = 0;
+       pkt.u.alter.assoc_group_id = p->binding->assoc_group_id;
        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 = talloc_array(c, struct dcerpc_ctx_list, 1);
+       if (composite_nomem(pkt.u.alter.ctx_list, c)) return c;
        pkt.u.alter.ctx_list[0].context_id = p->context_id;
        pkt.u.alter.ctx_list[0].num_transfer_syntaxes = 1;
        pkt.u.alter.ctx_list[0].abstract_syntax = p->syntax;
@@ -1605,26 +1602,33 @@ struct composite_context *dcerpc_alter_context_send(struct dcerpc_pipe *p,
        /* 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;
-       }
+       if (!composite_is_ok(c)) return c;
 
        p->conn->transport.recv_data = dcerpc_recv_data;
-       p->conn->alter_private = c;
+
+       /*
+        * 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;
+
+       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);
 
        c->status = p->conn->transport.send_request(p->conn, &blob, True);
-       if (!NT_STATUS_IS_OK(c->status)) {
-               goto failed;
-       }
+       if (!composite_is_ok(c)) return c;
 
-       event_add_timed(c->event_ctx, c,
+       event_add_timed(c->event_ctx, req,
                        timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
-                       bind_timeout_handler, c);
-
-       return c;
+                       dcerpc_timeout_handler, req);
 
- failed:
-       composite_error(c, c->status);
        return c;
 }
 
@@ -1640,8 +1644,8 @@ NTSTATUS dcerpc_alter_context_recv(struct composite_context *ctx)
 */
 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)
+                             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);