lib/util: consolidate module loading into common code
[samba.git] / source4 / rpc_server / dcerpc_server.c
index 6af4be9cc14f53cac8300025267674f698109707..a16e6ac9d5f2e4894239ac44043f4d748d4f5d63 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 "librpc/gen_ndr/ndr_dcerpc.h"
 #include "auth/auth.h"
 #include "auth/gensec/gensec.h"
-#include "lib/util/dlinklist.h"
+#include "../lib/util/dlinklist.h"
 #include "rpc_server/dcerpc_server.h"
-#include "lib/events/events.h"
-#include "smbd/service_task.h"
-#include "smbd/service_stream.h"
-#include "smbd/service.h"
+#include "rpc_server/dcerpc_server_proto.h"
+#include "rpc_server/common/proto.h"
+#include "librpc/rpc/dcerpc_proto.h"
 #include "system/filesys.h"
 #include "libcli/security/security.h"
-#include "build.h"
+#include "param/param.h"
+#include "../lib/tsocket/tsocket.h"
+#include "../libcli/named_pipe_auth/npa_tstream.h"
+#include "smbd/service_stream.h"
+#include "../lib/tsocket/tsocket.h"
+#include "lib/socket/socket.h"
+#include "smbd/process_model.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/rpc/rpc_common.h"
+#include "lib/util/samba_modules.h"
+
+/* this is only used when the client asks for an unknown interface */
+#define DUMMY_ASSOC_GROUP 0x0FFFFFFF
 
 extern const struct dcesrv_interface dcesrv_mgmt_interface;
 
+
+/*
+  find an association group given a assoc_group_id
+ */
+static struct dcesrv_assoc_group *dcesrv_assoc_group_find(struct dcesrv_context *dce_ctx,
+                                                         uint32_t id)
+{
+       void *id_ptr;
+
+       id_ptr = idr_find(dce_ctx->assoc_groups_idr, id);
+       if (id_ptr == NULL) {
+               return NULL;
+       }
+       return talloc_get_type_abort(id_ptr, struct dcesrv_assoc_group);
+}
+
+/*
+  take a reference to an existing association group
+ */
+static struct dcesrv_assoc_group *dcesrv_assoc_group_reference(TALLOC_CTX *mem_ctx,
+                                                              struct dcesrv_context *dce_ctx,
+                                                              uint32_t id)
+{
+       struct dcesrv_assoc_group *assoc_group;
+
+       assoc_group = dcesrv_assoc_group_find(dce_ctx, id);
+       if (assoc_group == NULL) {
+               DEBUG(0,(__location__ ": Failed to find assoc_group 0x%08x\n", id));
+               return NULL;
+       }
+       return talloc_reference(mem_ctx, assoc_group);
+}
+
+static int dcesrv_assoc_group_destructor(struct dcesrv_assoc_group *assoc_group)
+{
+       int ret;
+       ret = idr_remove(assoc_group->dce_ctx->assoc_groups_idr, assoc_group->id);
+       if (ret != 0) {
+               DEBUG(0,(__location__ ": Failed to remove assoc_group 0x%08x\n",
+                        assoc_group->id));
+       }
+       return 0;
+}
+
+/*
+  allocate a new association group
+ */
+static struct dcesrv_assoc_group *dcesrv_assoc_group_new(TALLOC_CTX *mem_ctx,
+                                                        struct dcesrv_context *dce_ctx)
+{
+       struct dcesrv_assoc_group *assoc_group;
+       int id;
+
+       assoc_group = talloc_zero(mem_ctx, struct dcesrv_assoc_group);
+       if (assoc_group == NULL) {
+               return NULL;
+       }
+       
+       id = idr_get_new_random(dce_ctx->assoc_groups_idr, assoc_group, UINT16_MAX);
+       if (id == -1) {
+               talloc_free(assoc_group);
+               DEBUG(0,(__location__ ": Out of association groups!\n"));
+               return NULL;
+       }
+
+       assoc_group->id = id;
+       assoc_group->dce_ctx = dce_ctx;
+
+       talloc_set_destructor(assoc_group, dcesrv_assoc_group_destructor);
+
+       return assoc_group;
+}
+
+
 /*
   see if two endpoints match
 */
