1343 lines
41 KiB
C
1343 lines
41 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-1999 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"
|
||
|
||
|
||
|
||
NTSTATUS
|
||
AfdRestartAbort(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
);
|
||
|
||
NTSTATUS
|
||
AfdRestartDisconnect(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
);
|
||
|
||
NTSTATUS
|
||
AfdRestartDgDisconnect(
|
||
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, AfdBeginDisconnect )
|
||
#pragma alloc_text( PAGEAFD, AfdRestartDisconnect )
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
AfdPartialDisconnect(
|
||
IN PFILE_OBJECT FileObject,
|
||
IN ULONG IoctlCode,
|
||
IN KPROCESSOR_MODE RequestorMode,
|
||
IN PVOID InputBuffer,
|
||
IN ULONG InputBufferLength,
|
||
IN PVOID OutputBuffer,
|
||
IN ULONG OutputBufferLength,
|
||
OUT PUINT_PTR Information
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
||
PAFD_ENDPOINT endpoint;
|
||
PAFD_CONNECTION connection;
|
||
AFD_PARTIAL_DISCONNECT_INFO disconnectInfo;
|
||
|
||
//
|
||
// Nothing to return.
|
||
//
|
||
|
||
*Information = 0;
|
||
|
||
status = STATUS_SUCCESS;
|
||
connection = NULL;
|
||
|
||
endpoint = FileObject->FsContext;
|
||
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
||
#ifdef _WIN64
|
||
{
|
||
C_ASSERT (sizeof (AFD_PARTIAL_DISCONNECT_INFO)==sizeof (AFD_PARTIAL_DISCONNECT_INFO32));
|
||
}
|
||
#endif
|
||
if (InputBufferLength<sizeof (disconnectInfo)) {
|
||
status = STATUS_INVALID_PARAMETER;
|
||
goto exit;
|
||
}
|
||
try {
|
||
|
||
#ifdef _WIN64
|
||
if (IoIs32bitProcess (NULL)) {
|
||
//
|
||
// Validate the input structure if it comes from the user mode
|
||
// application
|
||
//
|
||
|
||
if (RequestorMode != KernelMode ) {
|
||
ProbeForRead (InputBuffer,
|
||
sizeof (disconnectInfo),
|
||
PROBE_ALIGNMENT32 (AFD_PARTIAL_DISCONNECT_INFO32));
|
||
}
|
||
|
||
//
|
||
// Make local copies of the embeded pointer and parameters
|
||
// that we will be using more than once in case malicios
|
||
// application attempts to change them while we are
|
||
// validating
|
||
//
|
||
|
||
disconnectInfo.DisconnectMode = ((PAFD_PARTIAL_DISCONNECT_INFO32)InputBuffer)->DisconnectMode;
|
||
disconnectInfo.Timeout = ((PAFD_PARTIAL_DISCONNECT_INFO32)InputBuffer)->Timeout;
|
||
}
|
||
else
|
||
#endif _WIN64
|
||
{
|
||
//
|
||
// Validate the input structure if it comes from the user mode
|
||
// application
|
||
//
|
||
|
||
if (RequestorMode != KernelMode ) {
|
||
ProbeForRead (InputBuffer,
|
||
sizeof (disconnectInfo),
|
||
PROBE_ALIGNMENT (AFD_PARTIAL_DISCONNECT_INFO));
|
||
}
|
||
|
||
//
|
||
// Make local copies of the embeded pointer and parameters
|
||
// that we will be using more than once in case malicios
|
||
// application attempts to change them while we are
|
||
// validating
|
||
//
|
||
|
||
disconnectInfo = *((PAFD_PARTIAL_DISCONNECT_INFO)InputBuffer);
|
||
}
|
||
} except( AFD_EXCEPTION_FILTER(&status) ) {
|
||
goto exit;
|
||
}
|
||
|
||
IF_DEBUG(CONNECT) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdPartialDisconnect: disconnecting endpoint %p, "
|
||
"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( &endpoint->SpinLock, &lockHandle );
|
||
|
||
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 (AFD_START_STATE_CHANGE (endpoint, AfdEndpointStateBound)) {
|
||
if ( (disconnectInfo.DisconnectMode & AFD_UNCONNECT_DATAGRAM) != 0 &&
|
||
endpoint->State==AfdEndpointStateConnected) {
|
||
if( endpoint->Common.Datagram.RemoteAddress != NULL ) {
|
||
AFD_RETURN_REMOTE_ADDRESS(
|
||
endpoint->Common.Datagram.RemoteAddress,
|
||
endpoint->Common.Datagram.RemoteAddressLength
|
||
);
|
||
}
|
||
endpoint->Common.Datagram.RemoteAddress = NULL;
|
||
endpoint->Common.Datagram.RemoteAddressLength = 0;
|
||
|
||
//
|
||
// Even if disconnect fails, we consider
|
||
// ourselves not connected anymore
|
||
//
|
||
endpoint->State = AfdEndpointStateBound;
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
|
||
if (IS_TDI_DGRAM_CONNECTION(endpoint)) {
|
||
PIRP irp;
|
||
KEVENT event;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
|
||
|
||
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
|
||
irp = TdiBuildInternalDeviceControlIrp (
|
||
TDI_DISCONNECT,
|
||
endpoint->AddressDeviceObject,
|
||
endpoint->AddressFileObject,
|
||
&event,
|
||
&ioStatusBlock
|
||
);
|
||
|
||
if ( irp != NULL ) {
|
||
TdiBuildDisconnect(
|
||
irp,
|
||
endpoint->AddressDeviceObject,
|
||
endpoint->AddressFileObject,
|
||
NULL,
|
||
NULL,
|
||
&disconnectInfo.Timeout,
|
||
(disconnectInfo.DisconnectMode & AFD_ABORTIVE_DISCONNECT)
|
||
? TDI_DISCONNECT_ABORT
|
||
: TDI_DISCONNECT_RELEASE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
status = IoCallDriver( endpoint->AddressDeviceObject, irp );
|
||
if ( status == STATUS_PENDING ) {
|
||
status = KeWaitForSingleObject( (PVOID)&event, Executive, KernelMode, FALSE, NULL );
|
||
ASSERT (status==STATUS_SUCCESS);
|
||
}
|
||
else {
|
||
//
|
||
// The IRP must have been completed then and event set.
|
||
//
|
||
ASSERT (NT_ERROR (status) || KeReadStateEvent (&event));
|
||
}
|
||
}
|
||
else {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
AFD_END_STATE_CHANGE (endpoint);
|
||
}
|
||
else {
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
goto exit;
|
||
}
|
||
|
||
//
|
||
// Make sure that the endpoint is in the correct state.
|
||
//
|
||
|
||
if ( (endpoint->Type & AfdBlockTypeVcConnecting)!=AfdBlockTypeVcConnecting ||
|
||
endpoint->Listening ||
|
||
endpoint->afdC_Root ||
|
||
endpoint->State != AfdEndpointStateConnected ||
|
||
((connection=AfdGetConnectionReferenceFromEndpoint (endpoint))==NULL)) {
|
||
status = STATUS_INVALID_PARAMETER;
|
||
goto exit;
|
||
}
|
||
|
||
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) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdPartialDisconnect: abortively disconnecting endp %p\n",
|
||
endpoint ));
|
||
}
|
||
|
||
status = AfdBeginAbort( connection );
|
||
if ( status == STATUS_PENDING ) {
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
goto exit;
|
||
}
|
||
|
||
//
|
||
// 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( &endpoint->SpinLock, &lockHandle );
|
||
|
||
//
|
||
// 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) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdPartialDisconnect: unreceived data on endp %p, conn %p, aborting.\n",
|
||
endpoint, connection ));
|
||
}
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
|
||
(VOID)AfdBeginAbort( connection );
|
||
|
||
status = STATUS_SUCCESS;
|
||
goto exit;
|
||
|
||
} else {
|
||
|
||
IF_DEBUG(CONNECT) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdPartialDisconnect: disconnecting recv for endp %p\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( &endpoint->SpinLock, &lockHandle );
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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 exit;
|
||
}
|
||
if ( status == STATUS_PENDING ) {
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
exit:
|
||
if (connection!=NULL) {
|
||
DEREFERENCE_CONNECTION (connection);
|
||
}
|
||
|
||
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;
|
||
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
||
NTSTATUS status;
|
||
BOOLEAN result;
|
||
|
||
ASSERT( connection != NULL );
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
CHECK_REFERENCE_CONNECTION( connection, result);
|
||
if (!result) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
ASSERT( connection->Type == AfdBlockTypeConnection );
|
||
|
||
endpoint = connection->Endpoint;
|
||
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ||
|
||
endpoint->Type == AfdBlockTypeVcListening ||
|
||
endpoint->Type == AfdBlockTypeVcBoth);
|
||
|
||
IF_DEBUG(CONNECT) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdDisconnectEventHandler called for endpoint %p, connection %p\n",
|
||
connection->Endpoint, connection ));
|
||
}
|
||
|
||
UPDATE_CONN2( connection, "DisconnectEvent, flags: %lx", DisconnectFlags );
|
||
|
||
|
||
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
|
||
//
|
||
// Check if connection was accepted and use accept endpoint instead
|
||
// of the listening. Note that accept cannot happen while we are
|
||
// holding listening endpoint spinlock, nor can endpoint change after
|
||
// the accept and while connection is referenced, so it is safe to
|
||
// release listening spinlock if we discover that endpoint was accepted.
|
||
//
|
||
if (((endpoint->Type & AfdBlockTypeVcListening) == AfdBlockTypeVcListening)
|
||
&& (connection->Endpoint != endpoint)) {
|
||
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
|
||
endpoint = connection->Endpoint;
|
||
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting );
|
||
ASSERT( !IS_TDI_BUFFERRING(endpoint) );
|
||
ASSERT( IS_VC_ENDPOINT (endpoint) );
|
||
|
||
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
if ( !IS_MESSAGE_ENDPOINT(endpoint) ) {
|
||
status = STATUS_SUCCESS;
|
||
} else {
|
||
status = STATUS_GRACEFUL_DISCONNECT;
|
||
}
|
||
AfdRecordGracefulDisconnectIndications();
|
||
}
|
||
|
||
if (connection->State==AfdConnectionStateConnected) {
|
||
ASSERT (endpoint->Type & AfdBlockTypeVcConnecting);
|
||
if ( (DisconnectFlags & TDI_DISCONNECT_ABORT) != 0 ) {
|
||
|
||
AfdIndicateEventSelectEvent(
|
||
endpoint,
|
||
AFD_POLL_ABORT,
|
||
STATUS_SUCCESS
|
||
);
|
||
|
||
} else {
|
||
|
||
AfdIndicateEventSelectEvent(
|
||
endpoint,
|
||
AFD_POLL_DISCONNECT,
|
||
STATUS_SUCCESS
|
||
);
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// If this is a nonbufferring transport, complete any pended receives.
|
||
//
|
||
|
||
if ( !connection->TdiBufferring ) {
|
||
|
||
//
|
||
// If this is an abort indication, complete all pended sends and
|
||
// discard any bufferred receive data.
|
||
//
|
||
|
||
if ( connection->AbortIndicated ) {
|
||
|
||
connection->VcBufferredReceiveBytes = 0;
|
||
connection->VcBufferredReceiveCount = 0;
|
||
connection->VcBufferredExpeditedBytes = 0;
|
||
connection->VcBufferredExpeditedCount = 0;
|
||
connection->VcReceiveBytesInTransport = 0;
|
||
|
||
while ( !IsListEmpty( &connection->VcReceiveBufferListHead ) ) {
|
||
|
||
PAFD_BUFFER_HEADER afdBuffer;
|
||
PLIST_ENTRY listEntry;
|
||
|
||
listEntry = RemoveHeadList( &connection->VcReceiveBufferListHead );
|
||
afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER_HEADER, BufferListEntry );
|
||
|
||
DEBUG afdBuffer->BufferListEntry.Flink = NULL;
|
||
|
||
if (afdBuffer->RefCount==1 || // Can't change once off the list
|
||
InterlockedDecrement (&afdBuffer->RefCount)==0) {
|
||
afdBuffer->ExpeditedData = FALSE;
|
||
AfdReturnBuffer( afdBuffer, connection->OwningProcess);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Check for the most typical case where we do not
|
||
// have anything to complete and thus do not need to
|
||
// make a call and take/release the spinlock.
|
||
//
|
||
if ( (IsListEmpty (&connection->VcSendIrpListHead) &&
|
||
IsListEmpty (&connection->VcReceiveIrpListHead)) ||
|
||
((endpoint->Type & AfdBlockTypeVcListening) == AfdBlockTypeVcListening) ) {
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
}
|
||
else {
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
AfdCompleteIrpList(
|
||
&connection->VcSendIrpListHead,
|
||
endpoint,
|
||
status,
|
||
AfdCleanupSendIrp
|
||
);
|
||
|
||
AfdCompleteIrpList(
|
||
&connection->VcReceiveIrpListHead,
|
||
endpoint,
|
||
status,
|
||
NULL
|
||
);
|
||
}
|
||
}
|
||
else {
|
||
//
|
||
// Check for the most typical case where we do not
|
||
// have anything to complete and thus do not need to
|
||
// make a call and take/release the spinlock.
|
||
//
|
||
if ( IsListEmpty (&connection->VcReceiveIrpListHead) ||
|
||
((endpoint->Type & AfdBlockTypeVcListening) == AfdBlockTypeVcListening)) {
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
}
|
||
else {
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
AfdCompleteIrpList(
|
||
&connection->VcReceiveIrpListHead,
|
||
endpoint,
|
||
status,
|
||
NULL
|
||
);
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
else {
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
}
|
||
|
||
//
|
||
// If we got disconnect data or options, save it.
|
||
//
|
||
|
||
if( ( DisconnectData != NULL && DisconnectDataLength > 0 ) ||
|
||
( DisconnectInformation != NULL && DisconnectInformationLength > 0 ) ) {
|
||
|
||
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
|
||
//
|
||
// Check if connection was accepted and use accept endpoint instead
|
||
// of the listening. Note that accept cannot happen while we are
|
||
// holding listening endpoint spinlock, nor can endpoint change after
|
||
// the accept and while connection is referenced, so it is safe to
|
||
// release listening spinlock if we discover that endpoint was accepted.
|
||
//
|
||
if (((endpoint->Type & AfdBlockTypeVcListening) == AfdBlockTypeVcListening)
|
||
&& (connection->Endpoint != endpoint)) {
|
||
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
|
||
endpoint = connection->Endpoint;
|
||
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting );
|
||
ASSERT( !IS_TDI_BUFFERRING(endpoint) );
|
||
ASSERT( IS_VC_ENDPOINT (endpoint) );
|
||
|
||
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
}
|
||
|
||
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.
|
||
//
|
||
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_WARNING_LEVEL,
|
||
"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.
|
||
//
|
||
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_WARNING_LEVEL,
|
||
"AfdSaveReceivedConnectData failed: %08lx\n",
|
||
status
|
||
));
|
||
|
||
}
|
||
|
||
}
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
|
||
}
|
||
|
||
//
|
||
// Call AfdIndicatePollEvent in case anyone is polling on this
|
||
// connection getting disconnected or aborted.
|
||
//
|
||
// Make sure the connection was accepted/connected
|
||
// in order not to signal on listening endpoint
|
||
//
|
||
|
||
if (connection->State==AfdConnectionStateConnected) {
|
||
ASSERT (endpoint->Type & AfdBlockTypeVcConnecting);
|
||
if ( (DisconnectFlags & TDI_DISCONNECT_ABORT) != 0 ) {
|
||
|
||
AfdIndicatePollEvent(
|
||
endpoint,
|
||
AFD_POLL_ABORT,
|
||
STATUS_SUCCESS
|
||
);
|
||
|
||
} else {
|
||
|
||
AfdIndicatePollEvent(
|
||
endpoint,
|
||
AFD_POLL_DISCONNECT,
|
||
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;
|
||
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
||
|
||
IF_DEBUG(CONNECT) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdBeginAbort: aborting on endpoint %p\n",
|
||
endpoint ));
|
||
}
|
||
|
||
UPDATE_CONN( 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( &endpoint->SpinLock, &lockHandle );
|
||
|
||
//
|
||
// Check if connection was accepted and use accept endpoint instead
|
||
// of the listening. Note that accept cannot happen while we are
|
||
// holding listening endpoint spinlock, nor can endpoint change after
|
||
// the accept and while connection is referenced, so it is safe to
|
||
// release listening spinlock if we discover that endpoint was accepted.
|
||
//
|
||
if (((endpoint->Type & AfdBlockTypeVcListening) == AfdBlockTypeVcListening)
|
||
&& (Connection->Endpoint != endpoint)) {
|
||
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
|
||
endpoint = Connection->Endpoint;
|
||
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting );
|
||
ASSERT( !IS_TDI_BUFFERRING(endpoint) );
|
||
ASSERT( IS_VC_ENDPOINT (endpoint) );
|
||
|
||
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
}
|
||
|
||
//
|
||
// 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 and complete outstanding send IRPs if ANY.
|
||
//
|
||
|
||
if ( (endpoint->DisconnectMode & AFD_ABORTIVE_DISCONNECT) != 0 ||
|
||
Connection->AbortIndicated ||
|
||
(Connection->DisconnectIndicated &&
|
||
!IS_TDI_ORDERLY_RELEASE(endpoint) )) {
|
||
if ( !IS_TDI_BUFFERRING(endpoint) &&
|
||
endpoint->Type != AfdBlockTypeVcListening ) {
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
AfdCompleteIrpList(
|
||
&Connection->VcSendIrpListHead,
|
||
endpoint,
|
||
STATUS_LOCAL_DISCONNECT,
|
||
AfdCleanupSendIrp
|
||
);
|
||
}
|
||
else {
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
}
|
||
AfdDeleteConnectedReference( Connection, FALSE );
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Remember that the connection has been aborted.
|
||
//
|
||
|
||
if ( (endpoint->Type & AfdBlockTypeVcListening)!= 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 ( IS_TDI_BUFFERRING(endpoint) ) {
|
||
|
||
Connection->Common.Bufferring.ReceiveBytesTaken =
|
||
Connection->Common.Bufferring.ReceiveBytesIndicated;
|
||
Connection->Common.Bufferring.ReceiveExpeditedBytesTaken =
|
||
Connection->Common.Bufferring.ReceiveExpeditedBytesIndicated;
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
|
||
} else if ( endpoint->Type != AfdBlockTypeVcListening ) {
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
|
||
//
|
||
// Complete all of the connection's pended sends and receives.
|
||
//
|
||
|
||
AfdCompleteIrpList(
|
||
&Connection->VcReceiveIrpListHead,
|
||
endpoint,
|
||
STATUS_LOCAL_DISCONNECT,
|
||
NULL
|
||
);
|
||
|
||
AfdCompleteIrpList(
|
||
&Connection->VcSendIrpListHead,
|
||
endpoint,
|
||
STATUS_LOCAL_DISCONNECT,
|
||
AfdCleanupSendIrp
|
||
);
|
||
|
||
} else {
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
}
|
||
|
||
//
|
||
// 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 );
|
||
|
||
AfdRecordAbortiveDisconnectsInitiated();
|
||
|
||
//
|
||
// Pass the request to the transport provider.
|
||
//
|
||
|
||
return IoCallDriver( deviceObject, irp );
|
||
|
||
} // AfdBeginAbort
|
||
|
||
|
||
|
||
NTSTATUS
|
||
AfdRestartAbort(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
{
|
||
|
||
PAFD_CONNECTION connection;
|
||
PAFD_ENDPOINT endpoint;
|
||
|
||
connection = Context;
|
||
ASSERT( connection != NULL );
|
||
ASSERT( connection->Type == AfdBlockTypeConnection );
|
||
|
||
IF_DEBUG(CONNECT) {
|
||
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdRestartAbort: abort completed, status = %X, endpoint = %p\n",
|
||
Irp->IoStatus.Status,
|
||
connection->Endpoint
|
||
));
|
||
|
||
}
|
||
endpoint = connection->Endpoint;
|
||
|
||
UPDATE_CONN2 ( connection, "Restart abort, status: %lx", Irp->IoStatus.Status);
|
||
AfdRecordAbortiveDisconnectsCompleted();
|
||
|
||
//
|
||
// Remember that the connection has been aborted, and indicate if
|
||
// necessary.
|
||
//
|
||
|
||
if( connection->State==AfdConnectionStateConnected ) {
|
||
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
||
ASSERT (endpoint->Type & AfdBlockTypeVcConnecting);
|
||
|
||
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
AfdIndicateEventSelectEvent (
|
||
endpoint,
|
||
AFD_POLL_ABORT,
|
||
STATUS_SUCCESS
|
||
);
|
||
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
|
||
AfdIndicatePollEvent(
|
||
endpoint,
|
||
AFD_POLL_ABORT,
|
||
STATUS_SUCCESS
|
||
);
|
||
|
||
}
|
||
|
||
if( !connection->TdiBufferring ) {
|
||
|
||
//
|
||
// Complete all of the connection's pended sends and receives.
|
||
//
|
||
|
||
AfdCompleteIrpList(
|
||
&connection->VcReceiveIrpListHead,
|
||
endpoint,
|
||
STATUS_LOCAL_DISCONNECT,
|
||
NULL
|
||
);
|
||
|
||
AfdCompleteIrpList(
|
||
&connection->VcSendIrpListHead,
|
||
endpoint,
|
||
STATUS_LOCAL_DISCONNECT,
|
||
AfdCleanupSendIrp
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// 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 );
|
||
|
||
//
|
||
// 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
|
||
|
||
|
||
|
||
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;
|
||
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
||
PFILE_OBJECT fileObject;
|
||
PDEVICE_OBJECT deviceObject;
|
||
PAFD_DISCONNECT_CONTEXT disconnectContext;
|
||
PIRP irp;
|
||
|
||
|
||
if ( DisconnectIrp != NULL ) {
|
||
*DisconnectIrp = NULL;
|
||
}
|
||
|
||
AfdAcquireSpinLock( &Endpoint->SpinLock, &lockHandle );
|
||
|
||
ASSERT( Endpoint->Type == AfdBlockTypeVcConnecting );
|
||
|
||
connection = AFD_CONNECTION_FROM_ENDPOINT( Endpoint );
|
||
|
||
if (connection==NULL) {
|
||
AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle );
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
ASSERT( connection->Type == AfdBlockTypeConnection );
|
||
UPDATE_CONN( connection );
|
||
|
||
|
||
//
|
||
// If the endpoint has already been abortively disconnected,
|
||
// just succeed this request.
|
||
//
|
||
if ( (Endpoint->DisconnectMode & AFD_ABORTIVE_DISCONNECT) != 0 ||
|
||
connection->AbortIndicated ) {
|
||
AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle );
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// If this connection has already been disconnected, just succeed.
|
||
//
|
||
|
||
if ( (Endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND) != 0 ) {
|
||
AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle );
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
fileObject = connection->FileObject;
|
||
ASSERT( fileObject != NULL );
|
||
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
||
|
||
|
||
//
|
||
// Allocate and initialize a disconnect IRP.
|
||
//
|
||
|
||
irp = IoAllocateIrp( (CCHAR)(deviceObject->StackSize), FALSE );
|
||
if ( irp == NULL ) {
|
||
AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle );
|
||
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;
|
||
|
||
|
||
//
|
||
// Use the disconnect context space in the connection structure.
|
||
//
|
||
|
||
disconnectContext = &connection->DisconnectContext;
|
||
disconnectContext->Irp = irp;
|
||
|
||
//
|
||
// 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 = &connection->ConnectDataBuffers->RequestConnectionInfo;
|
||
RtlZeroMemory (requestConnectionInformation, sizeof (*requestConnectionInformation));
|
||
|
||
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 = &connection->ConnectDataBuffers->ReturnConnectionInfo;
|
||
RtlZeroMemory (returnConnectionInformation, sizeof (*returnConnectionInformation));
|
||
|
||
returnConnectionInformation->UserData =
|
||
connection->ConnectDataBuffers->ReceiveDisconnectData.Buffer;
|
||
returnConnectionInformation->UserDataLength =
|
||
connection->ConnectDataBuffers->ReceiveDisconnectData.BufferLength;
|
||
returnConnectionInformation->Options =
|
||
connection->ConnectDataBuffers->ReceiveDisconnectOptions.Buffer;
|
||
returnConnectionInformation->OptionsLength =
|
||
connection->ConnectDataBuffers->ReceiveDisconnectOptions.BufferLength;
|
||
}
|
||
|
||
//
|
||
// Set up the timeout for the disconnect.
|
||
//
|
||
|
||
if (Timeout==NULL) {
|
||
disconnectContext->Timeout.QuadPart = -1;
|
||
}
|
||
else {
|
||
disconnectContext->Timeout.QuadPart = Timeout->QuadPart;
|
||
}
|
||
|
||
//
|
||
// Build a disconnect Irp to pass to the TDI provider.
|
||
//
|
||
|
||
TdiBuildDisconnect(
|
||
irp,
|
||
connection->DeviceObject,
|
||
connection->FileObject,
|
||
AfdRestartDisconnect,
|
||
connection,
|
||
&disconnectContext->Timeout,
|
||
TDI_DISCONNECT_RELEASE,
|
||
requestConnectionInformation,
|
||
returnConnectionInformation
|
||
);
|
||
|
||
IF_DEBUG(CONNECT) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdBeginDisconnect: disconnecting endpoint %p\n",
|
||
Endpoint ));
|
||
}
|
||
|
||
//
|
||
// Reference the connection so the space stays
|
||
// allocated until the disconnect completes.
|
||
//
|
||
|
||
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 ( !IS_TDI_ORDERLY_RELEASE(Endpoint) &&
|
||
!IS_TDI_BUFFERRING(Endpoint) && connection->VcBufferredSendCount != 0 ) {
|
||
|
||
ASSERT( connection->VcDisconnectIrp == NULL );
|
||
|
||
connection->VcDisconnectIrp = irp;
|
||
connection->SpecialCondition = TRUE;
|
||
AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle );
|
||
|
||
return STATUS_PENDING;
|
||
}
|
||
|
||
AfdRecordGracefulDisconnectsInitiated();
|
||
AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle );
|
||
|
||
|
||
//
|
||
// 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_CONNECT_DATA_BUFFERS connectDataBuffers;
|
||
PAFD_CONNECTION connection=Context;
|
||
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
||
|
||
|
||
UPDATE_CONN2( connection, "Restart disconnect, status: %lx", Irp->IoStatus.Status );
|
||
AfdRecordGracefulDisconnectsCompleted();
|
||
|
||
ASSERT( connection != NULL );
|
||
ASSERT( connection->Type == AfdBlockTypeConnection );
|
||
|
||
IF_DEBUG(CONNECT) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdRestartDisconnect: disconnect completed, status = %X, endpoint = %p\n",
|
||
Irp->IoStatus.Status, connection->Endpoint ));
|
||
}
|
||
|
||
if (NT_SUCCESS (Irp->IoStatus.Status)) {
|
||
if (connection->ConnectDataBuffers!=NULL) {
|
||
PAFD_ENDPOINT endpoint = connection->Endpoint;
|
||
|
||
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
|
||
//
|
||
// Check if connection was accepted and use accept endpoint instead
|
||
// of the listening. Note that accept cannot happen while we are
|
||
// holding listening endpoint spinlock, nor can endpoint change after
|
||
// the accept and while connection is referenced, so it is safe to
|
||
// release listening spinlock if we discover that endpoint was accepted.
|
||
//
|
||
if (((endpoint->Type & AfdBlockTypeVcListening) == AfdBlockTypeVcListening)
|
||
&& (connection->Endpoint != endpoint)) {
|
||
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
|
||
endpoint = connection->Endpoint;
|
||
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting );
|
||
ASSERT( !IS_TDI_BUFFERRING(endpoint) );
|
||
ASSERT( IS_VC_ENDPOINT (endpoint) );
|
||
|
||
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
}
|
||
|
||
connectDataBuffers = connection->ConnectDataBuffers;
|
||
if (connectDataBuffers!=NULL) {
|
||
if( connectDataBuffers->ReturnConnectionInfo.UserData != NULL &&
|
||
connectDataBuffers->ReturnConnectionInfo.UserDataLength > 0 ) {
|
||
NTSTATUS status;
|
||
|
||
|
||
status = AfdSaveReceivedConnectData(
|
||
&connectDataBuffers,
|
||
IOCTL_AFD_SET_DISCONNECT_DATA,
|
||
connectDataBuffers->ReturnConnectionInfo.UserData,
|
||
connectDataBuffers->ReturnConnectionInfo.UserDataLength
|
||
);
|
||
ASSERT (NT_SUCCESS(status));
|
||
}
|
||
|
||
if( connectDataBuffers->ReturnConnectionInfo.Options != NULL &&
|
||
connectDataBuffers->ReturnConnectionInfo.OptionsLength > 0 ) {
|
||
NTSTATUS status;
|
||
|
||
status = AfdSaveReceivedConnectData(
|
||
&connectDataBuffers,
|
||
IOCTL_AFD_SET_DISCONNECT_OPTIONS,
|
||
connectDataBuffers->ReturnConnectionInfo.Options,
|
||
connectDataBuffers->ReturnConnectionInfo.OptionsLength
|
||
);
|
||
|
||
ASSERT (NT_SUCCESS(status));
|
||
}
|
||
|
||
}
|
||
AfdReleaseSpinLock (&connection->Endpoint->SpinLock, &lockHandle);
|
||
}
|
||
}
|
||
else {
|
||
AfdBeginAbort (connection);
|
||
}
|
||
|
||
|
||
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
|