You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1093 lines
32 KiB
1093 lines
32 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1996 - 1999
|
|
|
|
Module Name:
|
|
|
|
Complete.cxx
|
|
|
|
Abstract:
|
|
|
|
The place that IO completes
|
|
|
|
Author:
|
|
|
|
Mario Goertzel [MarioGo]
|
|
|
|
Revision History:
|
|
|
|
MarioGo 3/19/1996 Bits 'n pieces
|
|
MarioGo 10/25/1996 Async RPC
|
|
|
|
--*/
|
|
#include <precomp.hxx>
|
|
#include <trans.hxx>
|
|
#include <cotrans.hxx>
|
|
|
|
HANDLE RpcCompletionPort = 0;
|
|
HANDLE InactiveRpcCompletionPort = 0;
|
|
|
|
HANDLE *RpcCompletionPorts;
|
|
long *CompletionPortHandleLoads;
|
|
|
|
BASE_ADDRESS *AddressList = 0;
|
|
|
|
HANDLE g_NotificationHandle = 0;
|
|
LONG g_ListeningForPNPNotifications = 0;
|
|
LONG g_NotifyRt = 0;
|
|
|
|
OVERLAPPED g_Overlapped;
|
|
CRITICAL_SECTION AddressListLock;
|
|
|
|
|
|
RPC_STATUS
|
|
RPC_ENTRY
|
|
COMMON_PostNonIoEvent(
|
|
RPC_TRANSPORT_EVENT Event,
|
|
DWORD Type,
|
|
PVOID Context
|
|
)
|
|
{
|
|
BOOL b;
|
|
int i = 5;
|
|
|
|
ASSERT(Event != TRANSPORT_POSTED_KEY);
|
|
|
|
do
|
|
{
|
|
// Kick a listening thread
|
|
b = PostQueuedCompletionStatus(RpcCompletionPort,
|
|
Type,
|
|
Event,
|
|
(LPOVERLAPPED)Context
|
|
);
|
|
if (b)
|
|
{
|
|
break;
|
|
}
|
|
|
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
RPCTRANS "PostQueuedCompleitonStatus failed %d\n",
|
|
GetLastError()));
|
|
|
|
Sleep(100);
|
|
i--;
|
|
}
|
|
while(i);
|
|
|
|
//
|
|
// If this has failed we are out of luck unless something else manages
|
|
// to wake up the listen thread.
|
|
//
|
|
// As of 4/19/96 PostQueuedCompletionStatus will only fail if the handle
|
|
// is invalid or the kernel is unable to allocate a small bit of non-paged
|
|
// pool. Either way we're toast...
|
|
//
|
|
|
|
ASSERT(b);
|
|
|
|
if (!b)
|
|
{
|
|
return(RPC_S_OUT_OF_RESOURCES);
|
|
}
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
RPC_ENTRY
|
|
COMMON_PostRuntimeEvent(
|
|
IN DWORD Type,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Posts an event to the completion port. This will complete
|
|
with an event type of RuntimePosted, event status RPC_S_OK
|
|
and event context of Context.
|
|
|
|
Arguments:
|
|
Context - Context associated with the event
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
|
|
RPC_S_OUT_OF_RESOURCES
|
|
RPC_S_OUT_OF_MEMORY
|
|
|
|
--*/
|
|
{
|
|
return(COMMON_PostNonIoEvent(RuntimePosted, Type, Context));
|
|
}
|
|
|
|
|
|
|
|
void
|
|
COMMON_AddressManager(
|
|
BASE_ADDRESS *pAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
When an address does not have an outstanding connect/accept/recv for some
|
|
reason it is added to the AddressList global list of address objects. Listen
|
|
threads will try to submit a listen on these as time passed. New addresses
|
|
are put onto this list when they are ready to start listening.
|
|
|
|
Arguments:
|
|
|
|
pAddress - An address without an outstanding listen.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
|
|
EnterCriticalSection(&AddressListLock);
|
|
|
|
if (pAddress->InAddressList == NotInList)
|
|
{
|
|
|
|
#if DBG
|
|
// The address should not be in the list.
|
|
BASE_ADDRESS *pT = AddressList;
|
|
while(pT)
|
|
{
|
|
ASSERT(pT != pAddress);
|
|
pT = pT->pNext;
|
|
}
|
|
#endif
|
|
|
|
pAddress->pNext = AddressList;
|
|
AddressList = pAddress;
|
|
pAddress->InAddressList = InTheList;
|
|
}
|
|
|
|
LeaveCriticalSection(&AddressListLock);
|
|
}
|
|
|
|
|
|
void RPC_ENTRY
|
|
COMMON_ServerCompleteListen(
|
|
IN RPC_TRANSPORT_ADDRESS ThisAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called on an address once the runtime is really ready to start
|
|
processing connections on this address.
|
|
|
|
Arguments:
|
|
|
|
Address - A fully initalized address which the runtime is
|
|
ready to start receiving connection on.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
BASE_ADDRESS *pList = (BASE_ADDRESS *) ThisAddress;
|
|
|
|
while(pList)
|
|
{
|
|
COMMON_AddressManager(pList);
|
|
pList = pList->pNextAddress;
|
|
}
|
|
|
|
COMMON_ListenForPNPNotifications();
|
|
|
|
// The TRANSPORT message indicates that a new
|
|
// address has been added to the AddressList.
|
|
|
|
COMMON_PostNonIoEvent(TRANSPORT, 0, 0);
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
RPC_STATUS RPC_ENTRY
|
|
COMMON_PrepareNewHandle(HANDLE hAdd)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generic wrapper used to add a newly create IO handle to
|
|
to the IO completion port.
|
|
|
|
Arguments:
|
|
|
|
hAdd - The handle to be added to the port.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
RPC_S_OUT_OF_MEMORY
|
|
|
|
--*/
|
|
{
|
|
HANDLE h = CreateIoCompletionPort(hAdd,
|
|
RpcCompletionPort,
|
|
TRANSPORT_POSTED_KEY,
|
|
0);
|
|
|
|
if (h)
|
|
{
|
|
ASSERT(h == RpcCompletionPort);
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
RPCTRANS "CreateIoCompletionPort failed %d\n",
|
|
GetLastError()));
|
|
|
|
ASSERT((GetLastError() == ERROR_NO_SYSTEM_RESOURCES) ||
|
|
(GetLastError() == ERROR_NOT_ENOUGH_QUOTA));
|
|
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
|
|
void
|
|
COMMON_RemoveAddress (
|
|
IN BASE_ADDRESS *Address
|
|
)
|
|
/*++
|
|
Function Name:COMMON_RemoveAddress
|
|
|
|
Parameters:
|
|
|
|
Description:
|
|
This function must be called only when AddressListLock is held
|
|
Remove address from the address manager list
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
Address->InAddressList = Inactive;
|
|
|
|
//
|
|
// Close the sockets in the address
|
|
//
|
|
|
|
if (Address->type & DATAGRAM)
|
|
{
|
|
DG_DeactivateAddress((WS_DATAGRAM_ENDPOINT *) Address);
|
|
}
|
|
else
|
|
{
|
|
WS_DeactivateAddress((WS_ADDRESS *) Address);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
RPC_ENTRY
|
|
COMMON_StartPnpNotifications (
|
|
)
|
|
{
|
|
ASSERT(RpcCompletionPort);
|
|
|
|
g_NotifyRt = TRUE;
|
|
COMMON_ListenForPNPNotifications();
|
|
}
|
|
|
|
|
|
VOID
|
|
RPC_ENTRY
|
|
COMMON_ListenForPNPNotifications (
|
|
)
|
|
/*++
|
|
Function Name:COMMON_ListenForPNPNotifications
|
|
|
|
Parameters:
|
|
|
|
Description:
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
int retval;
|
|
HANDLE h;
|
|
|
|
if (hWinsock2 == 0)
|
|
{
|
|
//
|
|
// Winsock not loaded, don't need to do any PNP stuff
|
|
//
|
|
return;
|
|
}
|
|
|
|
if (InterlockedIncrement(&g_ListeningForPNPNotifications) != 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// REVIEW: We may need to provide a mechanism to prevent spinning for lack of
|
|
// resources
|
|
|
|
if (g_NotificationHandle == 0)
|
|
{
|
|
retval = WSAProviderConfigChange(
|
|
&g_NotificationHandle,
|
|
0, 0);
|
|
if (retval != 0 || g_NotificationHandle == 0)
|
|
{
|
|
if (g_NotificationHandle)
|
|
CloseHandle(g_NotificationHandle);
|
|
goto Cleanup;
|
|
}
|
|
|
|
h = CreateIoCompletionPort(g_NotificationHandle,
|
|
RpcCompletionPort,
|
|
NewAddress,
|
|
0);
|
|
|
|
if (h == 0)
|
|
{
|
|
CloseHandle(g_NotificationHandle);
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(h == RpcCompletionPort);
|
|
}
|
|
}
|
|
|
|
// if the previous request is still there, we don't want to submit another one
|
|
if (g_Overlapped.Internal != STATUS_PENDING)
|
|
{
|
|
g_Overlapped.hEvent = 0;
|
|
g_Overlapped.Offset = 0;
|
|
g_Overlapped.OffsetHigh = 0;
|
|
|
|
retval = WSAProviderConfigChange(
|
|
&g_NotificationHandle,
|
|
&g_Overlapped,
|
|
0);
|
|
if (retval != 0)
|
|
{
|
|
if (GetLastError() != WSA_IO_PENDING)
|
|
{
|
|
CloseHandle(g_NotificationHandle);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!TransportProtocol::ResubmitQueriesIfNecessary())
|
|
{
|
|
CloseHandle(g_NotificationHandle);
|
|
goto Cleanup;
|
|
}
|
|
|
|
g_ListeningForPNPNotifications = 2;
|
|
return;
|
|
|
|
Cleanup:
|
|
g_ListeningForPNPNotifications = 0;
|
|
g_NotificationHandle = 0;
|
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
RPCTRANS "COMMON_ListenForPNPNotifications failed\n"));
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
RPC_ENTRY
|
|
COMMON_ProcessCalls(
|
|
IN INT Timeout,
|
|
OUT RPC_TRANSPORT_EVENT *pEvent,
|
|
OUT RPC_STATUS *pEventStatus,
|
|
IN OUT PVOID *ppEventContext,
|
|
OUT UINT *pBufferLength,
|
|
OUT BUFFER *pBuffer,
|
|
OUT PVOID *ppSourceContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine waits for any async IO to complete for all protocols
|
|
within a transport DLL. It maybe called by multiple threads at a
|
|
time. A minimum of one thread should always be calling this function
|
|
for each DLL.
|
|
|
|
Note: async clients with no outstanding IO may allow the
|
|
last thread to timeout and only call this function again
|
|
when a new call is started.
|
|
|
|
Note: During calls to this API in connection oriented servers
|
|
a callback to I_RpcTransServerNewConnection() may occur.
|
|
|
|
Arguments:
|
|
|
|
Timeout - -1 - infinite
|
|
other - number of milliseconds to wait for IO
|
|
|
|
pEvent - Set on return to the type of IO event which finished.
|
|
|
|
pEventStatus - The status of the IO event
|
|
|
|
ppEventContext - On IN, the handle that the thread should dequeue on.
|
|
On output the context of the event
|
|
|
|
pBufferLength - If the event is successful then the number of
|
|
bytes transferred.
|
|
|
|
pBuffer - If the even is successful then the buffer associated
|
|
with the IO.
|
|
|
|
ppSourceContext - For datagram recvs this is the address
|
|
of the sender.
|
|
For connection sends this is the SendContext associated
|
|
with the IO. For connection recvs it is NULL.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - IO completed, see pEventStatus.
|
|
|
|
RPC_P_TIMEOUT - only if Timeout != INFINITE and is exceeded.
|
|
|
|
--*/
|
|
{
|
|
BOOL b;
|
|
ULONG_PTR key;
|
|
DWORD bytes;
|
|
RPC_STATUS status;
|
|
LPOVERLAPPED lpOverlapped;
|
|
PBASE_OVERLAPPED pBaseOverlapped;
|
|
PREQUEST pRequest;
|
|
PCONNECTION pConnection;
|
|
PADDRESS pAddress;
|
|
INT LocalTimeout;
|
|
HANDLE hCompletionPortHandle = (HANDLE) *ppEventContext;
|
|
DWORD LastError;
|
|
|
|
ASSERT(RpcCompletionPort);
|
|
|
|
*pEvent = 0;
|
|
*pBuffer = 0;
|
|
|
|
for(;;)
|
|
{
|
|
|
|
//
|
|
// Do general house keeping work here. If it appears that more
|
|
// house keeping work will be required in the future make
|
|
// sure to reduce the LocalTimeout to something < INFINITE.
|
|
//
|
|
|
|
LocalTimeout = Timeout;
|
|
|
|
// House keeping - look for any non-listening addresses and see if we
|
|
// can make them listen now. Addresses start in this list and are added
|
|
// back into the list if they are unable to submit a listen for some reason.
|
|
|
|
if (AddressList)
|
|
{
|
|
EnterCriticalSection(&AddressListLock);
|
|
|
|
if (AddressList)
|
|
{
|
|
|
|
pAddress = (PADDRESS)AddressList;
|
|
AddressList = 0;
|
|
|
|
if (Timeout == INFINITE)
|
|
{
|
|
// We want to wake up again soon and recheck the AddressList.
|
|
LocalTimeout = 7*1000;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pAddress = 0;
|
|
}
|
|
|
|
LeaveCriticalSection(&AddressListLock);
|
|
|
|
while(pAddress)
|
|
{
|
|
PADDRESS pNext = (PADDRESS)pAddress->pNext;
|
|
pAddress->pNext = 0;
|
|
|
|
if (pAddress->InAddressList == InTheList)
|
|
{
|
|
pAddress->InAddressList = NotInList;
|
|
pAddress->SubmitListen(pAddress);
|
|
}
|
|
pAddress = pNext;
|
|
}
|
|
}
|
|
|
|
if (!g_ListeningForPNPNotifications)
|
|
{
|
|
COMMON_ListenForPNPNotifications();
|
|
}
|
|
|
|
//
|
|
// The good part! Wait for something to happen...
|
|
//
|
|
b = GetQueuedCompletionStatus(hCompletionPortHandle,
|
|
&bytes,
|
|
&key,
|
|
&lpOverlapped,
|
|
LocalTimeout
|
|
);
|
|
|
|
if (!b && !lpOverlapped)
|
|
{
|
|
// If lpOverlapped is NULL this mean no IO completed.
|
|
if ((status = GetLastError()) == STATUS_TIMEOUT)
|
|
{
|
|
if (Timeout == INFINITE)
|
|
{
|
|
continue;
|
|
}
|
|
return(RPC_P_TIMEOUT);
|
|
}
|
|
else
|
|
{
|
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
RPCTRANS "GetQueuedCompletionStatus failed %d\n",
|
|
status));
|
|
|
|
ASSERT(0);
|
|
Sleep(1); // Avoid burning all the CPU in case we are hosed.
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//PrintToDebugger("A request arrived at the completion port\n");
|
|
if (key != TRANSPORT_POSTED_KEY)
|
|
{
|
|
if (b)
|
|
{
|
|
// Internal Non-IO posted event
|
|
// Key - The type of event
|
|
// lpOverlapped - The context associated with the event
|
|
|
|
ASSERT( key == RuntimePosted
|
|
|| key == TRANSPORT
|
|
|| key == NewAddress);
|
|
|
|
// RuntimePosted events allowed the RPC runtime to wake
|
|
// a listening thread with an atbitrary context.
|
|
if (key == RuntimePosted)
|
|
{
|
|
*pEvent = RuntimePosted;
|
|
*pEventStatus = RPC_S_OK;
|
|
*ppEventContext = lpOverlapped;
|
|
*pBufferLength = bytes;
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
//
|
|
// A protocol was just loaded or unloaded. Take care of it
|
|
//
|
|
if (key == NewAddress)
|
|
{
|
|
if (TransportProtocol::HandlePnPStateChange())
|
|
{
|
|
g_ListeningForPNPNotifications = 0;
|
|
*pEvent = NewAddress;
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
// REVIEW: Not processing notification handling failures
|
|
// may create problems where new protocols, or unloading of
|
|
// old ones are ignored. This is not very bad, so we keep
|
|
// it simple and ignore it.
|
|
g_ListeningForPNPNotifications = 0;
|
|
continue;
|
|
}
|
|
|
|
// TRANSPORT event is posted when a new address
|
|
// has been added to the AddressListen. Simply continue
|
|
// around the loop.
|
|
|
|
ASSERT(bytes == 0);
|
|
ASSERT(lpOverlapped == 0);
|
|
}
|
|
else
|
|
{
|
|
|
|
if (key == NewAddress)
|
|
{
|
|
g_ListeningForPNPNotifications = 0;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
ASSERT(!b || lpOverlapped);
|
|
|
|
status = RPC_S_OK;
|
|
|
|
if (!b)
|
|
{
|
|
pBaseOverlapped = FindOverlapped(lpOverlapped);
|
|
pRequest = FindRequest(lpOverlapped);
|
|
|
|
LastError = GetLastError();
|
|
if (( pRequest->type & ADDRESS)
|
|
&& (LastError != ERROR_MORE_DATA))
|
|
{
|
|
VALIDATE(GetLastError())
|
|
{
|
|
ERROR_NETNAME_DELETED,
|
|
ERROR_BAD_NETPATH,
|
|
ERROR_NO_SYSTEM_RESOURCES,
|
|
ERROR_SEM_TIMEOUT,
|
|
ERROR_OPERATION_ABORTED,
|
|
ERROR_HOST_UNREACHABLE,
|
|
ERROR_NETWORK_UNREACHABLE,
|
|
ERROR_UNEXP_NET_ERR,
|
|
ERROR_NOT_ENOUGH_QUOTA,
|
|
ERROR_BROKEN_PIPE,
|
|
ERROR_CONNECTION_ABORTED
|
|
} END_VALIDATE;
|
|
|
|
COMMON_AddressManager((BASE_ADDRESS *)pRequest);
|
|
continue;
|
|
}
|
|
|
|
switch (LastError)
|
|
{
|
|
case ERROR_MORE_DATA:
|
|
{
|
|
// Normal parital read of a connection request
|
|
// or an oversized datagram. This is ok, falls
|
|
// into the normal path.
|
|
|
|
status = RPC_P_OVERSIZE_PACKET;
|
|
break;
|
|
}
|
|
|
|
case ERROR_INVALID_HANDLE:
|
|
// Named pipes allows a close to reach the server before
|
|
// the read. When this happens the server rejects the read
|
|
// with an invalid handle error.
|
|
ASSERT(pRequest->id == NMP);
|
|
ASSERT(pRequest->fAborted);
|
|
|
|
// Fall into normal close case.
|
|
|
|
case ERROR_NETNAME_DELETED:
|
|
case ERROR_BROKEN_PIPE:
|
|
case ERROR_PIPE_NOT_CONNECTED:
|
|
case ERROR_NO_DATA:
|
|
case ERROR_SEM_TIMEOUT:
|
|
case ERROR_GRACEFUL_DISCONNECT:
|
|
case WSAECONNRESET:
|
|
case WSAESHUTDOWN:
|
|
case WSAECONNABORTED:
|
|
case WSAEHOSTDOWN:
|
|
case ERROR_CONNECTION_ABORTED:
|
|
{
|
|
bytes = 0;
|
|
ASSERT((pRequest->type & PROTO_MASK) == CONNECTION);
|
|
// Will be handled as a close
|
|
break;
|
|
}
|
|
|
|
case ERROR_NO_SYSTEM_RESOURCES:
|
|
{
|
|
//
|
|
// This is just like the errors above except that both c/o and datagram requests
|
|
// can generate it.
|
|
//
|
|
if ((pRequest->type & PROTO_MASK) == CONNECTION)
|
|
{
|
|
bytes = 0;
|
|
// Will be handled as a close
|
|
}
|
|
else
|
|
{
|
|
bytes = 0;
|
|
status = ERROR_OPERATION_ABORTED;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case ERROR_OPERATION_ABORTED:
|
|
{
|
|
//
|
|
// When a thread that issued an I/O dies the operation
|
|
// completes with this error.
|
|
// There are a couple cases here:
|
|
// 1) The IO is datagram in which case we can just
|
|
// reissue the I/O on this thread. In an idle
|
|
// server eventually all DG I/O will migrate to
|
|
// the single listening thread.
|
|
// 2) The IO is on a client connection and the
|
|
// the client thread has died. In this case
|
|
// we need to abort the connection and return
|
|
// to the runtime.
|
|
// 3) If this happens on an address we have a bug.
|
|
// 4) If this happens on a server connection we have a bug.
|
|
//
|
|
|
|
if (pRequest->type & DATAGRAM)
|
|
{
|
|
// We deal with this in the normal datagram path
|
|
ASSERT(bytes == 0);
|
|
status = ERROR_OPERATION_ABORTED;
|
|
break;
|
|
}
|
|
|
|
ASSERT((pRequest->type & PROTO_MASK) == CONNECTION);
|
|
// zero out the bytes just in case. Sometimes network operations
|
|
// return positive byte count on operation aborted
|
|
bytes = 0;
|
|
|
|
// We'll treat this as a connection close on the client.
|
|
// REVIEW: Maybe do something better.
|
|
|
|
break;
|
|
}
|
|
|
|
case ERROR_NETWORK_UNREACHABLE:
|
|
case ERROR_HOST_UNREACHABLE:
|
|
case ERROR_PORT_UNREACHABLE:
|
|
//
|
|
// errors coming from ICMP packets to our UDP endpoint.
|
|
// Winsock does not present this in a way our async architecture
|
|
// can use, so ignore them.
|
|
//
|
|
if ((pRequest->type & PROTO_MASK) == CONNECTION)
|
|
{
|
|
bytes = 0;
|
|
// Will be handled as a close
|
|
}
|
|
else
|
|
{
|
|
status = ERROR_OPERATION_ABORTED;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
RPCTRANS "IO failed %lX %d\n",
|
|
pRequest,
|
|
GetLastError()));
|
|
|
|
ASSERT(0);
|
|
status = RPC_S_OUT_OF_RESOURCES;
|
|
// treat as a close
|
|
bytes = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// here we actually have a completed IO
|
|
pBaseOverlapped = FindOverlapped(lpOverlapped);
|
|
pRequest = FindRequest(lpOverlapped);
|
|
|
|
if (pRequest == NULL)
|
|
{
|
|
ASSERT(0);
|
|
return (RPC_S_INTERNAL_ERROR);
|
|
}
|
|
|
|
switch(pRequest->type & PROTO_MASK)
|
|
{
|
|
case CONNECTION:
|
|
//
|
|
// Connection IO completed.
|
|
//
|
|
I_RpcTransUnprotectThread(pBaseOverlapped->thread);
|
|
|
|
pConnection = (PCONNECTION)pRequest;
|
|
|
|
// A read or write either completed or failed
|
|
|
|
*ppEventContext = pConnection;
|
|
|
|
if (pBaseOverlapped == &pConnection->Read)
|
|
{
|
|
// Read completed
|
|
*ppSourceContext = UlongToPtr(bytes);
|
|
|
|
if (bytes == 0)
|
|
{
|
|
*pEvent = pConnection->type | RECEIVE;
|
|
pConnection->Abort();
|
|
|
|
*pEventStatus = RPC_P_CONNECTION_SHUTDOWN;
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
status = pConnection->ProcessRead(bytes,
|
|
pBuffer,
|
|
pBufferLength);
|
|
|
|
// N.B. Do not move the reading of the pConnection->type
|
|
// before ProcessRead. ProcessRead can change the type based
|
|
// on what it reads
|
|
*pEvent = pConnection->type | RECEIVE;
|
|
|
|
if (status != RPC_P_PARTIAL_RECEIVE)
|
|
{
|
|
ASSERT( status == RPC_P_RECEIVE_FAILED
|
|
|| status == RPC_S_OK
|
|
|| status == RPC_P_INITIALIZE_HTTP2_CONNECTION);
|
|
|
|
|
|
*pEventStatus = status;
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
// Message is not complete, submit the next read and continue.
|
|
|
|
status = CO_SubmitRead(pConnection);
|
|
|
|
if (status != RPC_S_OK)
|
|
{
|
|
ASSERT(status == RPC_P_RECEIVE_FAILED);
|
|
*pEventStatus = status;
|
|
return(RPC_S_OK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Write completed
|
|
CO_SEND_CONTEXT *pSend = (CO_SEND_CONTEXT *)pBaseOverlapped;
|
|
|
|
ASSERT(pSend->Write.pAsyncObject == pConnection);
|
|
|
|
*pEvent = pConnection->type | SEND;
|
|
*ppSourceContext = pSend;
|
|
|
|
*pBuffer = pSend->pWriteBuffer;
|
|
|
|
if (bytes == 0)
|
|
{
|
|
pConnection->Abort();
|
|
|
|
*pEventStatus = RPC_P_SEND_FAILED;
|
|
*pBufferLength = 0;
|
|
}
|
|
else
|
|
{
|
|
status = RPC_S_OK;
|
|
|
|
*pEventStatus = status;
|
|
*pBufferLength = pSend->maxWriteBuffer;
|
|
|
|
// Netbios client-side writes are sizeof(DWORD) too big since
|
|
// they also include the sequence number.
|
|
|
|
ASSERT( bytes == pSend->maxWriteBuffer
|
|
|| ( (bytes == pSend->maxWriteBuffer + sizeof(DWORD))
|
|
&& ((pConnection->type & TYPE_MASK) == CLIENT) ) );
|
|
}
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
break;
|
|
|
|
case ADDRESS:
|
|
{
|
|
// ASSERT(bytes == 0);
|
|
pAddress = (PADDRESS)pRequest;
|
|
PCONNECTION pNewConnection = 0;
|
|
|
|
status = pAddress->NewConnection(pAddress, &pNewConnection);
|
|
|
|
if (RPC_S_OK == status)
|
|
{
|
|
// Opened a connection, now try to submit the first recv.
|
|
|
|
ASSERT(pNewConnection);
|
|
RPC_CONNECTION_TRANSPORT *pInfo;
|
|
|
|
pInfo = (RPC_CONNECTION_TRANSPORT *)TransportTable[pAddress->id].pInfo;
|
|
|
|
ASSERT(pInfo->Recv);
|
|
|
|
status = (pInfo->Recv)(pNewConnection);
|
|
|
|
if (RPC_S_OK != status)
|
|
{
|
|
ASSERT(status == RPC_P_RECEIVE_FAILED);
|
|
*pEvent = pNewConnection->type | RECEIVE;
|
|
*ppEventContext = pNewConnection;
|
|
*pEventStatus = status;
|
|
return(RPC_S_OK);
|
|
}
|
|
}
|
|
|
|
// Connection has been established or closed, either
|
|
// way we can continue around the loop.
|
|
}
|
|
break;
|
|
|
|
case DATAGRAM:
|
|
{
|
|
BASE_ASYNC_OBJECT *pBase = (BASE_ASYNC_OBJECT*)pRequest;
|
|
|
|
#ifdef NCADG_MQ_ON
|
|
if (pBase->id == MSMQ)
|
|
{
|
|
// MSMQ (Falcon) datagram path:
|
|
MQ_DATAGRAM *pDatagram = (MQ_DATAGRAM*)pRequest;
|
|
MQ_DATAGRAM_ENDPOINT *pEndpoint = (MQ_DATAGRAM_ENDPOINT*)pDatagram->pEndpoint;
|
|
|
|
if (status == RPC_P_OVERSIZE_PACKET)
|
|
{
|
|
// Data still pending, get it:
|
|
status = MQ_ResizePacket( pEndpoint,
|
|
(void**)&pDatagram->pAddress,
|
|
(unsigned int*)pBufferLength,
|
|
pBuffer );
|
|
}
|
|
|
|
if (status == RPC_S_OK)
|
|
{
|
|
MQ_FillInAddress(pDatagram->pAddress,pDatagram->Read.aMsgPropVar);
|
|
|
|
*pEvent = pDatagram->type;
|
|
*pEventStatus = status;
|
|
*ppEventContext = pEndpoint;
|
|
// WATCH OUT! MSMQ doesn't return the size in "bytes"
|
|
// from GetQueuedCompletionStatus() like everything
|
|
// else does! We need to extract the #bytes from the
|
|
// message structure.
|
|
//
|
|
// DON'T: *pBufferLength = bytes;
|
|
*pBufferLength = pDatagram->Read.aMsgPropVar[1].ulVal;
|
|
*pBuffer = (BUFFER)pDatagram->pPacket;
|
|
*ppSourceContext = pDatagram->pAddress;
|
|
|
|
pDatagram->pPacket = 0;
|
|
pDatagram->dwPacketSize = 0;
|
|
}
|
|
|
|
pDatagram->Busy = 0;
|
|
|
|
LONG c = InterlockedDecrement(&pEndpoint->cPendingIos);
|
|
|
|
ASSERT(c >= 0);
|
|
|
|
if (c == 0)
|
|
{
|
|
// No pending receives, time to post more. This doesn't
|
|
// get hit very often, normally additional recieves are
|
|
// after sending a packet. (see DG_SendPacket)
|
|
|
|
MQ_SubmitReceives(pEndpoint);
|
|
}
|
|
|
|
if (status == RPC_S_OK)
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// Normal datagram path:
|
|
WS_DATAGRAM *pDatagram = (WS_DATAGRAM *)pRequest;
|
|
WS_DATAGRAM_ENDPOINT *pEndpoint = (WS_DATAGRAM_ENDPOINT*)pDatagram->pEndpoint;
|
|
|
|
if (status == RPC_P_OVERSIZE_PACKET)
|
|
{
|
|
ASSERT(bytes == pDatagram->Packet.len);
|
|
}
|
|
|
|
if ( status == RPC_S_OK
|
|
|| status == RPC_P_OVERSIZE_PACKET)
|
|
{
|
|
// A receive completed
|
|
|
|
ASSERT(bytes);
|
|
|
|
*pEvent = pDatagram->type;
|
|
*pEventStatus = status;
|
|
*ppEventContext = pEndpoint;
|
|
*pBufferLength = bytes;
|
|
*pBuffer = (BUFFER)pDatagram->Packet.buf;
|
|
*ppSourceContext = pDatagram->AddressPair;
|
|
|
|
ASSERT( pDatagram->Packet.buf );
|
|
|
|
// Ready the datagram for another IO operation.
|
|
pDatagram->Packet.buf = 0;
|
|
|
|
status = RPC_S_OK;
|
|
}
|
|
|
|
#if DBG
|
|
if (status != RPC_S_OK &&
|
|
status != ERROR_OPERATION_ABORTED)
|
|
{
|
|
DbgPrint("RPC: I/O completed with 0x%x\n", status);
|
|
|
|
ASSERT( 0 );
|
|
}
|
|
#endif
|
|
|
|
// Do not touch the datagram after this!
|
|
pDatagram->Busy = 0;
|
|
|
|
LONG c = InterlockedDecrement(&pEndpoint->cPendingIos);
|
|
|
|
ASSERT(c >= 0);
|
|
|
|
if (c == 0)
|
|
{
|
|
// No pending receives, time to post more. This doesn't
|
|
// get hit very often, normally additional recieves are
|
|
// after sending a packet. (see DG_SendPacket)
|
|
|
|
DG_SubmitReceives(pEndpoint);
|
|
}
|
|
|
|
if (status == RPC_S_OK)
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
}
|
|
}
|
|
// Operation aborted, continue around the loop.
|
|
break;
|
|
|
|
default:
|
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
RPCTRANS "Invalid request type: 0x%x (%p)\n",
|
|
pRequest->type, pRequest));
|
|
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
// Loop
|
|
}
|
|
|
|
ASSERT(0);
|
|
return(RPC_S_INTERNAL_ERROR);
|
|
}
|
|
|