-static BOOL endpoints_match(const struct dcerpc_binding *ep1,
+static bool endpoints_match(const struct dcerpc_binding *ep1,
                            const struct dcerpc_binding *ep2)
 {
        if (ep1->transport != ep2->transport) {
-               return False;
+               return false;
        }
 
        if (!ep1->endpoint || !ep2->endpoint) {
@@ -52,9 +135,9 @@ static BOOL endpoints_match(const struct dcerpc_binding *ep1,
        }
 
        if (strcasecmp(ep1->endpoint, ep2->endpoint) != 0) 
-               return False;
+               return false;
 
-       return True;
+       return true;
 }
 
 /*
@@ -88,7 +171,7 @@ static struct dcesrv_connection_context *dcesrv_find_context(struct dcesrv_conne
 /*
   see if a uuid and if_version match to an interface
 */
-static BOOL interface_match(const struct dcesrv_interface *if1,
+static bool interface_match(const struct dcesrv_interface *if1,
                                                        const struct dcesrv_interface *if2)
 {
        return (if1->syntax_id.if_version == if2->syntax_id.if_version && 
@@ -113,7 +196,7 @@ static const struct dcesrv_interface *find_interface(const struct dcesrv_endpoin
 /*
   see if a uuid and if_version match to an interface
 */
-static BOOL interface_match_by_uuid(const struct dcesrv_interface *iface,
+static bool interface_match_by_uuid(const struct dcesrv_interface *iface,
                                    const struct GUID *uuid, uint32_t if_version)
 {
        return (iface->syntax_id.if_version == if_version && 
@@ -160,7 +243,7 @@ _PUBLIC_ NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
        struct dcesrv_endpoint *ep;
        struct dcesrv_if_list *ifl;
        struct dcerpc_binding *binding;
-       BOOL add_ep = False;
+       bool add_ep = false;
        NTSTATUS status;
        
        status = dcerpc_parse_binding(dce_ctx, ep_name, &binding);
@@ -179,7 +262,7 @@ _PUBLIC_ NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
                }
                ZERO_STRUCTP(ep);
                ep->ep_description = talloc_reference(ep, binding);
-               add_ep = True;
+               add_ep = true;
 
                /* add mgmt interface */
                ifl = talloc(dce_ctx, struct dcesrv_if_list);
@@ -248,8 +331,8 @@ _PUBLIC_ NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
        return NT_STATUS_OK;
 }
 
-static NTSTATUS dcesrv_inherited_session_key(struct dcesrv_connection *p,
-                                             DATA_BLOB *session_key)
+NTSTATUS dcesrv_inherited_session_key(struct dcesrv_connection *p,
+                                     DATA_BLOB *session_key)
 {
        if (p->auth_state.session_info->session_key.length) {
                *session_key = p->auth_state.session_info->session_key;
@@ -258,53 +341,33 @@ static NTSTATUS dcesrv_inherited_session_key(struct dcesrv_connection *p,
        return NT_STATUS_NO_USER_SESSION_KEY;
 }
 
-NTSTATUS dcesrv_generic_session_key(struct dcesrv_connection *p,
-                                   DATA_BLOB *session_key)
-{
-       /* this took quite a few CPU cycles to find ... */
-       session_key->data = discard_const_p(uint8_t, "SystemLibraryDTC");
-       session_key->length = 16;
-       return NT_STATUS_OK;
-}
-
 /*
   fetch the user session key - may be default (above) or the SMB session key
+
+  The key is always truncated to 16 bytes 
 */
 _PUBLIC_ NTSTATUS dcesrv_fetch_session_key(struct dcesrv_connection *p,
                                  DATA_BLOB *session_key)
 {
-       return p->auth_state.session_key(p, session_key);
-}
-
-
-/*
-  destroy a link to an endpoint
-*/
-static int dcesrv_endpoint_destructor(struct dcesrv_connection *p)
-{
-       while (p->contexts) {
-               struct dcesrv_connection_context *c = p->contexts;
-
-               DLIST_REMOVE(p->contexts, c);
-
-               if (c->iface) {
-                       c->iface->unbind(c, c->iface);
-               }
+       NTSTATUS status = p->auth_state.session_key(p, session_key);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
-       return 0;
-}
+       session_key->length = MIN(session_key->length, 16);
 
+       return NT_STATUS_OK;
+}
 
 /*
   connect to a dcerpc endpoint
 */
-NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
+_PUBLIC_ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
                                 TALLOC_CTX *mem_ctx,
                                 const struct dcesrv_endpoint *ep,
                                 struct auth_session_info *session_info,
-                                struct event_context *event_ctx,
-                                struct messaging_context *msg_ctx,
+                                struct tevent_context *event_ctx,
+                                struct imessaging_context *msg_ctx,
                                 struct server_id server_id,
                                 uint32_t state_flags,
                                 struct dcesrv_connection **_p)
@@ -327,6 +390,7 @@ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
        p->endpoint = ep;
        p->contexts = NULL;
        p->call_list = NULL;
+       p->packet_log_dir = lpcfg_lockdir(dce_ctx->lp_ctx);
        p->incoming_fragmented_call_list = NULL;
        p->pending_call_list = NULL;
        p->cli_max_recv_frag = 0;
@@ -338,104 +402,49 @@ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
        p->event_ctx = event_ctx;
        p->msg_ctx = msg_ctx;
        p->server_id = server_id;
-       p->processing = False;
+       p->processing = false;
        p->state_flags = state_flags;
        ZERO_STRUCT(p->transport);
 
-       talloc_set_destructor(p, dcesrv_endpoint_destructor);
-
        *_p = p;
        return NT_STATUS_OK;
 }
 
 /*
-  search and connect to a dcerpc endpoint
-*/
-_PUBLIC_ NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
-                                       TALLOC_CTX *mem_ctx,
-                                       const struct dcerpc_binding *ep_description,
-                                       struct auth_session_info *session_info,
-                                       struct event_context *event_ctx,
-                                       struct messaging_context *msg_ctx,
-                                       struct server_id server_id,
-                                       uint32_t state_flags,
-                                       struct dcesrv_connection **dce_conn_p)
-{
-       NTSTATUS status;
-       const struct dcesrv_endpoint *ep;
-
-       /* make sure this endpoint exists */
-       ep = find_endpoint(dce_ctx, ep_description);
-       if (!ep) {
-               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
-       }
-
-       status = dcesrv_endpoint_connect(dce_ctx, mem_ctx, ep, session_info,
-                                        event_ctx, msg_ctx, server_id,
-                                        state_flags, dce_conn_p);
-       NT_STATUS_NOT_OK_RETURN(status);
-
-       (*dce_conn_p)->auth_state.session_key = dcesrv_inherited_session_key;
-
-       /* TODO: check security descriptor of the endpoint here 
-        *       if it's a smb named pipe
-        *       if it's failed free dce_conn_p
-        */
-
-       return NT_STATUS_OK;
-}
-
-
-static void dcesrv_init_hdr(struct ncacn_packet *pkt)
-{
-       pkt->rpc_vers = 5;
-       pkt->rpc_vers_minor = 0;
-       if (lp_rpc_big_endian()) {
-               pkt->drep[0] = 0;
-       } else {
-               pkt->drep[0] = DCERPC_DREP_LE;
-       }
-       pkt->drep[1] = 0;
-       pkt->drep[2] = 0;
-       pkt->drep[3] = 0;
-}
-
-/*
-  return a dcerpc fault
-*/
-static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
+  move a call from an existing linked list to the specified list. This
+  prevents bugs where we forget to remove the call from a previous
+  list when moving it.
+ */
+static void dcesrv_call_set_list(struct dcesrv_call_state *call, 
+                                enum dcesrv_call_list list)
 {
-       struct ncacn_packet pkt;
-       struct data_blob_list_item *rep;
-       NTSTATUS status;
-
-       /* setup a bind_ack */
-       dcesrv_init_hdr(&pkt);
-       pkt.auth_length = 0;
-       pkt.call_id = call->pkt.call_id;
-       pkt.ptype = DCERPC_PKT_FAULT;
-       pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
-       pkt.u.fault.alloc_hint = 0;
-       pkt.u.fault.context_id = 0;
-       pkt.u.fault.cancel_count = 0;
-       pkt.u.fault.status = fault_code;
-
-       rep = talloc(call, struct data_blob_list_item);
-       if (!rep) {
-               return NT_STATUS_NO_MEMORY;
+       switch (call->list) {
+       case DCESRV_LIST_NONE:
+               break;
+       case DCESRV_LIST_CALL_LIST:
+               DLIST_REMOVE(call->conn->call_list, call);
+               break;
+       case DCESRV_LIST_FRAGMENTED_CALL_LIST:
+               DLIST_REMOVE(call->conn->incoming_fragmented_call_list, call);
+               break;
+       case DCESRV_LIST_PENDING_CALL_LIST:
+               DLIST_REMOVE(call->conn->pending_call_list, call);
+               break;
        }
-
-       status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       call->list = list;
+       switch (list) {
+       case DCESRV_LIST_NONE:
+               break;
+       case DCESRV_LIST_CALL_LIST:
+               DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
+               break;
+       case DCESRV_LIST_FRAGMENTED_CALL_LIST:
+               DLIST_ADD_END(call->conn->incoming_fragmented_call_list, call, struct dcesrv_call_state *);
+               break;
+       case DCESRV_LIST_PENDING_CALL_LIST:
+               DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
+               break;
        }
-
-       dcerpc_set_frag_length(&rep->blob, rep->blob.length);
-
-       DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
-       DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
-
-       return NT_STATUS_OK;    
 }
 
 
@@ -449,7 +458,7 @@ static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
        NTSTATUS status;
 
        /* setup a bind_nak */
-       dcesrv_init_hdr(&pkt);
+       dcesrv_init_hdr(&pkt, lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
        pkt.auth_length = 0;
        pkt.call_id = call->pkt.call_id;
        pkt.ptype = DCERPC_PKT_BIND_NAK;
@@ -472,11 +481,27 @@ static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
        dcerpc_set_frag_length(&rep->blob, rep->blob.length);
 
        DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
-       DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
+       dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
+
+       if (call->conn->call_list && call->conn->call_list->replies) {
+               if (call->conn->transport.report_output_data) {
+                       call->conn->transport.report_output_data(call->conn);
+               }
+       }
 
        return NT_STATUS_OK;    
 }
 
+static int dcesrv_connection_context_destructor(struct dcesrv_connection_context *c)
+{
+       DLIST_REMOVE(c->conn->contexts, c);
+
+       if (c->iface && c->iface->unbind) {
+               c->iface->unbind(c, c->iface);
+       }
+
+       return 0;
+}
 
 /*
   handle a bind request
@@ -491,6 +516,16 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
        uint32_t result=0, reason=0;
        uint32_t context_id;
        const struct dcesrv_interface *iface;
+       uint32_t extra_flags = 0;
+
+       /*
+         if provided, check the assoc_group is valid
+        */
+       if (call->pkt.u.bind.assoc_group_id != 0 &&
+           lpcfg_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","assoc group checking", true) &&
+           dcesrv_assoc_group_find(call->conn->dce_ctx, call->pkt.u.bind.assoc_group_id) == NULL) {
+               return dcesrv_bind_nak(call, 0);        
+       }
 
        if (call->pkt.u.bind.num_contexts < 1 ||
            call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
@@ -539,30 +574,74 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
                context->conn = call->conn;
                context->iface = iface;
                context->context_id = context_id;
-               context->private = NULL;
-               context->handles = NULL;
+               if (call->pkt.u.bind.assoc_group_id != 0) {
+                       context->assoc_group = dcesrv_assoc_group_reference(context,
+                                                                           call->conn->dce_ctx, 
+                                                                           call->pkt.u.bind.assoc_group_id);
+               } else {
+                       context->assoc_group = dcesrv_assoc_group_new(context, call->conn->dce_ctx);
+               }
+               if (context->assoc_group == NULL) {
+                       talloc_free(context);
+                       return dcesrv_bind_nak(call, 0);
+               }
+               context->private_data = NULL;
                DLIST_ADD(call->conn->contexts, context);
                call->context = context;
+               talloc_set_destructor(context, dcesrv_connection_context_destructor);
+
+               status = iface->bind(call, iface, if_version);
+               if (!NT_STATUS_IS_OK(status)) {
+                       char *uuid_str = GUID_string(call, &uuid);
+                       DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n",
+                                uuid_str, if_version, nt_errstr(status)));
+                       talloc_free(uuid_str);
+                       /* we don't want to trigger the iface->unbind() hook */
+                       context->iface = NULL;
+                       talloc_free(call->context);
+                       call->context = NULL;
+                       return dcesrv_bind_nak(call, 0);
+               }
        }
 
        if (call->conn->cli_max_recv_frag == 0) {
-               call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
+               call->conn->cli_max_recv_frag = MIN(0x2000, call->pkt.u.bind.max_recv_frag);
+       }
+
+       if ((call->pkt.pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) &&
+           lpcfg_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","header signing", false)) {
+               call->conn->state_flags |= DCESRV_CALL_STATE_FLAG_HEADER_SIGNING;
+               extra_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
        }
 
        /* handle any authentication that is being requested */
        if (!dcesrv_auth_bind(call)) {
+               talloc_free(call->context);
+               call->context = NULL;
                return dcesrv_bind_nak(call, DCERPC_BIND_REASON_INVALID_AUTH_TYPE);
        }
 
        /* setup a bind_ack */
-       dcesrv_init_hdr(&pkt);
+       dcesrv_init_hdr(&pkt, lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
        pkt.auth_length = 0;
        pkt.call_id = call->pkt.call_id;
        pkt.ptype = DCERPC_PKT_BIND_ACK;
-       pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
-       pkt.u.bind_ack.max_xmit_frag = 0x2000;
+       pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST | extra_flags;
+       pkt.u.bind_ack.max_xmit_frag = call->conn->cli_max_recv_frag;
        pkt.u.bind_ack.max_recv_frag = 0x2000;
-       pkt.u.bind_ack.assoc_group_id = call->pkt.u.bind.assoc_group_id;
+
+       /*
+         make it possible for iface->bind() to specify the assoc_group_id
+         This helps the openchange mapiproxy plugin to work correctly.
+         
+         metze
+       */
+       if (call->context) {
+               pkt.u.bind_ack.assoc_group_id = call->context->assoc_group->id;
+       } else {
+               pkt.u.bind_ack.assoc_group_id = DUMMY_ASSOC_GROUP;
+       }
+
        if (iface) {
                /* FIXME: Use pipe name as specified by endpoint instead of interface name */
                pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
@@ -572,6 +651,8 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
        pkt.u.bind_ack.num_results = 1;
        pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
        if (!pkt.u.bind_ack.ctx_list) {
+               talloc_free(call->context);
+               call->context = NULL;
                return NT_STATUS_NO_MEMORY;
        }
        pkt.u.bind_ack.ctx_list[0].result = result;
@@ -579,36 +660,38 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
        pkt.u.bind_ack.ctx_list[0].syntax = ndr_transfer_syntax;
        pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
 
-       if (!dcesrv_auth_bind_ack(call, &pkt)) {
+       status = dcesrv_auth_bind_ack(call, &pkt);
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(call->context);
+               call->context = NULL;
                return dcesrv_bind_nak(call, 0);
        }
 
-       if (iface) {
-               status = iface->bind(call, iface);
-               if (!NT_STATUS_IS_OK(status)) {
-                       char *uuid_str = GUID_string(call, &uuid);
-                       DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n", 
-                                uuid_str, if_version, nt_errstr(status)));
-                       talloc_free(uuid_str);
-                       return dcesrv_bind_nak(call, 0);
-               }
-       }
-
        rep = talloc(call, struct data_blob_list_item);
        if (!rep) {
+               talloc_free(call->context);
+               call->context = NULL;
                return NT_STATUS_NO_MEMORY;
        }
 
-       status = ncacn_push_auth(&rep->blob, call, &pkt, 
-                                 call->conn->auth_state.auth_info);
+       status = ncacn_push_auth(&rep->blob, call, &pkt,
+                                                        call->conn->auth_state.auth_info);
        if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(call->context);
+               call->context = NULL;
                return status;
        }
 
        dcerpc_set_frag_length(&rep->blob, rep->blob.length);
 
        DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
-       DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
+       dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
+
+       if (call->conn->call_list && call->conn->call_list->replies) {
+               if (call->conn->transport.report_output_data) {
+                       call->conn->transport.report_output_data(call->conn);
+               }
+       }
 
        return NT_STATUS_OK;
 }
@@ -641,6 +724,7 @@ static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_
        struct dcesrv_connection_context *context;
        const struct dcesrv_interface *iface;
        struct GUID uuid, *transfer_syntax_uuid;
+       NTSTATUS status;
 
        if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
        uuid = call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid;
@@ -669,10 +753,31 @@ static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_
        context->conn = call->conn;
        context->iface = iface;
        context->context_id = context_id;
-       context->private = NULL;
-       context->handles = NULL;
+       if (call->pkt.u.alter.assoc_group_id != 0) {
+               context->assoc_group = dcesrv_assoc_group_reference(context,
+                                                                   call->conn->dce_ctx, 
+                                                                   call->pkt.u.alter.assoc_group_id);
+       } else {
+               context->assoc_group = dcesrv_assoc_group_new(context, call->conn->dce_ctx);
+       }
+       if (context->assoc_group == NULL) {
+               talloc_free(context);
+               call->context = NULL;
+               return NT_STATUS_NO_MEMORY;
+       }
+       context->private_data = NULL;
        DLIST_ADD(call->conn->contexts, context);
        call->context = context;
+       talloc_set_destructor(context, dcesrv_connection_context_destructor);
+
+       status = iface->bind(call, iface, if_version);
+       if (!NT_STATUS_IS_OK(status)) {
+               /* we don't want to trigger the iface->unbind() hook */
+               context->iface = NULL;
+               talloc_free(context);
+               call->context = NULL;
+               return status;
+       }
 
        return NT_STATUS_OK;
 }
@@ -699,24 +804,41 @@ static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
        context_id = call->pkt.u.alter.ctx_list[0].context_id;
 
        /* see if they are asking for a new interface */
-       if (result == 0 &&
-           dcesrv_find_context(call->conn, context_id) == NULL) {
-               status = dcesrv_alter_new_context(call, context_id);
-               if (!NT_STATUS_IS_OK(status)) {
-                       result = DCERPC_BIND_PROVIDER_REJECT;
-                       reason = DCERPC_BIND_REASON_ASYNTAX;            
+       if (result == 0) {
+               call->context = dcesrv_find_context(call->conn, context_id);
+               if (!call->context) {
+                       status = dcesrv_alter_new_context(call, context_id);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               result = DCERPC_BIND_PROVIDER_REJECT;
+                               reason = DCERPC_BIND_REASON_ASYNTAX;
+                       }
                }
        }
 
+       if (result == 0 &&
+           call->pkt.u.alter.assoc_group_id != 0 &&
+           lpcfg_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","assoc group checking", true) &&
+           call->pkt.u.alter.assoc_group_id != call->context->assoc_group->id) {
+               DEBUG(0,(__location__ ": Failed attempt to use new assoc_group in alter context (0x%08x 0x%08x)\n",
+                        call->context->assoc_group->id, call->pkt.u.alter.assoc_group_id));
+               /* TODO: can they ask for a new association group? */
+               result = DCERPC_BIND_PROVIDER_REJECT;
+               reason = DCERPC_BIND_REASON_ASYNTAX;
+       }
+
        /* setup a alter_resp */
-       dcesrv_init_hdr(&pkt);
+       dcesrv_init_hdr(&pkt, lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
        pkt.auth_length = 0;
        pkt.call_id = call->pkt.call_id;
        pkt.ptype = DCERPC_PKT_ALTER_RESP;
        pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
        pkt.u.alter_resp.max_xmit_frag = 0x2000;
        pkt.u.alter_resp.max_recv_frag = 0x2000;
-       pkt.u.alter_resp.assoc_group_id = call->pkt.u.alter.assoc_group_id;
+       if (result == 0) {
+               pkt.u.alter_resp.assoc_group_id = call->context->assoc_group->id;
+       } else {
+               pkt.u.alter_resp.assoc_group_id = 0;
+       }
        pkt.u.alter_resp.num_results = 1;
        pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
        if (!pkt.u.alter_resp.ctx_list) {
@@ -728,8 +850,15 @@ static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
        pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
        pkt.u.alter_resp.secondary_address = "";
 
-       if (!dcesrv_auth_alter_ack(call, &pkt)) {
-               return dcesrv_bind_nak(call, 0);
+       status = dcesrv_auth_alter_ack(call, &pkt);
+       if (!NT_STATUS_IS_OK(status)) {
+               if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)
+                   || NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE)
+                   || NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)
+                   || NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+                       return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
+               }
+               return dcesrv_fault(call, 0);
        }
 
        rep = talloc(call, struct data_blob_list_item);
@@ -737,8 +866,7 @@ static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
                return NT_STATUS_NO_MEMORY;
        }
 
-       status = ncacn_push_auth(&rep->blob, call, &pkt, 
-                                 call->conn->auth_state.auth_info);
+       status = ncacn_push_auth(&rep->blob, call, &pkt, call->conn->auth_state.auth_info);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -746,11 +874,41 @@ static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
        dcerpc_set_frag_length(&rep->blob, rep->blob.length);
 
        DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
-       DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
+       dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
+
+       if (call->conn->call_list && call->conn->call_list->replies) {
+               if (call->conn->transport.report_output_data) {
+                       call->conn->transport.report_output_data(call->conn);
+               }
+       }
 
        return NT_STATUS_OK;
 }
 
