NT4/private/ntos/afd/disconn.c
2020-09-30 17:12:29 +02:00

1184 lines
31 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
disconn.c
Abstract:
This module contains the dispatch routines for AFD.
Author:
David Treadwell (davidtr) 31-Mar-1992
Revision History:
--*/
#include "afdp.h"
#if ENABLE_ABORT_TIMER_HACK
//
// Hack-O-Rama. TDI has a fundamental flaw in that it is often impossible
// to determine exactly when a TDI protocol is "done" with a connection
// object. The biggest problem here is that AFD may get a suprious TDI
// indication *after* an abort request has completed. As a temporary work-
// around, whenever an abort request completes, we'll start a timer. AFD
// will defer further processing on the connection until that timer fires.
//
typedef struct _AFD_ABORT_TIMER_INFO {
KDPC Dpc;
KTIMER Timer;
} AFD_ABORT_TIMER_INFO, *PAFD_ABORT_TIMER_INFO;
VOID
AfdAbortTimerHack(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
#endif // ENABLE_ABORT_TIMER_HACK
NTSTATUS
AfdRestartAbort(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
VOID
AfdRestartAbortHelper(
IN PAFD_CONNECTION Connection
);
NTSTATUS
AfdRestartDisconnect(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
typedef struct _AFD_ABORT_CONTEXT {
PAFD_CONNECTION Connection;
} AFD_ABORT_CONTEXT, *PAFD_ABORT_CONTEXT;
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGEAFD, AfdPartialDisconnect )
#pragma alloc_text( PAGEAFD, AfdDisconnectEventHandler )
#pragma alloc_text( PAGEAFD, AfdBeginAbort )
#pragma alloc_text( PAGEAFD, AfdRestartAbort )
#pragma alloc_text( PAGEAFD, AfdRestartAbortHelper )
#pragma alloc_text( PAGEAFD, AfdBeginDisconnect )
#pragma alloc_text( PAGEAFD, AfdRestartDisconnect )
#if ENABLE_ABORT_TIMER_HACK
#pragma alloc_text( PAGEAFD, AfdAbortTimerHack )
#endif // ENABLE_ABORT_TIMER_HACK
#endif
NTSTATUS
AfdPartialDisconnect(
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
{
NTSTATUS status;
KIRQL oldIrql;
PAFD_ENDPOINT endpoint;
PAFD_CONNECTION connection;
PAFD_PARTIAL_DISCONNECT_INFO disconnectInfo;
Irp->IoStatus.Information = 0;
endpoint = IrpSp->FileObject->FsContext;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
disconnectInfo = Irp->AssociatedIrp.SystemBuffer;
IF_DEBUG(CONNECT) {
KdPrint(( "AfdPartialDisconnect: disconnecting endpoint %lx, "
"mode %lx, endp mode %lx\n",
endpoint, disconnectInfo->DisconnectMode,
endpoint->DisconnectMode ));
}
//
// If this is a datagram endpoint, just remember how the endpoint
// was shut down, don't actually do anything. Note that it is legal
// to do a shutdown() on an unconnected datagram socket, so the
// test that the socket must be connected is after this case.
//
if ( IS_DGRAM_ENDPOINT(endpoint) ) {
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
if ( (disconnectInfo->DisconnectMode & AFD_ABORTIVE_DISCONNECT) != 0 ) {
endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_RECEIVE;
endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_SEND;
endpoint->DisconnectMode |= AFD_ABORTIVE_DISCONNECT;
}
if ( (disconnectInfo->DisconnectMode & AFD_PARTIAL_DISCONNECT_RECEIVE) != 0 ) {
endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_RECEIVE;
}
if ( (disconnectInfo->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND) != 0 ) {
endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_SEND;
}
if ( (disconnectInfo->DisconnectMode & AFD_UNCONNECT_DATAGRAM) != 0 ) {
if( endpoint->Common.Datagram.RemoteAddress != NULL ) {
AFD_FREE_POOL(
endpoint->Common.Datagram.RemoteAddress,
AFD_REMOTE_ADDRESS_POOL_TAG
);
}
endpoint->Common.Datagram.RemoteAddress = NULL;
endpoint->Common.Datagram.RemoteAddressLength = 0;
endpoint->State = AfdEndpointStateBound;
}
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
status = STATUS_SUCCESS;
goto complete;
}
//
// Make sure that the endpoint is in the correct state.
//
if ( endpoint->Type != AfdBlockTypeVcConnecting ||
endpoint->State != AfdEndpointStateConnected ) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
connection = endpoint->Common.VcConnecting.Connection;
ASSERT( connection != NULL );
ASSERT( connection->Type == AfdBlockTypeConnection );
//
// If we're doing an abortive disconnect, remember that the receive
// side is shut down and issue a disorderly release.
//
if ( (disconnectInfo->DisconnectMode & AFD_ABORTIVE_DISCONNECT) != 0 ) {
IF_DEBUG(CONNECT) {
KdPrint(( "AfdPartialDisconnect: abortively disconnecting endp %lx\n",
endpoint ));
}
status = AfdBeginAbort( connection );
if ( status == STATUS_PENDING ) {
status = STATUS_SUCCESS;
}
goto complete;
}
//
// If the receive side of the connection is being shut down,
// remember the fact in the endpoint. If there is pending data on
// the VC, do a disorderly release on the endpoint. If the receive
// side has already been shut down, do nothing.
//
if ( (disconnectInfo->DisconnectMode & AFD_PARTIAL_DISCONNECT_RECEIVE) != 0 &&
(endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_RECEIVE) == 0 ) {
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
//
// Determine whether there is pending data.
//
if ( IS_DATA_ON_CONNECTION( connection ) ||
IS_EXPEDITED_DATA_ON_CONNECTION( connection ) ) {
//
// There is unreceived data. Abort the connection.
//
IF_DEBUG(CONNECT) {
KdPrint(( "AfdPartialDisconnect: unreceived data on endp %lx,"
"conn %lx, aborting.\n",
endpoint, connection ));
}
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
(VOID)AfdBeginAbort( connection );
status = STATUS_SUCCESS;
goto complete;
} else {
IF_DEBUG(CONNECT) {
KdPrint(( "AfdPartialDisconnect: disconnecting recv for endp %lx\n",
endpoint ));
}
//
// Remember that the receive side is shut down. This will cause
// the receive indication handlers to dump any data that
// arrived.
//
// !!! This is a minor violation of RFC1122 4.2.2.13. We
// should really do an abortive disconnect if data
// arrives after a receive shutdown.
//
endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_RECEIVE;
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
}
}
//
// If the send side is being shut down, remember it in the endpoint
// and pass the request on to the TDI provider for a graceful
// disconnect. If the send side is already shut down, do nothing here.
//
if ( (disconnectInfo->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND) != 0 &&
(endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND) == 0 ) {
status = AfdBeginDisconnect( endpoint, &disconnectInfo->Timeout, NULL );
if ( !NT_SUCCESS(status) ) {
goto complete;
}
if ( status == STATUS_PENDING ) {
status = STATUS_SUCCESS;
}
}
status = STATUS_SUCCESS;
complete:
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, AfdPriorityBoost );
return status;
} // AfdPartialDisconnect
NTSTATUS
AfdDisconnectEventHandler(
IN PVOID TdiEventContext,
IN CONNECTION_CONTEXT ConnectionContext,
IN int DisconnectDataLength,
IN PVOID DisconnectData,
IN int DisconnectInformationLength,
IN PVOID DisconnectInformation,
IN ULONG DisconnectFlags
)
{
PAFD_CONNECTION connection = ConnectionContext;
PAFD_ENDPOINT endpoint;
KIRQL oldIrql;
NTSTATUS status;
ASSERT( connection != NULL );
ASSERT( connection->Type == AfdBlockTypeConnection );
endpoint = connection->Endpoint;
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ||
endpoint->Type == AfdBlockTypeVcListening );
IF_DEBUG(CONNECT) {
KdPrint(( "AfdDisconnectEventHandler called for endpoint %lx, "
"connection %lx\n",
connection->Endpoint, connection ));
}
UPDATE_CONN( connection, DisconnectFlags );
//
// Reference the connection object so that it does not go away while
// we're processing it inside this function. Without this
// reference, the user application could close the endpoint object,
// the connection reference count could go to zero, and the
// AfdDeleteConnectedReference call at the end of this function
// could cause a crash if the AFD connection object has been
// completely cleaned up.
//
REFERENCE_CONNECTION( connection );
//
// Set up in the connection the fact that the remote side has
// disconnected or aborted.
//
if ( (DisconnectFlags & TDI_DISCONNECT_ABORT) != 0 ) {
connection->AbortIndicated = TRUE;
status = STATUS_REMOTE_DISCONNECT;
AfdRecordAbortiveDisconnectIndications();
} else {
connection->DisconnectIndicated = TRUE;
status = STATUS_SUCCESS;
AfdRecordGracefulDisconnectIndications();
}
//
// If this is a nonbufferring transport, complete any pended receives.
//
if ( !connection->TdiBufferring ) {
AfdCompleteIrpList(
&connection->VcReceiveIrpListHead,
&endpoint->SpinLock,
status,
NULL
);
//
// If this is an abort indication, complete all pended sends and
// discard any bufferred receive data.
//
if ( connection->AbortIndicated ) {
AfdCompleteIrpList(
&connection->VcSendIrpListHead,
&endpoint->SpinLock,
status,
NULL
);
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
connection->VcBufferredReceiveBytes = 0;
connection->VcBufferredReceiveCount = 0;
connection->VcBufferredExpeditedBytes = 0;
connection->VcBufferredExpeditedCount = 0;
connection->VcReceiveBytesInTransport = 0;
connection->VcReceiveCountInTransport = 0;
while ( !IsListEmpty( &connection->VcReceiveBufferListHead ) ) {
PAFD_BUFFER afdBuffer;
PLIST_ENTRY listEntry;
listEntry = RemoveHeadList( &connection->VcReceiveBufferListHead );
afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry );
afdBuffer->ExpeditedData = FALSE;
afdBuffer->DataOffset = 0;
AfdReturnBuffer( afdBuffer );
}
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
}
}
//
// If we got disconnect data or options, save it.
//
if( ( DisconnectData != NULL && DisconnectDataLength > 0 ) ||
( DisconnectInformation != NULL && DisconnectInformationLength > 0 ) ) {
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
if( DisconnectData != NULL & DisconnectDataLength > 0 ) {
status = AfdSaveReceivedConnectData(
&connection->ConnectDataBuffers,
IOCTL_AFD_SET_DISCONNECT_DATA,
DisconnectData,
DisconnectDataLength
);
if( !NT_SUCCESS(status) ) {
//
// We hit an allocation failure, but press on regardless.
//
KdPrint((
"AfdSaveReceivedConnectData failed: %08lx\n",
status
));
}
}
if( DisconnectInformation != NULL & DisconnectInformationLength > 0 ) {
status = AfdSaveReceivedConnectData(
&connection->ConnectDataBuffers,
IOCTL_AFD_SET_DISCONNECT_DATA,
DisconnectInformation,
DisconnectInformationLength
);
if( !NT_SUCCESS(status) ) {
//
// We hit an allocation failure, but press on regardless.
//
KdPrint((
"AfdSaveReceivedConnectData failed: %08lx\n",
status
));
}
}
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
}
//
// Call AfdIndicatePollEvent in case anyone is polling on this
// connection getting disconnected or aborted.
//
if ( (DisconnectFlags & TDI_DISCONNECT_ABORT) != 0 ) {
AfdIndicatePollEvent(
endpoint,
AFD_POLL_ABORT_BIT,
STATUS_SUCCESS
);
} else {
AfdIndicatePollEvent(
endpoint,
AFD_POLL_DISCONNECT_BIT,
STATUS_SUCCESS
);
}
//
// Remove the connected reference on the connection object. We must
// do this AFTER setting up the flag which remembers the disconnect
// type that occurred. We must also do this AFTER we have finished
// handling everything in the endpoint, since the endpoint structure
// may no longer have any information about the connection object if
// a transmit request with AFD_TF_REUSE_SOCKET happenned on it.
//
AfdDeleteConnectedReference( connection, FALSE );
//
// Dereference the connection from the reference added above.
//
DEREFERENCE_CONNECTION( connection );
return STATUS_SUCCESS;
} // AfdDisconnectEventHandler
NTSTATUS
AfdBeginAbort(
IN PAFD_CONNECTION Connection
)
{
PAFD_ENDPOINT endpoint = Connection->Endpoint;
PIRP irp;
PFILE_OBJECT fileObject;
PDEVICE_OBJECT deviceObject;
KIRQL oldIrql;
IF_DEBUG(CONNECT) {
KdPrint(( "AfdBeginAbort: aborting on endpoint %lx\n", endpoint ));
}
// Yet another hack to keep it from crashing
// Reduce the timing window were this connection can be removed
// from under us. (VadimE)
REFERENCE_CONNECTION( Connection );
//
// Build an IRP to reset the connection. First get the address
// of the target device object.
//
ASSERT( Connection->Type == AfdBlockTypeConnection );
fileObject = Connection->FileObject;
ASSERT( fileObject != NULL );
deviceObject = IoGetRelatedDeviceObject( fileObject );
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
//
// If the endpoint has already been abortively disconnected,
// or if has been gracefully disconnected and the transport
// does not support orderly (i.e. two-phase) release, then just
// succeed this request.
//
// Note that, since the abort completion routine (AfdRestartAbort)
// will not be called, we must delete the connected reference
// ourselves.
//
if ( (endpoint->DisconnectMode & AFD_ABORTIVE_DISCONNECT) != 0 ||
Connection->AbortIndicated ||
(Connection->DisconnectIndicated &&
(endpoint->TransportInfo->ProviderInfo.ServiceFlags &
TDI_SERVICE_ORDERLY_RELEASE) == 0) ) {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
AfdDeleteConnectedReference( Connection, FALSE );
// Accounts for the reference hack above (VadimE)
DEREFERENCE_CONNECTION( Connection );
return STATUS_SUCCESS;
}
#if ENABLE_ABORT_TIMER_HACK
//
// Allocate a new abort timer if necessary.
//
if( Connection->AbortTimerInfo == NULL ) {
Connection->AbortTimerInfo = AFD_ALLOCATE_POOL(
NonPagedPoolMustSucceed,
sizeof(AFD_ABORT_TIMER_INFO),
AFD_ABORT_TIMER_HACK_POOL_TAG
);
}
ASSERT( Connection->AbortTimerInfo != NULL );
#endif // ENABLE_ABORT_TIMER_HACK
//
// Remember that the connection has been aborted.
//
if ( endpoint->Type != AfdBlockTypeVcListening ) {
endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_RECEIVE;
endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_SEND;
endpoint->DisconnectMode |= AFD_ABORTIVE_DISCONNECT;
}
Connection->AbortIndicated = TRUE;
//
// Set the BytesTaken fields equal to the BytesIndicated fields so
// that no more AFD_POLL_RECEIVE or AFD_POLL_RECEIVE_EXPEDITED
// events get completed.
//
if ( endpoint->TdiBufferring ) {
Connection->Common.Bufferring.ReceiveBytesTaken =
Connection->Common.Bufferring.ReceiveBytesIndicated;
Connection->Common.Bufferring.ReceiveExpeditedBytesTaken =
Connection->Common.Bufferring.ReceiveExpeditedBytesIndicated;
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
} else if ( endpoint->Type != AfdBlockTypeVcListening ) {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
//
// Complete all of the connection's pended sends and receives.
//
AfdCompleteIrpList(
&Connection->VcReceiveIrpListHead,
&endpoint->SpinLock,
STATUS_LOCAL_DISCONNECT,
NULL
);
AfdCompleteIrpList(
&Connection->VcSendIrpListHead,
&endpoint->SpinLock,
STATUS_LOCAL_DISCONNECT,
NULL
);
} else {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
}
//
// Allocate an IRP. The stack size is one higher than that of the
// target device, to allow for the caller's completion routine.
//
irp = IoAllocateIrp( (CCHAR)(deviceObject->StackSize), FALSE );
if ( irp == NULL ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Initialize the IRP for an abortive disconnect.
//
irp->MdlAddress = NULL;
irp->Flags = 0;
irp->RequestorMode = KernelMode;
irp->PendingReturned = FALSE;
irp->UserIosb = NULL;
irp->UserEvent = NULL;
irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
irp->AssociatedIrp.SystemBuffer = NULL;
irp->UserBuffer = NULL;
irp->Tail.Overlay.Thread = PsGetCurrentThread();
irp->Tail.Overlay.OriginalFileObject = fileObject;
irp->Tail.Overlay.AuxiliaryBuffer = NULL;
TdiBuildDisconnect(
irp,
deviceObject,
fileObject,
AfdRestartAbort,
Connection,
NULL,
TDI_DISCONNECT_ABORT,
NULL,
NULL
);
//
// Reference the connection object so that it does not go away
// until the abort completes.
//
// REFERENCE_CONNECTION( Connection ); Done above (VadimE)
AfdRecordAbortiveDisconnectsInitiated();
//
// Pass the request to the transport provider.
//
return IoCallDriver( deviceObject, irp );
} // AfdBeginAbort
#if ENABLE_ABORT_TIMER_HACK
NTSTATUS
AfdRestartAbort(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PAFD_CONNECTION connection;
PAFD_ABORT_TIMER_INFO timerInfo;
connection = Context;
ASSERT( connection != NULL );
ASSERT( connection->Type == AfdBlockTypeConnection );
UPDATE_CONN( connection, Irp->IoStatus.Status );
timerInfo = connection->AbortTimerInfo;
ASSERT( timerInfo != NULL );
IF_DEBUG(CONNECT) {
KdPrint((
"AfdRestartAbort: abort completed, status = %X, endpoint = %lx\n",
Irp->IoStatus.Status,
connection->Endpoint
));
}
//
// Setup a timer so we know it's safe to free the connection.
//
KeInitializeDpc(
&timerInfo->Dpc,
AfdAbortTimerHack,
connection
);
KeInitializeTimer(
&timerInfo->Timer
);
KeSetTimer(
&timerInfo->Timer,
AfdAbortTimerTimeout,
&timerInfo->Dpc
);
//
// Free the IRP now since it is no longer needed.
//
IoFreeIrp( Irp );
//
// Return STATUS_MORE_PROCESSING_REQUIRED so that IoCompleteRequest
// will stop working on the IRP.
//
return STATUS_MORE_PROCESSING_REQUIRED;
} // AfdRestartAbort
VOID
AfdAbortTimerHack(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
{
PAFD_CONNECTION connection;
connection = DeferredContext;
ASSERT( connection != NULL );
ASSERT( connection->Type == AfdBlockTypeConnection );
//
// Let the helper do the dirty work.
//
AfdRestartAbortHelper( connection );
} // AfdAbortTimerHack
#else // !ENABLE_ABORT_TIMER_HACK
NTSTATUS
AfdRestartAbort(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PAFD_CONNECTION connection;
connection = Context;
ASSERT( connection != NULL );
ASSERT( connection->Type == AfdBlockTypeConnection );
IF_DEBUG(CONNECT) {
KdPrint((
"AfdRestartAbort: abort completed, status = %X, endpoint = %lx\n",
Irp->IoStatus.Status,
connection->Endpoint
));
}
//
// Let the helper do the dirty work.
//
AfdRestartAbortHelper( connection );
//
// Free the IRP now since it is no longer needed.
//
IoFreeIrp( Irp );
//
// Return STATUS_MORE_PROCESSING_REQUIRED so that IoCompleteRequest
// will stop working on the IRP.
//
return STATUS_MORE_PROCESSING_REQUIRED;
} // AfdRestartAbort
#endif // ENABLE_ABORT_TIMER_HACK
VOID
AfdRestartAbortHelper(
IN PAFD_CONNECTION Connection
)
{
PAFD_ENDPOINT endpoint;
ASSERT( Connection != NULL );
ASSERT( Connection->Type == AfdBlockTypeConnection );
endpoint = Connection->Endpoint;
UPDATE_CONN( Connection, 0 );
AfdRecordAbortiveDisconnectsCompleted();
//
// Remember that the connection has been aborted, and indicate if
// necessary.
//
if( endpoint->Type != AfdBlockTypeVcListening ) {
AfdIndicatePollEvent(
endpoint,
AFD_POLL_ABORT_BIT,
STATUS_SUCCESS
);
}
if( !Connection->TdiBufferring ) {
//
// Complete all of the connection's pended sends and receives.
//
AfdCompleteIrpList(
&Connection->VcReceiveIrpListHead,
&endpoint->SpinLock,
STATUS_LOCAL_DISCONNECT,
NULL
);
AfdCompleteIrpList(
&Connection->VcSendIrpListHead,
&endpoint->SpinLock,
STATUS_LOCAL_DISCONNECT,
NULL
);
}
//
// Remove the connected reference from the connection, since we
// know that the connection will not be active any longer.
//
AfdDeleteConnectedReference( Connection, FALSE );
//
// Dereference the AFD connection object.
//
DEREFERENCE_CONNECTION( Connection );
} // AfdRestartAbortHelper
NTSTATUS
AfdBeginDisconnect(
IN PAFD_ENDPOINT Endpoint,
IN PLARGE_INTEGER Timeout OPTIONAL,
OUT PIRP *DisconnectIrp OPTIONAL
)
{
PTDI_CONNECTION_INFORMATION requestConnectionInformation = NULL;
PTDI_CONNECTION_INFORMATION returnConnectionInformation = NULL;
PAFD_CONNECTION connection;
KIRQL oldIrql;
PFILE_OBJECT fileObject;
PDEVICE_OBJECT deviceObject;
PAFD_DISCONNECT_CONTEXT disconnectContext;
PIRP irp;
ASSERT( Endpoint->Type == AfdBlockTypeVcConnecting );
connection = Endpoint->Common.VcConnecting.Connection;
ASSERT( connection != NULL );
ASSERT( connection->Type == AfdBlockTypeConnection );
fileObject = connection->FileObject;
ASSERT( fileObject != NULL );
deviceObject = IoGetRelatedDeviceObject( fileObject );
UPDATE_CONN( connection, 0 );
if ( DisconnectIrp != NULL ) {
*DisconnectIrp = NULL;
}
//
// Allocate and initialize a disconnect IRP.
//
irp = IoAllocateIrp( (CCHAR)(deviceObject->StackSize), FALSE );
if ( irp == NULL ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Initialize the IRP.
//
irp->MdlAddress = NULL;
irp->Flags = 0;
irp->RequestorMode = KernelMode;
irp->PendingReturned = FALSE;
irp->UserIosb = NULL;
irp->UserEvent = NULL;
irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
irp->AssociatedIrp.SystemBuffer = NULL;
irp->UserBuffer = NULL;
irp->Tail.Overlay.Thread = PsGetCurrentThread();
irp->Tail.Overlay.OriginalFileObject = fileObject;
irp->Tail.Overlay.AuxiliaryBuffer = NULL;
//
// If the endpoint has already been abortively disconnected,
// just succeed this request.
//
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
if ( (Endpoint->DisconnectMode & AFD_ABORTIVE_DISCONNECT) != 0 ||
connection->AbortIndicated ) {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
IoFreeIrp( irp );
return STATUS_SUCCESS;
}
//
// If this connection has already been disconnected, just succeed.
//
if ( (Endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND) != 0 ) {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
IoFreeIrp( irp );
return STATUS_SUCCESS;
}
//
// Use the disconnect context space in the connection structure.
//
disconnectContext = &connection->DisconnectContext;
disconnectContext->Endpoint = Endpoint;
disconnectContext->Connection = connection;
disconnectContext->TdiConnectionInformation = NULL;
disconnectContext->Irp = irp;
InsertHeadList(
&AfdDisconnectListHead,
&disconnectContext->DisconnectListEntry
);
//
// Remember that the send side has been disconnected.
//
Endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_SEND;
//
// If there are disconnect data buffers, allocate request
// and return connection information structures and copy over
// pointers to the structures.
//
if ( connection->ConnectDataBuffers != NULL ) {
requestConnectionInformation =
AFD_ALLOCATE_POOL(
NonPagedPool,
sizeof(*requestConnectionInformation) +
sizeof(*returnConnectionInformation),
AFD_CONNECT_DATA_POOL_TAG
);
if ( requestConnectionInformation != NULL ) {
returnConnectionInformation =
requestConnectionInformation + 1;
requestConnectionInformation->UserData =
connection->ConnectDataBuffers->SendDisconnectData.Buffer;
requestConnectionInformation->UserDataLength =
connection->ConnectDataBuffers->SendDisconnectData.BufferLength;
requestConnectionInformation->Options =
connection->ConnectDataBuffers->SendDisconnectOptions.Buffer;
requestConnectionInformation->OptionsLength =
connection->ConnectDataBuffers->SendDisconnectOptions.BufferLength;
returnConnectionInformation->UserData =
connection->ConnectDataBuffers->ReceiveDisconnectData.Buffer;
returnConnectionInformation->UserDataLength =
connection->ConnectDataBuffers->ReceiveDisconnectData.BufferLength;
returnConnectionInformation->Options =
connection->ConnectDataBuffers->ReceiveDisconnectOptions.Buffer;
returnConnectionInformation->OptionsLength =
connection->ConnectDataBuffers->ReceiveDisconnectOptions.BufferLength;
}
disconnectContext->TdiConnectionInformation =
requestConnectionInformation;
}
//
// Set up the timeout for the disconnect.
//
disconnectContext->Timeout = RtlConvertLongToLargeInteger( -1 );
//
// Build a disconnect Irp to pass to the TDI provider.
//
TdiBuildDisconnect(
irp,
connection->DeviceObject,
connection->FileObject,
AfdRestartDisconnect,
disconnectContext,
&disconnectContext->Timeout,
TDI_DISCONNECT_RELEASE,
requestConnectionInformation,
returnConnectionInformation
);
IF_DEBUG(CONNECT) {
KdPrint(( "AfdBeginDisconnect: disconnecting endpoint %lx\n",
Endpoint ));
}
//
// Reference the endpoint and connection so the space stays
// allocated until the disconnect completes.
//
REFERENCE_ENDPOINT( Endpoint );
REFERENCE_CONNECTION( connection );
//
// If there are still outstanding sends and this is a nonbufferring
// TDI transport which does not support orderly release, pend the
// IRP until all the sends have completed.
//
if ( (Endpoint->TransportInfo->ProviderInfo.ServiceFlags &
TDI_SERVICE_ORDERLY_RELEASE) == 0 &&
!Endpoint->TdiBufferring && connection->VcBufferredSendCount != 0 ) {
ASSERT( connection->VcDisconnectIrp == NULL );
connection->VcDisconnectIrp = irp;
connection->SpecialCondition = TRUE;
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
return STATUS_PENDING;
}
AfdRecordGracefulDisconnectsInitiated();
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
//
// Pass the disconnect request on to the TDI provider.
//
if ( DisconnectIrp == NULL ) {
return IoCallDriver( connection->DeviceObject, irp );
} else {
*DisconnectIrp = irp;
return STATUS_SUCCESS;
}
} // AfdBeginDisconnect
NTSTATUS
AfdRestartDisconnect(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PAFD_DISCONNECT_CONTEXT disconnectContext = Context;
PAFD_ENDPOINT endpoint;
PAFD_CONNECTION connection;
KIRQL oldIrql;
endpoint = disconnectContext->Endpoint;
connection = disconnectContext->Connection;
UPDATE_CONN( connection, 0 );
AfdRecordGracefulDisconnectsCompleted();
ASSERT( connection != NULL );
ASSERT( connection->Type == AfdBlockTypeConnection );
IF_DEBUG(CONNECT) {
KdPrint(( "AfdRestartDisconnect: disconnect completed, status = %X, "
"endpoint = %lx\n", Irp->IoStatus.Status, endpoint ));
}
//
// Free context structures.
//
if ( disconnectContext->TdiConnectionInformation != NULL ) {
AFD_FREE_POOL(
disconnectContext->TdiConnectionInformation,
AFD_CONNECT_DATA_POOL_TAG
);
}
//
// Remove the request from the list of disconnect requests and
// Dereference the connection and endpoint. We must remove it from
// the list before dereferencing the endpoint because when we do the
// dereference AFD might get unloaded, and we cannot acquire a spin
// lock after AFD gets unloaded.
//
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
RemoveEntryList( &disconnectContext->DisconnectListEntry );
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
DEREFERENCE_ENDPOINT( endpoint );
DEREFERENCE_CONNECTION( connection );
//
// Free the IRP and return a status code so that the IO system will
// stop working on the IRP.
//
IoFreeIrp( Irp );
return STATUS_MORE_PROCESSING_REQUIRED;
} // AfdRestartDisconnect