Leaked source code of windows server 2003
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

/*++
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