xref: /reactos/dll/win32/rpcrt4/rpc_assoc.c (revision 6feb8748)
1 /*
2  * Associations
3  *
4  * Copyright 2007 Robert Shearman (for CodeWeavers)
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  */
21 
22 #include <stdarg.h>
23 #include <assert.h>
24 
25 #include "rpc.h"
26 #include "rpcndr.h"
27 #include "wine/winternl.h"
28 
29 #include "wine/debug.h"
30 
31 #include "rpc_binding.h"
32 #include "rpc_assoc.h"
33 #include "rpc_message.h"
34 
35 WINE_DEFAULT_DEBUG_CHANNEL(rpc);
36 
37 static CRITICAL_SECTION assoc_list_cs;
38 static CRITICAL_SECTION_DEBUG assoc_list_cs_debug =
39 {
40     0, 0, &assoc_list_cs,
41     { &assoc_list_cs_debug.ProcessLocksList, &assoc_list_cs_debug.ProcessLocksList },
42       0, 0, { (DWORD_PTR)(__FILE__ ": assoc_list_cs") }
43 };
44 static CRITICAL_SECTION assoc_list_cs = { &assoc_list_cs_debug, -1, 0, 0, 0, 0 };
45 
46 static struct list client_assoc_list = LIST_INIT(client_assoc_list);
47 static struct list server_assoc_list = LIST_INIT(server_assoc_list);
48 
49 static LONG last_assoc_group_id;
50 
51 typedef struct _RpcContextHandle
52 {
53     struct list entry;
54     void *user_context;
55     NDR_RUNDOWN rundown_routine;
56     void *ctx_guard;
57     UUID uuid;
58     RTL_RWLOCK rw_lock;
59     unsigned int refs;
60 } RpcContextHandle;
61 
62 static void RpcContextHandle_Destroy(RpcContextHandle *context_handle);
63 
RpcAssoc_Alloc(LPCSTR Protseq,LPCSTR NetworkAddr,LPCSTR Endpoint,LPCWSTR NetworkOptions,RpcAssoc ** assoc_out)64 static RPC_STATUS RpcAssoc_Alloc(LPCSTR Protseq, LPCSTR NetworkAddr,
65                                  LPCSTR Endpoint, LPCWSTR NetworkOptions,
66                                  RpcAssoc **assoc_out)
67 {
68     RpcAssoc *assoc;
69     assoc = HeapAlloc(GetProcessHeap(), 0, sizeof(*assoc));
70     if (!assoc)
71         return RPC_S_OUT_OF_RESOURCES;
72     assoc->refs = 1;
73     list_init(&assoc->free_connection_pool);
74     list_init(&assoc->context_handle_list);
75     InitializeCriticalSection(&assoc->cs);
76     assoc->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": RpcAssoc.cs");
77     assoc->Protseq = RPCRT4_strdupA(Protseq);
78     assoc->NetworkAddr = RPCRT4_strdupA(NetworkAddr);
79     assoc->Endpoint = RPCRT4_strdupA(Endpoint);
80     assoc->NetworkOptions = NetworkOptions ? RPCRT4_strdupW(NetworkOptions) : NULL;
81     assoc->assoc_group_id = 0;
82     assoc->connection_cnt = 0;
83     UuidCreate(&assoc->http_uuid);
84     list_init(&assoc->entry);
85     *assoc_out = assoc;
86     return RPC_S_OK;
87 }
88 
compare_networkoptions(LPCWSTR opts1,LPCWSTR opts2)89 static BOOL compare_networkoptions(LPCWSTR opts1, LPCWSTR opts2)
90 {
91     if ((opts1 == NULL) && (opts2 == NULL))
92         return TRUE;
93     if ((opts1 == NULL) || (opts2 == NULL))
94         return FALSE;
95     return !wcscmp(opts1, opts2);
96 }
97 
RPCRT4_GetAssociation(LPCSTR Protseq,LPCSTR NetworkAddr,LPCSTR Endpoint,LPCWSTR NetworkOptions,RpcAssoc ** assoc_out)98 RPC_STATUS RPCRT4_GetAssociation(LPCSTR Protseq, LPCSTR NetworkAddr,
99                                  LPCSTR Endpoint, LPCWSTR NetworkOptions,
100                                  RpcAssoc **assoc_out)
101 {
102     RpcAssoc *assoc;
103     RPC_STATUS status;
104 
105     EnterCriticalSection(&assoc_list_cs);
106     LIST_FOR_EACH_ENTRY(assoc, &client_assoc_list, RpcAssoc, entry)
107     {
108         if (!strcmp(Protseq, assoc->Protseq) &&
109             !strcmp(NetworkAddr, assoc->NetworkAddr) &&
110             !strcmp(Endpoint, assoc->Endpoint) &&
111             compare_networkoptions(NetworkOptions, assoc->NetworkOptions))
112         {
113             assoc->refs++;
114             *assoc_out = assoc;
115             LeaveCriticalSection(&assoc_list_cs);
116             TRACE("using existing assoc %p\n", assoc);
117             return RPC_S_OK;
118         }
119     }
120 
121     status = RpcAssoc_Alloc(Protseq, NetworkAddr, Endpoint, NetworkOptions, &assoc);
122     if (status != RPC_S_OK)
123     {
124         LeaveCriticalSection(&assoc_list_cs);
125         return status;
126     }
127     list_add_head(&client_assoc_list, &assoc->entry);
128     *assoc_out = assoc;
129 
130     LeaveCriticalSection(&assoc_list_cs);
131 
132     TRACE("new assoc %p\n", assoc);
133 
134     return RPC_S_OK;
135 }
136 
RpcServerAssoc_GetAssociation(LPCSTR Protseq,LPCSTR NetworkAddr,LPCSTR Endpoint,LPCWSTR NetworkOptions,ULONG assoc_gid,RpcAssoc ** assoc_out)137 RPC_STATUS RpcServerAssoc_GetAssociation(LPCSTR Protseq, LPCSTR NetworkAddr,
138                                          LPCSTR Endpoint, LPCWSTR NetworkOptions,
139                                          ULONG assoc_gid,
140                                          RpcAssoc **assoc_out)
141 {
142     RpcAssoc *assoc;
143     RPC_STATUS status;
144 
145     EnterCriticalSection(&assoc_list_cs);
146     if (assoc_gid)
147     {
148         LIST_FOR_EACH_ENTRY(assoc, &server_assoc_list, RpcAssoc, entry)
149         {
150             /* FIXME: NetworkAddr shouldn't be NULL */
151             if (assoc->assoc_group_id == assoc_gid &&
152                 !strcmp(Protseq, assoc->Protseq) &&
153                 (!NetworkAddr || !assoc->NetworkAddr || !strcmp(NetworkAddr, assoc->NetworkAddr)) &&
154                 !strcmp(Endpoint, assoc->Endpoint) &&
155                 ((!assoc->NetworkOptions == !NetworkOptions) &&
156                  (!NetworkOptions || !wcscmp(NetworkOptions, assoc->NetworkOptions))))
157             {
158                 assoc->refs++;
159                 *assoc_out = assoc;
160                 LeaveCriticalSection(&assoc_list_cs);
161                 TRACE("using existing assoc %p\n", assoc);
162                 return RPC_S_OK;
163             }
164         }
165         *assoc_out = NULL;
166         LeaveCriticalSection(&assoc_list_cs);
167         return RPC_S_NO_CONTEXT_AVAILABLE;
168     }
169 
170     status = RpcAssoc_Alloc(Protseq, NetworkAddr, Endpoint, NetworkOptions, &assoc);
171     if (status != RPC_S_OK)
172     {
173         LeaveCriticalSection(&assoc_list_cs);
174         return status;
175     }
176     assoc->assoc_group_id = InterlockedIncrement(&last_assoc_group_id);
177     list_add_head(&server_assoc_list, &assoc->entry);
178     *assoc_out = assoc;
179 
180     LeaveCriticalSection(&assoc_list_cs);
181 
182     TRACE("new assoc %p\n", assoc);
183 
184     return RPC_S_OK;
185 }
186 
RpcAssoc_Release(RpcAssoc * assoc)187 ULONG RpcAssoc_Release(RpcAssoc *assoc)
188 {
189     ULONG refs;
190 
191     EnterCriticalSection(&assoc_list_cs);
192     refs = --assoc->refs;
193     if (!refs)
194         list_remove(&assoc->entry);
195     LeaveCriticalSection(&assoc_list_cs);
196 
197     if (!refs)
198     {
199         RpcConnection *Connection, *cursor2;
200         RpcContextHandle *context_handle, *context_handle_cursor;
201 
202         TRACE("destroying assoc %p\n", assoc);
203 
204         LIST_FOR_EACH_ENTRY_SAFE(Connection, cursor2, &assoc->free_connection_pool, RpcConnection, conn_pool_entry)
205         {
206             list_remove(&Connection->conn_pool_entry);
207             RPCRT4_ReleaseConnection(Connection);
208         }
209 
210         LIST_FOR_EACH_ENTRY_SAFE(context_handle, context_handle_cursor, &assoc->context_handle_list, RpcContextHandle, entry)
211             RpcContextHandle_Destroy(context_handle);
212 
213         HeapFree(GetProcessHeap(), 0, assoc->NetworkOptions);
214         HeapFree(GetProcessHeap(), 0, assoc->Endpoint);
215         HeapFree(GetProcessHeap(), 0, assoc->NetworkAddr);
216         HeapFree(GetProcessHeap(), 0, assoc->Protseq);
217 
218         assoc->cs.DebugInfo->Spare[0] = 0;
219         DeleteCriticalSection(&assoc->cs);
220 
221         HeapFree(GetProcessHeap(), 0, assoc);
222     }
223 
224     return refs;
225 }
226 
227 #define ROUND_UP(value, alignment) (((value) + ((alignment) - 1)) & ~((alignment)-1))
228 
RpcAssoc_BindConnection(const RpcAssoc * assoc,RpcConnection * conn,const RPC_SYNTAX_IDENTIFIER * InterfaceId,const RPC_SYNTAX_IDENTIFIER * TransferSyntax)229 static RPC_STATUS RpcAssoc_BindConnection(const RpcAssoc *assoc, RpcConnection *conn,
230                                           const RPC_SYNTAX_IDENTIFIER *InterfaceId,
231                                           const RPC_SYNTAX_IDENTIFIER *TransferSyntax)
232 {
233     RpcPktHdr *hdr;
234     RpcPktHdr *response_hdr;
235     RPC_MESSAGE msg;
236     RPC_STATUS status;
237     unsigned char *auth_data = NULL;
238     ULONG auth_length;
239 
240     TRACE("sending bind request to server\n");
241 
242     hdr = RPCRT4_BuildBindHeader(NDR_LOCAL_DATA_REPRESENTATION,
243                                  RPC_MAX_PACKET_SIZE, RPC_MAX_PACKET_SIZE,
244                                  assoc->assoc_group_id,
245                                  InterfaceId, TransferSyntax);
246 
247     status = RPCRT4_Send(conn, hdr, NULL, 0);
248     RPCRT4_FreeHeader(hdr);
249     if (status != RPC_S_OK)
250         return status;
251 
252     status = RPCRT4_ReceiveWithAuth(conn, &response_hdr, &msg, &auth_data, &auth_length);
253     if (status != RPC_S_OK)
254     {
255         ERR("receive failed with error %d\n", status);
256         return status;
257     }
258 
259     switch (response_hdr->common.ptype)
260     {
261     case PKT_BIND_ACK:
262     {
263         RpcAddressString *server_address = msg.Buffer;
264         if ((msg.BufferLength >= FIELD_OFFSET(RpcAddressString, string[0])) ||
265             (msg.BufferLength >= ROUND_UP(FIELD_OFFSET(RpcAddressString, string[server_address->length]), 4)))
266         {
267             unsigned short remaining = msg.BufferLength -
268             ROUND_UP(FIELD_OFFSET(RpcAddressString, string[server_address->length]), 4);
269             RpcResultList *results = (RpcResultList*)((ULONG_PTR)server_address +
270                 ROUND_UP(FIELD_OFFSET(RpcAddressString, string[server_address->length]), 4));
271             if ((results->num_results == 1) &&
272                 (remaining >= FIELD_OFFSET(RpcResultList, results[results->num_results])))
273             {
274                 switch (results->results[0].result)
275                 {
276                 case RESULT_ACCEPT:
277                     /* respond to authorization request */
278                     if (auth_length > sizeof(RpcAuthVerifier))
279                         status = RPCRT4_ClientConnectionAuth(conn,
280                                                              auth_data + sizeof(RpcAuthVerifier),
281                                                              auth_length);
282                     if (status == RPC_S_OK)
283                     {
284                         conn->assoc_group_id = response_hdr->bind_ack.assoc_gid;
285                         conn->MaxTransmissionSize = response_hdr->bind_ack.max_tsize;
286                         conn->ActiveInterface = *InterfaceId;
287                     }
288                     break;
289                 case RESULT_PROVIDER_REJECTION:
290                     switch (results->results[0].reason)
291                     {
292                     case REASON_ABSTRACT_SYNTAX_NOT_SUPPORTED:
293                         ERR("syntax %s, %d.%d not supported\n",
294                             debugstr_guid(&InterfaceId->SyntaxGUID),
295                             InterfaceId->SyntaxVersion.MajorVersion,
296                             InterfaceId->SyntaxVersion.MinorVersion);
297                         status = RPC_S_UNKNOWN_IF;
298                         break;
299                     case REASON_TRANSFER_SYNTAXES_NOT_SUPPORTED:
300                         ERR("transfer syntax not supported\n");
301                         status = RPC_S_SERVER_UNAVAILABLE;
302                         break;
303                     case REASON_NONE:
304                     default:
305                         status = RPC_S_CALL_FAILED_DNE;
306                     }
307                     break;
308                 case RESULT_USER_REJECTION:
309                 default:
310                     ERR("rejection result %d\n", results->results[0].result);
311                     status = RPC_S_CALL_FAILED_DNE;
312                 }
313             }
314             else
315             {
316                 ERR("incorrect results size\n");
317                 status = RPC_S_CALL_FAILED_DNE;
318             }
319         }
320         else
321         {
322             ERR("bind ack packet too small (%d)\n", msg.BufferLength);
323             status = RPC_S_PROTOCOL_ERROR;
324         }
325         break;
326     }
327     case PKT_BIND_NACK:
328         switch (response_hdr->bind_nack.reject_reason)
329         {
330         case REJECT_LOCAL_LIMIT_EXCEEDED:
331         case REJECT_TEMPORARY_CONGESTION:
332             ERR("server too busy\n");
333             status = RPC_S_SERVER_TOO_BUSY;
334             break;
335         case REJECT_PROTOCOL_VERSION_NOT_SUPPORTED:
336             ERR("protocol version not supported\n");
337             status = RPC_S_PROTOCOL_ERROR;
338             break;
339         case REJECT_UNKNOWN_AUTHN_SERVICE:
340             ERR("unknown authentication service\n");
341             status = RPC_S_UNKNOWN_AUTHN_SERVICE;
342             break;
343         case REJECT_INVALID_CHECKSUM:
344             ERR("invalid checksum\n");
345             status = RPC_S_ACCESS_DENIED;
346             break;
347         default:
348             ERR("rejected bind for reason %d\n", response_hdr->bind_nack.reject_reason);
349             status = RPC_S_CALL_FAILED_DNE;
350         }
351         break;
352     default:
353         ERR("wrong packet type received %d\n", response_hdr->common.ptype);
354         status = RPC_S_PROTOCOL_ERROR;
355         break;
356     }
357 
358     I_RpcFree(msg.Buffer);
359     RPCRT4_FreeHeader(response_hdr);
360     HeapFree(GetProcessHeap(), 0, auth_data);
361     return status;
362 }
363 
RpcAssoc_GetIdleConnection(RpcAssoc * assoc,const RPC_SYNTAX_IDENTIFIER * InterfaceId,const RPC_SYNTAX_IDENTIFIER * TransferSyntax,const RpcAuthInfo * AuthInfo,const RpcQualityOfService * QOS)364 static RpcConnection *RpcAssoc_GetIdleConnection(RpcAssoc *assoc,
365                                                  const RPC_SYNTAX_IDENTIFIER *InterfaceId,
366                                                  const RPC_SYNTAX_IDENTIFIER *TransferSyntax, const RpcAuthInfo *AuthInfo,
367                                                  const RpcQualityOfService *QOS)
368 {
369     RpcConnection *Connection;
370     EnterCriticalSection(&assoc->cs);
371     /* try to find a compatible connection from the connection pool */
372     LIST_FOR_EACH_ENTRY(Connection, &assoc->free_connection_pool, RpcConnection, conn_pool_entry)
373     {
374         if (!memcmp(&Connection->ActiveInterface, InterfaceId,
375                     sizeof(RPC_SYNTAX_IDENTIFIER)) &&
376             RpcAuthInfo_IsEqual(Connection->AuthInfo, AuthInfo) &&
377             RpcQualityOfService_IsEqual(Connection->QOS, QOS))
378         {
379             list_remove(&Connection->conn_pool_entry);
380             LeaveCriticalSection(&assoc->cs);
381             TRACE("got connection from pool %p\n", Connection);
382             return Connection;
383         }
384     }
385 
386     LeaveCriticalSection(&assoc->cs);
387     return NULL;
388 }
389 
RpcAssoc_GetClientConnection(RpcAssoc * assoc,const RPC_SYNTAX_IDENTIFIER * InterfaceId,const RPC_SYNTAX_IDENTIFIER * TransferSyntax,RpcAuthInfo * AuthInfo,RpcQualityOfService * QOS,LPCWSTR CookieAuth,RpcConnection ** Connection,BOOL * from_cache)390 RPC_STATUS RpcAssoc_GetClientConnection(RpcAssoc *assoc,
391                                         const RPC_SYNTAX_IDENTIFIER *InterfaceId,
392                                         const RPC_SYNTAX_IDENTIFIER *TransferSyntax, RpcAuthInfo *AuthInfo,
393                                         RpcQualityOfService *QOS, LPCWSTR CookieAuth,
394                                         RpcConnection **Connection, BOOL *from_cache)
395 {
396     RpcConnection *NewConnection;
397     RPC_STATUS status;
398 
399     *Connection = RpcAssoc_GetIdleConnection(assoc, InterfaceId, TransferSyntax, AuthInfo, QOS);
400     if (*Connection) {
401         TRACE("return idle connection %p for association %p\n", *Connection, assoc);
402         if (from_cache) *from_cache = TRUE;
403         return RPC_S_OK;
404     }
405 
406     /* create a new connection */
407     status = RPCRT4_CreateConnection(&NewConnection, FALSE /* is this a server connection? */,
408         assoc->Protseq, assoc->NetworkAddr,
409         assoc->Endpoint, assoc->NetworkOptions,
410         AuthInfo, QOS, CookieAuth);
411     if (status != RPC_S_OK)
412         return status;
413 
414     NewConnection->assoc = assoc;
415     status = RPCRT4_OpenClientConnection(NewConnection);
416     if (status != RPC_S_OK)
417     {
418         RPCRT4_ReleaseConnection(NewConnection);
419         return status;
420     }
421 
422     status = RpcAssoc_BindConnection(assoc, NewConnection, InterfaceId, TransferSyntax);
423     if (status != RPC_S_OK)
424     {
425         RPCRT4_ReleaseConnection(NewConnection);
426         return status;
427     }
428 
429     InterlockedIncrement(&assoc->connection_cnt);
430 
431     TRACE("return new connection %p for association %p\n", *Connection, assoc);
432     *Connection = NewConnection;
433     if (from_cache) *from_cache = FALSE;
434     return RPC_S_OK;
435 }
436 
RpcAssoc_ReleaseIdleConnection(RpcAssoc * assoc,RpcConnection * Connection)437 void RpcAssoc_ReleaseIdleConnection(RpcAssoc *assoc, RpcConnection *Connection)
438 {
439     assert(!Connection->server);
440     Connection->async_state = NULL;
441     EnterCriticalSection(&assoc->cs);
442     if (!assoc->assoc_group_id) assoc->assoc_group_id = Connection->assoc_group_id;
443     list_add_head(&assoc->free_connection_pool, &Connection->conn_pool_entry);
444     LeaveCriticalSection(&assoc->cs);
445 }
446 
RpcAssoc_ConnectionReleased(RpcAssoc * assoc)447 void RpcAssoc_ConnectionReleased(RpcAssoc *assoc)
448 {
449     if (InterlockedDecrement(&assoc->connection_cnt))
450         return;
451 
452     TRACE("Last %p connection released\n", assoc);
453     assoc->assoc_group_id = 0;
454 }
455 
RpcServerAssoc_AllocateContextHandle(RpcAssoc * assoc,void * CtxGuard,NDR_SCONTEXT * SContext)456 RPC_STATUS RpcServerAssoc_AllocateContextHandle(RpcAssoc *assoc, void *CtxGuard,
457                                                 NDR_SCONTEXT *SContext)
458 {
459     RpcContextHandle *context_handle;
460 
461     context_handle = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*context_handle));
462     if (!context_handle)
463         return RPC_S_OUT_OF_MEMORY;
464 
465     context_handle->ctx_guard = CtxGuard;
466     RtlInitializeResource(&context_handle->rw_lock);
467     context_handle->refs = 1;
468 
469     /* lock here to mirror unmarshall, so we don't need to special-case the
470      * freeing of a non-marshalled context handle */
471     RtlAcquireResourceExclusive(&context_handle->rw_lock, TRUE);
472 
473     EnterCriticalSection(&assoc->cs);
474     list_add_tail(&assoc->context_handle_list, &context_handle->entry);
475     LeaveCriticalSection(&assoc->cs);
476 
477     *SContext = (NDR_SCONTEXT)context_handle;
478     return RPC_S_OK;
479 }
480 
RpcContextHandle_IsGuardCorrect(NDR_SCONTEXT SContext,void * CtxGuard)481 BOOL RpcContextHandle_IsGuardCorrect(NDR_SCONTEXT SContext, void *CtxGuard)
482 {
483     RpcContextHandle *context_handle = (RpcContextHandle *)SContext;
484     return context_handle->ctx_guard == CtxGuard;
485 }
486 
RpcServerAssoc_FindContextHandle(RpcAssoc * assoc,const UUID * uuid,void * CtxGuard,ULONG Flags,NDR_SCONTEXT * SContext)487 RPC_STATUS RpcServerAssoc_FindContextHandle(RpcAssoc *assoc, const UUID *uuid,
488                                             void *CtxGuard, ULONG Flags, NDR_SCONTEXT *SContext)
489 {
490     RpcContextHandle *context_handle;
491 
492     EnterCriticalSection(&assoc->cs);
493     LIST_FOR_EACH_ENTRY(context_handle, &assoc->context_handle_list, RpcContextHandle, entry)
494     {
495         if (RpcContextHandle_IsGuardCorrect((NDR_SCONTEXT)context_handle, CtxGuard) &&
496             !memcmp(&context_handle->uuid, uuid, sizeof(*uuid)))
497         {
498             *SContext = (NDR_SCONTEXT)context_handle;
499             if (context_handle->refs++)
500             {
501                 LeaveCriticalSection(&assoc->cs);
502                 TRACE("found %p\n", context_handle);
503                 RtlAcquireResourceExclusive(&context_handle->rw_lock, TRUE);
504                 return RPC_S_OK;
505             }
506         }
507     }
508     LeaveCriticalSection(&assoc->cs);
509 
510     ERR("no context handle found for uuid %s, guard %p\n",
511         debugstr_guid(uuid), CtxGuard);
512     return ERROR_INVALID_HANDLE;
513 }
514 
RpcServerAssoc_UpdateContextHandle(RpcAssoc * assoc,NDR_SCONTEXT SContext,void * CtxGuard,NDR_RUNDOWN rundown_routine)515 RPC_STATUS RpcServerAssoc_UpdateContextHandle(RpcAssoc *assoc,
516                                               NDR_SCONTEXT SContext,
517                                               void *CtxGuard,
518                                               NDR_RUNDOWN rundown_routine)
519 {
520     RpcContextHandle *context_handle = (RpcContextHandle *)SContext;
521     RPC_STATUS status;
522 
523     if (!RpcContextHandle_IsGuardCorrect((NDR_SCONTEXT)context_handle, CtxGuard))
524         return ERROR_INVALID_HANDLE;
525 
526     EnterCriticalSection(&assoc->cs);
527     if (UuidIsNil(&context_handle->uuid, &status))
528     {
529         /* add a ref for the data being valid */
530         context_handle->refs++;
531         UuidCreate(&context_handle->uuid);
532         context_handle->rundown_routine = rundown_routine;
533         TRACE("allocated uuid %s for context handle %p\n",
534               debugstr_guid(&context_handle->uuid), context_handle);
535     }
536     LeaveCriticalSection(&assoc->cs);
537 
538     return RPC_S_OK;
539 }
540 
RpcContextHandle_GetUuid(NDR_SCONTEXT SContext,UUID * uuid)541 void RpcContextHandle_GetUuid(NDR_SCONTEXT SContext, UUID *uuid)
542 {
543     RpcContextHandle *context_handle = (RpcContextHandle *)SContext;
544     *uuid = context_handle->uuid;
545 }
546 
RpcContextHandle_Destroy(RpcContextHandle * context_handle)547 static void RpcContextHandle_Destroy(RpcContextHandle *context_handle)
548 {
549     TRACE("freeing %p\n", context_handle);
550 
551     if (context_handle->user_context && context_handle->rundown_routine)
552     {
553         TRACE("calling rundown routine %p with user context %p\n",
554               context_handle->rundown_routine, context_handle->user_context);
555         context_handle->rundown_routine(context_handle->user_context);
556     }
557 
558     RtlDeleteResource(&context_handle->rw_lock);
559 
560     HeapFree(GetProcessHeap(), 0, context_handle);
561 }
562 
RpcServerAssoc_ReleaseContextHandle(RpcAssoc * assoc,NDR_SCONTEXT SContext,BOOL release_lock)563 unsigned int RpcServerAssoc_ReleaseContextHandle(RpcAssoc *assoc, NDR_SCONTEXT SContext, BOOL release_lock)
564 {
565     RpcContextHandle *context_handle = (RpcContextHandle *)SContext;
566     unsigned int refs;
567 
568     if (release_lock)
569         RtlReleaseResource(&context_handle->rw_lock);
570 
571     EnterCriticalSection(&assoc->cs);
572     refs = --context_handle->refs;
573     if (!refs)
574         list_remove(&context_handle->entry);
575     LeaveCriticalSection(&assoc->cs);
576 
577     if (!refs)
578         RpcContextHandle_Destroy(context_handle);
579 
580     return refs;
581 }
582