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