/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Copyright (c) 1995-2000 Microsoft Corporation Module Name : pipe.c Abstract : This file contains the idl pipe implementetion code. Author : Ryszard K. Kott (ryszardk) Dec 1995 Revision History : ---------------------------------------------------------------------*/ #define USE_STUBLESS_PROXY #define CINTERFACE #include #include "ndrp.h" #include "ndrole.h" #include "objidl.h" #include "rpcproxy.h" #include "interp.h" #include "interp2.h" #include "mulsyntx.h" #include "pipendr.h" #include "asyncndr.h" #if DBG typedef enum { PIPE_LOG_NONE, PIPE_LOG_CRITICAL, PIPE_LOG_API, PIPE_LOG_NOISE } NDR_PIPE_LOG_LEVEL; NDR_PIPE_LOG_LEVEL NdrPipeLogLevel = PIPE_LOG_NONE; #define NDR_PIPE_LOG( level, args ) \ if ( NdrPipeLogLevel > level ) \ { \ DbgPrint( "NdrPipeLog: %d : ", level ); \ DbgPrint args ; \ DbgPrint( "\n" ); \ } \ #else #define NDR_PIPE_LOG( level, args ) #endif RPC_STATUS RPC_ENTRY NdrSend( NDR_PIPE_DESC * pPipeDesc, PMIDL_STUB_MESSAGE pStubMsg, BOOL fPartial ) /*++ Routine Description : Performs a I_RpcSend in all the context it could be used: - pipes, - async, no pipes Arguments : pStubMsg - Pointer to stub message structure. pBufferEnd - taken as StubMsg->Buffer Return : The new message buffer pointer returned from the runtime after the partial Send to the server. --*/ { RPC_STATUS Status; PRPC_MESSAGE pRpcMsg; pRpcMsg = pStubMsg->RpcMsg; if ( pRpcMsg->BufferLength < (uint)(pStubMsg->Buffer - (uchar *)pRpcMsg->Buffer)) { NDR_ASSERT( 0, "NdrSend : buffer overflow" ); NdrpRaisePipeException( pPipeDesc, RPC_S_INTERNAL_ERROR ); } pRpcMsg->BufferLength = (ulong)(pStubMsg->Buffer - (uchar *)pRpcMsg->Buffer); pStubMsg->fBufferValid = FALSE; if ( fPartial ) pRpcMsg->RpcFlags |= RPC_BUFFER_PARTIAL; else pRpcMsg->RpcFlags &= ~RPC_BUFFER_PARTIAL; if ( pStubMsg->pRpcChannelBuffer ) { ((IRpcChannelBuffer3 *)pStubMsg->pRpcChannelBuffer)->lpVtbl->Send( (IRpcChannelBuffer3 *)pStubMsg->pRpcChannelBuffer, (RPCOLEMESSAGE *)pRpcMsg, (unsigned long*) &Status ); NDR_ASSERT( Status != RPC_S_SEND_INCOMPLETE, "Unexpected channel error" ); } else Status = I_RpcSend( pRpcMsg ); if ( ! ( Status == RPC_S_OK || (Status == RPC_S_SEND_INCOMPLETE && fPartial) ) ) { if ( pStubMsg->pAsyncMsg ) pStubMsg->pAsyncMsg->Flags.RuntimeCleanedUp = 1; else if ( fPartial && ! pStubMsg->IsClient ) { // The buffer on which it failed has been freed by the runtime. // The stub has to return to runtime with the original buffer. // See ResetToDispatchBuffer for more info. pRpcMsg->Buffer = pPipeDesc->DispatchBuffer; pStubMsg->BufferStart = pPipeDesc->DispatchBuffer; pStubMsg->BufferEnd = pPipeDesc->DispatchBuffer + pPipeDesc->DispatchBufferLength; } NdrpRaisePipeException( pPipeDesc, Status ); } // in the server side, after send succeed, pRpcMsg passed in from runtime is freed and // shouldn't be accessed. if ( pStubMsg->IsClient || Status != RPC_S_OK ) { pStubMsg->Buffer = (uchar*) pRpcMsg->Buffer; pStubMsg->fBufferValid = TRUE; } return( Status ); } void RPC_ENTRY NdrPartialSend( NDR_PIPE_DESC * pPipeDesc, PMIDL_STUB_MESSAGE pStubMsg ) /*++ Routine Description : Performs a partial I_RpcSend. It's used in the following contexts: - synchronous pipes - async pipes Arguments : pStubMsg - Pointer to stub message structure. pBufferEnd - taken as StubMsg->Buffer Return : The new message buffer pointer returned from the runtime after the partial Send to the server. Note: The partial I_RpcSend sends out full packets, the data from the last packet is left over and stays in the same buffer. That buffer can later be "reallocated" or reallocated for the new size. This is done in the NdrGetPartialBuffer. --*/ { RPC_STATUS Status; PRPC_MESSAGE pRpcMsg = pStubMsg->RpcMsg; // This routine needs to send only multiple of 8s now. pPipeDesc->LeftoverSize = PtrToUlong(pStubMsg->Buffer) & 0x7; if ( pPipeDesc->LeftoverSize ) { pStubMsg->Buffer -= pPipeDesc->LeftoverSize; RpcpMemoryCopy( pPipeDesc->Leftover, pStubMsg->Buffer, pPipeDesc->LeftoverSize ); } Status = NdrSend( pPipeDesc, pStubMsg, TRUE ); // send partial // Handle unsent buffer case. if ( Status == RPC_S_SEND_INCOMPLETE ) { pPipeDesc->LastPartialBuffer = (uchar*) pRpcMsg->Buffer; pPipeDesc->LastPartialSize = pRpcMsg->BufferLength; NDR_ASSERT( ((LONG_PTR)pRpcMsg->Buffer & 0x7) == 0, "unsent buffer should still be a multiple of 8" ); } else { // means no buffer left behind. pPipeDesc->LastPartialBuffer = NULL; pPipeDesc->LastPartialSize = 0; } // Handle the modulo 8 leftover. if ( pPipeDesc->LeftoverSize ) { pPipeDesc->LastPartialBuffer = (uchar*) pRpcMsg->Buffer; pPipeDesc->LastPartialSize += pPipeDesc->LeftoverSize; } } void RPC_ENTRY NdrCompleteSend( NDR_PIPE_DESC * pPipeDesc, PMIDL_STUB_MESSAGE pStubMsg ) /*++ Routine Description : Performs a complete send via I_RpcSend. Arguments : pStubMsg - Pointer to stub message structure. pBufferEnd - taken as StubMsg->Buffer Return : Note : I_RpcSend with partial bit zeroed out is a rough equivalant of RpcSendReceive; this also covers the way the buffer is handled. The runtime takes care of the sent buffer, returns a buffer with data that needs to be freed later by the stub. If the buffer coming back is partial, the partial Receives take care of it and only the last one needs to be freed w/RpcFreeBuffer. --*/ { NdrSend( pPipeDesc, pStubMsg, FALSE ); // send not partial pPipeDesc->LastPartialBuffer = NULL; pPipeDesc->LastPartialSize = 0; } void RPC_ENTRY NdrReceive( NDR_PIPE_DESC * pPipeDesc, PMIDL_STUB_MESSAGE pStubMsg, unsigned long Size, BOOL fPartial ) /*++ Routine Description : Performs a partial I_RpcReceive. Arguments : pStubMsg - Pointer to stub message structure. pBufferEnd - taken as StubMsg->Buffer Return : --*/ { RPC_STATUS Status; PRPC_MESSAGE pRpcMsg = pStubMsg->RpcMsg; unsigned long CurOffset = 0; pStubMsg->fBufferValid = FALSE; if ( ! pStubMsg->pRpcChannelBuffer ) { // Channel sets the flag on the last, complete send // so we cannot assert for channel for now. NDR_ASSERT( !(pRpcMsg->RpcFlags & RPC_BUFFER_COMPLETE), "no more buffers" ); } if ( fPartial ) { pRpcMsg->RpcFlags |= RPC_BUFFER_PARTIAL; pRpcMsg->RpcFlags &= ~RPC_BUFFER_EXTRA; // For partials, the current offset will be zero. } else { pRpcMsg->RpcFlags &= ~RPC_BUFFER_PARTIAL; // The extra flag may be set or cleared by the caller. // On the client // it should be cleared if there is no out pipes // it should be set only on the last receive after out pipes // On the server // it should be set on the initial receives that get non pipe data // it should cleared on all the other receives. // For a complete with extra (i.e. appending new data), // the current offset needs to be preserved. // For a complete without extra, the offset is zero // as the receive follows immediately after a send call. CurOffset = (ulong)(pStubMsg->Buffer - (uchar*)pRpcMsg->Buffer); } if ( pStubMsg->pRpcChannelBuffer ) { ((IRpcChannelBuffer3 *)pStubMsg->pRpcChannelBuffer)->lpVtbl->Receive( (IRpcChannelBuffer3 *)pStubMsg->pRpcChannelBuffer, (RPCOLEMESSAGE *)pRpcMsg, Size, (unsigned long *)&Status ); } else Status = I_RpcReceive( pRpcMsg, Size ); if ( Status ) { // Pending is also OK: if so, we should not touch the state, // just return from the call to the app. if ( Status != RPC_S_ASYNC_CALL_PENDING ) { // Real bug happened, the state will be teared down etc. if ( ! pStubMsg->IsClient ) { // The buffer on which it failed has been freed by the runtime. // See ResetToDispatchBuffer for explanations why we do this. // Also note, on server we never call with async-nonpipe context. pRpcMsg->Buffer = pPipeDesc->DispatchBuffer; pStubMsg->BufferStart = pPipeDesc->DispatchBuffer; pStubMsg->BufferEnd = pPipeDesc->DispatchBuffer + pPipeDesc->DispatchBufferLength; } if ( pStubMsg->pAsyncMsg ) { // raw rpc: if async, prevent calling abort later. if ( pStubMsg->pAsyncMsg ) pStubMsg->pAsyncMsg->Flags.RuntimeCleanedUp = 1; } } else { // // Even though the runtime did not return a buffer, we need to mark // the buffer as valid. This is necessary to ensure proper call cleanup // when the user call RpcAsyncCompleteCall upon receiving // an async failure. // // This only affects async pipes. // NDR_ASSERT(pRpcMsg->Buffer == NULL, "Rpc runtime returned RPC_S_ASYNC_CALL_PENDING and a buffer." ); pStubMsg->fBufferValid = TRUE; } NdrpRaisePipeException( pPipeDesc, Status); } NDR_ASSERT( 0 == pRpcMsg->BufferLength || NULL != pRpcMsg->Buffer, "Rpc runtime returned an invalid buffer." ); if ( fPartial ) { NDR_ASSERT( pRpcMsg->BufferLength, "a partial buffer can't be empty" ); if ( pRpcMsg->BufferLength == 0) RpcRaiseException( RPC_X_INVALID_BUFFER ); } pStubMsg->Buffer = (uchar*) pRpcMsg->Buffer + CurOffset; pStubMsg->BufferStart = (uchar*)pRpcMsg->Buffer; pStubMsg->BufferEnd = (uchar*)pRpcMsg->Buffer + pRpcMsg->BufferLength; pStubMsg->fBufferValid = TRUE; } void RPC_ENTRY NdrPartialReceive( NDR_PIPE_DESC * pPipeDesc, PMIDL_STUB_MESSAGE pStubMsg, unsigned long Size ) /*++ Routine Description : Performs a partial I_RpcReceive. Arguments : pStubMsg - Pointer to stub message structure. pBufferEnd - taken as StubMsg->Buffer Return : --*/ { // // On the server we need to keep the dispatch buffer as the non-pipe // arguments may actually reside in the buffer. // pPipeDesc->DispatchBuffer always points to the original buffer. // // Note that the runtime would free any buffer passed in the receive call // unless the extra flag is set. // Buffer being zero means a request for a new buffer. if ( pPipeDesc->Flags.NoMoreBuffersToRead ) NdrpRaisePipeException( pPipeDesc, RPC_X_BAD_STUB_DATA ); if ( ! pStubMsg->IsClient && (pPipeDesc->DispatchBuffer == pStubMsg->RpcMsg->Buffer ) ) { // Setup a request for a new buffer. pStubMsg->RpcMsg->Buffer = NULL; } NdrReceive( pPipeDesc, pStubMsg, Size, TRUE ); // partial if ( !( pStubMsg->RpcMsg->RpcFlags & RPC_BUFFER_COMPLETE ) && ( pStubMsg->RpcMsg->BufferLength & 0x7 ) ) { NDR_ASSERT( 0, "Partial buffer length not multiple of 8"); NdrpRaisePipeException( pPipeDesc, RPC_S_INTERNAL_ERROR ); } if ( pStubMsg->RpcMsg->RpcFlags & RPC_BUFFER_COMPLETE ) pPipeDesc->Flags.NoMoreBuffersToRead = 1; } unsigned char * RPC_ENTRY NdrGetPipeBuffer( PMIDL_STUB_MESSAGE pStubMsg, unsigned long BufferLength, RPC_BINDING_HANDLE Handle ) /*++ Routine Description : This is the first call at the client or server side. Needs to be different from NdrGetBuffer as later calls to get buffer are different. - raw: the buffer will be reallocated by means of I_RpcReallocPipeBuffer --*/ { unsigned char * pBuffer; PRPC_MESSAGE pRpcMsg = pStubMsg->RpcMsg; pRpcMsg->RpcFlags &= ~RPC_BUFFER_COMPLETE; pRpcMsg->RpcFlags |= RPC_BUFFER_PARTIAL; //#if !defined(__RPC_WIN64__) pRpcMsg->ProcNum |= RPC_FLAGS_VALID_BIT; //#endif pBuffer = NdrGetBuffer( pStubMsg, BufferLength, Handle ); return pBuffer; } void RPC_ENTRY NdrGetPartialBuffer( PMIDL_STUB_MESSAGE pStubMsg ) /*++ Routine Description : Gets the next buffer for a partial send depending on the pipe state. Because of the way partial I_RpcSend works, this routine takes care of the leftover from the last send by means of setting the buffer pointer to the first free position. See NdrPartialSend for more comments. Return : --*/ { RPC_STATUS Status = RPC_S_OK; NDR_PIPE_DESC * pPipeDesc = (NDR_PIPE_DESC *) pStubMsg->pContext->pPipeDesc; pStubMsg->RpcMsg->RpcFlags &= ~RPC_BUFFER_COMPLETE; pStubMsg->RpcMsg->RpcFlags |= RPC_BUFFER_PARTIAL; Status = I_RpcReallocPipeBuffer( pStubMsg->RpcMsg, pStubMsg->BufferLength ); if ( Status != RPC_S_OK ) { // Raw rpc: if async, don't call runtime to abort later. if ( pStubMsg->pAsyncMsg ) pStubMsg->pAsyncMsg->Flags.RuntimeCleanedUp = 1; NdrpRaisePipeException( pPipeDesc, Status ); } ASSERT( pStubMsg->RpcMsg->BufferLength >= pStubMsg->BufferLength ); pStubMsg->Buffer = (uchar*) pStubMsg->RpcMsg->Buffer + pPipeDesc->LastPartialSize; if ( pPipeDesc->LeftoverSize ) { // Because of sizing, LastPartialSize already had LeftoverSize in it. RpcpMemoryCopy( pStubMsg->Buffer - pPipeDesc->LeftoverSize, pPipeDesc->Leftover, pPipeDesc->LeftoverSize ); } } void RPC_ENTRY NdrPipesInitialize( PMIDL_STUB_MESSAGE pStubMsg, PNDR_PIPE_HELPER pPipeHelper, PNDR_ALLOCA_CONTEXT pAllocContext ) /*+ Initializes all the pipe structures. Only standard RPC (non-DCOM) pipes are supported now. +*/ { NDR_PIPE_STATE * pRuntimeState; NDR_ASSERT( ! pStubMsg->pRpcChannelBuffer, "DCOM pipes not supported" ); NDR_PIPE_DESC *pPipeDesc = pPipeHelper->GetPipeDesc(); MIDL_memset( pPipeDesc, 0, sizeof(NDR_PIPE_DESC) ); pPipeDesc->pPipeHelper = pPipeHelper; pPipeDesc->pAllocContext = pAllocContext; pStubMsg->pContext->pPipeDesc = pPipeDesc; // See how many pipes we have and what the maximum wire size is. if ( pPipeHelper->InitParamEnum() ) { do { unsigned short PipeFlags = pPipeHelper->GetParamPipeFlags(); if ( PipeFlags ) { if ( PipeFlags & NDR_IN_PIPE ) pPipeDesc->InPipes++; if ( PipeFlags & NDR_OUT_PIPE ) pPipeDesc->OutPipes++; pPipeDesc->TotalPipes++; } } while ( pPipeHelper->GotoNextParam() ); } pPipeDesc->pPipeMsg = (NDR_PIPE_MESSAGE *)NdrpAlloca( pPipeDesc->pAllocContext, pPipeDesc->TotalPipes * sizeof( NDR_PIPE_MESSAGE ) ); MIDL_memset( pPipeDesc->pPipeMsg, 0, pPipeDesc->TotalPipes * sizeof( NDR_PIPE_MESSAGE )); pPipeDesc->CurrentPipe = -1; pPipeDesc->PrevPipe = -1; pPipeDesc->PipeVersion = NDR_PIPE_VERSION; // Now set the individual pipe messages. NDR_PIPE_MESSAGE *pLastInPipe = NULL; NDR_PIPE_MESSAGE *pLastOutPipe = NULL; int PipeNo = 0; if ( pPipeHelper->InitParamEnum() ) { do { unsigned short PipeFlags = pPipeHelper->GetParamPipeFlags(); if ( !PipeFlags) continue; NDR_PIPE_MESSAGE * pPipe = & pPipeDesc->pPipeMsg[ PipeNo ]; if ( PipeFlags & NDR_IN_PIPE ) pLastInPipe = pPipe; if ( PipeFlags & NDR_OUT_PIPE ) pLastOutPipe = pPipe; pPipe->Signature = NDR_PIPE_SIGNATURE; pPipe->PipeId = (ushort)PipeNo; pPipe->PipeStatus = NDR_PIPE_NOT_OPENED; pPipe->PipeFlags = PipeFlags; pPipe->pTypeFormat = pPipeHelper->GetParamTypeFormat(); pPipe->pStubMsg = pStubMsg; pPipe->pPipeObject = (GENERIC_PIPE_TYPE *) pPipeHelper->GetParamArgument(); #if defined(_AMD64_) // in the server side, we need to allocate the GENERIC_PIPE_TYPE as it's not no stack now. // don't want to check non-amd64 code so I just exclude the code path for NDR_REF_PIPE if ( ! (pPipe->PipeFlags & NDR_REF_PIPE ) ) { if (!pStubMsg->IsClient ) { void * temp = NdrpAlloca( pPipeDesc->pAllocContext, sizeof(GENERIC_PIPE_TYPE ) ); MIDL_memset( temp, 0, sizeof( GENERIC_PIPE_TYPE ) ); *(void **)pPipe->pPipeObject = (void **)temp; pPipe->pPipeObject = (GENERIC_PIPE_TYPE *)temp; } else pPipe->pPipeObject = *(GENERIC_PIPE_TYPE **)pPipe->pPipeObject ; } #endif if ( pPipe->PipeFlags & NDR_REF_PIPE ) { // dereference the argument to get the pipe control block. if ( ! pStubMsg->IsClient ) { // For the server, under interpreter, we don't have // the actual pipe object that is referenced. // The stack argument should be null. NDR_ASSERT( ! *(void **)pPipe->pPipeObject, "null expected for out pipe by ref" ); // The pipe object is not a real parameter in the // same sense as the other RPC parameters. The user // can not free the object. void * temp = NdrpAlloca( pPipeDesc->pAllocContext, sizeof(GENERIC_PIPE_TYPE ) ); MIDL_memset( temp, 0, sizeof( GENERIC_PIPE_TYPE ) ); *(void **)pPipe->pPipeObject = temp; pPipe->PipeFlags |= NDR_OUT_ALLOCED; } pPipe->pPipeObject = *(GENERIC_PIPE_TYPE **)pPipe->pPipeObject; } // For raw async rpc set up the pipe arg on both sides. // For non-async raw set up the pipe on server only. if ( pStubMsg->IsClient ) { if ( pStubMsg->pAsyncMsg ) { GENERIC_PIPE_TYPE * pPipeType = pPipe->pPipeObject; pPipeType->pfnPull = NdrAsyncPipePull; pPipeType->pfnPush = NdrAsyncPipePush; pPipeType->pfnAlloc= NdrAsyncPipeAlloc; pPipeType->pState = (char *)pPipe; } } else { GENERIC_PIPE_TYPE * pPipeType = pPipe->pPipeObject; if ( pStubMsg->pAsyncMsg ) { pPipeType->pfnPull = NdrAsyncPipePull; pPipeType->pfnPush = NdrAsyncPipePush; pPipeType->pfnAlloc= NdrAsyncPipeAlloc; pPipeType->pState = (char *)pPipe; } else { pPipeType->pfnPull = (NDR_HR_PIPE_PULL_RTN) NdrPipePull; pPipeType->pfnPush = (NDR_HR_PIPE_PUSH_RTN) NdrPipePush; pPipeType->pfnAlloc= (NDR_HR_PIPE_ALLOC_RTN)NdrPipeAlloc; pPipeType->pState = (char *)pPipe; } } PipeNo++; } while ( pPipeHelper->GotoNextParam() ); } // Mark the last in and out pipes. if ( pLastInPipe ) pLastInPipe->PipeFlags |= NDR_LAST_IN_PIPE; if ( pLastOutPipe ) pLastOutPipe->PipeFlags |= NDR_LAST_OUT_PIPE; // Set up structures for receiving pipes. pPipeDesc->DispatchBuffer = (uchar *) pStubMsg->RpcMsg->Buffer; pPipeDesc->DispatchBufferLength = pStubMsg->RpcMsg->BufferLength; if ( pPipeDesc->OutPipes && pStubMsg->IsClient || pPipeDesc->InPipes && ! pStubMsg->IsClient ) { pRuntimeState = & pPipeDesc->RuntimeState; pRuntimeState->CurrentState = START; pRuntimeState->TotalElemsCount = 0; pRuntimeState->PartialElem = 0; // temp buf for elem pRuntimeState->PartialBufferSize = 0; // temp buf for elem } if ( ! pStubMsg->IsClient && (pStubMsg->RpcMsg->RpcFlags & RPC_BUFFER_COMPLETE )) pPipeDesc->Flags.NoMoreBuffersToRead = 1; } class NDR_PIPE_HELPER32 : public NDR_PIPE_HELPER { private: PMIDL_STUB_MESSAGE pStubMsg; char *pStackTop; unsigned long NumberParameters; PPARAM_DESCRIPTION pFirstParameter; PPARAM_DESCRIPTION pCurrentParameter; unsigned long CurrentParamNumber; NDR_PIPE_DESC PipeDesc; public: void *operator new( size_t stAllocateBlock, PNDR_ALLOCA_CONTEXT pAllocContext ) { return NdrpAlloca( pAllocContext, (UINT)stAllocateBlock ); } // Do nothing since the memory will be deleted automatically void operator delete(void *pMemory) {} NDR_PIPE_HELPER32( PMIDL_STUB_MESSAGE pStubMsg, PFORMAT_STRING Params, char * pStackTop, unsigned long NumberParams ) { NDR_PIPE_HELPER32::pStubMsg = pStubMsg; NDR_PIPE_HELPER32::pStackTop = pStackTop; pFirstParameter = (PPARAM_DESCRIPTION)Params; NumberParameters = NumberParams; } virtual PNDR_PIPE_DESC GetPipeDesc() { return &PipeDesc; } virtual bool InitParamEnum() { pCurrentParameter = pFirstParameter; CurrentParamNumber = 0; return NumberParameters > 0; } virtual bool GotoNextParam() { if ( CurrentParamNumber + 1 >= NumberParameters ) { return false; } CurrentParamNumber++; pCurrentParameter = pFirstParameter + CurrentParamNumber; return true; } virtual unsigned short GetParamPipeFlags() { if ( !pCurrentParameter->ParamAttr.IsPipe ) return 0; unsigned short Flags = 0; if ( pCurrentParameter->ParamAttr.IsIn ) Flags |= NDR_IN_PIPE; if ( pCurrentParameter->ParamAttr.IsOut ) Flags |= NDR_OUT_PIPE; if ( pCurrentParameter->ParamAttr.IsSimpleRef ) Flags |= NDR_REF_PIPE; return Flags; } virtual PFORMAT_STRING GetParamTypeFormat() { return pStubMsg->StubDesc->pFormatTypes + pCurrentParameter->TypeOffset; } virtual char *GetParamArgument() { return pStackTop + pCurrentParameter->StackOffset; } virtual void InitPipeStateWithType( PNDR_PIPE_MESSAGE pPipeMsg ) { FC_PIPE_DEF * pPipeFc = (FC_PIPE_DEF *) pPipeMsg->pTypeFormat; NDR_PIPE_STATE *pState = & PipeDesc.RuntimeState; pState->LowChunkLimit = 0; pState->HighChunkLimit = NDR_DEFAULT_PIPE_HIGH_CHUNK_LIMIT; pState->ElemAlign = pPipeFc->Align; if ( pPipeFc->BigPipe ) { pState->ElemMemSize = * (long UNALIGNED *) & pPipeFc->Big.MemSize; pState->ElemWireSize = * (long UNALIGNED *) & pPipeFc->Big.WireSize; if ( pPipeFc->HasRange ) { pState->LowChunkLimit = * (long UNALIGNED *) &pPipeFc->Big.LowChunkLimit; pState->HighChunkLimit = * (long UNALIGNED *) &pPipeFc->Big.HighChunkLimit; } } else { pState->ElemMemSize = pPipeFc->s.MemSize; pState->ElemWireSize = pPipeFc->s.WireSize; if ( pPipeFc->HasRange ) { pState->LowChunkLimit = * (long UNALIGNED *) &pPipeFc->s.LowChunkLimit; pState->HighChunkLimit = * (long UNALIGNED *) &pPipeFc->s.HighChunkLimit; } } pState->ElemPad = WIRE_PAD( pState->ElemWireSize, pState->ElemAlign ); pState->fBlockCopy = (pState->ElemMemSize == pState->ElemWireSize + pState->ElemPad); } virtual void MarshallType( PNDR_PIPE_MESSAGE pPipeMsg, uchar *pMemory, unsigned long Elements ) { unsigned long ElemMemSize = PipeDesc.RuntimeState.ElemMemSize; FC_PIPE_DEF * pPipeFc = (FC_PIPE_DEF *) pPipeMsg->pTypeFormat; PFORMAT_STRING pElemFormat = (uchar *) & pPipeFc->TypeOffset + pPipeFc->TypeOffset; while( Elements-- ) { (*pfnMarshallRoutines[ROUTINE_INDEX(*pElemFormat)]) ( pPipeMsg->pStubMsg, pMemory, pElemFormat); pMemory += ElemMemSize; } } virtual void UnmarshallType( PNDR_PIPE_MESSAGE pPipeMsg, uchar *pMemory, unsigned long Elements ) { unsigned long ElemMemSize = PipeDesc.RuntimeState.ElemMemSize; FC_PIPE_DEF * pPipeFc = (FC_PIPE_DEF *) pPipeMsg->pTypeFormat; PFORMAT_STRING pElemFormat = (uchar *) & pPipeFc->TypeOffset + pPipeFc->TypeOffset; while( Elements-- ) { (*pfnUnmarshallRoutines[ROUTINE_INDEX(*pElemFormat)]) ( pPipeMsg->pStubMsg, &pMemory, pElemFormat, FALSE ); pMemory += ElemMemSize; } } virtual void BufferSizeType( PNDR_PIPE_MESSAGE pPipeMsg, uchar *pMemory, unsigned long Elements ) { unsigned long ElemMemSize = PipeDesc.RuntimeState.ElemMemSize; FC_PIPE_DEF * pPipeFc = (FC_PIPE_DEF *) pPipeMsg->pTypeFormat; PFORMAT_STRING pElemFormat = (uchar *) & pPipeFc->TypeOffset + pPipeFc->TypeOffset; while( Elements-- ) { (*pfnSizeRoutines[ROUTINE_INDEX(*pElemFormat)]) ( pPipeMsg->pStubMsg, pMemory, pElemFormat); pMemory += ElemMemSize; } } virtual void ConvertType( PNDR_PIPE_MESSAGE pPipeMsg, unsigned long Elements ) { unsigned long ElemMemSize = PipeDesc.RuntimeState.ElemMemSize; FC_PIPE_DEF * pPipeFc = (FC_PIPE_DEF *) pPipeMsg->pTypeFormat; PFORMAT_STRING pElemFormat = (uchar *) & pPipeFc->TypeOffset + pPipeFc->TypeOffset; PMIDL_STUB_MESSAGE pStubMsg = pPipeMsg->pStubMsg; if ( pStubMsg->RpcMsg->DataRepresentation == NDR_LOCAL_DATA_REPRESENTATION ) return; uchar * BufferSaved = pStubMsg->Buffer; // We can end up here for any object. while ( Elements-- ) { if ( IS_SIMPLE_TYPE( *pElemFormat) ) { NdrSimpleTypeConvert( pStubMsg, *pElemFormat ); } else { (*pfnConvertRoutines[ ROUTINE_INDEX( *pElemFormat ) ]) ( pStubMsg, pElemFormat, FALSE ); // embedded pointers } } pStubMsg->Buffer = BufferSaved; } virtual void BufferSizeChunkCounter( PNDR_PIPE_MESSAGE pPipeMsg ) { PMIDL_STUB_MESSAGE pStubMsg = pPipeMsg->pStubMsg; LENGTH_ALIGN( pStubMsg->BufferLength, 3 ); pStubMsg->BufferLength += sizeof(ulong); } virtual bool UnmarshallChunkCounter( PNDR_PIPE_MESSAGE pPipeMsg, ulong *pOut ) { PMIDL_STUB_MESSAGE pStubMsg = pPipeMsg->pStubMsg; ALIGN( pStubMsg->Buffer, 3); if ( 0 == REMAINING_BYTES() ) { return false; } // transition: end of src if (REMAINING_BYTES() < sizeof(DWORD)) { // with packet sizes being a multiple of 8, // this cannot happen. NDR_ASSERT( 0, "Chunk counter split is not possible."); NdrpRaisePipeException( &PipeDesc, RPC_S_INTERNAL_ERROR ); return false; } if ( pStubMsg->RpcMsg->DataRepresentation != NDR_LOCAL_DATA_REPRESENTATION ) { uchar * BufferSaved = pStubMsg->Buffer; NdrSimpleTypeConvert( pStubMsg, FC_LONG ); pStubMsg->Buffer = BufferSaved; } ulong Value = *(ulong*)pStubMsg->Buffer; pStubMsg->Buffer += sizeof(ulong); CHECK_BOUND( Value, FC_ULONG ); *pOut = Value; return true; } virtual void MarshallChunkCounter( PNDR_PIPE_MESSAGE pPipeMsg, ulong Counter ) { PMIDL_STUB_MESSAGE pStubMsg = pPipeMsg->pStubMsg; CHECK_BOUND( Counter, FC_ULONG ); ALIGN( pStubMsg->Buffer, 3); *(ulong*)pStubMsg->Buffer = Counter; pStubMsg->Buffer += sizeof(ulong); } virtual void BufferSizeChunkTailCounter( PNDR_PIPE_MESSAGE pPipeMsg ) { pPipeMsg; }; virtual void MarshallChunkTailCounter( PNDR_PIPE_MESSAGE pPipeMsg, ulong Counter ) { pPipeMsg; Counter; } virtual bool VerifyChunkTailCounter( PNDR_PIPE_MESSAGE pPipeMsg, ulong HeaderCounter ) { pPipeMsg; HeaderCounter; return true; } virtual bool HasChunkTailCounter() { return FALSE; } }; typedef NDR_PIPE_HELPER *PNDR_PIPE_HELPER; void NdrpPipesInitialize32( PMIDL_STUB_MESSAGE pStubMsg, PNDR_ALLOCA_CONTEXT pAllocContext, PFORMAT_STRING Params, char * pStackTop, unsigned long NumberParams ) { /* C wrapper to initialize the 32 pipe helper and call NdrPipesInitialize*/ NDR_PIPE_HELPER32 *pPipeHelper = new( pAllocContext ) NDR_PIPE_HELPER32( pStubMsg, Params, pStackTop, NumberParams ); NdrPipesInitialize( pStubMsg, pPipeHelper, pAllocContext ); } void RPC_ENTRY ResetToDispatchBuffer( NDR_PIPE_DESC * pPipeDesc, PMIDL_STUB_MESSAGE pStubMsg ) /* This is a server side routine that makes sure that the original dispatch buffer is restored to the rpc message. These are the rules: - the engine can return to the runtime with the original or a new buffer; if it's a new buffer it has to be valid (not freed yet). When exception happens, the runtime will be freeing any buffer that is different from the original buffer. - when I_RpcReceive or I_RpcSend fail, they free the current buffer, and so the stub cannot free it. If this is the second buffer, the original dispatch buffer pointer should be restored in the rpcmsg. - the second buffer does not have to be freed in case of a normal return or an exception clean up in a situation different from above. The runtime will free it. Note that we never free the original dispatch buffer. */ { PRPC_MESSAGE pRpcMsg = pStubMsg->RpcMsg; // If the dispatch buffer was replaced by a partial buffer, // free the partial buffer and restore the dispatch buffer. if ( pPipeDesc->DispatchBuffer != pRpcMsg->Buffer ) { I_RpcFreePipeBuffer( pRpcMsg ); pRpcMsg->Buffer = pPipeDesc->DispatchBuffer; pStubMsg->BufferStart = pPipeDesc->DispatchBuffer; pStubMsg->BufferEnd = pStubMsg->BufferStart + pPipeDesc->DispatchBufferLength; } } void RPC_ENTRY NdrPipeSendReceive( PMIDL_STUB_MESSAGE pStubMsg, NDR_PIPE_DESC * pPipeDesc ) /*+ Complete send-receive routine for client pipes. It takes over with a buffer filled with non-pipe args, sends [in] pipes, receives [out] pipes and then receives the buffer with non-pipe args. +*/ { int CurrentPipe; NDR_PIPE_MESSAGE * pPipeMsg; RPC_STATUS Status; // Service the in pipes if ( pPipeDesc->InPipes ) { // Once we know we have an [in] pipe, we can send the non-pipe // arguments via a partial I_RpcSend. // It is OK to call that with the BufferLength equal zero. NdrPartialSend( pPipeDesc, pStubMsg ); for ( CurrentPipe = 0; CurrentPipe < pPipeDesc->TotalPipes; CurrentPipe++ ) { long LastChunkSent; pPipeMsg = & pPipeDesc->pPipeMsg[ CurrentPipe ]; if ( ! (pPipeMsg->PipeFlags & NDR_IN_PIPE) ) continue; pPipeDesc->CurrentPipe = (short) CurrentPipe; pPipeMsg->PipeStatus = (ushort) NDR_PIPE_ACTIVE_IN; Status = NdrpPushPipeForClient( pStubMsg, pPipeDesc, TRUE, // whole pipe &LastChunkSent ); // The call above would raise an exception for any case other than // dcom async pipe case. This covers sync usage of this code path. // So, if we are here, the call succeeded, the status should be ok // and last chunk zero, as we requested to push the whole pipe. NDR_ASSERT( Status == RPC_S_OK && LastChunkSent == 0, "should process all or raise exception" ); pPipeMsg->PipeStatus = NDR_PIPE_DRAINED; } // for [in] pipes } NdrCompleteSend( pPipeDesc, pStubMsg ); // The above call uses I_RpcSend and requires that I_RpcReceive is called // later. This has to be done regardless whether any data is expected // in the buffer or not. // The receive call is complete or partial depending on the context. if ( pPipeDesc->OutPipes == 0 ) { // After send we would still have the [in] buffer around so we // need to clear the extra flag to avoid appending. pStubMsg->RpcMsg->RpcFlags &= ~RPC_BUFFER_EXTRA; NdrReceive( pPipeDesc, pStubMsg, 0, // size, ignored for complete calls FALSE ); // complete buffer return; } // Service the out pipes // Partial calls always clear up the extra flag before calling runtime. NdrPartialReceive( pPipeDesc, pStubMsg, PIPE_PARTIAL_BUFFER_SIZE ); // The buffer has some pipe elemements pPipeDesc->BufferSave = pStubMsg->Buffer; pPipeDesc->LengthSave = pStubMsg->RpcMsg->BufferLength; for ( CurrentPipe = 0; CurrentPipe < pPipeDesc->TotalPipes; CurrentPipe++ ) { long LastChunk; BOOL EndOfPipe; pPipeMsg = & pPipeDesc->pPipeMsg[ CurrentPipe ]; if ( ! (pPipeMsg->PipeFlags & NDR_OUT_PIPE) ) continue; pPipeDesc->CurrentPipe = (short) CurrentPipe; pPipeMsg->PipeStatus = NDR_PIPE_ACTIVE_OUT; Status = NdrpPullPipeForClient( pStubMsg, pPipeDesc, TRUE, // whole pipe & LastChunk, & EndOfPipe ); NDR_ASSERT( Status == RPC_S_OK && EndOfPipe, "should process all or raise exception" ); pPipeMsg->PipeStatus = NDR_PIPE_DRAINED; } // for [out] pipes // After all the partial receives, have the last one that is complete. if ( ! (pStubMsg->RpcMsg->RpcFlags & RPC_BUFFER_COMPLETE) ) { // On the last call at client we need the extra flag as some // non-pipe data may have already been received. pStubMsg->RpcMsg->RpcFlags |= RPC_BUFFER_EXTRA; NdrReceive( pPipeDesc, pStubMsg, 0, // size, ignored for complete calls FALSE ); // complete buffer } } RPC_STATUS NdrpPushPipeForClient( PMIDL_STUB_MESSAGE pStubMsg, NDR_PIPE_DESC * pPipeDesc, BOOL fWholePipe, long * pLastChunkSent ) { PFORMAT_STRING pElemFormat; ulong ElemAlign, ElemMemSize, ElemWireSize, ElemPad; ulong PipeAllocSize, CopySize; BOOL fBlockCopy; RPC_STATUS Status = RPC_S_OK; NDR_PIPE_LOG(PIPE_LOG_NOISE, ("NdrpPushPipeForClient: pStubMsg->Buffer %p", pStubMsg->Buffer) ); RpcTryExcept { NDR_PIPE_MESSAGE * pPipeMsg = & pPipeDesc->pPipeMsg[ pPipeDesc->CurrentPipe ]; // Service an in pipe. GENERIC_PIPE_TYPE * pPipeType = pPipeMsg->pPipeObject; pPipeDesc->pPipeHelper->InitPipeStateWithType( pPipeMsg ); ElemAlign = pPipeDesc->RuntimeState.ElemAlign; ElemMemSize = pPipeDesc->RuntimeState.ElemMemSize; ElemWireSize = pPipeDesc->RuntimeState.ElemWireSize; ElemPad = pPipeDesc->RuntimeState.ElemPad; fBlockCopy = pPipeDesc->RuntimeState.fBlockCopy; if ( PIPE_PARTIAL_BUFFER_SIZE < ElemMemSize ) PipeAllocSize = ElemMemSize; else PipeAllocSize = PIPE_PARTIAL_BUFFER_SIZE; uchar * pMemory; unsigned long bcChunkSize; unsigned long ActElems, ReqElems; NDR_HR_PIPE_PULL_RTN pfnPull = pPipeType->pfnPull; NDR_HR_PIPE_ALLOC_RTN pfnAlloc = pPipeType->pfnAlloc; void * pThis = pPipeType->pState; do { HRESULT Hr; // Get memory for the pipe elements Hr = (*pfnAlloc)( (char *)pThis, PipeAllocSize, (void **) & pMemory, & bcChunkSize ); if ( pMemory == 0 ) NdrpRaisePipeException( pPipeDesc, RPC_X_PIPE_APP_MEMORY ); // Get the pipe elements to the buffer. ActElems = bcChunkSize / ElemMemSize; ReqElems = ActElems; if ( ActElems == 0 ) NdrpRaisePipeException( pPipeDesc, RPC_X_INVALID_BUFFER ); Hr = (*pfnPull)( (char *)pThis, pMemory, ActElems, & ActElems ); NDR_PIPE_LOG( PIPE_LOG_NOISE, ("NdrpPushPipeForClient: pfnPull returned %d ActElems", ActElems) ); if ( ReqElems < ActElems ) NdrpRaisePipeException( pPipeDesc, RPC_X_INVALID_BUFFER ); // // Size the chunk and get the marshaling buffer // pStubMsg->BufferLength = pPipeDesc->LastPartialSize; pPipeDesc->pPipeHelper->BufferSizeChunkCounter( pPipeMsg ); CopySize = ( ActElems - 1) * (ElemWireSize + ElemPad) + ElemWireSize; if ( ActElems ) { LENGTH_ALIGN( pStubMsg->BufferLength, ElemAlign ); if ( fBlockCopy ) pStubMsg->BufferLength += CopySize; else { NdrpPipeElementBufferSize( pPipeDesc, pStubMsg, pMemory, ActElems ); } } pPipeDesc->pPipeHelper->BufferSizeChunkTailCounter( pPipeMsg ); // Get the new buffer, put the frame leftover in it. NdrGetPartialBuffer( pStubMsg ); // // Marshal the chunk // pPipeDesc->pPipeHelper->MarshallChunkCounter( pPipeMsg, ActElems ); NDR_PIPE_LOG( PIPE_LOG_NOISE, ( "Writting pipe chunk: %d", ActElems ) ); if ( ActElems ) { ALIGN( pStubMsg->Buffer, ElemAlign ); if ( fBlockCopy ) { RpcpMemoryCopy( pStubMsg->Buffer, pMemory, CopySize ); pStubMsg->Buffer += CopySize; } else { // Again: only complex is possible pPipeDesc->pPipeHelper->MarshallType( pPipeMsg, pMemory, ActElems ); pMemory += ActElems * ElemMemSize; } } pPipeDesc->pPipeHelper->MarshallChunkTailCounter( pPipeMsg, ActElems ); // Send it if it is not the last partial send. if ( !(pPipeMsg->PipeFlags & NDR_LAST_IN_PIPE) || ((pPipeMsg->PipeFlags & NDR_LAST_IN_PIPE) && ActElems) ) NdrPartialSend( pPipeDesc, pStubMsg ); } while( fWholePipe && ActElems > 0 ); *pLastChunkSent = ActElems; NDR_PIPE_LOG( PIPE_LOG_NOISE, ("NdrpPushPipeForClient: exit *pLastChunkSent", *pLastChunkSent ) ); } RpcExcept( ! (RPC_BAD_STUB_DATA_EXCEPTION_FILTER) ) { Status = RpcExceptionCode(); NdrpRaisePipeException( pPipeDesc, Status ); } RpcEndExcept return Status; } RPC_STATUS NdrpPullPipeForClient( PMIDL_STUB_MESSAGE pStubMsg, NDR_PIPE_DESC * pPipeDesc, BOOL fWholePipe, long * pActElems, BOOL * pEndOfPipe ) { PFORMAT_STRING pElemFormat; ulong ElemAlign, ElemMemSize, ElemWireSize, ElemPad; BOOL fBlockCopy; long ActElems; RPC_STATUS Status = RPC_S_OK; RpcTryExcept { NDR_PIPE_MESSAGE * pPipeMsg = & pPipeDesc->pPipeMsg[ pPipeDesc->CurrentPipe ]; // Service an out pipe pPipeDesc->pPipeHelper->InitPipeStateWithType( pPipeMsg ); pPipeDesc->RuntimeState.EndOfPipe = 0; ElemAlign = pPipeDesc->RuntimeState.ElemAlign; ElemMemSize = pPipeDesc->RuntimeState.ElemMemSize; ElemWireSize = pPipeDesc->RuntimeState.ElemWireSize; ElemPad = pPipeDesc->RuntimeState.ElemPad; fBlockCopy = pPipeDesc->RuntimeState.fBlockCopy; GENERIC_PIPE_TYPE * pPipeType = pPipeMsg->pPipeObject; NDR_HR_PIPE_PUSH_RTN pfnPush = pPipeType->pfnPush; NDR_HR_PIPE_ALLOC_RTN pfnAlloc = pPipeType->pfnAlloc; void * pThis = pPipeType->pState; BOOL EndOfPipe; // RequestedSize estimates a reasonable size for pfnAlloc call. HRESULT Hr; ulong RequestedSize; uchar * pMemory; if (ElemWireSize< PIPE_PARTIAL_BUFFER_SIZE) RequestedSize = (PIPE_PARTIAL_BUFFER_SIZE / ElemWireSize) * ElemMemSize; else RequestedSize = 2 * ElemMemSize; do { unsigned long bcChunkSize; // Get a chunk of memory for pipe elements to push Hr = (*pfnAlloc)( (char *)pThis, RequestedSize, (void **) & pMemory, & bcChunkSize ); if ( pMemory == 0 ) NdrpRaisePipeException( pPipeDesc, RPC_X_PIPE_APP_MEMORY ); ActElems = bcChunkSize / ElemMemSize; if ( ActElems == 0 ) NdrpRaisePipeException( pPipeDesc, RPC_X_INVALID_BUFFER ); EndOfPipe = NdrReadPipeElements( pPipeDesc, pStubMsg, pMemory, & ActElems ); Hr = (*pfnPush)( (char *)pThis, pMemory, ActElems ); } while ( fWholePipe && ActElems && ! EndOfPipe ); if ( ActElems ) { Hr = (*pfnPush)( (char *)pThis, pMemory + ActElems * ElemMemSize, 0 ); } *pActElems = ActElems; *pEndOfPipe = EndOfPipe; } RpcExcept( ! (RPC_BAD_STUB_DATA_EXCEPTION_FILTER) ) { Status = RpcExceptionCode(); NdrpRaisePipeException( pPipeDesc, Status ); } RpcEndExcept return Status; } void NdrMarkNextActivePipe( NDR_PIPE_DESC * pPipeDesc ) /* Description: This routine is used on the server side sync calls, or on both side of async calls to manage the proper sequence of pipes to service. Note: When the last possible pipe is done, this routine restores the original dispatch buffer to the rpc message. */ { unsigned long Mask; int NextPipe = 0; int CurrentPipe = pPipeDesc->CurrentPipe; if ( CurrentPipe == -1 ) { // This means an initial call at any side. // Find the first in pipe, or if none, find first out pipe. Mask = pPipeDesc->InPipes ? NDR_IN_PIPE : NDR_OUT_PIPE; } else { // Switching from one active pipe to another. NDR_PIPE_MESSAGE * pPipeMsg; unsigned short LastPipeStatus; pPipeMsg = & pPipeDesc->pPipeMsg[ pPipeDesc->CurrentPipe ]; LastPipeStatus = pPipeMsg->PipeStatus; pPipeMsg->PipeStatus = NDR_PIPE_DRAINED; // Mark no active pipe. pPipeDesc->PrevPipe = pPipeDesc->CurrentPipe; pPipeDesc->CurrentPipe = -1; // See if the drained pipe was the last possible pipe. if ( (LastPipeStatus == NDR_PIPE_ACTIVE_OUT) && (pPipeMsg->PipeFlags & NDR_LAST_OUT_PIPE) ) { return; } // See if the drained pipe was the last in pipe. // Set how to look for the next active pipe. if ( (LastPipeStatus == NDR_PIPE_ACTIVE_IN) && (pPipeMsg->PipeFlags & NDR_LAST_IN_PIPE) ) { ResetToDispatchBuffer( pPipeDesc, pPipeMsg->pStubMsg ); if (pPipeDesc->OutPipes == 0) return; // Change direction after the last in pipe. // The search will be from the beginning. Mask = NDR_OUT_PIPE; } else { // Same direction, start the search with the next pipe. NextPipe = CurrentPipe + 1; Mask = (LastPipeStatus == NDR_PIPE_ACTIVE_IN) ? NDR_IN_PIPE : NDR_OUT_PIPE; } } // First fit. We are here only when there is another pipe to service. while( NextPipe < pPipeDesc->TotalPipes ) { if ( pPipeDesc->pPipeMsg[NextPipe].PipeFlags & Mask ) { pPipeDesc->CurrentPipe = (short) NextPipe; if ( Mask == NDR_IN_PIPE ) { pPipeDesc->pPipeMsg[NextPipe].PipeStatus = NDR_PIPE_ACTIVE_IN; } else { pPipeDesc->pPipeMsg[NextPipe].PipeStatus = NDR_PIPE_ACTIVE_OUT; } pPipeDesc->pPipeHelper->InitPipeStateWithType( &pPipeDesc->pPipeMsg[NextPipe] ); pPipeDesc->RuntimeState.EndOfPipe = 0; break; } NextPipe++; } // If it is the first out pipe on server, get the buffer. PMIDL_STUB_MESSAGE pStubMsg = pPipeDesc->pPipeMsg[NextPipe].pStubMsg; if ( ! pStubMsg->IsClient && Mask == NDR_OUT_PIPE && ! pPipeDesc->Flags.AuxOutBufferAllocated ) { NdrGetPipeBuffer( pStubMsg, PIPE_PARTIAL_BUFFER_SIZE, pStubMsg->SavedHandle ); pPipeDesc->Flags.AuxOutBufferAllocated = 1; } } void NdrpVerifyPipe( char * pState ) /* This routine verifies the context for server application calling pull or push emtries of the engine. */ { NDR_PIPE_MESSAGE * pPipeMsg = (NDR_PIPE_MESSAGE *) pState; if ( ! pPipeMsg || ! pPipeMsg->pStubMsg || pPipeMsg->Signature != NDR_PIPE_SIGNATURE ) RpcRaiseException( RPC_X_INVALID_PIPE_OBJECT ); NDR_PIPE_DESC * pPipeDesc = (NDR_PIPE_DESC *) pPipeMsg->pStubMsg->pContext->pPipeDesc; if ( ! pPipeDesc ) RpcRaiseException( RPC_X_INVALID_PIPE_OBJECT ); // An exception occured on the pipe call, but the app // for some unknown reason is trying to call Rpc again. // Just rethrow the exception that the app received the first time. if ( 0 != pPipeDesc->PipeException ) RpcRaiseException( pPipeDesc->PipeException ); // See if a different pipe is currently active. if ( pPipeDesc->CurrentPipe != -1 && & pPipeDesc->pPipeMsg[ pPipeDesc->CurrentPipe ] != pPipeMsg ) NdrpRaisePipeException( pPipeDesc, RPC_X_WRONG_PIPE_ORDER ); } void RPC_ENTRY NdrIsAppDoneWithPipes( NDR_PIPE_DESC * pPipeDesc ) /* This routine is called from the engine after the manager code returned to the engine to see if all the pipes have been processed. It is also called from NdrCompleteAsyncClientCall. It is not called from the synchronous client as it is the stub that manages the pipe processing for synchronous case. */ { // It is possible for the server in sync rpc and both side in async rpc // to receive an error, ignore it and continue. // To prevent this, rethrow the exception if an exception was hit before. if ( pPipeDesc->PipeException ) RpcRaiseException( pPipeDesc->PipeException ); if ( pPipeDesc->CurrentPipe != -1 ) NdrpRaisePipeException( pPipeDesc, RPC_X_PIPE_DISCIPLINE_ERROR ); for (int i = 0; i < pPipeDesc->TotalPipes; i++ ) if ( pPipeDesc->pPipeMsg[i].PipeStatus != NDR_PIPE_DRAINED ) NdrpRaisePipeException( pPipeDesc, RPC_X_PIPE_DISCIPLINE_ERROR ); } void RPC_ENTRY NdrPipePull( char * pState, void * buf, unsigned long esize, unsigned long * ecount ) /*+ Server side [in] pipe arguments. -*/ { NDR_PIPE_LOG( PIPE_LOG_API, ( "NdrPipePull: pState %p", pState ) ); NDR_PIPE_LOG( PIPE_LOG_API, ( " buf %p, esize %u", buf, esize ) ); NDR_PIPE_LOG( PIPE_LOG_API, ( " ecount %p", ecount ) ); NdrpVerifyPipe( pState ); NDR_PIPE_MESSAGE * pPipeMsg = (NDR_PIPE_MESSAGE *) pState; PMIDL_STUB_MESSAGE pStubMsg = pPipeMsg->pStubMsg; NDR_PIPE_DESC * pPipeDesc = (NDR_PIPE_DESC *) pStubMsg->pContext->pPipeDesc; if ( pPipeDesc->CurrentPipe == -1 && & pPipeDesc->pPipeMsg[ pPipeDesc->PrevPipe ] == pPipeMsg ) { // An attempt to read from the pipe that was the last one used. NdrpRaisePipeException( pPipeDesc, RPC_X_PIPE_EMPTY ); } // Pulling in pipe on async raw client, out pipe on any server if ( pPipeMsg->PipeStatus == NDR_PIPE_ACTIVE_IN && pStubMsg->IsClient || pPipeMsg->PipeStatus == NDR_PIPE_ACTIVE_OUT && !pStubMsg->IsClient ) NdrpRaisePipeException( pPipeDesc, RPC_X_WRONG_PIPE_ORDER ); if ( esize == 0 ) NdrpRaisePipeException( pPipeDesc, RPC_S_INVALID_ARG ); long ElemCount = (long) esize; *ecount = 0; if ( pPipeDesc->RuntimeState.EndOfPipe ) { NdrMarkNextActivePipe( pPipeDesc ); return; } uchar * pMemory = (uchar*) buf; BOOL EndOfPipe; EndOfPipe = NdrReadPipeElements( pPipeDesc, pStubMsg, pMemory, & ElemCount ); NDR_ASSERT( ElemCount <= (long)esize, "read more than asked for" ); *ecount = ElemCount; if ( EndOfPipe && ElemCount == 0 ) NdrMarkNextActivePipe( pPipeDesc ); } void RPC_ENTRY NdrPipePush( char * pState, void * buf, unsigned long ecount ) /*+ Server side [out] pipe arguments. -*/ { NDR_PIPE_LOG( PIPE_LOG_API, ( "NdrPipePush: pState %p", pState ) ); NDR_PIPE_LOG( PIPE_LOG_API, ( " Buf %p, ecount %u", buf, ecount ) ); NdrpVerifyPipe( pState ); NDR_PIPE_MESSAGE * pPipeMsg = (NDR_PIPE_MESSAGE *) pState; PMIDL_STUB_MESSAGE pStubMsg = pPipeMsg->pStubMsg; NDR_PIPE_DESC * pPipeDesc = (NDR_PIPE_DESC *) pStubMsg->pContext->pPipeDesc; if ( pPipeDesc->CurrentPipe == -1 && & pPipeDesc->pPipeMsg[ pPipeDesc->PrevPipe ] == pPipeMsg ) { // An attempt to write the pipe that was the last one used. NdrpRaisePipeException( pPipeDesc, RPC_X_PIPE_CLOSED ); } // Pushing out pipe on async raw client, in pipe on any server if ( pPipeMsg->PipeStatus == NDR_PIPE_ACTIVE_OUT && pStubMsg->IsClient || pPipeMsg->PipeStatus == NDR_PIPE_ACTIVE_IN && !pStubMsg->IsClient ) NdrpRaisePipeException( pPipeDesc, RPC_X_WRONG_PIPE_ORDER ); ulong ElemAlign = pPipeDesc->RuntimeState.ElemAlign; ulong ElemMemSize = pPipeDesc->RuntimeState.ElemMemSize; ulong ElemWireSize = pPipeDesc->RuntimeState.ElemWireSize; ulong ElemPad = pPipeDesc->RuntimeState.ElemPad; BOOL fBlockCopy = pPipeDesc->RuntimeState.fBlockCopy; // Size the chunk and get the marshaling buffer // pStubMsg->BufferLength = pPipeDesc->LastPartialSize; pPipeDesc->pPipeHelper->BufferSizeChunkCounter( pPipeMsg ); ulong CopySize = ( ecount - 1) * (ElemWireSize + ElemPad) + ElemWireSize; uchar * pMemory = (uchar*) buf; if ( ecount ) { LENGTH_ALIGN( pStubMsg->BufferLength, ElemAlign ); if ( fBlockCopy ) pStubMsg->BufferLength += CopySize; else { NdrpPipeElementBufferSize( pPipeDesc, pStubMsg, pMemory, ecount ); } } pPipeDesc->pPipeHelper->BufferSizeChunkTailCounter( pPipeMsg ); // Get the new buffer, put the frame leftover in it. NdrGetPartialBuffer( pStubMsg ); // Marshal the chunk pPipeDesc->pPipeHelper->MarshallChunkCounter( pPipeMsg, ecount ); if ( ecount ) { ALIGN( pStubMsg->Buffer, ElemAlign ); if ( fBlockCopy ) { RpcpMemoryCopy( pStubMsg->Buffer, pMemory, CopySize ); pStubMsg->Buffer += CopySize; } else { // Again: only complex is possible pPipeDesc->pPipeHelper->MarshallType( pPipeMsg, pMemory, ecount ); pMemory += ElemMemSize * ecount; } } pPipeDesc->pPipeHelper->MarshallChunkTailCounter( pPipeMsg, ecount ); // If it is not the last terminator, use a partial send. // On client (async only) the last send should be complete, // On server (sync or async) the complete send will happen after marshaling // non-pipe out data. // Channel requires the last call on server. if ( pStubMsg->IsClient ) { if ( ecount == 0 && (pPipeMsg->PipeFlags & NDR_LAST_IN_PIPE)) NdrCompleteSend( pPipeDesc, pStubMsg ); else NdrPartialSend( pPipeDesc, pStubMsg ); } else NdrPartialSend( pPipeDesc, pStubMsg ); if ( ecount == 0 ) NdrMarkNextActivePipe( pPipeDesc ); } void RPC_ENTRY NdrPipeAlloc( char * pState, unsigned long bsize, void **buf, unsigned long * bcount ) /* This method has been introduced to support pipe chaining - when a server becomes a client and passes a pipe argument along. Only one buffer is ever there: next call releases the previous one. */ { NDR_PIPE_LOG( PIPE_LOG_API, ( "NdrPipeAlloc: pState %p", pState ) ); NDR_PIPE_LOG( PIPE_LOG_API, ( " bsize %d, buf %p", bsize, buf ) ); NDR_PIPE_LOG( PIPE_LOG_API, ( " bcount %p", bcount ) ); NdrpVerifyPipe( pState ); NDR_PIPE_MESSAGE * pPipeMsg = (NDR_PIPE_MESSAGE *) pState; PMIDL_STUB_MESSAGE pStubMsg = pPipeMsg->pStubMsg; NDR_PIPE_DESC * pPipeDesc = (NDR_PIPE_DESC *) pStubMsg->pContext->pPipeDesc; if ( pPipeDesc->ChainingBuffer ) { if ( pPipeDesc->ChainingBufferSize >= bsize ) { *bcount = pPipeDesc->ChainingBufferSize; *buf = pPipeDesc->ChainingBuffer; return; } else { NdrpPrivateFree( pPipeDesc->pAllocContext, pPipeDesc->ChainingBuffer ); pPipeDesc->ChainingBuffer = NULL; pPipeDesc->ChainingBufferSize = 0; } } RpcTryExcept { pPipeDesc->ChainingBuffer = NdrpPrivateAllocate( pPipeDesc->pAllocContext, bsize ); } RpcExcept( RpcExceptionCode() == RPC_S_OUT_OF_MEMORY ) { NdrpRaisePipeException( pPipeDesc, RPC_S_OUT_OF_MEMORY ); } RpcEndExcept *bcount = bsize; *buf = pPipeDesc->ChainingBuffer; } void NdrpAsyncHandlePipeError( char * pState, RPC_STATUS Status ) /*++ Routine Description : Forces the connect to close by either freeing the buffer or aborting the call on an async pipe error. Arguments : pState - Supplies the pipe state. Statue - Supplies the error code. Return : None. --*/ { NDR_PIPE_MESSAGE *pPipeMsg; MIDL_STUB_MESSAGE *pStubMsg; PNDR_ASYNC_MESSAGE pAsyncMsg; // Pending isn't really an error. if ( RPC_S_ASYNC_CALL_PENDING == Status ) return; if ( !pState ) { return; } pPipeMsg = (NDR_PIPE_MESSAGE *)pState; if ( ! pPipeMsg->pStubMsg || pPipeMsg->Signature != NDR_PIPE_SIGNATURE ) return; pStubMsg = pPipeMsg->pStubMsg; if ( !pStubMsg->pAsyncMsg ) { return; } pAsyncMsg = pStubMsg->pAsyncMsg; RpcTryExcept { if ( ! pAsyncMsg->Flags.RuntimeCleanedUp ) { if ( pStubMsg->IsClient ) { NdrFreeBuffer( pStubMsg ); } else { I_RpcAsyncAbortCall( pAsyncMsg->AsyncHandle, Status); } } } RpcExcept(! (RPC_BAD_STUB_DATA_EXCEPTION_FILTER) ) { // Ignore and exceptions that occured } RpcEndExcept pAsyncMsg->Flags.RuntimeCleanedUp = 1; } RPC_STATUS RPC_ENTRY NdrAsyncPipePull( char * pState, void * buf, unsigned long esize, unsigned long * ecount ) { RPC_STATUS Status = RPC_S_OK; NDR_PIPE_LOG( PIPE_LOG_API, ( "NdrAsyncPipePull: pState %p", pState ) ); NDR_PIPE_LOG( PIPE_LOG_API, ( " buf %p, esize %u", buf, esize ) ); NDR_PIPE_LOG( PIPE_LOG_API, ( " ecount %u", ecount ) ); RpcTryExcept { NdrPipePull( pState, buf, esize, ecount ); } RpcExcept( ! (RPC_BAD_STUB_DATA_EXCEPTION_FILTER) ) { Status = RpcExceptionCode(); NdrpAsyncHandlePipeError( pState, Status ); } RpcEndExcept return Status; } RPC_STATUS RPC_ENTRY NdrAsyncPipePush( char * pState, void * buf, unsigned long ecount ) { RPC_STATUS Status = RPC_S_OK; NDR_PIPE_LOG( PIPE_LOG_API, ( "NdrAsyncPipePush: pState %p", pState ) ); NDR_PIPE_LOG( PIPE_LOG_API, ( " buf %p, count %u", buf, ecount ) ); RpcTryExcept { NdrPipePush( pState, buf, ecount ); } RpcExcept( ! (RPC_BAD_STUB_DATA_EXCEPTION_FILTER) ) { Status = RpcExceptionCode(); NdrpAsyncHandlePipeError( pState, Status ); } RpcEndExcept return Status; } RPC_STATUS RPC_ENTRY NdrAsyncPipeAlloc( char * pState, unsigned long bsize, void **buf, unsigned long * bcount ) /* */ { RPC_STATUS Status = RPC_S_OK; NDR_PIPE_LOG( PIPE_LOG_API, ( "NdrAsyncPipeAlloc: pState %p", pState ) ); NDR_PIPE_LOG( PIPE_LOG_API, ( " bsize %u, buf %p", bsize, buf ) ); NDR_PIPE_LOG( PIPE_LOG_API, ( " bcount %p", bcount ) ); RpcTryExcept { NdrPipeAlloc( pState, bsize, buf, bcount ); } RpcExcept( RpcExceptionCode() == RPC_S_OUT_OF_MEMORY ) { Status = RpcExceptionCode(); NdrpAsyncHandlePipeError( pState, Status ); } RpcEndExcept return Status; } void NdrpPipeElementBufferSize( NDR_PIPE_DESC * pPipeDesc, PMIDL_STUB_MESSAGE pStubMsg, uchar * pMemory, ulong ElemCount ) /*++ Routine Description : Computes the needed buffer size for the pipe elements. Arguments : pStubMsg - Pointer to the stub message. pMemory - Pointer to the data being sized. pFormat - Pointer's format string description. Return : None. --*/ { if ( ElemCount == 0 ) return; // We can end up here only for complex objects. // For objects without unions, we may be forced to size because // of different packing levels. if ( pPipeDesc->RuntimeState.ElemWireSize ) { // There is a fixed WireSize, so use it. ulong WireSize = pPipeDesc->RuntimeState.ElemWireSize; ulong WireAlign = pPipeDesc->RuntimeState.ElemAlign; pStubMsg->BufferLength += (ElemCount -1) * (WireSize + WIRE_PAD( WireSize, WireAlign )) + WireSize; } else { NDR_PIPE_MESSAGE * pPipeMsg = & pPipeDesc->pPipeMsg[ pPipeDesc->CurrentPipe ]; pPipeDesc->pPipeHelper->BufferSizeType( pPipeMsg, pMemory, ElemCount ); } } void NdrpPipeElementConvertAndUnmarshal( NDR_PIPE_DESC * pPipeDesc, PMIDL_STUB_MESSAGE pStubMsg, uchar * * ppMemory, long ActElems, long * pActCount ) /*++ Routine Description : Computes the needed buffer size for the pipe elements. Arguments : pStubMsg - Pointer to the stub message. pMemory - Pointer to the data being sized. pFormat - Pointer's format string description. Return : None. --*/ { NDR_PIPE_STATE * state = & pPipeDesc->RuntimeState; NDR_PIPE_MESSAGE * pPipeMsg = & pPipeDesc->pPipeMsg[ pPipeDesc->CurrentPipe ]; uchar * pMemory = *ppMemory; ulong ElemWireSize = state->ElemWireSize; ulong ElemPad = state->ElemPad; BOOL fBlockCopy = state->fBlockCopy; NDR_PIPE_LOG( PIPE_LOG_NOISE, ( " NdrpPipeElementConvertAndUnmarshal: ActElems %d", ActElems ) ); NDR_PIPE_LOG( PIPE_LOG_NOISE, (" pStubMsg->Buffer %p, pMemory %p", pStubMsg->Buffer, pMemory ) ); if ( ActElems ) { pPipeDesc->pPipeHelper->ConvertType( pPipeMsg, ActElems ); // Unmarshal the chunk ALIGN( pStubMsg->Buffer, state->ElemAlign ); if ( fBlockCopy ) { ulong CopySize = ( ActElems - 1) * (ElemWireSize + ElemPad) + ElemWireSize; RpcpMemoryCopy( pMemory, pStubMsg->Buffer, CopySize ); pStubMsg->Buffer += CopySize; } else { // Only complex and enum is possible here. pPipeDesc->pPipeHelper->UnmarshallType( pPipeMsg, pMemory, ActElems ); pMemory += state->ElemMemSize; } *ppMemory += state->ElemMemSize * ActElems; } *pActCount += ActElems; } BOOL NdrReadPipeElements( NDR_PIPE_DESC * pPipeDesc, PMIDL_STUB_MESSAGE pStubMsg, unsigned char * pTargetBuffer, long * pElementsRead ) /* This procedure encapsulates reading pipe elements from the RPC runtime. */ { NDR_PIPE_STATE * pRuntimeState = & pPipeDesc->RuntimeState; ulong ElemWireSize = pRuntimeState->ElemWireSize; // Get the temporary buffers if ( ( pRuntimeState->PartialBufferSize / ElemWireSize ) == 0 ) { // buffer too small. // We preallocate a buffer that is of an arbitrary big size. // If the element is even bigger, we allocate a buffer big // enough for one element. if ( pRuntimeState->PartialElem ) NdrpPrivateFree( pPipeDesc->pAllocContext, pRuntimeState->PartialElem ); if ( PIPE_PARTIAL_BUFFER_SIZE < ElemWireSize ) pRuntimeState->PartialBufferSize = ElemWireSize; else pRuntimeState->PartialBufferSize = PIPE_PARTIAL_BUFFER_SIZE; // We allocate more for alignment padding. RpcTryExcept { pRuntimeState->PartialElem = (uchar *) NdrpPrivateAllocate( pPipeDesc->pAllocContext, pRuntimeState->PartialBufferSize + 8); } RpcExcept(! (RPC_BAD_STUB_DATA_EXCEPTION_FILTER) ) { if ( ! pRuntimeState->PartialElem ) NdrpRaisePipeException( pPipeDesc, RPC_S_OUT_OF_MEMORY ); } RpcEndExcept } long ElemsToRead = *pElementsRead; long LeftToRead = *pElementsRead; long ElemsRead = 0; *pElementsRead = 0; // New semantics - unmarshal only what we have at hand, don't call // for the next buffer unless it would mean giving back 0 elems. while ( ( LeftToRead > 0 && ! pPipeDesc->RuntimeState.EndOfPipe ) || pPipeDesc->RuntimeState.EndOfPipePending ) { // See if there is a buffer to process first if ( ! pPipeDesc->Flags.NoBufferCallPending ) { // Read elements from the marshaling buffer (the StubMsg->Buffer) // to the user's target buffer (converted and unmarshaled). // ElemsRead would be cumulative across the calls when looping. NdrpReadPipeElementsFromBuffer( pPipeDesc, pStubMsg, & pTargetBuffer, LeftToRead, & ElemsRead ); } LeftToRead = ElemsToRead - ElemsRead; if ( ( LeftToRead > 0 && ! pPipeDesc->RuntimeState.EndOfPipe ) || pPipeDesc->RuntimeState.EndOfPipePending ) { // We ran out of data in the current buffer. // Check if we unmarshaled some elems already - if so, don't read. if ( ElemsRead == 0 ) { pPipeDesc->Flags.NoBufferCallPending = 1; NdrPartialReceive( pPipeDesc, pStubMsg, pRuntimeState->PartialBufferSize ); pPipeDesc->Flags.NoBufferCallPending = 0; continue; } } break; } *pElementsRead = ElemsRead; return pPipeDesc->RuntimeState.EndOfPipe; } void NdrpReadPipeElementsFromBuffer ( NDR_PIPE_DESC * pPipeDesc, PMIDL_STUB_MESSAGE pStubMsg, uchar ** pTargetBuffer, long ElemsToRead, long * NumCopied ) { NDR_PIPE_STATE * state = & pPipeDesc->RuntimeState; NDR_PIPE_MESSAGE * pPipeMsg = & pPipeDesc->pPipeMsg[ pPipeDesc->CurrentPipe ]; BOOL fHasChunkTailCounter = pPipeDesc->pPipeHelper->HasChunkTailCounter(); long len; uchar * BufferSave; NDR_ASSERT( state->CurrentState == START || state->PartialElemSize < state->ElemWireSize, "when starting to read pipe elements" ); NDR_PIPE_LOG( PIPE_LOG_NOISE, ( "NdrpReadPipeElementsFromBuffer: ElemsToRead %x", ElemsToRead ) ); NDR_PIPE_LOG( PIPE_LOG_NOISE, ( " pStubMsg->Buffer %p", pStubMsg->Buffer ) ); if ( ( ElemsToRead == 0 ) && ( !state->EndOfPipePending ) ) { return ; } while (1) { switch( state->CurrentState ) { case START: NDR_PIPE_LOG( PIPE_LOG_NOISE, ( " START: pStubMgs->Buffer %p", pStubMsg->Buffer ) ); ASSERT(pStubMsg->Buffer >= pStubMsg->RpcMsg->Buffer); ASSERT(pStubMsg->Buffer - pStubMsg->RpcMsg->BufferLength <= pStubMsg->RpcMsg->Buffer); // The state to read the chunk counter. state->PartialElemSize = 0 ; // Read the element count. { ulong ElemsInChunk; if ( !pPipeDesc->pPipeHelper->UnmarshallChunkCounter( pPipeMsg, &ElemsInChunk ) ) return; state->ElemsInChunk = state->OrigElemsInChunk = ElemsInChunk; } NDR_PIPE_LOG( PIPE_LOG_NOISE, ("Read pipe chuck: %d", state->ElemsInChunk ) ); if (state->ElemsInChunk == 0) { if ( fHasChunkTailCounter ) { state->EndOfPipePending = 1; state->CurrentState = VERIFY_TAIL_CHUNK; NDR_PIPE_LOG( PIPE_LOG_NOISE, ("Waiting for duplicate 0...") ); } else { state->EndOfPipe = 1; return; } } else { if ( state->LowChunkLimit > state->ElemsInChunk || state->HighChunkLimit < state->ElemsInChunk ) NdrpRaisePipeException( pPipeDesc, RPC_X_INVALID_BOUND ); state->CurrentState = COPY_PIPE_ELEM; } break; case COPY_PIPE_ELEM: NDR_PIPE_LOG( PIPE_LOG_NOISE, (" COPY_PIPE_ELEM: state->ElemsInChunk %d", state->ElemsInChunk ) ); NDR_PIPE_LOG( PIPE_LOG_NOISE, (" pStubMsg->Buffer %p, ElemsToRead %d", pStubMsg->Buffer, ElemsToRead ) ); // The state with some elements in the current chunk left. // The elements may not be in the current buffer, though. NDR_ASSERT( state->ElemsInChunk != 0xbaadf00d, "bogus chunk count" ); NDR_ASSERT( state->ElemsInChunk, "empty chunk!" ); ALIGN( pStubMsg->Buffer, state->ElemAlign ); if ( state->ElemWireSize <= REMAINING_BYTES() ) { // There is enough on wire to unmarshal at least one. if ( ElemsToRead ) { long ElemsReady, ActCount, EffectiveSize, WirePad; WirePad = WIRE_PAD( state->ElemWireSize, state->ElemAlign ); EffectiveSize = state->ElemWireSize + WirePad; ElemsReady = (REMAINING_BYTES() + WirePad) / EffectiveSize; if ( ElemsReady > state->ElemsInChunk ) ElemsReady = state->ElemsInChunk; if ( ElemsReady > ElemsToRead ) ElemsReady = ElemsToRead; ActCount = 0; NdrpPipeElementConvertAndUnmarshal( pPipeDesc, pStubMsg, pTargetBuffer, ElemsReady, & ActCount ); ElemsToRead -= ActCount; state->ElemsInChunk -= ActCount; *NumCopied += ActCount; if (state->ElemsInChunk == 0) { state->CurrentState = fHasChunkTailCounter ? VERIFY_TAIL_CHUNK : START; if ( ElemsToRead == 0 ) return; } } else { // End of target buffer: return the count. // Keep the same state for the next round. return; } } else { // Not enough wire bytes to unmarshal element. if ( REMAINING_BYTES() ) { NDR_ASSERT( 0 < REMAINING_BYTES(), "buffer pointer not within the buffer" ); state->CurrentState = RETURN_PARTIAL_ELEM; } else { state->PartialElemSize = 0; return; } } break; case RETURN_PARTIAL_ELEM: NDR_PIPE_LOG( PIPE_LOG_NOISE, (" RETURN_PARTIAL_ELEM: state->ElemsInChunk %d", state->ElemsInChunk ) ); // This happens when there is no whole element left // during copying. The chunk has some elements. NDR_ASSERT( state->ElemsInChunk, "empty chunk" ); len = REMAINING_BYTES(); NDR_ASSERT( 0 < len && len < state->ElemWireSize, "element remnant expected" ); // Save the remnants of the elem in PartialElem; // Pay attention to the alignment of the remnant, though. state->PartialOffset = 0; state->PartialElemSize = 0; if ( len ) { // we need to simulate the original alignment by // means of an offset in the PartialElem buffer. state->PartialOffset = 0x7 & PtrToUlong( pStubMsg->Buffer ); RpcpMemoryCopy( state->PartialElem + state->PartialOffset, pStubMsg->Buffer, len ); pStubMsg->Buffer += len; state->PartialElemSize = len; } state->CurrentState = READ_PARTIAL_ELEM ; return; case READ_PARTIAL_ELEM: //also a start state NDR_PIPE_LOG( PIPE_LOG_NOISE, (" READ_PARTIAL_ELEM: state->ElemsInChunk %d", state->ElemsInChunk ) ); NDR_ASSERT( state->PartialElemSize > 0 && state->PartialElemSize < state->ElemWireSize, "element remnant expected" ); NDR_ASSERT( ElemsToRead, "no elements to read" ); len = state->ElemWireSize - state->PartialElemSize; if ( len > REMAINING_BYTES() ) { // Add another piece to the partial element, // then wait for another round in the same state. RpcpMemoryCopy( state->PartialElem + state->PartialOffset + state->PartialElemSize, pStubMsg->Buffer, REMAINING_BYTES() ); pStubMsg->Buffer += REMAINING_BYTES(); state->PartialElemSize += REMAINING_BYTES(); return; } // Assemble a complete partial element, unmarshal it, // then switch to the regular element copying. RpcpMemoryCopy( state->PartialElem + state->PartialOffset + state->PartialElemSize, pStubMsg->Buffer, len ); pStubMsg->Buffer += len; state->PartialElemSize += len; // Assemble a fake STUB_MESSAGE and RPC_MESSAGE so that // the buffer looks likes the a regular RPC buffer. { // Save modified fields. void * RpcBufferSave = pStubMsg->RpcMsg->Buffer; unsigned int RpcBufferLength = pStubMsg->RpcMsg->BufferLength; unsigned char * BufferSave = pStubMsg->Buffer; unsigned char * BufferStartSave = pStubMsg->BufferStart; unsigned char * BufferEndSave = pStubMsg->BufferEnd; RpcTryFinally { pStubMsg->RpcMsg->Buffer = state->PartialElem + state->PartialOffset; pStubMsg->RpcMsg->BufferLength = state->PartialElemSize; pStubMsg->Buffer = (unsigned char *)pStubMsg->RpcMsg->Buffer; pStubMsg->BufferStart = pStubMsg->Buffer; pStubMsg->BufferEnd = pStubMsg->Buffer + pStubMsg->RpcMsg->BufferLength; len = 0; NdrpPipeElementConvertAndUnmarshal( pPipeDesc, pStubMsg, pTargetBuffer, 1, & len ); NDR_ASSERT( len == 1, "partial element count" ); ElemsToRead -= 1; state->ElemsInChunk -= 1; *NumCopied += 1 ; // reset partial element state. state->PartialOffset = 0; state->PartialElemSize = 0; } RpcFinally { // Switch back to regular elem unmarshaling. pStubMsg->RpcMsg->Buffer = RpcBufferSave; pStubMsg->RpcMsg->BufferLength = RpcBufferLength; pStubMsg->Buffer = BufferSave; pStubMsg->BufferStart = BufferStartSave; pStubMsg->BufferEnd = BufferEndSave; } RpcEndFinally } if ( state->ElemsInChunk == 0 ) { state->CurrentState = fHasChunkTailCounter ? VERIFY_TAIL_CHUNK : START; if ( ElemsToRead == 0 ) return; } else state->CurrentState = COPY_PIPE_ELEM; break; case VERIFY_TAIL_CHUNK: NDR_PIPE_LOG( PIPE_LOG_NOISE, (" VERIFY_TAIL_CHUNK: state->OrigElemsInChunk %d", state->OrigElemsInChunk ) ); NDR_PIPE_LOG( PIPE_LOG_NOISE, (" pStubMsg->Buffer %p", pStubMsg->Buffer ) ); ASSERT(pStubMsg->Buffer >= pStubMsg->RpcMsg->Buffer); ASSERT(pStubMsg->Buffer - pStubMsg->RpcMsg->BufferLength <= pStubMsg->RpcMsg->Buffer); // The state to verify the tail chunk counter. state->PartialElemSize = 0 ; if (! pPipeDesc->pPipeHelper->VerifyChunkTailCounter( pPipeMsg, state->OrigElemsInChunk ) ) { NDR_PIPE_LOG( PIPE_LOG_NOISE, ( "Not enough buffer for tail chunk counter..Leaving state machine.")) return; } // Get read for the next chunk. state->CurrentState = START; if ( state->EndOfPipePending ) { state->EndOfPipePending = 0; state->EndOfPipe = 1; return; } break; default: NDR_ASSERT(0, "unknown state") ; break; } } } void NdrpRaisePipeException( NDR_PIPE_DESC * pPipeDesc, RPC_STATUS Exception ) { // Remember the first exception that happened, // ignore all the subsequent exceptions by reraising the first one. if ( Exception != RPC_S_ASYNC_CALL_PENDING && pPipeDesc ) { if ( pPipeDesc->PipeException == 0 ) pPipeDesc->PipeException = Exception; RpcRaiseException( pPipeDesc->PipeException ); } else RpcRaiseException( Exception ); } // private API called from runtime to validate the current status of call // RPC_STATUS RPC_ENTRY NdrCheckAsyncPipeStatus( IN char * AsyncHandle, OUT long * pState ) { long Mask = 0; // This function can be called indirectly from COM via I_RpcSetAsyncHandle. If that // is done, StubInfo will be NULL. We will just return RPC_X_INVALID_PIPE_OBJECT. if ( ((PRPC_ASYNC_STATE)AsyncHandle)->StubInfo == NULL ) return RPC_X_INVALID_PIPE_OBJECT; // There is no need to validate the async handle since // this API can only be called from the RPC runtime or COM. PNDR_ASYNC_MESSAGE pAsyncMsg = (PNDR_ASYNC_MESSAGE) ((PRPC_ASYNC_STATE)AsyncHandle)->StubInfo; NDR_PROC_CONTEXT * pContext = & pAsyncMsg->ProcContext; if ( !pContext->HasPipe ) return RPC_X_INVALID_PIPE_OBJECT; NDR_PIPE_DESC * pPipeDesc = pContext->pPipeDesc; if ( ! pPipeDesc ) return RPC_X_INVALID_PIPE_OBJECT; if ( pPipeDesc->CurrentPipe == -1 ) { // This is the first pipe to be processed. if ( pPipeDesc->PrevPipe == -1 ) { Mask = pPipeDesc->InPipes? NDR_PIPE_ACTIVE_IN: NDR_PIPE_ACTIVE_OUT; } else // we are done with the last pipe return RPC_X_INVALID_PIPE_OBJECT; } // after the first pipe, pipemsg are initialized. NDR_PIPE_MESSAGE * pPipeMsg = &pPipeDesc->pPipeMsg[ pPipeDesc->CurrentPipe]; Mask = pPipeMsg->PipeStatus; if ( Mask == NDR_PIPE_DRAINED ) return RPC_X_INVALID_PIPE_OBJECT; *pState = (long) Mask; return RPC_S_OK; }