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.
1433 lines
45 KiB
1433 lines
45 KiB
/*++
|
|
|
|
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, AfdAbortConnection )
|
|
#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 PULONG_PTR Information
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
PAFD_ENDPOINT endpoint;
|
|
PAFD_CONNECTION connection;
|
|
AFD_PARTIAL_DISCONNECT_INFO disconnectInfo;
|
|
|
|
UNREFERENCED_PARAMETER (IoctlCode);
|
|
UNREFERENCED_PARAMETER (OutputBuffer);
|
|
UNREFERENCED_PARAMETER (OutputBufferLength);
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
|
|
try {
|
|
|
|
#ifdef _WIN64
|
|
if (IoIs32bitProcess (NULL)) {
|
|
//
|
|
// Validate the input structure if it comes from the user mode
|
|
// application
|
|
//
|
|
|
|
if (RequestorMode != KernelMode ) {
|
|
ProbeForReadSmallStructure (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 ) {
|
|
ProbeForReadSmallStructure (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) ) {
|
|
ASSERT (NT_ERROR (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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
UNREFERENCED_PARAMETER (TdiEventContext);
|
|
|
|
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;
|
|
if (endpoint==NULL) {
|
|
//
|
|
// Indication after connection reuse, ignore.
|
|
//
|
|
DEREFERENCE_CONNECTION (connection);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
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: 0x%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->Aborted = TRUE;
|
|
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 ( DisconnectFlags & TDI_DISCONNECT_ABORT ) {
|
|
|
|
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 ( Connection->Aborted ||
|
|
(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->Aborted = 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 ) {
|
|
//
|
|
// Note that abort failed so we do not attempt to
|
|
// reuse the connection.
|
|
//
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
Connection->AbortFailed = TRUE;
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
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;
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
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: 0x%lX", Irp->IoStatus.Status);
|
|
AfdRecordAbortiveDisconnectsCompleted();
|
|
|
|
//
|
|
// Remember that the connection has been aborted, and indicate if
|
|
// necessary.
|
|
//
|
|
|
|
|
|
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
if (!NT_SUCCESS (Irp->IoStatus.Status)) {
|
|
//
|
|
// Note that abort failed so we do not attempt to reuse
|
|
// the connection.
|
|
//
|
|
connection->AbortFailed = TRUE;
|
|
}
|
|
|
|
if( connection->State==AfdConnectionStateConnected ) {
|
|
ASSERT (endpoint->Type & AfdBlockTypeVcConnecting);
|
|
|
|
AfdIndicateEventSelectEvent (
|
|
endpoint,
|
|
AFD_POLL_ABORT,
|
|
STATUS_SUCCESS
|
|
);
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
|
|
AfdIndicatePollEvent(
|
|
endpoint,
|
|
AFD_POLL_ABORT,
|
|
STATUS_SUCCESS
|
|
);
|
|
|
|
}
|
|
else {
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
}
|
|
|
|
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
|
|
|
|
|
|
VOID
|
|
AfdAbortConnection (
|
|
IN PAFD_CONNECTION Connection
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Aborts connection not yet associated with the accepting endpoint
|
|
Forces cleanup path by dereferencing connection.
|
|
|
|
Arguments:
|
|
|
|
Connection - a pointer to the connection.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS status;
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
|
|
ASSERT( Connection->Endpoint != NULL );
|
|
ASSERT( Connection->ConnectedReferenceAdded );
|
|
|
|
//
|
|
// Abort the connection. We need to set the CleanupBegun flag
|
|
// before initiating the abort so that the connected reference
|
|
// will get properly removed in AfdRestartAbort.
|
|
//
|
|
// Note that if AfdBeginAbort fails then AfdRestartAbort will not
|
|
// get invoked, so we must remove the connected reference ourselves.
|
|
//
|
|
|
|
AfdAcquireSpinLock (&Connection->Endpoint->SpinLock, &lockHandle);
|
|
Connection->CleanupBegun = TRUE;
|
|
AfdReleaseSpinLock (&Connection->Endpoint->SpinLock, &lockHandle);
|
|
|
|
status = AfdBeginAbort( Connection );
|
|
|
|
if( !NT_SUCCESS(status) ) {
|
|
AfdDeleteConnectedReference( Connection, FALSE );
|
|
}
|
|
|
|
//
|
|
// Remove the active reference.
|
|
//
|
|
|
|
DEREFERENCE_CONNECTION( Connection );
|
|
|
|
} // AfdAbortConnection
|
|
|
|
|
|
|
|
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 ( connection->Aborted ) {
|
|
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;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
UPDATE_CONN2( connection, "Restart disconnect, status: 0x%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
|