/*++ Copyright (c) 1991-1992 Microsoft Corporation Module Name: RpcBind.c Abstract: This file contains RPC Bind caching functions: (public functions) NetpBindRpc NetpCloseRpcBindCache NetpInitRpcBindCache NetpUnbindRpc (local functions) AddCacheEntry AdjustCache DecrementUseCount FindAddLocation FindCacheEntry CheckImpersonation (local debug functions) DumpBindCache DumpCacheEntry NOTE: Initialization is done via a dllinit routine for netapi.dll. Author: Dan Lafferty danl 25-Oct-1991 Environment: User Mode - Win32 Revision History: 12-Oct-1993 Danl #IFDEF out the caching code when we are not building with the cache. (Make it smaller). 15-Jan-1992 Danl Make sure LocalComputerName is not NULL prior to doing the string compare. Make the string compare case insensitive. 10-Jun-1992 JohnRo Tons of debug output changes. 25-Oct-1991 danl Created --*/ // These must be included first: #include // NTSTATUS, etc. #include // needed for nturtl.h #include // needed for windows.h when I have nt.h #include // win32 typedefs #include // NET_API_STATUS #include // rpc prototypes #include #include // NetpNtStatusToApiStatus #include // STRSIZE, STRLEN, STRCPY, etc. #include #include // RpcpBindRpc // These may be included in any order: #include // NetError codes //#include // MIDL_user_allocate(), MIDL_user_free(). #include // for strcpy strcat strlen memcmp #include // IF_DEBUG #include // FORMAT_ equates, NetpKdPrint(()), NetpAssert(), etc. #include // PREFIX_ equates. //********************************* // // Turn On Binding Caching. // (currently off) // //********************************* //#define TURN_ON_CACHING 1 // // CONSTANTS & MACROS // #ifndef FORMAT_HANDLE #define FORMAT_HANDLE "0x%lX" #endif #if DBG #define STATIC #else #define STATIC static #endif #define CACHE_MAX_SIZE 10 #define SET_RPC_CACHE_ENTRY_FREE(entryPtr) (entryPtr->ServiceName = NULL) #define RPC_CACHE_ENTRY_FREE(entryPtr) (entryPtr->ServiceName == NULL) // // RIPPLE_DOWN causes the array of cache pointers to be shifted down // to fill a hole in the 'i'th entry. This leaves a hole at the top. // #define RIPPLE_DOWN(i) while (i > 0) { \ GlobalRpcCachePtr[i] = GlobalRpcCachePtr[i-1]; \ i--; \ } // // This macro returns 0 if both pointers are NULL, or if they both // pointers point to identical strings. Otherwise, a non-zero value is // returned. // #define SAFE_STRCMP(x,y) \ ((((x)==NULL) || ((y)==NULL)) ? (x)!=(y) : STRCMP((x),(y))) #define SAFE_STRSIZE(x) (((x)==NULL) ? 0 : STRSIZE(x)) // // DataTypes // typedef struct _CACHE_ENTRY { LPTSTR ServiceName; LPTSTR ServerName; LPTSTR NetworkOptions; RPC_BINDING_HANDLE RpcHandle; INT UseCount; } CACHE_ENTRY, *LPCACHE_ENTRY; // // STATIC GLOBALS // // // Maintains a copy of the local computername. // static LPTSTR LocalComputerName; #ifdef TURN_ON_CACHING // // GlobalRpcCachePtr points to an array of pointers to CACHE_ENTRY structs. // The cache entries are also allocated in the same block. // static LPCACHE_ENTRY *GlobalRpcCachePtr; // // GlobalRpcCacheMaxSize Indicates the maximum number of cache entries // that the RPC binding cache manager will manage. // static DWORD GlobalRpcCacheMaxSize; // // Reads and Writes to the Cache is guarded with a critical section // static CRITICAL_SECTION RpcCacheCritSec; // // LOCAL FUNCTION PROTOTYPES // STATIC BOOL AddCacheEntry( IN LPTSTR ServerName, IN LPTSTR ServiceName, IN LPTSTR NetworkOptions, OUT LPDWORD Index ); STATIC VOID AdjustCache( DWORD Index ); STATIC BOOL DecrementUseCount( IN RPC_BINDING_HANDLE BindingHandle, OUT LPDWORD pIndex ); STATIC BOOL FindAddLocation( OUT LPDWORD pIndex ); STATIC BOOL FindCacheEntry( IN LPTSTR ServerName, IN LPTSTR ServiceName, IN LPTSTR NetworkOptions, OUT RPC_BINDING_HANDLE *BindingHandlePtr, OUT LPDWORD pEntryIndex ); BOOL CheckImpersonation( PSECURITY_IMPERSONATION_LEVEL pLevel ); #if DBG STATIC VOID DumpBindCache( VOID ); STATIC VOID DumpCacheEntry( DWORD EntryNum, LPCACHE_ENTRY CacheEntryPtr ); #endif #endif // TURN_ON_CACHING RPC_STATUS NetpBindRpc( IN LPTSTR ServerName, IN LPTSTR ServiceName, IN LPTSTR NetworkOptions, OUT RPC_BINDING_HANDLE *BindingHandlePtr ) /*++ Routine Description: Binds to the RPC server if possible. Arguments: ServerName - Name of server to bind with. This may be NULL if it is to bind with the local server. ServiceName - Name of service to bind with. This is typically the name of the interface as specified in the .idl file. Although it doesn't HAVE to be, it would be consistant to use that name. NetworkOptions - Supplies network options which describe the security to be used for named pipe instances created for this binding handle. BindingHandlePtr - Location where binding handle is to be placed. Return Value: NERR_Success if the binding was successful. An error value if it was not. --*/ { NTSTATUS ntStatus; NET_API_STATUS status = NERR_Success; LPTSTR tempServerName; #ifdef TURN_ON_CACHING DWORD entryIndex; BOOL entryFound; LPCACHE_ENTRY cacheEntry; SECURITY_IMPERSONATION_LEVEL level; #endif // // If the ServerName is Local, then make tempServerName // be a NULL pointer. // if ((ServerName == NULL) || (*ServerName == (TCHAR)'\0')) { tempServerName = NULL; } else if ((LocalComputerName != NULL) && (STRICMP(ServerName, LocalComputerName) == 0)) { tempServerName = NULL; } else { tempServerName = ServerName; } // // If this thread is impersonating a client, then skip all caching // code. // #ifdef TURN_ON_CACHING if (CheckImpersonation(&level)) { #endif // // Create a New binding // ntStatus = RpcpBindRpc( (LPWSTR)tempServerName, (LPWSTR)ServiceName, (LPWSTR)NetworkOptions, BindingHandlePtr); if ( ntStatus != RPC_S_OK ) { IF_DEBUG(RPC) { NetpKdPrint((PREFIX_NETLIB "[NetpBindRpc]RpcpBindRpc Failed " "(impersonating) "FORMAT_NTSTATUS "\n",ntStatus)); } } return(NetpNtStatusToApiStatus(ntStatus)); #ifdef TURN_ON_CACHING } // // Lock other threads from using the Cache. We will hold the lock until // we either find an entry for that interface and adjust the cache, or // until we have obtained a new cache entry for use. When a new cache // entry is obtained, it is locked until it can be updated with the // new bind handle. While the cache entry is locked, other cache // entries are available for use. // EnterCriticalSection(&RpcCacheCritSec); if (FindCacheEntry( tempServerName, ServiceName, NetworkOptions, BindingHandlePtr, &entryIndex)) { // // Entry was found. Move it to the top of the cache. // IF_DEBUG(RPC) { NetpKdPrint(( PREFIX_NETLIB "[NetpBindRpc]Handle Found in Cache\n")); } AdjustCache(entryIndex); } else { // // If there is an entry slot available in the cache, grab it and // fill in the Names and impersonation level. // entryFound = AddCacheEntry( tempServerName, ServiceName, NetworkOptions, &entryIndex); // // BUGBUG: Is this needed anymore? // if (entryFound) { cacheEntry = GlobalRpcCachePtr[entryIndex]; cacheEntry->UseCount = 128; } // // Create a New binding // ntStatus = RpcpBindRpc( (LPWSTR)tempServerName, (LPWSTR)ServiceName, (LPWSTR)NetworkOptions, BindingHandlePtr); if ( ntStatus != RPC_S_OK ) { IF_DEBUG(RPC) { NetpKdPrint((PREFIX_NETLIB "[NetpBindRpc]RpcpBindRpc Failed " FORMAT_NTSTATUS "\n",ntStatus)); } status = NetpNtStatusToApiStatus(ntStatus); if(entryFound) { // // We failed to bind! // Since we had obtained a cache entry, we must free it // and its associated memory, then unlock it. // (VOID) LocalFree(cacheEntry->ServiceName); cacheEntry->UseCount = 0; SET_RPC_CACHE_ENTRY_FREE(cacheEntry); } } else { // // The bind was successful. // if (entryFound) { // // If we have a cache entry to put the handle in, // add the new binding handle to the cache entry. // IF_DEBUG(RPC) { NetpKdPrint((PREFIX_NETLIB "[NetpBindRpc]Bind Success New handle added" "to the cache " FORMAT_HANDLE "\n", *BindingHandlePtr)); } cacheEntry->UseCount = 1; cacheEntry->RpcHandle = *BindingHandlePtr; } } } #if DBG IF_DEBUG(RPC) { cacheEntry = GlobalRpcCachePtr[entryIndex]; NetpKdPrint((PREFIX_NETLIB "[NetpBindRpc]Bind for " FORMAT_LPTSTR "," FORMAT_LPTSTR "\n", cacheEntry->ServerName, cacheEntry->ServiceName)); IF_DEBUG(RPCCACHE) { DumpBindCache(); } NetpKdPrint((PREFIX_NETLIB "[NetpBindRpc]RpcHandle = " FORMAT_HANDLE "\n\n",*BindingHandlePtr)); } #endif // DBG LeaveCriticalSection(&RpcCacheCritSec); return(status); #endif // TURN_ON_CACHING } RPC_STATUS NetpUnbindRpc( IN RPC_BINDING_HANDLE BindingHandle ) /*++ Routine Description: This function is called when the application desired to unbind an RPC handle. If the handle is cached, the handle is not actually unbound. Instead the entry for this handle has its UseCount decremented. If the handle is not in the cache, the RpcUnbind routine is called and the win32 mapped status is returned. Arguments: BindingHandle - This points to the binding handle that is to have its UseCount decremented. Return Value: NERR_Success - If the handle was successfully unbound, or if the cache entry UseCount was decremented. --*/ { RPC_STATUS status = NERR_Success; #ifdef TURN_ON_CACHING DWORD index; EnterCriticalSection(&RpcCacheCritSec); if (!DecrementUseCount(BindingHandle, &index)) { // // If the UseCount could not be decremented because the Binding // Handle is not in the Cache, then Unbind. // LeaveCriticalSection(&RpcCacheCritSec); #endif // TURN_ON_CACHING IF_DEBUG(RPC) { NetpKdPrint((PREFIX_NETLIB "[NetpUnbindRpc] UnBinding Handle " FORMAT_HANDLE "\n", BindingHandle)); } status = RpcpUnbindRpc( BindingHandle ); IF_DEBUG(RPC) { if (status) { NetpKdPrint((PREFIX_NETLIB "Unbind Failure! RpcUnbind = " FORMAT_NTSTATUS "\n",status)); } } status = NetpNtStatusToApiStatus(status); #ifdef TURN_ON_CACHING } else { // // If the UseCount was decremented, then simply return with a // success status. // LeaveCriticalSection(&RpcCacheCritSec); } #endif // TURN_ON_CACHING return(status); } #ifdef TURN_ON_CACHING STATIC BOOL FindCacheEntry( IN LPTSTR ServerName, IN LPTSTR ServiceName, IN LPTSTR NetworkOptions, OUT RPC_BINDING_HANDLE *BindingHandlePtr, OUT LPDWORD pEntryIndex ) /*++ Routine Description: This function scans through the cache to see if the interface description that is passed in matches any that are stored in the cache. If a match is found, the use count is incremented, and the EntryIndex is returned. NOTE: It is assumed that the Global RpcCacheCritSec Lock is held when this function is called. Arguments: ServerName - This is an optional parameter that points to a string containing the server name that we desire a connection to. If this is NULL or points to a NULL string, the connection is assumed to be local. ServiceName - This points to a string containing the name of the service. Typically, this string is the same string that is used as the interface name in the .idl file. NetworkOptions - Supplies network options which describe the security to be used for named pipe instances created for this binding handle. pEntryIndex - This is a pointer to a location where the index into the CacheEntryPtr Table is returned. This value is only good if the return value is TRUE. Return Value: TRUE - Indicates that the interface description was found in the cache. FALSE - Indicates that the interface description was not found in the cache. --*/ { DWORD i; for(i=0; iServerName, ServerName) == 0) && (STRCMP(GlobalRpcCachePtr[i]->ServiceName, ServiceName) == 0) && (SAFE_STRCMP(GlobalRpcCachePtr[i]->NetworkOptions, NetworkOptions)== 0)){ // // An entry for this interface was found in the Cache. // // if (!RPC_CACHE_ENTRY_FREE(GlobalRpcCachePtr[i])) { (GlobalRpcCachePtr[i]->UseCount)++; IF_DEBUG(RPC) { NetpKdPrint((PREFIX_NETLIB "Cache Entry Found. It is Number " FORMAT_DWORD ", handle = " FORMAT_HANDLE "\n", i,GlobalRpcCachePtr[i]->RpcHandle)); } *BindingHandlePtr = GlobalRpcCachePtr[i]->RpcHandle; *pEntryIndex = i; return(TRUE); } else { // // The entry went away. But since this design only // allows one unique ServerName-ServiceName-ImpLevel // in the cache, there is no point in going any // further because there won't be another entry of // this kind in the cache. // return(FALSE); } } } } // // An entry was not found in the Cache. // return(FALSE); } STATIC VOID AdjustCache( DWORD Index ) /*++ Routine Description: This function removes a cache entry pointer from its current location in the pointer array. Then each entry pointer is pushed down to fill the hole. Then the removed pointer is placed at the top of the array. NOTE: It is assumed that the Global RpcCacheCritSec Lock is held when this function is called. Arguments: Index - This is the index of the item in the table that is getting moved to the top. Return Value: none --*/ { LPCACHE_ENTRY tempPtr; NetpAssert(Index < GlobalRpcCacheMaxSize); if (Index == 0) { return; } // // Save away the pointer to the CacheRecord. // tempPtr = GlobalRpcCachePtr[Index]; // // Move all entries down one starting at the top of the table, and // ending at the location of Index. // RIPPLE_DOWN(Index) // // Fill in the top entry with the saved information. // GlobalRpcCachePtr[0] = tempPtr; return; } STATIC BOOL AddCacheEntry( IN LPTSTR ServerName, IN LPTSTR ServiceName, IN LPTSTR NetworkOptions, OUT LPDWORD IndexPtr ) /*++ Routine Description: This function Adds a new entry to the cache by finding the next available location in the cache. If a space is available, then new name space is allocated, and the server and service names are stored there. Then the pointer to that entry is placed at the top of the array of cache entry pointers because it is the most recently used. NOTE: It is assumed that the Global RpcCacheCritSec Lock is held when this function is called. Arguments: ServerName - This is a pointer to a string that identifies the server name for the cache entry. A NULL ServerName indicates the local computer. ServiceName - This is a pointer to a string that identifies the service name for the cache entry. NetworkOptions - Supplies network options which describe the security to be used for named pipe instances created for this binding handle. IndexPtr - The index of the added cache entry is placed into this pointer location. Return Value: TRUE - The operation was successful. FALSE - The entry was not added to the cache either because we couldn't allocate memory, or because there were no available slots in the cache. The function call did not lock an entry if it returned false. --*/ { DWORD i; DWORD bufferSize; if (!FindAddLocation(&i)) { // // There was no room in the cache for this entry, Therefore, // it will not be cached. // return(FALSE); } // // Adjust the cache so that the entry we are interested in is placed // at the top of the array of cache entry pointers. This indicates // that is is the most recently used. // AdjustCache(i); // // Calculate how many bytes are required to store both strings. // bufferSize = SAFE_STRSIZE(ServerName) + STRSIZE(ServiceName) + SAFE_STRSIZE(NetworkOptions); // // Allocate memory for the strings. // GlobalRpcCachePtr[0]->ServiceName = (LPWSTR)LocalAlloc(LMEM_FIXED, bufferSize); if (GlobalRpcCachePtr[0]->ServiceName == NULL) { // // ERROR // IF_DEBUG(RPC) { NetpKdPrint((PREFIX_NETLIB "[NetpBindRpc]AddCacheEntry:LocalAlloc Failed " FORMAT_API_STATUS "\n", (NET_API_STATUS) GetLastError())); } return(FALSE); } // // Copy the ServiceName text into the buffer. // // bufferSize keeps track of the offset to the next string pointer. // For instance, after the copy, the buffer size is set to the offset // of the next string (either ServiceName or NetworkOptions). // STRCPY(GlobalRpcCachePtr[0]->ServiceName, ServiceName); bufferSize = STRSIZE(ServiceName); // // Find the location of the pointer to the server name. This comes // right after the service name. // Then copy the text into the buffer. // if(ServerName == NULL) { GlobalRpcCachePtr[0]->ServerName = NULL; } else { GlobalRpcCachePtr[0]->ServerName = (LPTSTR) (((LPBYTE)GlobalRpcCachePtr[0]->ServiceName) + bufferSize); STRCPY(GlobalRpcCachePtr[0]->ServerName, ServerName); bufferSize += STRSIZE(ServerName); } // // Find the location of the pointer to the NetworkOptions string. // This comes right after the ServerName. // Then copy the text into the buffer. // if(NetworkOptions == NULL) { GlobalRpcCachePtr[0]->NetworkOptions = NULL; } else { GlobalRpcCachePtr[0]->NetworkOptions = (LPTSTR) ( ((LPBYTE)GlobalRpcCachePtr[0]->ServiceName) + bufferSize); STRCPY(GlobalRpcCachePtr[0]->NetworkOptions, NetworkOptions); } IF_DEBUG(RPC) { NetpKdPrint((PREFIX_NETLIB "New Cache Entry\n")); } *IndexPtr = 0; return(TRUE); } #endif // TURN_ON_CACHING BOOL NetpInitRpcBindCache( VOID ) /*++ Routine Description: This function is called whenever a new process attaches to the dll that it resides in. Arguments: none Return Value: TRUE - The initialization was successful. FALSE - A fatal error occured during the initialization. --*/ { LPTSTR computerName; NET_API_STATUS status; #ifdef TURN_ON_CACHING DWORD allocSize; DWORD i; LPCACHE_ENTRY cacheEntryPtr; //NetlibpTrace = NETLIB_DEBUG_RPC; // To turn on debugging GlobalRpcCacheMaxSize = CACHE_MAX_SIZE; allocSize = GlobalRpcCacheMaxSize * (sizeof(CACHE_ENTRY ) + sizeof(LPCACHE_ENTRY)); // // Allocate memory for the Cache and the table of pointers // GlobalRpcCachePtr = (LPCACHE_ENTRY *)LocalAlloc(LMEM_ZEROINIT, allocSize); if(GlobalRpcCachePtr == NULL) { IF_DEBUG(RPC) { NetpKdPrint((PREFIX_NETLIB "[NetpBindRpc]InitRpcBindCache LocalAlloc Failed " FORMAT_API_STATUS ".\n" PREFIX_NETLIB "[NetpBindRpc] Rpc Binding Caching will be disabled.\n", (NET_API_STATUS) GetLastError())); } // // If we cannot allocate memory for the cache, then we will // continue, but no caching will be done because the Cache size // is set to zero, and the GlobalRpcCachePtr is NULL. // GlobalRpcCacheMaxSize = 0; return(TRUE); } // // Fill in the CachePtr Table & initialize each entry critical section. // cacheEntryPtr = (LPCACHE_ENTRY)(GlobalRpcCachePtr + GlobalRpcCacheMaxSize); for(i=0; iRpcHandle)); } RpcpUnbindRpc(GlobalRpcCachePtr[i]->RpcHandle); IF_DEBUG(RPC) { if (GlobalRpcCachePtr[i]->UseCount > 0) { NetpKdPrint((PREFIX_NETLIB "Unclosed Handled for " FORMAT_LPTSTR "," FORMAT_LPTSTR " being forced closed\n", GlobalRpcCachePtr[i]->ServerName, GlobalRpcCachePtr[i]->ServiceName)); } } } } DeleteCriticalSection(&RpcCacheCritSec); return(TRUE); } STATIC BOOL FindAddLocation( OUT LPDWORD pIndex ) /*++ Routine Description: Finds a location in the table where a new cache entry can be stored. This function looks in the cache table for either an empty slot, or a replacable slot. IF EMPTY - return the index to it. IF REPLACABLE (use count < 2) UseCount = 0, unbind the handle and return the index UseCount = 1, remove the handle, it will be closed later. return the index to it. NOTE: If UseCount is >= 2, the entry is not replacable. Therefore, in the following code, the initial value for the savedUseCount is set to 1. The value of savedUseCount is updated if an entry's UseCount value is found to be less than 2. NOTE: It is assumed that the Global RpcCacheCritSec Lock is held when this function is called. Arguments: pIndex - This is a pointer to a location where the index to the usable slot is returned. Return Value: TRUE - A usable slot was found in the table, the returned index is usable. FALSE - A usable slot was NOT found in the table, the index is NOT usable. --*/ { INT i; DWORD savedIndex = 0; INT savedUseCount = 2; // // If caching is disabled, return FALSE. // if (GlobalRpcCacheMaxSize == 0) { return(FALSE); } // // Look through the whole table. We want to return an index to the // oldest, most expendable entry in the cache. // for(i=GlobalRpcCacheMaxSize-1; i >= 0; i--) { if (RPC_CACHE_ENTRY_FREE(GlobalRpcCachePtr[i])) { *pIndex = i; return(TRUE); } // // If the current entry's UseCount is better (less than) the // savedUseCount, then update the savedUseCount, and store away // the index for that entry. // if (GlobalRpcCachePtr[i]->UseCount < savedUseCount) { savedUseCount = GlobalRpcCachePtr[i]->UseCount; savedIndex = i; } } *pIndex = savedIndex; i = savedIndex; switch(savedUseCount) { case 0: // // This handle is currently not being used. Unbind from it. // // We need to get the lock prior to doing anything with the // binding handle. // if(RPC_CACHE_ENTRY_FREE(GlobalRpcCachePtr[i])) { return(FALSE); } IF_DEBUG(RPC) { NetpKdPrint((PREFIX_NETLIB "FindAddLocation: Entry #" FORMAT_DWORD " Being Removed From Cache (Handle Unbound): " FORMAT_HANDLE "\n", (DWORD) i, GlobalRpcCachePtr[i]->RpcHandle)); } RpcpUnbindRpc(GlobalRpcCachePtr[i]->RpcHandle); (VOID) LocalFree(GlobalRpcCachePtr[i]->ServiceName); GlobalRpcCachePtr[i]->RpcHandle = NULL; SET_RPC_CACHE_ENTRY_FREE(GlobalRpcCachePtr[i]); return(TRUE); case 1: // // The handle is in use by one caller. Therefore we can remove // it from the cache. The handle will be unbound when NetpUnbindRpc // is called. // if(RPC_CACHE_ENTRY_FREE(GlobalRpcCachePtr[i])) { return(TRUE); } IF_DEBUG(RPC) { NetpKdPrint((PREFIX_NETLIB "FindAddLocation: Entry #" FORMAT_DWORD " Being Removed From Cache (Unbind Later): " FORMAT_HANDLE "\n", (DWORD) i, GlobalRpcCachePtr[i]->RpcHandle)); } (VOID) LocalFree(GlobalRpcCachePtr[i]->ServiceName); GlobalRpcCachePtr[i]->RpcHandle = NULL; GlobalRpcCachePtr[i]->UseCount = 0; SET_RPC_CACHE_ENTRY_FREE(GlobalRpcCachePtr[i]); return(TRUE); case 2: // // All handles in the table are used by one or more callers. // IF_DEBUG(RPC) { NetpKdPrint((PREFIX_NETLIB "All Handles in Cache are used by one or more callers\n")); } return(FALSE); } } STATIC BOOL DecrementUseCount( IN RPC_BINDING_HANDLE BindingHandle, OUT LPDWORD pIndex ) /*++ Routine Description: This function searches through the Cache looking for a match with the BindingHandle. If a match is found, the UseCount for that entry is decremented. The Use Count should never go below 0. NOTE: It is assumed that the Global RpcCacheCritSec Lock is held when this function is called. Arguments: BindingHandle - This is the handle that is searched for in the cache. Return Value: TRUE - The handle exists in the table, and the UseCount was decremented. FALSE - The handle does not exist in the table. --*/ { DWORD i; for(i=0; iRpcHandle == BindingHandle) ) { // // The BindingHandle was found in the Cache. Decrement the // UseCount if it is not already 0. It should never be 0 when // this procedure is entered. // IF_DEBUG(RPC) { NetpKdPrint((PREFIX_NETLIB "Decrement UseCount for " FORMAT_HANDLE ". OldCount = " FORMAT_DWORD "\n\n", GlobalRpcCachePtr[i]->RpcHandle, (DWORD) GlobalRpcCachePtr[i]->UseCount)); } NetpAssert(GlobalRpcCachePtr[i]->UseCount > 0); if (GlobalRpcCachePtr[i]->UseCount > 0) { (GlobalRpcCachePtr[i]->UseCount)--; } *pIndex = i; return(TRUE); } } return(FALSE); } BOOL CheckImpersonation( PSECURITY_IMPERSONATION_LEVEL pLevel ) /*++ Routine Description: This function checks to see if the current thread is impersonating a client. If it is, it finds the level of impersonation. Arguments: pLevel - This is a pointer to the location where the impersonation level is to be placed. Return Value: TRUE - If this thread is impersonating. In this case, the pLevel variable will be filled in with correct information. FALSE - If the thread is not impersonating, or if any of the calls to find this out failed. In this case, the pLevel variable will NOT contain correction information. --*/ { HANDLE TokenHandle; DWORD ReturnLength; DWORD status; SECURITY_IMPERSONATION_LEVEL TokenInformation; if (!OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, // Desired Access FALSE, // OpenAsSelf &TokenHandle)) { status = GetLastError(); if (status != ERROR_NO_TOKEN) { IF_DEBUG(RPC) { NetpKdPrint((PREFIX_NETLIB "[CheckImpersonation]OpenThreadToken " "Failed %d\n",status)); } } return(FALSE); } else { if (!GetTokenInformation ( TokenHandle, TokenImpersonationLevel, (LPVOID)&TokenInformation, sizeof(SECURITY_IMPERSONATION_LEVEL), &ReturnLength)) { IF_DEBUG(RPC) { NetpKdPrint((PREFIX_NETLIB"[CheckImpersonation] " "GetTokenInformation Failed %d\n",GetLastError())); } CloseHandle(TokenHandle); return(FALSE); } else { *pLevel = TokenInformation; CloseHandle(TokenHandle); return(TRUE); } } } #if DBG STATIC VOID DumpBindCache( VOID ) { DWORD i; NetpKdPrint(("\n")); for(i=0; iServiceName, CacheEntryPtr->ServerName, CacheEntryPtr->RpcHandle, (DWORD) CacheEntryPtr->UseCount, CacheEntryPtr->NetworkOptions)); } #endif //DBG #else BOOL NetpCloseRpcBindCache( VOID ) { if ( LocalComputerName != NULL ) { (VOID) LocalFree(LocalComputerName); } return TRUE; } #endif // TURN_ON_CACHING