windows-nt/Source/XPSP1/NT/com/rpc/ndr20/pipes.cxx
2020-09-26 16:20:57 +08:00

2683 lines
82 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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 <stdarg.h>
#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 ( 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;
}
if ( pStubMsg->pAsyncMsg )
pStubMsg->pAsyncMsg->Flags.RuntimeCleanedUp = 1;
NdrpRaisePipeException( pPipeDesc, Status );
}
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;
}
}
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 ( 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(1)
{
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(1)
{
// 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( ! (RPC_BAD_STUB_DATA_EXCEPTION_FILTER) )
{
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(1)
{
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 );
}