windows-nt/Source/XPSP1/NT/net/sockets/winsock2/wsp/afdsys/disconn.c

1343 lines
41 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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