|
|
/*++
Copyright (C) Microsoft Corporation, 1996 - 1999
Module Name:
CoTrans.cxx
Abstract:
Common connection-oriented helper functions
Author:
Mario Goertzel [MarioGo]
Revision History:
MarioGo 11/11/1996 Async RPC
--*/
#include <precomp.hxx>
#include <trans.hxx>
#include <cotrans.hxx>
RPC_STATUS RPC_ENTRY CO_Send( RPC_TRANSPORT_CONNECTION ThisConnection, UINT Length, BUFFER Buffer, PVOID SendContext ) /*++
Routine Description:
Submits a send of the buffer on the connection. Will complete with ConnectionServerSend or ConnectionClientSend event either when the data has been sent on the network or when the send fails.
Arguments:
ThisConnection - The connection to send the data on. Length - The length of the data to send. Buffer - The data to send. SendContext - A buffer to use as the CO_SEND_CONTEXT for this operation.
Return Value:
RPC_S_OK
RPC_P_SEND_FAILED - Connection aborted
--*/ { PCONNECTION pConnection = (PCONNECTION)ThisConnection; CO_SEND_CONTEXT *pSend = (CO_SEND_CONTEXT *)SendContext; BOOL b; DWORD ignored; RPC_STATUS status;
pConnection->StartingWriteIO();
if (pConnection->fAborted) { pConnection->WriteIOFinished(); return(RPC_P_SEND_FAILED); }
pSend->maxWriteBuffer = Length; pSend->pWriteBuffer = Buffer; pSend->Write.pAsyncObject = pConnection; pSend->Write.ol.hEvent = 0; pSend->Write.ol.Offset = 0; pSend->Write.ol.OffsetHigh = 0; pSend->Write.thread = I_RpcTransProtectThread();
#ifdef _INTERNAL_RPC_BUILD_
if (gpfnFilter) { (*gpfnFilter) (Buffer, Length, 0); } #endif
status = pConnection->Send( pConnection->Conn.Handle, Buffer, Length, &ignored, &pSend->Write.ol );
pConnection->WriteIOFinished();
if ( (status != RPC_S_OK) && (status != ERROR_IO_PENDING) ) { RpcpErrorAddRecord(EEInfoGCIO, status, EEInfoDLCOSend10, (ULONGLONG)pConnection, (ULONGLONG)Buffer, Length);
VALIDATE(status) { ERROR_NETNAME_DELETED, ERROR_BROKEN_PIPE, ERROR_GRACEFUL_DISCONNECT, ERROR_NO_DATA, ERROR_NO_SYSTEM_RESOURCES, ERROR_WORKING_SET_QUOTA, ERROR_BAD_COMMAND, ERROR_OPERATION_ABORTED, ERROR_WORKING_SET_QUOTA, ERROR_PIPE_NOT_CONNECTED, WSAECONNABORTED, WSAECONNRESET, WSAENOBUFS, WSASYSCALLFAILURE } END_VALIDATE;
I_RpcTransUnprotectThread(pSend->Write.thread);
pConnection->Abort();
return(RPC_P_SEND_FAILED); }
return(RPC_S_OK); }
RPC_STATUS RPC_ENTRY CO_SubmitRead( PCONNECTION pConnection ) /*++
Routine Description:
Generic routine to submit an async read on an existing connection.
Arguments:
pConnection - The connection to submit the read on. pConnection->pReadBuffer - valid buffer to receive into or null. pConnection->maxReadBuffer - size of pReadBuffer or null. pConnection->iLastRead is an offset into pReadBuffer of data already read.
Return Value:
RPC_S_OK - Read pending
RPC_P_RECEIVE_FAILED - Connection aborted
--*/ { BOOL b; DWORD ignored; RPC_STATUS status;
if (pConnection->pReadBuffer == 0) { ASSERT(pConnection->iLastRead == 0);
pConnection->pReadBuffer = TransConnectionAllocatePacket(pConnection, pConnection->iPostSize); if (pConnection->pReadBuffer == 0) { pConnection->Abort(); return(RPC_P_RECEIVE_FAILED); }
pConnection->maxReadBuffer = pConnection->iPostSize; } else { ASSERT(pConnection->iLastRead < pConnection->maxReadBuffer); }
pConnection->StartingReadIO(); if (pConnection->fAborted) { pConnection->ReadIOFinished(); return(RPC_P_RECEIVE_FAILED); }
pConnection->Read.thread = I_RpcTransProtectThread(); pConnection->Read.ol.hEvent = 0;
ASSERT(pConnection->Read.ol.Internal != STATUS_PENDING);
status = pConnection->Receive( pConnection->Conn.Handle, pConnection->pReadBuffer + pConnection->iLastRead, pConnection->maxReadBuffer - pConnection->iLastRead, &ignored, &pConnection->Read.ol );
pConnection->ReadIOFinished();
if ( (status != RPC_S_OK) && (status != ERROR_IO_PENDING) && (status != ERROR_MORE_DATA) ) { if ( status != ERROR_NETNAME_DELETED && status != ERROR_BROKEN_PIPE && status != ERROR_GRACEFUL_DISCONNECT) { TransDbgPrint((DPFLTR_RPCPROXY_ID, DPFLTR_WARNING_LEVEL, RPCTRANS "UTIL_ReadFile failed %d on %p\n", status, pConnection)); }
RpcpErrorAddRecord(EEInfoGCIO, status, EEInfoDLCOSubmitRead10);
// the IO system does not necessarily reset the Internal on sync failure.
// Reset it because in HTTP when we encounted a sync failure on RTS receive
// we may submit a second receive after a failed receive and this will
// trigger the ASSERT above
pConnection->Read.ol.Internal = status;
I_RpcTransUnprotectThread(pConnection->Read.thread);
pConnection->Abort(); return(RPC_P_RECEIVE_FAILED); }
// Even if the read completed here, it will also be posted to the
// completion port. This means we don't need to handle the read here.
return(RPC_S_OK); }
RPC_STATUS RPC_ENTRY CO_Recv( RPC_TRANSPORT_CONNECTION ThisConnection ) /*++
Routine Description:
Called be the runtime on a connection without a currently pending recv.
Arguments:
ThisConnection - A connection without a read pending on it.
Return Value:
RPC_S_OK RPC_P_RECEIVE_FAILED
--*/ { PCONNECTION p = (PCONNECTION)ThisConnection;
if ( p->iLastRead && p->iLastRead == p->maxReadBuffer) { ASSERT(p->pReadBuffer);
// This means we received a coalesced read of a complete
// message. (Or that we received a coalesced read < header size)
// We should complete that as it's own IO. This is very
// rare.
TransDbgDetail((DPFLTR_RPCPROXY_ID, DPFLTR_INFO_LEVEL, RPCTRANS "Posted coalesced data in %p of %d byte\n", p, p->iLastRead));
UINT bytes;
bytes = p->iLastRead; p->iLastRead = 0; p->Read.thread = I_RpcTransProtectThread();
// This means we want to process this as a new receive
BOOL b = PostQueuedCompletionStatus(RpcCompletionPort, bytes, TRANSPORT_POSTED_KEY, &p->Read.ol);
ASSERT(b); // See complete.cxx - we can handle it here if needed.
return(RPC_S_OK); }
ASSERT(p->iLastRead == 0 || (p->iLastRead < p->maxReadBuffer));
return(CO_SubmitRead(p)); }
RPC_STATUS BASE_CONNECTION::ProcessRead(IN DWORD bytes, OUT BUFFER *pBuffer, OUT PUINT pBufferLength) /*++
Routine Description:
Receives a message from a message or byte mode protocol.
Arguments:
bytes - The number of read (not including those in iLastRead). pBuffer - When returning RPC_S_OK will contain the message. pBufferLength - When return RPC_S_OK will contain the message length.
Return Value:
RPC_S_OK - A complete message has been returned.
RPC_P_RECEIVE_FAILED - something failed.
RPC_P_PARTIAL_RECEIVE - Partial message recv'd, need to submit another recv.
--*/ { DWORD message_size; RPC_STATUS status;
bytes += iLastRead;
if (bytes < sizeof(CONN_RPC_HEADER)) { // Not a whole header, resubmit the read and continue.
iLastRead = bytes;
return(RPC_P_PARTIAL_RECEIVE); }
message_size = MessageLength((PCONN_RPC_HEADER)pReadBuffer);
if (message_size < sizeof(CONN_RPC_HEADER)) { ASSERT(message_size >= sizeof(CONN_RPC_HEADER)); Abort(); return(RPC_P_RECEIVE_FAILED); }
if (bytes == message_size) { // All set, have a complete request.
*pBuffer = pReadBuffer; *pBufferLength = message_size;
iLastRead = 0; pReadBuffer = 0; return(RPC_S_OK); } else if (message_size > bytes) { // Don't have a complete message, realloc if needed and
// resubmit a read for the remaining bytes.
if (maxReadBuffer < message_size) { // Buffer too small for the message.
status = TransConnectionReallocPacket(this, &pReadBuffer, bytes, message_size);
if (status != RPC_S_OK) { ASSERT(status == RPC_S_OUT_OF_MEMORY); Abort(); return(RPC_P_RECEIVE_FAILED); }
// increase the post size, but not if we are in direct
// buffer mode.
if (gBCacheMode == BCacheModeCached) iPostSize = message_size; }
// Setup to receive exactly the remaining bytes of the message.
iLastRead = bytes; maxReadBuffer = message_size;
return(RPC_P_PARTIAL_RECEIVE); }
// Coalesced read, save extra data. Very uncommon, impossible for
// message mode protocols.
ASSERT(bytes > message_size);
#ifdef SPX_ON
ASSERT((id == TCP) || (id == SPX) || (id == HTTP) || (id == TCP_IPv6) || (id == HTTPv2)); #else
ASSERT((id == TCP) || (id == HTTP) || (id == TCP_IPv6) || (id == HTTPv2)); #endif
TransDbgPrint((DPFLTR_RPCPROXY_ID, DPFLTR_WARNING_LEVEL, RPCTRANS "Coalesced read of %d bytes, connection %p\n", bytes - message_size, this));
// The first message and size will be returned
*pBuffer = pReadBuffer; *pBufferLength = message_size;
UINT extra = bytes - message_size; UINT alloc_size;
// Try to find a good size of the extra PDU(s)
if (extra < sizeof(CONN_RPC_HEADER)) { // Not a whole header, we'll assume iPostSize;
alloc_size = iPostSize; } else { #ifdef _M_IA64
// The first packet may not contain a number of bytes
// that align the second on an 8-byte boundary. Hence, the
// structure may end up unaligned.
alloc_size = MessageLengthUnaligned((PCONN_RPC_HEADER)(pReadBuffer + message_size)); #else
alloc_size = MessageLength((PCONN_RPC_HEADER)(pReadBuffer + message_size)); #endif
}
if (alloc_size < extra) { // This can happen if there are more than two PDUs coalesced together
// in the buffer. Or if the PDU is invalid. Or if the iPostSize is
// smaller than the next PDU.
alloc_size = extra; }
// Allocate a new buffer to save the extra data for the next read.
PBYTE pNewBuffer;
pNewBuffer = TransConnectionAllocatePacket(this, alloc_size);
if (0 == pNewBuffer) { // We have a complete request. We could process the request and
// close the connection only after trying to send the reply.
*pBuffer = 0; *pBufferLength = 0;
Abort(); return(RPC_P_RECEIVE_FAILED); }
ASSERT(*pBuffer);
// Save away extra data for the next receive
RpcpMemoryCopy(pNewBuffer, pReadBuffer + *pBufferLength, extra); pReadBuffer = pNewBuffer; iLastRead = extra; maxReadBuffer = alloc_size;
ASSERT(iLastRead <= maxReadBuffer);
ASSERT(pReadBuffer != *pBuffer);
return(RPC_S_OK); }
RPC_STATUS CO_SubmitSyncRead( IN PCONNECTION pConnection, OUT BUFFER *pBuffer, OUT PUINT pMessageLength ) /*++
Routine Description:
Called in the synchronous receive path when more data is needed in to complete the message. This function is non-blocking but it will try to read as much data as it can and may return a completed PDU.
Arguments:
pConnection - The connection to receive from. ->pReadBuffer ->maxReadBuffer ->iLastRead
Return Value:
RPC_S_OK - Ok and a complete PDU has arrived
RPC_P_IO_PENDING - A receive is now outstanding on the connection. Wait for it to complete..
RPC_P_RECEIVE_FAILED - Failure RPC_P_CONNECTION_SHUTDOWN - Failure - graceful close received.
--*/ { RPC_STATUS status;
ASSERT(pConnection->pReadBuffer);
if (pConnection->maxReadBuffer == pConnection->iLastRead) { // Coalesced receive and we've got one (or more) PDUs
status = pConnection->ProcessRead(0, pBuffer, pMessageLength);
ASSERT(status != RPC_P_PARTIAL_RECEIVE);
return(status); }
DWORD bytes; DWORD readbytes;
ASSERT_READ_EVENT_IS_THERE(pConnection);
do { BOOL b;
readbytes = pConnection->maxReadBuffer - pConnection->iLastRead;
pConnection->StartingReadIO(); if (pConnection->fAborted) { pConnection->ReadIOFinished(); return(RPC_P_RECEIVE_FAILED); }
status = pConnection->Receive(pConnection->Conn.Handle, pConnection->pReadBuffer + pConnection->iLastRead, readbytes, &bytes, &pConnection->Read.ol);
pConnection->ReadIOFinished();
if ((status == ERROR_IO_PENDING) || (status == ERROR_IO_INCOMPLETE)) { // The most common path
return(RPC_P_IO_PENDING); }
if (status != RPC_S_OK) { switch (status) { case ERROR_MORE_DATA: // Treat as success
// Note: ReadFile doesn't return the number of bytes read in this
// case even though the data is available...
// It should still be right, but this double checks it.
ASSERT(pConnection->Read.ol.InternalHigh == readbytes);
ASSERT(MessageLength((PCONN_RPC_HEADER)pConnection->pReadBuffer) > pConnection->maxReadBuffer);
bytes = readbytes;
status = RPC_S_OK; break;
case ERROR_GRACEFUL_DISCONNECT: RpcpErrorAddRecord(EEInfoGCIO, status, EEInfoDLCOSubmitSyncRead10); status = RPC_P_CONNECTION_SHUTDOWN; break;
default: RpcpErrorAddRecord(EEInfoGCIO, status, EEInfoDLCOSubmitSyncRead20); VALIDATE(status) { STATUS_TIME_DIFFERENCE_AT_DC, ERROR_NETNAME_DELETED, ERROR_BROKEN_PIPE, ERROR_PIPE_NOT_CONNECTED, ERROR_NO_SYSTEM_RESOURCES, ERROR_COMMITMENT_LIMIT, WSAECONNRESET, WSAESHUTDOWN, WSAECONNABORTED, ERROR_UNEXP_NET_ERR, ERROR_WORKING_SET_QUOTA, ERROR_NO_DATA } END_VALIDATE; status = RPC_P_RECEIVE_FAILED; break; } }
if (bytes == 0) { status = RPC_P_CONNECTION_SHUTDOWN; }
if (status != RPC_S_OK) { pConnection->Abort(); return(status); }
// Read completed, process the data now..
status = pConnection->ProcessRead(bytes, pBuffer, pMessageLength); } while (status == RPC_P_PARTIAL_RECEIVE );
return(status); }
RPC_STATUS RPC_ENTRY CO_SyncRecv( IN RPC_TRANSPORT_CONNECTION ThisConnection, OUT BUFFER *pBuffer, OUT PUINT pBufferLength, IN DWORD dwTimeout ) /*++
Routine Description:
Receive the next PDU to arrive at the connection.
Arguments:
ThisConnection - The connection to read from.
pBuffer - If successful, points to a buffer containing the next PDU. pBufferLength - If successful, contains the length of the message.
Return Value:
RPC_S_OK
RPC_P_RECEIVE_FAILED - Connection aborted. RPC_S_CALL_CANCELLED - Connection aborted.
--*/ { PCONNECTION p = (PCONNECTION)ThisConnection; DWORD bytes; RPC_STATUS status; HANDLE hEvent;
ASSERT((p->type & TYPE_MASK) == CLIENT);
ASSERT(p->pReadBuffer == 0);
p->pReadBuffer = TransConnectionAllocatePacket(p, p->iPostSize);
hEvent = I_RpcTransGetThreadEvent();
if (p->pReadBuffer == 0) { p->Abort(); return(RPC_P_RECEIVE_FAILED); }
p->maxReadBuffer = p->iPostSize; p->iLastRead = 0; p->Read.ol.hEvent = (HANDLE)((ULONG_PTR)hEvent | 0x01);
do { status = CO_SubmitSyncRead(p, pBuffer, pBufferLength);
if (status != RPC_P_IO_PENDING) { ASSERT(status != RPC_S_CALL_CANCELLED); break; }
status = UTIL_GetOverlappedResultEx(ThisConnection, &p->Read.ol, &bytes, TRUE, // Alertable
dwTimeout);
if (status != RPC_S_OK) { if (status != ERROR_MORE_DATA) { RpcpErrorAddRecord(EEInfoGCIO, status, EEInfoDLCOSyncRecv10); if ((status != RPC_S_CALL_CANCELLED) && (status != RPC_P_TIMEOUT)) { status = RPC_P_RECEIVE_FAILED; }
break; }
// ERROR_MORE_DATA is success
}
status = p->ProcessRead(bytes, pBuffer, pBufferLength);
} while (status == RPC_P_PARTIAL_RECEIVE);
if (status == RPC_S_OK) { ASSERT(p->pReadBuffer == 0);
return(RPC_S_OK); }
p->Abort();
if ((status == RPC_S_CALL_CANCELLED) || (status == RPC_P_TIMEOUT)) { // Wait for the read to complete. Since the connection has
// just been closed this won't take very long.
UTIL_WaitForSyncIO(&p->Read.ol, FALSE, INFINITE); }
return(status); }
RPC_STATUS RPC_ENTRY CO_SyncRecv_Avrf( IN RPC_TRANSPORT_CONNECTION ThisConnection, OUT BUFFER *pBuffer, OUT PUINT pBufferLength, IN DWORD dwTimeout ) /*++
Routine Description:
Wrapper for CO_SyncRecv implementing corruption injection under the RPC verifier.
SyncRecv member of the transport interface may only be called by the cliet, hence we inject the corruption for a client receive.
Arguments:
Return Value:
--*/ { RPC_STATUS Status;
Status = CO_SyncRecv( ThisConnection, pBuffer, pBufferLength, dwTimeout);
if (!Status) { if (gfRPCVerifierEnabled) { CorruptionInject(ClientReceive, pBufferLength, (void **)pBuffer); } }
return Status; }
void BASE_CONNECTION::Initialize ( void ) /*++
Routine Description:
Initializes a base connection. Prior initialization ensures orderly cleanup.
Arguments:
Return Value:
--*/ { type = CLIENT | CONNECTION; pReadBuffer = 0; Conn.Handle = 0; fAborted = FALSE; pReadBuffer = 0; maxReadBuffer = 0; iPostSize = gPostSize; iLastRead = 0; RpcpMemorySet(&Read.ol, 0, sizeof(Read.ol)); Read.pAsyncObject = this; Read.thread = 0; InitIoCounter(); }
|