+/*
+  possibly save the call for inspection with ndrdump
+ */
+static void dcesrv_save_call(struct dcesrv_call_state *call, const char *why)
+{
+#ifdef DEVELOPER
+       char *fname;
+       const char *dump_dir;
+       dump_dir = lpcfg_parm_string(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv", "stubs directory");
+       if (!dump_dir) {
+               return;
+       }
+       fname = talloc_asprintf(call, "%s/RPC-%s-%u-%s.dat",
+                               dump_dir,
+                               call->context->iface->name,
+                               call->pkt.u.request.opnum,
+                               why);
+       if (file_save(fname, call->pkt.u.request.stub_and_verifier.data, call->pkt.u.request.stub_and_verifier.length)) {
+               DEBUG(0,("RPC SAVED %s\n", fname));
+       }
+       talloc_free(fname);
+#endif
+}
+
 /*
   handle a dcerpc request packet
 */
@@ -779,10 +937,6 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
        call->context   = context;
        call->ndr_pull  = pull;
 
-       if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_ORPC) {
-               pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
-       }
-
        if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
                pull->flags |= LIBNDR_FLAG_BIGENDIAN;
        }
@@ -790,13 +944,21 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
        /* unravel the NDR for the packet */
        status = context->iface->ndr_pull(call, call, pull, &call->r);
        if (!NT_STATUS_IS_OK(status)) {
+               if (call->fault_code == DCERPC_FAULT_OP_RNG_ERROR) {
+                       /* we got an unknown call */
+                       DEBUG(3,(__location__ ": Unknown RPC call %u on %s\n",
+                                call->pkt.u.request.opnum, context->iface->name));
+                       dcesrv_save_call(call, "unknown");
+               } else {
+                       dcesrv_save_call(call, "pullfail");
+               }
                return dcesrv_fault(call, call->fault_code);
        }
 
        if (pull->offset != pull->data_size) {
+               dcesrv_save_call(call, "extrabytes");
                DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n", 
                         pull->data_size - pull->offset));
