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/unicode.h" 30 #include "wine/debug.h" 31 32 #include "rpc_binding.h" 33 #include "rpc_assoc.h" 34 #include "rpc_message.h" 35 36 WINE_DEFAULT_DEBUG_CHANNEL(rpc); 37 38 static CRITICAL_SECTION assoc_list_cs; 39 static CRITICAL_SECTION_DEBUG assoc_list_cs_debug = 40 { 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") } 44 }; 45 static CRITICAL_SECTION assoc_list_cs = { &assoc_list_cs_debug, -1, 0, 0, 0, 0 }; 46 47 static struct list client_assoc_list = LIST_INIT(client_assoc_list); 48 static struct list server_assoc_list = LIST_INIT(server_assoc_list); 49 50 static LONG last_assoc_group_id; 51 52 typedef struct _RpcContextHandle 53 { 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; 62 63 static void RpcContextHandle_Destroy(RpcContextHandle *context_handle); 64 65 static RPC_STATUS RpcAssoc_Alloc(LPCSTR Protseq, LPCSTR NetworkAddr, 66 LPCSTR Endpoint, LPCWSTR NetworkOptions, 67 RpcAssoc **assoc_out) 68 { 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; 87 } 88 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 !strcmpW(opts1, opts2); 96 } 97 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 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 || !strcmpW(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 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 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 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 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) 394 { 395 RpcConnection *NewConnection; 396 RPC_STATUS status; 397 398 *Connection = RpcAssoc_GetIdleConnection(assoc, InterfaceId, TransferSyntax, AuthInfo, QOS); 399 if (*Connection) 400 return RPC_S_OK; 401 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; 409 410 NewConnection->assoc = assoc; 411 status = RPCRT4_OpenClientConnection(NewConnection); 412 if (status != RPC_S_OK) 413 { 414 RPCRT4_ReleaseConnection(NewConnection); 415 return status; 416 } 417 418 status = RpcAssoc_BindConnection(assoc, NewConnection, InterfaceId, TransferSyntax); 419 if (status != RPC_S_OK) 420 { 421 RPCRT4_ReleaseConnection(NewConnection); 422 return status; 423 } 424 425 *Connection = NewConnection; 426 427 return RPC_S_OK; 428 } 429 430 void RpcAssoc_ReleaseIdleConnection(RpcAssoc *assoc, RpcConnection *Connection) 431 { 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); 438 } 439 440 RPC_STATUS RpcServerAssoc_AllocateContextHandle(RpcAssoc *assoc, void *CtxGuard, 441 NDR_SCONTEXT *SContext) 442 { 443 RpcContextHandle *context_handle; 444 445 context_handle = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*context_handle)); 446 if (!context_handle) 447 return RPC_S_OUT_OF_MEMORY; 448 449 context_handle->ctx_guard = CtxGuard; 450 RtlInitializeResource(&context_handle->rw_lock); 451 context_handle->refs = 1; 452 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); 456 457 EnterCriticalSection(&assoc->cs); 458 list_add_tail(&assoc->context_handle_list, &context_handle->entry); 459 LeaveCriticalSection(&assoc->cs); 460 461 *SContext = (NDR_SCONTEXT)context_handle; 462 return RPC_S_OK; 463 } 464 465 BOOL RpcContextHandle_IsGuardCorrect(NDR_SCONTEXT SContext, void *CtxGuard) 466 { 467 RpcContextHandle *context_handle = (RpcContextHandle *)SContext; 468 return context_handle->ctx_guard == CtxGuard; 469 } 470 471 RPC_STATUS RpcServerAssoc_FindContextHandle(RpcAssoc *assoc, const UUID *uuid, 472 void *CtxGuard, ULONG Flags, NDR_SCONTEXT *SContext) 473 { 474 RpcContextHandle *context_handle; 475 476 EnterCriticalSection(&assoc->cs); 477 LIST_FOR_EACH_ENTRY(context_handle, &assoc->context_handle_list, RpcContextHandle, entry) 478 { 479 if (RpcContextHandle_IsGuardCorrect((NDR_SCONTEXT)context_handle, CtxGuard) && 480 !memcmp(&context_handle->uuid, uuid, sizeof(*uuid))) 481 { 482 *SContext = (NDR_SCONTEXT)context_handle; 483 if (context_handle->refs++) 484 { 485 LeaveCriticalSection(&assoc->cs); 486 TRACE("found %p\n", context_handle); 487 RtlAcquireResourceExclusive(&context_handle->rw_lock, TRUE); 488 return RPC_S_OK; 489 } 490 } 491 } 492 LeaveCriticalSection(&assoc->cs); 493 494 ERR("no context handle found for uuid %s, guard %p\n", 495 debugstr_guid(uuid), CtxGuard); 496 return ERROR_INVALID_HANDLE; 497 } 498 499 RPC_STATUS RpcServerAssoc_UpdateContextHandle(RpcAssoc *assoc, 500 NDR_SCONTEXT SContext, 501 void *CtxGuard, 502 NDR_RUNDOWN rundown_routine) 503 { 504 RpcContextHandle *context_handle = (RpcContextHandle *)SContext; 505 RPC_STATUS status; 506 507 if (!RpcContextHandle_IsGuardCorrect((NDR_SCONTEXT)context_handle, CtxGuard)) 508 return ERROR_INVALID_HANDLE; 509 510 EnterCriticalSection(&assoc->cs); 511 if (UuidIsNil(&context_handle->uuid, &status)) 512 { 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); 519 } 520 LeaveCriticalSection(&assoc->cs); 521 522 return RPC_S_OK; 523 } 524 525 void RpcContextHandle_GetUuid(NDR_SCONTEXT SContext, UUID *uuid) 526 { 527 RpcContextHandle *context_handle = (RpcContextHandle *)SContext; 528 *uuid = context_handle->uuid; 529 } 530 531 static void RpcContextHandle_Destroy(RpcContextHandle *context_handle) 532 { 533 TRACE("freeing %p\n", context_handle); 534 535 if (context_handle->user_context && context_handle->rundown_routine) 536 { 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); 540 } 541 542 RtlDeleteResource(&context_handle->rw_lock); 543 544 HeapFree(GetProcessHeap(), 0, context_handle); 545 } 546 547 unsigned int RpcServerAssoc_ReleaseContextHandle(RpcAssoc *assoc, NDR_SCONTEXT SContext, BOOL release_lock) 548 { 549 RpcContextHandle *context_handle = (RpcContextHandle *)SContext; 550 unsigned int refs; 551 552 if (release_lock) 553 RtlReleaseResource(&context_handle->rw_lock); 554 555 EnterCriticalSection(&assoc->cs); 556 refs = --context_handle->refs; 557 if (!refs) 558 list_remove(&context_handle->entry); 559 LeaveCriticalSection(&assoc->cs); 560 561 if (!refs) 562 RpcContextHandle_Destroy(context_handle); 563 564 return refs; 565 } 566