riched20: Rewrite the picture destination parser to handle embedded groups.
[wine/multimedia.git] / dlls / rpcrt4 / rpc_assoc.c
blob339ac12a94ca4e1aa0aa47924f268b37b8887bf0
1 /*
2 * Associations
4 * Copyright 2007 Robert Shearman (for CodeWeavers)
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.
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.
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
22 #include <stdarg.h>
23 #include <assert.h>
25 #include "rpc.h"
26 #include "rpcndr.h"
27 #include "winternl.h"
29 #include "wine/unicode.h"
30 #include "wine/debug.h"
32 #include "rpc_binding.h"
33 #include "rpc_assoc.h"
34 #include "rpc_message.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(rpc);
38 static CRITICAL_SECTION assoc_list_cs;
39 static CRITICAL_SECTION_DEBUG assoc_list_cs_debug =
41 0, 0, &assoc_list_cs,
42 { &assoc_list_cs_debug.ProcessLocksList, &assoc_list_cs_debug.ProcessLocksList },
43 0, 0, { (DWORD_PTR)(__FILE__ ": assoc_list_cs") }
45 static CRITICAL_SECTION assoc_list_cs = { &assoc_list_cs_debug, -1, 0, 0, 0, 0 };
47 static struct list client_assoc_list = LIST_INIT(client_assoc_list);
48 static struct list server_assoc_list = LIST_INIT(server_assoc_list);
50 static LONG last_assoc_group_id;
52 typedef struct _RpcContextHandle
54 struct list entry;
55 void *user_context;
56 NDR_RUNDOWN rundown_routine;
57 void *ctx_guard;
58 UUID uuid;
59 RTL_RWLOCK rw_lock;
60 unsigned int refs;
61 } RpcContextHandle;
63 static void RpcContextHandle_Destroy(RpcContextHandle *context_handle);
65 static RPC_STATUS RpcAssoc_Alloc(LPCSTR Protseq, LPCSTR NetworkAddr,
66 LPCSTR Endpoint, LPCWSTR NetworkOptions,
67 RpcAssoc **assoc_out)
69 RpcAssoc *assoc;
70 assoc = HeapAlloc(GetProcessHeap(), 0, sizeof(*assoc));
71 if (!assoc)
72 return RPC_S_OUT_OF_RESOURCES;
73 assoc->refs = 1;
74 list_init(&assoc->free_connection_pool);
75 list_init(&assoc->context_handle_list);
76 InitializeCriticalSection(&assoc->cs);
77 assoc->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": RpcAssoc.cs");
78 assoc->Protseq = RPCRT4_strdupA(Protseq);
79 assoc->NetworkAddr = RPCRT4_strdupA(NetworkAddr);
80 assoc->Endpoint = RPCRT4_strdupA(Endpoint);
81 assoc->NetworkOptions = NetworkOptions ? RPCRT4_strdupW(NetworkOptions) : NULL;
82 assoc->assoc_group_id = 0;
83 UuidCreate(&assoc->http_uuid);
84 list_init(&assoc->entry);
85 *assoc_out = assoc;
86 return RPC_S_OK;
89 static BOOL compare_networkoptions(LPCWSTR opts1, LPCWSTR opts2)
91 if ((opts1 == NULL) && (opts2 == NULL))
92 return TRUE;
93 if ((opts1 == NULL) || (opts2 == NULL))
94 return FALSE;
95 return !strcmpW(opts1, opts2);
98 RPC_STATUS RPCRT4_GetAssociation(LPCSTR Protseq, LPCSTR NetworkAddr,
99 LPCSTR Endpoint, LPCWSTR NetworkOptions,
100 RpcAssoc **assoc_out)
102 RpcAssoc *assoc;
103 RPC_STATUS status;
105 EnterCriticalSection(&assoc_list_cs);
106 LIST_FOR_EACH_ENTRY(assoc, &client_assoc_list, RpcAssoc, entry)
108 if (!strcmp(Protseq, assoc->Protseq) &&
109 !strcmp(NetworkAddr, assoc->NetworkAddr) &&
110 !strcmp(Endpoint, assoc->Endpoint) &&
111 compare_networkoptions(NetworkOptions, assoc->NetworkOptions))
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;
121 status = RpcAssoc_Alloc(Protseq, NetworkAddr, Endpoint, NetworkOptions, &assoc);
122 if (status != RPC_S_OK)
124 LeaveCriticalSection(&assoc_list_cs);
125 return status;
127 list_add_head(&client_assoc_list, &assoc->entry);
128 *assoc_out = assoc;
130 LeaveCriticalSection(&assoc_list_cs);
132 TRACE("new assoc %p\n", assoc);
134 return RPC_S_OK;
137 RPC_STATUS RpcServerAssoc_GetAssociation(LPCSTR Protseq, LPCSTR NetworkAddr,
138 LPCSTR Endpoint, LPCWSTR NetworkOptions,
139 ULONG assoc_gid,
140 RpcAssoc **assoc_out)
142 RpcAssoc *assoc;
143 RPC_STATUS status;
145 EnterCriticalSection(&assoc_list_cs);
146 if (assoc_gid)
148 LIST_FOR_EACH_ENTRY(assoc, &server_assoc_list, RpcAssoc, entry)
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 || !strcmpW(NetworkOptions, assoc->NetworkOptions))))
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;
165 *assoc_out = NULL;
166 LeaveCriticalSection(&assoc_list_cs);
167 return RPC_S_NO_CONTEXT_AVAILABLE;
170 status = RpcAssoc_Alloc(Protseq, NetworkAddr, Endpoint, NetworkOptions, &assoc);
171 if (status != RPC_S_OK)
173 LeaveCriticalSection(&assoc_list_cs);
174 return status;
176 assoc->assoc_group_id = InterlockedIncrement(&last_assoc_group_id);
177 list_add_head(&server_assoc_list, &assoc->entry);
178 *assoc_out = assoc;
180 LeaveCriticalSection(&assoc_list_cs);
182 TRACE("new assoc %p\n", assoc);
184 return RPC_S_OK;
187 ULONG RpcAssoc_Release(RpcAssoc *assoc)
189 ULONG refs;
191 EnterCriticalSection(&assoc_list_cs);
192 refs = --assoc->refs;
193 if (!refs)
194 list_remove(&assoc->entry);
195 LeaveCriticalSection(&assoc_list_cs);
197 if (!refs)
199 RpcConnection *Connection, *cursor2;
200 RpcContextHandle *context_handle, *context_handle_cursor;
202 TRACE("destroying assoc %p\n", assoc);
204 LIST_FOR_EACH_ENTRY_SAFE(Connection, cursor2, &assoc->free_connection_pool, RpcConnection, conn_pool_entry)
206 list_remove(&Connection->conn_pool_entry);
207 RPCRT4_ReleaseConnection(Connection);
210 LIST_FOR_EACH_ENTRY_SAFE(context_handle, context_handle_cursor, &assoc->context_handle_list, RpcContextHandle, entry)
211 RpcContextHandle_Destroy(context_handle);
213 HeapFree(GetProcessHeap(), 0, assoc->NetworkOptions);
214 HeapFree(GetProcessHeap(), 0, assoc->Endpoint);
215 HeapFree(GetProcessHeap(), 0, assoc->NetworkAddr);
216 HeapFree(GetProcessHeap(), 0, assoc->Protseq);
218 assoc->cs.DebugInfo->Spare[0] = 0;
219 DeleteCriticalSection(&assoc->cs);
221 HeapFree(GetProcessHeap(), 0, assoc);
224 return refs;
227 #define ROUND_UP(value, alignment) (((value) + ((alignment) - 1)) & ~((alignment)-1))
229 static RPC_STATUS RpcAssoc_BindConnection(const RpcAssoc *assoc, RpcConnection *conn,
230 const RPC_SYNTAX_IDENTIFIER *InterfaceId,
231 const RPC_SYNTAX_IDENTIFIER *TransferSyntax)
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;
240 TRACE("sending bind request to server\n");
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);
247 status = RPCRT4_Send(conn, hdr, NULL, 0);
248 RPCRT4_FreeHeader(hdr);
249 if (status != RPC_S_OK)
250 return status;
252 status = RPCRT4_ReceiveWithAuth(conn, &response_hdr, &msg, &auth_data, &auth_length);
253 if (status != RPC_S_OK)
255 ERR("receive failed with error %d\n", status);
256 return status;
259 switch (response_hdr->common.ptype)
261 case PKT_BIND_ACK:
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)))
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])))
274 switch (results->results[0].result)
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)
284 conn->assoc_group_id = response_hdr->bind_ack.assoc_gid;
285 conn->MaxTransmissionSize = response_hdr->bind_ack.max_tsize;
286 conn->ActiveInterface = *InterfaceId;
288 break;
289 case RESULT_PROVIDER_REJECTION:
290 switch (results->results[0].reason)
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;
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;
314 else
316 ERR("incorrect results size\n");
317 status = RPC_S_CALL_FAILED_DNE;
320 else
322 ERR("bind ack packet too small (%d)\n", msg.BufferLength);
323 status = RPC_S_PROTOCOL_ERROR;
325 break;
327 case PKT_BIND_NACK:
328 switch (response_hdr->bind_nack.reject_reason)
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;
351 break;
352 default:
353 ERR("wrong packet type received %d\n", response_hdr->common.ptype);
354 status = RPC_S_PROTOCOL_ERROR;
355 break;
358 I_RpcFree(msg.Buffer);
359 RPCRT4_FreeHeader(response_hdr);
360 HeapFree(GetProcessHeap(), 0, auth_data);
361 return status;
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)
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)
374 if (!memcmp(&Connection->ActiveInterface, InterfaceId,
375 sizeof(RPC_SYNTAX_IDENTIFIER)) &&
376 RpcAuthInfo_IsEqual(Connection->AuthInfo, AuthInfo) &&
377 RpcQualityOfService_IsEqual(Connection->QOS, QOS))
379 list_remove(&Connection->conn_pool_entry);
380 LeaveCriticalSection(&assoc->cs);
381 TRACE("got connection from pool %p\n", Connection);
382 return Connection;
386 LeaveCriticalSection(&assoc->cs);
387 return NULL;
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, RpcConnection **Connection)
395 RpcConnection *NewConnection;
396 RPC_STATUS status;
398 *Connection = RpcAssoc_GetIdleConnection(assoc, InterfaceId, TransferSyntax, AuthInfo, QOS);
399 if (*Connection)
400 return RPC_S_OK;
402 /* create a new connection */
403 status = RPCRT4_CreateConnection(&NewConnection, FALSE /* is this a server connection? */,
404 assoc->Protseq, assoc->NetworkAddr,
405 assoc->Endpoint, assoc->NetworkOptions,
406 AuthInfo, QOS, CookieAuth);
407 if (status != RPC_S_OK)
408 return status;
410 NewConnection->assoc = assoc;
411 status = RPCRT4_OpenClientConnection(NewConnection);
412 if (status != RPC_S_OK)
414 RPCRT4_ReleaseConnection(NewConnection);
415 return status;
418 status = RpcAssoc_BindConnection(assoc, NewConnection, InterfaceId, TransferSyntax);
419 if (status != RPC_S_OK)
421 RPCRT4_ReleaseConnection(NewConnection);
422 return status;
425 *Connection = NewConnection;
427 return RPC_S_OK;
430 void RpcAssoc_ReleaseIdleConnection(RpcAssoc *assoc, RpcConnection *Connection)
432 assert(!Connection->server);
433 Connection->async_state = NULL;
434 EnterCriticalSection(&assoc->cs);
435 if (!assoc->assoc_group_id) assoc->assoc_group_id = Connection->assoc_group_id;
436 list_add_head(&assoc->free_connection_pool, &Connection->conn_pool_entry);
437 LeaveCriticalSection(&assoc->cs);
440 RPC_STATUS RpcServerAssoc_AllocateContextHandle(RpcAssoc *assoc, void *CtxGuard,
441 NDR_SCONTEXT *SContext)
443 RpcContextHandle *context_handle;
445 context_handle = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*context_handle));
446 if (!context_handle)
447 return RPC_S_OUT_OF_MEMORY;
449 context_handle->ctx_guard = CtxGuard;
450 RtlInitializeResource(&context_handle->rw_lock);
451 context_handle->refs = 1;
453 /* lock here to mirror unmarshall, so we don't need to special-case the
454 * freeing of a non-marshalled context handle */
455 RtlAcquireResourceExclusive(&context_handle->rw_lock, TRUE);
457 EnterCriticalSection(&assoc->cs);
458 list_add_tail(&assoc->context_handle_list, &context_handle->entry);
459 LeaveCriticalSection(&assoc->cs);
461 *SContext = (NDR_SCONTEXT)context_handle;
462 return RPC_S_OK;
465 BOOL RpcContextHandle_IsGuardCorrect(NDR_SCONTEXT SContext, void *CtxGuard)
467 RpcContextHandle *context_handle = (RpcContextHandle *)SContext;
468 return context_handle->ctx_guard == CtxGuard;
471 RPC_STATUS RpcServerAssoc_FindContextHandle(RpcAssoc *assoc, const UUID *uuid,
472 void *CtxGuard, ULONG Flags, NDR_SCONTEXT *SContext)
474 RpcContextHandle *context_handle;
476 EnterCriticalSection(&assoc->cs);
477 LIST_FOR_EACH_ENTRY(context_handle, &assoc->context_handle_list, RpcContextHandle, entry)
479 if (RpcContextHandle_IsGuardCorrect((NDR_SCONTEXT)context_handle, CtxGuard) &&
480 !memcmp(&context_handle->uuid, uuid, sizeof(*uuid)))
482 *SContext = (NDR_SCONTEXT)context_handle;
483 if (context_handle->refs++)
485 LeaveCriticalSection(&assoc->cs);
486 TRACE("found %p\n", context_handle);
487 RtlAcquireResourceExclusive(&context_handle->rw_lock, TRUE);
488 return RPC_S_OK;
492 LeaveCriticalSection(&assoc->cs);
494 ERR("no context handle found for uuid %s, guard %p\n",
495 debugstr_guid(uuid), CtxGuard);
496 return ERROR_INVALID_HANDLE;
499 RPC_STATUS RpcServerAssoc_UpdateContextHandle(RpcAssoc *assoc,
500 NDR_SCONTEXT SContext,
501 void *CtxGuard,
502 NDR_RUNDOWN rundown_routine)
504 RpcContextHandle *context_handle = (RpcContextHandle *)SContext;
505 RPC_STATUS status;
507 if (!RpcContextHandle_IsGuardCorrect((NDR_SCONTEXT)context_handle, CtxGuard))
508 return ERROR_INVALID_HANDLE;
510 EnterCriticalSection(&assoc->cs);
511 if (UuidIsNil(&context_handle->uuid, &status))
513 /* add a ref for the data being valid */
514 context_handle->refs++;
515 UuidCreate(&context_handle->uuid);
516 context_handle->rundown_routine = rundown_routine;
517 TRACE("allocated uuid %s for context handle %p\n",
518 debugstr_guid(&context_handle->uuid), context_handle);
520 LeaveCriticalSection(&assoc->cs);
522 return RPC_S_OK;
525 void RpcContextHandle_GetUuid(NDR_SCONTEXT SContext, UUID *uuid)
527 RpcContextHandle *context_handle = (RpcContextHandle *)SContext;
528 *uuid = context_handle->uuid;
531 static void RpcContextHandle_Destroy(RpcContextHandle *context_handle)
533 TRACE("freeing %p\n", context_handle);
535 if (context_handle->user_context && context_handle->rundown_routine)
537 TRACE("calling rundown routine %p with user context %p\n",
538 context_handle->rundown_routine, context_handle->user_context);
539 context_handle->rundown_routine(context_handle->user_context);
542 RtlDeleteResource(&context_handle->rw_lock);
544 HeapFree(GetProcessHeap(), 0, context_handle);
547 unsigned int RpcServerAssoc_ReleaseContextHandle(RpcAssoc *assoc, NDR_SCONTEXT SContext, BOOL release_lock)
549 RpcContextHandle *context_handle = (RpcContextHandle *)SContext;
550 unsigned int refs;
552 if (release_lock)
553 RtlReleaseResource(&context_handle->rw_lock);
555 EnterCriticalSection(&assoc->cs);
556 refs = --context_handle->refs;
557 if (!refs)
558 list_remove(&context_handle->entry);
559 LeaveCriticalSection(&assoc->cs);
561 if (!refs)
562 RpcContextHandle_Destroy(context_handle);
564 return refs;