r4359: using talloc_free() of a child struct in a talloc_destructor is useless
[samba.git] / source4 / rpc_server / dcerpc_server.c
index 35661d913e6cb79fa475b492945374975ac1bca7..c53036b45b7261d057c043e7054c7ca0f2e6c420 100644 (file)
@@ -4,6 +4,7 @@
    server side dcerpc core code
 
    Copyright (C) Andrew Tridgell 2003
+   Copyright (C) Stefan (metze) Metzmacher 2004
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 */
 
 #include "includes.h"
+#include "librpc/gen_ndr/ndr_epmapper.h"
+#include "librpc/gen_ndr/ndr_oxidresolver.h"
+#include "auth/auth.h"
+#include "dlinklist.h"
+#include "rpc_server/dcerpc_server.h"
 
 /*
-  find the set of endpoint operations for an endpoint server
+  see if two endpoints match
 */
-static const struct dcesrv_endpoint_ops *find_endpoint(struct server_context *smb,
-                                                      const struct dcesrv_endpoint *endpoint)
+static BOOL endpoints_match(const struct dcerpc_binding *ep1,
+                           const struct dcerpc_binding *ep2)
 {
-       struct dce_endpoint *ep;
-       for (ep=smb->dcesrv.endpoint_list; ep; ep=ep->next) {
-               if (ep->endpoint_ops->query_endpoint(endpoint)) {
-                       return ep->endpoint_ops;
+       if (ep1->transport != ep2->transport) {
+               return False;
+       }
+
+       if (!ep1->endpoint || !ep2->endpoint) {
+               return ep1->endpoint == ep2->endpoint;
+       }
+
+       if (strcasecmp(ep1->endpoint, ep2->endpoint) != 0) 
+               return False;
+
+       return True;
+}
+
+/*
+  find an endpoint in the dcesrv_context
+*/
+static struct dcesrv_endpoint *find_endpoint(struct dcesrv_context *dce_ctx,
+                                            const struct dcerpc_binding *ep_description)
+{
+       struct dcesrv_endpoint *ep;
+       for (ep=dce_ctx->endpoint_list; ep; ep=ep->next) {
+               if (endpoints_match(&ep->ep_description, ep_description)) {
+                       return ep;
+               }
+       }
+       return NULL;
+}
+
+/*
+  see if a uuid and if_version match to an interface
+*/
+static BOOL interface_match(const struct dcesrv_interface *if1,
+                                                       const struct dcesrv_interface *if2)
+{
+       if (if1->if_version != if2->if_version) {
+               return False;
+       }
+
+       if (strcmp(if1->uuid, if2->uuid)==0) {
+               return True;
+       }                       
+
+       return False;
+}
+
+/*
+  find the interface operations on an endpoint
+*/
+static const struct dcesrv_interface *find_interface(const struct dcesrv_endpoint *endpoint,
+                                                      const struct dcesrv_interface *iface)
+{
+       struct dcesrv_if_list *ifl;
+       for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
+               if (interface_match(&(ifl->iface), iface)) {
+                       return &(ifl->iface);
+               }
+       }
+       return NULL;
+}
+
+/*
+  see if a uuid and if_version match to an interface
+*/
+static BOOL interface_match_by_uuid(const struct dcesrv_interface *iface,
+                                   const char *uuid, uint32_t if_version)
+{
+       if (iface->if_version != if_version) {
+               return False;
+       }
+
+       if (strcmp(iface->uuid, uuid)==0) {
+               return True;
+       }                       
+
+       return False;
+}
+
+/*
+  find the interface operations on an endpoint by uuid
+*/
+static const struct dcesrv_interface *find_interface_by_uuid(const struct dcesrv_endpoint *endpoint,
+                                                            const char *uuid, uint32_t if_version)
+{
+       struct dcesrv_if_list *ifl;
+       for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
+               if (interface_match_by_uuid(&(ifl->iface), uuid, if_version)) {
+                       return &(ifl->iface);
                }
        }
        return NULL;
@@ -40,10 +130,10 @@ static const struct dcesrv_endpoint_ops *find_endpoint(struct server_context *sm
 /*
   find a call that is pending in our call list
 */
-static struct dcesrv_call_state *dcesrv_find_call(struct dcesrv_state *dce, uint16 call_id)
+static struct dcesrv_call_state *dcesrv_find_call(struct dcesrv_connection *dce_conn, uint16_t call_id)
 {
        struct dcesrv_call_state *c;
-       for (c=dce->call_list;c;c=c->next) {
+       for (c=dce_conn->call_list;c;c=c->next) {
                if (c->pkt.call_id == call_id) {
                        return c;
                }
@@ -52,106 +142,232 @@ static struct dcesrv_call_state *dcesrv_find_call(struct dcesrv_state *dce, uint
 }
 
 /*
-  register an endpoint server
+  register an interface on an endpoint
 */
-BOOL dcesrv_endpoint_register(struct server_context *smb, 
-                             const struct dcesrv_endpoint_ops *ops)
+NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
+                                  const char *ep_name,
+                                  const struct dcesrv_interface *iface,
+                                  const struct security_descriptor *sd)
 {
-       struct dce_endpoint *ep;
-       ep = malloc(sizeof(*ep));
-       if (!ep) {
-               return False;
+       struct dcesrv_endpoint *ep;
+       struct dcesrv_if_list *ifl;
+       struct dcerpc_binding binding;
+       BOOL add_ep = False;
+       NTSTATUS status;
+       
+       status = dcerpc_parse_binding(dce_ctx, ep_name, &binding);
+
+       if (NT_STATUS_IS_ERR(status)) {
+               DEBUG(0, ("Trouble parsing binding string '%s'\n", ep_name));
+               return status;
        }
-       ep->endpoint_ops = ops;
-       DLIST_ADD(smb->dcesrv.endpoint_list, ep);
-       return True;
+
+       /* check if this endpoint exists
+        */
+       if ((ep=find_endpoint(dce_ctx, &binding))==NULL) {
+               ep = talloc_p(dce_ctx, struct dcesrv_endpoint);
+               if (!ep) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               ZERO_STRUCTP(ep);
+               ep->ep_description = binding;
+               add_ep = True;
+       }
+
+       /* see if the interface is already registered on te endpoint */
+       if (find_interface(ep, iface)!=NULL) {
+               DEBUG(0,("dcesrv_interface_register: interface '%s' already registered on endpoint '%s'\n",
+                       iface->name, ep_name));
+               return NT_STATUS_OBJECT_NAME_COLLISION;
+       }
+
+       /* talloc a new interface list element */
+       ifl = talloc_p(dce_ctx, struct dcesrv_if_list);
+       if (!ifl) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* copy the given interface struct to the one on the endpoints interface list */
+       memcpy(&(ifl->iface),iface, sizeof(struct dcesrv_interface));
+
+       /* if we have a security descriptor given,
+        * we should see if we can set it up on the endpoint
+        */
+       if (sd != NULL) {
+               /* if there's currently no security descriptor given on the endpoint
+                * we try to set it
+                */
+               if (ep->sd == NULL) {
+                       ep->sd = security_descriptor_copy(dce_ctx, sd);
+               }
+
+               /* if now there's no security descriptor given on the endpoint
+                * something goes wrong, either we failed to copy the security descriptor
+                * or there was already one on the endpoint
+                */
+               if (ep->sd != NULL) {
+                       DEBUG(0,("dcesrv_interface_register: interface '%s' failed to setup a security descriptor\n"
+                                "                           on endpoint '%s'\n",
+                               iface->name, ep_name));
+                       if (add_ep) free(ep);
+                       free(ifl);
+                       return NT_STATUS_OBJECT_NAME_COLLISION;
+               }
+       }
+
+       /* finally add the interface on the endpoint */
+       DLIST_ADD(ep->interface_list, ifl);
+
+       /* if it's a new endpoint add it to the dcesrv_context */
+       if (add_ep) {
+               DLIST_ADD(dce_ctx->endpoint_list, ep);
+       }
+
+       DEBUG(4,("dcesrv_interface_register: interface '%s' registered on endpoint '%s'\n",
+               iface->name, ep_name));
+
+       return NT_STATUS_OK;
+}
+
+static 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;
+               return NT_STATUS_OK;
+       }
+       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;
 }
 
 /*
-  connect to a dcerpc endpoint
+  fetch the user session key - may be default (above) or the SMB session key
 */
-NTSTATUS dcesrv_endpoint_connect(struct server_context *smb,
-                                const struct dcesrv_endpoint *endpoint,
-                                struct dcesrv_state **p)
+NTSTATUS dcesrv_fetch_session_key(struct dcesrv_connection *p,
+                                 DATA_BLOB *session_key)
 {
-       TALLOC_CTX *mem_ctx;
-       NTSTATUS status;
-       const struct dcesrv_endpoint_ops *ops;
+       return p->auth_state.session_key(p, session_key);
+}
 
-       /* make sure this endpoint exists */
-       ops = find_endpoint(smb, endpoint);
-       if (!ops) {
-               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+/*
+  destroy a link to an endpoint
+*/
+static int dcesrv_endpoint_destructor(void *ptr)
+{
+       struct dcesrv_connection *p = ptr;
+       if (p->iface) {
+               p->iface->unbind(p, p->iface);
        }
 
-       mem_ctx = talloc_init("dcesrv_endpoint_connect");
-       if (!mem_ctx) {
-               return NT_STATUS_NO_MEMORY;
+       /* destroy any handles */
+       while (p->handles) {
+               dcesrv_handle_destroy(p, p->handles);
        }
 
-       *p = talloc_p(mem_ctx, struct dcesrv_state);
+       return 0;
+}
+
+
+/*
+  connect to a dcerpc endpoint
+*/
+NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
+                                const struct dcesrv_endpoint *ep,
+                                struct dcesrv_connection **p)
+{
+       *p = talloc_p(dce_ctx, struct dcesrv_connection);
        if (! *p) {
-               talloc_destroy(mem_ctx);
                return NT_STATUS_NO_MEMORY;
        }
 
-       (*p)->smb = smb;
-       (*p)->mem_ctx = mem_ctx;
-       (*p)->endpoint = *endpoint;
-       (*p)->ops = ops;
+       (*p)->dce_ctx = dce_ctx;
+       (*p)->endpoint = ep;
+       (*p)->iface = NULL;
        (*p)->private = NULL;
        (*p)->call_list = NULL;
        (*p)->cli_max_recv_frag = 0;
-       (*p)->ndr = NULL;
-       (*p)->dispatch = NULL;
        (*p)->handles = NULL;
-       (*p)->next_handle = 0;
+       (*p)->partial_input = data_blob(NULL, 0);
+       (*p)->auth_state.auth_info = NULL;
+       (*p)->auth_state.gensec_security = NULL;
+       (*p)->auth_state.session_info = NULL;
+       (*p)->auth_state.session_key = dcesrv_generic_session_key;
+       (*p)->srv_conn = NULL;
+
+       talloc_set_destructor(*p, dcesrv_endpoint_destructor);
+
+       return NT_STATUS_OK;
+}
+
+/*
+  search and connect to a dcerpc endpoint
+*/
+NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
+                                       const struct dcerpc_binding *ep_description,
+                                       struct auth_session_info *session_info,
+                                       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;
+       }
 
-       /* make sure the endpoint server likes the connection */
-       status = ops->connect(*p);
+       status = dcesrv_endpoint_connect(dce_ctx, ep, dce_conn_p);
        if (!NT_STATUS_IS_OK(status)) {
-               talloc_destroy(mem_ctx);
                return status;
        }
-       
+
+       session_info->refcount++;
+       (*dce_conn_p)->auth_state.session_info = session_info;
+       (*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;
 }
 
 
-/*
-  disconnect a link to an endpoint
-*/
-void dcesrv_endpoint_disconnect(struct dcesrv_state *p)
+static void dcesrv_init_hdr(struct dcerpc_packet *pkt)
 {
-       p->ops->disconnect(p);
-
-       /* destroy any handles */
-       while (p->handles) {
-               TALLOC_CTX *m = p->handles->mem_ctx;
-               DLIST_REMOVE(p->handles, p->handles);
-               talloc_destroy(m);
+       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;
        }
-       
-       talloc_destroy(p->mem_ctx);
+       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 fault_code)
+static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
 {
-       struct ndr_push *push;
        struct dcerpc_packet pkt;
        struct dcesrv_call_reply *rep;
        NTSTATUS status;
 
        /* setup a bind_ack */
-       pkt.rpc_vers = 5;
-       pkt.rpc_vers_minor = 0;
-       pkt.drep[0] = 0x10; /* Little endian */
-       pkt.drep[1] = 0;
-       pkt.drep[2] = 0;
-       pkt.drep[3] = 0;
+       dcesrv_init_hdr(&pkt);
        pkt.auth_length = 0;
        pkt.call_id = call->pkt.call_id;
        pkt.ptype = DCERPC_PKT_FAULT;
@@ -161,64 +377,36 @@ static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32 fault_code)
        pkt.u.fault.cancel_count = 0;
        pkt.u.fault.status = fault_code;
 
-       /* now form the NDR for the fault */
-       push = ndr_push_init_ctx(call->mem_ctx);
-       if (!push) {
+       rep = talloc_p(call, struct dcesrv_call_reply);
+       if (!rep) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
+       status = dcerpc_push_auth(&rep->data, call, &pkt, NULL);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
 
-       rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
-       if (!rep) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       rep->data = ndr_push_blob(push);
-       SSVAL(rep->data.data,  DCERPC_FRAG_LEN_OFFSET, rep->data.length);
+       dcerpc_set_frag_length(&rep->data, rep->data.length);
 
        DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
+       DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
 
        return NT_STATUS_OK;    
 }
 
 
-/*
-  return a dcerpc fault from a ntstatus code
-*/
-static NTSTATUS dcesrv_fault_nt(struct dcesrv_call_state *call, NTSTATUS status)
-{
-       uint32 fault_code = DCERPC_FAULT_OTHER;
-
-       /* TODO: we need to expand this table to include more mappings */
-       if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
-               fault_code = DCERPC_FAULT_CONTEXT_MISMATCH;
-       }
-
-       return dcesrv_fault(call, fault_code);
-}
-
-
 /*
   return a dcerpc bind_nak
 */
-static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32 reason)
+static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
 {
-       struct ndr_push *push;
        struct dcerpc_packet pkt;
        struct dcesrv_call_reply *rep;
        NTSTATUS status;
 
-       /* setup a bind_ack */
-       pkt.rpc_vers = 5;
-       pkt.rpc_vers_minor = 0;
-       pkt.drep[0] = 0x10; /* Little endian */
-       pkt.drep[1] = 0;
-       pkt.drep[2] = 0;
-       pkt.drep[3] = 0;
+       /* setup a bind_nak */
+       dcesrv_init_hdr(&pkt);
        pkt.auth_length = 0;
        pkt.call_id = call->pkt.call_id;
        pkt.ptype = DCERPC_PKT_BIND_NAK;
@@ -226,26 +414,20 @@ static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32 reason)
        pkt.u.bind_nak.reject_reason = reason;
        pkt.u.bind_nak.num_versions = 0;
 
-       /* now form the NDR for the bind_nak */
-       push = ndr_push_init_ctx(call->mem_ctx);
-       if (!push) {
+       rep = talloc_p(call, struct dcesrv_call_reply);
+       if (!rep) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
+       status = dcerpc_push_auth(&rep->data, call, &pkt, NULL);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
 
-       rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
-       if (!rep) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       rep->data = ndr_push_blob(push);
-       SSVAL(rep->data.data,  DCERPC_FRAG_LEN_OFFSET, rep->data.length);
+       dcerpc_set_frag_length(&rep->data, rep->data.length);
 
        DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
+       DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
 
        return NT_STATUS_OK;    
 }
@@ -257,26 +439,25 @@ static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32 reason)
 static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
 {
        const char *uuid, *transfer_syntax;
-       uint32 if_version, transfer_syntax_version;
+       uint32_t if_version, transfer_syntax_version;
        struct dcerpc_packet pkt;
-       struct ndr_push *push;
        struct dcesrv_call_reply *rep;
        NTSTATUS status;
-       uint32 result=0, reason=0;
+       uint32_t result=0, reason=0;
 
        if (call->pkt.u.bind.num_contexts != 1 ||
            call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
                return dcesrv_bind_nak(call, 0);
        }
 
-       if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.major_version;
-       uuid = GUID_string(call->mem_ctx, &call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid);
+       if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
+       uuid = GUID_string(call, &call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid);
        if (!uuid) {
                return dcesrv_bind_nak(call, 0);
        }
 
-       transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].major_version;
-       transfer_syntax = GUID_string(call->mem_ctx
+       transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
+       transfer_syntax = GUID_string(call, 
                                      &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid);
        if (!transfer_syntax ||
            strcasecmp(NDR_GUID, transfer_syntax) != 0 ||
@@ -285,24 +466,26 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
                return dcesrv_bind_nak(call, 0);
        }
 
-       if (!call->dce->ops->set_interface(call->dce, uuid, if_version)) {
+       call->conn->iface = find_interface_by_uuid(call->conn->endpoint, uuid, if_version);
+       if (!call->conn->iface) {
                DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version));
                /* we don't know about that interface */
                result = DCERPC_BIND_PROVIDER_REJECT;
-               reason = DCERPC_BIND_REASON_ASYNTAX;
+               reason = DCERPC_BIND_REASON_ASYNTAX;            
        }
 