-               dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
        }
 
        /* call the dispatch function */
@@ -810,7 +972,7 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
        }
 
        /* add the call to the pending list */
-       DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
+       dcesrv_call_set_list(call, DCESRV_LIST_PENDING_CALL_LIST);
 
        if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
                return NT_STATUS_OK;
@@ -819,163 +981,40 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
        return dcesrv_reply(call);
 }
 
-_PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
-{
-       struct ndr_push *push;
-       NTSTATUS status;
-       DATA_BLOB stub;
-       uint32_t total_length;
-       struct dcesrv_connection_context *context = call->context;
-
-       /* call the reply function */
-       status = context->iface->reply(call, call, call->r);
-       if (!NT_STATUS_IS_OK(status)) {
-               return dcesrv_fault(call, call->fault_code);
-       }
-
-       /* form the reply NDR */
-       push = ndr_push_init_ctx(call);
-       NT_STATUS_HAVE_NO_MEMORY(push);
-
-       /* carry over the pointer count to the reply in case we are
-          using full pointer. See NDR specification for full
-          pointers */
-       push->ptr_count = call->ndr_pull->ptr_count;
-
-       if (lp_rpc_big_endian()) {
-               push->flags |= LIBNDR_FLAG_BIGENDIAN;
-       }
-
-       status = context->iface->ndr_push(call, call, push, call->r);
-       if (!NT_STATUS_IS_OK(status)) {
-               return dcesrv_fault(call, call->fault_code);
-       }
-
-       stub = ndr_push_blob(push);
-
-       total_length = stub.length;
-
-       do {
-               uint32_t length;
-               struct data_blob_list_item *rep;
-               struct ncacn_packet pkt;
-
-               rep = talloc(call, struct data_blob_list_item);
-               NT_STATUS_HAVE_NO_MEMORY(rep);
-
-               length = stub.length;
-               if (length + DCERPC_RESPONSE_LENGTH > call->conn->cli_max_recv_frag) {
-                       /* the 32 is to cope with signing data */
-                       length = call->conn->cli_max_recv_frag - 
-                               (DCERPC_MAX_SIGN_SIZE+DCERPC_RESPONSE_LENGTH);
-               }
-
-               /* form the dcerpc response packet */
-               dcesrv_init_hdr(&pkt);
-               pkt.auth_length = 0;
-               pkt.call_id = call->pkt.call_id;
-               pkt.ptype = DCERPC_PKT_RESPONSE;
-               pkt.pfc_flags = 0;
-               if (stub.length == total_length) {
-                       pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
-               }
-               if (length == stub.length) {
-                       pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
-               }
-               pkt.u.response.alloc_hint = stub.length;
-               pkt.u.response.context_id = call->pkt.u.request.context_id;
-               pkt.u.response.cancel_count = 0;
-               pkt.u.response.stub_and_verifier.data = stub.data;
-               pkt.u.response.stub_and_verifier.length = length;
-
-               if (!dcesrv_auth_response(call, &rep->blob, &pkt)) {
-                       return dcesrv_fault(call, DCERPC_FAULT_OTHER);          
-               }
-
-               dcerpc_set_frag_length(&rep->blob, rep->blob.length);
-
-               DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
-               
-               stub.data += length;
-               stub.length -= length;
-       } while (stub.length != 0);
-
-       /* move the call from the pending to the finished calls list */
-       DLIST_REMOVE(call->conn->pending_call_list, call);
-       DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
-
-       if (call->conn->call_list && call->conn->call_list->replies) {
-               if (call->conn->transport.report_output_data) {
-                       call->conn->transport.report_output_data(call->conn);
-               }
-       }
-
-       return NT_STATUS_OK;
-}
-
-_PUBLIC_ struct socket_address *dcesrv_connection_get_my_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
-{
-       if (!conn->transport.get_my_addr) {
-               return NULL;
-       }
-
-       return conn->transport.get_my_addr(conn, mem_ctx);
-}
 
