/*++ Copyright (C) Microsoft Corporation, 1996 - 1999 Module Name: loader.cxx Abstract: Configuration and loading of RPC transports Revision History: MarioGo 03-18-96 Cloned from parts of old common.c MarioGo 10-31-96 Async RPC --*/ #include #include #include #include #include // Globals - see loader.hxx DWORD gdwComputerNameLength = 0; RPC_CHAR gpstrComputerName[MAX_COMPUTERNAME_LENGTH + 1]; UINT gPostSize = CO_MIN_RECV; #ifdef _INTERNAL_RPC_BUILD_ RPCLT_PDU_FILTER_FUNC gpfnFilter = NULL; #endif // // Used to convert numbers to hex strings // const RPC_CHAR HexDigits[] = { RPC_CONST_CHAR('0'), RPC_CONST_CHAR('1'), RPC_CONST_CHAR('2'), RPC_CONST_CHAR('3'), RPC_CONST_CHAR('4'), RPC_CONST_CHAR('5'), RPC_CONST_CHAR('6'), RPC_CONST_CHAR('7'), RPC_CONST_CHAR('8'), RPC_CONST_CHAR('9'), RPC_CONST_CHAR('A'), RPC_CONST_CHAR('B'), RPC_CONST_CHAR('C'), RPC_CONST_CHAR('D'), RPC_CONST_CHAR('E'), RPC_CONST_CHAR('F') }; // WARNING: The order of these protocols must be consistent with the // definition of PROTOCOL_ID. const TRANSPORT_TABLE_ENTRY TransportTable[] = { { 0, 0, 0 }, // TCP/IP { TCP_TOWER_ID, IP_ADDRESS_ID, (RPC_TRANSPORT_INTERFACE)&TCP_TransportInterface }, #ifdef SPX_ON // SPX { SPX_TOWER_ID, IPX_ADDRESS_ID, (RPC_TRANSPORT_INTERFACE)&SPX_TransportInterface }, #else { 0, 0, NULL }, #endif // Named pipes { NMP_TOWER_ID, UNC_ADDRESS_ID, (RPC_TRANSPORT_INTERFACE)&NMP_TransportInterface }, #ifdef NETBIOS_ON // Netbeui { NB_TOWER_ID, NBF_ADDRESS_ID, (RPC_TRANSPORT_INTERFACE)&NBF_TransportInterface }, // Netbios over TCP/IP { NB_TOWER_ID, IP_ADDRESS_ID, (RPC_TRANSPORT_INTERFACE)&NBT_TransportInterface }, // Netbios over IPX { NB_TOWER_ID, IPX_ADDRESS_ID, (RPC_TRANSPORT_INTERFACE)&NBI_TransportInterface }, #else // Netbeui { 0, 0, NULL }, // Netbios over TCP/IP { 0, 0, NULL }, // Netbios over IPX { 0, 0, NULL }, #endif #ifdef APPLETALK_ON // Appletalk Datastream protocol { DSP_TOWER_ID, NBP_ADDRESS_ID, (RPC_TRANSPORT_INTERFACE)&DSP_TransportInterface }, #else // Appletalk Datastream protocol { 0, 0, NULL }, #endif // Banyan Vines SSP { 0, 0, NULL }, // Hyper-Text Tranfer Protocol (HTTP) { HTTP_TOWER_ID, HTTP_ADDRESS_ID, (RPC_TRANSPORT_INTERFACE)&HTTP_TransportInterface }, // UDP/IP { UDP_TOWER_ID, IP_ADDRESS_ID, (RPC_TRANSPORT_INTERFACE)&UDP_TransportInterface }, #ifdef IPX_ON // IPX { IPX_TOWER_ID, IPX_ADDRESS_ID, (RPC_TRANSPORT_INTERFACE)&IPX_TransportInterface }, #else // IPX { 0, 0, 0 }, #endif // CDP/UDP/IP { CDP_TOWER_ID, IP_ADDRESS_ID, (RPC_TRANSPORT_INTERFACE)&CDP_TransportInterface }, #ifdef NCADG_MQ_ON // MSMQ (Falcon/RPC) { MQ_TOWER_ID, MQ_ADDRESS_ID, (RPC_TRANSPORT_INTERFACE)&MQ_TransportInterface }, #else // MSMQ (Falcon/RPC) { 0, 0, NULL }, #endif // TCP over IPv6 { TCP_TOWER_ID, IP_ADDRESS_ID, (RPC_TRANSPORT_INTERFACE)&TCP_TransportInterface }, // HTTP2 - same as HTTP in terms of contents. { HTTP_TOWER_ID, HTTP_ADDRESS_ID, (RPC_TRANSPORT_INTERFACE)&HTTP_TransportInterface } }; const DWORD cTransportTable = sizeof(TransportTable)/sizeof(TRANSPORT_TABLE_ENTRY); inline BOOL CompareProtseqs( IN const CHAR *p1, IN const RPC_CHAR *p2) // Note: protseqs use only ANSI characters so this is ok. { while(*p1) { if (*p1 != *p2) { return FALSE; } p1++; p2++; } return(*p2 == 0); } PROTOCOL_ID MapProtseq( IN const RPC_CHAR *RpcProtocolSequence ) { PROTOCOL_ID index; for(index = 1; index < cTransportTable; index++) { if (TransportTable[index].pInfo != NULL) { if (RpcpStringCompare(RpcProtocolSequence, TransportTable[index].pInfo->ProtocolSequence) == 0) { return(index); } } } TransDbgPrint((DPFLTR_RPCPROXY_ID, DPFLTR_WARNING_LEVEL, RPCTRANS "Called with unknown protseq %S\n", RpcProtocolSequence)); ASSERT(0); return(0); } PROTOCOL_ID MapProtseq( IN const CHAR *RpcProtocolSequence ) { PROTOCOL_ID index; for(index = 1; index < cTransportTable; index++) { if (TransportTable[index].pInfo != NULL) { if (CompareProtseqs(RpcProtocolSequence, TransportTable[index].pInfo->ProtocolSequence)) { return(index); } } } TransDbgPrint((DPFLTR_RPCPROXY_ID, DPFLTR_WARNING_LEVEL, RPCTRANS "Called with unknown protseq %S\n", RpcProtocolSequence)); ASSERT(0); return(0); } // NB: must be called before RpcCompletionPort is zeroed out, because it is used for comparison void FreeCompletionPortHashTable(void) { DWORD i; HANDLE hCurrentHandle; // walk through the table, not closing if there is next entry, and it is the same as this for (i = 0; i < gNumberOfProcessors * 2; i ++) { hCurrentHandle = RpcCompletionPorts[i]; if (hCurrentHandle && (hCurrentHandle != RpcCompletionPort)) { CloseHandle(hCurrentHandle); } } } HANDLE GetCompletionPortHandleForThread(void) { DWORD i; DWORD nMinLoad = (DWORD) -1; int nMinLoadIndex = -1; for (i = 0; i < gNumberOfProcessors * 2; i ++) { if ((DWORD)CompletionPortHandleLoads[i] < nMinLoad) { nMinLoadIndex = i; nMinLoad = CompletionPortHandleLoads[i]; } } ASSERT (nMinLoadIndex >= 0); InterlockedIncrement(&CompletionPortHandleLoads[nMinLoadIndex]); ASSERT(RpcCompletionPorts[nMinLoadIndex] != 0); return RpcCompletionPorts[nMinLoadIndex]; } void ReleaseCompletionPortHandleForThread(HANDLE h) { DWORD i; for (i = 0; i < gNumberOfProcessors * 2; i ++) { if (h == RpcCompletionPorts[i]) { InterlockedDecrement((long *)&CompletionPortHandleLoads[i]); ASSERT(CompletionPortHandleLoads[i] >= 0); return; } } ASSERT(0); } RPC_TRANSPORT_INTERFACE TransportLoad ( IN const RPC_CHAR * RpcProtocolSequence ) { static fLoaded = FALSE; RPC_STATUS RpcStatus; PROTOCOL_ID index; RPC_STATUS status; RPC_TRANSPORT_INTERFACE pInfo; if (fLoaded == FALSE) { RpcStatus = InitTransportProtocols(); if (RpcStatus != RPC_S_OK) return NULL; // // Query the computer name - used by most protocols. // gdwComputerNameLength = sizeof(gpstrComputerName)/sizeof(RPC_CHAR); if (!GetComputerName((RPC_SCHAR *)gpstrComputerName, &gdwComputerNameLength)) { TransDbgPrint((DPFLTR_RPCPROXY_ID, DPFLTR_WARNING_LEVEL, "RPCTRANS: GetComputerNameW failed: %d\n", GetLastError())); return(0); } gdwComputerNameLength++; // Include the null in the count. // Create initial IO completion port. This saves us from a race // assigning the global io completion port. ASSERT(RpcCompletionPort == 0); RpcCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0); // PERF REVIEW if (RpcCompletionPort == 0) { TransDbgPrint((DPFLTR_RPCPROXY_ID, DPFLTR_WARNING_LEVEL, RPCTRANS "Failed to create initial completion port: %d\n", GetLastError())); return(0); } InactiveRpcCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, MAXULONG); // PERF REVIEW if (InactiveRpcCompletionPort == 0) { TransDbgPrint((DPFLTR_RPCPROXY_ID, DPFLTR_WARNING_LEVEL, RPCTRANS "Failed to create initial completion port: %d\n", GetLastError())); CloseHandle(RpcCompletionPort); return(0); } HANDLE hCurrentCompletionPortHandle; DWORD i; BOOL fSuccess = TRUE; HANDLE hSourceProcessHandle = GetCurrentProcess(); RpcCompletionPorts = new HANDLE[gNumberOfProcessors * 2]; CompletionPortHandleLoads = new long[gNumberOfProcessors * 2]; if ((RpcCompletionPorts == NULL) || (CompletionPortHandleLoads == NULL)) { CloseHandle(RpcCompletionPort); RpcCompletionPort = 0; return 0; } for (i = 0; i < gNumberOfProcessors * 2; i ++) { RpcCompletionPorts[i] = 0; CompletionPortHandleLoads[i] = 0; } RpcCompletionPorts[0] = RpcCompletionPort; for (i = 1; i < gNumberOfProcessors * 2; i ++) { fSuccess = DuplicateHandle(hSourceProcessHandle, RpcCompletionPort, hSourceProcessHandle, &hCurrentCompletionPortHandle, 0, FALSE, DUPLICATE_SAME_ACCESS); if (!fSuccess) break; ASSERT(hCurrentCompletionPortHandle != 0); RpcCompletionPorts[i] = hCurrentCompletionPortHandle; } if (!fSuccess) { FreeCompletionPortHashTable(); CloseHandle(RpcCompletionPort); RpcCompletionPort = 0; return 0; } // // Initalize locks, use Rtl* so we don't need to catch exception. // NTSTATUS NtStatus; NtStatus = RtlInitializeCriticalSectionAndSpinCount(&AddressListLock, PREALLOCATE_EVENT_MASK); if (!NT_SUCCESS(NtStatus)) { FreeCompletionPortHashTable(); CloseHandle(RpcCompletionPort); RpcCompletionPort = 0; return 0; } if (fPagedBCacheMode) { // allocate minimum post size. This guarantees that buffer // will always be at the end. gPostSize = sizeof(CONN_RPC_HEADER); } fLoaded = TRUE; } index = MapProtseq(RpcProtocolSequence); if (!index) { return(0); } pInfo = 0; switch (index) { case NMP: pInfo = (RPC_TRANSPORT_INTERFACE) NMP_TransportLoad(); break; #ifdef NETBIOS_ON case NBF: case NBT: case NBI: pInfo = (RPC_TRANSPORT_INTERFACE) NB_TransportLoad(index); break; #endif case TCP: #ifdef SPX_ON case SPX: #endif #ifdef APPLETALK_ON case DSP: #endif case HTTP: pInfo = (RPC_TRANSPORT_INTERFACE) WS_TransportLoad(index); break; #ifdef NCADG_MQ_ON case MSMQ: #endif case CDP: case UDP: #ifdef IPX_ON case IPX: #endif pInfo = (RPC_TRANSPORT_INTERFACE) DG_TransportLoad(index); break; } if (pInfo == 0) { #ifdef UNICODE TransDbgPrint((DPFLTR_RPCPROXY_ID, DPFLTR_WARNING_LEVEL, RPCTRANS "Load of %S failed\n", RpcProtocolSequence)); #else TransDbgPrint((DPFLTR_RPCPROXY_ID, DPFLTR_WARNING_LEVEL, RPCTRANS "Load of %s failed\n", RpcProtocolSequence)); #endif return(0); } ASSERT(pInfo == TransportTable[index].pInfo); return(pInfo); } void UnjoinCompletionPort ( void ) { DWORD NumberOfBytes; ULONG_PTR CompletionKey; LPOVERLAPPED Overlapped; BOOL b; // The kernel today doesn't have the functionality to // unjoin a thread from a completion port. Therefore // we fake unjoining by joining another completion port which has // unlimited concurrency called the inactive completion port. // Thus threads unjoined from the main completion port will not // affect its concurrency. One undesirable effect is that each // time a thread joined to the inactive completion port blocks, // it will try to wake up another thread, and there won't be any // there, which is a waste of CPU. Ideally, we should have had // a capability to set KTHREAD::Queue to NULL, but we don't b = GetQueuedCompletionStatus(InactiveRpcCompletionPort, &NumberOfBytes, &CompletionKey, &Overlapped, 0 ); // this operation should either timeout or fail - it should never // succeed. If it does, this means somebody has erroneously posted // an IO on the inactive completion port ASSERT(b == FALSE); } #ifdef _INTERNAL_RPC_BUILD_ void I_RpcltDebugSetPDUFilter ( IN RPCLT_PDU_FILTER_FUNC pfnFilter ) { gpfnFilter = pfnFilter; } #endif