/*++ 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 (InputBufferLengthDisconnectMode; 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