-_PUBLIC_ struct socket_address *dcesrv_connection_get_peer_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
+/*
+  remove the call from the right list when freed
+ */
+static int dcesrv_call_dequeue(struct dcesrv_call_state *call)
 {
-       if (!conn->transport.get_peer_addr) {
-               return NULL;
-       }
-
-       return conn->transport.get_peer_addr(conn, mem_ctx);
+       dcesrv_call_set_list(call, DCESRV_LIST_NONE);
+       return 0;
 }
 
-/*
-  work out if we have a full packet yet
-*/
-static BOOL dce_full_packet(const DATA_BLOB *data)
+_PUBLIC_ const struct tsocket_address *dcesrv_connection_get_local_address(struct dcesrv_connection *conn)
 {
-       if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
-               return False;
-       }
-       if (dcerpc_get_frag_length(data) > data->length) {
-               return False;
-       }
-       return True;
+       return conn->local_address;
 }
 
-/*
-  we might have consumed only part of our input - advance past that part
-*/
-static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
+_PUBLIC_ const struct tsocket_address *dcesrv_connection_get_remote_address(struct dcesrv_connection *conn)
 {
-       DATA_BLOB blob;
-
-       if (dce_conn->partial_input.length == offset) {
-               data_blob_free(&dce_conn->partial_input);
-               return;
-       }
-
-       blob = dce_conn->partial_input;
-       dce_conn->partial_input = data_blob(blob.data + offset,
-                                           blob.length - offset);
-       data_blob_free(&blob);
+       return conn->remote_address;
 }
 
 /*
   process some input to a dcerpc endpoint server.
 */
-NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
+NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn,
+                                    struct ncacn_packet *pkt,
+                                    DATA_BLOB blob)
 {
-       struct ndr_pull *ndr;
        NTSTATUS status;
        struct dcesrv_call_state *call;
-       DATA_BLOB blob;
 
        call = talloc_zero(dce_conn, struct dcesrv_call_state);
        if (!call) {
-               talloc_free(dce_conn->partial_input.data);
+               data_blob_free(&blob);
+               talloc_free(pkt);
                return NT_STATUS_NO_MEMORY;
        }
        call->conn              = dce_conn;
@@ -983,38 +1022,21 @@ NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
        call->msg_ctx           = dce_conn->msg_ctx;
        call->state_flags       = call->conn->state_flags;
        call->time              = timeval_current();
+       call->list              = DCESRV_LIST_NONE;
 
-       blob = dce_conn->partial_input;
-       blob.length = dcerpc_get_frag_length(&blob);
+       talloc_steal(call, pkt);
+       talloc_steal(call, blob.data);
+       call->pkt = *pkt;
 
-       ndr = ndr_pull_init_blob(&blob, call);
-       if (!ndr) {
-               talloc_free(dce_conn->partial_input.data);
-               talloc_free(call);
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
-               ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
-       }
-
-       status = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
-       if (!NT_STATUS_IS_OK(status)) {
-               talloc_free(dce_conn->partial_input.data);
-               talloc_free(call);
-               return status;
-       }
+       talloc_set_destructor(call, dcesrv_call_dequeue);
 
        /* we have to check the signing here, before combining the
           pdus */
        if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
            !dcesrv_auth_request(call, &blob)) {
-               dce_partial_advance(dce_conn, blob.length);
                return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);          
        }
 
-       dce_partial_advance(dce_conn, blob.length);
-
        /* see if this is a continued packet */
        if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
            !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
@@ -1026,8 +1048,8 @@ NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
                        return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
                }
 
-               /* this is a continuation of an existing call - find the call then
-                  tack it on the end */
+               /* this is a continuation of an existing call - find the call
+                  then tack it on the end */
                call = dcesrv_find_fragmented_call(dce_conn, call2->pkt.call_id);
                if (!call) {
                        return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
@@ -1067,13 +1089,12 @@ NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
           just put it on the incoming_fragmented_call_list and wait for the rest */
        if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
            !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
-               DLIST_ADD_END(dce_conn->incoming_fragmented_call_list, call, 
-                             struct dcesrv_call_state *);
+               dcesrv_call_set_list(call, DCESRV_LIST_FRAGMENTED_CALL_LIST);
                return NT_STATUS_OK;
        } 
        
        /* This removes any fragments we may have had stashed away */
-       DLIST_REMOVE(dce_conn->incoming_fragmented_call_list, call);
+       dcesrv_call_set_list(call, DCESRV_LIST_NONE);
 
        switch (call->pkt.ptype) {
        case DCERPC_PKT_BIND:
@@ -1103,91 +1124,9 @@ NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
        return status;
 }
 
-
-/*
-  provide some input to a dcerpc endpoint server. This passes data
-  from a dcerpc client into the server
-*/
-_PUBLIC_ NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
-{
-       NTSTATUS status;
-
-       dce_conn->partial_input.data = talloc_realloc(dce_conn,
-                                                     dce_conn->partial_input.data,
-                                                     uint8_t,
-                                                     dce_conn->partial_input.length + data->length);
-       if (!dce_conn->partial_input.data) {
-               return NT_STATUS_NO_MEMORY;
-       }
-       memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
-              data->data, data->length);
-       dce_conn->partial_input.length += data->length;
-
-       while (dce_full_packet(&dce_conn->partial_input)) {
-               status = dcesrv_input_process(dce_conn);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
-               }
-       }
-
-       return NT_STATUS_OK;
-}
-
-/*
-  retrieve some output from a dcerpc server
-  The caller supplies a function that will be called to do the
-  actual output. 
-
-  The first argument to write_fn() will be 'private', the second will
-  be a pointer to a buffer containing the data to be sent and the 3rd
-  will be a pointer to a size_t variable that will be set to the
-  number of bytes that are consumed from the output.
-
-  from the current fragment
-*/
-_PUBLIC_ NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn, 
-                      void *private_data,
-                      NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
-{
-       NTSTATUS status;
-       struct dcesrv_call_state *call;
-       struct data_blob_list_item *rep;
-       size_t nwritten;
-
-       call = dce_conn->call_list;
-       if (!call || !call->replies) {
-               if (dce_conn->pending_call_list) {
-                       /* TODO: we need to say act async here
-                        *       as we know we have pending requests
-                        *       which will be finished at a time
-                        */
-                       return NT_STATUS_FOOBAR;
-               }
-               return NT_STATUS_FOOBAR;
-       }
-       rep = call->replies;
-
-       status = write_fn(private_data, &rep->blob, &nwritten);
-       NT_STATUS_IS_ERR_RETURN(status);
-
-       rep->blob.length -= nwritten;
-       rep->blob.data += nwritten;
-
-       if (rep->blob.length == 0) {
-               /* we're done with this section of the call */
-               DLIST_REMOVE(call->replies, rep);
-       }
-
-       if (call->replies == NULL) {
-               /* we're done with the whole call */
-               DLIST_REMOVE(dce_conn->call_list, call);
-               talloc_free(call);
-       }
-
-       return status;
-}
-
-static NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_servers, struct dcesrv_context **_dce_ctx)
+_PUBLIC_ NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, 
+                                     struct loadparm_context *lp_ctx,
+                                     const char **endpoint_servers, struct dcesrv_context **_dce_ctx)
 {
        NTSTATUS status;
        struct dcesrv_context *dce_ctx;
@@ -1201,6 +1140,9 @@ static NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_s
        dce_ctx = talloc(mem_ctx, struct dcesrv_context);
        NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
        dce_ctx->endpoint_list  = NULL;
+       dce_ctx->lp_ctx = lp_ctx;
+       dce_ctx->assoc_groups_idr = idr_init(dce_ctx);
+       NT_STATUS_HAVE_NO_MEMORY(dce_ctx->assoc_groups_idr);
 
        for (i=0;endpoint_servers[i];i++) {
                const struct dcesrv_endpoint_server *ep_server;
@@ -1223,21 +1165,6 @@ static NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_s
        return NT_STATUS_OK;
 }
 
-/*
-  initialise the dcerpc server context for ncacn_np based services
-*/
-_PUBLIC_ NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct dcesrv_context **_dce_ctx)
-{
-       NTSTATUS status;
-       struct dcesrv_context *dce_ctx;
-
-       status = dcesrv_init_context(mem_ctx, lp_dcerpc_endpoint_servers(), &dce_ctx);
-       NT_STATUS_NOT_OK_RETURN(status);
-
-       *_dce_ctx = dce_ctx;
-       return NT_STATUS_OK;
-}
-
 /* the list of currently registered DCERPC endpoint servers.
  */
 static struct ep_server {
@@ -1296,6 +1223,27 @@ const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
        return NULL;
 }
 
