mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1328 lines
35 KiB
1328 lines
35 KiB
/*++
|
|
|
|
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 <nt.h> // NTSTATUS, etc.
|
|
#include <ntrtl.h> // needed for nturtl.h
|
|
#include <nturtl.h> // needed for windows.h when I have nt.h
|
|
#include <windows.h> // win32 typedefs
|
|
#include <lmcons.h> // NET_API_STATUS
|
|
#include <rpc.h> // rpc prototypes
|
|
|
|
#include <netlib.h>
|
|
#include <netlibnt.h> // NetpNtStatusToApiStatus
|
|
#include <tstring.h> // STRSIZE, STRLEN, STRCPY, etc.
|
|
#include <rpcutil.h>
|
|
#include <ntrpcp.h> // RpcpBindRpc
|
|
|
|
// These may be included in any order:
|
|
|
|
#include <lmerr.h> // NetError codes
|
|
//#include <rpcutil.h> // MIDL_user_allocate(), MIDL_user_free().
|
|
#include <string.h> // for strcpy strcat strlen memcmp
|
|
#include <debuglib.h> // IF_DEBUG
|
|
#include <netdebug.h> // FORMAT_ equates, NetpKdPrint(()), NetpAssert(), etc.
|
|
#include <prefix.h> // 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; i<GlobalRpcCacheMaxSize; i++) {
|
|
|
|
//
|
|
// Check the cache entry to determine if an entry exists for this
|
|
// Interface.
|
|
//
|
|
|
|
if ( !RPC_CACHE_ENTRY_FREE(GlobalRpcCachePtr[i]) ) {
|
|
|
|
//
|
|
// If the ServerName, ServiceName, and ImpersonationLevel match
|
|
// the CacheEntry, then return the binding handle and index
|
|
// from that entry.
|
|
//
|
|
|
|
if((SAFE_STRCMP(GlobalRpcCachePtr[i]->ServerName, 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; i<GlobalRpcCacheMaxSize; i++) {
|
|
GlobalRpcCachePtr[i] = cacheEntryPtr++;
|
|
|
|
}
|
|
|
|
//
|
|
// Initialize the Global Cache Critical Section
|
|
//
|
|
InitializeCriticalSection(&RpcCacheCritSec);
|
|
|
|
#endif // TURN_ON_CACHING
|
|
//
|
|
// Get the Local Computername. (space is allocated by the function)
|
|
// If for some reason we are unable to get the name or allocate the
|
|
// space for it, then the global pointer to the name (LocalComputerName)
|
|
// will be set to NULL and life will go on.
|
|
//
|
|
status = NetpGetComputerName(&computerName);
|
|
|
|
if (status != NERR_Success) {
|
|
IF_DEBUG(RPC) {
|
|
NetpKdPrint((PREFIX_NETLIB
|
|
"[NetpBindRpc]InitRpcBindCache NetpGetComputerName Failed "
|
|
FORMAT_API_STATUS "\n", status));
|
|
}
|
|
LocalComputerName = NULL;
|
|
return(TRUE);
|
|
}
|
|
|
|
//
|
|
// Add Leading BackSlashes to ComputerName.
|
|
//
|
|
LocalComputerName = (LPWSTR)LocalAlloc(
|
|
LMEM_FIXED,
|
|
STRSIZE(computerName)+ (2*sizeof(TCHAR)));
|
|
|
|
if (LocalComputerName == NULL) {
|
|
IF_DEBUG(RPC) {
|
|
NetpKdPrint((PREFIX_NETLIB
|
|
"[NetpBindRpc]LocalAlloc(computerName) Failed "
|
|
FORMAT_API_STATUS "\n", GetLastError()));
|
|
}
|
|
}
|
|
STRCPY(LocalComputerName, TEXT("\\\\"));
|
|
STRCAT(LocalComputerName, computerName);
|
|
//
|
|
// Free up the memory allocated by NetpGetComputerName
|
|
//
|
|
(VOID) LocalFree(computerName);
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
#ifdef TURN_ON_CACHING
|
|
BOOL
|
|
NetpCloseRpcBindCache(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function unbinds from all the Rpc Handles stored in the cache.
|
|
It is called when a process detaches from the DLL.
|
|
|
|
NOTE: This function doesn't bother to free memory allocated with
|
|
LocalAlloc because it is assumed that will go away when the
|
|
process terminates.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
TRUE
|
|
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
|
|
//
|
|
// Loop through the Cache, and close all Rpc handles for entries
|
|
// that are not free. An entry is not free if it has a ServiceName
|
|
// pointer.
|
|
//
|
|
for(i=0; i<GlobalRpcCacheMaxSize; i++) {
|
|
|
|
if (!RPC_CACHE_ENTRY_FREE(GlobalRpcCachePtr[i])) {
|
|
IF_DEBUG(RPC) {
|
|
NetpKdPrint((PREFIX_NETLIB "[CLOSE_DLL] UnBinding Handle "
|
|
FORMAT_HANDLE "\n", GlobalRpcCachePtr[i]->RpcHandle));
|
|
}
|
|
|
|
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; i<GlobalRpcCacheMaxSize; i++) {
|
|
|
|
if( (!RPC_CACHE_ENTRY_FREE(GlobalRpcCachePtr[i])) &&
|
|
( GlobalRpcCachePtr[i]->RpcHandle == 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; i<GlobalRpcCacheMaxSize; i++) {
|
|
DumpCacheEntry(i, GlobalRpcCachePtr[i]);
|
|
}
|
|
NetpKdPrint(("\n"));
|
|
}
|
|
|
|
STATIC VOID
|
|
DumpCacheEntry(
|
|
DWORD EntryNum,
|
|
LPCACHE_ENTRY CacheEntryPtr
|
|
)
|
|
{
|
|
|
|
NetpKdPrint((PREFIX_NETLIB "****** RPC CACHE ENTRY #" FORMAT_DWORD " ******"
|
|
" SERVICE: " FORMAT_LPTSTR "\tSERVER: " FORMAT_LPTSTR "\n"
|
|
"Handle " FORMAT_HANDLE "\n"
|
|
"UseCount " FORMAT_DWORD "\n"
|
|
"NetWorkOptions " FORMAT_LPTSTR "\n",
|
|
EntryNum,
|
|
CacheEntryPtr->ServiceName,
|
|
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
|