diff options
Diffstat (limited to 'private/ole32/com/dcomrem/channelb.cxx')
-rw-r--r-- | private/ole32/com/dcomrem/channelb.cxx | 2665 |
1 files changed, 2665 insertions, 0 deletions
diff --git a/private/ole32/com/dcomrem/channelb.cxx b/private/ole32/com/dcomrem/channelb.cxx new file mode 100644 index 000000000..c602b2f40 --- /dev/null +++ b/private/ole32/com/dcomrem/channelb.cxx @@ -0,0 +1,2665 @@ +//+--------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1993 - 1994. +// +// File: d:\nt\private\cairole\com\remote\channelb.cxx +// +// Contents: This module contains thunking classes that allow proxies +// and stubs to use a buffer interface on top of RPC for Cairo +// +// Classes: CRpcChannelBuffer +// +// Functions: +// ChannelThreadInitialize +// ChannelProcessInitialize +// ChannelRegisterProtseq +// ChannelThreadUninitialize +// ChannelProcessUninitialize +// CRpcChannelBuffer::AddRef +// CRpcChannelBuffer::AppInvoke +// CRpcChannelBuffer::CRpcChannelBuffer +// CRpcChannelBuffer::FreeBuffer +// CRpcChannelBuffer::GetBuffer +// CRpcChannelBuffer::QueryInterface +// CRpcChannelBuffer::Release +// CRpcChannelBuffer::SendReceive +// DebugCoSetRpcFault +// DllDebugObjectRPCHook +// ThreadInvoke +// ThreadSendReceive +// +// History: 22 Jun 93 AlexMi Created +// 31 Dec 93 ErikGav Chicago port +// 15 Mar 94 JohannP Added call category support. +// 09 Jun 94 BruceMa Get call category from RPC message +// 19 Jul 94 CraigWi Added support for ASYNC calls +// 01-Aug-94 BruceMa Memory sift fix +// +//---------------------------------------------------------------------- + +#include <ole2int.h> +#include <channelb.hxx> +#include <hash.hxx> // CUUIDHashTable +#include <riftbl.hxx> // gRIFTbl +#include <callctrl.hxx> // CAptRpcChnl, AptInvoke +#include <threads.hxx> // CRpcThreadCache +#include <service.hxx> // StopListen +#include <resolver.hxx> // CRpcResolver + +extern "C" +{ +#include "orpc_dbg.h" +} + +#include <rpcdcep.h> +#include <rpcndr.h> + +#include <obase.h> +#include <ipidtbl.hxx> +#include <security.hxx> +#include <chock.hxx> + + +// This is needed for the debug hooks. See orpc_dbg.h +#pragma code_seg(".orpc") + +/***************************************************************************/ +/* Defines. */ + +#define MAKE_WIN32( status ) \ + MAKE_SCODE( SEVERITY_ERROR, FACILITY_WIN32, (status) ) + +// This should just return a status to runtime, but runtime does not +// support both comm and fault status yet. +#ifdef _CHICAGO_ +#define RETURN_COMM_STATUS( status ) return (status) +#else +#define RETURN_COMM_STATUS( status ) RpcRaiseException( status ) +#endif + +// Flags for local rpc header. +// These are only valid on a request (in a ORPCTHIS header). +const int ORPCF_INPUT_SYNC = ORPCF_RESERVED1; +const int ORPCF_ASYNC = ORPCF_RESERVED2; + +// These are only valid on a reply (in a ORPCTHAT header). +const int ORPCF_REJECTED = ORPCF_RESERVED1; +const int ORPCF_RETRY_LATER = ORPCF_RESERVED2; + +// Default size of hash table. +const int INITIAL_NUM_BUCKETS = 20; + + +/***************************************************************************/ +/* Typedefs. */ + +// This structure contains a copy of all the information needed to make a +// call. It is copied so it can be canceled without stray pointer references. +const DWORD CALLCACHE_SIZE = 8; +struct working_call : public CChannelCallInfo +{ + working_call( CALLCATEGORY callcat, + RPCOLEMESSAGE *message, + DWORD flags, + REFIPID ipidServer, + DWORD destctx, + CRpcChannelBuffer *channel, + DWORD authn_level ); + void *operator new ( size_t ); + void operator delete( void * ); + static void Cleanup ( void ); + static void Initialize ( void ); + + RPCOLEMESSAGE message; + +private: + static void *list[CALLCACHE_SIZE]; + static DWORD next; +}; + +void *working_call::list[CALLCACHE_SIZE] = + { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; +DWORD working_call::next = 0; + + +/***************************************************************************/ +/* Macros. */ + +// Compute the size needed for the implicit this pointer including the +// various optional headers. +inline DWORD SIZENEEDED_ORPCTHIS( BOOL local, DWORD debug_size ) +{ + if (debug_size == 0) + return sizeof(WireThisPart1) + ((local) ? sizeof(LocalThis) : 0); + else + return sizeof(WireThisPart2) + ((local) ? sizeof(LocalThis) : 0) + + debug_size; +} + +inline DWORD SIZENEEDED_ORPCTHAT( DWORD debug_size ) +{ + if (debug_size == 0) + return sizeof(WireThatPart1); + else + return sizeof(WireThatPart2) + debug_size; +} + +inline CALLCATEGORY GetCallCat( void *header ) +{ + WireThis *inb = (WireThis *) header; + if (inb->c.flags & ORPCF_ASYNC) + return CALLCAT_ASYNC; + else if (inb->c.flags & ORPCF_INPUT_SYNC) + return CALLCAT_INPUTSYNC; + else + return CALLCAT_SYNCHRONOUS; +} + + +/***************************************************************************/ +/* Globals. */ + +// Should the debugger hooks be called? +BOOL DoDebuggerHooks = FALSE; +LPORPC_INIT_ARGS DebuggerArg = NULL; + +// The extension identifier for debug data. +const uuid_t DEBUG_EXTENSION = +{ 0xf1f19680, 0x4d2a, 0x11ce, {0xa6, 0x6a, 0x00, 0x20, 0xaf, 0x6e, 0x72, 0xf4}}; + +#if DBG == 1 +// strings that prefix the call +WCHAR *wszDebugORPCCallPrefixString[4] = { L"--> [BEG]", // Invoke + L" --> [end]", + L"<-- [BEG]", // SendReceive + L" <-- [end]" }; + +LONG ulDebugORPCCallNestingLevel[4] = {1, -1, 1, -1}; +#endif + + +SHashChain OIDBuckets[23] = { {&OIDBuckets[0], &OIDBuckets[0]}, + {&OIDBuckets[1], &OIDBuckets[1]}, + {&OIDBuckets[2], &OIDBuckets[2]}, + {&OIDBuckets[3], &OIDBuckets[3]}, + {&OIDBuckets[4], &OIDBuckets[4]}, + {&OIDBuckets[5], &OIDBuckets[5]}, + {&OIDBuckets[6], &OIDBuckets[6]}, + {&OIDBuckets[7], &OIDBuckets[7]}, + {&OIDBuckets[8], &OIDBuckets[8]}, + {&OIDBuckets[9], &OIDBuckets[9]}, + {&OIDBuckets[10], &OIDBuckets[10]}, + {&OIDBuckets[11], &OIDBuckets[11]}, + {&OIDBuckets[12], &OIDBuckets[12]}, + {&OIDBuckets[13], &OIDBuckets[13]}, + {&OIDBuckets[14], &OIDBuckets[14]}, + {&OIDBuckets[15], &OIDBuckets[15]}, + {&OIDBuckets[16], &OIDBuckets[16]}, + {&OIDBuckets[17], &OIDBuckets[17]}, + {&OIDBuckets[18], &OIDBuckets[18]}, + {&OIDBuckets[19], &OIDBuckets[19]}, + {&OIDBuckets[20], &OIDBuckets[20]}, + {&OIDBuckets[21], &OIDBuckets[21]}, + {&OIDBuckets[22], &OIDBuckets[22]} + }; + +CUUIDHashTable gClientRegisteredOIDs; + + +// flag whether or not the channel has been initialized for current process +BOOL gfChannelProcessInitialized = 0; +BOOL gfMTAChannelInitialized = 0; + +// count of multi-threaded apartment inits (see CoInitializeEx) +extern DWORD g_cMTAInits; + + +// Channel debug hook object. +CDebugChannelHook gDebugHook; + +// Channel error hook object. +CErrorChannelHook gErrorHook; + +#if DBG==1 +//------------------------------------------------------------------------- +// +// Function: GetInterfaceName +// +// synopsis: Gets the human readable name of an Interface given it's IID. +// +// History: 12-Jun-95 Rickhi Created +// +//------------------------------------------------------------------------- +LONG GetInterfaceName(REFIID riid, WCHAR *pwszName) +{ + // convert the iid to a string + CDbgGuidStr dbgsIID(riid); + + // Read the registry entry for the interface to get the interface name + LONG ulcb=256; + WCHAR szKey[80]; + + szKey[0] = L'\0'; + lstrcatW(szKey, L"Interface\\"); + lstrcatW(szKey, dbgsIID._wszGuid); + + LONG result = RegQueryValue( + HKEY_CLASSES_ROOT, + szKey, + pwszName, + &ulcb); + + Win4Assert( result == 0 ); + return result; +} + +//--------------------------------------------------------------------------- +// +// Function: DebugPrintORPCCall +// +// synopsis: Prints the interface name and method number to the debugger +// to allow simple ORPC call tracing. +// +// History: 12-Jun-95 Rickhi Created +// +//--------------------------------------------------------------------------- +void DebugPrintORPCCall(DWORD dwFlag, REFIID riid, DWORD iMethod, DWORD Callcat) +{ + if (CairoleInfoLevel & DEB_USER15) + { + Win4Assert (dwFlag < 4); + + // adjust the nesting level for this thread. + COleTls tls; + tls->cORPCNestingLevel += ulDebugORPCCallNestingLevel[dwFlag]; + + + // set the indentation string according to the nesting level + CHAR szNesting[100]; + memset(szNesting, 0x20, 100); + + if (tls->cORPCNestingLevel > 99) // watch for overflow + szNesting[99] = '\0'; + else + szNesting[tls->cORPCNestingLevel] = '\0'; + + + // construct the debug strings + WCHAR *pwszDirection = wszDebugORPCCallPrefixString[dwFlag]; + WCHAR wszName[100]; + GetInterfaceName(riid, wszName); + + ComDebOut((DEB_USER15, "%s%ws [%x] %ws:: %x\n", + szNesting, pwszDirection, Callcat, wszName, iMethod)); + } +} +#endif + +/***************************************************************************/ +void ByteSwapThis( DWORD drep, WireThis *inb ) +{ + if ((drep & NDR_LOCAL_DATA_REPRESENTATION) != NDR_LITTLE_ENDIAN) + { + // Extensions are swapped later. If we ever use the reserved field, + // swap it. + ByteSwapShort( inb->c.version.MajorVersion ); + ByteSwapShort( inb->c.version.MinorVersion ); + ByteSwapLong( inb->c.flags ); + // ByteSwapLong( inb->c.reserved1 ); + ByteSwapLong( inb->c.cid.Data1 ); + ByteSwapShort( inb->c.cid.Data2 ); + ByteSwapShort( inb->c.cid.Data3 ); + } +} + +/***************************************************************************/ +void ByteSwapThat( DWORD drep, WireThat *outb ) +{ + if ((drep & NDR_LOCAL_DATA_REPRESENTATION) != NDR_LITTLE_ENDIAN) + { + // Extensions are swapped later. + ByteSwapLong( outb->c.flags ); + } +} + +//+------------------------------------------------------------------- +// +// Function: ChannelProcessInitialize, public +// +// Synopsis: Initializes the channel subsystem per process data. +// +// History: 23-Nov-93 AlexMit Created +// +//-------------------------------------------------------------------- +STDAPI ChannelProcessInitialize() +{ + TRACECALL(TRACE_RPC, "ChannelProcessInitialize"); + ComDebOut((DEB_COMPOBJ, "ChannelProcessInitialize [IN]\n")); + + Win4Assert( (sizeof(WireThisPart1) & 7) == 0 ); + Win4Assert( (sizeof(WireThisPart2) & 7) == 0 ); + Win4Assert( (sizeof(LocalThis) & 7) == 0 ); + Win4Assert( (sizeof(WireThatPart1) & 7) == 0 ); + Win4Assert( (sizeof(WireThatPart2) & 7) == 0 ); + + // we want to take the gComLock since that prevents other Rpc + // threads from accessing anything we are about to create, in + // particular, the event cache and working_call cache are accessed + // by Rpc worker threads of cancelled calls. + + ASSERT_LOCK_RELEASED + LOCK + + HRESULT hr = S_OK; + + if (!gfChannelProcessInitialized) + { + // Initialize the interface hash tables, the OID hash table, and + // the MID hash table. We dont need to cleanup these on errors. + + gMIDTbl.Initialize(); + gOXIDTbl.Initialize(); + gRIFTbl.Initialize(); + gIPIDTbl.Initialize(); + gSRFTbl.Initialize(); + gClientRegisteredOIDs.Initialize(OIDBuckets); + + // Register the debug channel hook. + hr = CoRegisterChannelHook( DEBUG_EXTENSION, &gDebugHook ); + + // Register the error channel hook. + if(SUCCEEDED(hr)) + { + hr = CoRegisterChannelHook( ERROR_EXTENSION, &gErrorHook ); + } + + // Initialize security. + if (SUCCEEDED(hr)) + { + hr = InitializeSecurity(); + } + + // always set to TRUE if we initialized ANYTHING, regardless of + // whether there were any errors. That way, ChannelProcessUninit + // will cleanup anything we have initialized. + gfChannelProcessInitialized = TRUE; + } + + UNLOCK + ASSERT_LOCK_RELEASED + + if (FAILED(hr)) + { + // cleanup anything we have created thus far. + ChannelProcessUninitialize(); + } + + ComDebOut((DEB_COMPOBJ, "ChannelProcessInitialize [OUT] hr:%x\n", hr)); + return hr; +} + +//+------------------------------------------------------------------- +// +// Function: CleanupRegOIDs, public +// +// Synopsis: called to delete each node of the registered OID list. +// +//+------------------------------------------------------------------- +void CleanupRegOIDs(SHashChain *pNode) +{ + delete pNode; +} + +//+------------------------------------------------------------------- +// +// Function: ChannelProcessUninitialize, public +// +// Synopsis: Uninitializes the channel subsystem global data. +// +// History: 23-Nov-93 Rickhi Created +// +// Notes: This is called at process uninitialize, not thread +// uninitialize. +// +//-------------------------------------------------------------------- +STDAPI_(void) ChannelProcessUninitialize(void) +{ + TRACECALL(TRACE_RPC, "ChannelProcessUninitialize"); + ComDebOut((DEB_COMPOBJ, "ChannelProcessUninitialize [IN]\n")); + + if (gfChannelProcessInitialized) + { + // Stop accepting calls from the object resolver and flag that service + // is no longer initialized. This can result in calls being + // dispatched. Do not hold the lock around this call. + + UnregisterDcomInterfaces(); + } + + gResolver.ReleaseSCMProxy(); + + // we want to take the gComLock since that prevents other Rpc + // threads from accessing anything we are about to cleanup, in + // particular, the event cache and working_call are accessed by + // Rpc worker threaded for cancelled calls. + ASSERT_LOCK_RELEASED + LOCK + + if (gfChannelProcessInitialized) + { + // Release the interface tables. This causes RPC to stop dispatching + // DCOM calls. This can result in calls being dispatched. + // UnRegisterServerInterface releases and reaquires the lock each + // time it is called. + gRIFTbl.Cleanup(); + + if (gpLocalMIDEntry) + { + // release the local MIDEntry + DecMIDRefCnt(gpLocalMIDEntry); + gpLocalMIDEntry = NULL; + } + + // release the MTA apartment's OXIDEntry if there is one. Do this + // after the RIFTble cleanup so we are not processing any calls + // while it happens. + gOXIDTbl.ReleaseLocalMTAEntry(); + + if (gpsaCurrentProcess) + { + // delete the string bindings for the current process + PrivMemFree(gpsaCurrentProcess); + gpsaCurrentProcess = NULL; + } + + // cleanup the IPID, OXID, and MID tables + gOXIDTbl.FreeExpiredEntries(GetTickCount()+1); + gIPIDTbl.Cleanup(); + gOXIDTbl.Cleanup(); + gMIDTbl.Cleanup(); + gSRFTbl.Cleanup(); + + // Cleanup the OID registration table. + gClientRegisteredOIDs.Cleanup(CleanupRegOIDs); + + // Cleanup the call cache. + working_call::Cleanup(); + + // Release all cached threads. + gRpcThreadCache.ClearFreeList(); + + // cleanup the event cache + gEventCache.Cleanup(); + + // Cleanup the channel hooks. + CleanupChannelHooks(); + } + + // Always cleanup the RPC OXID resolver since security may initialize it. + gResolver.Cleanup(); + + // Cleanup security. + UninitializeSecurity(); + + // mark the channel as no longer intialized for this process + gfChannelProcessInitialized = FALSE; + gfMTAChannelInitialized = FALSE; + + UNLOCK + ASSERT_LOCK_RELEASED + + // release the static unmarshaler + if (gpStdMarshal) + { + ((CStdIdentity *)gpStdMarshal)->UnlockAndRelease(); + gpStdMarshal = NULL; + } + + ComDebOut((DEB_COMPOBJ, "ChannelProcessUninitialize [OUT]\n")); + return; +} + +//+------------------------------------------------------------------- +// +// Function: STAChannelInitialize, public +// +// Synopsis: Initializes the channel subsystem per thread data +// for single-threaded apartments. +// +// History: 23-Nov-93 Rickhi Created +// +// Notes: This is called at thread initialize, not process +// initialize. Cleanup is done in ChannelThreadUninitialize. +// +//-------------------------------------------------------------------- +STDAPI STAChannelInitialize(void) +{ + ComDebOut((DEB_COMPOBJ, "STAChannelInitialize [IN]\n")); + Win4Assert(IsSTAThread()); + + HRESULT hr = S_OK; + + if (!gfChannelProcessInitialized) + { + // process initialization has not been done, do that now. + if (FAILED(hr = ChannelProcessInitialize())) + return hr; + } + + // create the callctrl before calling ThreadStart, since the latter + // tries to register with the call controller. We might already have + // a callctrl if some DDE stuff has already run. + + COleTls tls; + + if (tls->pCallCtrl == NULL) + { + // assume OOM and try to create callctrl. ctor sets tls. + hr = E_OUTOFMEMORY; + CAptCallCtrl *pCallCtrl = new CAptCallCtrl(); + } + + if (tls->pCallCtrl) + { + // mark the channel as initialized now to prevent re-entracy in + // GetLocalEntry. + + tls->dwFlags |= OLETLS_CHANNELTHREADINITIALZED; + + // Precreate the thread window. The window is normally only used + // for requests (and thus created during marshalling). But in WOW + // it is used for responses (and thus created during initialization). + // We do it for normal cases here too in order to avoid recursion + // when marshaling the first interface. + + ASSERT_LOCK_RELEASED + LOCK + + OXIDEntry *pOxid; + hr = gOXIDTbl.GetLocalEntry( &pOxid ); + + UNLOCK + ASSERT_LOCK_RELEASED + + if (SUCCEEDED(hr)) + { + hr = ThreadStart(); + } + + // Clear the channel initialized flag if initialization fails. + // Everything gets cleaned up in uninitialize regardless of the + // channel flag. + if (FAILED(hr)) + tls->dwFlags &= ~OLETLS_CHANNELTHREADINITIALZED; + } + + ComDebOut((DEB_COMPOBJ, "STAChannelInitialize [OUT] hr:%x\n", hr)); + return hr; +} + +//+------------------------------------------------------------------- +// +// Function: MTAChannelInitialize, public +// +// Synopsis: Initializes the channel subsystem per thread data +// for multi-threaded apartments. +// +// History: 19-Mar-96 Rickhi Created +// +// Notes: This is called at thread initialize, not process +// initialize. Cleanup is done in ChannelThreadUninitialize. +// +//-------------------------------------------------------------------- +STDAPI MTAChannelInitialize(void) +{ + ComDebOut((DEB_COMPOBJ, "MTAChannelInitialize [IN]\n")); + Win4Assert(IsMTAThread()); + + HRESULT hr = S_OK; + + if (!gfChannelProcessInitialized) + { + // process initialization has not been done, do that now. + if (FAILED(hr = ChannelProcessInitialize())) + return hr; + } + + ASSERT_LOCK_RELEASED + LOCK + + if (!gfMTAChannelInitialized) + { + // Create the OXID entry for this apartment. Do it now to avoid + // any races with two threads creating it simultaneously. + + OXIDEntry *pOxid; + hr = gOXIDTbl.GetLocalEntry( &pOxid ); + if (SUCCEEDED(hr)) + { + pOxid->dwFlags &= ~ OXIDF_STOPPED; + gfMTAChannelInitialized = TRUE; + } + + } + + UNLOCK + ASSERT_LOCK_RELEASED + + ComDebOut((DEB_COMPOBJ, "MTAChannelInitialize [OUT] hr:%x\n", hr)); + return hr; +} + +//+------------------------------------------------------------------- +// +// Function: ChannelThreadUninitialize, private +// +// Synopsis: Uninitializes the channel subsystem per thread data. +// +// History: 23-Nov-93 Rickhi Created +// +// Notes: This is called at thread uninitialize, not process +// uninitialize. +// +//-------------------------------------------------------------------- +STDAPI_(void) ChannelThreadUninitialize(void) +{ + TRACECALL(TRACE_RPC, "ChannelThreadUninitialize"); + ComDebOut((DEB_COMPOBJ, "ChannelThreadUninitialize [IN]\n")); + + COleTls tls; + + if (tls->dwFlags & OLETLS_APARTMENTTHREADED) + { + // Cleanup the window for this thread. + ThreadCleanup(); + + // Free the apartment call control. + delete tls->pCallCtrl; + tls->pCallCtrl = NULL; + + // Free any registered MessageFilter that has not been picked + // up by the call ctrl. + if (tls->pMsgFilter) + { + tls->pMsgFilter->Release(); + tls->pMsgFilter = NULL; + } + + // release the OXIDEntry for this thread. + ASSERT_LOCK_RELEASED + LOCK + + gOXIDTbl.ReleaseLocalSTAEntry(); + + UNLOCK + ASSERT_LOCK_RELEASED + + // mark the thread as no longer intialized for the channel + tls->dwFlags &= ~OLETLS_CHANNELTHREADINITIALZED; + } + else + { + // the MTA channel is no longer initialized. + gfMTAChannelInitialized = FALSE; + } + + ComDebOut((DEB_COMPOBJ, "ChannelThreadUninitialize [OUT]\n")); +} + +// count of multi-threaded inits +//+------------------------------------------------------------------- +// +// Function: InitChannelIfNecessary, private +// +// Synopsis: Checks if the ORPC channel has been initialized for +// the current apartment and initializes if not. This is +// required by the delayed initialization logic. +// +// History: 26-Oct-95 Rickhi Created +// +//-------------------------------------------------------------------- +INTERNAL InitChannelIfNecessary() +{ + HRESULT hr; + COleTls tls(hr); + + if (FAILED(hr)) + return hr; + + if (!(tls->dwFlags & OLETLS_APARTMENTTHREADED)) + { + if (!gfMTAChannelInitialized) + { + if (g_cMTAInits > 0) + { + // initialize the MTAChannel + return MTAChannelInitialize(); + } + + // CoInitializeEx(MULTITHREADED) has not been called + return CO_E_NOTINITIALIZED; + } + } + else if (!(tls->dwFlags & OLETLS_CHANNELTHREADINITIALZED)) + { + if (tls->cComInits > 0 && + !(tls->dwFlags & OLETLS_THREADUNINITIALIZING)) + return STAChannelInitialize(); + + // CoInitializeEx(APARTMENTTHREADED) has not been called, + // or the thread is Uninitializing + return CO_E_NOTINITIALIZED; + } + + return S_OK; +} + + +/***************************************************************************/ +/* + Make a copy of a message for an asyncronous call. Also make a fake +reply for the call. +*/ +CChannelCallInfo *MakeAsyncCopy( CChannelCallInfo *original ) +{ + void *pBuffer = NULL; + WireThat *outb; + BOOL success; + + ASSERT_LOCK_HELD + + working_call *copy = new working_call( original->category, + original->pmessage, + original->iFlags, + original->ipid, + original->iDestCtx, + original->pChannel, + original->lAuthnLevel ); + + if (copy != NULL) + { + original->hResult = S_OK; + + if (original->pmessage->rpcFlags & RPCFLG_LOCAL_CALL) + { + // no need to duplicate the buffer, just use it as is. + copy->message.Buffer = original->pmessage->Buffer; + } + else + { + pBuffer = PrivMemAlloc8(original->pmessage->cbBuffer); + Win4Assert(((ULONG)pBuffer & 0x7) == 0 && "Buffer not aligned properly"); + + if (pBuffer != NULL) + { + copy->message.Buffer = pBuffer; + memcpy(pBuffer, original->pmessage->Buffer, + original->pmessage->cbBuffer); + } + else + { + original->hResult = RPC_E_OUT_OF_RESOURCES; + } + } + + if (SUCCEEDED(original->hResult)) + { + // pretend local so we don't touch rpc for more buffers, etc. + copy->message.rpcFlags |= RPCFLG_LOCAL_CALL; + + // Create a fake reply containing a result even though the + // client will never see it. + original->pmessage->cbBuffer = SIZENEEDED_ORPCTHAT(0) + 4; + + if (original->pmessage->rpcFlags & RPCFLG_LOCAL_CALL) + { + original->pmessage->Buffer = PrivMemAlloc8(original->pmessage->cbBuffer); + success = original->pmessage->Buffer != NULL; + } + else + { + success = I_RpcGetBuffer((RPC_MESSAGE *) original->pmessage) == RPC_S_OK; + } + + // simulate success in method call + if (success) + { + outb = (WireThat *) original->pmessage->Buffer; + outb->c.flags = ORPCF_NULL; + outb->c.unique = 0; + *(SCODE *)((WireThatPart1 *)outb + 1) = S_OK; + return copy; + } + } + + PrivMemFree(pBuffer); + delete copy; + } + + return NULL; +} + + +/***************************************************************************/ +STDMETHODIMP_(ULONG) CRpcChannelBuffer::AddRef( THIS ) +{ + // can't call AssertValid(FALSE) since it is used in asserts + InterlockedIncrement( (long *) &ref_count ); + return ref_count; +} + +/***************************************************************************/ +HRESULT CRpcChannelBuffer::AppInvoke( CChannelCallInfo *call, + IRpcStubBuffer *stub, + void *pv, + void *orig_stub_buffer, + LocalThis *localb ) +{ + ASSERT_LOCK_RELEASED + + RPC_MESSAGE *message = (RPC_MESSAGE *) call->pmessage; + void *orig_buffer = message->Buffer; + WireThat *outb = NULL; + HRESULT result; + + // Save a pointer to the inbound header. + call->pHeader = message->Buffer; + + // Adjust the buffer. + message->BufferLength -= (char *) orig_stub_buffer - (char *) message->Buffer; + message->Buffer = orig_stub_buffer; + message->ProcNum &= ~RPC_FLAGS_VALID_BIT; + + // if the incoming call is from a non-NDR client, then set a bit in + // the message flags field so the stub can figure out how to dispatch + // the call. This allows a 32bit server to simultaneously service a + // 32bit client using NDR and a 16bit client using non-NDR, in particular, + // to support OLE Automation. + if (localb != NULL && localb->flags & LOCALF_NONNDR) + message->RpcFlags |= RPCFLG_NON_NDR; + + if (IsMTAThread()) + { + // do multi-threaded apartment invoke + result = MTAInvoke((RPCOLEMESSAGE *)message, GetCallCat( call->pHeader ), + stub, this, &call->server_fault); + } + else + { + // do single-threaded apartment invoke + result = STAInvoke((RPCOLEMESSAGE *)message, GetCallCat( call->pHeader ), + stub, this, pv, &call->server_fault); + } + + // For local calls, just free the in buffer. For non-local calls, + // the RPC runtime does this for us. + if (message->RpcFlags & RPCFLG_LOCAL_CALL) + PrivMemFree( orig_buffer ); + + // If an exception occurred before a new buffer was allocated, + // set the Buffer field to point to the original buffer. + if (message->Buffer == orig_stub_buffer) + { + // The buffer pointer in the message must be correct so RPC can free it. + if (message->RpcFlags & RPCFLG_LOCAL_CALL) + message->Buffer = NULL; + else + message->Buffer = orig_buffer; + } + else if (message->Buffer != NULL) + { + // An out buffer exists, get the pointer to the channel header. + Win4Assert( call->pHeader != orig_buffer ); + message->BufferLength += (char *) message->Buffer - (char *) call->pHeader; + message->Buffer = call->pHeader; + outb = (WireThat *) message->Buffer; + } + + // If successful, adjust the buffer. + if (result == S_OK) + { + if (call->iDestCtx == MSHCTX_DIFFERENTMACHINE) + outb->c.flags = 0; + else + outb->c.flags = ORPCF_LOCAL; + + // For asynchronous calls, MSWMSG will delete the out buffer. If MSWMSG + // is not the transport, delete it here. Non-Mswmsg async calls have + // been converted to local calls. + if (message->RpcFlags & RPCFLG_LOCAL_CALL && + call->category == CALLCAT_ASYNC) + { + PrivMemFree( message->Buffer ); + message->Buffer = NULL; + } + } + else if (result == RPC_E_CALL_REJECTED) + { + // Call was rejected. If the caller is on another machine, just fail the + // call. + if (call->iDestCtx != MSHCTX_DIFFERENTMACHINE && outb != NULL) + { + // Otherwise return S_OK so the buffer gets back, but set the flag + // to indicate it was rejected. + outb->c.flags = ORPCF_LOCAL | ORPCF_REJECTED; + result = S_OK; + } + } + else if (result == RPC_E_SERVERCALL_RETRYLATER) + { + // Call was rejected. If the caller is on another machine, just fail the + // call. + if (call->iDestCtx != MSHCTX_DIFFERENTMACHINE && outb != NULL) + { + // Otherwise return S_OK so the buffer gets back, but set the flag + // to indicate it was rejected with retry later. + outb->c.flags = ORPCF_LOCAL | ORPCF_RETRY_LATER; + result = S_OK; + } + } + else if (message->RpcFlags & RPCFLG_LOCAL_CALL) + { + // call failed and the call is local, free the out buffer. For + // non-local calls the RPC runtime does this for us. + PrivMemFree( message->Buffer ); + message->Buffer = NULL; + } + + ASSERT_LOCK_RELEASED + return result; +} + +//+--------------------------------------------------------------------------- +// +// Function: AppInvokeExceptionFilter +// +// Synopsis: Determine if the application as thrown an exception we want +// to report. If it has, then print out enough information for +// the 'user' to debug the problem +// +// Arguments: [lpep] -- Exception context records +// +// History: 6-20-95 kevinro Created +// +// Notes: +// +// At the moment, I was unable to get this to work for Win95, so I have +// commented out the code. +// +//---------------------------------------------------------------------------- + + +#ifdef _CHICAGO_ + +// +// Win95 doesn't appear to support this functionality by default. +// + +inline LONG +AppInvokeExceptionFilter( + LPEXCEPTION_POINTERS lpep + ) +{ + return(EXCEPTION_EXECUTE_HANDLER); +} + +#else + +#include <imagehlp.h> + +#define SYM_HANDLE GetCurrentProcess() + +#if defined(_M_IX86) +#define MACHINE_TYPE IMAGE_FILE_MACHINE_I386 +#elif defined(_M_MRX000) +#define MACHINE_TYPE IMAGE_FILE_MACHINE_R4000 +#elif defined(_M_ALPHA) +#define MACHINE_TYPE IMAGE_FILE_MACHINE_ALPHA +#elif defined(_M_PPC) +#define MACHINE_TYPE IMAGE_FILE_MACHINE_POWERPC +#else +#error( "unknown target machine" ); +#endif + +LONG +AppInvokeExceptionFilter( + LPEXCEPTION_POINTERS lpep + ) +{ +#if DBG == 1 + BOOL rVal; + STACKFRAME StackFrame; + CONTEXT Context; + + + SymSetOptions( SYMOPT_UNDNAME ); + SymInitialize( SYM_HANDLE, NULL, TRUE ); + ZeroMemory( &StackFrame, sizeof(StackFrame) ); + Context = *lpep->ContextRecord; + +#if defined(_M_IX86) + StackFrame.AddrPC.Offset = Context.Eip; + StackFrame.AddrPC.Mode = AddrModeFlat; + StackFrame.AddrFrame.Offset = Context.Ebp; + StackFrame.AddrFrame.Mode = AddrModeFlat; + StackFrame.AddrStack.Offset = Context.Esp; + StackFrame.AddrStack.Mode = AddrModeFlat; +#endif + + + ComDebOut((DEB_FORCE,"An Exception occurred while calling into app\n")); + ComDebOut((DEB_FORCE, + "Exception address = 0x%x Exception number 0x%x\n", + lpep->ExceptionRecord->ExceptionAddress, + lpep->ExceptionRecord->ExceptionCode )); + + ComDebOut((DEB_FORCE,"The following stack trace is where the exception occured\n")); + ComDebOut((DEB_FORCE,"Frame RetAddr mod!symbol\n")); + do + { + rVal = StackWalk(MACHINE_TYPE,SYM_HANDLE,0,&StackFrame,&Context,ReadProcessMemory, + SymFunctionTableAccess,SymGetModuleBase,NULL); + + if (rVal) + { + DWORD dump[200]; + ULONG Displacement; + PIMAGEHLP_SYMBOL sym = (PIMAGEHLP_SYMBOL) &dump; + IMAGEHLP_MODULE ModuleInfo; + LPSTR pModuleName = "???"; + BOOL fSuccess; + + sym->SizeOfStruct = sizeof(dump); + + fSuccess = SymGetSymFromAddr(SYM_HANDLE,StackFrame.AddrPC.Offset, + &Displacement,sym); + + // + // If there is module name information available, then grab it. + // + if(SymGetModuleInfo(SYM_HANDLE,StackFrame.AddrPC.Offset,&ModuleInfo)) + { + pModuleName = ModuleInfo.ModuleName; + } + + if (fSuccess) + { + ComDebOut((DEB_FORCE, + "%08x %08x %s!%s + %x\n", + StackFrame.AddrFrame.Offset, + StackFrame.AddrReturn.Offset, + pModuleName, + sym->Name, + Displacement)); + } + else + { + ComDebOut((DEB_FORCE, + "%08x %08x %s!%08x\n", + StackFrame.AddrFrame.Offset, + StackFrame.AddrReturn.Offset, + pModuleName, + StackFrame.AddrPC.Offset)); + } + } + } while( rVal ); + + SymCleanup( SYM_HANDLE ); + +#endif + + return EXCEPTION_EXECUTE_HANDLER; +} +#endif // _CHICAGO_ + +/***************************************************************************/ +#if DBG == 1 +DWORD AppInvoke_break = 0; +DWORD AppInvoke_count = 0; +#endif + +HRESULT StubInvoke(RPCOLEMESSAGE *pMsg, IRpcStubBuffer *pStub, + IRpcChannelBuffer *pChnl, DWORD *pdwFault) +{ + ComDebOut((DEB_CHANNEL, "StubInvoke pMsg:%x pStub:%x pChnl:%x pdwFault:%x\n", + pMsg, pStub, pChnl, pdwFault)); + ASSERT_LOCK_RELEASED + + HRESULT hr; + +#if DBG==1 + DWORD dwMethod = pMsg->iMethod; + IID iidBeingCalled = ((RPC_SERVER_INTERFACE *) pMsg->reserved2[1])->InterfaceId.SyntaxGUID; +#endif + + _try + { + TRACECALL(TRACE_RPC, "StubInvoke"); +#if DBG == 1 + // + // On a debug build, we are able to break on a call by serial number. + // This isn't really 100% thread safe, but is still extremely useful + // when debugging a problem. + // + DWORD dwBreakCount = ++AppInvoke_count; + + ComDebOut((DEB_CHANNEL, "AppInvoke(0x%x) calling method 0x%x iid %I\n", + dwBreakCount,dwMethod, &iidBeingCalled)); + + if(AppInvoke_break == dwBreakCount) + { + DebugBreak(); + } +#endif + +#ifdef WX86OLE + if (! gcwx86.IsN2XProxy(pStub)) + { + IUnknown *pActual; + + hr = pStub->DebugServerQueryInterface((void **)&pActual); + if (SUCCEEDED(hr)) + { + if (gcwx86.IsN2XProxy(pActual)) + { + // If we are going to invoke a native stub that is + // connected to an object on the x86 side then + // set a flag in the Wx86 thread environment to + // let the thunk layer know that the call is a + // stub invoked call and allow any in or out + // custom interface pointers to be thunked as + // IUnknown rather than failing the interface thunking + gcwx86.SetStubInvokeFlag((BOOL)1); + } + pStub->DebugServerRelease(pActual); + } + } +#endif + + hr = pStub->Invoke(pMsg, pChnl); + + } + _except(AppInvokeExceptionFilter( GetExceptionInformation())) + { + hr = RPC_E_SERVERFAULT; + *pdwFault = GetExceptionCode(); + +#if DBG == 1 + // + // OLE catches exceptions when the server generates them. This is so we can + // cleanup properly, and allow the client to continue. + // + if (*pdwFault == STATUS_ACCESS_VIOLATION || + *pdwFault == STATUS_POSSIBLE_DEADLOCK || + *pdwFault == STATUS_INSTRUCTION_MISALIGNMENT || + *pdwFault == STATUS_DATATYPE_MISALIGNMENT ) + { + + WCHAR iidName[256]; + iidName[0] = 0; + char achProgname[256]; + achProgname[0] = 0; + + GetModuleFileNameA(NULL,achProgname,sizeof(achProgname)); + + GetInterfaceName(iidBeingCalled,iidName); + + ComDebOut((DEB_FORCE, + "OLE has caught a fault 0x%08x on behalf of the server %s\n", + *pdwFault, + achProgname)); + + ComDebOut((DEB_FORCE, + "The fault occured when OLE called the interface %I (%ws) method 0x%x\n", + &iidBeingCalled,iidName,dwMethod)); + + Win4Assert(!"The server application has faulted processing an inbound RPC request. Check the kernel debugger for useful output. OLE can continue but you probably want to stop and debug the application."); + } +#endif + } + + ASSERT_LOCK_RELEASED + ComDebOut((DEB_CHANNEL, "StubInvoke hr:%x dwFault:%x\n", hr, *pdwFault)); + return hr; +} + +/***************************************************************************/ +#if DBG==1 +LONG ComInvokeExceptionFilter( DWORD lCode, + LPEXCEPTION_POINTERS lpep ) +{ + ComDebOut((DEB_ERROR, "Exception 0x%x in ComInvoke at address 0x%x\n", + lCode, lpep->ExceptionRecord->ExceptionAddress)); + DebugBreak(); + return EXCEPTION_EXECUTE_HANDLER; +} +#endif + +/***************************************************************************/ +HRESULT ComInvoke( CChannelCallInfo *call ) +{ + TRACECALL(TRACE_RPC, "ComInvoke"); + ASSERT_LOCK_RELEASED + + RPC_MESSAGE *message = (RPC_MESSAGE *) call->pmessage; + LocalThis *localb; + void *saved_buffer; + RPC_STATUS status; + HRESULT result; + IPIDEntry *ipid_entry = NULL; + CRpcChannelBuffer *server_channel = NULL; + DWORD TIDCallerSaved; + BOOL fLocalSaved; + UUID saved_threadid; + IUnknown *save_context; + DWORD saved_authn_level; + char *stub_data; + WireThis *inb = (WireThis *) message->Buffer; + OXIDEntry *oxid; + HANDLE hWakeup = NULL; + + ComDebOut((DEB_CHANNEL, "ComInvoke callinfo:%x header:%x\n", + call, message->Buffer)); + + COleTls tls(result); + if (FAILED(result)) + return result; + + // Catch exceptions that might keep the lock. +#if DBG == 1 + _try + { +#endif + + // Find the IPID entry. Fail if the IPID or the OXID are not ready. + LOCK + ipid_entry = gIPIDTbl.LookupIPID( call->ipid ); + Win4Assert( ipid_entry == NULL || ipid_entry->pOXIDEntry != NULL ); + if (ipid_entry == NULL || (ipid_entry->dwFlags & IPIDF_DISCONNECTED) || + (ipid_entry->pOXIDEntry->dwFlags & OXIDF_STOPPED) || + ipid_entry->pChnl == NULL) + result = RPC_E_DISCONNECTED; + else if (ipid_entry->pStub == NULL) + result = E_NOINTERFACE; + + // Keep the server object and our associated objects alive during the call. + if (SUCCEEDED(result)) + { + oxid = ipid_entry->pOXIDEntry; + server_channel = ipid_entry->pChnl; + Win4Assert( server_channel != NULL && server_channel->pStdId != NULL ); + server_channel->pStdId->LockServer(); + InterlockedIncrement( &oxid->cCalls ); + } + UNLOCK + ASSERT_LOCK_RELEASED + + if (FAILED(result)) + { + return result; + } + + // Create a new security call context; + CServerSecurity security( call ); + save_context = tls->pCallContext; + tls->pCallContext = &security; + + // save the original threadid & copy in the new one. + if (!(tls->dwFlags & OLETLS_UUIDINITIALIZED)) + { + UuidCreate(&tls->LogicalThreadId); + tls->dwFlags |= OLETLS_UUIDINITIALIZED; + } + saved_threadid = tls->LogicalThreadId; + tls->LogicalThreadId = inb->c.cid; + + ComDebOut((DEB_CALLCONT, "ComInvoke: LogicalThreads Old:%I New:%I\n", + &saved_threadid, &tls->LogicalThreadId)); + + // Save the call info in TLS. + call->pNext = (CChannelCallInfo *) tls->pCallInfo; + tls->pCallInfo = call; + saved_authn_level = tls->dwAuthnLevel; + tls->dwAuthnLevel = call->lAuthnLevel; + + // Call the channel hooks. Set up as much TLS data as possible before + // calling the hooks so they can access it. + result = ServerNotify( + ((RPC_SERVER_INTERFACE *) message->RpcInterfaceInformation)->InterfaceId.SyntaxGUID, + (WireThis *) message->Buffer, + message->BufferLength, + (void **) &stub_data, + message->DataRepresentation ); + + // Find the local header. + if (inb->c.flags & ORPCF_LOCAL) + { + localb = (LocalThis *) stub_data; + stub_data += sizeof(LocalThis); + } + else + localb = NULL; + + // Set the caller TID. This is needed by some interop code in order + // to do focus management via tying queues together. We first save the + // current one so we can restore later to deal with nested calls + // correctly. + TIDCallerSaved = tls->dwTIDCaller; + fLocalSaved = tls->dwFlags & OLETLS_LOCALTID; + tls->dwTIDCaller = localb != NULL ? localb->client_thread : 0; + + if (call->iFlags & CF_PROCESS_LOCAL) + tls->dwFlags |= OLETLS_LOCALTID; // turn the local bit on + else + tls->dwFlags &= ~OLETLS_LOCALTID; // turn the local bit off + + // Continue dispatching the call. + if (result == S_OK) + { + result = server_channel->AppInvoke( + call, + (IRpcStubBuffer *) ipid_entry->pStub, + ipid_entry->pv, + stub_data, + localb ); + } + + // Restore the original thread id, call info, dest context and thread id. + tls->LogicalThreadId = saved_threadid; + tls->pCallInfo = call->pNext; + tls->dwTIDCaller = TIDCallerSaved; + tls->dwAuthnLevel = saved_authn_level; + + if (fLocalSaved) + tls->dwFlags |= OLETLS_LOCALTID; + else + tls->dwFlags &= ~OLETLS_LOCALTID; + + // Restore the security context; + tls->pCallContext = save_context; + security.EndCall(); + + // Decrement the call count. If the MTA is waiting to uninitialize + // and this is the last call, wake up the uninitializing thread, but + // do this *after* calling UnLockServer so the other thread does not + // blow away the server. + if (InterlockedDecrement( &oxid->cCalls ) == 0 && + (oxid->dwFlags & (OXIDF_MTASERVER | OXIDF_STOPPED)) == (OXIDF_MTASERVER | OXIDF_STOPPED)) + hWakeup = oxid->hComplete; + + // Release our hold on the object and channel. + server_channel->pStdId->UnLockServer(); + + if (hWakeup) + SetEvent(hWakeup); + + // Catch exceptions that might keep the lock. +#if DBG == 1 + } + _except( ComInvokeExceptionFilter(GetExceptionCode(), + GetExceptionInformation()) ) + { + } +#endif + + ASSERT_LOCK_RELEASED + return result; +} + +/***************************************************************************/ +CRpcChannelBuffer *CRpcChannelBuffer::Copy(OXIDEntry *pOXIDEntry, + REFIPID ripid, REFIID riid) +{ + Win4Assert( !(state & server_cs) ); + + CRpcChannelBuffer *chan; + + if (IsMTAThread()) + { + // make client side multi-threaded apartment version of channel + chan = new CMTARpcChnl(pStdId, pOXIDEntry, state); + } + else + { + // make client side single-threaded apartment version of channel + chan = new CAptRpcChnl(pStdId, pOXIDEntry, state); + } + + if (chan != NULL) + { + chan->state = proxy_cs | (state & ~client_cs); + chan->lAuthnLevel = lAuthnLevel; + } + + return chan; +} + +/***************************************************************************/ +HRESULT CRpcChannelBuffer::InitClientSideHandle() +{ + Win4Assert((state & proxy_cs)); + ASSERT_LOCK_HELD + + if (state & initialized_cs) + return S_OK; + + // Lookup the interface info. This cant fail. + pInterfaceInfo = gRIFTbl.GetClientInterfaceInfo(pIPIDEntry->iid); + + RPC_STATUS status; +#ifndef _CHICAGO_ + if (state & process_local_cs) + { + handle = NULL; + status = RPC_S_OK; + } + else +#endif + { + status = RpcBindingCopy(pOXIDEntry->hServerSTA, &handle); + + if (status == RPC_S_OK) + + // If this is a single threaded apartment, give LRPC the blocking + // hook. + if (state & mswmsg_cs) + status = I_RpcBindingSetAsync(handle, OleModalLoopBlockFn); + +#ifndef CHICAGO + // If the server is a single threaded apartment, tell LRPC to + // use MSWMSG to dispatch. + else if (pOXIDEntry->dwTid != 0) + status = I_RpcBindingSetAsync(handle, NULL); +#endif + + if (status == RPC_S_OK) + status = RpcBindingSetObject(handle, (GUID *)&pIPIDEntry->ipid); + } + + if (status == RPC_S_OK) + { + state |= initialized_cs; + return S_OK; + } + + return MAKE_WIN32(status); +} + + +/***************************************************************************/ +CRpcChannelBuffer::CRpcChannelBuffer(CStdIdentity *standard_identity, + OXIDEntry *pOXID, + DWORD eState ) +{ + ComDebOut((DEB_MARSHAL, "CRpcChannelBuffer %s Created this:%x pOXID:%x\n", + (eState & client_cs) ? "CLIENT" : "SERVER", this, pOXID)); + + // Fill in the easy fields first. + ref_count = 1; + pStdId = standard_identity; + handle = NULL; + pOXIDEntry = pOXID; + pIPIDEntry = NULL; + pInterfaceInfo = NULL; + hToken = NULL; + lAuthnLevel = gAuthnLevel; + state = eState; + state |= pOXID->dwPid == GetCurrentProcessId() ? process_local_cs : 0; + SetImpLevel( gImpLevel ); + + if ((pOXID->dwFlags & OXIDF_MSWMSG) && IsSTAThread()) + { + // use MSWMSG protocol with the blocking hook + state |= mswmsg_cs; + } + + if (state & (client_cs | proxy_cs)) + { + // Determine the destination context. + if (pOXID->dwFlags & OXIDF_MACHINE_LOCAL) + if (!IsWOWThread() && (state & process_local_cs)) + iDestCtx = MSHCTX_INPROC; + else + iDestCtx = MSHCTX_LOCAL; + else + iDestCtx = MSHCTX_DIFFERENTMACHINE; + } + else + { + // On the server side, the destination context isn't known + // untill a call arrives. + iDestCtx = 0; + } +} + +/***************************************************************************/ +CRpcChannelBuffer::~CRpcChannelBuffer() +{ + ComDebOut((DEB_MARSHAL, "CRpcChannelBuffer %s Deleted this:%x\n", + (state & server_cs) ? "SERVER" : "CLIENT", this)); + + if (handle != NULL) + RpcBindingFree( &handle ); + if (hToken != NULL) + CloseHandle( hToken ); +} + +/***************************************************************************/ +STDMETHODIMP CRpcChannelBuffer::FreeBuffer( RPCOLEMESSAGE *pMessage ) +{ + TRACECALL(TRACE_RPC, "CRpcChannelBuffer::FreeBuffer"); + ASSERT_LOCK_RELEASED + AssertValid(FALSE, TRUE); + + if (pMessage->Buffer == NULL) + return S_OK; + + // Pop the call stack. + COleTls tls; + Win4Assert( tls->pCallInfo != NULL ); + working_call *pCall = (working_call *) tls->pCallInfo; + tls->pCallInfo = pCall->pNext; + tls->dwAuthnLevel = pCall->lSavedAuthnLevel; + pMessage->Buffer = pCall->pHeader;; + + DeallocateBuffer(pCall->pmessage); + + // Resume any outstanding impersonation. + ResumeImpersonate( tls->pCallContext, pCall->iFlags & CF_WAS_IMPERSONATING ); + + // Release the AddRef we did earlier. Note that we cant do this until + // after DeallocateBuffer since it may release a binding handle that + // I_RpcFreeBuffer needs. + if (pCall->Locked()) + pStdId->UnLockClient(); + + pMessage->Buffer = NULL; + delete pCall; + + ASSERT_LOCK_RELEASED + return S_OK; +} + +//------------------------------------------------------------------------- +// +// Member: CRpcChannelBuffer::GetBuffer +// +// Synopsis: Calls ClientGetBuffer or ServerGetBuffer +// +//------------------------------------------------------------------------- +STDMETHODIMP CRpcChannelBuffer::GetBuffer( RPCOLEMESSAGE *pMessage, + REFIID riid ) +{ + gOXIDTbl.ValidateOXID(); + if (state & proxy_cs) + return ClientGetBuffer( pMessage, riid ); + else + return ServerGetBuffer( pMessage, riid ); +} + +//------------------------------------------------------------------------- +// +// Member: CRpcChannelBuffer::ClientGetBuffer +// +// Synopsis: Gets a buffer and sets up client side stuff +// +//------------------------------------------------------------------------- +HRESULT CRpcChannelBuffer::ClientGetBuffer( RPCOLEMESSAGE *pMessage, + REFIID riid ) +{ + TRACECALL(TRACE_RPC, "CRpcChannelBuffer::ClientGetBuffer"); + ASSERT_LOCK_RELEASED + + RPC_STATUS status; + CALLCATEGORY callcat = CALLCAT_SYNCHRONOUS; + ULONG debug_size; + ULONG num_extent; + WireThis *inb; + LocalThis *localb; + IID *logical_thread; + working_call *call; + DWORD flags; + BOOL resume; + DWORD orig_size = pMessage->cbBuffer; + COleTls tls; + + Win4Assert(state & proxy_cs); + AssertValid(FALSE, TRUE); + + // Don't allow remote calls if DCOM is disabled. + if (gDisableDCOM && iDestCtx == MSHCTX_DIFFERENTMACHINE) + return RPC_E_REMOTE_DISABLED; + + // Fetch the call category from the RPC message structure + if (pMessage->rpcFlags & RPCFLG_ASYNCHRONOUS) + { + // only allow async for these two interfaces for now + if (riid != IID_IAdviseSink && riid != IID_IAdviseSink2) + return E_UNEXPECTED; + callcat = CALLCAT_ASYNC; + } + else + { + logical_thread = TLSGetLogicalThread(); + if (logical_thread == NULL) + { + return RPC_E_OUT_OF_RESOURCES; + } + if (pMessage->rpcFlags & RPCFLG_INPUT_SYNCHRONOUS) + { + callcat = CALLCAT_INPUTSYNC; + } + } + + // Set the buffer complete flag for local calls. + pMessage->rpcFlags |= RPC_BUFFER_COMPLETE; + + // Note - RPC requires that the 16th bit of the proc num be set because + // we use the rpcFlags field of the RPC_MESSAGE struct. + pMessage->iMethod |= RPC_FLAGS_VALID_BIT; + + // if service object of destination is in same process, definitely local + // calls; async calls are also forced to be local. + if (state & process_local_cs) + { + pMessage->rpcFlags |= RPCFLG_LOCAL_CALL; + flags = CF_PROCESS_LOCAL; + } + else + flags = 0; + + // Find out if we need hook data. + debug_size = ClientGetSize( riid, &num_extent ); + + LOCK + + // Complete the channel initialization if needed. + status = InitClientSideHandle(); + if (status != RPC_S_OK) + { + UNLOCK; + ASSERT_LOCK_RELEASED; + return status; + } + + // Fill in the binding handle. Adjust the size. Clear the transfer + // syntax. Set the interface identifier. + if ((pMessage->rpcFlags & RPCFLG_LOCAL_CALL) == 0) + pMessage->reserved1 = handle; + pMessage->cbBuffer += SIZENEEDED_ORPCTHIS( pOXIDEntry->dwFlags & OXIDF_MACHINE_LOCAL, + debug_size ); + pMessage->reserved2[0] = 0; + pMessage->reserved2[1] = pInterfaceInfo; + Win4Assert( pMessage->reserved2[1] != NULL ); + + // Allocate a call record. + call = new working_call( callcat, pMessage, flags, pIPIDEntry->ipid, + iDestCtx, this, lAuthnLevel ); + pMessage->cbBuffer = orig_size; + UNLOCK + ASSERT_LOCK_RELEASED + + if (call == NULL) + return E_OUTOFMEMORY; + + // Suspend any outstanding impersonation and ignore failures. + SuspendImpersonate( tls->pCallContext, &resume ); + + // Get a buffer. + if (call->pmessage->rpcFlags & RPCFLG_LOCAL_CALL) + { + // NDR_DREP_ASCII | NDR_DREP_LITTLE_ENDIAN | NDR_DREP_IEEE + call->pmessage->dataRepresentation = 0x00 | 0x10 | 0x0000; + call->pmessage->Buffer = PrivMemAlloc8( call->pmessage->cbBuffer ); + if (call->pmessage->Buffer == NULL) + status = RPC_S_OUT_OF_MEMORY; + else + status = RPC_S_OK; + } + else + { + TRACECALL(TRACE_RPC, "I_RpcGetBuffer"); + status = I_RpcGetBuffer( (RPC_MESSAGE *) call->pmessage ); + } + + if (status != RPC_S_OK) + { + // Resume any outstanding impersonation. + ResumeImpersonate( tls->pCallContext, resume ); + + // Cleanup. + pMessage->cbBuffer = 0; + tls->fault = MAKE_WIN32( status ); + delete call; + return MAKE_WIN32( status ); + } + + // Save the impersonation flag. + if (resume) + call->iFlags |= CF_WAS_IMPERSONATING; + + // Chain the call info in TLS. + call->pNext = (CChannelCallInfo *)tls->pCallInfo; + tls->pCallInfo = call; + call->pHeader = call->message.Buffer; + + // Adjust the authentication level in TLS. + call->lSavedAuthnLevel = tls->dwAuthnLevel; + tls->dwAuthnLevel = lAuthnLevel; + + // Fill in the COM header. + pMessage->Buffer = call->message.Buffer; + inb = (WireThis *) pMessage->Buffer; + inb->c.version.MajorVersion = COM_MAJOR_VERSION; + inb->c.version.MinorVersion = COM_MINOR_VERSION; + inb->c.reserved1 = 0; + + // Generate a new logical thread for async calls. + if (callcat == CALLCAT_ASYNC) + UuidCreate( &inb->c.cid ); + // Find the logical thread id. + else + inb->c.cid = *logical_thread; + + if (pOXIDEntry->dwFlags & OXIDF_MACHINE_LOCAL) + inb->c.flags = ORPCF_LOCAL; + else + inb->c.flags = ORPCF_NULL; + + // Fill in any hook data and adjust the buffer pointer. + if (debug_size != 0) + { + pMessage->Buffer = FillBuffer( riid, &inb->d.ea, debug_size, num_extent, + TRUE ); + inb->c.unique = 0x77646853; // Any non-zero value. + } + else + { + pMessage->Buffer = (void *) &inb->d.ea; + inb->c.unique = FALSE; + } + + // Fill in the local header. + if (pOXIDEntry->dwFlags & OXIDF_MACHINE_LOCAL) + { + localb = (LocalThis *) pMessage->Buffer; + localb->client_thread = GetCurrentApartmentId(); + localb->flags = 0; + pMessage->Buffer = localb + 1; + if (callcat == CALLCAT_ASYNC) + inb->c.flags |= ORPCF_ASYNC; + else if (callcat == CALLCAT_INPUTSYNC) + inb->c.flags |= ORPCF_INPUT_SYNC; + + // if the caller is using a non-NDR proxy, set a bit in the local + // header flags so that server side stub knows which way to unmarshal + // the parameters. This lets a 32bit server simultaneously service calls + // from 16bit non-NDR clients and 32bit NDR clients, in particular, to + // support OLE Automation. + + if (pIPIDEntry->dwFlags & (IPIDF_NONNDRPROXY | IPIDF_NONNDRSTUB)) + localb->flags |= LOCALF_NONNDR; + } + + ComDebOut((DEB_CALLCONT, "ClientGetBuffer: LogicalThreadId:%I\n", + &(tls->LogicalThreadId))); + + ASSERT_LOCK_RELEASED + return S_OK; +} + +//------------------------------------------------------------------------- +// +// Member: CRpcChannelBuffer::ServerGetBuffer +// +// Synopsis: Gets a buffer and sets up server side stuff +// +//------------------------------------------------------------------------- +HRESULT CRpcChannelBuffer::ServerGetBuffer( RPCOLEMESSAGE *pMessage, + REFIID riid ) +{ + TRACECALL(TRACE_RPC, "CRpcChannelBuffer::ServerGetBuffer"); + ASSERT_LOCK_RELEASED + + RPC_STATUS status; + ULONG debug_size; + ULONG num_extent; + HRESULT result = S_OK; + WireThis *inb; + WireThat *outb; + CChannelCallInfo *call; + void *stub_data; + DWORD orig_size = pMessage->cbBuffer; + + Win4Assert( state & server_cs ); + + AssertValid(FALSE, TRUE); + + // Get the call info from TLS. + COleTls tls; + call = (CChannelCallInfo *) tls->pCallInfo; + Win4Assert( call != NULL ); + + // Find out if we need debug data. + pMessage->Buffer = call->pHeader; + debug_size = ServerGetSize( riid, &num_extent ); + + // Adjust the buffer size. + pMessage->cbBuffer += SIZENEEDED_ORPCTHAT( debug_size ); + + // Get a buffer. + if (pMessage->rpcFlags & RPCFLG_LOCAL_CALL) + { + // NDR_DREP_ASCII | NDR_DREP_LITTLE_ENDIAN | NDR_DREP_IEEE + pMessage->dataRepresentation = 0x00 | 0x10 | 0x0000; + pMessage->Buffer = PrivMemAlloc8( pMessage->cbBuffer ); + if (pMessage->Buffer == NULL) + status = RPC_S_OUT_OF_MEMORY; + else + status = RPC_S_OK; + } + else + { + TRACECALL(TRACE_RPC, "I_RpcGetBuffer"); + status = I_RpcGetBuffer( (RPC_MESSAGE *) pMessage ); + Win4Assert( call->pHeader != pMessage->Buffer || status != RPC_S_OK ); + } + + if (status != RPC_S_OK) + { + pMessage->cbBuffer = 0; + pMessage->Buffer = NULL; + tls->fault = MAKE_WIN32( status ); + return MAKE_WIN32( status ); + } + + // Fill in the outbound COM header. + call->pHeader = pMessage->Buffer; + outb = (WireThat *) pMessage->Buffer; + outb->c.flags = ORPCF_NULL; + pMessage->cbBuffer = orig_size; + if (debug_size != 0) + { + stub_data = FillBuffer( riid, &outb->d.ea, debug_size, num_extent, FALSE ); + outb->c.unique = 0x77646853; // Any non-zero value. + pMessage->Buffer = stub_data; + } + else + { + outb->c.unique = 0; + pMessage->Buffer = &outb->d.ea; + } + + ComDebOut((DEB_CALLCONT, "ServerGetBuffer: LogicalThreadId:%I\n", + &(tls->LogicalThreadId))); + ASSERT_LOCK_RELEASED + return S_OK; +} + +/***************************************************************************/ +STDMETHODIMP CRpcChannelBuffer::GetDestCtx( DWORD FAR* lpdwDestCtx, + LPVOID FAR* lplpvDestCtx ) +{ + TRACECALL(TRACE_RPC, "CRpcChannelBuffer::GetDestCtx"); + AssertValid(FALSE, FALSE); + + // On the client side, get the destination context from the channel. + if (state & (client_cs | proxy_cs)) + { + *lpdwDestCtx = iDestCtx; + } + + // On the server side, get the destination context from TLS. + else + { + COleTls tls; + Win4Assert( tls->pCallInfo != NULL ); + *lpdwDestCtx = ((CChannelCallInfo *) tls->pCallInfo)->iDestCtx; + } + + if (lplpvDestCtx != NULL) + *lplpvDestCtx = NULL; + + return S_OK; +} + +/***************************************************************************/ +STDMETHODIMP CRpcChannelBuffer::IsConnected( THIS ) +{ + // must be on right thread because it is only called by proxies and stubs. + AssertValid(FALSE, TRUE); + + // Server channels never know if they are connected. The only time the + // client side knows it is disconnected is after the standard identity + // has disconnected the proxy from the channel. In that case it doesn't + // matter. + return S_OK; +} + +/***************************************************************************/ +STDMETHODIMP CRpcChannelBuffer::QueryInterface( THIS_ REFIID riid, LPVOID FAR* ppvObj) +{ + AssertValid(FALSE, FALSE); + + // IMarshal is queried more frequently than any other interface, so + // check for that first. + + if (IsEqualIID(riid, IID_IMarshal)) + { + *ppvObj = (IMarshal *) this; + } + else if (IsEqualIID(riid, IID_IUnknown) || + IsEqualIID(riid, IID_IRpcChannelBuffer)) + { + *ppvObj = (IRpcChannelBuffer *) this; + } + else if (IsEqualIID(riid, IID_INonNDRStub) && + (state & proxy_cs) && pIPIDEntry && + (pIPIDEntry->dwFlags & IPIDF_NONNDRSTUB)) + { + // this interface is used to tell proxies whether the server side speaks + // NDR or not. Returns S_OK if NOT NDR. + *ppvObj = (IUnknown *) this; + } + else + { + *ppvObj = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +/***************************************************************************/ +STDMETHODIMP_(ULONG) CRpcChannelBuffer::Release( THIS ) +{ + // can't call AssertValid(FALSE) since it is used in asserts + ULONG lRef = ref_count - 1; + + if (InterlockedDecrement( (long*) &ref_count ) == 0) + { + delete this; + return 0; + } + else + { + return lRef; + } +} + +/***************************************************************************/ +STDMETHODIMP CRpcChannelBuffer::SendReceive( THIS_ RPCOLEMESSAGE *pMessage, + ULONG *status ) +{ + return CRpcChannelBuffer::SendReceive2(pMessage, status); +} + +/***************************************************************************/ +STDMETHODIMP CRpcChannelBuffer::SendReceive2( THIS_ RPCOLEMESSAGE *pMessage, + ULONG *status ) +{ + TRACECALL(TRACE_RPC, "CRpcChannelBuffer::SendReceive"); + ComDebOut((DEB_CHANNEL, "CRpcChannelBuffer::SendReceive pChnl:%x pMsg:%x\n", + this, pMessage)); + + AssertValid(FALSE, TRUE); + Win4Assert( state & proxy_cs ); + gOXIDTbl.ValidateOXID(); + ASSERT_LOCK_RELEASED + + HRESULT result; + working_call *call; + working_call *next_call; + IID iid; + WireThis *inb; + WireThat *outb; + DWORD saved_authn_level; + BOOL resume; + char *stub_data; + + // Get the information about the call stored in TLS + COleTls tls; + call = (working_call *) tls->pCallInfo; + Win4Assert( call != NULL ); + next_call = (working_call *) call->pNext; + saved_authn_level = call->lSavedAuthnLevel; + resume = call->iFlags & CF_WAS_IMPERSONATING; + + // Set up the header pointers. + inb = (WireThis *) call->pHeader; + iid = + ((RPC_CLIENT_INTERFACE *) ((RPC_MESSAGE *) call->pmessage)->RpcInterfaceInformation)->InterfaceId.SyntaxGUID; + + // we must ensure that we dont go away during this call. we will Release + // ourselves in the FreeBuffer call, or in the error handling at the + // end of this function. + pStdId->LockClient(); + +#if DBG==1 + DWORD CallCat = GetCallCat( inb ); + DebugPrintORPCCall(ORPC_SENDRECEIVE_BEGIN, iid, call->message.iMethod, CallCat); + RpcSpy((CALLOUT_BEGIN, inb, iid, call->message.iMethod, 0)); +#endif + + // Send the request. + if ((state & mswmsg_cs) || (IsMTAThread() && !call->Local())) + { + // For MSWMSG or non-local MTA, call ThreadSendReceive directly. + result = ThreadSendReceive( call ); + } + else + { + if (call->Local()) + call->message.reserved2[3] = NULL; + + if (IsMTAThread()) + { + LOCK + result = GetToSTA( pOXIDEntry, call); + UNLOCK + } + else + { + result = SwitchSTA( pOXIDEntry, (CChannelCallInfo **) &call ); + } + } + +#if DBG==1 + DebugPrintORPCCall(ORPC_SENDRECEIVE_END, iid, pMessage->iMethod, CallCat); + RpcSpy((CALLOUT_END, inb, iid, pMessage->iMethod, result)); +#endif + + // We can't look at the call structure if the call was canceled. + if (result != RPC_E_CALL_CANCELED) + { + // Get the reply header if there is a reply buffer. + if ((state & mswmsg_cs) && (pMessage->rpcFlags & RPCFLG_ASYNCHRONOUS)) + outb = NULL; + else + outb = (WireThat *) call->message.Buffer; + + // Local calls reuse pNext on the server side. + call->pNext = next_call; + + // Save the real buffer pointer for FreeBuffer. + call->pHeader = call->message.Buffer; + } + else + outb = NULL; + + // Figure out when to retry. + // FreeThreaded - treat retry as a failure. + // Apartment - return the buffer and let call control decide. + + if (result == S_OK) + { + // No buffer was returned for async calls on MSWMSG. + if (outb == NULL) + *status = S_OK; + else if (IsMTAThread()) + { + if (outb->c.flags & ORPCF_REJECTED) + result = RPC_E_CALL_REJECTED; + else if (outb->c.flags & ORPCF_RETRY_LATER) + result = RPC_E_SERVERCALL_RETRYLATER; + else + *status = S_OK; + } + else if (outb->c.flags & ORPCF_REJECTED) + *status = (ULONG) RPC_E_CALL_REJECTED; + else if (outb->c.flags & ORPCF_RETRY_LATER) + *status = (ULONG) RPC_E_SERVERCALL_RETRYLATER; + else + *status = S_OK; + } + + // Check the packet extensions. + if (result != RPC_E_CALL_CANCELED) + { + stub_data = (char *) call->message.Buffer; + result = ClientNotify( iid, outb, call->message.cbBuffer, + (void **) &stub_data, + call->message.dataRepresentation, + result ); + } + else + result = ClientNotify( iid, outb, 0, (void **) &stub_data, 0, result ); + + // Call succeeded. + if (result == S_OK && outb != NULL) + { + // The locked flag lets FreeBuffer know that it has to call + // RH->UnlockClient. + call->iFlags |= CF_LOCKED; + pMessage->Buffer = stub_data; + pMessage->cbBuffer = call->message.cbBuffer - + (stub_data - (char *) call->message.Buffer); + pMessage->dataRepresentation = call->message.dataRepresentation; + result = *status; + + // Copy a portion of the message structure that RPC updated on SendReceive. + // This is needed to free the buffer. Note that we still have to free + // the buffer in certain failure cases (reject). + pMessage->reserved2[2] = call->message.reserved2[2]; + + } + else + { + // Resume any outstanding impersonation. + ResumeImpersonate( tls->pCallContext, resume ); + + // Clean up the call. + pStdId->UnLockClient(); + tls->pCallInfo = next_call; + tls->dwAuthnLevel = saved_authn_level; + delete call; + + // Make sure FreeBuffer doesn't try to free the in buffer. + pMessage->Buffer = NULL; + + // If the result is server fault, get the exception code from the CChannelCallInfo. + if (result == RPC_E_SERVERFAULT) + { + *status = call->server_fault; + } + // Everything else is a comm fault. + else if (result != S_OK) + { + *status = result; + result = RPC_E_FAULT; + } + tls->fault = *status; + + // Since result is almost always mapped to RPC_E_FAULT, display the + // real error here to assist debugging. + if (*status != S_OK) + ComDebOut((DEB_CHANNEL, "ORPC call failed. status = %x\n", *status)); + } + + ASSERT_LOCK_RELEASED + gOXIDTbl.ValidateOXID(); + ComDebOut((DEB_CHANNEL, "CRpcChannelBuffer::SendReceive hr:%x\n", result)); + return result; +} + +/***************************************************************************/ +HANDLE CRpcChannelBuffer::SwapSecurityToken( HANDLE hNew ) +{ + HANDLE hOld = hToken; + hToken = hNew; + return hOld; +} + +#if DBG == 1 +//+------------------------------------------------------------------- +// +// Member: CRpcChannelBuffer::AssertValid +// +// Synopsis: Validates that the state of the object is consistent. +// +// History: 25-Jan-94 CraigWi Created. +// +// DCOMWORK - Put in some asserts. +// +//-------------------------------------------------------------------- +void CRpcChannelBuffer::AssertValid(BOOL fKnownDisconnected, + BOOL fMustBeOnCOMThread) +{ + Win4Assert(state & (proxy_cs | client_cs | server_cs )); + + if (state & (client_cs | proxy_cs)) + { + ; + } + else if (state & server_cs) + { + Win4Assert( !(state & freethreaded_cs) ); + if (fMustBeOnCOMThread && IsSTAThread()) + Win4Assert(IsMTAThread() || pOXIDEntry->dwTid == GetCurrentThreadId()); + // ref count can be 0 in various stages of connection and disconnection + Win4Assert(ref_count < 0x7fff && "Channel ref count unreasonably high"); + + // the pStdId pointer can not be NULL + // Win4Assert(IsValidInterface(pStdId)); + } +} +#endif // DBG == 1 + + +/***************************************************************************/ +STDAPI_(ULONG) DebugCoGetRpcFault() +{ + HRESULT hr; + COleTls tls(hr); + + if (SUCCEEDED(hr)) + return tls->fault; + + return 0; +} + +/***************************************************************************/ +STDAPI_(void) DebugCoSetRpcFault( ULONG fault ) +{ + HRESULT hr; + COleTls tls(hr); + + if (SUCCEEDED(hr)) + tls->fault = fault; +} + +/***************************************************************************/ +extern "C" +BOOL _stdcall DllDebugObjectRPCHook( BOOL trace, LPORPC_INIT_ARGS pass_through ) +{ + if (!IsWOWThread()) + { + DoDebuggerHooks = trace; + DebuggerArg = pass_through; + return TRUE; + } + else + return FALSE; +} + +/***************************************************************************/ +BOOL LocalCall() +{ + CChannelCallInfo *call; + + // Get the call info from TLS. + COleTls tls; + call = (CChannelCallInfo *) tls->pCallInfo; + Win4Assert( call != NULL ); + return call->iFlags & CF_PROCESS_LOCAL; +} + +/***************************************************************************/ +LONG ThreadInvokeExceptionFilter( DWORD lCode, + LPEXCEPTION_POINTERS lpep ) +{ + ComDebOut((DEB_ERROR, "Exception 0x%x in ThreadInvoke at address 0x%x\n", + lCode, lpep->ExceptionRecord->ExceptionAddress)); + DebugBreak(); + return EXCEPTION_EXECUTE_HANDLER; +} + +/***************************************************************************/ +/* This routine returns both comm status and server faults to the runtime + by raising exceptions. If FreeThreading is true, ComInvoke will throw + exceptions to indicate server faults. These will not be caught and will + propogate directly to the runtime. If FreeThreading is false, ComInvoke + will return the result and fault in the CChannelCallInfo record. + + NOTE: + This function switches to the 32 bit stack under WIN95. + An exception has to be caught while switched to the 32 bit stack. + The exceptions has to be pass as a value and rethrown again on the + 16 bit stack (see SSInvoke in stkswtch.cxx) +*/ + +#ifdef _CHICAGO_ +DWORD +#else +void +#endif +SSAPI(ThreadInvoke)(RPC_MESSAGE *message ) +{ + HRESULT result = S_OK; + + TRACECALL(TRACE_RPC, "ThreadInvoke"); + ComDebOut((DEB_CHANNEL,"ThreadInvoke pMsg:%x\n", message)); + gOXIDTbl.ValidateOXID(); + ASSERT_LOCK_RELEASED + + BOOL success; + WireThis *inb = (WireThis *) message->Buffer; + IPID ipid; + RPC_STATUS status; + OXIDEntry *pOxid; + unsigned int transport_type; + DWORD authn_level; + + // Byte swap the header. + ByteSwapThis( message->DataRepresentation, inb ); + + // Validate several things: + // The packet size is larger then the first header size. + // No extra flags are set. + // The procedure number is greater then 2 (not QI, AddRef, Release). + if (sizeof(WireThisPart1) > message->BufferLength || + (inb->c.flags & ~(ORPCF_LOCAL | ORPCF_RESERVED1 | + ORPCF_RESERVED2 | ORPCF_RESERVED3 | ORPCF_RESERVED4)) != 0 || + message->ProcNum < 3) + RETURN_COMM_STATUS( RPC_E_INVALID_HEADER ); + + // Validate the version. + if (inb->c.version.MajorVersion != COM_MAJOR_VERSION || + inb->c.version.MinorVersion > COM_MINOR_VERSION) + RETURN_COMM_STATUS( RPC_E_VERSION_MISMATCH ); + + // Get the transport the call arrived on. + status = I_RpcServerInqTransportType( &transport_type ); + if (status != RPC_S_OK) + RETURN_COMM_STATUS( RPC_E_SYS_CALL_FAILED ); + + // Don't accept the local header on remote calls. + if (inb->c.flags & ORPCF_LOCAL) + { + if (transport_type != TRANSPORT_TYPE_LPC && + transport_type != TRANSPORT_TYPE_WMSG) + RETURN_COMM_STATUS( RPC_E_INVALID_HEADER ); + + // For local calls the authentication level will always be encrypt. + authn_level = RPC_C_AUTHN_LEVEL_PKT_PRIVACY; + } + + // Don't accept remote calls if DCOM is diabled. + else if (gDisableDCOM && + (transport_type == TRANSPORT_TYPE_CN || transport_type == TRANSPORT_TYPE_DG)) + RETURN_COMM_STATUS( RPC_E_CALL_REJECTED ); + + // Lookup the authentication level. + else + { + result = RpcBindingInqAuthClient( message->Handle, NULL, + NULL, &authn_level, NULL, NULL ); + if (result == RPC_S_BINDING_HAS_NO_AUTH) + authn_level = RPC_C_AUTHN_LEVEL_NONE; + else if (result != RPC_S_OK) + { + Win4Assert( result == RPC_S_OUT_OF_RESOURCES ); + RETURN_COMM_STATUS( MAKE_WIN32( result ) ); + } + + // Verify the authentication level. + if (gAuthnLevel > RPC_C_AUTHN_LEVEL_NONE || + gImpLevel > 0) + { + if (authn_level < gAuthnLevel) + RETURN_COMM_STATUS( RPC_E_ACCESS_DENIED ); + } + } + +#if DBG==1 + _try + { +#endif + + // Find the ipid entry from the ipid. + status = RpcBindingInqObject( message->Handle, &ipid ); + if (status == RPC_S_OK) + { + // The CChannelCallInfo is created in a nested scope so that it + // is destroyed before the calls to throw an exception at the + // end of this function. + CChannelCallInfo call( + GetCallCat( inb ), + (RPCOLEMESSAGE *) message, + 0, + ipid, + (inb->c.flags & ORPCF_LOCAL) ? MSHCTX_LOCAL : MSHCTX_DIFFERENTMACHINE, + NULL, + authn_level ); + + + // Find the OXIDEntry of the server apartment. + + ASSERT_LOCK_RELEASED + LOCK + + IPIDEntry *ipid_entry = gIPIDTbl.LookupIPID( ipid ); + + if (ipid_entry == NULL || (ipid_entry->dwFlags & IPIDF_DISCONNECTED) + || ipid_entry->pChnl == NULL ) + { + UNLOCK + ASSERT_LOCK_RELEASED + result = RPC_E_DISCONNECTED; + } + else + { + pOxid = ipid_entry->pOXIDEntry; + + // NCALRPC always gets the thread right (except on Chicago). + // For MTAs, any thread will do. + if (transport_type == TRANSPORT_TYPE_WMSG || +#ifndef _CHICAGO_ + transport_type == TRANSPORT_TYPE_LPC || +#endif + (pOxid->dwFlags & OXIDF_MTASERVER)) + { + UNLOCK + ASSERT_LOCK_RELEASED + result = ComInvoke( &call ); + } + else + { + // Pass the message to the app thread. + + IncOXIDRefCnt( pOxid ); + result = GetToSTA( pOxid, &call ); + DecOXIDRefCnt( pOxid ); + + UNLOCK + ASSERT_LOCK_RELEASED + } + } + } + else + { + result = MAKE_WIN32( status ); + } + +#if DBG==1 + } + _except( ThreadInvokeExceptionFilter(GetExceptionCode(), + GetExceptionInformation()) ) + { + } +#endif + + // For comm and server faults, generate an exception. Otherwise the buffer + // is set up correctly. + gOXIDTbl.ValidateOXID(); + if (result == RPC_E_SERVERFAULT) + { + ASSERT_LOCK_RELEASED + RETURN_COMM_STATUS( RPC_E_SERVERFAULT ); + } + else if (result != S_OK) + { + ASSERT_LOCK_RELEASED + RETURN_COMM_STATUS( result ); + } + +#ifdef _CHICAGO_ + return 0; +#endif //_CHICAGO_ +} + + +/***************************************************************************/ +HRESULT ThreadSendReceive( CChannelCallInfo *call ) +{ + TRACECALL(TRACE_RPC, "ThreadSendReceive"); + ComDebOut((DEB_CHANNEL, "ThreadSendReceive pCall:%x\n", call)); + + ASSERT_LOCK_RELEASED + + HRESULT result; + RPCOLEMESSAGE *message = call->pmessage; + WireThat *outb; + + // Call the runtime. In the future, detect server faults and + // change the value of result to RPC_E_SERVERFAULT. + if (call->pChannel->state & mswmsg_cs) + { + CAptCallCtrl *pACC = GetAptCallCtrl(); + CCliModalLoop *pCML = (pACC) ? pACC->GetTopCML() : NULL; + OXIDEntry *pOxidClient; + HWND hwnd = NULL; + + if (IsWOWThread()) + { + LOCK + result = gOXIDTbl.GetLocalEntry( &pOxidClient ); + UNLOCK + Win4Assert( result == S_OK ); + hwnd = (HWND) pOxidClient->hServerSTA; + } + TRACECALL(TRACE_RPC, "I_RpcAsyncSendReceive"); + result = I_RpcAsyncSendReceive( (RPC_MESSAGE *) message, pCML, hwnd ); + + // If the call was canceled, the rest of the code path assumes that + // the call was deleted (by SwitchComThread). So delete it. + if (result == RPC_S_CALL_CANCELLED) + { + // Convert the win32 error to a hresult. + result = RPC_E_CALL_CANCELED; + delete call; + } + } + else + { + TRACECALL(TRACE_RPC, "I_RpcSendReceive"); + result = I_RpcSendReceive( (RPC_MESSAGE *) message ); + } + + // If the result is small, it is probably a Win32 code. + if (result != 0) + { + message->Buffer = NULL; + if ((ULONG) result > 0xfffffff7 || (ULONG) result < 0x2000) + result = MAKE_WIN32( result ); + } + else + { + // No buffer is returned for asynchronous calls on MSWMSG. + if ((call->pChannel->state & mswmsg_cs) == 0 || + (message->rpcFlags & RPCFLG_ASYNCHRONOUS) == 0) + { + // Byte swap the reply header. Fail the call if the buffer is too + // small. + outb = (WireThat *) message->Buffer; + if (message->cbBuffer >= sizeof(WireThatPart1)) + ByteSwapThat( message->dataRepresentation, outb); + else + result = RPC_E_INVALID_HEADER; + } + } + + ComDebOut((DEB_CHANNEL, "ThreadSendReceive pCall:%x hr:%x\n", call, result)); + return result; +} + +/***************************************************************************/ +/* static */ + +void working_call::Cleanup() +{ + ASSERT_LOCK_HELD + + DWORD i; + + // Release everything. + if (next <= CALLCACHE_SIZE) + { + for (i = 0; i < next; i++) + if (list[i] != NULL) + { + PrivMemFree( list[i] ); + list[i] = NULL; + } + + next = 0; + } +} + +/***************************************************************************/ +/* static */ + +void working_call::Initialize() +{ + ASSERT_LOCK_HELD + next = 0; +} + +//--------------------------------------------------------------------------- +// +// Method: working_call:: operator delete +// +// Synopsis: Cache or actually free a working call. +// +// Notes: gComLock need not be held before calling this function. +// +//--------------------------------------------------------------------------- +void working_call::operator delete( void *call ) +{ + // Add the structure to the list if the list is not full and + // if the process is still initialized (since latent threads may try + // to return stuff). + + LOCK + if (next < CALLCACHE_SIZE && gfChannelProcessInitialized) + { + list[next] = call; + next += 1; + } + + // Otherwise just free it. + else + { + PrivMemFree( call ); + } + UNLOCK +} + +//--------------------------------------------------------------------------- +// +// Method: working_call:: operator new +// +// Synopsis: Keep a cache of working_calls. Since the destructor is +// virtual, the correct delete will be called if any base +// class is deleted. +// +// Notes: gComLock must be held before calling this function. +// +//--------------------------------------------------------------------------- +void *working_call::operator new( size_t size ) +{ + ASSERT_LOCK_HELD + + void *call; + + // Get the last entry from the cache. + Win4Assert( size == sizeof( working_call ) ); + if (next > 0 && next < CALLCACHE_SIZE+1) + { + next -= 1; + call = list[next]; + list[next] = NULL; + } + + // If there are none, allocate a new one. + else + call = PrivMemAlloc(size); + return call; +} + +/**********************************************************************/ +working_call::working_call( CALLCATEGORY callcat, + RPCOLEMESSAGE *original_msg, + DWORD flags, + REFIPID ipidServer, + DWORD destctx, + CRpcChannelBuffer *channel, + DWORD authn_level ) : + CChannelCallInfo( callcat, &message, flags, ipidServer, destctx, channel, + authn_level ) +{ + message = *original_msg; +} + |