+void dcerpc_server_init(struct loadparm_context *lp_ctx)
+{
+       static bool initialized;
+#define _MODULE_PROTO(init) extern NTSTATUS init(void);
+       STATIC_dcerpc_server_MODULES_PROTO;
+       init_module_fn static_init[] = { STATIC_dcerpc_server_MODULES };
+       init_module_fn *shared_init;
+
+       if (initialized) {
+               return;
+       }
+       initialized = true;
+
+       shared_init = load_samba_modules(NULL, "dcerpc_server");
+
+       run_init_functions(static_init);
+       run_init_functions(shared_init);
+
+       talloc_free(shared_init);
+}
+
 /*
   return the DCERPC module version, and the size of some critical types
   This can be used by endpoint server modules to either detect compilation errors, or provide
@@ -1319,79 +1267,488 @@ const struct dcesrv_critical_sizes *dcerpc_module_version(void)
        return &critical_sizes;
 }
 
-/*
-  open the dcerpc server sockets
-*/
-static void dcesrv_task_init(struct task_server *task)
+static void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, const char *reason)
+{
+       struct stream_connection *srv_conn;
+       srv_conn = talloc_get_type(dce_conn->transport.private_data,
+                                  struct stream_connection);
+
+       stream_terminate_connection(srv_conn, reason);
+}
+/* We need this include to be able to compile on some plateforms
+ * (ie. freebsd 7.2) as it seems that <sys/uio.h> is not included
+ * correctly.
+ * It has to be that deep because otherwise we have a conflict on
+ * const struct dcesrv_interface declaration.
+ * This is mostly due to socket_wrapper defining #define bind swrap_bind
+ * which conflict with the bind used before.
+ */
+#include "system/network.h"
+
+struct dcesrv_sock_reply_state {
+       struct dcesrv_connection *dce_conn;
+       struct dcesrv_call_state *call;
+       struct iovec iov;
+};
+
+static void dcesrv_sock_reply_done(struct tevent_req *subreq);
+
+static void dcesrv_sock_report_output_data(struct dcesrv_connection *dce_conn)
+{
+       struct dcesrv_call_state *call;
+
+       call = dce_conn->call_list;
+       if (!call || !call->replies) {
+               return;
+       }
+
+       while (call->replies) {
+               struct data_blob_list_item *rep = call->replies;
+               struct dcesrv_sock_reply_state *substate;
+               struct tevent_req *subreq;
+
+               substate = talloc(call, struct dcesrv_sock_reply_state);
+               if (!substate) {
+                       dcesrv_terminate_connection(dce_conn, "no memory");
+                       return;
+               }
+
+               substate->dce_conn = dce_conn;
+               substate->call = NULL;
+
+               DLIST_REMOVE(call->replies, rep);
+
+               if (call->replies == NULL) {
+                       substate->call = call;
+               }
+
+               substate->iov.iov_base = (void *) rep->blob.data;
+               substate->iov.iov_len = rep->blob.length;
+
+               subreq = tstream_writev_queue_send(substate,
+                                                  dce_conn->event_ctx,
+                                                  dce_conn->stream,
+                                                  dce_conn->send_queue,
+                                                  &substate->iov, 1);
+               if (!subreq) {
+                       dcesrv_terminate_connection(dce_conn, "no memory");
+                       return;
+               }
+               tevent_req_set_callback(subreq, dcesrv_sock_reply_done,
+                                       substate);
+       }
+
+       DLIST_REMOVE(call->conn->call_list, call);
+       call->list = DCESRV_LIST_NONE;
+}
+
+static void dcesrv_sock_reply_done(struct tevent_req *subreq)
 {
+       struct dcesrv_sock_reply_state *substate = tevent_req_callback_data(subreq,
+                                               struct dcesrv_sock_reply_state);
+       int ret;
+       int sys_errno;
        NTSTATUS status;
-       struct dcesrv_context *dce_ctx;
-       struct dcesrv_endpoint *e;
-
-       task_server_set_title(task, "task[dcesrv]");
-
-       status = dcesrv_init_context(task->event_ctx,
-                                    lp_dcerpc_endpoint_servers(),
-                                    &dce_ctx);
-       if (!NT_STATUS_IS_OK(status)) goto failed;
-
-       /* Make sure the directory for NCALRPC exists */
-       if (!directory_exist(lp_ncalrpc_dir())) {
-               mkdir(lp_ncalrpc_dir(), 0755);
-       }
-
-       for (e=dce_ctx->endpoint_list;e;e=e->next) {
-               switch (e->ep_description->transport) {
-               case NCACN_UNIX_STREAM:
-                       status = dcesrv_add_ep_unix(dce_ctx, e, task->event_ctx, task->model_ops);
-                       if (!NT_STATUS_IS_OK(status)) goto failed;
-                       break;
-               
-               case NCALRPC:
-                       status = dcesrv_add_ep_ncalrpc(dce_ctx, e, task->event_ctx, task->model_ops);
-                       if (!NT_STATUS_IS_OK(status)) goto failed;
-                       break;
-
-               case NCACN_IP_TCP:
-                       status = dcesrv_add_ep_tcp(dce_ctx, e, task->event_ctx, task->model_ops);
-                       if (!NT_STATUS_IS_OK(status)) goto failed;
-                       break;
-                       
-               case NCACN_NP:
-/*                     FIXME: status = dcesrv_add_ep_np(dce_ctx, e, task->event_ctx, task->model_ops);
-                       if (!NT_STATUS_IS_OK(status)) goto failed;
-*/                     break;
-
-               default:
-                       status = NT_STATUS_NOT_SUPPORTED;
-                       if (!NT_STATUS_IS_OK(status)) goto failed;
+       struct dcesrv_call_state *call = substate->call;
+
+       ret = tstream_writev_queue_recv(subreq, &sys_errno);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               status = map_nt_error_from_unix_common(sys_errno);
+               dcesrv_terminate_connection(substate->dce_conn, nt_errstr(status));
+               return;
+       }
+
+       talloc_free(substate);
+       if (call) {
+               talloc_free(call);
+       }
+}
+
+
+
+
+struct dcesrv_socket_context {
+       const struct dcesrv_endpoint *endpoint;
+       struct dcesrv_context *dcesrv_ctx;
+};
+
+
+static void dcesrv_read_fragment_done(struct tevent_req *subreq);
+
+static void dcesrv_sock_accept(struct stream_connection *srv_conn)
+{
+       NTSTATUS status;
+       struct dcesrv_socket_context *dcesrv_sock = 
+               talloc_get_type(srv_conn->private_data, struct dcesrv_socket_context);
+       struct dcesrv_connection *dcesrv_conn = NULL;
+       int ret;
+       struct tevent_req *subreq;
+       struct loadparm_context *lp_ctx = dcesrv_sock->dcesrv_ctx->lp_ctx;
+
+       if (!srv_conn->session_info) {
+               status = auth_anonymous_session_info(srv_conn,
+                                                    lp_ctx,
+                                                    &srv_conn->session_info);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0,("dcesrv_sock_accept: auth_anonymous_session_info failed: %s\n",
+                               nt_errstr(status)));
+                       stream_terminate_connection(srv_conn, nt_errstr(status));
+                       return;
                }
        }
 
