summaryrefslogtreecommitdiff
path: root/source4/rpc_server
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2003-12-13 02:20:40 +0000
committerAndrew Tridgell <tridge@samba.org>2003-12-13 02:20:40 +0000
commit340d9b71f9e75d634389104da5949ba59669ede2 (patch)
tree1bc2c22f57eade574841d8e4d9f3f5a5ff1c1022 /source4/rpc_server
parentf7065cc0a5555a32499908a499f926ede3f7d851 (diff)
downloadsamba-340d9b71f9e75d634389104da5949ba59669ede2.tar.gz
samba-340d9b71f9e75d634389104da5949ba59669ede2.tar.bz2
samba-340d9b71f9e75d634389104da5949ba59669ede2.zip
added a basic dcerpc endpoint mapper to Samba4. Currently only
implements the epm_Lookup() call, I'll add the other important calls soon. I was rather pleased to find that epm_Lookup() worked first time, which is particularly surprising given its complexity. This required quite a bit of new infrastructure: * a generic way of handling dcerpc policy handles in the rpc server * added type checked varients of talloc. These are much less error prone. I'd like to move to using these for nearly all uses of talloc. * added more dcerpc fault handling code, and translation from NTSTATUS to a dcerpc fault code * added data_blob_talloc_zero() for allocating an initially zero blob * added a endpoint enumeration hook in the dcerpc endpoint server operations (This used to be commit 3f85f9b782dc17417baf1ca557fcae22f5b6a83a)
Diffstat (limited to 'source4/rpc_server')
-rw-r--r--source4/rpc_server/dcerpc_server.c64
-rw-r--r--source4/rpc_server/dcerpc_server.h31
-rw-r--r--source4/rpc_server/echo/rpc_echo.c8
-rw-r--r--source4/rpc_server/epmapper/rpc_epmapper.c274
-rw-r--r--source4/rpc_server/handles.c92
5 files changed, 459 insertions, 10 deletions
diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c
index 4711f4e6ff..e2a6ab0132 100644
--- a/source4/rpc_server/dcerpc_server.c
+++ b/source4/rpc_server/dcerpc_server.c
@@ -89,12 +89,13 @@ NTSTATUS dcesrv_endpoint_connect(struct server_context *smb,
return NT_STATUS_NO_MEMORY;
}
- *p = talloc(mem_ctx, sizeof(struct dcesrv_state));
+ *p = talloc_p(mem_ctx, struct dcesrv_state);
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;
@@ -103,6 +104,7 @@ NTSTATUS dcesrv_endpoint_connect(struct server_context *smb,
(*p)->cli_max_recv_frag = 0;
(*p)->ndr = NULL;
(*p)->dispatch = NULL;
+ (*p)->handles = NULL;
/* make sure the endpoint server likes the connection */
status = ops->connect(*p);
@@ -121,6 +123,14 @@ NTSTATUS dcesrv_endpoint_connect(struct server_context *smb,
void dcesrv_endpoint_disconnect(struct dcesrv_state *p)
{
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);
+ }
+
talloc_destroy(p->mem_ctx);
}
@@ -161,7 +171,7 @@ static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32 fault_code)
return status;
}
- rep = talloc(call->mem_ctx, sizeof(*rep));
+ rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
if (!rep) {
return NT_STATUS_NO_MEMORY;
}
@@ -176,6 +186,22 @@ static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32 fault_code)
/*
+ 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)
@@ -210,7 +236,7 @@ static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32 reason)
return status;
}
- rep = talloc(call->mem_ctx, sizeof(*rep));
+ rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
if (!rep) {
return NT_STATUS_NO_MEMORY;
}
@@ -290,7 +316,7 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
pkt.u.bind_ack.secondary_address = "";
}
pkt.u.bind_ack.num_results = 1;
- pkt.u.bind_ack.ctx_list = talloc(call->mem_ctx, sizeof(struct dcerpc_ack_ctx));
+ pkt.u.bind_ack.ctx_list = talloc_p(call->mem_ctx, struct dcerpc_ack_ctx);
if (!pkt.u.bind_ack.ctx_list) {
return NT_STATUS_NO_MEMORY;
}
@@ -312,7 +338,7 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
return status;
}
- rep = talloc(call->mem_ctx, sizeof(*rep));
+ rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
if (!rep) {
return NT_STATUS_NO_MEMORY;
}
@@ -363,7 +389,7 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
/* call the dispatch function */
status = call->dce->dispatch[opnum](call->dce, call->mem_ctx, r);
if (!NT_STATUS_IS_OK(status)) {
- return dcesrv_fault(call, DCERPC_FAULT_NDR);
+ return dcesrv_fault_nt(call, status);
}
/* form the reply NDR */
@@ -384,7 +410,7 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
struct dcesrv_call_reply *rep;
struct dcerpc_packet pkt;
- rep = talloc(call->mem_ctx, sizeof(*rep));
+ rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply);
if (!rep) {
return NT_STATUS_NO_MEMORY;
}
@@ -455,7 +481,7 @@ NTSTATUS dcesrv_input(struct dcesrv_state *dce, const DATA_BLOB *data)
if (!mem_ctx) {
return NT_STATUS_NO_MEMORY;
}
- call = talloc(mem_ctx, sizeof(*call));
+ call = talloc_p(mem_ctx, struct dcesrv_call_state);
if (!call) {
talloc_destroy(mem_ctx);
return NT_STATUS_NO_MEMORY;
@@ -613,10 +639,32 @@ BOOL dcesrv_table_query(const struct dcerpc_interface_table *table,
/*
+ a useful function for implementing the lookup_endpoints op
+ */
+int dcesrv_lookup_endpoints(const struct dcerpc_interface_table *table,
+ TALLOC_CTX *mem_ctx,
+ struct dcesrv_ep_iface **e)
+{
+ *e = talloc_p(mem_ctx, struct dcesrv_ep_iface);
+ if (! *e) {
+ return -1;
+ }
+
+ (*e)->uuid = table->uuid;
+ (*e)->if_version = table->if_version;
+ (*e)->endpoint.type = ENDPOINT_SMB;
+ (*e)->endpoint.info.smb_pipe = table->endpoints->names[0];
+
+ return 1;
+}
+
+
+/*
initialise the dcerpc server subsystem
*/
BOOL dcesrv_init(struct server_context *smb)
{
rpc_echo_init(smb);
+ rpc_epmapper_init(smb);
return True;
}
diff --git a/source4/rpc_server/dcerpc_server.h b/source4/rpc_server/dcerpc_server.h
index 165eddc5e6..3f2f5d039f 100644
--- a/source4/rpc_server/dcerpc_server.h
+++ b/source4/rpc_server/dcerpc_server.h
@@ -23,7 +23,8 @@
enum endpoint_type {ENDPOINT_SMB, ENDPOINT_TCP};
-/* a description of a single dcerpc endpoint */
+/* a description of a single dcerpc endpoint. Not as flexible as a full epm tower,
+ but much easier to work with */
struct dcesrv_endpoint {
enum endpoint_type type;
union {
@@ -32,6 +33,13 @@ struct dcesrv_endpoint {
} info;
};
+/* a endpoint combined with an interface description */
+struct dcesrv_ep_iface {
+ struct dcesrv_endpoint endpoint;
+ const char *uuid;
+ uint32 if_version;
+};
+
struct dcesrv_state;
/* the dispatch functions for an interface take this form */
@@ -50,8 +58,20 @@ struct dcesrv_call_state {
} *replies;
};
+
+/* a dcerpc handle in internal format */
+struct dcesrv_handle {
+ struct dcesrv_handle *next, *prev;
+ struct policy_handle wire_handle;
+ TALLOC_CTX *mem_ctx;
+ void *data;
+};
+
/* the state associated with a dcerpc server connection */
struct dcesrv_state {
+ /* the top level context for this server */
+ struct server_context *smb;
+
TALLOC_CTX *mem_ctx;
/* the endpoint that was opened */
@@ -75,6 +95,11 @@ struct dcesrv_state {
/* private data for the endpoint server */
void *private;
+
+ /* current rpc handles - this is really the wrong scope for
+ them, but it will do for now */
+ uint32 next_handle;
+ struct dcesrv_handle *handles;
};
@@ -92,6 +117,10 @@ struct dcesrv_endpoint_ops {
/* disconnect() is called when the endpoint is disconnected */
void (*disconnect)(struct dcesrv_state *);
+
+ /* this function is used to ask an endpoint server for a list
+ of endpoints it wants to handle */
+ int (*lookup_endpoints)(TALLOC_CTX *mem_ctx, struct dcesrv_ep_iface **);
};
diff --git a/source4/rpc_server/echo/rpc_echo.c b/source4/rpc_server/echo/rpc_echo.c
index 42856737c4..f741e63d62 100644
--- a/source4/rpc_server/echo/rpc_echo.c
+++ b/source4/rpc_server/echo/rpc_echo.c
@@ -163,11 +163,17 @@ static void op_disconnect(struct dcesrv_state *dce)
}
+static int op_lookup_endpoints(TALLOC_CTX *mem_ctx, struct dcesrv_ep_iface **e)
+{
+ return dcesrv_lookup_endpoints(&dcerpc_table_rpcecho, mem_ctx, e);
+}
+
static const struct dcesrv_endpoint_ops rpc_echo_ops = {
op_query_endpoint,
op_set_interface,
op_connect,
- op_disconnect
+ op_disconnect,
+ op_lookup_endpoints
};
/*
diff --git a/source4/rpc_server/epmapper/rpc_epmapper.c b/source4/rpc_server/epmapper/rpc_epmapper.c
new file mode 100644
index 0000000000..945823c013
--- /dev/null
+++ b/source4/rpc_server/epmapper/rpc_epmapper.c
@@ -0,0 +1,274 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the epmapper pipe
+
+ Copyright (C) Andrew Tridgell 2003
+
+ 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
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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.
+*/
+
+#include "includes.h"
+
+
+/* handle types for this module */
+enum handle_types {HTYPE_LOOKUP};
+
+static NTSTATUS epm_Insert(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx,
+ struct epm_Lookup *r)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS epm_Delete(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx,
+ struct epm_Lookup *r)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ implement epm_Lookup. This call is used to enumerate the interfaces
+ available on a rpc server
+*/
+static NTSTATUS epm_Lookup(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx,
+ struct epm_Lookup *r)
+{
+ struct dcesrv_handle *h;
+ struct rpc_eps {
+ uint32 count;
+ struct dcesrv_ep_iface *e;
+ } *eps;
+ uint32 num_ents;
+ int i;
+
+ h = dcesrv_handle_fetch(dce, r->in.entry_handle, HTYPE_LOOKUP);
+ if (!h) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ eps = h->data;
+
+ if (!eps) {
+ /* this is the first call - fill the list. Subsequent calls
+ will feed from this list, stored in the handle */
+ struct dce_endpoint *d;
+ struct dcesrv_ep_iface *e;
+
+ eps = talloc_p(h->mem_ctx, struct rpc_eps);
+ if (!eps) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ eps->count = 0;
+ eps->e = NULL;
+ h->data = eps;
+
+ for (d=dce->smb->dcesrv.endpoint_list; d; d=d->next) {
+ int count = d->endpoint_ops->lookup_endpoints(h->mem_ctx, &e);
+ if (count > 0) {
+ eps->e = talloc_realloc_p(h->mem_ctx,
+ eps->e,
+ struct dcesrv_ep_iface,
+ eps->count + count);
+ if (!eps->e) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(eps->e + eps->count, e, sizeof(*e) * count);
+ eps->count += count;
+ }
+ }
+ }
+
+ /* return the next N elements */
+ num_ents = r->in.max_ents;
+ if (num_ents > eps->count) {
+ num_ents = eps->count;
+ }
+
+ *r->out.entry_handle = h->wire_handle;
+ r->out.num_ents = num_ents;
+ r->out.status = 0;
+
+ if (num_ents == 0) {
+ r->out.entries = NULL;
+ return NT_STATUS_OK;
+ }
+
+ r->out.entries = talloc_array_p(mem_ctx, struct epm_entry_t, num_ents);
+ if (!r->out.entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<num_ents;i++) {
+ struct epm_twr_t *t;
+ struct epm_towers *twr;
+
+ ZERO_STRUCT(r->out.entries[i].object);
+ r->out.entries[i].annotation = "";
+ t = talloc_p(mem_ctx, struct epm_twr_t);
+ if (!twr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.entries[i].tower = t;
+ twr = &t->towers;
+ twr->num_floors = 5;
+ twr->floors = talloc_array_p(mem_ctx, struct epm_floor, 5);
+ if (!twr->floors) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ twr->floors[0].lhs.protocol = EPM_PROTOCOL_UUID;
+ GUID_from_string(eps->e[i].uuid, &twr->floors[0].lhs.info.uuid.uuid);
+ twr->floors[0].lhs.info.uuid.version = eps->e[i].if_version;
+ twr->floors[0].rhs.rhs_data = data_blob_talloc_zero(mem_ctx, 2);
+
+ /* encoded with NDR ... */
+ twr->floors[1].lhs.protocol = EPM_PROTOCOL_UUID;
+ GUID_from_string(NDR_GUID, &twr->floors[1].lhs.info.uuid.uuid);
+ twr->floors[1].lhs.info.uuid.version = NDR_GUID_VERSION;
+ twr->floors[1].rhs.rhs_data = data_blob_talloc_zero(mem_ctx, 2);
+
+ /* on an RPC connection ... */
+ twr->floors[2].lhs.protocol = EPM_PROTOCOL_RPC_C;
+ twr->floors[2].lhs.info.lhs_data = data_blob(NULL, 0);
+ twr->floors[2].rhs.rhs_data = data_blob_talloc_zero(mem_ctx, 2);
+
+ /* on a SMB pipe ... */
+ twr->floors[3].lhs.protocol = EPM_PROTOCOL_SMB;
+ twr->floors[3].lhs.info.lhs_data = data_blob(NULL, 0);
+ twr->floors[3].rhs.rhs_data.data = talloc_asprintf(mem_ctx, "\\PIPE\\%s",
+ eps->e[i].endpoint.info.smb_pipe);
+ twr->floors[3].rhs.rhs_data.length = strlen(twr->floors[3].rhs.rhs_data.data);
+
+ /* on an NetBIOS link ... */
+ twr->floors[4].lhs.protocol = EPM_PROTOCOL_NETBIOS;
+ twr->floors[4].lhs.info.lhs_data = data_blob(NULL, 0);
+ twr->floors[4].rhs.rhs_data.data = talloc_asprintf(mem_ctx, "\\\\%s",
+ lp_netbios_name());
+ twr->floors[4].rhs.rhs_data.length = strlen(twr->floors[4].rhs.rhs_data.data);
+ }
+
+ eps->count -= num_ents;
+ eps->e += num_ents;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ implement epm_Map. This is used to find the specific endpoint to talk to given
+ a generic protocol tower
+*/
+static NTSTATUS epm_Map(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx,
+ struct epm_Lookup *r)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS epm_LookupHandleFree(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx,
+ struct epm_Lookup *r)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS epm_InqObject(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx,
+ struct epm_Lookup *r)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS epm_MgmtDelete(struct dcesrv_state *dce, TALLOC_CTX *mem_ctx,
+ struct epm_Lookup *r)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/**************************************************************************
+ all the code below this point is boilerplate that will be auto-generated
+***************************************************************************/
+
+static const dcesrv_dispatch_fn_t dispatch_table[] = {
+ (dcesrv_dispatch_fn_t)epm_Insert,
+ (dcesrv_dispatch_fn_t)epm_Delete,
+ (dcesrv_dispatch_fn_t)epm_Lookup,
+ (dcesrv_dispatch_fn_t)epm_Map,
+ (dcesrv_dispatch_fn_t)epm_LookupHandleFree,
+ (dcesrv_dispatch_fn_t)epm_InqObject,
+ (dcesrv_dispatch_fn_t)epm_MgmtDelete
+};
+
+
+/*
+ return True if we want to handle the given endpoint
+*/
+static BOOL op_query_endpoint(const struct dcesrv_endpoint *ep)
+{
+ return dcesrv_table_query(&dcerpc_table_epmapper, ep);
+}
+
+/*
+ setup for a particular rpc interface
+*/
+static BOOL op_set_interface(struct dcesrv_state *dce, const char *uuid, uint32 if_version)
+{
+ if (strcasecmp(uuid, dcerpc_table_epmapper.uuid) != 0 ||
+ if_version != dcerpc_table_epmapper.if_version) {
+ DEBUG(2,("Attempt to use unknown interface %s/%d\n", uuid, if_version));
+ return False;
+ }
+
+ dce->ndr = &dcerpc_table_epmapper;
+ dce->dispatch = dispatch_table;
+
+ return True;
+}
+
+
+/* op_connect is called when a connection is made to an endpoint */
+static NTSTATUS op_connect(struct dcesrv_state *dce)
+{
+ return NT_STATUS_OK;
+}
+
+static void op_disconnect(struct dcesrv_state *dce)
+{
+ /* nothing to do */
+}
+
+
+static int op_lookup_endpoints(TALLOC_CTX *mem_ctx, struct dcesrv_ep_iface **e)
+{
+ return dcesrv_lookup_endpoints(&dcerpc_table_epmapper, mem_ctx, e);
+}
+
+
+static const struct dcesrv_endpoint_ops rpc_epmapper_ops = {
+ op_query_endpoint,
+ op_set_interface,
+ op_connect,
+ op_disconnect,
+ op_lookup_endpoints
+};
+
+/*
+ register with the dcerpc server
+*/
+void rpc_epmapper_init(struct server_context *smb)
+{
+ if (!dcesrv_endpoint_register(smb, &rpc_epmapper_ops)) {
+ DEBUG(1,("Failed to register epmapper endpoint\n"));
+ }
+}
diff --git a/source4/rpc_server/handles.c b/source4/rpc_server/handles.c
new file mode 100644
index 0000000000..16e4175184
--- /dev/null
+++ b/source4/rpc_server/handles.c
@@ -0,0 +1,92 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ server side dcerpc handle code
+
+ Copyright (C) Andrew Tridgell 2003
+
+ 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
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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.
+*/
+
+#include "includes.h"
+
+/*
+ allocate a new rpc handle
+*/
+struct dcesrv_handle *dcesrv_handle_new(struct dcesrv_state *dce,
+ uint8 handle_type)
+{
+ TALLOC_CTX *mem_ctx;
+ struct dcesrv_handle *h;
+
+ mem_ctx = talloc_init("rpc handle type %d\n", handle_type);
+ if (!mem_ctx) {
+ return NULL;
+ }
+ h = talloc(mem_ctx, sizeof(*h));
+ if (!h) {
+ talloc_destroy(mem_ctx);
+ return NULL;
+ }
+ h->mem_ctx = mem_ctx;
+ h->data = NULL;
+
+ memset(h->wire_handle.data, 'H', sizeof(h->wire_handle.data));
+ strncpy(h->wire_handle.data, dce->ndr->name, 11);
+ h->wire_handle.data[11] = handle_type;
+
+ /* TODO: check for wraparound here */
+ SIVAL(&h->wire_handle.data, 12, random());
+ SIVAL(&h->wire_handle.data, 16, dce->next_handle);
+ dce->next_handle++;
+
+ DLIST_ADD(dce->handles, h);
+
+ return h;
+}
+
+/*
+ destroy a rpc handle
+*/
+void dcesrv_handle_destroy(struct dcesrv_state *dce,
+ struct dcesrv_handle *h)
+{
+ DLIST_REMOVE(dce->handles, h);
+ talloc_destroy(h->mem_ctx);
+}
+
+
+/*
+ find an internal handle given a wire handle. If the wire handle is NULL then
+ allocate a new handle
+*/
+struct dcesrv_handle *dcesrv_handle_fetch(struct dcesrv_state *dce,
+ struct policy_handle *p,
+ uint8 handle_type)
+{
+ struct dcesrv_handle *h;
+
+ if (all_zero(p->data, sizeof(p->data))) {
+ return dcesrv_handle_new(dce, handle_type);
+ }
+
+ for (h=dce->handles; h; h=h->next) {
+ if (memcmp(h->wire_handle.data, p->data, sizeof(p->data)) == 0) {
+ return h;
+ }
+ }
+
+ return NULL;
+}