/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 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 #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; }