+       status = dcesrv_endpoint_connect(dcesrv_sock->dcesrv_ctx,
+                                        srv_conn,
+                                        dcesrv_sock->endpoint,
+                                        srv_conn->session_info,
+                                        srv_conn->event.ctx,
+                                        srv_conn->msg_ctx,
+                                        srv_conn->server_id,
+                                        DCESRV_CALL_STATE_FLAG_MAY_ASYNC,
+                                        &dcesrv_conn);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("dcesrv_sock_accept: dcesrv_endpoint_connect failed: %s\n", 
+                       nt_errstr(status)));
+               stream_terminate_connection(srv_conn, nt_errstr(status));
+               return;
+       }
+
+       dcesrv_conn->transport.private_data             = srv_conn;
+       dcesrv_conn->transport.report_output_data       = dcesrv_sock_report_output_data;
+
+       TALLOC_FREE(srv_conn->event.fde);
+
+       dcesrv_conn->send_queue = tevent_queue_create(dcesrv_conn, "dcesrv send queue");
+       if (!dcesrv_conn->send_queue) {
+               status = NT_STATUS_NO_MEMORY;
+               DEBUG(0,("dcesrv_sock_accept: tevent_queue_create(%s)\n",
+                       nt_errstr(status)));
+               stream_terminate_connection(srv_conn, nt_errstr(status));
+               return;
+       }
+
+       if (dcesrv_sock->endpoint->ep_description->transport == NCACN_NP) {
+               dcesrv_conn->auth_state.session_key = dcesrv_inherited_session_key;
+               dcesrv_conn->stream = talloc_move(dcesrv_conn,
+                                                 &srv_conn->tstream);
+       } else {
+               ret = tstream_bsd_existing_socket(dcesrv_conn,
+                                                 socket_get_fd(srv_conn->socket),
+                                                 &dcesrv_conn->stream);
+               if (ret == -1) {
+                       status = map_nt_error_from_unix_common(errno);
+                       DEBUG(0, ("dcesrv_sock_accept: "
+                                 "failed to setup tstream: %s\n",
+                                 nt_errstr(status)));
+                       stream_terminate_connection(srv_conn, nt_errstr(status));
+                       return;
+               }
+               socket_set_flags(srv_conn->socket, SOCKET_FLAG_NOCLOSE);
+       }
+
+       dcesrv_conn->local_address = srv_conn->local_address;
+       dcesrv_conn->remote_address = srv_conn->remote_address;
+
+       srv_conn->private_data = dcesrv_conn;
+
+       irpc_add_name(srv_conn->msg_ctx, "rpc_server");
+
+       subreq = dcerpc_read_ncacn_packet_send(dcesrv_conn,
+                                              dcesrv_conn->event_ctx,
+                                              dcesrv_conn->stream);
+       if (!subreq) {
+               status = NT_STATUS_NO_MEMORY;
+               DEBUG(0,("dcesrv_sock_accept: dcerpc_read_fragment_buffer_send(%s)\n",
+                       nt_errstr(status)));
+               stream_terminate_connection(srv_conn, nt_errstr(status));
+               return;
+       }
+       tevent_req_set_callback(subreq, dcesrv_read_fragment_done, dcesrv_conn);
+
        return;
-failed:
-       task_server_terminate(task, "Failed to startup dcerpc server task");    
+}
+
+static void dcesrv_read_fragment_done(struct tevent_req *subreq)
+{
+       struct dcesrv_connection *dce_conn = tevent_req_callback_data(subreq,
+                                            struct dcesrv_connection);
+       struct ncacn_packet *pkt;
+       DATA_BLOB buffer;
+       NTSTATUS status;
+
+       status = dcerpc_read_ncacn_packet_recv(subreq, dce_conn,
+                                              &pkt, &buffer);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               dcesrv_terminate_connection(dce_conn, nt_errstr(status));
+               return;
+       }
+
+       status = dcesrv_process_ncacn_packet(dce_conn, pkt, buffer);
+       if (!NT_STATUS_IS_OK(status)) {
+               dcesrv_terminate_connection(dce_conn, nt_errstr(status));
+               return;
+       }
+
+       subreq = dcerpc_read_ncacn_packet_send(dce_conn,
+                                              dce_conn->event_ctx,
+                                              dce_conn->stream);
+       if (!subreq) {
+               status = NT_STATUS_NO_MEMORY;
+               dcesrv_terminate_connection(dce_conn, nt_errstr(status));
+               return;
+       }
+       tevent_req_set_callback(subreq, dcesrv_read_fragment_done, dce_conn);
+}
+
+static void dcesrv_sock_recv(struct stream_connection *conn, uint16_t flags)
+{
+       struct dcesrv_connection *dce_conn = talloc_get_type(conn->private_data,
+                                            struct dcesrv_connection);
+       dcesrv_terminate_connection(dce_conn, "dcesrv_sock_recv triggered");
+}
+
+static void dcesrv_sock_send(struct stream_connection *conn, uint16_t flags)
+{
+       struct dcesrv_connection *dce_conn = talloc_get_type(conn->private_data,
+                                            struct dcesrv_connection);
+       dcesrv_terminate_connection(dce_conn, "dcesrv_sock_send triggered");
+}
+
+
+static const struct stream_server_ops dcesrv_stream_ops = {
+       .name                   = "rpc",
+       .accept_connection      = dcesrv_sock_accept,
+       .recv_handler           = dcesrv_sock_recv,
+       .send_handler           = dcesrv_sock_send,
+};
+
+static NTSTATUS dcesrv_add_ep_unix(struct dcesrv_context *dce_ctx, 
+                                  struct loadparm_context *lp_ctx,
+                                  struct dcesrv_endpoint *e,
+                           struct tevent_context *event_ctx, const struct model_ops *model_ops)
+{
+       struct dcesrv_socket_context *dcesrv_sock;
+       uint16_t port = 1;
+       NTSTATUS status;
+
+       dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context);
+       NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
+
+       /* remember the endpoint of this socket */
+       dcesrv_sock->endpoint           = e;
+       dcesrv_sock->dcesrv_ctx         = talloc_reference(dcesrv_sock, dce_ctx);
+
+       status = stream_setup_socket(dcesrv_sock, event_ctx, lp_ctx,
+                                    model_ops, &dcesrv_stream_ops, 
+                                    "unix", e->ep_description->endpoint, &port, 
+                                    lpcfg_socket_options(lp_ctx),
+                                    dcesrv_sock);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("service_setup_stream_socket(path=%s) failed - %s\n",
+                        e->ep_description->endpoint, nt_errstr(status)));
+       }
+
+       return status;
+}
+
+static NTSTATUS dcesrv_add_ep_ncalrpc(struct dcesrv_context *dce_ctx, 
+                                     struct loadparm_context *lp_ctx,
+                                     struct dcesrv_endpoint *e,
+                                     struct tevent_context *event_ctx, const struct model_ops *model_ops)
+{
+       struct dcesrv_socket_context *dcesrv_sock;
+       uint16_t port = 1;
+       char *full_path;
+       NTSTATUS status;
+
+       if (!e->ep_description->endpoint) {
+               /* No identifier specified: use DEFAULT. 
+                * DO NOT hardcode this value anywhere else. Rather, specify 
+                * no endpoint and let the epmapper worry about it. */
+               e->ep_description->endpoint = talloc_strdup(dce_ctx, "DEFAULT");
+       }
+
+       full_path = talloc_asprintf(dce_ctx, "%s/%s", lpcfg_ncalrpc_dir(lp_ctx),
+                                   e->ep_description->endpoint);
+
+       dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context);
+       NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
+
+       /* remember the endpoint of this socket */
+       dcesrv_sock->endpoint           = e;
+       dcesrv_sock->dcesrv_ctx         = talloc_reference(dcesrv_sock, dce_ctx);
+
+       status = stream_setup_socket(dcesrv_sock, event_ctx, lp_ctx,
+                                    model_ops, &dcesrv_stream_ops, 
+                                    "unix", full_path, &port, 
+                                    lpcfg_socket_options(lp_ctx),
+                                    dcesrv_sock);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("service_setup_stream_socket(identifier=%s,path=%s) failed - %s\n",
+                        e->ep_description->endpoint, full_path, nt_errstr(status)));
+       }
+       return status;
+}
+
+static NTSTATUS dcesrv_add_ep_np(struct dcesrv_context *dce_ctx,
+                                struct loadparm_context *lp_ctx,
+                                struct dcesrv_endpoint *e,
+                                struct tevent_context *event_ctx, const struct model_ops *model_ops)
+{
+       struct dcesrv_socket_context *dcesrv_sock;
+       NTSTATUS status;
+                       
+       if (e->ep_description->endpoint == NULL) {
+               DEBUG(0, ("Endpoint mandatory for named pipes\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context);
+       NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
+
+       /* remember the endpoint of this socket */
+       dcesrv_sock->endpoint           = e;
+       dcesrv_sock->dcesrv_ctx         = talloc_reference(dcesrv_sock, dce_ctx);
+
+       status = tstream_setup_named_pipe(dce_ctx, event_ctx, lp_ctx,
+                                         model_ops, &dcesrv_stream_ops,
+                                         e->ep_description->endpoint,
+                                         dcesrv_sock);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("stream_setup_named_pipe(pipe=%s) failed - %s\n",
+                        e->ep_description->endpoint, nt_errstr(status)));
+               return status;
+       }
+
+       return NT_STATUS_OK;
 }
 
 /*
-  called on startup of the smb server service It's job is to start
-  listening on all configured sockets
+  add a socket address to the list of events, one event per dcerpc endpoint
 */
-static NTSTATUS dcesrv_init(struct event_context *event_context, 
-                           const struct model_ops *model_ops)
-{      
-       return task_server_startup(event_context, model_ops, dcesrv_task_init);
+static NTSTATUS add_socket_rpc_tcp_iface(struct dcesrv_context *dce_ctx, struct dcesrv_endpoint *e,
+                                        struct tevent_context *event_ctx, const struct model_ops *model_ops,
+                                        const char *address)
+{
+       struct dcesrv_socket_context *dcesrv_sock;
+       uint16_t port = 0;
+       NTSTATUS status;
+                       
+       if (e->ep_description->endpoint) {
+               port = atoi(e->ep_description->endpoint);
+       }
+
+       dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context);
+       NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
+
+       /* remember the endpoint of this socket */
+       dcesrv_sock->endpoint           = e;
+       dcesrv_sock->dcesrv_ctx         = talloc_reference(dcesrv_sock, dce_ctx);
+
+       status = stream_setup_socket(dcesrv_sock, event_ctx, dce_ctx->lp_ctx,
+                                    model_ops, &dcesrv_stream_ops, 
+                                    "ip", address, &port,
+                                    lpcfg_socket_options(dce_ctx->lp_ctx),
+                                    dcesrv_sock);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("service_setup_stream_socket(address=%s,port=%u) failed - %s\n", 
+                        address, port, nt_errstr(status)));
+       }
+
+       if (e->ep_description->endpoint == NULL) {
+               e->ep_description->endpoint = talloc_asprintf(dce_ctx, "%d", port);
+       }
+
+       return status;
 }
 
