This should be about the last of the stuff from Ove's partial RPC implementation. I'm not sure this should, or will, go in as-is, as it includes the controversial wineserver portions. From Jürgen Schmied's RPC patches. Minor changes may exist between this and the original patch but it's intended to be basically the same stuff. LICENSE: X11 CHANGELOG: * dlls/rpcrt4: rpcrt4.spec, Makefile.in, rpc_epmap.c (new); include/wine/server_protocol.h; server: Makefile.in, trace.c, request.h, protocol.def, rpc_epmap.c (new): Ove Kåven <ovek@transgaming.com> - implement RpcEpRegisterA, RpcEpResolveBinding, and RpcEpUnregister - extend the wineserver to do endpoint mapping =============== Notes on this: So far, I've tested the "hello world" example from the latest Platform SDK (compiled native). The server bumps up against the lack of a RpcMgmtWaitServerListen() function: greg@yodull midled $ wine hellos.exe fixme:win32:PE_CreateModule Security directory ignored RpcServerUseProtseqEp returned 0x0 RpcServerRegisterIf returned 0x0 Calling RpcServerListen fixme:ole:RpcServerListen can't wait yet RpcServerListen returned: 0x0 The client fails due to the stubby RPCRT4_NdrClientCall2, and raises some exception: greg@yodull midled $ wine -- helloc -n 10.10.1.96 fixme:win32:PE_CreateModule Security directory ignored RpcStringBindingCompose returned 0x0 pszStringBinding = ncacn_np:10.10.1.96[\pipe\hello] RpcBindingFromStringBinding returned 0x0 Calling the remote procedure 'HelloProc' Print the string 'hello, world' on the server fixme:ole:RPCRT4_NdrClientCall2 (0x4020c2,0x406f2d90,...) Runtime reported exception 0xc0000005 = -1073741819 RpcStringFree returned 0x0 RpcBindingFree returned 0x0 Not sure I'm even using the right transport ("\pipe\hello" looks about right tho), I've just begun to play with it... nevertheless, here you are. I'll do a final audit of the patches to make sure nothing was left out from the originals. Barring that, further work will attempt to start filling in these gaps, extend widl to provide for RPC needs, and establish some meaningful tests, or at least that's the direction I'd like to head in if I prove sufficiently competent :) Any advice, help, tips, harsh criticism, or kind words would be appreciated. Have fun, -- gmt "It has been well said that really up-to-date liberals do not care what people do, as long as it is compulsory." -George F. Will
Index: dlls/rpcrt4/Makefile.in =================================================================== RCS file: /home/wine/wine/dlls/rpcrt4/Makefile.in,v retrieving revision 1.18 diff -u -r1.18 Makefile.in --- dlls/rpcrt4/Makefile.in 11 Oct 2002 18:45:02 -0000 1.18 +++ dlls/rpcrt4/Makefile.in 12 Oct 2002 18:41:02 -0000 @@ -20,6 +20,7 @@ rpc_binding.c \ rpc_message.c \ rpc_server.c \ + rpc_epmap.c \ rpcrt4_main.c SUBDIRS = tests Index: dlls/rpcrt4/rpcrt4.spec =================================================================== RCS file: /home/wine/wine/dlls/rpcrt4/rpcrt4.spec,v retrieving revision 1.29 diff -u -r1.29 rpcrt4.spec --- dlls/rpcrt4/rpcrt4.spec 11 Oct 2002 18:45:02 -0000 1.29 +++ dlls/rpcrt4/rpcrt4.spec 12 Oct 2002 18:41:02 -0000 @@ -55,12 +55,12 @@ @ stub RpcCertGeneratePrincipalNameA @ stub RpcCertGeneratePrincipalNameW @ stub RpcCompleteAsyncCall -@ stub RpcEpRegisterA +@ stdcall RpcEpRegisterA(ptr ptr ptr str) RpcEpRegisterA @ stub RpcEpRegisterW @ stub RpcEpRegisterNoReplaceA @ stub RpcEpRegisterNoReplaceW -@ stub RpcEpResolveBinding -@ stub RpcEpUnregister +@ stdcall RpcEpResolveBinding(ptr ptr) RpcEpResolveBinding +@ stdcall RpcEpUnregister(ptr ptr ptr) RpcEpUnregister @ stub RpcGetAsyncCallStatus @ stub RpcIfIdVectorFree @ stub RpcIfInqId Index: include/wine/server_protocol.h =================================================================== RCS file: /home/wine/wine/include/wine/server_protocol.h,v retrieving revision 1.44 diff -u -r1.44 server_protocol.h --- include/wine/server_protocol.h 12 Oct 2002 01:24:37 -0000 1.44 +++ include/wine/server_protocol.h 12 Oct 2002 18:41:03 -0000 @@ -2731,6 +2731,48 @@ +struct register_rpc_endpoints_request +{ + struct request_header __header; + int objects; + int bindings; + int no_replace; + /* VARARG(eps,bytes); */ +}; +struct register_rpc_endpoints_reply +{ + struct reply_header __header; +}; + + + +struct unregister_rpc_endpoints_request +{ + struct request_header __header; + int objects; + int bindings; + /* VARARG(eps,bytes); */ +}; +struct unregister_rpc_endpoints_reply +{ + struct reply_header __header; +}; + + + +struct resolve_rpc_endpoint_request +{ + struct request_header __header; + /* VARARG(binding,bytes); */ +}; +struct resolve_rpc_endpoint_reply +{ + struct reply_header __header; + /* VARARG(ep,bytes); */ +}; + + + struct attach_thread_input_request { struct request_header __header; @@ -2976,6 +3018,9 @@ REQ_remove_window_property, REQ_get_window_property, REQ_get_window_properties, + REQ_register_rpc_endpoints, + REQ_unregister_rpc_endpoints, + REQ_resolve_rpc_endpoint, REQ_attach_thread_input, REQ_get_thread_input, REQ_set_foreground_window, @@ -3145,6 +3190,9 @@ struct remove_window_property_request remove_window_property_request; struct get_window_property_request get_window_property_request; struct get_window_properties_request get_window_properties_request; + struct register_rpc_endpoints_request register_rpc_endpoints_request; + struct unregister_rpc_endpoints_request unregister_rpc_endpoints_request; + struct resolve_rpc_endpoint_request resolve_rpc_endpoint_request; struct attach_thread_input_request attach_thread_input_request; struct get_thread_input_request get_thread_input_request; struct set_foreground_window_request set_foreground_window_request; @@ -3312,6 +3360,9 @@ struct remove_window_property_reply remove_window_property_reply; struct get_window_property_reply get_window_property_reply; struct get_window_properties_reply get_window_properties_reply; + struct register_rpc_endpoints_reply register_rpc_endpoints_reply; + struct unregister_rpc_endpoints_reply unregister_rpc_endpoints_reply; + struct resolve_rpc_endpoint_reply resolve_rpc_endpoint_reply; struct attach_thread_input_reply attach_thread_input_reply; struct get_thread_input_reply get_thread_input_reply; struct set_foreground_window_reply set_foreground_window_reply; @@ -3320,6 +3371,6 @@ struct set_capture_window_reply set_capture_window_reply; }; -#define SERVER_PROTOCOL_VERSION 87 +#define SERVER_PROTOCOL_VERSION 88 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ Index: server/Makefile.in =================================================================== RCS file: /home/wine/wine/server/Makefile.in,v retrieving revision 1.34 diff -u -r1.34 Makefile.in --- server/Makefile.in 17 Aug 2002 01:19:06 -0000 1.34 +++ server/Makefile.in 12 Oct 2002 18:41:04 -0000 @@ -29,6 +29,7 @@ queue.c \ registry.c \ request.c \ + rpc_epmap.c \ select.c \ semaphore.c \ serial.c \ Index: server/protocol.def =================================================================== RCS file: /home/wine/wine/server/protocol.def,v retrieving revision 1.45 diff -u -r1.45 protocol.def --- server/protocol.def 12 Oct 2002 01:24:37 -0000 1.45 +++ server/protocol.def 12 Oct 2002 18:41:05 -0000 @@ -1911,6 +1911,31 @@ @END +/* Register RPC endpoints */ +@REQ(register_rpc_endpoints) + int objects; /* number of objects */ + int bindings; /* number of bindings */ + int no_replace; /* don't replace existing endpoints */ + VARARG(eps,bytes); /* list of bindings and objects */ +@END + + +/* Unregister RPC endpoints */ +@REQ(unregister_rpc_endpoints) + int objects; /* number of objects */ + int bindings; /* number of bindings */ + VARARG(eps,bytes); /* list of bindings and objects */ +@END + + +/* Resolve RPC endpoint */ +@REQ(resolve_rpc_endpoint) + VARARG(binding,bytes); /* unmapped binding */ +@REPLY + VARARG(ep,bytes); /* mapped endpoint */ +@END + + /* Attach (or detach) thread inputs */ @REQ(attach_thread_input) thread_id_t tid_from; /* thread to be attached */ Index: server/request.h =================================================================== RCS file: /home/wine/wine/server/request.h,v retrieving revision 1.76 diff -u -r1.76 request.h --- server/request.h 12 Oct 2002 01:24:37 -0000 1.76 +++ server/request.h 12 Oct 2002 18:41:05 -0000 @@ -259,6 +259,9 @@ DECL_HANDLER(remove_window_property); DECL_HANDLER(get_window_property); DECL_HANDLER(get_window_properties); +DECL_HANDLER(register_rpc_endpoints); +DECL_HANDLER(unregister_rpc_endpoints); +DECL_HANDLER(resolve_rpc_endpoint); DECL_HANDLER(attach_thread_input); DECL_HANDLER(get_thread_input); DECL_HANDLER(set_foreground_window); @@ -427,6 +430,9 @@ (req_handler)req_remove_window_property, (req_handler)req_get_window_property, (req_handler)req_get_window_properties, + (req_handler)req_register_rpc_endpoints, + (req_handler)req_unregister_rpc_endpoints, + (req_handler)req_resolve_rpc_endpoint, (req_handler)req_attach_thread_input, (req_handler)req_get_thread_input, (req_handler)req_set_foreground_window, Index: server/trace.c =================================================================== RCS file: /home/wine/wine/server/trace.c,v retrieving revision 1.141 diff -u -r1.141 trace.c --- server/trace.c 12 Oct 2002 01:24:37 -0000 1.141 +++ server/trace.c 12 Oct 2002 18:41:06 -0000 @@ -2176,6 +2176,35 @@ dump_varargs_properties( cur_size ); } +static void dump_register_rpc_endpoints_request( const struct register_rpc_endpoints_request *req ) +{ + fprintf( stderr, " objects=%d,", req->objects ); + fprintf( stderr, " bindings=%d,", req->bindings ); + fprintf( stderr, " no_replace=%d,", req->no_replace ); + fprintf( stderr, " eps=" ); + dump_varargs_bytes( cur_size ); +} + +static void dump_unregister_rpc_endpoints_request( const struct unregister_rpc_endpoints_request *req ) +{ + fprintf( stderr, " objects=%d,", req->objects ); + fprintf( stderr, " bindings=%d,", req->bindings ); + fprintf( stderr, " eps=" ); + dump_varargs_bytes( cur_size ); +} + +static void dump_resolve_rpc_endpoint_request( const struct resolve_rpc_endpoint_request *req ) +{ + fprintf( stderr, " binding=" ); + dump_varargs_bytes( cur_size ); +} + +static void dump_resolve_rpc_endpoint_reply( const struct resolve_rpc_endpoint_reply *req ) +{ + fprintf( stderr, " ep=" ); + dump_varargs_bytes( cur_size ); +} + static void dump_attach_thread_input_request( const struct attach_thread_input_request *req ) { fprintf( stderr, " tid_from=%08x,", req->tid_from ); @@ -2402,6 +2431,9 @@ (dump_func)dump_remove_window_property_request, (dump_func)dump_get_window_property_request, (dump_func)dump_get_window_properties_request, + (dump_func)dump_register_rpc_endpoints_request, + (dump_func)dump_unregister_rpc_endpoints_request, + (dump_func)dump_resolve_rpc_endpoint_request, (dump_func)dump_attach_thread_input_request, (dump_func)dump_get_thread_input_request, (dump_func)dump_set_foreground_window_request, @@ -2568,6 +2600,9 @@ (dump_func)dump_get_window_property_reply, (dump_func)dump_get_window_properties_reply, (dump_func)0, + (dump_func)0, + (dump_func)dump_resolve_rpc_endpoint_reply, + (dump_func)0, (dump_func)dump_get_thread_input_reply, (dump_func)dump_set_foreground_window_reply, (dump_func)dump_set_focus_window_reply, @@ -2732,6 +2767,9 @@ "remove_window_property", "get_window_property", "get_window_properties", + "register_rpc_endpoints", + "unregister_rpc_endpoints", + "resolve_rpc_endpoint", "attach_thread_input", "get_thread_input", "set_foreground_window", --- /dev/null 1969-12-31 18:00:00.000000000 -0600 +++ dlls/rpcrt4/rpc_epmap.c 2002-10-12 12:48:49.000000000 -0500 @@ -0,0 +1,172 @@ +/* + * RPC endpoint mapper + * + * Copyright 2001 Ove Kåven, TransGaming Technologies + * + * TODO: + * - actually do things right + */ + +#include <stdio.h> +#include <string.h> + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winreg.h" + +#include "wine/server.h" +#include "rpc.h" + +#include "wine/debug.h" + +#include "rpc_binding.h" + +WINE_DEFAULT_DEBUG_CHANNEL(ole); + +/* The "real" RPC portmapper endpoints that I know of are: + * + * ncadg_ip_udp: 135 + * ncacn_ip_tcp: 135 + * ncacn_np: \\pipe\epmapper (?) + * ncalrpc: epmapper + * + * If the user's machine ran a DCE RPC daemon, it would + * probably be possible to connect to it, but there are many + * reasons not to, like: + * - the user probably does *not* run one, and probably + * shouldn't be forced to run one just for local COM + * - very few Unix systems use DCE RPC... if they run a RPC + * daemon at all, it's usually Sun RPC + * - DCE RPC registrations are persistent and saved on disk, + * while MS-RPC registrations are documented as non-persistent + * and stored only in RAM, and auto-destroyed when the process + * dies (something DCE RPC can't do) + * + * Of course, if the user *did* want to run a DCE RPC daemon anyway, + * there would be interoperability advantages, like the possibility + * of running a fully functional DCOM server using Wine... + * + * But for now, I'll just use the wineserver... + */ + +/*********************************************************************** + * RpcEpRegisterA (RPCRT4.@) + */ +RPC_STATUS WINAPI RpcEpRegisterA( RPC_IF_HANDLE IfSpec, RPC_BINDING_VECTOR* BindingVector, + UUID_VECTOR* UuidVector, LPSTR Annotation ) +{ + NTSTATUS ret; + PRPC_SERVER_INTERFACE If = (PRPC_SERVER_INTERFACE)IfSpec; + unsigned long c; + + TRACE("(%p,%p,%p,%s)\n", IfSpec, BindingVector, UuidVector, debugstr_a(Annotation)); + TRACE(" ifid=%s\n", debugstr_guid(&If->InterfaceId.SyntaxGUID)); + for (c=0; c<BindingVector->Count; c++) { + RpcBinding* bind = (RpcBinding*)(BindingVector->BindingH[c]); + TRACE(" protseq[%ld]=%s\n", c, bind->Protseq); + TRACE(" endpoint[%ld]=%s\n", c, bind->Endpoint); + } + if (UuidVector) { + for (c=0; c<UuidVector->Count; c++) + TRACE(" obj[%ld]=%s\n", c, debugstr_guid(UuidVector->Uuid[c])); + } + + SERVER_START_REQ( register_rpc_endpoints ) + { + wine_server_add_data( req, &If->InterfaceId, sizeof(RPC_SYNTAX_IDENTIFIER) ); + if (UuidVector) { + req->objects = UuidVector->Count; + for (c=0; c<req->objects; c++) + wine_server_add_data( req, UuidVector->Uuid[c], sizeof(UUID) ); + } + else req->objects = 0; + req->bindings = BindingVector->Count; + for (c=0; c<req->bindings; c++) { + RpcBinding* bind = (RpcBinding*)(BindingVector->BindingH[c]); + wine_server_add_data( req, bind->Protseq, strlen(bind->Protseq)+1 ); + wine_server_add_data( req, bind->Endpoint, strlen(bind->Endpoint)+1 ); + } + req->no_replace = 0; + /* FIXME: annotation */ + ret = wine_server_call( req ); + } + SERVER_END_REQ; + + return RtlNtStatusToDosError(ret); +} + +/*********************************************************************** + * RpcEpUnregister (RPCRT4.@) + */ +RPC_STATUS WINAPI RpcEpUnregister( RPC_IF_HANDLE IfSpec, RPC_BINDING_VECTOR* BindingVector, + UUID_VECTOR* UuidVector ) +{ + NTSTATUS ret; + PRPC_SERVER_INTERFACE If = (PRPC_SERVER_INTERFACE)IfSpec; + unsigned long c; + + TRACE("(%p,%p,%p)\n", IfSpec, BindingVector, UuidVector); + TRACE(" ifid=%s\n", debugstr_guid(&If->InterfaceId.SyntaxGUID)); + for (c=0; c<BindingVector->Count; c++) { + RpcBinding* bind = (RpcBinding*)(BindingVector->BindingH[c]); + TRACE(" protseq[%ld]=%s\n", c, bind->Protseq); + TRACE(" endpoint[%ld]=%s\n", c, bind->Endpoint); + } + if (UuidVector) { + for (c=0; c<UuidVector->Count; c++) + TRACE(" obj[%ld]=%s\n", c, debugstr_guid(UuidVector->Uuid[c])); + } + + SERVER_START_REQ( unregister_rpc_endpoints ) + { + wine_server_add_data( req, &If->InterfaceId, sizeof(RPC_SYNTAX_IDENTIFIER) ); + if (UuidVector) { + req->objects = UuidVector->Count; + for (c=0; c<req->objects; c++) + wine_server_add_data( req, UuidVector->Uuid[c], sizeof(UUID) ); + } + else req->objects = 0; + req->bindings = BindingVector->Count; + for (c=0; c<req->bindings; c++) { + RpcBinding* bind = (RpcBinding*)(BindingVector->BindingH[c]); + wine_server_add_data( req, bind->Protseq, strlen(bind->Protseq)+1 ); + wine_server_add_data( req, bind->Endpoint, strlen(bind->Endpoint)+1 ); + } + ret = wine_server_call( req ); + } + SERVER_END_REQ; + + return RtlNtStatusToDosError(ret); +} + +/*********************************************************************** + * RpcEpResolveBinding (RPCRT4.@) + */ +RPC_STATUS WINAPI RpcEpResolveBinding( RPC_BINDING_HANDLE Binding, RPC_IF_HANDLE IfSpec ) +{ + NTSTATUS ret; + PRPC_CLIENT_INTERFACE If = (PRPC_CLIENT_INTERFACE)IfSpec; + RpcBinding* bind = (RpcBinding*)Binding; + char Endpoint[64]; + + TRACE("(%p,%p)\n", Binding, IfSpec); + TRACE(" protseq=%s\n", bind->Protseq); + TRACE(" obj=%s\n", debugstr_guid(&bind->ObjectUuid)); + TRACE(" ifid=%s\n", debugstr_guid(&If->InterfaceId.SyntaxGUID)); + + SERVER_START_REQ( resolve_rpc_endpoint ) + { + wine_server_add_data( req, &If->InterfaceId, sizeof(RPC_SYNTAX_IDENTIFIER) ); + wine_server_add_data( req, &bind->ObjectUuid, sizeof(UUID) ); + wine_server_add_data( req, bind->Protseq, strlen(bind->Protseq)+1 ); + wine_server_set_reply( req, Endpoint, sizeof(Endpoint) ); + ret = wine_server_call( req ); + } + SERVER_END_REQ; + + if (ret) return RtlNtStatusToDosError(ret); + + return RPCRT4_ResolveBinding(Binding, Endpoint); +} + --- /dev/null 1969-12-31 18:00:00.000000000 -0600 +++ server/rpc_epmap.c 2002-10-12 12:48:59.000000000 -0500 @@ -0,0 +1,191 @@ +/* + * RPC endpoint mapper + * + * Copyright (C) 2001 Ove Kåven, TransGaming Technologies Inc. + */ + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "unicode.h" +#include "request.h" +#include "process.h" + +#include "rpc.h" + +struct epmap_entry +{ + struct epmap_entry *next; + RPC_SYNTAX_IDENTIFIER iface; + UUID object; + char *protseq; + char *endpoint; +}; + +struct epmap_entry *epmap; + +static const UUID nil_object; + +static struct epmap_entry *find_endpoint(const RPC_SYNTAX_IDENTIFIER *iface, + const char *protseq, const UUID *object) +{ + struct epmap_entry *map; + for (map=epmap; map; map=map->next) { + if (memcmp(&map->iface, iface, sizeof(RPC_SYNTAX_IDENTIFIER))) continue; + if (memcmp(&map->object, object, sizeof(UUID))) continue; + if (strcmp(map->protseq, protseq)) continue; + return map; + } + return NULL; +} + +static void register_endpoint(const RPC_SYNTAX_IDENTIFIER *iface, const char *protseq, + const char *endpoint, const UUID *objects, int objcount, + int no_replace) +{ + int c; + + if (!objcount) { + objects = &nil_object; + objcount = 1; + } + + for (c=0; c<objcount; c++) { + struct epmap_entry *map = NULL; + if (!no_replace) + map = find_endpoint(iface, protseq, &objects[c]); + if (map) { + free(map->endpoint); + } + else { + map = mem_alloc(sizeof(struct epmap_entry)); + memcpy(&map->iface, iface, sizeof(RPC_SYNTAX_IDENTIFIER)); + memcpy(&map->object, &objects[c], sizeof(UUID)); + map->protseq = strdup(protseq); + map->next = epmap; + epmap = map; + } + map->endpoint = strdup(endpoint); + } +} + +static void unregister_endpoint(const RPC_SYNTAX_IDENTIFIER *iface, const char *protseq, + const char *endpoint, const UUID *objects, int objcount) +{ + struct epmap_entry *map, *prev, *nprev, *next; + int c; + + if (!objcount) { + objects = &nil_object; + objcount = 1; + } + + for (prev=NULL,nprev=NULL,map=epmap,next=map->next; map; prev=nprev,map=next,next=map->next) { + nprev = map; + if (memcmp(&map->iface, iface, sizeof(RPC_SYNTAX_IDENTIFIER))) continue; + for (c=0; c<objcount; c++) + if (!memcmp(&map->object, &objects[c], sizeof(UUID))) break; + if (c==objcount) continue; + if (strcmp(map->protseq, protseq)) continue; + + if (prev) prev->next = map->next; + else epmap = map->next; + nprev = prev; + + free(map->protseq); + free(map->endpoint); + free(map); + } +} + +static void resolve_endpoint(const RPC_SYNTAX_IDENTIFIER *iface, const char *protseq, + const UUID *object) +{ + size_t len; + char *data; + struct epmap_entry *map; + + if (!(map = find_endpoint(iface, protseq, object))) { + set_error( EPT_NT_NOT_REGISTERED ); + return; + } + + len = min( get_reply_max_size(), strlen(map->endpoint)+1 ); + if (len && ((data = set_reply_data_size(len)))) + memcpy(data, map->endpoint, len); +} + +static const void *get_struct(const char**ptr, const char*end, size_t size) +{ + const char *data = *ptr; + + *ptr = data + size; + if (*ptr > end) { + set_error( STATUS_INVALID_PARAMETER ); + return NULL; + } + + return data; +} + +static const char *get_string(const char**ptr, const char*end) +{ + const char *str = *ptr, *nptr = str; + + while (nptr < end && *nptr) nptr++; + if (nptr == end) { + set_error( STATUS_INVALID_PARAMETER ); + return NULL; + } + *ptr = nptr + 1; + + return str; +} + +DECL_HANDLER(register_rpc_endpoints) +{ + const char *data = get_req_data(); + const char *end = data + get_req_data_size(); + const RPC_SYNTAX_IDENTIFIER *iface = get_struct(&data, end, sizeof(RPC_SYNTAX_IDENTIFIER)); + const UUID *objects = get_struct(&data, end, req->objects * sizeof(UUID)); + if (iface && objects) { + int c; + for (c=0; c<req->bindings; c++) { + const char *protseq = get_string(&data, end); + const char *endpoint = get_string(&data, end); + if (protseq && endpoint) + register_endpoint(iface, protseq, endpoint, objects, req->objects, req->no_replace); + } + } +} + +DECL_HANDLER(unregister_rpc_endpoints) +{ + const char *data = get_req_data(); + const char *end = data + get_req_data_size(); + const RPC_SYNTAX_IDENTIFIER *iface = get_struct(&data, end, sizeof(RPC_SYNTAX_IDENTIFIER)); + const UUID *objects = get_struct(&data, end, req->objects * sizeof(UUID)); + if (iface && objects) { + int c; + for (c=0; c<req->bindings; c++) { + const char *protseq = get_string(&data, end); + const char *endpoint = get_string(&data, end); + if (protseq && endpoint) + unregister_endpoint(iface, protseq, endpoint, objects, req->objects); + } + } +} + +DECL_HANDLER(resolve_rpc_endpoint) +{ + const char *data = get_req_data(); + const char *end = data + get_req_data_size(); + const RPC_SYNTAX_IDENTIFIER *iface = get_struct(&data, end, sizeof(RPC_SYNTAX_IDENTIFIER)); + const UUID *object = get_struct(&data, end, sizeof(UUID)); + const char *protseq = get_string(&data, end); + if (iface && object && protseq) { + resolve_endpoint(iface, protseq, object); + } +}