-       if (call->dce->cli_max_recv_frag == 0) {
-               call->dce->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
+       if (call->conn->cli_max_recv_frag == 0) {
+               call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
+       }
+
+       /* handle any authentication that is being requested */
+       if (!dcesrv_auth_bind(call)) {
+               /* TODO: work out the right reject code */
+               return dcesrv_bind_nak(call, 0);
        }
 
        /* setup a bind_ack */
-       pkt.rpc_vers = 5;
-       pkt.rpc_vers_minor = 0;
-       pkt.drep[0] = 0x10; /* Little endian */
-       pkt.drep[1] = 0;
-       pkt.drep[2] = 0;
-       pkt.drep[3] = 0;
+       dcesrv_init_hdr(&pkt);
        pkt.auth_length = 0;
        pkt.call_id = call->pkt.call_id;
        pkt.ptype = DCERPC_PKT_BIND_ACK;
@@ -310,49 +493,133 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
        pkt.u.bind_ack.max_xmit_frag = 0x2000;
        pkt.u.bind_ack.max_recv_frag = 0x2000;
        pkt.u.bind_ack.assoc_group_id = call->pkt.u.bind.assoc_group_id;
-       if (call->dce->ndr) {
-               pkt.u.bind_ack.secondary_address = talloc_asprintf(call->mem_ctx, "\\PIPE\\%s", 
-                                                                  call->dce->ndr->name);
+       if (call->conn->iface) {
+               /* FIXME: Use pipe name as specified by endpoint instead of interface name */
+               pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", 
+                                                                  call->conn->iface->name);
        } else {
                pkt.u.bind_ack.secondary_address = "";
        }
        pkt.u.bind_ack.num_results = 1;
-       pkt.u.bind_ack.ctx_list = talloc_p(call->mem_ctx, struct dcerpc_ack_ctx);
+       pkt.u.bind_ack.ctx_list = talloc_p(call, struct dcerpc_ack_ctx);
        if (!pkt.u.bind_ack.ctx_list) {
                return NT_STATUS_NO_MEMORY;
        }
        pkt.u.bind_ack.ctx_list[0].result = result;
        pkt.u.bind_ack.ctx_list[0].reason = reason;
-       GUID_from_string(uuid, &pkt.u.bind_ack.ctx_list[0].syntax.uuid);
-       pkt.u.bind_ack.ctx_list[0].syntax.major_version = if_version;
-       pkt.u.bind_ack.ctx_list[0].syntax.minor_version = 0;
+       GUID_from_string(NDR_GUID, &pkt.u.bind_ack.ctx_list[0].syntax.uuid);
+       pkt.u.bind_ack.ctx_list[0].syntax.if_version = NDR_GUID_VERSION;
        pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
 
-       /* now form the NDR for the bind_ack */
-       push = ndr_push_init_ctx(call->mem_ctx);
-       if (!push) {
+       if (!dcesrv_auth_bind_ack(call, &pkt)) {
+               return dcesrv_bind_nak(call, 0);
+       }
+
+       if (call->conn->iface) {
+               status = call->conn->iface->bind(call, call->conn->iface);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n", uuid, if_version, nt_errstr(status)));
+                       return dcesrv_bind_nak(call, 0);
+               }
+       }
+
+       rep = talloc_p(call, struct dcesrv_call_reply);
+       if (!rep) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
+       status = dcerpc_push_auth(&rep->data, call, &pkt, 
+                                 call->conn->auth_state.auth_info);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
 
-       rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
+       dcerpc_set_frag_length(&rep->data, rep->data.length);
+
+       DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
+       DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
+
+       return NT_STATUS_OK;
+}
+
+
+/*
+  handle a auth3 request
+*/
+static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
+{
+       /* handle the auth3 in the auth code */
+       if (!dcesrv_auth_auth3(call)) {
+               return dcesrv_fault(call, DCERPC_FAULT_OTHER);
+       }
+
+       talloc_free(call);
+
+       /* we don't send a reply to a auth3 request, except by a
+          fault */
+       return NT_STATUS_OK;
+}
+
+/*
+  handle a bind request
+*/
+static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
+{
+       struct dcerpc_packet pkt;
+       struct dcesrv_call_reply *rep;
+       NTSTATUS status;
+       uint32_t result=0, reason=0;
+
+       /* handle any authentication that is being requested */
+       if (!dcesrv_auth_alter(call)) {
+               /* TODO: work out the right reject code */
+               return dcesrv_bind_nak(call, 0);
+       }
+
+       /* setup a alter_ack */
+       dcesrv_init_hdr(&pkt);
+       pkt.auth_length = 0;
+       pkt.call_id = call->pkt.call_id;
+       pkt.ptype = DCERPC_PKT_ALTER_ACK;
+       pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+       pkt.u.alter_ack.max_xmit_frag = 0x2000;
+       pkt.u.alter_ack.max_recv_frag = 0x2000;
+       pkt.u.alter_ack.assoc_group_id = call->pkt.u.bind.assoc_group_id;
+       pkt.u.alter_ack.secondary_address = NULL;
+       pkt.u.alter_ack.num_results = 1;
+       pkt.u.alter_ack.ctx_list = talloc_p(call, struct dcerpc_ack_ctx);
+       if (!pkt.u.alter_ack.ctx_list) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       pkt.u.alter_ack.ctx_list[0].result = result;
+       pkt.u.alter_ack.ctx_list[0].reason = reason;
+       GUID_from_string(NDR_GUID, &pkt.u.alter_ack.ctx_list[0].syntax.uuid);
+       pkt.u.alter_ack.ctx_list[0].syntax.if_version = NDR_GUID_VERSION;
+       pkt.u.alter_ack.auth_info = data_blob(NULL, 0);
+
+       if (!dcesrv_auth_alter_ack(call, &pkt)) {
+               return dcesrv_bind_nak(call, 0);
+       }
+
+       rep = talloc_p(call, struct dcesrv_call_reply);
        if (!rep) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       rep->data = ndr_push_blob(push);
-       SSVAL(rep->data.data,  DCERPC_FRAG_LEN_OFFSET, rep->data.length);
+       status = dcerpc_push_auth(&rep->data, call, &pkt, 
+                                 call->conn->auth_state.auth_info);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       dcerpc_set_frag_length(&rep->data, rep->data.length);
 
        DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
+       DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
 
        return NT_STATUS_OK;
 }
 
