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.
2743 lines
78 KiB
2743 lines
78 KiB
/*++
|
|
|
|
Copyright (c) 2000-2002 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
uctdi.c
|
|
|
|
Abstract:
|
|
|
|
Contains the TDI related functionality for the HTTP client side stuff.
|
|
|
|
Author:
|
|
|
|
Henry Sanders (henrysa) 07-Aug-2000
|
|
Rajesh Sundaram (rajeshsu) 01-Oct-2000
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#pragma alloc_text( PAGEUC, UcClientConnect)
|
|
#pragma alloc_text( PAGEUC, UcCloseConnection)
|
|
#pragma alloc_text( PAGEUC, UcSendData)
|
|
#pragma alloc_text( PAGEUC, UcReceiveData)
|
|
#pragma alloc_text( PAGEUC, UcpTdiDisconnectHandler)
|
|
#pragma alloc_text( PAGEUC, UcpCloseRawConnection)
|
|
#pragma alloc_text( PAGEUC, UcCloseRawFilterConnection)
|
|
#pragma alloc_text( PAGEUC, UcDisconnectRawFilterConnection)
|
|
#pragma alloc_text( PAGEUC, UcpSendRawData)
|
|
#pragma alloc_text( PAGEUC, UcpReceiveRawData)
|
|
#pragma alloc_text( PAGEUC, UcpTdiReceiveHandler)
|
|
#pragma alloc_text( PAGEUC, UcpReceiveExpeditedHandler)
|
|
#pragma alloc_text( PAGEUC, UcpRestartSendData)
|
|
#pragma alloc_text( PAGEUC, UcpBeginDisconnect)
|
|
#pragma alloc_text( PAGEUC, UcpRestartDisconnect)
|
|
#pragma alloc_text( PAGEUC, UcpBeginAbort)
|
|
#pragma alloc_text( PAGEUC, UcpRestartAbort)
|
|
#pragma alloc_text( PAGEUC, UcpRestartReceive)
|
|
#pragma alloc_text( PAGEUC, UcpRestartClientReceive)
|
|
#pragma alloc_text( PAGEUC, UcpConnectComplete)
|
|
#pragma alloc_text( PAGEUC, UcSetFlag)
|
|
#pragma alloc_text( PAGEUC, UcpBuildTdiReceiveBuffer)
|
|
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
//
|
|
// Public functions.
|
|
//
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Connects an UC connection to a remote server. We take as input an
|
|
HTTP connection object. It's assumed that the connection object
|
|
already has the remote address information filled in.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Pointer to the HTTP connection object to be connected.
|
|
pIrp - Pointer to Irp to use for the connect request.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UcClientConnect(
|
|
IN PUC_CLIENT_CONNECTION pConnection,
|
|
IN PIRP pIrp
|
|
)
|
|
|
|
{
|
|
NTSTATUS status;
|
|
LONGLONG llTimeOut;
|
|
PLONGLONG pllTimeOut = NULL;
|
|
USHORT AddressType;
|
|
|
|
#if CLIENT_IP_ADDRESS_TRACE
|
|
|
|
CHAR IpAddressString[MAX_IP_ADDR_AND_PORT_STRING_LEN + 1];
|
|
ULONG Length;
|
|
|
|
#endif
|
|
|
|
AddressType = pConnection->pNextAddress->AddressType;
|
|
|
|
ASSERT(AddressType == TDI_ADDRESS_TYPE_IP ||
|
|
AddressType == TDI_ADDRESS_TYPE_IP6);
|
|
|
|
#if CLIENT_IP_ADDRESS_TRACE
|
|
|
|
Length = HostAddressAndPortToString(
|
|
IpAddressString,
|
|
pConnection->pNextAddress->Address,
|
|
AddressType
|
|
);
|
|
|
|
ASSERT(Length < sizeof(IpAddressString));
|
|
|
|
UlTrace(TDI, ("[UcClientConnect]: Trying Address %s \n", IpAddressString));
|
|
|
|
#endif
|
|
|
|
//
|
|
// Format the connect IRP. When the IRP completes our completion routine
|
|
// (UcConnectComplete) will be called.
|
|
//
|
|
|
|
pConnection->pTdiObjects->TdiInfo.RemoteAddress =
|
|
&pConnection->RemoteAddress;
|
|
|
|
pConnection->pTdiObjects->TdiInfo.RemoteAddressLength =
|
|
(FIELD_OFFSET(TRANSPORT_ADDRESS, Address) +
|
|
FIELD_OFFSET(TA_ADDRESS, Address) +
|
|
pConnection->pNextAddress->AddressLength
|
|
);
|
|
|
|
pConnection->RemoteAddress.GenericTransportAddress.TAAddressCount = 1;
|
|
|
|
ASSERT(sizeof(pConnection->RemoteAddress) >=
|
|
pConnection->pTdiObjects->TdiInfo.RemoteAddressLength);
|
|
|
|
RtlCopyMemory(
|
|
pConnection->RemoteAddress.GenericTransportAddress.Address,
|
|
pConnection->pNextAddress,
|
|
FIELD_OFFSET(TA_ADDRESS, Address) +
|
|
pConnection->pNextAddress->AddressLength
|
|
);
|
|
|
|
if(pConnection->pServerInfo->ConnectionTimeout)
|
|
{
|
|
llTimeOut = Int32x32To64(pConnection->pServerInfo->ConnectionTimeout,
|
|
-10000);
|
|
|
|
pllTimeOut = &llTimeOut;
|
|
}
|
|
|
|
TdiBuildConnect(
|
|
pIrp,
|
|
pConnection->pTdiObjects->ConnectionObject.pDeviceObject,
|
|
pConnection->pTdiObjects->ConnectionObject.pFileObject,
|
|
UcpConnectComplete,
|
|
pConnection,
|
|
pllTimeOut,
|
|
&pConnection->pTdiObjects->TdiInfo,
|
|
NULL
|
|
);
|
|
|
|
status = UlCallDriver(
|
|
pConnection->pTdiObjects->ConnectionObject.pDeviceObject,
|
|
pIrp
|
|
);
|
|
|
|
return status;
|
|
}
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Closes a previously accepted connection.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies a pointer to a connection as previously
|
|
indicated to the PUL_CONNECTION_REQUEST handler.
|
|
|
|
AbortiveDisconnect - Supplies TRUE if the connection is to be abortively
|
|
disconnected, FALSE if it should be gracefully disconnected.
|
|
|
|
pCompletionRoutine - Supplies a pointer to a completion routine to
|
|
invoke after the connection is fully closed.
|
|
|
|
pCompletionContext - Supplies an uninterpreted context value for the
|
|
completion routine.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UcCloseConnection(
|
|
IN PVOID pConnectionContext,
|
|
IN BOOLEAN AbortiveDisconnect,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext,
|
|
IN NTSTATUS status
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
PUC_CLIENT_CONNECTION pConnection;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pConnection = (PUC_CLIENT_CONNECTION) pConnectionContext;
|
|
ASSERT( UC_IS_VALID_CLIENT_CONNECTION( pConnection ) );
|
|
|
|
UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
|
|
|
|
if(AbortiveDisconnect)
|
|
{
|
|
switch(pConnection->ConnectionState)
|
|
{
|
|
case UcConnectStateConnectComplete:
|
|
case UcConnectStateProxySslConnect:
|
|
case UcConnectStateProxySslConnectComplete:
|
|
case UcConnectStateConnectReady:
|
|
case UcConnectStateDisconnectComplete:
|
|
case UcConnectStatePerformingSslHandshake:
|
|
|
|
pConnection->ConnectionState = UcConnectStateAbortPending;
|
|
pConnection->ConnectionStatus = status;
|
|
|
|
UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
|
|
|
|
status = UcpCloseRawConnection(
|
|
pConnection,
|
|
AbortiveDisconnect,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
break;
|
|
|
|
case UcConnectStateDisconnectPending:
|
|
|
|
// We had originally sent a graceful disconnect, but now
|
|
// we intend to RST the connection. We should propagate the
|
|
// new error code.
|
|
|
|
pConnection->ConnectionStatus = status;
|
|
pConnection->Flags |= CLIENT_CONN_FLAG_ABORT_PENDING;
|
|
|
|
UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
|
|
|
|
break;
|
|
|
|
default:
|
|
UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch(pConnection->ConnectionState)
|
|
{
|
|
case UcConnectStateConnectReady:
|
|
|
|
//
|
|
// We only send graceful disconnects through the filter
|
|
// process. There's also no point in going through the
|
|
// filter if the connection is already being closed or
|
|
// aborted.
|
|
//
|
|
pConnection->ConnectionStatus = status;
|
|
|
|
if(pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
pConnection->ConnectionState =
|
|
UcConnectStateIssueFilterClose;
|
|
|
|
ASSERT(pCompletionRoutine == NULL);
|
|
ASSERT(pCompletionContext == NULL);
|
|
|
|
UcKickOffConnectionStateMachine(
|
|
pConnection,
|
|
OldIrql,
|
|
UcConnectionWorkItem
|
|
);
|
|
}
|
|
else
|
|
{
|
|
pConnection->ConnectionState =
|
|
UcConnectStateDisconnectPending;
|
|
|
|
UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
|
|
|
|
//
|
|
// Really close the connection.
|
|
//
|
|
|
|
status = UcpCloseRawConnection(
|
|
pConnection,
|
|
AbortiveDisconnect,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
|
|
} // UcCloseConnection
|
|
|
|
/*********************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
This is our basic TDI send routine. We take an request structure, format
|
|
the IRP as a TDI send IRP, and send it.
|
|
|
|
Arguments:
|
|
|
|
pRequest - Pointer to request to be sent.
|
|
pConnection - Connection on which request is to be sent.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of send.
|
|
|
|
--*********************************************************************/
|
|
NTSTATUS
|
|
UcSendData(
|
|
IN PUC_CLIENT_CONNECTION pConnection,
|
|
IN PMDL pMdlChain,
|
|
IN ULONG Length,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext,
|
|
IN PIRP pIrp,
|
|
IN BOOLEAN RawSend
|
|
)
|
|
{
|
|
PUL_IRP_CONTEXT pIrpContext;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Sanity Checks.
|
|
//
|
|
ASSERT( UC_IS_VALID_CLIENT_CONNECTION(pConnection) );
|
|
ASSERT( pMdlChain != NULL);
|
|
ASSERT( Length > 0);
|
|
ASSERT( pCompletionRoutine != NULL);
|
|
|
|
|
|
//
|
|
// Allocate and initialize the IRP context
|
|
//
|
|
pIrpContext = UlPplAllocateIrpContext();
|
|
|
|
if(pIrpContext == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto fatal;
|
|
}
|
|
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
|
|
pIrpContext->pConnectionContext = (PVOID)pConnection;
|
|
pIrpContext->pCompletionContext = pCompletionContext;
|
|
pIrpContext->pOwnIrp = pIrp;
|
|
pIrpContext->pCompletionRoutine = pCompletionRoutine;
|
|
pIrpContext->OwnIrpContext = FALSE;
|
|
|
|
//
|
|
// Try to send the data.
|
|
//
|
|
|
|
if (pConnection->FilterInfo.pFilterChannel && !RawSend)
|
|
{
|
|
PAGED_CODE();
|
|
//
|
|
// First go through the filter.
|
|
//
|
|
status = UlFilterSendHandler(
|
|
&pConnection->FilterInfo,
|
|
pMdlChain,
|
|
Length,
|
|
pIrpContext
|
|
);
|
|
|
|
ASSERT(status == STATUS_PENDING);
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// Just send it directly to the network.
|
|
//
|
|
|
|
status = UcpSendRawData(
|
|
pConnection,
|
|
pMdlChain,
|
|
Length,
|
|
pIrpContext,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
goto fatal;
|
|
}
|
|
|
|
return STATUS_PENDING;
|
|
|
|
fatal:
|
|
|
|
ASSERT(!NT_SUCCESS(status));
|
|
|
|
if(pIrpContext != NULL)
|
|
{
|
|
UlPplFreeIrpContext(pIrpContext);
|
|
}
|
|
|
|
UC_CLOSE_CONNECTION(pConnection, TRUE, status);
|
|
|
|
|
|
status = UlInvokeCompletionRoutine(
|
|
status,
|
|
0,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
|
|
return status;
|
|
|
|
} // UcSendData
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Receives data from the specified connection. This function is
|
|
typically used after a receive indication handler has failed to
|
|
consume all of the indicated data.
|
|
|
|
If the connection is filtered, the data will be read from the filter
|
|
channel.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies a pointer to a connection as previously
|
|
indicated to the PUL_CONNECTION_REQUEST handler.
|
|
|
|
pBuffer - Supplies a pointer to the target buffer for the received
|
|
data.
|
|
|
|
BufferLength - Supplies the length of pBuffer.
|
|
|
|
pCompletionRoutine - Supplies a pointer to a completion routine to
|
|
invoke after the listening endpoint is fully closed.
|
|
|
|
pCompletionContext - Supplies an uninterpreted context value for the
|
|
completion routine.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UcReceiveData(
|
|
IN PVOID pConnectionContext,
|
|
IN PVOID pBuffer,
|
|
IN ULONG BufferLength,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PUC_CLIENT_CONNECTION pConnection;
|
|
|
|
pConnection = (PUC_CLIENT_CONNECTION) pConnectionContext;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( UC_IS_VALID_CLIENT_CONNECTION( pConnection ) );
|
|
|
|
if(pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
//
|
|
// This is a filtered connection, get the data from the
|
|
// filter.
|
|
//
|
|
|
|
status = UlFilterReadHandler(
|
|
&pConnection->FilterInfo,
|
|
(PBYTE)pBuffer,
|
|
BufferLength,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This is not a filtered connection. Get the data from
|
|
// TDI.
|
|
//
|
|
|
|
status = UcpReceiveRawData(
|
|
pConnectionContext,
|
|
pBuffer,
|
|
BufferLength,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// Private Functions
|
|
//
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Handler for disconnect requests.
|
|
|
|
Arguments:
|
|
|
|
pTdiEventContext - Supplies the context associated with the address
|
|
object. This should be a PUL_ENDPOINT.
|
|
|
|
ConnectionContext - Supplies the context associated with the
|
|
connection object. This should be a PUC_CONNECTION.
|
|
|
|
DisconnectDataLength - Optionally supplies the length of any
|
|
disconnect data associated with the disconnect request.
|
|
|
|
pDisconnectData - Optionally supplies a pointer to any disconnect
|
|
data associated with the disconnect request.
|
|
|
|
DisconnectInformationLength - Optionally supplies the length of any
|
|
disconnect information associated with the disconnect request.
|
|
|
|
pDisconnectInformation - Optionally supplies a pointer to any
|
|
disconnect information associated with the disconnect request.
|
|
|
|
DisconnectFlags - Supplies the disconnect flags. This will be zero
|
|
or more TDI_DISCONNECT_* flags.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UcpTdiDisconnectHandler(
|
|
IN PVOID pTdiEventContext,
|
|
IN CONNECTION_CONTEXT ConnectionContext,
|
|
IN LONG DisconnectDataLength,
|
|
IN PVOID pDisconnectData,
|
|
IN LONG DisconnectInformationLength,
|
|
IN PVOID pDisconnectInformation,
|
|
IN ULONG DisconnectFlags
|
|
)
|
|
{
|
|
PUC_CLIENT_CONNECTION pConnection;
|
|
PUC_TDI_OBJECTS pTdiObjects;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
KIRQL OldIrql;
|
|
|
|
|
|
UNREFERENCED_PARAMETER(pDisconnectInformation);
|
|
UNREFERENCED_PARAMETER(DisconnectInformationLength);
|
|
UNREFERENCED_PARAMETER(pDisconnectData);
|
|
UNREFERENCED_PARAMETER(DisconnectDataLength);
|
|
UNREFERENCED_PARAMETER(pTdiEventContext);
|
|
|
|
UL_ENTER_DRIVER("UcpTdiDisconnectHandler", NULL);
|
|
|
|
pTdiObjects = (PUC_TDI_OBJECTS) ConnectionContext;
|
|
|
|
pConnection = pTdiObjects->pConnection;
|
|
|
|
if(pConnection == NULL)
|
|
{
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
goto end;
|
|
}
|
|
|
|
ASSERT( UC_IS_VALID_CLIENT_CONNECTION(pConnection) );
|
|
|
|
UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
|
|
|
|
//
|
|
// Update the connection state based on the type of disconnect.
|
|
//
|
|
|
|
if(DisconnectFlags & TDI_DISCONNECT_ABORT)
|
|
{
|
|
pConnection->Flags |= CLIENT_CONN_FLAG_ABORT_RECEIVED;
|
|
|
|
UC_WRITE_TRACE_LOG(
|
|
g_pUcTraceLog,
|
|
UC_ACTION_CONNECTION_TDI_DISCONNECT,
|
|
pConnection,
|
|
UlongToPtr((ULONG)STATUS_CONNECTION_ABORTED),
|
|
UlongToPtr(pConnection->ConnectionState),
|
|
UlongToPtr(pConnection->Flags)
|
|
);
|
|
|
|
switch(pConnection->ConnectionState)
|
|
{
|
|
case UcConnectStateConnectReady:
|
|
case UcConnectStateDisconnectComplete:
|
|
case UcConnectStatePerformingSslHandshake:
|
|
case UcConnectStateConnectComplete:
|
|
case UcConnectStateProxySslConnectComplete:
|
|
case UcConnectStateProxySslConnect:
|
|
|
|
// Received an abort when we were connected or have completed
|
|
// our half close, proceed to cleanup. Cleanup can be done
|
|
// only at passive, so we start the worker.
|
|
|
|
pConnection->ConnectionStatus = STATUS_CONNECTION_ABORTED;
|
|
pConnection->ConnectionState = UcConnectStateConnectCleanup;
|
|
|
|
UC_WRITE_TRACE_LOG(
|
|
g_pUcTraceLog,
|
|
UC_ACTION_CONNECTION_CLEANUP,
|
|
pConnection,
|
|
UlongToPtr((ULONG)pConnection->ConnectionStatus),
|
|
UlongToPtr(pConnection->ConnectionState),
|
|
UlongToPtr(pConnection->Flags)
|
|
);
|
|
|
|
UcKickOffConnectionStateMachine(
|
|
pConnection,
|
|
OldIrql,
|
|
UcConnectionWorkItem
|
|
);
|
|
|
|
break;
|
|
|
|
|
|
case UcConnectStateDisconnectPending:
|
|
|
|
// We got a RST when we had a pending disconnect. Let's flag
|
|
// the connection so that we complete the cleanup when our
|
|
// Disconnect Completes.
|
|
|
|
pConnection->ConnectionStatus = STATUS_CONNECTION_ABORTED;
|
|
|
|
UlReleaseSpinLock(&pConnection->SpinLock,OldIrql);
|
|
|
|
break;
|
|
|
|
case UcConnectStateDisconnectIndicatedPending:
|
|
|
|
// When we get a disconnect indication, we issue one ourselves.
|
|
// Therefore, there is no need for us to do anything with this
|
|
// abort. When our pending disconnect compeltes, we'll
|
|
// cleanup anyway.
|
|
|
|
pConnection->ConnectionStatus = STATUS_CONNECTION_ABORTED;
|
|
|
|
UlReleaseSpinLock(&pConnection->SpinLock,OldIrql);
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
//
|
|
// We don't have to do anything here.
|
|
//
|
|
|
|
UlReleaseSpinLock(&pConnection->SpinLock,OldIrql);
|
|
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pConnection->Flags |= CLIENT_CONN_FLAG_DISCONNECT_RECEIVED;
|
|
|
|
UC_WRITE_TRACE_LOG(
|
|
g_pUcTraceLog,
|
|
UC_ACTION_CONNECTION_TDI_DISCONNECT,
|
|
pConnection,
|
|
UlongToPtr((ULONG)STATUS_CONNECTION_DISCONNECTED),
|
|
UlongToPtr(pConnection->ConnectionState),
|
|
UlongToPtr(pConnection->Flags)
|
|
);
|
|
|
|
switch(pConnection->ConnectionState)
|
|
{
|
|
case UcConnectStateConnectReady:
|
|
|
|
pConnection->ConnectionStatus = STATUS_CONNECTION_DISCONNECTED;
|
|
|
|
if(pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
//
|
|
// When we receive a graceful close, it means that the
|
|
// server has finished sending data on this connection
|
|
// & has initiated a half close. However, some of this
|
|
// received data might be stuck in the filter.
|
|
//
|
|
// Therefore, we have to wait till the filter calls us back
|
|
// in the receive handler before we cleanup this
|
|
// connection. Hence we will send the disconnect
|
|
// indication via the filter.
|
|
//
|
|
// This allows the filter routine to call us back
|
|
// (via HttpCloseFilter, which will result in a call to
|
|
// UcpCloseRawConnection) after it has indicated all the
|
|
// data.
|
|
//
|
|
// Since we are at DPC, we cannot issue this from here.
|
|
// We'll fire the connection worker to achieve this.
|
|
//
|
|
|
|
pConnection->ConnectionState =
|
|
UcConnectStateIssueFilterDisconnect;
|
|
|
|
UcKickOffConnectionStateMachine(
|
|
pConnection,
|
|
OldIrql,
|
|
UcConnectionWorkItem
|
|
);
|
|
|
|
}
|
|
else
|
|
{
|
|
pConnection->ConnectionState =
|
|
UcConnectStateDisconnectIndicatedPending;
|
|
|
|
UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
|
|
|
|
UcpCloseRawConnection( pConnection,
|
|
FALSE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
case UcConnectStateConnectComplete:
|
|
case UcConnectStatePerformingSslHandshake:
|
|
case UcConnectStateProxySslConnectComplete:
|
|
case UcConnectStateProxySslConnect:
|
|
|
|
// We were waiting for the server cert to be negotiated, but
|
|
// we got called in the disconnect handler. We'll treat this
|
|
// as a normal Disconnect.
|
|
|
|
pConnection->ConnectionStatus = STATUS_CONNECTION_DISCONNECTED;
|
|
pConnection->ConnectionState =
|
|
UcConnectStateDisconnectIndicatedPending;
|
|
|
|
UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
|
|
|
|
UcpCloseRawConnection( pConnection,
|
|
FALSE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
break;
|
|
|
|
case UcConnectStateDisconnectComplete:
|
|
|
|
//
|
|
// If we receive a graceful close in this state, we still
|
|
// need to bounce this via the filter, since we need to
|
|
// synchronize this close with the already indicated data.
|
|
// (see description above). However, when the filter calls
|
|
// us back, we must proceed directly to clean the
|
|
// connection.
|
|
//
|
|
|
|
if(pConnection->FilterInfo.pFilterChannel &&
|
|
!(pConnection->Flags & CLIENT_CONN_FLAG_FILTER_CLOSED))
|
|
{
|
|
//
|
|
// Flag it so that we will directly cleanup when we get
|
|
// called back by the filter.
|
|
//
|
|
|
|
pConnection->Flags |= CLIENT_CONN_FLAG_FILTER_CLEANUP;
|
|
|
|
pConnection->ConnectionState =
|
|
UcConnectStateIssueFilterDisconnect;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
pConnection->ConnectionState = UcConnectStateConnectCleanup;
|
|
|
|
UC_WRITE_TRACE_LOG(
|
|
g_pUcTraceLog,
|
|
UC_ACTION_CONNECTION_CLEANUP,
|
|
pConnection,
|
|
UlongToPtr((ULONG)pConnection->ConnectionStatus),
|
|
UlongToPtr(pConnection->ConnectionState),
|
|
UlongToPtr(pConnection->Flags)
|
|
);
|
|
}
|
|
|
|
UcKickOffConnectionStateMachine(
|
|
pConnection,
|
|
OldIrql,
|
|
UcConnectionWorkItem
|
|
);
|
|
|
|
break;
|
|
|
|
case UcConnectStateDisconnectPending:
|
|
|
|
// We have received a disconnect when we have sent ours,
|
|
// which is not yet complete. Flag the connection so that
|
|
// we do the cleanup when the disconnect is complete.
|
|
|
|
|
|
UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
|
|
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
end:
|
|
|
|
UL_LEAVE_DRIVER("UcpTdiDisconnectHandler");
|
|
|
|
return status;
|
|
}
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Closes a previously open connection.
|
|
|
|
Arguments:
|
|
|
|
pConnection- Supplies the connection object
|
|
|
|
AbortiveDisconnect - TRUE if the connection has to be abortively
|
|
disconnected, FALSE if it has to be gracefully
|
|
disconnected.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UcpCloseRawConnection(
|
|
IN PVOID pConn,
|
|
IN BOOLEAN AbortiveDisconnect,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
)
|
|
{
|
|
PUC_CLIENT_CONNECTION pConnection = (PUC_CLIENT_CONNECTION) pConn;
|
|
|
|
ASSERT( UC_IS_VALID_CLIENT_CONNECTION(pConnection) );
|
|
|
|
UC_WRITE_TRACE_LOG(
|
|
g_pUcTraceLog,
|
|
UC_ACTION_CONNECTION_RAW_CLOSE,
|
|
pConnection,
|
|
UlongToPtr(AbortiveDisconnect),
|
|
UlongToPtr(pConnection->Flags),
|
|
UlongToPtr(pConnection->ConnectionState)
|
|
);
|
|
|
|
//
|
|
// This is the final close handler for all types of connections
|
|
// filter, non filter. We should not go through this path twice
|
|
//
|
|
|
|
if(AbortiveDisconnect)
|
|
{
|
|
ASSERT(pConnection->ConnectionState == UcConnectStateAbortPending);
|
|
|
|
return UcpBeginAbort(
|
|
pConnection,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(pConnection->ConnectionState ==
|
|
UcConnectStateDisconnectIndicatedPending ||
|
|
pConnection->ConnectionState ==
|
|
UcConnectStateDisconnectPending);
|
|
|
|
return UcpBeginDisconnect(
|
|
pConnection,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Closes a previously open connection; called from the filter code. The
|
|
server code just uses UlpCloseRawConnection for this routine.
|
|
|
|
We need a seperate routine because we want to conditionally call
|
|
UcpCloseRawConnection based on some state.
|
|
|
|
Arguments:
|
|
|
|
pConnection- Supplies the connection object
|
|
|
|
AbortiveDisconnect - TRUE if the connection has to be abortively
|
|
disconnected, FALSE if it has to be gracefully
|
|
disconnected.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UcCloseRawFilterConnection(
|
|
IN PVOID pConn,
|
|
IN BOOLEAN AbortiveDisconnect,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
PUC_CLIENT_CONNECTION pConnection = (PUC_CLIENT_CONNECTION) pConn;
|
|
|
|
ASSERT( UC_IS_VALID_CLIENT_CONNECTION(pConnection) );
|
|
|
|
UC_WRITE_TRACE_LOG(
|
|
g_pUcTraceLog,
|
|
UC_ACTION_CONNECTION_RAW_FILTER_CLOSE,
|
|
pConnection,
|
|
UlongToPtr(AbortiveDisconnect),
|
|
UlongToPtr(pConnection->Flags),
|
|
UlongToPtr(pConnection->ConnectionState)
|
|
);
|
|
|
|
|
|
if(AbortiveDisconnect)
|
|
{
|
|
//
|
|
// This will do some state checks & land up calling
|
|
// UcpCloseRawConnection. In order to modularize the code, we just
|
|
// call UcCloseConnection.
|
|
//
|
|
|
|
return UcCloseConnection(pConnection,
|
|
AbortiveDisconnect,
|
|
pCompletionRoutine,
|
|
pCompletionContext,
|
|
STATUS_CONNECTION_ABORTED
|
|
);
|
|
}
|
|
|
|
UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
|
|
|
|
pConnection->Flags |= CLIENT_CONN_FLAG_FILTER_CLOSED;
|
|
|
|
if(pConnection->ConnectionState == UcConnectStateDisconnectPending ||
|
|
pConnection->ConnectionState == UcConnectStatePerformingSslHandshake ||
|
|
pConnection->ConnectionState == UcConnectStateIssueFilterDisconnect ||
|
|
pConnection->ConnectionState == UcConnectStateDisconnectIndicatedPending)
|
|
{
|
|
pConnection->ConnectionState = UcConnectStateDisconnectPending;
|
|
|
|
UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
|
|
|
|
//
|
|
// We've routed the disconnect via the filter. We can just proceed
|
|
// to close the raw connection.
|
|
//
|
|
|
|
return UcpCloseRawConnection(
|
|
pConnection,
|
|
AbortiveDisconnect,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
}
|
|
else
|
|
{
|
|
|
|
UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
|
|
|
|
return UlInvokeCompletionRoutine(
|
|
STATUS_SUCCESS,
|
|
0,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
The filter calls us back in this routine after it's processed the incoming
|
|
disconnet indication.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies a pointer to a connection
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UcDisconnectRawFilterConnection(
|
|
IN PVOID pConnectionContext
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
PUC_CLIENT_CONNECTION pConnection;
|
|
|
|
pConnection = (PUC_CLIENT_CONNECTION)pConnectionContext;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT(UC_IS_VALID_CLIENT_CONNECTION(pConnection));
|
|
|
|
UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
|
|
|
|
UC_WRITE_TRACE_LOG(
|
|
g_pUcTraceLog,
|
|
UC_ACTION_CONNECTION_RAW_FILTER_DISCONNECT,
|
|
pConnection,
|
|
UlongToPtr(pConnection->Flags),
|
|
UlongToPtr(pConnection->ConnectionState),
|
|
0
|
|
);
|
|
|
|
if(pConnection->ConnectionState == UcConnectStateDisconnectIndicatedPending)
|
|
{
|
|
if(pConnection->Flags & CLIENT_CONN_FLAG_FILTER_CLEANUP)
|
|
{
|
|
pConnection->ConnectionState = UcConnectStateConnectCleanup;
|
|
|
|
UcKickOffConnectionStateMachine(
|
|
pConnection,
|
|
OldIrql,
|
|
UcConnectionWorkItem
|
|
);
|
|
}
|
|
else
|
|
{
|
|
UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
|
|
|
|
UcpCloseRawConnection(
|
|
pConnection,
|
|
FALSE, // Abortive Disconnect
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Sometimes, the wierd filter calls us more than once for the
|
|
// same connection.
|
|
|
|
UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
|
|
}
|
|
|
|
} // UcDisconnectRawFilterConnection
|
|
|
|
/*********************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Sends a block of data on the specified connection.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies a pointer to a connection as previously
|
|
indicated to the PUL_CONNECTION_REQUEST handler.
|
|
|
|
pMdlChain - Supplies a pointer to a MDL chain describing the
|
|
data buffers to send.
|
|
|
|
Length - Supplies the length of the data referenced by the MDL
|
|
chain.
|
|
|
|
pIrpContext - used to indicate completion to the caller.
|
|
|
|
InitiateDisconnect - Supplies TRUE if a graceful disconnect should
|
|
be initiated immediately after initiating the send (i.e. before
|
|
the send actually completes).
|
|
|
|
--*********************************************************************/
|
|
NTSTATUS
|
|
UcpSendRawData(
|
|
IN PVOID pConnectionContext,
|
|
IN PMDL pMdlChain,
|
|
IN ULONG Length,
|
|
IN PUL_IRP_CONTEXT pIrpContext,
|
|
IN BOOLEAN InitiateDisconnect
|
|
)
|
|
{
|
|
PUC_CLIENT_CONNECTION pConnection;
|
|
NTSTATUS status;
|
|
PIRP pIrp;
|
|
BOOLEAN OwnIrpContext = TRUE;
|
|
|
|
UNREFERENCED_PARAMETER(InitiateDisconnect);
|
|
|
|
pConnection = (PUC_CLIENT_CONNECTION) pConnectionContext;
|
|
pIrp = pIrpContext->pOwnIrp;
|
|
|
|
ASSERT( UC_IS_VALID_CLIENT_CONNECTION(pConnection) );
|
|
|
|
//
|
|
// See if there is space in the IRP to handle this request.
|
|
//
|
|
|
|
if(pIrp == NULL ||
|
|
pIrp->CurrentLocation -
|
|
pConnection->pTdiObjects->ConnectionObject.pDeviceObject->StackSize < 1)
|
|
{
|
|
pIrp =
|
|
UlAllocateIrp(
|
|
pConnection->pTdiObjects->ConnectionObject.pDeviceObject->StackSize,
|
|
FALSE
|
|
);
|
|
|
|
if(!pIrp)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
goto fatal;
|
|
}
|
|
|
|
OwnIrpContext = FALSE;
|
|
}
|
|
|
|
|
|
ASSERT( pIrp );
|
|
|
|
//
|
|
// The connection is already referenced for us while the request is
|
|
// on a queue, so we don't need to do it again.
|
|
|
|
pIrp->RequestorMode = KernelMode;
|
|
// pIrp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
// pIrp->Tail.Overlay.OriginalFileObject = pTdiObject->pFileObject;
|
|
|
|
TdiBuildSend(
|
|
pIrp,
|
|
pConnection->pTdiObjects->ConnectionObject.pDeviceObject,
|
|
pConnection->pTdiObjects->ConnectionObject.pFileObject,
|
|
&UcpRestartSendData,
|
|
pIrpContext,
|
|
pMdlChain,
|
|
0,
|
|
Length
|
|
);
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pMdlTraceLog,
|
|
REF_ACTION_SEND_MDL,
|
|
PtrToLong(pMdlChain->Next), // bugbug64
|
|
pMdlChain,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
|
|
//
|
|
// Submit the IRP.
|
|
// UC_BUGBUG (PERF) UL does this thing called fast send, check later.
|
|
//
|
|
|
|
UlCallDriver(
|
|
pConnection->pTdiObjects->ConnectionObject.pDeviceObject,
|
|
pIrp
|
|
);
|
|
|
|
return STATUS_PENDING;
|
|
|
|
fatal:
|
|
|
|
ASSERT(!NT_SUCCESS(status));
|
|
|
|
if(pIrp != NULL && OwnIrpContext == FALSE)
|
|
{
|
|
UlFreeIrp(pIrp);
|
|
}
|
|
|
|
UC_CLOSE_CONNECTION(pConnection, TRUE, status);
|
|
|
|
return status;
|
|
}
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Receives data from the specified connection. This function is
|
|
typically used after a receive indication handler has failed to
|
|
consume all of the indicated data.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies a pointer to a connection as previously
|
|
indicated to the PUL_CONNECTION_REQUEST handler.
|
|
|
|
pBuffer - Supplies a pointer to the target buffer for the received
|
|
data.
|
|
|
|
BufferLength - Supplies the length of pBuffer.
|
|
|
|
pCompletionRoutine - Supplies a pointer to a completion routine to
|
|
invoke after the listening endpoint is fully closed.
|
|
|
|
pCompletionContext - Supplies an uninterpreted context value for the
|
|
completion routine.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UcpReceiveRawData(
|
|
IN PVOID pConnectionContext,
|
|
IN PVOID pBuffer,
|
|
IN ULONG BufferLength,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PUX_TDI_OBJECT pTdiObject;
|
|
PUL_IRP_CONTEXT pIrpContext;
|
|
PIRP pIrp;
|
|
PMDL pMdl;
|
|
PUC_CLIENT_CONNECTION pConnection;
|
|
|
|
pConnection = (PUC_CLIENT_CONNECTION) pConnectionContext;
|
|
|
|
ASSERT(UC_IS_VALID_CLIENT_CONNECTION(pConnection));
|
|
|
|
pTdiObject = &pConnection->pTdiObjects->ConnectionObject;
|
|
ASSERT( IS_VALID_TDI_OBJECT( pTdiObject ) );
|
|
|
|
ASSERT( pCompletionRoutine != NULL );
|
|
|
|
//
|
|
// Setup locals so we know how to cleanup on failure.
|
|
//
|
|
|
|
pIrpContext = NULL;
|
|
pIrp = NULL;
|
|
pMdl = NULL;
|
|
|
|
//
|
|
// Create & initialize a receive IRP.
|
|
//
|
|
|
|
pIrp = UlAllocateIrp(
|
|
pTdiObject->pDeviceObject->StackSize, // StackSize
|
|
FALSE // ChargeQuota
|
|
);
|
|
|
|
if (pIrp != NULL)
|
|
{
|
|
//
|
|
// Snag an IRP context.
|
|
//
|
|
|
|
pIrpContext = UlPplAllocateIrpContext();
|
|
|
|
if (pIrpContext != NULL)
|
|
{
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
|
|
pIrpContext->pConnectionContext = (PVOID)pConnection;
|
|
pIrpContext->pCompletionRoutine = pCompletionRoutine;
|
|
pIrpContext->pCompletionContext = pCompletionContext;
|
|
pIrpContext->OwnIrpContext = FALSE;
|
|
|
|
//
|
|
// Create an MDL describing the client's buffer.
|
|
//
|
|
|
|
pMdl = UlAllocateMdl(
|
|
pBuffer, // VirtualAddress
|
|
BufferLength, // Length
|
|
FALSE, // SecondaryBuffer
|
|
FALSE, // ChargeQuota
|
|
NULL // Irp
|
|
);
|
|
|
|
if (pMdl != NULL)
|
|
{
|
|
//
|
|
// Adjust the MDL for our non-paged buffer.
|
|
//
|
|
|
|
MmBuildMdlForNonPagedPool( pMdl );
|
|
|
|
//
|
|
// Reference the connection, finish building the IRP.
|
|
//
|
|
|
|
REFERENCE_CLIENT_CONNECTION( pConnection );
|
|
|
|
TdiBuildReceive(
|
|
pIrp, // Irp
|
|
pTdiObject->pDeviceObject, // DeviceObject
|
|
pTdiObject->pFileObject, // FileObject
|
|
&UcpRestartClientReceive, // CompletionRoutine
|
|
pIrpContext, // CompletionContext
|
|
pMdl, // Mdl
|
|
TDI_RECEIVE_NORMAL, // Flags
|
|
BufferLength // Length
|
|
);
|
|
|
|
//
|
|
// Let the transport do the rest.
|
|
//
|
|
|
|
UlCallDriver( pTdiObject->pDeviceObject, pIrp );
|
|
return STATUS_PENDING;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We only make it this point if we hit an allocation failure.
|
|
//
|
|
|
|
if (pMdl != NULL)
|
|
{
|
|
UlFreeMdl( pMdl );
|
|
}
|
|
|
|
if (pIrpContext != NULL)
|
|
{
|
|
UlPplFreeIrpContext( pIrpContext );
|
|
}
|
|
|
|
if (pIrp != NULL)
|
|
{
|
|
UlFreeIrp( pIrp );
|
|
}
|
|
|
|
Status = UlInvokeCompletionRoutine(
|
|
STATUS_INSUFFICIENT_RESOURCES,
|
|
0,
|
|
pCompletionRoutine,
|
|
pCompletionContext
|
|
);
|
|
|
|
return Status;
|
|
|
|
} // UcpReceiveRawData
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Handler for normal receive data.
|
|
|
|
Arguments:
|
|
|
|
pTdiEventContext - Supplies the context associated with the address
|
|
object. This should be a PUL_ENDPOINT.
|
|
|
|
ConnectionContext - Supplies the context associated with the
|
|
connection object. This should be a PUC_CONNECTION.
|
|
|
|
ReceiveFlags - Supplies the receive flags. This will be zero or more
|
|
TDI_RECEIVE_* flags.
|
|
|
|
BytesIndicated - Supplies the number of bytes indicated in pTsdu.
|
|
|
|
BytesAvailable - Supplies the number of bytes available in this
|
|
TSDU.
|
|
|
|
pBytesTaken - Receives the number of bytes consumed by this handler.
|
|
|
|
pTsdu - Supplies a pointer to the indicated data.
|
|
|
|
pIrp - Receives an IRP if the handler needs more data than indicated.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UcpTdiReceiveHandler(
|
|
IN PVOID pTdiEventContext,
|
|
IN CONNECTION_CONTEXT ConnectionContext,
|
|
IN ULONG ReceiveFlags,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesAvailable,
|
|
OUT ULONG *pBytesTaken,
|
|
IN PVOID pTsdu,
|
|
OUT PIRP *pIrp
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PUC_TDI_OBJECTS pTdiObjects;
|
|
PUC_CLIENT_CONNECTION pConnection;
|
|
PUX_TDI_OBJECT pTdiObject;
|
|
KIRQL OldIrql;
|
|
|
|
UNREFERENCED_PARAMETER(ReceiveFlags);
|
|
UNREFERENCED_PARAMETER(pTdiEventContext);
|
|
|
|
UL_ENTER_DRIVER("UcpTdiReceiveHandler", NULL);
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pTdiObjects = (PUC_TDI_OBJECTS) ConnectionContext;
|
|
|
|
pConnection = pTdiObjects->pConnection;
|
|
ASSERT( UC_IS_VALID_CLIENT_CONNECTION( pConnection ) );
|
|
|
|
pTdiObject = &pConnection->pTdiObjects->ConnectionObject;
|
|
|
|
ASSERT( IS_VALID_TDI_OBJECT( pTdiObject ) );
|
|
|
|
//
|
|
// Clear the bytes taken output var
|
|
//
|
|
|
|
*pBytesTaken = 0;
|
|
|
|
if(pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
if(pConnection->ConnectionState ==
|
|
UcConnectStateConnectReady ||
|
|
pConnection->ConnectionState ==
|
|
UcConnectStatePerformingSslHandshake
|
|
)
|
|
{
|
|
//
|
|
// Needs to go through a filter.
|
|
//
|
|
|
|
status = UlFilterReceiveHandler(
|
|
&pConnection->FilterInfo,
|
|
pTsdu,
|
|
BytesIndicated,
|
|
BytesAvailable - BytesIndicated,
|
|
pBytesTaken
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// We have not delivered the connection to the filter as yet.
|
|
// Let's first do that with the state transistion & then pass the
|
|
// data on.
|
|
|
|
UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
|
|
|
|
switch(pConnection->ConnectionState)
|
|
{
|
|
case UcConnectStateConnectComplete:
|
|
{
|
|
ULONG TakenLength;
|
|
|
|
pConnection->ConnectionState =
|
|
UcConnectStatePerformingSslHandshake;
|
|
|
|
UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
|
|
|
|
UlDeliverConnectionToFilter(
|
|
&pConnection->FilterInfo,
|
|
NULL,
|
|
0,
|
|
&TakenLength
|
|
);
|
|
|
|
ASSERT(TakenLength == 0);
|
|
|
|
status = UlFilterReceiveHandler(
|
|
&pConnection->FilterInfo,
|
|
pTsdu,
|
|
BytesIndicated,
|
|
BytesAvailable - BytesIndicated,
|
|
pBytesTaken
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
case UcConnectStateProxySslConnect:
|
|
UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
|
|
goto handle_response;
|
|
break;
|
|
|
|
default:
|
|
UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
|
|
status = UlFilterReceiveHandler(
|
|
&pConnection->FilterInfo,
|
|
pTsdu,
|
|
BytesIndicated,
|
|
BytesAvailable - BytesIndicated,
|
|
pBytesTaken
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT( *pBytesTaken <= BytesIndicated);
|
|
ASSERT( status != STATUS_MORE_PROCESSING_REQUIRED);
|
|
}
|
|
else
|
|
{
|
|
handle_response:
|
|
if(BytesAvailable > BytesIndicated)
|
|
{
|
|
status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// otherwise, give the client a crack at the data.
|
|
//
|
|
|
|
status = UcHandleResponse(
|
|
NULL,
|
|
pConnection,
|
|
pTsdu,
|
|
BytesIndicated,
|
|
0,
|
|
pBytesTaken
|
|
);
|
|
|
|
ASSERT( status != STATUS_MORE_PROCESSING_REQUIRED);
|
|
}
|
|
}
|
|
|
|
if (status == STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// done.
|
|
//
|
|
}
|
|
else if (status == STATUS_MORE_PROCESSING_REQUIRED)
|
|
{
|
|
//
|
|
// The client consumed part of the indicated data.
|
|
//
|
|
// A subsequent receive indication will be made to the client when
|
|
// additional data is available. This subsequent indication will
|
|
// include the unconsumed data from the current indication plus
|
|
// any additional data received.
|
|
//
|
|
// We need to allocate a receive buffer so we can pass an IRP back
|
|
// to the transport.
|
|
//
|
|
|
|
status = UcpBuildTdiReceiveBuffer(pTdiObject,
|
|
pConnection,
|
|
pIrp
|
|
);
|
|
|
|
if(status == STATUS_MORE_PROCESSING_REQUIRED)
|
|
{
|
|
//
|
|
// Make the next stack location current. Normally, UlCallDriver
|
|
// would do this for us, but since we're bypassing UlCallDriver,
|
|
// we must do it ourselves.
|
|
//
|
|
|
|
IoSetNextIrpStackLocation( *pIrp );
|
|
|
|
}
|
|
else
|
|
{
|
|
goto fatal;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fatal:
|
|
//
|
|
// If we made it this far, then we've hit a fatal condition. Either the
|
|
// client returned a status code other than STATUS_SUCCESS or
|
|
// STATUS_MORE_PROCESSING_REQUIRED, or we failed to allocation the
|
|
// receive IRP to pass back to the transport. In either case, we need
|
|
// to abort the connection.
|
|
//
|
|
|
|
UC_CLOSE_CONNECTION(pConnection, TRUE, status);
|
|
}
|
|
|
|
UL_LEAVE_DRIVER("UcpTdiReceiveHandler");
|
|
|
|
return status;
|
|
|
|
} // UcpTdiReceiveHandler
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Handler for expedited receive data.
|
|
|
|
Arguments:
|
|
|
|
pTdiEventContext - Supplies the context associated with the address
|
|
object. This should be a PUL_ENDPOINT.
|
|
|
|
ConnectionContext - Supplies the context associated with the
|
|
connection object. This should be a PUL_CONNECTION.
|
|
|
|
ReceiveFlags - Supplies the receive flags. This will be zero or more
|
|
TDI_RECEIVE_* flags.
|
|
|
|
BytesIndiated - Supplies the number of bytes indicated in pTsdu.
|
|
|
|
BytesAvailable - Supplies the number of bytes available in this
|
|
TSDU.
|
|
|
|
pBytesTaken - Receives the number of bytes consumed by this handler.
|
|
|
|
pTsdu - Supplies a pointer to the indicated data.
|
|
|
|
pIrp - Receives an IRP if the handler needs more data than indicated.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UcpReceiveExpeditedHandler(
|
|
IN PVOID pTdiEventContext,
|
|
IN CONNECTION_CONTEXT ConnectionContext,
|
|
IN ULONG ReceiveFlags,
|
|
IN ULONG BytesIndicated,
|
|
IN ULONG BytesAvailable,
|
|
OUT ULONG *pBytesTaken,
|
|
IN PVOID pTsdu,
|
|
OUT PIRP *pIrp
|
|
)
|
|
{
|
|
PUC_CLIENT_CONNECTION pConnection;
|
|
|
|
UNREFERENCED_PARAMETER(pIrp);
|
|
UNREFERENCED_PARAMETER(pTsdu);
|
|
UNREFERENCED_PARAMETER(BytesIndicated);
|
|
UNREFERENCED_PARAMETER(ReceiveFlags);
|
|
UNREFERENCED_PARAMETER(pTdiEventContext);
|
|
|
|
UL_ENTER_DRIVER("UcpReceiveExpeditedHandler", NULL);
|
|
|
|
pConnection = (PUC_CLIENT_CONNECTION)ConnectionContext;
|
|
|
|
ASSERT( UC_IS_VALID_CLIENT_CONNECTION(pConnection) );
|
|
|
|
//
|
|
// We don't support expedited data, so just consume it all.
|
|
//
|
|
*pBytesTaken = BytesAvailable;
|
|
|
|
UL_LEAVE_DRIVER("UcpReceiveExpeditedHandler");
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Completion handler for send IRPs.
|
|
|
|
Arguments:
|
|
|
|
pDeviceObject - Supplies the device object for the IRP being
|
|
completed.
|
|
|
|
pIrp - Supplies the IRP being completed.
|
|
|
|
pContext - Supplies the context associated with this request.
|
|
This is actually a PUL_IRP_CONTEXT.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS if IO should continue processing this
|
|
IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
|
|
this IRP.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UcpRestartSendData(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
)
|
|
{
|
|
PUC_CLIENT_CONNECTION pConnection;
|
|
PUL_IRP_CONTEXT pIrpContext;
|
|
BOOLEAN OwnIrpContext;
|
|
|
|
UNREFERENCED_PARAMETER(pDeviceObject);
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pIrpContext = (PUL_IRP_CONTEXT) pContext;
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
ASSERT( pIrpContext->pCompletionRoutine != NULL );
|
|
|
|
pConnection = (PUC_CLIENT_CONNECTION) pIrpContext->pConnectionContext;
|
|
|
|
ASSERT( UC_IS_VALID_CLIENT_CONNECTION( pConnection ) );
|
|
|
|
OwnIrpContext = (BOOLEAN)(pIrpContext->pOwnIrp == NULL);
|
|
|
|
//
|
|
// Tell the client that the send is complete.
|
|
//
|
|
|
|
(pIrpContext->pCompletionRoutine)(
|
|
pIrpContext->pCompletionContext,
|
|
pIrp->IoStatus.Status,
|
|
pIrp->IoStatus.Information
|
|
);
|
|
|
|
//
|
|
// Free the context & the IRP since we're done with them, then
|
|
// tell IO to stop processing the IRP.
|
|
//
|
|
|
|
UlPplFreeIrpContext( pIrpContext );
|
|
|
|
if(OwnIrpContext)
|
|
{
|
|
UlFreeIrp( pIrp );
|
|
}
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} // UcpRestartSendData
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Initiates a graceful disconnect on the specified connection.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies the connection to disconnect.
|
|
|
|
pCompletionRoutine - Supplies a pointer to a completion routine to
|
|
invoke after the connection is disconnected.
|
|
|
|
pCompletionContext - Supplies an uninterpreted context value for the
|
|
completion routine.
|
|
|
|
CleaningUp - TRUE if we're cleaning up the connection.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UcpBeginDisconnect(
|
|
IN PUC_CLIENT_CONNECTION pConnection,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
)
|
|
{
|
|
PIRP pIrp;
|
|
PUL_IRP_CONTEXT pIrpContext;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( UC_IS_VALID_CLIENT_CONNECTION( pConnection ) );
|
|
|
|
UC_WRITE_TRACE_LOG(
|
|
g_pUcTraceLog,
|
|
UC_ACTION_CONNECTION_BEGIN_DISCONNECT,
|
|
pConnection,
|
|
0,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
pIrpContext = &pConnection->pTdiObjects->IrpContext;
|
|
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
|
|
pIrpContext->pConnectionContext = (PVOID)pConnection;
|
|
pIrpContext->pCompletionRoutine = pCompletionRoutine;
|
|
pIrpContext->pCompletionContext = pCompletionContext;
|
|
pIrpContext->OwnIrpContext = TRUE;
|
|
|
|
pIrp = pConnection->pTdiObjects->pIrp;
|
|
|
|
UxInitializeDisconnectIrp(
|
|
pIrp,
|
|
&pConnection->pTdiObjects->ConnectionObject,
|
|
TDI_DISCONNECT_RELEASE,
|
|
&UcpRestartDisconnect,
|
|
pIrpContext
|
|
);
|
|
|
|
//
|
|
// Add a reference to the connection, then call the driver to initiate
|
|
// the disconnect.
|
|
//
|
|
|
|
REFERENCE_CLIENT_CONNECTION( pConnection );
|
|
|
|
UlCallDriver(
|
|
pConnection->pTdiObjects->ConnectionObject.pDeviceObject,
|
|
pIrp
|
|
);
|
|
|
|
return STATUS_PENDING;
|
|
|
|
} // BeginDisconnect
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Completion handler for graceful disconnect IRPs.
|
|
|
|
Arguments:
|
|
|
|
pDeviceObject - Supplies the device object for the IRP being
|
|
completed.
|
|
|
|
pIrp - Supplies the IRP being completed.
|
|
|
|
pContext - Supplies the context associated with this request.
|
|
This is actually a PUL_IRP_CONTEXT.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS if IO should continue processing this
|
|
IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
|
|
this IRP.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UcpRestartDisconnect(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
)
|
|
{
|
|
PUL_IRP_CONTEXT pIrpContext;
|
|
PUC_CLIENT_CONNECTION pConnection;
|
|
KIRQL OldIrql;
|
|
NTSTATUS Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
NTSTATUS IrpStatus;
|
|
ULONG_PTR IrpInformation;
|
|
PUL_COMPLETION_ROUTINE pCompletionRoutine;
|
|
PVOID pCompletionContext;
|
|
|
|
UNREFERENCED_PARAMETER(pDeviceObject);
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pIrpContext = (PUL_IRP_CONTEXT) pContext;
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
|
|
pConnection = (PUC_CLIENT_CONNECTION) pIrpContext->pConnectionContext;
|
|
ASSERT( UC_IS_VALID_CLIENT_CONNECTION( pConnection ) );
|
|
|
|
UC_WRITE_TRACE_LOG(
|
|
g_pUcTraceLog,
|
|
UC_ACTION_CONNECTION_RESTART_DISCONNECT,
|
|
pConnection,
|
|
0,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
//
|
|
// Remember the completion routine, completion context, Irp status,
|
|
// Irp information fields before calling the connection state machine.
|
|
// This is done because the connection state machine might change/free
|
|
// them.
|
|
//
|
|
|
|
pCompletionRoutine = pIrpContext->pCompletionRoutine;
|
|
pCompletionContext = pIrpContext->pCompletionContext;
|
|
IrpStatus = pIrp->IoStatus.Status;
|
|
IrpInformation = pIrp->IoStatus.Information;
|
|
|
|
UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
|
|
|
|
pConnection->Flags |= CLIENT_CONN_FLAG_DISCONNECT_COMPLETE;
|
|
|
|
if(pConnection->Flags & CLIENT_CONN_FLAG_ABORT_RECEIVED)
|
|
{
|
|
pConnection->ConnectionState = UcConnectStateConnectCleanup;
|
|
|
|
UC_WRITE_TRACE_LOG(
|
|
g_pUcTraceLog,
|
|
UC_ACTION_CONNECTION_CLEANUP,
|
|
pConnection,
|
|
UlongToPtr((ULONG)pConnection->ConnectionStatus),
|
|
UlongToPtr(pConnection->ConnectionState),
|
|
UlongToPtr(pConnection->Flags)
|
|
);
|
|
|
|
|
|
UcKickOffConnectionStateMachine(
|
|
pConnection,
|
|
OldIrql,
|
|
UcConnectionWorkItem
|
|
);
|
|
}
|
|
else if(pConnection->Flags & CLIENT_CONN_FLAG_ABORT_PENDING)
|
|
{
|
|
pConnection->ConnectionState = UcConnectStateAbortPending;
|
|
|
|
UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
|
|
|
|
UcpBeginAbort(pConnection,
|
|
pIrpContext->pCompletionRoutine,
|
|
pIrpContext->pCompletionContext
|
|
);
|
|
|
|
//
|
|
// Don't complete the user's completion routine below, as it will
|
|
// be handled when the Abort completes.
|
|
//
|
|
|
|
DEREFERENCE_CLIENT_CONNECTION( pConnection );
|
|
|
|
return Status;
|
|
|
|
}
|
|
else if(pConnection->Flags & CLIENT_CONN_FLAG_DISCONNECT_RECEIVED ||
|
|
pConnection->ConnectionState ==
|
|
UcConnectStateDisconnectIndicatedPending)
|
|
{
|
|
pConnection->ConnectionState = UcConnectStateConnectCleanup;
|
|
UC_WRITE_TRACE_LOG(
|
|
g_pUcTraceLog,
|
|
UC_ACTION_CONNECTION_CLEANUP,
|
|
pConnection,
|
|
UlongToPtr((ULONG)pConnection->ConnectionStatus),
|
|
UlongToPtr(pConnection->ConnectionState),
|
|
UlongToPtr(pConnection->Flags)
|
|
);
|
|
|
|
|
|
UcKickOffConnectionStateMachine(
|
|
pConnection,
|
|
OldIrql,
|
|
UcConnectionWorkItem
|
|
);
|
|
}
|
|
else
|
|
{
|
|
pConnection->ConnectionState = UcConnectStateDisconnectComplete;
|
|
|
|
UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
|
|
}
|
|
|
|
#if 0
|
|
if(!newFlags.DisconnectIndicated && !newFlags.AbortIndicated)
|
|
{
|
|
//
|
|
// Only try to drain if it is not already aborted or disconnect
|
|
// indication is not already happened.
|
|
//
|
|
|
|
if (pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
//
|
|
// Put a reference on filter connection until the drain
|
|
// is done.
|
|
//
|
|
REFERENCE_FILTER_CONNECTION(&pConnection->FilterInfo);
|
|
|
|
UL_QUEUE_WORK_ITEM(
|
|
&pConnection->FilterInfo.WorkItem,
|
|
&UlFilterDrainIndicatedData
|
|
);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Invoke the user's completion routine.
|
|
//
|
|
|
|
if (pCompletionRoutine)
|
|
{
|
|
pCompletionRoutine(pCompletionContext, IrpStatus, IrpInformation);
|
|
}
|
|
|
|
//
|
|
// The connection was referenced in BeginDisconnect function.
|
|
// Deference it.
|
|
//
|
|
|
|
DEREFERENCE_CLIENT_CONNECTION( pConnection );
|
|
|
|
return Status;
|
|
|
|
} // UcpRestartDisconnect
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Initiates an abortive disconnect on the specified connection.
|
|
|
|
Arguments:
|
|
|
|
pConnection - Supplies the connection to disconnect.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UcpBeginAbort(
|
|
IN PUC_CLIENT_CONNECTION pConnection,
|
|
IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
|
|
IN PVOID pCompletionContext
|
|
)
|
|
{
|
|
PIRP pIrp;
|
|
PUL_IRP_CONTEXT pIrpContext;
|
|
|
|
ASSERT( UC_IS_VALID_CLIENT_CONNECTION(pConnection) );
|
|
|
|
UC_WRITE_TRACE_LOG(
|
|
g_pUcTraceLog,
|
|
UC_ACTION_CONNECTION_BEGIN_ABORT,
|
|
pConnection,
|
|
0,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
pIrpContext = &pConnection->pTdiObjects->IrpContext;
|
|
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
|
|
pIrpContext->pConnectionContext = (PVOID)pConnection;
|
|
pIrpContext->pCompletionRoutine = pCompletionRoutine;
|
|
pIrpContext->pCompletionContext = pCompletionContext;
|
|
pIrpContext->OwnIrpContext = TRUE;
|
|
|
|
pIrp = pConnection->pTdiObjects->pIrp;
|
|
|
|
UxInitializeDisconnectIrp(
|
|
pIrp,
|
|
&pConnection->pTdiObjects->ConnectionObject,
|
|
TDI_DISCONNECT_ABORT,
|
|
&UcpRestartAbort,
|
|
pIrpContext
|
|
);
|
|
|
|
//
|
|
// Add a reference to the connection, then call the driver to initialize
|
|
// the disconnect.
|
|
//
|
|
|
|
REFERENCE_CLIENT_CONNECTION(pConnection);
|
|
|
|
UlCallDriver(
|
|
pConnection->pTdiObjects->ConnectionObject.pDeviceObject,
|
|
pIrp
|
|
);
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Completion handler for abortive disconnect IRPs.
|
|
|
|
Arguments:
|
|
|
|
pDeviceObject - Supplies the device object for the IRP being
|
|
completed.
|
|
|
|
pIrp - Supplies the IRP being completed.
|
|
|
|
pContext - Supplies the context associated with this request.
|
|
This is actually a PUL_IRP_CONTEXT.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS if IO should continue processing this
|
|
IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
|
|
this IRP.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UcpRestartAbort(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
)
|
|
{
|
|
PUL_IRP_CONTEXT pIrpContext;
|
|
PUC_CLIENT_CONNECTION pConnection;
|
|
KIRQL OldIrql;
|
|
|
|
PUL_COMPLETION_ROUTINE pCompletionRoutine;
|
|
PVOID pCompletionContext;
|
|
NTSTATUS IrpStatus;
|
|
ULONG_PTR IrpInformation;
|
|
|
|
UNREFERENCED_PARAMETER(pDeviceObject);
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pIrpContext = (PUL_IRP_CONTEXT)pContext;
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
|
|
pConnection = (PUC_CLIENT_CONNECTION)pIrpContext->pConnectionContext;
|
|
ASSERT( UC_IS_VALID_CLIENT_CONNECTION( pConnection ) );
|
|
|
|
UC_WRITE_TRACE_LOG(
|
|
g_pUcTraceLog,
|
|
UC_ACTION_CONNECTION_RESTART_ABORT,
|
|
pConnection,
|
|
0,
|
|
0,
|
|
0
|
|
);
|
|
|
|
//
|
|
// Remember the completion routine, completion context, Irp status,
|
|
// Irp information fields before calling the connection state machine.
|
|
// This is done because the connection state machine might change/free
|
|
// them.
|
|
//
|
|
|
|
pCompletionRoutine = pIrpContext->pCompletionRoutine;
|
|
pCompletionContext = pIrpContext->pCompletionContext;
|
|
IrpStatus = pIrp->IoStatus.Status;
|
|
IrpInformation = pIrp->IoStatus.Information;
|
|
|
|
UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
|
|
|
|
pConnection->Flags |= CLIENT_CONN_FLAG_ABORT_COMPLETE;
|
|
|
|
pConnection->ConnectionState = UcConnectStateConnectCleanup;
|
|
|
|
UC_WRITE_TRACE_LOG(
|
|
g_pUcTraceLog,
|
|
UC_ACTION_CONNECTION_CLEANUP,
|
|
pConnection,
|
|
UlongToPtr((ULONG)pConnection->ConnectionStatus),
|
|
UlongToPtr(pConnection->ConnectionState),
|
|
UlongToPtr(pConnection->Flags)
|
|
);
|
|
|
|
UcKickOffConnectionStateMachine(
|
|
pConnection,
|
|
OldIrql,
|
|
UcConnectionWorkItem
|
|
);
|
|
|
|
//
|
|
// Invoke the user's completion routine.
|
|
//
|
|
|
|
if (pCompletionRoutine)
|
|
{
|
|
pCompletionRoutine(pCompletionContext, IrpStatus, IrpInformation);
|
|
}
|
|
|
|
//
|
|
// The connection was referenced in BeginAbort. Dereference it.
|
|
//
|
|
|
|
DEREFERENCE_CLIENT_CONNECTION( pConnection );
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} // UcpRestartAbort
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Completion handler for receive IRPs passed back to the transport from
|
|
our receive indication handler.
|
|
|
|
Arguments:
|
|
|
|
pDeviceObject - Supplies the device object for the IRP being
|
|
completed.
|
|
|
|
pIrp - Supplies the IRP being completed.
|
|
|
|
pContext - Supplies the context associated with this request.
|
|
This is actually a PUL_RECEIVE_BUFFER.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS if IO should continue processing this
|
|
IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
|
|
this IRP.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UcpRestartReceive(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PUL_RECEIVE_BUFFER pBuffer;
|
|
PUC_CLIENT_CONNECTION pConnection;
|
|
PUX_TDI_OBJECT pTdiObject;
|
|
ULONG bytesTaken;
|
|
ULONG bytesRemaining;
|
|
|
|
UNREFERENCED_PARAMETER(pDeviceObject);
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pBuffer = (PUL_RECEIVE_BUFFER)pContext;
|
|
ASSERT( IS_VALID_RECEIVE_BUFFER( pBuffer ) );
|
|
|
|
pConnection = (PUC_CLIENT_CONNECTION) pBuffer->pConnectionContext;
|
|
ASSERT( UC_IS_VALID_CLIENT_CONNECTION( pConnection ) );
|
|
|
|
pTdiObject = &pConnection->pTdiObjects->ConnectionObject;
|
|
ASSERT( IS_VALID_TDI_OBJECT( pTdiObject ) );
|
|
|
|
// The connection could be destroyed before we get a chance to
|
|
// receive the completion for the receive IRP. In that case the
|
|
// irp status won't be success but STATUS_CONNECTION_RESET or similar.
|
|
// We should not attempt to pass this case to the client.
|
|
//
|
|
|
|
status = pBuffer->pIrp->IoStatus.Status;
|
|
|
|
if(status != STATUS_SUCCESS)
|
|
{
|
|
// The HttpConnection has already been destroyed
|
|
// or receive completion failed for some reason
|
|
// no need to go to client
|
|
|
|
goto end;
|
|
}
|
|
|
|
//
|
|
// Fake a receive indication to the client.
|
|
//
|
|
|
|
pBuffer->UnreadDataLength += (ULONG)pBuffer->pIrp->IoStatus.Information;
|
|
|
|
bytesTaken = 0;
|
|
|
|
//
|
|
// Pass the data on.
|
|
//
|
|
|
|
if (pConnection->FilterInfo.pFilterChannel)
|
|
{
|
|
//
|
|
// Needs to go through a filter.
|
|
//
|
|
status = UlFilterReceiveHandler(
|
|
&pConnection->FilterInfo,
|
|
pBuffer->pDataArea,
|
|
pBuffer->UnreadDataLength,
|
|
0,
|
|
&bytesTaken
|
|
);
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Go directly to client.
|
|
//
|
|
|
|
status = UcHandleResponse(
|
|
NULL,
|
|
pConnection,
|
|
pBuffer->pDataArea,
|
|
pBuffer->UnreadDataLength,
|
|
0,
|
|
&bytesTaken
|
|
);
|
|
}
|
|
|
|
ASSERT( bytesTaken <= pBuffer->UnreadDataLength );
|
|
ASSERT(status != STATUS_MORE_PROCESSING_REQUIRED);
|
|
|
|
//
|
|
// Note that this basically duplicates the logic that's currently in
|
|
// UcpTdiReceiveHandler.
|
|
//
|
|
|
|
if(status == STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// The client consumed part of the indicated data.
|
|
//
|
|
// We'll need to copy the untaken data forward within the receive
|
|
// buffer, build an MDL describing the remaining part of the buffer,
|
|
// then repost the receive IRP.
|
|
//
|
|
|
|
bytesRemaining = pBuffer->UnreadDataLength - bytesTaken;
|
|
|
|
if(bytesRemaining != 0)
|
|
{
|
|
//
|
|
// Do we have enough buffer space for more?
|
|
//
|
|
|
|
if (bytesRemaining < g_UlReceiveBufferSize)
|
|
{
|
|
//
|
|
// Move the unread portion of the buffer to the beginning.
|
|
//
|
|
|
|
RtlMoveMemory(
|
|
pBuffer->pDataArea,
|
|
(PUCHAR)pBuffer->pDataArea + bytesTaken,
|
|
bytesRemaining
|
|
);
|
|
|
|
pBuffer->UnreadDataLength = bytesRemaining;
|
|
|
|
//
|
|
// Build a partial mdl representing the remainder of the
|
|
// buffer.
|
|
//
|
|
|
|
IoBuildPartialMdl(
|
|
pBuffer->pMdl, // SourceMdl
|
|
pBuffer->pPartialMdl, // TargetMdl
|
|
(PUCHAR)pBuffer->pDataArea + bytesRemaining,// VA
|
|
g_UlReceiveBufferSize - bytesRemaining // Length
|
|
);
|
|
|
|
//
|
|
// Finish initializing the IRP.
|
|
//
|
|
|
|
TdiBuildReceive(
|
|
pBuffer->pIrp, // Irp
|
|
pTdiObject->pDeviceObject, // DeviceObject
|
|
pTdiObject->pFileObject, // FileObject
|
|
&UcpRestartReceive, // CompletionRoutine
|
|
pBuffer, // CompletionContext
|
|
pBuffer->pPartialMdl, // MdlAddress
|
|
TDI_RECEIVE_NORMAL, // Flags
|
|
g_UlReceiveBufferSize - bytesRemaining // Length
|
|
);
|
|
|
|
//
|
|
// Call the driver.
|
|
//
|
|
|
|
UlCallDriver(
|
|
pConnection->pTdiObjects->ConnectionObject.pDeviceObject,
|
|
pIrp
|
|
);
|
|
|
|
//
|
|
// Tell IO to stop processing this request.
|
|
//
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
}
|
|
}
|
|
|
|
end:
|
|
if (status != STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// The client failed the indication. Abort the connection.
|
|
//
|
|
|
|
UC_CLOSE_CONNECTION(pConnection, TRUE, status);
|
|
}
|
|
|
|
if (pTdiObject->pDeviceObject->StackSize > DEFAULT_IRP_STACK_SIZE)
|
|
{
|
|
UlFreeReceiveBufferPool( pBuffer );
|
|
}
|
|
else
|
|
{
|
|
UlPplFreeReceiveBuffer( pBuffer );
|
|
}
|
|
|
|
//
|
|
// Remove the connection we added in the receive indication handler,
|
|
// free the receive buffer, then tell IO to stop processing the IRP.
|
|
//
|
|
|
|
DEREFERENCE_CLIENT_CONNECTION( pConnection );
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} // UcpRestartReceive
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Completion handler for receive IRPs initiated from UcReceiveData().
|
|
|
|
Arguments:
|
|
|
|
pDeviceObject - Supplies the device object for the IRP being
|
|
completed.
|
|
|
|
pIrp - Supplies the IRP being completed.
|
|
|
|
pContext - Supplies the context associated with this request.
|
|
This is actually a PUL_IRP_CONTEXT.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS if IO should continue processing this
|
|
IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
|
|
this IRP.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UcpRestartClientReceive(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PVOID pContext
|
|
)
|
|
{
|
|
PUL_IRP_CONTEXT pIrpContext;
|
|
PUC_CLIENT_CONNECTION pConnection;
|
|
|
|
UNREFERENCED_PARAMETER(pDeviceObject);
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pIrpContext= (PUL_IRP_CONTEXT)pContext;
|
|
ASSERT( IS_VALID_IRP_CONTEXT( pIrpContext ) );
|
|
|
|
pConnection = (PUC_CLIENT_CONNECTION)pIrpContext->pConnectionContext;
|
|
ASSERT( UC_IS_VALID_CLIENT_CONNECTION( pConnection ) );
|
|
|
|
//
|
|
// Invoke the client's completion handler.
|
|
//
|
|
|
|
(pIrpContext->pCompletionRoutine)(
|
|
pIrpContext->pCompletionContext,
|
|
pIrp->IoStatus.Status,
|
|
pIrp->IoStatus.Information
|
|
);
|
|
|
|
//
|
|
// Free the IRP context we allocated.
|
|
//
|
|
UlPplFreeIrpContext(pIrpContext);
|
|
|
|
//
|
|
// IO can't handle completing an IRP with a non-paged MDL attached
|
|
// to it, so we'll free the MDL here.
|
|
//
|
|
|
|
ASSERT( pIrp->MdlAddress != NULL );
|
|
UlFreeMdl( pIrp->MdlAddress );
|
|
pIrp->MdlAddress = NULL;
|
|
|
|
//
|
|
// Remove the connection we added in UcReceiveData(), then tell IO to
|
|
// continue processing this IRP.
|
|
//
|
|
|
|
DEREFERENCE_CLIENT_CONNECTION( pConnection );
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
}
|
|
|
|
/*********************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
This is our connection completion routine. It's called by an underlying
|
|
transport when a connection request completes, either good or bad. We
|
|
figure out what happened, free the IRP, and call upwards to notify
|
|
the rest of the code.
|
|
|
|
Arguments:
|
|
|
|
pDeviceObject - The device object we called.
|
|
pIrp - The IRP that is completing.
|
|
Context - Our context value, really a pointer to an
|
|
HTTP client connection structure.
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED so the I/O system doesn't do anything
|
|
else.
|
|
|
|
--*********************************************************************/
|
|
NTSTATUS
|
|
UcpConnectComplete(
|
|
PDEVICE_OBJECT pDeviceObject,
|
|
PIRP pIrp,
|
|
PVOID Context
|
|
)
|
|
{
|
|
PUC_CLIENT_CONNECTION pConnection;
|
|
NTSTATUS Status;
|
|
KIRQL OldIrql;
|
|
|
|
UNREFERENCED_PARAMETER(pDeviceObject);
|
|
|
|
pConnection = (PUC_CLIENT_CONNECTION)Context;
|
|
|
|
|
|
Status = pIrp->IoStatus.Status;
|
|
|
|
ASSERT( UC_IS_VALID_CLIENT_CONNECTION( pConnection ) );
|
|
|
|
UcRestartClientConnect(pConnection, Status);
|
|
|
|
//
|
|
// We need to kick off the connection state machine.
|
|
//
|
|
|
|
UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
|
|
|
|
UcKickOffConnectionStateMachine(
|
|
pConnection,
|
|
OldIrql,
|
|
UcConnectionWorkItem
|
|
);
|
|
|
|
//
|
|
// Deref for the CONNECT
|
|
//
|
|
|
|
DEREFERENCE_CLIENT_CONNECTION( pConnection );
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
This function sets a new flag in the connection's flag set. The setting
|
|
of the flag is synchronized such that only one flag is set at a time.
|
|
|
|
Arguments:
|
|
|
|
ConnFlag - Supplies a pointer to the location which stores the current flag.
|
|
|
|
NewFlag - Supplies a 32-bit value to be or-ed into the current flag set.
|
|
|
|
Return Value:
|
|
|
|
The new set of connection flags after the update.
|
|
|
|
--***************************************************************************/
|
|
ULONG
|
|
UcSetFlag(
|
|
IN OUT PLONG ConnFlag,
|
|
IN LONG NewFlag
|
|
)
|
|
{
|
|
LONG MynewFlags;
|
|
LONG oldFlags;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
do
|
|
{
|
|
//
|
|
// Capture the current value and initialize the new value.
|
|
//
|
|
|
|
oldFlags = *ConnFlag;
|
|
|
|
MynewFlags = (*ConnFlag) | NewFlag;
|
|
|
|
if (InterlockedCompareExchange(
|
|
ConnFlag,
|
|
MynewFlags,
|
|
oldFlags) == oldFlags)
|
|
{
|
|
break;
|
|
}
|
|
|
|
} while (TRUE);
|
|
|
|
return MynewFlags;
|
|
|
|
} // UcSetFlag
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Build a receive buffer and IRP to TDI to get any pending data.
|
|
|
|
Arguments:
|
|
|
|
pTdiObject - Supplies the TDI connection object to manipulate.
|
|
|
|
pConnection - Supplies the UL_CONNECTION object.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UcpBuildTdiReceiveBuffer(
|
|
IN PUX_TDI_OBJECT pTdiObject,
|
|
IN PUC_CLIENT_CONNECTION pConnection,
|
|
OUT PIRP *pIrp
|
|
)
|
|
{
|
|
PUL_RECEIVE_BUFFER pBuffer;
|
|
|
|
if (pTdiObject->pDeviceObject->StackSize > DEFAULT_IRP_STACK_SIZE)
|
|
{
|
|
pBuffer = UlAllocateReceiveBuffer(
|
|
pTdiObject->pDeviceObject->StackSize
|
|
);
|
|
}
|
|
else
|
|
{
|
|
pBuffer = UlPplAllocateReceiveBuffer();
|
|
}
|
|
|
|
if (pBuffer != NULL)
|
|
{
|
|
//
|
|
// Finish initializing the buffer and the IRP.
|
|
//
|
|
|
|
REFERENCE_CLIENT_CONNECTION( pConnection );
|
|
pBuffer->pConnectionContext = pConnection;
|
|
pBuffer->UnreadDataLength = 0;
|
|
|
|
TdiBuildReceive(
|
|
pBuffer->pIrp, // Irp
|
|
pTdiObject->pDeviceObject, // DeviceObject
|
|
pTdiObject->pFileObject, // FileObject
|
|
&UcpRestartReceive, // CompletionRoutine
|
|
pBuffer, // CompletionContext
|
|
pBuffer->pMdl, // MdlAddress
|
|
TDI_RECEIVE_NORMAL, // Flags
|
|
g_UlReceiveBufferSize // Length
|
|
);
|
|
|
|
//
|
|
// We must trace the IRP before we set the next stack
|
|
// location so the trace code can pull goodies from the
|
|
// IRP correctly.
|
|
//
|
|
|
|
TRACE_IRP( IRP_ACTION_CALL_DRIVER, pBuffer->pIrp );
|
|
|
|
//
|
|
// Pass the IRP back to the transport.
|
|
//
|
|
|
|
*pIrp = pBuffer->pIrp;
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
} // UcpBuildTdiReceiveBuffer
|