windows-nt/Source/XPSP1/NT/com/rpc/ndr20/async.cxx

1501 lines
45 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Copyright (c) 1996-2000 Microsoft Corporation
Module Name :
async.c
Abstract :
This file contains the ndr async implementation.
Author :
Ryszard K. Kott (ryszardk) Nov 1996
Revision History :
---------------------------------------------------------------------*/
#define USE_STUBLESS_PROXY
#define CINTERFACE
#include "ndrp.h"
#include "ndrole.h"
#include "rpcproxy.h"
#include "hndl.h"
#include "interp.h"
#include "interp2.h"
#include "pipendr.h"
#include "mulsyntx.h"
#include "asyncndr.h"
#include "attack.h"
#include <stdarg.h>
#pragma code_seg(".orpc")
#ifdef _PPC_
#error PPC code has been removed
#endif
CLIENT_CALL_RETURN RPC_VAR_ENTRY
NdrAsyncClientCall(
PMIDL_STUB_DESC pStubDescriptor,
PFORMAT_STRING pFormat,
...
)
/*
This entry is used for raw rpc only.
No support for OLE [async] attribute anymore.
*/
{
va_list ArgList;
unsigned char * StartofStack;
CLIENT_CALL_RETURN Ret;
//
// Get address of argument to this function following pFormat. This
// is the address of the address of the first argument of the function
// calling this function.
// Then get the address of the stack where the parameters are.
//
RPC_ASYNC_HANDLE AsyncHandle;
PNDR_ASYNC_MESSAGE pAsyncMsg;
RPC_MESSAGE * pRpcMsg;
MIDL_STUB_MESSAGE * pStubMsg;
ulong ProcNum, RpcFlags;
handle_t Handle;
uchar HandleType;
INTERPRETER_FLAGS OldOiFlags;
INTERPRETER_OPT_FLAGS NewOiFlags;
PPARAM_DESCRIPTION Params;
RPC_STATUS Status;
NDR_PROC_CONTEXT *pContext;
Ret.Simple = 0;
INIT_ARG( ArgList, pFormat);
GET_FIRST_IN_ARG(ArgList);
StartofStack = (uchar *) GET_STACK_START(ArgList);
pAsyncMsg = (NDR_ASYNC_MESSAGE*) I_RpcBCacheAllocate( sizeof( NDR_ASYNC_MESSAGE) );
if ( ! pAsyncMsg )
Status = RPC_S_OUT_OF_MEMORY;
else
{
memset( pAsyncMsg, 0, sizeof( NDR_ASYNC_MESSAGE ) );
ProcNum = MulNdrpInitializeContextFromProc(
XFER_SYNTAX_DCE,
pFormat,
&pAsyncMsg->ProcContext,
NULL ); // server StartofStack
Status = NdrpInitializeAsyncMsg(
StartofStack,
pAsyncMsg );
}
if ( Status )
{
// if something is wrong during setup, we need to cleanup
// immediately. We have to process format string here explicitly
NDR_PROC_CONTEXT TempContext;
MIDL_STUB_MESSAGE StubMsgTemp;
ProcNum = MulNdrpInitializeContextFromProc(
XFER_SYNTAX_DCE,
pFormat,
&TempContext,
NULL ); // server StartofStack
OldOiFlags = TempContext.NdrInfo.InterpreterFlags;
if ( OldOiFlags.HasCommOrFault )
{
// It's easier to map the error here than to go through the error
// recovery and normal cleanup without valid handle etc.
StubMsgTemp.StubDesc = pStubDescriptor;
StubMsgTemp.StackTop = StartofStack;
NdrClientMapCommFault( & StubMsgTemp,
ProcNum,
Status,
(ULONG_PTR*) &Ret.Simple );
return Ret;
}
else
RpcRaiseException( Status );
}
pContext = &pAsyncMsg->ProcContext;
HandleType = pContext->HandleType;
NewOiFlags = pContext->NdrInfo.pProcDesc->Oi2Flags;
OldOiFlags = pContext->NdrInfo.InterpreterFlags;
Params = (PARAM_DESCRIPTION *)pContext->Params;
// We need to switch to our copy of the stack everywhere, including pStubMsg.
StartofStack = pAsyncMsg->ProcContext.StartofStack;
// We abstract the level of indirection here.
AsyncHandle = pAsyncMsg->AsyncHandle;
pRpcMsg = & pAsyncMsg->RpcMsg;
pStubMsg = & pAsyncMsg->StubMsg;
//
// Set Params and NumberParams before a call to initialization.
//
// Wrap everything in a try-finally pair. The finally clause does the
// required freeing of resources (RpcBuffer and Full ptr package).
//
RpcTryFinally
{
// Use a nested try-except pair to support [comm_status][fault_status].
//
RpcTryExcept
{
BOOL fRaiseExcFlag;
NdrClientInitializeNew( pRpcMsg,
pStubMsg,
pStubDescriptor,
(uint) ProcNum );
if ( HandleType )
{
//
// We have an implicit handle.
//
Handle = ImplicitBindHandleMgr( pStubDescriptor,
HandleType,
&pContext->SavedGenericHandle);
}
else
{
Handle = ExplicitBindHandleMgr( pStubDescriptor,
StartofStack,
(PFORMAT_STRING)pContext->pHandleFormatSave,
&pContext->SavedGenericHandle );
}
pStubMsg->pAsyncMsg = pAsyncMsg;
NdrpClientInit( pStubMsg,
NULL ); // return value
//
// Skip buffer size pass if possible.
//
if ( NewOiFlags.ClientMustSize )
{
NdrpSizing( pStubMsg,
TRUE ); // isclient
}
pRpcMsg->RpcFlags |= RPC_BUFFER_ASYNC;
if ( NewOiFlags.HasPipes )
NdrGetPipeBuffer( pStubMsg,
pStubMsg->BufferLength,
Handle );
else
NdrGetBuffer( pStubMsg,
pStubMsg->BufferLength,
Handle );
NDR_ASSERT( pStubMsg->fBufferValid, "Invalid buffer" );
// Let runtime associate async handle with the call.
NdrpRegisterAsyncHandle( pStubMsg, AsyncHandle );
pAsyncMsg->StubPhase = NDR_ASYNC_SET_PHASE;
//
// ----------------------------------------------------------
// Marshall Pass.
// ----------------------------------------------------------
//
NdrpClientMarshal( pStubMsg,
FALSE ); // IsObject
pAsyncMsg->StubPhase = NDR_ASYNC_CALL_PHASE;
NdrAsyncSend( pStubMsg,
NewOiFlags.HasPipes && pContext->pPipeDesc->InPipes );
pAsyncMsg->Flags.ValidCallPending = 1;
}
RpcExcept( OldOiFlags.HasCommOrFault )
{
RPC_STATUS ExceptionCode = RpcExceptionCode();
// Actually dismantle the call.
// This is a request call and there is nothing left at the runtime.
pAsyncMsg->StubPhase = NDR_ASYNC_ERROR_PHASE;
NdrClientMapCommFault( pStubMsg,
ProcNum,
ExceptionCode,
(ULONG_PTR*) &Ret.Simple );
}
RpcEndExcept
}
RpcFinally
{
if ( pAsyncMsg->Flags.ValidCallPending )
{
if ( NewOiFlags.HasPipes )
{
NdrMarkNextActivePipe( pContext->pPipeDesc );
pContext->pPipeDesc->Flags.NoBufferCallPending = 1;
}
}
else
{
// Cleanup everything but the user's handle.
NdrpFreeAsyncMsg( pAsyncMsg );
AsyncHandle->StubInfo = 0;
}
InterlockedDecrement( & AsyncHandle->Lock );
}
RpcEndFinally
return Ret;
}
RPC_STATUS
NdrpCompleteAsyncClientCall(
RPC_ASYNC_HANDLE AsyncHandle,
PNDR_ASYNC_MESSAGE pAsyncMsg,
void * pReturnValue
)
{
RPC_MESSAGE * pRpcMsg = & pAsyncMsg->RpcMsg;
MIDL_STUB_MESSAGE * pStubMsg = & pAsyncMsg->StubMsg;
PFORMAT_STRING pFormatParam;
NDR_PROC_CONTEXT * pContext = &pAsyncMsg->ProcContext;
NDR_PROC_INFO * pNdrInfo = &pContext->NdrInfo;
INTERPRETER_FLAGS OldOiFlags = pNdrInfo->InterpreterFlags;
INTERPRETER_OPT_FLAGS NewOiFlags = pNdrInfo->pProcDesc->Oi2Flags;
PPARAM_DESCRIPTION Params = (PPARAM_DESCRIPTION )pContext->Params;
uchar * StartofStack = pContext->StartofStack ;
PMIDL_STUB_DESC pStubDescriptor = pStubMsg->StubDesc;
ulong RpcFlags = pRpcMsg->RpcFlags;
ULONG_PTR RetVal = 0;
long NumberParams = pContext->NumberParams;
long n;
NDR_ASYNC_CALL_FLAGS CallFlags = pAsyncMsg->Flags;
RpcTryFinally
{
// Use a nested try-except pair to support [comm_status][fault_status].
//
RpcTryExcept
{
if ( ! CallFlags.ValidCallPending )
RpcRaiseException( RPC_S_INVALID_ASYNC_HANDLE );
CallFlags.ValidCallPending = 0;
// Non-pipe case or after pipe args case.
if ( NewOiFlags.HasPipes )
NdrIsAppDoneWithPipes( pContext->pPipeDesc );
NdrLastAsyncReceive( pStubMsg );
//
// ----------------------------------------------------------
// Unmarshall Pass.
// ----------------------------------------------------------
//
NdrpClientUnMarshal( pStubMsg,
(CLIENT_CALL_RETURN *)pReturnValue );
}
RpcExcept( OldOiFlags.HasCommOrFault )
{
RPC_STATUS ExceptionCode = RpcExceptionCode();
CallFlags.ValidCallPending = ExceptionCode == RPC_S_ASYNC_CALL_PENDING;
NdrClientMapCommFault( pStubMsg,
pRpcMsg->ProcNum,
ExceptionCode,
&RetVal );
if ( ExceptionCode == RPC_S_ASYNC_CALL_PENDING )
{
// If the call is just pending, force the pending error code
// to show up in the return value of RpcAsyncCallComplete.
RetVal = RPC_S_ASYNC_CALL_PENDING;
}
}
RpcEndExcept
}
RpcFinally
{
// There is only one way a valid call may be pending at this stage:
// that is the receive call returned with RPC_S_CALL_PENDING.
if ( ! CallFlags.ValidCallPending )
{
// Cleanup everything. However, don't free user's handle.
NdrpFreeAsyncMsg( pAsyncMsg );
AsyncHandle->StubInfo = 0;
}
InterlockedDecrement( & AsyncHandle->Lock );
}
RpcEndFinally
return (RPC_STATUS)RetVal;
}
void RPC_ENTRY
NdrAsyncServerCall(
PRPC_MESSAGE pRpcMsg
)
/*++
Routine Description :
The server side entry point for regular asynchronous RPC procs.
Arguments :
pRpcMsg - The RPC message.
Return :
None.
--*/
{
RPC_ASYNC_HANDLE AsyncHandle = 0;
PNDR_ASYNC_MESSAGE pAsyncMsg;
PRPC_SERVER_INTERFACE pServerIfInfo;
PMIDL_SERVER_INFO pServerInfo;
PMIDL_STUB_DESC pStubDesc;
const SERVER_ROUTINE * DispatchTable;
ushort ProcNum;
ushort FormatOffset;
PFORMAT_STRING pFormat;
PFORMAT_STRING pFormatParam;
PMIDL_STUB_MESSAGE pStubMsg;
uchar * pArgBuffer;
uchar ** ppArg;
PPARAM_DESCRIPTION Params;
INTERPRETER_FLAGS OldOiFlags;
INTERPRETER_OPT_FLAGS NewOiFlags;
long NumberParams;
ushort ClientBufferSize;
BOOL HasExplicitHandle;
long n;
NDR_PROC_CONTEXT * pContext;
RPC_STATUS Status = RPC_S_OK;
//
// In the case of a context handle, the server side manager function has
// to be called with NDRSContextValue(ctxthandle). But then we may need to
// marshall the handle, so NDRSContextValue(ctxthandle) is put in the
// argument buffer and the handle itself is stored in the following array.
// When marshalling a context handle, we marshall from this array.
//
// The handle table is part of the async handle.
ProcNum = (ushort) pRpcMsg->ProcNum;
NDR_ASSERT( ! ((ULONG_PTR)pRpcMsg->Buffer & 0x7),
"marshaling buffer misaligned at server" );
pServerIfInfo = (PRPC_SERVER_INTERFACE)pRpcMsg->RpcInterfaceInformation;
pServerInfo = (PMIDL_SERVER_INFO)pServerIfInfo->InterpreterInfo;
DispatchTable = pServerInfo->DispatchTable;
pStubDesc = pServerInfo->pStubDesc;
FormatOffset = pServerInfo->FmtStringOffset[ProcNum];
pFormat = &((pServerInfo->ProcString)[FormatOffset]);
AsyncHandle = 0;
pAsyncMsg = (NDR_ASYNC_MESSAGE*) I_RpcBCacheAllocate( sizeof( NDR_ASYNC_MESSAGE) );
if ( ! pAsyncMsg )
Status = RPC_S_OUT_OF_MEMORY;
else
{
memset( pAsyncMsg, 0, sizeof( NDR_ASYNC_MESSAGE ) );
MulNdrpInitializeContextFromProc(
XFER_SYNTAX_DCE,
pFormat,
&pAsyncMsg->ProcContext,
NULL ); // server StartofStack
Status = NdrpInitializeAsyncMsg( 0, // StartofStack, server
pAsyncMsg
);
}
if ( Status )
RpcRaiseException( Status );
pAsyncMsg->StubPhase = STUB_UNMARSHAL;
pStubMsg = & pAsyncMsg->StubMsg;
// from Kamen:
// we don't need to copy RpcMsg : it's provided by runtime in server side
pContext = &pAsyncMsg->ProcContext;
// setup the type format string in old code.
pContext->DceTypeFormatString = pStubDesc->pFormatTypes;
pStubMsg->RpcMsg = pRpcMsg;
pStubMsg->SavedContextHandles = pAsyncMsg->CtxtHndl;
// The arg buffer is zeroed out already.
pArgBuffer = pAsyncMsg->ProcContext.StartofStack;
OldOiFlags = pContext->NdrInfo.InterpreterFlags;
NewOiFlags = pContext->NdrInfo.pProcDesc->Oi2Flags;
NumberParams = pContext->NumberParams;
Params = ( PPARAM_DESCRIPTION ) pContext->Params;
//
// Wrap the unmarshalling and the invoke call in the try block of
// a try-finally. Put the free phase in the associated finally block.
//
BOOL fManagerCodeInvoked = FALSE;
BOOL fErrorInInvoke = FALSE;
RPC_STATUS ExceptionCode = 0;
// We abstract the level of indirection here.
AsyncHandle = pAsyncMsg->AsyncHandle;
RpcTryFinally
{
RpcTryExcept
{
// Put the async handle on stack.
((void **)pArgBuffer)[0] = AsyncHandle;
NdrpServerInit( pStubMsg,
pRpcMsg,
pStubDesc,
NULL, // pThis
NULL, // pChannel
pAsyncMsg );
// Let runtime associate async handle with the call.
NdrpRegisterAsyncHandle( pStubMsg, AsyncHandle );
pAsyncMsg->StubPhase = NDR_ASYNC_SET_PHASE;
NdrpServerUnMarshal( pStubMsg );
if ( pRpcMsg->BufferLength <
(uint)(pStubMsg->Buffer - (uchar *)pRpcMsg->Buffer) )
{
RpcRaiseException( RPC_X_BAD_STUB_DATA );
}
}
RpcExcept( NdrServerUnmarshallExceptionFlag(GetExceptionInformation()) )
{
ExceptionCode = RpcExceptionCode();
if( RPC_BAD_STUB_DATA_EXCEPTION_FILTER )
{
ExceptionCode = RPC_X_BAD_STUB_DATA;
}
// we always free the memory list if exception happened
// during unmarshalling. Exception code will tell if this
// is really RPC_X_BAD_STUB_DATA;
NdrpFreeMemoryList( pStubMsg );
pAsyncMsg->Flags.BadStubData = 1;
pAsyncMsg->ErrorCode = ExceptionCode;
RpcRaiseException( ExceptionCode );
}
RpcEndExcept
// Two separate blocks because the filters are different.
// We need to catch exception in the manager code separately
// as the model implies that there will be no other call from
// the server app to clean up.
RpcTryExcept
{
//
// Do [out] initialization before the invoke.
//
NdrpServerOutInit( pStubMsg );
//
// Unblock the first pipe; this needs to be after unmarshalling
// because the buffer may need to be changed to the secondary one.
// In the out only pipes case this happens immediately.
//
if ( NewOiFlags.HasPipes )
NdrMarkNextActivePipe( pContext->pPipeDesc );
pAsyncMsg->StubPhase = STUB_CALL_SERVER;
//
// Check for a thunk. Compiler does all the setup for us.
//
if ( pServerInfo->ThunkTable && pServerInfo->ThunkTable[ProcNum] )
{
pAsyncMsg->Flags.ValidCallPending = 1;
InterlockedDecrement( & AsyncHandle->Lock );
fManagerCodeInvoked = TRUE;
fErrorInInvoke = TRUE;
pServerInfo->ThunkTable[ProcNum]( pStubMsg );
}
else
{
//
// Note that this ArgNum is not the number of arguments declared
// in the function we called, but really the number of
// REGISTER_TYPEs occupied by the arguments to a function.
//
long ArgNum;
MANAGER_FUNCTION pFunc;
REGISTER_TYPE returnValue;
if ( pRpcMsg->ManagerEpv )
pFunc = ((MANAGER_FUNCTION *)pRpcMsg->ManagerEpv)[ProcNum];
else
pFunc = (MANAGER_FUNCTION) DispatchTable[ProcNum];
ArgNum = (long) pContext->StackSize / sizeof(REGISTER_TYPE);
//
// The StackSize includes the size of the return. If we want
// just the number of REGISTER_TYPES, then ArgNum must be reduced
// by 1 when there is a return value AND the current ArgNum count
// is greater than 0.
//
if ( ArgNum && NewOiFlags.HasReturn )
ArgNum--;
// Being here means that we can expect results. Note that the user
// can call RpcCompleteCall from inside of the manager code.
pAsyncMsg->Flags.ValidCallPending = 1;
// Unlock the handle - the app is allowed to call RpCAsyncManager
// or RpcAsyncAbort from the manager code.
InterlockedDecrement( & AsyncHandle->Lock );
fManagerCodeInvoked = TRUE;
fErrorInInvoke = TRUE;
returnValue = Invoke( pFunc,
(REGISTER_TYPE *)pArgBuffer,
#if defined(_IA64_)
NewOiFlags.HasExtensions ? ((PNDR_PROC_HEADER_EXTS64)&pContext->NdrInfo.pProcDesc->NdrExts)->FloatArgMask
: 0,
#endif
ArgNum);
// We are discarding the return value as it is not the real one.
// The real return value is passed in the complete call.
}
fErrorInInvoke = FALSE;
}
RpcExcept( 1 )
{
ExceptionCode = RpcExceptionCode();
if ( ExceptionCode == 0 )
ExceptionCode = ERROR_INVALID_PARAMETER;
// We may not have the async message around anymore.
RpcRaiseException( ExceptionCode );
}
RpcEndExcept
}
RpcFinally
{
if ( fManagerCodeInvoked && !fErrorInInvoke )
{
// Success. Just skip everything if the manager code was invoked
// and returned successfully.
// Note that manager code could have called Complete or Abort by now
// and so the async handle may not be valid anymore.
}
else
{
// See if we can clean up;
Status = RPC_S_OK;
if ( fErrorInInvoke )
{
// After an exception in invoking, let's see if we can get a hold
// of the handle. If so, we will be able to clean up.
// If not, there may be a leak there that we can do nothing about.
// The rule is: after an exception the app cannot call Abort or
// Complete. So, we need to force complete if we can.
Status = NdrValidateBothAndLockAsyncHandle( AsyncHandle );
}
if ( Status == RPC_S_OK )
{
// Something went wrong but we are able to do the cleanup.
// Cleanup parameters and async message/handle.
// propagate the exception.
NdrpCleanupServerContextHandles( pStubMsg,
pArgBuffer,
TRUE ); // die in manager routine
if (!pAsyncMsg->Flags.BadStubData)
{
NdrpFreeParams( pStubMsg,
NumberParams,
Params,
pArgBuffer );
}
NdrpFreeAsyncHandleAndMessage( AsyncHandle );
}
// else manager code invoked and we could not recover.
// Exception will be raised by the EndFinally below.
}
}
RpcEndFinally
}
RPC_STATUS
NdrpCompleteAsyncServerCall(
RPC_ASYNC_HANDLE AsyncHandle,
PNDR_ASYNC_MESSAGE pAsyncMsg,
void * pReturnValue
)
/*++
Routine Description :
Complete an async call on the server side. If an exception occurs, the
asynchronous rpc call is aborted with the exception code and the server side
caller is returned S_OK.
Arguments :
AsyncHandle - validated asynchronous handle,
pAsyncMsg - pointer to async msg structure,
pReturnValue - pointer to the return value to be passed to the client.
Return :
Status of S_OK.
--*/
{
// in the server side, we don't use pAsyncMsg->RpcMsg. for Kamen
MIDL_STUB_MESSAGE * pStubMsg = & pAsyncMsg->StubMsg;
RPC_MESSAGE * pRpcMsg = pStubMsg->RpcMsg;
PFORMAT_STRING pFormatParam;
NDR_PROC_CONTEXT * pContext = & pAsyncMsg->ProcContext;
INTERPRETER_FLAGS OldOiFlags = pContext->NdrInfo.InterpreterFlags;
INTERPRETER_OPT_FLAGS NewOiFlags = pContext->NdrInfo.pProcDesc->Oi2Flags;
PPARAM_DESCRIPTION Params = ( PPARAM_DESCRIPTION )pContext->Params;
uchar * pArgBuffer = pContext->StartofStack;
unsigned long StackSize = pContext->StackSize;
PMIDL_STUB_DESC pStubDesc = pStubMsg->StubDesc;
long NumberParams = pContext->NumberParams;
long n;
boolean fParamsFreed = FALSE;
//
// Wrap the unmarshalling, mgr call and marshalling in the try block of
// a try-except. Put the call to abort in the except clause.
//
RpcTryExcept
{
// At this point, this is a valid RPC call since the asynchronous handle
// is owned by NDR on the server side and NDR passes the handle
// to the server during the invoke call. During invoke
// the parameters have already been unmarshalled.
pAsyncMsg->StubPhase = STUB_MARSHAL;
if( NewOiFlags.HasReturn )
{
// Put user's return value on the stack as usual.
// See the invoke for comments on folding return into the arg satck.
NDR_ASSERT( !pContext->HasComplexReturn, "complex return is not supported in async" );
long ArgNum = (long) StackSize / sizeof(REGISTER_TYPE);
if ( ArgNum )
ArgNum--;
if ( ! pReturnValue )
RpcRaiseException( RPC_S_INVALID_ARG );
// We don't support return value larger than register_type,
// and memcpy avoid alignment issue.
if ( Params[NumberParams-1].ParamAttr.IsBasetype )
memcpy( &((REGISTER_TYPE *)pArgBuffer)[ArgNum],
pReturnValue,
(size_t)SIMPLE_TYPE_MEMSIZE( Params[NumberParams-1].SimpleType.Type ) );
else
((REGISTER_TYPE *)pArgBuffer)[ArgNum] = *(REGISTER_TYPE*)pReturnValue;
}
//
// Buffer size pass.
//
ushort ConstantBufferSize = pContext->NdrInfo.pProcDesc->ServerBufferSize;
if ( NewOiFlags.HasPipes )
{
NdrIsAppDoneWithPipes( pContext->pPipeDesc );
pStubMsg->BufferLength += ConstantBufferSize;
}
else
pStubMsg->BufferLength = ConstantBufferSize;
if ( NewOiFlags.ServerMustSize )
{
NdrpSizing( pStubMsg,
FALSE ); // this is server
}
// Get buffer.
if ( NewOiFlags.HasPipes && pContext->pPipeDesc->OutPipes )
{
NdrGetPartialBuffer( pStubMsg );
pStubMsg->RpcMsg->RpcFlags &= ~RPC_BUFFER_PARTIAL;
}
else
{
NdrGetBuffer( pStubMsg,
pStubMsg->BufferLength,
0 );
}
//
// Marshall pass.
//
NdrpServerMarshal( pStubMsg,
FALSE ); // IsObject
pRpcMsg->BufferLength = (ulong)(pStubMsg->Buffer - (uchar *)pRpcMsg->Buffer);
// We don't drop to the runtime like for synchronous calls,
// we send the last buffer explicitly.
// set the freed flag.
fParamsFreed = TRUE;
/*
we have to do release twice here:
After the last piece of data is sent via NdrAsyncSend, dispatch buffer
will be freed by runtime. We'll have problem calling ndr free routines to
free unique pointers (where both pointer and pointee are in the buffer). So
we call ndr free routines BEFORE the send, because it won't free anything
inside dispatch buffer, and runtime send only cares about dispatch buffer.
We still have to call ndr free routines in RpcFinally for exception cleanup. we
check the flag to avoid calling free twice.
*/
NdrpFreeParams( pStubMsg,
NumberParams,
Params,
pArgBuffer );
NdrAsyncSend( pStubMsg,
FALSE ); // the last call is always non-partial
}
RpcExcept(1)
{
// Abort the call which will result in the exception being propagated to
// the client.
NdrpAsyncAbortCall( AsyncHandle,
pAsyncMsg,
RpcExceptionCode(),
!fParamsFreed ); // Do not free if second attempt.
return S_OK;
}
RpcEndExcept
NdrpFreeAsyncHandleAndMessage( AsyncHandle );
return S_OK;
}
RPC_STATUS
NdrpInitializeAsyncMsg(
void * StartofStack,
PNDR_ASYNC_MESSAGE pAsyncMsg )
/*
This method creates and initializes async msg.
Additionally, it creates the handle itself in object case.
pHandleArg - pointer to the proc's async handle argument.
Note the handle arg semantics on the client:
*pHandleArg is
raw: a handle (arg is a *)
- the handle cannot be 0
obj: a pHandle (arg is a **)
pHandle ==0 means a "don't care" call
*pHandle ==0 means "create handle while calling"
*pHandle !=0 means a handle created by the app.
StartofStack - side marker as well:
!= 0 - client
== 0 - server
*/
{
RPC_STATUS Status = 0;
BOOL fIsClient = StartofStack != 0;
BOOL fHandleCreated = FALSE;
RPC_ASYNC_HANDLE AsyncHandle = 0;
RPC_ASYNC_HANDLE * pHandleArg = (RPC_ASYNC_HANDLE *)StartofStack;
// Do this first to simplify error conditions.
ulong StackSize = pAsyncMsg->ProcContext.StackSize;
if ( fIsClient )
{
// Client always supplies a handle.
if ( *pHandleArg )
{
AsyncHandle = *pHandleArg;
Status = NdrpValidateAndLockAsyncHandle( AsyncHandle );
}
else
Status = RPC_S_INVALID_ASYNC_HANDLE;
}
else
{
// Server - the stub creates the handle.
AsyncHandle = (RPC_ASYNC_STATE *)I_RpcAllocate( sizeof(RPC_ASYNC_STATE) );
if ( ! AsyncHandle )
Status = RPC_S_OUT_OF_MEMORY;
else
{
MIDL_memset( AsyncHandle, 0x0, sizeof( RPC_ASYNC_STATE) );
RpcAsyncInitializeHandle( AsyncHandle, RPC_ASYNC_VERSION_1_0 );
AsyncHandle->Lock = 1;
fHandleCreated = TRUE;
}
}
if ( Status )
{
I_RpcBCacheFree( pAsyncMsg );
return Status;
}
// Initialize the async message properly
pAsyncMsg->Signature = NDR_ASYNC_SIGNATURE;
pAsyncMsg->Version = NDR_ASYNC_VERSION;
pAsyncMsg->StubMsg.pContext = & pAsyncMsg->ProcContext;
pAsyncMsg->ProcContext.StartofStack = (uchar *) NdrpAlloca(
& pAsyncMsg->ProcContext.AllocateContext,
StackSize );
// Must do this before the sizing pass!
pAsyncMsg->StubMsg.StackTop = (uchar *)StartofStack;
pAsyncMsg->StubPhase = NDR_ASYNC_PREP_PHASE;
if ( fIsClient )
{
// Client: copy stack from the app's request call.
RpcpMemoryCopy( pAsyncMsg->ProcContext.StartofStack, StartofStack, StackSize );
}
else
{
// Server: zero out stack for allocs.
MIDL_memset( pAsyncMsg->ProcContext.StartofStack, 0x0, StackSize );
}
MIDL_memset( pAsyncMsg->AsyncGuard,
0x71,
NDR_ASYNC_GUARD_SIZE );
AsyncHandle->StubInfo = pAsyncMsg;
pAsyncMsg->AsyncHandle = AsyncHandle;
return RPC_S_OK;
}
void
NdrpFreeAsyncMsg(
PNDR_ASYNC_MESSAGE pAsyncMsg )
/*
This routine would free the AsyncMsg but not the AsyncHandle, as on the server
the user may need it and on the client it is user's to begin with.
*/
{
NDR_PROC_CONTEXT * pContext = & pAsyncMsg->ProcContext;
if ( pAsyncMsg )
{
PMIDL_STUB_MESSAGE pStubMsg = & pAsyncMsg->StubMsg;
NdrFullPointerXlatFree(pStubMsg->FullPtrXlatTables);
NdrCorrelationFree( pStubMsg );
// Free the RPC buffer.
if ( pStubMsg->IsClient )
{
if ( ! pAsyncMsg->Flags.RuntimeCleanedUp )
NdrFreeBuffer( pStubMsg );
}
RPC_STATUS Status = 0;
if ( pStubMsg->IsClient )
{
if ( pContext->SavedGenericHandle )
{
// Note that we cannot unbind after freeing the handle as the stack would be gone.
RpcTryExcept
{
GenericHandleUnbind( pStubMsg->StubDesc,
pContext->StartofStack,
pContext->pHandleFormatSave,
(pContext->HandleType) ? IMPLICIT_MASK : 0,
& pContext->SavedGenericHandle );
}
RpcExcept(1)
{
Status = RpcExceptionCode();
}
RpcEndExcept;
}
}
NdrpAllocaDestroy( &pContext->AllocateContext );
// Prevent reusing of a handle that has been freed;
pAsyncMsg->Signature = NDR_FREED_ASYNC_SIGNATURE;
I_RpcBCacheFree( pAsyncMsg );
if ( Status )
RpcRaiseException( Status );
}
}
VOID
NdrpFreeAsyncHandleAndMessage(
PRPC_ASYNC_STATE AsyncHandle)
/*++
Routine Description:
Frees an async handle and its associated async message.
Arguments:
AsyncHandle - Supplies the async handle to be freed.
Return Value:
None.
--*/
{
PNDR_ASYNC_MESSAGE pAsyncMsg = (PNDR_ASYNC_MESSAGE)AsyncHandle->StubInfo;
NdrpFreeAsyncMsg( pAsyncMsg );
AsyncHandle->StubInfo = 0;
AsyncHandle->Signature = RPC_FREED_ASYNC_SIGNATURE;
I_RpcFree( AsyncHandle );
}
void
NdrAsyncSend(
PMIDL_STUB_MESSAGE pStubMsg,
BOOL fPartialSend )
{
pStubMsg->RpcMsg->RpcFlags |= RPC_BUFFER_ASYNC;
if ( fPartialSend )
NdrPartialSend( pStubMsg->pContext->pPipeDesc,
pStubMsg );
else
{
NdrSend( 0, // not used
pStubMsg,
FALSE ); // not partial
}
}
void
NdrLastAsyncReceive(
PMIDL_STUB_MESSAGE pStubMsg )
{
pStubMsg->RpcMsg->RpcFlags |= RPC_BUFFER_ASYNC;
// A complete call.
// We may have a complete buffer already when pipes are involved.
if ( pStubMsg->pContext->pPipeDesc )
{
if ( pStubMsg->RpcMsg->RpcFlags & RPC_BUFFER_COMPLETE )
return;
if ( pStubMsg->pContext->pPipeDesc->OutPipes )
{
pStubMsg->RpcMsg->RpcFlags |= RPC_BUFFER_EXTRA;
}
else
{
pStubMsg->RpcMsg->RpcFlags &= ~RPC_BUFFER_EXTRA;
}
}
NdrReceive( pStubMsg->pContext->pPipeDesc,
pStubMsg,
0, // size, ignored for complete calls
FALSE ); // complete buffer
}
void
NdrpRegisterAsyncHandle(
PMIDL_STUB_MESSAGE pStubMsg,
void * AsyncHandle )
{
RPC_STATUS Status;
Status = I_RpcAsyncSetHandle( pStubMsg->RpcMsg,
(RPC_ASYNC_STATE *)AsyncHandle );
if ( Status )
RpcRaiseException( Status );
}
RPC_STATUS
NdrpValidateAsyncMsg(
PNDR_ASYNC_MESSAGE pAsyncMsg )
{
if ( ! pAsyncMsg )
return RPC_S_INVALID_ASYNC_HANDLE;
if ( 0 != IsBadWritePtr( pAsyncMsg, sizeof(NDR_ASYNC_MESSAGE)) )
return RPC_S_INVALID_ASYNC_HANDLE;
if ( pAsyncMsg->Signature != NDR_ASYNC_SIGNATURE ||
pAsyncMsg->Version != NDR_ASYNC_VERSION )
return RPC_S_INVALID_ASYNC_HANDLE;
return RPC_S_OK;
}
RPC_STATUS
NdrpValidateAndLockAsyncHandle(
IN PRPC_ASYNC_STATE AsyncHandle )
{
if ( 0 != IsBadWritePtr( AsyncHandle, sizeof(RPC_ASYNC_STATE)) )
return RPC_S_INVALID_ASYNC_HANDLE;
// Prevent multiple simultanous abort and complete calls.
if ( 0 != InterlockedCompareExchange( & AsyncHandle->Lock, 1, 0 ) )
{
return RPC_S_INVALID_ASYNC_CALL;
}
else
{
if ( AsyncHandle->Signature != RPC_ASYNC_SIGNATURE ||
AsyncHandle->Size != RPC_ASYNC_VERSION_1_0 )
{
InterlockedDecrement( & AsyncHandle->Lock );
return RPC_S_INVALID_ASYNC_HANDLE;
}
}
return RPC_S_OK;
}
RPC_STATUS
NdrValidateBothAndLockAsyncHandle(
IN PRPC_ASYNC_STATE AsyncHandle )
{
RPC_STATUS Status = NdrpValidateAndLockAsyncHandle( AsyncHandle );
if ( Status != RPC_S_OK )
return Status;
Status = NdrpValidateAsyncMsg( (PNDR_ASYNC_MESSAGE) AsyncHandle->StubInfo );
if ( Status != RPC_S_OK )
InterlockedDecrement( & AsyncHandle->Lock );
return Status;
}
RPC_STATUS
NdrpAsyncAbortCall (
PRPC_ASYNC_STATE AsyncHandle,
PNDR_ASYNC_MESSAGE pAsyncMsg,
unsigned long ExceptionCode,
BOOL bFreeParams
)
/*++
Routine Description:
Aborts the asynchronous RPC call indicated by AsyncHandle on the server and
frees memory allocated for the parameters, message, and handle.
Arguments:
AsyncHandle - supplies the async handle for the call
AsyncMessage - supplies the async message for the call
ExceptionCode - supplies the exception code to send to the client.
bFreeParams - TRUE if the parameters should be freed.
Return Value:
NONE.
--*/
{
RPC_STATUS Status = RPC_S_OK;
// If the async call is aborted, see if context handles are fine.
// We know there is no manager routine exception.
NdrpCleanupServerContextHandles( &pAsyncMsg->StubMsg,
pAsyncMsg->ProcContext.StartofStack,
FALSE );
if (bFreeParams)
{
NdrpFreeParams( & pAsyncMsg->StubMsg,
pAsyncMsg->ProcContext.NumberParams, //Number of parameters
( PPARAM_DESCRIPTION )pAsyncMsg->ProcContext.Params,
pAsyncMsg->ProcContext.StartofStack );
}
if ( ! pAsyncMsg->Flags.RuntimeCleanedUp )
Status = I_RpcAsyncAbortCall( AsyncHandle, ExceptionCode);
NdrpFreeAsyncHandleAndMessage( AsyncHandle );
return Status;
}
RPC_STATUS
Ndr64pCompleteAsyncClientCall(
RPC_ASYNC_HANDLE AsyncHandle,
IN PNDR_ASYNC_MESSAGE pAsyncMsg,
void * pReturnValue
);
RPC_STATUS
Ndr64pCompleteAsyncServerCall(
RPC_ASYNC_HANDLE AsyncHandle,
PNDR_ASYNC_MESSAGE pAsyncMsg,
void * pReturnValue
);
RPC_STATUS
Ndr64pAsyncAbortCall(
PRPC_ASYNC_STATE AsyncHandle,
PNDR_ASYNC_MESSAGE pAsyncMsg,
unsigned long ExceptionCode,
BOOL bFreeParams
);
//////////////////////////////////////////////////////////////////////////////
//
// Runtime APIs
//
//////////////////////////////////////////////////////////////////////////////
RPC_STATUS RPC_ENTRY
RpcAsyncInitializeHandle (
IN PRPC_ASYNC_STATE AsyncHandle,
IN unsigned int Size
)
/*++
Routine Description:
Initializes an async handle.
Arguments:
AsyncHandle - the async handle directing the call
Return Value:
RPC_S_OK - the call succeeded.
RPC_S_INVALID_HANDLE - the handle was bad.
--*/
{
if ( ! AsyncHandle ||
Size < RPC_ASYNC_VERSION_1_0 || Size > RPC_ASYNC_CURRENT_VERSION )
return RPC_S_INVALID_ARG;
if ( 0 != IsBadWritePtr( AsyncHandle, sizeof(RPC_ASYNC_STATE)) )
return RPC_S_INVALID_ASYNC_HANDLE;
AsyncHandle->Size = RPC_ASYNC_CURRENT_VERSION;
AsyncHandle->Signature = RPC_ASYNC_SIGNATURE;
AsyncHandle->Flags = 0;
AsyncHandle->Lock = 0;
AsyncHandle->StubInfo = 0;
AsyncHandle->RuntimeInfo = 0;
AsyncHandle->Reserved[0] = 0;
AsyncHandle->Reserved[1] = 0;
AsyncHandle->Reserved[2] = 0;
AsyncHandle->Reserved[3] = 0;
return RPC_S_OK;
}
RPC_STATUS RPC_ENTRY
RpcAsyncAbortCall (
IN PRPC_ASYNC_STATE AsyncHandle,
IN unsigned long ExceptionCode
)
/*++
Routine Description:
This API is valid only on the server side and is a request to abort
the call. The asynchronous handle is deleted and no additional API calls
on that handle are permitted.
Note that RpcCancelAsyncCall is a different API that is used on the client
side only.
Arguments:
AsyncHandle - the async handle directing the call
Return Value:
RPC_S_OK - the call succeeded.
RPC_S_INVALID_HANDLE - the handle was bad.
RPC_S_INVALID_ASYNC_CALL - May not be called on the client.
Other errors from the RPC runtime layer.
Note: This API cannot be called on the client side.
--*/
{
RPC_STATUS Status;
PNDR_ASYNC_MESSAGE pAsyncMsg;
Status = NdrValidateBothAndLockAsyncHandle( AsyncHandle );
if (RPC_S_OK != Status)
{
return Status;
}
pAsyncMsg = (PNDR_ASYNC_MESSAGE)AsyncHandle->StubInfo;
if ( pAsyncMsg->StubMsg.IsClient )
{
// Abort is not valid on the client.
InterlockedDecrement( & AsyncHandle->Lock );
return RPC_S_INVALID_ASYNC_CALL;
}
#if defined(BUILD_NDR64)
if ( pAsyncMsg->ProcContext.CurrentSyntaxType == XFER_SYNTAX_DCE )
return NdrpAsyncAbortCall( AsyncHandle,
pAsyncMsg,
ExceptionCode,
TRUE ); // Free parameters
else
return Ndr64pAsyncAbortCall( AsyncHandle,
pAsyncMsg,
ExceptionCode,
TRUE ); // Free parameters
#else
return NdrpAsyncAbortCall( AsyncHandle,
pAsyncMsg,
ExceptionCode,
TRUE ); // Free parameters
#endif
}
RPC_STATUS
Ndr64pCompleteAsyncCall (
IN PRPC_ASYNC_STATE AsyncHandle,
IN PNDR_ASYNC_MESSAGE pAsyncMsg,
IN void * pReply
)
/*++
Routine Description:
Completes the virtual async call on server or client side.
Arguments:
AsyncHandle - the async handle controlling the call
Reply - return value:
on server - passed in
on client - returned out
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
RPC_STATUS Status;
RpcTryExcept
{
if ( pAsyncMsg->StubMsg.IsClient )
{
#if defined(BUILD_NDR64)
if ( pAsyncMsg->ProcContext.CurrentSyntaxType == XFER_SYNTAX_DCE )
Status = NdrpCompleteAsyncClientCall( AsyncHandle, pAsyncMsg, pReply );
else
Status = Ndr64pCompleteAsyncClientCall( AsyncHandle, pAsyncMsg, pReply );
#else
NDR_ASSERT( pAsyncMsg->ProcContext.CurrentSyntaxType == XFER_SYNTAX_DCE,
"Invalid transfer syntax" );
Status = NdrpCompleteAsyncClientCall( AsyncHandle, pAsyncMsg, pReply );
#endif
}
else
{
#if defined(BUILD_NDR64)
if ( pAsyncMsg->ProcContext.CurrentSyntaxType == XFER_SYNTAX_DCE )
Status = NdrpCompleteAsyncServerCall ( AsyncHandle, pAsyncMsg, pReply );
else
Status = Ndr64pCompleteAsyncServerCall( AsyncHandle, pAsyncMsg, pReply );
#else
NDR_ASSERT( pAsyncMsg->ProcContext.CurrentSyntaxType == XFER_SYNTAX_DCE,
"Invalid transfer Syntax" );
Status = NdrpCompleteAsyncServerCall ( AsyncHandle, pAsyncMsg, pReply );
#endif
}
}
RpcExcept( !(RPC_BAD_STUB_DATA_EXCEPTION_FILTER) )
{
Status = RpcExceptionCode();
}
RpcEndExcept
return Status;
}
RPC_STATUS RPC_ENTRY
RpcAsyncCompleteCall (
IN PRPC_ASYNC_STATE AsyncHandle,
IN void * pReply
)
/*++
Routine Description:
Completes the virtual async call on server or client side.
Arguments:
AsyncHandle - the async handle controlling the call
Reply - return value:
on server - passed in
on client - returned out
Return Value:
RPC_S_OK - Function succeeded
RPC_S_OUT_OF_MEMORY - we ran out of memory
--*/
{
RPC_STATUS Status = NdrValidateBothAndLockAsyncHandle( AsyncHandle );
if ( Status == RPC_S_OK )
{
Status = Ndr64pCompleteAsyncCall( AsyncHandle,
(PNDR_ASYNC_MESSAGE) AsyncHandle->StubInfo,
pReply );
}
return Status;
}