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
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
=
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
56 NDR_RUNDOWN rundown_routine
;
63 static void RpcContextHandle_Destroy(RpcContextHandle
*context_handle
);
65 static RPC_STATUS
RpcAssoc_Alloc(LPCSTR Protseq
, LPCSTR NetworkAddr
,
66 LPCSTR Endpoint
, LPCWSTR NetworkOptions
,
70 assoc
= HeapAlloc(GetProcessHeap(), 0, sizeof(*assoc
));
72 return RPC_S_OUT_OF_RESOURCES
;
74 list_init(&assoc
->free_connection_pool
);
75 list_init(&assoc
->context_handle_list
);
76 InitializeCriticalSection(&assoc
->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 list_init(&assoc
->entry
);
87 RPC_STATUS
RPCRT4_GetAssociation(LPCSTR Protseq
, LPCSTR NetworkAddr
,
88 LPCSTR Endpoint
, LPCWSTR NetworkOptions
,
94 EnterCriticalSection(&assoc_list_cs
);
95 LIST_FOR_EACH_ENTRY(assoc
, &client_assoc_list
, RpcAssoc
, entry
)
97 if (!strcmp(Protseq
, assoc
->Protseq
) &&
98 !strcmp(NetworkAddr
, assoc
->NetworkAddr
) &&
99 !strcmp(Endpoint
, assoc
->Endpoint
) &&
100 ((!assoc
->NetworkOptions
&& !NetworkOptions
) || !strcmpW(NetworkOptions
, assoc
->NetworkOptions
)))
104 LeaveCriticalSection(&assoc_list_cs
);
105 TRACE("using existing assoc %p\n", assoc
);
110 status
= RpcAssoc_Alloc(Protseq
, NetworkAddr
, Endpoint
, NetworkOptions
, &assoc
);
111 if (status
!= RPC_S_OK
)
113 LeaveCriticalSection(&assoc_list_cs
);
116 list_add_head(&client_assoc_list
, &assoc
->entry
);
119 LeaveCriticalSection(&assoc_list_cs
);
121 TRACE("new assoc %p\n", assoc
);
126 RPC_STATUS
RpcServerAssoc_GetAssociation(LPCSTR Protseq
, LPCSTR NetworkAddr
,
127 LPCSTR Endpoint
, LPCWSTR NetworkOptions
,
128 unsigned long assoc_gid
,
129 RpcAssoc
**assoc_out
)
134 EnterCriticalSection(&assoc_list_cs
);
137 LIST_FOR_EACH_ENTRY(assoc
, &server_assoc_list
, RpcAssoc
, entry
)
139 /* FIXME: NetworkAddr shouldn't be NULL */
140 if (assoc
->assoc_group_id
== assoc_gid
&&
141 !strcmp(Protseq
, assoc
->Protseq
) &&
142 (!NetworkAddr
|| !assoc
->NetworkAddr
|| !strcmp(NetworkAddr
, assoc
->NetworkAddr
)) &&
143 !strcmp(Endpoint
, assoc
->Endpoint
) &&
144 ((!assoc
->NetworkOptions
== !NetworkOptions
) &&
145 (!NetworkOptions
|| !strcmpW(NetworkOptions
, assoc
->NetworkOptions
))))
149 LeaveCriticalSection(&assoc_list_cs
);
150 TRACE("using existing assoc %p\n", assoc
);
155 LeaveCriticalSection(&assoc_list_cs
);
156 return RPC_S_NO_CONTEXT_AVAILABLE
;
159 status
= RpcAssoc_Alloc(Protseq
, NetworkAddr
, Endpoint
, NetworkOptions
, &assoc
);
160 if (status
!= RPC_S_OK
)
162 LeaveCriticalSection(&assoc_list_cs
);
165 assoc
->assoc_group_id
= InterlockedIncrement(&last_assoc_group_id
);
166 list_add_head(&server_assoc_list
, &assoc
->entry
);
169 LeaveCriticalSection(&assoc_list_cs
);
171 TRACE("new assoc %p\n", assoc
);
176 ULONG
RpcAssoc_Release(RpcAssoc
*assoc
)
180 EnterCriticalSection(&assoc_list_cs
);
181 refs
= --assoc
->refs
;
183 list_remove(&assoc
->entry
);
184 LeaveCriticalSection(&assoc_list_cs
);
188 RpcConnection
*Connection
, *cursor2
;
189 RpcContextHandle
*context_handle
, *context_handle_cursor
;
191 TRACE("destroying assoc %p\n", assoc
);
193 LIST_FOR_EACH_ENTRY_SAFE(Connection
, cursor2
, &assoc
->free_connection_pool
, RpcConnection
, conn_pool_entry
)
195 list_remove(&Connection
->conn_pool_entry
);
196 RPCRT4_DestroyConnection(Connection
);
199 LIST_FOR_EACH_ENTRY_SAFE(context_handle
, context_handle_cursor
, &assoc
->context_handle_list
, RpcContextHandle
, entry
)
200 RpcContextHandle_Destroy(context_handle
);
202 HeapFree(GetProcessHeap(), 0, assoc
->NetworkOptions
);
203 HeapFree(GetProcessHeap(), 0, assoc
->Endpoint
);
204 HeapFree(GetProcessHeap(), 0, assoc
->NetworkAddr
);
205 HeapFree(GetProcessHeap(), 0, assoc
->Protseq
);
207 DeleteCriticalSection(&assoc
->cs
);
209 HeapFree(GetProcessHeap(), 0, assoc
);
215 #define ROUND_UP(value, alignment) (((value) + ((alignment) - 1)) & ~((alignment)-1))
217 static RPC_STATUS
RpcAssoc_BindConnection(const RpcAssoc
*assoc
, RpcConnection
*conn
,
218 const RPC_SYNTAX_IDENTIFIER
*InterfaceId
,
219 const RPC_SYNTAX_IDENTIFIER
*TransferSyntax
)
222 RpcPktHdr
*response_hdr
;
226 TRACE("sending bind request to server\n");
228 hdr
= RPCRT4_BuildBindHeader(NDR_LOCAL_DATA_REPRESENTATION
,
229 RPC_MAX_PACKET_SIZE
, RPC_MAX_PACKET_SIZE
,
230 assoc
->assoc_group_id
,
231 InterfaceId
, TransferSyntax
);
233 status
= RPCRT4_Send(conn
, hdr
, NULL
, 0);
234 RPCRT4_FreeHeader(hdr
);
235 if (status
!= RPC_S_OK
)
238 status
= RPCRT4_Receive(conn
, &response_hdr
, &msg
);
239 if (status
!= RPC_S_OK
)
241 ERR("receive failed\n");
245 switch (response_hdr
->common
.ptype
)
249 RpcAddressString
*server_address
= msg
.Buffer
;
250 if ((msg
.BufferLength
>= FIELD_OFFSET(RpcAddressString
, string
[0])) ||
251 (msg
.BufferLength
>= ROUND_UP(FIELD_OFFSET(RpcAddressString
, string
[server_address
->length
]), 4)))
253 unsigned short remaining
= msg
.BufferLength
-
254 ROUND_UP(FIELD_OFFSET(RpcAddressString
, string
[server_address
->length
]), 4);
255 RpcResults
*results
= (RpcResults
*)((ULONG_PTR
)server_address
+
256 ROUND_UP(FIELD_OFFSET(RpcAddressString
, string
[server_address
->length
]), 4));
257 if ((results
->num_results
== 1) && (remaining
>= sizeof(*results
)))
259 switch (results
->results
[0].result
)
262 conn
->assoc_group_id
= response_hdr
->bind_ack
.assoc_gid
;
263 conn
->MaxTransmissionSize
= response_hdr
->bind_ack
.max_tsize
;
264 conn
->ActiveInterface
= *InterfaceId
;
266 case RESULT_PROVIDER_REJECTION
:
267 switch (results
->results
[0].reason
)
269 case REASON_ABSTRACT_SYNTAX_NOT_SUPPORTED
:
270 ERR("syntax %s, %d.%d not supported\n",
271 debugstr_guid(&InterfaceId
->SyntaxGUID
),
272 InterfaceId
->SyntaxVersion
.MajorVersion
,
273 InterfaceId
->SyntaxVersion
.MinorVersion
);
274 status
= RPC_S_UNKNOWN_IF
;
276 case REASON_TRANSFER_SYNTAXES_NOT_SUPPORTED
:
277 ERR("transfer syntax not supported\n");
278 status
= RPC_S_SERVER_UNAVAILABLE
;
282 status
= RPC_S_CALL_FAILED_DNE
;
285 case RESULT_USER_REJECTION
:
287 ERR("rejection result %d\n", results
->results
[0].result
);
288 status
= RPC_S_CALL_FAILED_DNE
;
293 ERR("incorrect results size\n");
294 status
= RPC_S_CALL_FAILED_DNE
;
299 ERR("bind ack packet too small (%d)\n", msg
.BufferLength
);
300 status
= RPC_S_PROTOCOL_ERROR
;
305 switch (response_hdr
->bind_nack
.reject_reason
)
307 case REJECT_LOCAL_LIMIT_EXCEEDED
:
308 case REJECT_TEMPORARY_CONGESTION
:
309 ERR("server too busy\n");
310 status
= RPC_S_SERVER_TOO_BUSY
;
312 case REJECT_PROTOCOL_VERSION_NOT_SUPPORTED
:
313 ERR("protocol version not supported\n");
314 status
= RPC_S_PROTOCOL_ERROR
;
316 case REJECT_UNKNOWN_AUTHN_SERVICE
:
317 ERR("unknown authentication service\n");
318 status
= RPC_S_UNKNOWN_AUTHN_SERVICE
;
320 case REJECT_INVALID_CHECKSUM
:
321 ERR("invalid checksum\n");
322 status
= ERROR_ACCESS_DENIED
;
325 ERR("rejected bind for reason %d\n", response_hdr
->bind_nack
.reject_reason
);
326 status
= RPC_S_CALL_FAILED_DNE
;
330 ERR("wrong packet type received %d\n", response_hdr
->common
.ptype
);
331 status
= RPC_S_PROTOCOL_ERROR
;
335 I_RpcFree(msg
.Buffer
);
336 RPCRT4_FreeHeader(response_hdr
);
340 static RpcConnection
*RpcAssoc_GetIdleConnection(RpcAssoc
*assoc
,
341 const RPC_SYNTAX_IDENTIFIER
*InterfaceId
,
342 const RPC_SYNTAX_IDENTIFIER
*TransferSyntax
, const RpcAuthInfo
*AuthInfo
,
343 const RpcQualityOfService
*QOS
)
345 RpcConnection
*Connection
;
346 EnterCriticalSection(&assoc
->cs
);
347 /* try to find a compatible connection from the connection pool */
348 LIST_FOR_EACH_ENTRY(Connection
, &assoc
->free_connection_pool
, RpcConnection
, conn_pool_entry
)
350 if (!memcmp(&Connection
->ActiveInterface
, InterfaceId
,
351 sizeof(RPC_SYNTAX_IDENTIFIER
)) &&
352 RpcAuthInfo_IsEqual(Connection
->AuthInfo
, AuthInfo
) &&
353 RpcQualityOfService_IsEqual(Connection
->QOS
, QOS
))
355 list_remove(&Connection
->conn_pool_entry
);
356 LeaveCriticalSection(&assoc
->cs
);
357 TRACE("got connection from pool %p\n", Connection
);
362 LeaveCriticalSection(&assoc
->cs
);
366 RPC_STATUS
RpcAssoc_GetClientConnection(RpcAssoc
*assoc
,
367 const RPC_SYNTAX_IDENTIFIER
*InterfaceId
,
368 const RPC_SYNTAX_IDENTIFIER
*TransferSyntax
, RpcAuthInfo
*AuthInfo
,
369 RpcQualityOfService
*QOS
, RpcConnection
**Connection
)
371 RpcConnection
*NewConnection
;
374 *Connection
= RpcAssoc_GetIdleConnection(assoc
, InterfaceId
, TransferSyntax
, AuthInfo
, QOS
);
378 /* create a new connection */
379 status
= RPCRT4_CreateConnection(&NewConnection
, FALSE
/* is this a server connection? */,
380 assoc
->Protseq
, assoc
->NetworkAddr
,
381 assoc
->Endpoint
, assoc
->NetworkOptions
,
383 if (status
!= RPC_S_OK
)
386 status
= RPCRT4_OpenClientConnection(NewConnection
);
387 if (status
!= RPC_S_OK
)
389 RPCRT4_DestroyConnection(NewConnection
);
393 status
= RpcAssoc_BindConnection(assoc
, NewConnection
, InterfaceId
, TransferSyntax
);
394 if (status
!= RPC_S_OK
)
396 RPCRT4_DestroyConnection(NewConnection
);
400 *Connection
= NewConnection
;
405 void RpcAssoc_ReleaseIdleConnection(RpcAssoc
*assoc
, RpcConnection
*Connection
)
407 assert(!Connection
->server
);
408 EnterCriticalSection(&assoc
->cs
);
409 if (!assoc
->assoc_group_id
) assoc
->assoc_group_id
= Connection
->assoc_group_id
;
410 list_add_head(&assoc
->free_connection_pool
, &Connection
->conn_pool_entry
);
411 LeaveCriticalSection(&assoc
->cs
);
414 RPC_STATUS
RpcServerAssoc_AllocateContextHandle(RpcAssoc
*assoc
, void *CtxGuard
,
415 NDR_SCONTEXT
*SContext
)
417 RpcContextHandle
*context_handle
;
419 context_handle
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*context_handle
));
421 return ERROR_OUTOFMEMORY
;
423 context_handle
->ctx_guard
= CtxGuard
;
424 RtlInitializeResource(&context_handle
->rw_lock
);
425 context_handle
->refs
= 1;
427 /* lock here to mirror unmarshall, so we don't need to special-case the
428 * freeing of a non-marshalled context handle */
429 RtlAcquireResourceExclusive(&context_handle
->rw_lock
, TRUE
);
431 EnterCriticalSection(&assoc
->cs
);
432 list_add_tail(&assoc
->context_handle_list
, &context_handle
->entry
);
433 LeaveCriticalSection(&assoc
->cs
);
435 *SContext
= (NDR_SCONTEXT
)context_handle
;
439 BOOL
RpcContextHandle_IsGuardCorrect(NDR_SCONTEXT SContext
, void *CtxGuard
)
441 RpcContextHandle
*context_handle
= (RpcContextHandle
*)SContext
;
442 return context_handle
->ctx_guard
== CtxGuard
;
445 RPC_STATUS
RpcServerAssoc_FindContextHandle(RpcAssoc
*assoc
, const UUID
*uuid
,
446 void *CtxGuard
, ULONG Flags
, NDR_SCONTEXT
*SContext
)
448 RpcContextHandle
*context_handle
;
450 EnterCriticalSection(&assoc
->cs
);
451 LIST_FOR_EACH_ENTRY(context_handle
, &assoc
->context_handle_list
, RpcContextHandle
, entry
)
453 if (RpcContextHandle_IsGuardCorrect((NDR_SCONTEXT
)context_handle
, CtxGuard
) &&
454 !memcmp(&context_handle
->uuid
, uuid
, sizeof(*uuid
)))
456 *SContext
= (NDR_SCONTEXT
)context_handle
;
457 if (context_handle
->refs
++)
459 LeaveCriticalSection(&assoc
->cs
);
460 TRACE("found %p\n", context_handle
);
461 RtlAcquireResourceExclusive(&context_handle
->rw_lock
, TRUE
);
466 LeaveCriticalSection(&assoc
->cs
);
468 ERR("no context handle found for uuid %s, guard %p\n",
469 debugstr_guid(uuid
), CtxGuard
);
470 return ERROR_INVALID_HANDLE
;
473 RPC_STATUS
RpcServerAssoc_UpdateContextHandle(RpcAssoc
*assoc
,
474 NDR_SCONTEXT SContext
,
476 NDR_RUNDOWN rundown_routine
)
478 RpcContextHandle
*context_handle
= (RpcContextHandle
*)SContext
;
481 if (!RpcContextHandle_IsGuardCorrect((NDR_SCONTEXT
)context_handle
, CtxGuard
))
482 return ERROR_INVALID_HANDLE
;
484 EnterCriticalSection(&assoc
->cs
);
485 if (UuidIsNil(&context_handle
->uuid
, &status
))
487 /* add a ref for the data being valid */
488 context_handle
->refs
++;
489 UuidCreate(&context_handle
->uuid
);
490 context_handle
->rundown_routine
= rundown_routine
;
491 TRACE("allocated uuid %s for context handle %p\n",
492 debugstr_guid(&context_handle
->uuid
), context_handle
);
494 LeaveCriticalSection(&assoc
->cs
);
499 void RpcContextHandle_GetUuid(NDR_SCONTEXT SContext
, UUID
*uuid
)
501 RpcContextHandle
*context_handle
= (RpcContextHandle
*)SContext
;
502 *uuid
= context_handle
->uuid
;
505 static void RpcContextHandle_Destroy(RpcContextHandle
*context_handle
)
507 TRACE("freeing %p\n", context_handle
);
509 if (context_handle
->user_context
&& context_handle
->rundown_routine
)
511 TRACE("calling rundown routine %p with user context %p\n",
512 context_handle
->rundown_routine
, context_handle
->user_context
);
513 context_handle
->rundown_routine(context_handle
->user_context
);
516 RtlDeleteResource(&context_handle
->rw_lock
);
518 HeapFree(GetProcessHeap(), 0, context_handle
);
521 unsigned int RpcServerAssoc_ReleaseContextHandle(RpcAssoc
*assoc
, NDR_SCONTEXT SContext
, BOOL release_lock
)
523 RpcContextHandle
*context_handle
= (RpcContextHandle
*)SContext
;
527 RtlReleaseResource(&context_handle
->rw_lock
);
529 EnterCriticalSection(&assoc
->cs
);
530 refs
= --context_handle
->refs
;
532 list_remove(&context_handle
->entry
);
533 LeaveCriticalSection(&assoc
->cs
);
536 RpcContextHandle_Destroy(context_handle
);