mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1183 lines
31 KiB
1183 lines
31 KiB
/*++
|
|
|
|
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
|
|
|