1184 lines
31 KiB
C
1184 lines
31 KiB
C
/*++
|
||
|
||
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
|
||
|