-
 /*
   handle a dcerpc request packet
 */
@@ -360,79 +627,96 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
 {
        struct ndr_pull *pull;
        struct ndr_push *push;
-       uint16 opnum;
        void *r;
        NTSTATUS status;
        DATA_BLOB stub;
+       uint32_t total_length;
 
-       opnum = call->pkt.u.request.opnum;
+       call->fault_code = 0;
 
-       if (opnum >= call->dce->ndr->num_calls) {
-               return dcesrv_fault(call, DCERPC_FAULT_OP_RNG_ERROR);
+       if (!call->conn->iface) {
+               return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
        }
 
-       pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call->mem_ctx);
+       pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call);
        if (!pull) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       r = talloc(call->mem_ctx, call->dce->ndr->calls[opnum].struct_size);
-       if (!r) {
-               return NT_STATUS_NO_MEMORY;
+       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;
        }
 
        /* unravel the NDR for the packet */
-       status = call->dce->ndr->calls[opnum].ndr_pull(pull, NDR_IN, r);
+       status = call->conn->iface->ndr_pull(call, call, pull, &r);
        if (!NT_STATUS_IS_OK(status)) {
-               return dcesrv_fault(call, DCERPC_FAULT_NDR);
+               return dcesrv_fault(call, call->fault_code);
+       }
+
+       if (pull->offset != pull->data_size) {
+               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 */
-       status = call->dce->dispatch[opnum](call->dce, call->mem_ctx, r);
+       status = call->conn->iface->dispatch(call, call, r);
        if (!NT_STATUS_IS_OK(status)) {
-               return dcesrv_fault_nt(call, status);
+               return dcesrv_fault(call, call->fault_code);
        }
 
        /* form the reply NDR */
-       push = ndr_push_init_ctx(call->mem_ctx);
+       push = ndr_push_init_ctx(call);
        if (!push) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       status = call->dce->ndr->calls[opnum].ndr_push(push, NDR_OUT, r);
+       /* carry over the pointer count to the reply in case we are
+          using full pointer. See NDR specification for full
+          pointers */
+       push->ptr_count = pull->ptr_count;
+
+       if (lp_rpc_big_endian()) {
+               push->flags |= LIBNDR_FLAG_BIGENDIAN;
+       }
+
+       status = call->conn->iface->ndr_push(call, call, push, r);
        if (!NT_STATUS_IS_OK(status)) {
-               return dcesrv_fault(call, DCERPC_FAULT_NDR);
+               return dcesrv_fault(call, call->fault_code);
        }
 
        stub = ndr_push_blob(push);
 
+       total_length = stub.length;
+
        do {
-               uint32 length;
+               uint32_t length;
                struct dcesrv_call_reply *rep;
                struct dcerpc_packet pkt;
 
-               rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
+               rep = talloc_p(call, struct dcesrv_call_reply);
                if (!rep) {
                        return NT_STATUS_NO_MEMORY;
                }
 
                length = stub.length;
-               if (length + DCERPC_RESPONSE_LENGTH > call->dce->cli_max_recv_frag) {
-                       length = call->dce->cli_max_recv_frag - DCERPC_RESPONSE_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 */
-               pkt.rpc_vers = 5;
-               pkt.rpc_vers_minor = 0;
-               pkt.drep[0] = 0x10; /* Little endian */
-               pkt.drep[1] = 0;
-               pkt.drep[2] = 0;
-               pkt.drep[3] = 0;
+               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 (!call->replies) {
+               if (stub.length == total_length) {
                        pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
                }
                if (length == stub.length) {
@@ -444,18 +728,11 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
                pkt.u.response.stub_and_verifier.data = stub.data;
                pkt.u.response.stub_and_verifier.length = length;
 
-               push = ndr_push_init_ctx(call->mem_ctx);
-               if (!push) {
-                       return NT_STATUS_NO_MEMORY;
+               if (!dcesrv_auth_response(call, &rep->data, &pkt)) {
+                       return dcesrv_fault(call, DCERPC_FAULT_OTHER);          
                }
-               
-               status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
-               }
-               
-               rep->data = ndr_push_blob(push);
-               SSVAL(rep->data.data,  DCERPC_FRAG_LEN_OFFSET, rep->data.length);
+
+               dcerpc_set_frag_length(&rep->data, rep->data.length);
 
                DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *);
                
@@ -463,50 +740,98 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
                stub.length -= length;
        } while (stub.length != 0);
 
+       DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
+
        return NT_STATUS_OK;
 }
 
 
 /*
-  provide some input to a dcerpc endpoint server. This passes data
-  from a dcerpc client into the server
+  work out if we have a full packet yet
+*/
+static BOOL dce_full_packet(const DATA_BLOB *data)
+{
+       if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
+               return False;
+       }
+       if (dcerpc_get_frag_length(data) > data->length) {
+               return False;
+       }
+       return True;
+}
+
+/*
+  we might have consumed only part of our input - advance past that part
 */
-NTSTATUS dcesrv_input(struct dcesrv_state *dce, const DATA_BLOB *data)
+static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
+{
+       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);
+}
+
+/*
+  process some input to a dcerpc endpoint server.
+*/
+NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
 {
        struct ndr_pull *ndr;
-       TALLOC_CTX *mem_ctx;
        NTSTATUS status;
        struct dcesrv_call_state *call;
+       DATA_BLOB blob;
 
-       mem_ctx = talloc_init("dcesrv_input");
-       if (!mem_ctx) {
-               return NT_STATUS_NO_MEMORY;
-       }
-       call = talloc_p(mem_ctx, struct dcesrv_call_state);
+       call = talloc_p(dce_conn, struct dcesrv_call_state);
        if (!call) {
-               talloc_destroy(mem_ctx);
+               talloc_free(dce_conn->partial_input.data);
                return NT_STATUS_NO_MEMORY;
        }
-       call->mem_ctx = mem_ctx;
-       call->dce = dce;
+       call->conn = dce_conn;
        call->replies = NULL;
 
-       ndr = ndr_pull_init_blob(data, mem_ctx);
+       blob = dce_conn->partial_input;
+       blob.length = dcerpc_get_frag_length(&blob);
+
+       ndr = ndr_pull_init_blob(&blob, call);
        if (!ndr) {
-               talloc_destroy(mem_ctx);
+               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_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
        if (!NT_STATUS_IS_OK(status)) {
-               talloc_destroy(mem_ctx);
+               talloc_free(dce_conn->partial_input.data);
+               talloc_free(call);
                return status;
        }
 
+       /* 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_LOGON_FAILURE);          
+       }
+
+       dce_partial_advance(dce_conn, blob.length);
+
        /* see if this is a continued packet */
-       if (!(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
+       if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
+           !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
                struct dcesrv_call_state *call2 = call;
-               uint32 alloc_size;
+               uint32_t alloc_size;
 
                /* we only allow fragmented requests, no other packet types */
                if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
@@ -515,7 +840,7 @@ NTSTATUS dcesrv_input(struct dcesrv_state *dce, const DATA_BLOB *data)
 
                /* this is a continuation of an existing call - find the call then
                   tack it on the end */
-               call = dcesrv_find_call(dce, call2->pkt.call_id);
+               call = dcesrv_find_call(dce_conn, call2->pkt.call_id);
                if (!call) {
                        return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
                }
@@ -532,8 +857,7 @@ NTSTATUS dcesrv_input(struct dcesrv_state *dce, const DATA_BLOB *data)
                }
 
                call->pkt.u.request.stub_and_verifier.data = 
-                       talloc_realloc(call->mem_ctx,
-                                      call->pkt.u.request.stub_and_verifier.data, alloc_size);
+                       talloc_realloc(call, call->pkt.u.request.stub_and_verifier.data, alloc_size);
                if (!call->pkt.u.request.stub_and_verifier.data) {
                        return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
                }
@@ -545,12 +869,15 @@ NTSTATUS dcesrv_input(struct dcesrv_state *dce, const DATA_BLOB *data)
                        call2->pkt.u.request.stub_and_verifier.length;
 
                call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
+
+               talloc_free(call2);
        }
 
        /* this may not be the last pdu in the chain - if its isn't then
           just put it on the call_list and wait for the rest */
-       if (!(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
-               DLIST_ADD_END(dce->call_list, call, struct dcesrv_call_state *);
+       if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
+           !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
+               DLIST_ADD_END(dce_conn->call_list, call, struct dcesrv_call_state *);
                return NT_STATUS_OK;
        }
 
@@ -558,6 +885,12 @@ NTSTATUS dcesrv_input(struct dcesrv_state *dce, const DATA_BLOB *data)
        case DCERPC_PKT_BIND:
                status = dcesrv_bind(call);
                break;
+       case DCERPC_PKT_AUTH3:
+               status = dcesrv_auth3(call);
+               break;
+       case DCERPC_PKT_ALTER:
+               status = dcesrv_alter(call);
+               break;
        case DCERPC_PKT_REQUEST:
                status = dcesrv_request(call);
                break;
@@ -569,48 +902,91 @@ NTSTATUS dcesrv_input(struct dcesrv_state *dce, const DATA_BLOB *data)
        /* if we are going to be sending a reply then add
           it to the list of pending calls. We add it to the end to keep the call
           list in the order we will answer */
-       if (NT_STATUS_IS_OK(status)) {
-               DLIST_ADD_END(dce->call_list, call, struct dcesrv_call_state *);
-       } else {
-               talloc_destroy(mem_ctx);
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(call);
        }
 
        return status;
 }
 
+
 /*
-  retrieve some output from a dcerpc server. The amount of data that
-  is wanted is in data->length and data->data is already allocated
-  to hold that much data.
+  provide some input to a dcerpc endpoint server. This passes data
+  from a dcerpc client into the server
 */
-NTSTATUS dcesrv_output(struct dcesrv_state *dce, DATA_BLOB *data)
+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,
+                                                     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 the number of bytes to be sent.
+
+  write_fn() should return the number of bytes successfully written.
+
+  this will return STATUS_BUFFER_OVERFLOW if there is more to be read
+  from the current fragment
+*/
+NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn, 
+                      void *private,
+                      ssize_t (*write_fn)(void *, DATA_BLOB *))
 {
        struct dcesrv_call_state *call;
        struct dcesrv_call_reply *rep;
+       ssize_t nwritten;
 
-       call = dce->call_list;
+       call = dce_conn->call_list;
        if (!call || !call->replies) {
                return NT_STATUS_FOOBAR;
        }
        rep = call->replies;
 
-       if (data->length >= rep->data.length) {
-               data->length = rep->data.length;
+       nwritten = write_fn(private, &rep->data);
+       if (nwritten == -1) {
+               /* TODO: hmm, how do we cope with this? destroy the
+                  connection perhaps? */
+               return NT_STATUS_UNSUCCESSFUL;
        }
 
-       memcpy(data->data, rep->data.data, data->length);
-       rep->data.length -= data->length;
-       rep->data.data += data->length;
+       rep->data.length -= nwritten;
+       rep->data.data += nwritten;
 
        if (rep->data.length == 0) {
                /* we're done with this section of the call */
                DLIST_REMOVE(call->replies, rep);
+       } else {
+               return STATUS_BUFFER_OVERFLOW;
        }
 
        if (call->replies == NULL) {
                /* we're done with the whole call */
-               DLIST_REMOVE(dce->call_list, call);
-               talloc_destroy(call->mem_ctx);
+               DLIST_REMOVE(dce_conn->call_list, call);
+               talloc_free(call);
        }
 
        return NT_STATUS_OK;
@@ -618,54 +994,239 @@ NTSTATUS dcesrv_output(struct dcesrv_state *dce, DATA_BLOB *data)
 
 
 /*
-  a useful function for implementing the query endpoint op
- */
-BOOL dcesrv_table_query(const struct dcerpc_interface_table *table,
-                       const struct dcesrv_endpoint *ep)
+  write_fn() for dcesrv_output_blob()
+*/
+static ssize_t dcesrv_output_blob_write_fn(void *private, DATA_BLOB *out)
+{
+       DATA_BLOB *blob = private;
+       if (out->length < blob->length) {
+               blob->length = out->length;
+       }
+       memcpy(blob->data, out->data, blob->length);
+       return blob->length;
+}
+
+/*
+  a simple wrapper for dcesrv_output() for when we want to output
+  into a data blob
+*/
+NTSTATUS dcesrv_output_blob(struct dcesrv_connection *dce_conn, 
+                           DATA_BLOB *blob)
+{
+       return dcesrv_output(dce_conn, blob, dcesrv_output_blob_write_fn);
+}
+
+/*
+  initialise the dcerpc server context
+*/
+NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, struct dcesrv_context **dce_ctx)
 {
        int i;
-       const struct dcerpc_endpoint_list *endpoints = table->endpoints;
+       const char **endpoint_servers = lp_dcerpc_endpoint_servers();
 
-       if (ep->type != ENDPOINT_SMB) {
-               return False;
+       (*dce_ctx) = talloc_p(mem_ctx, struct dcesrv_context);
+       if (! *dce_ctx) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       (*dce_ctx)->endpoint_list = NULL;
+
+       if (!endpoint_servers) {
+               DEBUG(3,("dcesrv_init_context: no endpoint servers configured\n"));
+               return NT_STATUS_OK;
        }
 
-       for (i=0;i<endpoints->count;i++) {
-               if (strcasecmp(ep->info.smb_pipe, endpoints->names[i]) == 0) {
-                       return True;
+       for (i=0;endpoint_servers[i];i++) {
+               NTSTATUS ret;
+               const struct dcesrv_endpoint_server *ep_server;
+               
+               ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
+               if (!ep_server) {
+                       DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+
+               ret = ep_server->init_server(*dce_ctx, ep_server);
+               if (!NT_STATUS_IS_OK(ret)) {
+                       DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s'\n", endpoint_servers[i]));
+                       return ret;
                }
        }
-       return False;
+
+       return NT_STATUS_OK;
 }
 
+static void dcesrv_init(struct server_service *service, const struct model_ops *model_ops)
+{
+       struct dcesrv_context *dce_ctx;
+       int i;
+       const char **endpoint_servers = lp_dcerpc_endpoint_servers();
 
-/*
-  a useful function for implementing the lookup_endpoints op
+       DEBUG(1,("dcesrv_init\n"));
+
+       if (!endpoint_servers) {
+               DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
+               return;
+       }
+
+       dce_ctx = talloc_p(service, struct dcesrv_context);
+       if (!dce_ctx) {
+               DEBUG(0,("talloc_p(mem_ctx, struct dcesrv_context) failed\n"));
+               return;
+       }
+
+       ZERO_STRUCTP(dce_ctx);
+       dce_ctx->endpoint_list  = NULL;
+
+       for (i=0;endpoint_servers[i];i++) {
+               NTSTATUS ret;
+               const struct dcesrv_endpoint_server *ep_server;
+               
+               ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
+               if (!ep_server) {
+                       DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
+                       return;
+               }
+
+               ret = ep_server->init_server(dce_ctx, ep_server);
+               if (!NT_STATUS_IS_OK(ret)) {
+                       DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s'\n", endpoint_servers[i]));
+                       return;
+               }
+       }
+
+       dcesrv_sock_init(service, model_ops, dce_ctx);
+
+       return; 
+}
+
+static void dcesrv_accept(struct server_connection *srv_conn)
+{
+       dcesrv_sock_accept(srv_conn);
+}
+
+static void dcesrv_recv(struct server_connection *srv_conn, 
+                       struct timeval t, uint16_t flags)
+{
+       dcesrv_sock_recv(srv_conn, t, flags);
+}
+
+static void dcesrv_send(struct server_connection *srv_conn, 
+                       struct timeval t, uint16_t flags)
+{
+       dcesrv_sock_send(srv_conn, t, flags);
+}
+
+static void dcesrv_close(struct server_connection *srv_conn, const char *reason)
+{
+       dcesrv_sock_close(srv_conn, reason);
+       return; 
+}
+
+static void dcesrv_exit(struct server_service *service, const char *reason)
+{
+       dcesrv_sock_exit(service, reason);
+       return; 
+}
+
+/* the list of currently registered DCERPC endpoint servers.
  */
-int dcesrv_lookup_endpoints(const struct dcerpc_interface_table *table,
-                           TALLOC_CTX *mem_ctx,
-                           struct dcesrv_ep_iface **e)
+static struct ep_server {
+       struct dcesrv_endpoint_server *ep_server;
+} *ep_servers = NULL;
+static int num_ep_servers;
+
+/*
+  register a DCERPC endpoint server. 
+
+  The 'name' can be later used by other backends to find the operations
+  structure for this backend.  
+
+  The 'type' is used to specify whether this is for a disk, printer or IPC$ share
+*/
+NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
 {
-       *e = talloc_p(mem_ctx, struct dcesrv_ep_iface);
-       if (! *e) {
-               return -1;
+       const struct dcesrv_endpoint_server *ep_server = _ep_server;
+       
+       if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
+               /* its already registered! */
+               DEBUG(0,("DCERPC endpoint server '%s' already registered\n", 
+                        ep_server->name));
+               return NT_STATUS_OBJECT_NAME_COLLISION;
        }
 
-       (*e)->uuid = table->uuid;
-       (*e)->if_version = table->if_version;
-       (*e)->endpoint.type = ENDPOINT_SMB;
-       (*e)->endpoint.info.smb_pipe = table->endpoints->names[0];
+       ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
+       if (!ep_servers) {
+               smb_panic("out of memory in dcerpc_register");
+       }
 
-       return 1;
+       ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
+       ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
+
+       num_ep_servers++;
+
+       DEBUG(3,("DCERPC endpoint server '%s' registered\n", 
+                ep_server->name));
+
+       return NT_STATUS_OK;
 }
 
+/*
+  return the operations structure for a named backend of the specified type
+*/
+const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
+{
+       int i;
+
+       for (i=0;i<num_ep_servers;i++) {
+               if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
+                       return ep_servers[i].ep_server;
+               }
+       }
+
+       return NULL;
+}
 
 /*
-  initialise the dcerpc server subsystem
+  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
+  multiple implementations for different smbd compilation options in one module
 */
-BOOL dcesrv_init(struct server_context *smb)
+const struct dcesrv_critical_sizes *dcerpc_module_version(void)
 {
-       rpc_echo_init(smb);
-       rpc_epmapper_init(smb);
-       return True;
+       static const struct dcesrv_critical_sizes critical_sizes = {
+               DCERPC_MODULE_VERSION,
+               sizeof(struct dcesrv_context),
+               sizeof(struct dcesrv_endpoint),
+               sizeof(struct dcesrv_endpoint_server),
+               sizeof(struct dcesrv_interface),
+               sizeof(struct dcesrv_if_list),
+               sizeof(struct dcesrv_connection),
+               sizeof(struct dcesrv_call_state),
+               sizeof(struct dcesrv_auth),
+               sizeof(struct dcesrv_handle)
+       };
+
+       return &critical_sizes;
+}
+
+static const struct server_service_ops dcesrv_ops = {
+       .name                   = "rpc",
+       .service_init           = dcesrv_init,
+       .accept_connection      = dcesrv_accept,
+       .recv_handler           = dcesrv_recv,
+       .send_handler           = dcesrv_send,
+       .idle_handler           = NULL,
+       .close_connection       = dcesrv_close,
+       .service_exit           = dcesrv_exit,  
+};
+
+const struct server_service_ops *dcesrv_get_ops(void)
+{
+       return &dcesrv_ops;
+}
+
+NTSTATUS server_service_rpc_init(void)
+{
+       return NT_STATUS_OK;    
 }