-NTSTATUS server_service_rpc_init(void)
+#include "lib/socket/netif.h" /* Included here to work around the fact that socket_wrapper redefines bind() */
+
+static NTSTATUS dcesrv_add_ep_tcp(struct dcesrv_context *dce_ctx, 
+                                 struct loadparm_context *lp_ctx,
+                                 struct dcesrv_endpoint *e,
+                                 struct tevent_context *event_ctx, const struct model_ops *model_ops)
 {
-       init_module_fn static_init[] = STATIC_dcerpc_server_MODULES;
-       init_module_fn *shared_init = load_samba_modules(NULL, "dcerpc_server");
+       NTSTATUS status;
 
-       run_init_functions(static_init);
-       run_init_functions(shared_init);
+       /* Add TCP/IP sockets */
+       if (lpcfg_interfaces(lp_ctx) && lpcfg_bind_interfaces_only(lp_ctx)) {
+               int num_interfaces;
+               int i;
+               struct interface *ifaces;
 
-       talloc_free(shared_init);
-       
-       return register_server_service("rpc", dcesrv_init);
+               load_interface_list(dce_ctx, lp_ctx, &ifaces);
+
+               num_interfaces = iface_list_count(ifaces);
+               for(i = 0; i < num_interfaces; i++) {
+                       const char *address = iface_list_n_ip(ifaces, i);
+                       status = add_socket_rpc_tcp_iface(dce_ctx, e, event_ctx, model_ops, address);
+                       NT_STATUS_NOT_OK_RETURN(status);
+               }
+       } else {
+               const char **wcard;
+               int i;
+               wcard = iface_list_wildcard(dce_ctx, lp_ctx);
+               NT_STATUS_HAVE_NO_MEMORY(wcard);
+               for (i=0; wcard[i]; i++) {
+                       status = add_socket_rpc_tcp_iface(dce_ctx, e, event_ctx, model_ops, wcard[i]);
+                       NT_STATUS_NOT_OK_RETURN(status);
+               }
+               talloc_free(wcard);
+       }
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS dcesrv_add_ep(struct dcesrv_context *dce_ctx,
+                      struct loadparm_context *lp_ctx,
+                      struct dcesrv_endpoint *e,
+                      struct tevent_context *event_ctx,
+                      const struct model_ops *model_ops)
+{
+       switch (e->ep_description->transport) {
+       case NCACN_UNIX_STREAM:
+               return dcesrv_add_ep_unix(dce_ctx, lp_ctx, e, event_ctx, model_ops);
+
+       case NCALRPC:
+               return dcesrv_add_ep_ncalrpc(dce_ctx, lp_ctx, e, event_ctx, model_ops);
+
+       case NCACN_IP_TCP:
+               return dcesrv_add_ep_tcp(dce_ctx, lp_ctx, e, event_ctx, model_ops);
+
+       case NCACN_NP:
+               return dcesrv_add_ep_np(dce_ctx, lp_ctx, e, event_ctx, model_ops);
+
+       default:
+               return NT_STATUS_NOT_SUPPORTED;
+       }
+}
+
+
+/**
+ * retrieve credentials from a dce_call
+ */
+_PUBLIC_ struct cli_credentials *dcesrv_call_credentials(struct dcesrv_call_state *dce_call)
+{
+       return dce_call->conn->auth_state.session_info->credentials;
+}
+
+/**
+ * returns true if this is an authenticated call
+ */
+_PUBLIC_ bool dcesrv_call_authenticated(struct dcesrv_call_state *dce_call)
+{
+       enum security_user_level level;
+       level = security_session_user_level(dce_call->conn->auth_state.session_info, NULL);
+       return level >= SECURITY_USER;
+}
+
+/**
+ * retrieve account_name for a dce_call
+ */
+_PUBLIC_ const char *dcesrv_call_account_name(struct dcesrv_call_state *dce_call)
+{
+       return dce_call->context->conn->auth_state.session_info->info->account_name;
 }