diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/afd | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/afd')
60 files changed, 39038 insertions, 0 deletions
diff --git a/private/ntos/afd/accept.c b/private/ntos/afd/accept.c new file mode 100644 index 000000000..19f18138d --- /dev/null +++ b/private/ntos/afd/accept.c @@ -0,0 +1,1473 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + accept.c + +Abstract: + + This module contains the handling code for IOCTL_AFD_ACCEPT. + +Author: + + David Treadwell (davidtr) 21-Feb-1992 + +Revision History: + +--*/ + +#include "afdp.h" + +NTSTATUS +AfdAcceptCore ( + IN PAFD_ENDPOINT ListenEndpoint, + IN PAFD_ENDPOINT AcceptEndpoint, + IN ULONG Sequence + ); + +VOID +AfdDoListenBacklogReplenish ( + IN PVOID Context + ); + +VOID +AfdReplenishListenBacklog ( + IN PAFD_ENDPOINT Endpoint + ); + +NTSTATUS +AfdRestartSuperAcceptGetAddress ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +AfdRestartSuperAcceptListen ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +AfdRestartSuperAcceptReceive ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, AfdAccept ) +#pragma alloc_text( PAGE, AfdSuperAccept ) +#pragma alloc_text( PAGEAFD, AfdDeferAccept ) +#pragma alloc_text( PAGE, AfdDoListenBacklogReplenish ) +#pragma alloc_text( PAGEAFD, AfdAcceptCore ) +#pragma alloc_text( PAGE, AfdReplenishListenBacklog ) +#pragma alloc_text( PAGEAFD, AfdInitiateListenBacklogReplenish ) +#pragma alloc_text( PAGEAFD, AfdRestartSuperAcceptListen ) +#pragma alloc_text( PAGEAFD, AfdRestartSuperAcceptGetAddress ) +#pragma alloc_text( PAGEAFD, AfdRestartSuperAcceptReceive ) +#endif + + +NTSTATUS +AfdAccept ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Accepts an incoming connection. The connection is identified by the + sequence number returned in the wait for listen IRP, and then + associated with the endpoint specified in this request. When this + request completes, the connection is fully established and ready for + data transfer. + +Arguments: + + Irp - a pointer to a transmit file IRP. + + IrpSp - Our stack location for this IRP. + +Return Value: + + STATUS_SUCCESS if the request was completed successfully, or a + failure status code if there was an error. + +--*/ + +{ + NTSTATUS status; + PAFD_ACCEPT_INFO acceptInfo; + PAFD_ENDPOINT endpoint; + PFILE_OBJECT acceptEndpointFileObject; + PAFD_ENDPOINT acceptEndpoint; + PAFD_CONNECTION connection; + + PAGED_CODE( ); + + // + // Set up local variables. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( endpoint->Type == AfdBlockTypeVcListening ); + acceptInfo = Irp->AssociatedIrp.SystemBuffer; + + Irp->IoStatus.Information = 0; + + // + // Make sure that this request is valid. + // + + if ( endpoint->Type != AfdBlockTypeVcListening || + IrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof(AFD_ACCEPT_INFO) ) { + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + // + // Add another free connection to replace the one we're accepting. + // Also, add extra to account for past failures in calls to + // AfdAddFreeConnection(). + // + + InterlockedIncrement( + &endpoint->Common.VcListening.FailedConnectionAdds + ); + + AfdReplenishListenBacklog( endpoint ); + + // + // Obtain a pointer to the endpoint on which we're going to + // accept the connection. + // + + status = ObReferenceObjectByHandle( + acceptInfo->AcceptHandle, + 0L, // DesiredAccess + *IoFileObjectType, + KernelMode, + (PVOID *)&acceptEndpointFileObject, + NULL + ); + + if ( !NT_SUCCESS(status) ) { + goto complete; + } + + acceptEndpoint = acceptEndpointFileObject->FsContext; + + // + // We may have a file object that is not an AFD endpoint. Make sure + // that this is an actual AFD endpoint. + // + + if ( acceptEndpoint->Type != AfdBlockTypeEndpoint ) { + status = STATUS_INVALID_PARAMETER; + ObDereferenceObject( acceptEndpointFileObject ); + goto complete; + } + + ASSERT( InterlockedIncrement( &acceptEndpoint->ObReferenceBias ) > 0 ); + + IF_DEBUG(ACCEPT) { + KdPrint(( "AfdAccept: file object %lx, accept endpoint %lx, " + "listen endpoint %lx\n", + acceptEndpointFileObject, acceptEndpoint, endpoint )); + } + + status = AfdAcceptCore( endpoint, acceptEndpoint, acceptInfo->Sequence ); + + ASSERT( InterlockedDecrement( &acceptEndpoint->ObReferenceBias ) >= 0 ); + + ObDereferenceObject( acceptEndpointFileObject ); + +complete: + + Irp->IoStatus.Status = status; + ASSERT( Irp->CancelRoutine == NULL ); + + IoCompleteRequest( Irp, AfdPriorityBoost ); + + return status; + +} // AfdAccept + + +NTSTATUS +AfdAcceptCore ( + IN PAFD_ENDPOINT ListenEndpoint, + IN PAFD_ENDPOINT AcceptEndpoint, + IN ULONG Sequence + ) + +/*++ + +Routine Description: + + Performs the key functions of associating a connection accepted + on a listening endpoint with a new endpoint. + +Arguments: + + ListenEndpoint - the listening endpoint for the connection. + + AcceptEndpoint - the new endpoint with which to associate the + connectuion. + + Sequence - the sequence number which identifies the accepted + connection. + +Return Value: + + STATUS_SUCCESS if the operation was completed successfully, or a + failure status code if there was an error. + +--*/ + +{ + PAFD_CONNECTION connection; + KIRQL oldIrql; + ULONG eventsActive; + + // + // Fail if the accepting endpoint is not in the correct state. + // + + if ( AcceptEndpoint->State != AfdEndpointStateOpen ) { + return STATUS_INVALID_PARAMETER; + } + + // + // Store the local address of the accept endpoint from the listening + // endpoint. This keeps the address unusable as long as the accept + // endpoint is active. + // + // If the endpoint already has a local address structure that is + // sufficiently large, reuse it. + // + + if ( AcceptEndpoint->LocalAddress != NULL && + AcceptEndpoint->LocalAddressLength < + ListenEndpoint->LocalAddressLength ) { + + AFD_FREE_POOL( + AcceptEndpoint->LocalAddress, + AFD_LOCAL_ADDRESS_POOL_TAG + ); + AcceptEndpoint->LocalAddress = NULL; + } + + if ( AcceptEndpoint->LocalAddress == NULL ) { + + AcceptEndpoint->LocalAddress = AFD_ALLOCATE_POOL( + NonPagedPool, + ListenEndpoint->LocalAddressLength, + AFD_LOCAL_ADDRESS_POOL_TAG + ); + } + + AcceptEndpoint->LocalAddressLength = ListenEndpoint->LocalAddressLength; + + + if ( AcceptEndpoint->LocalAddress == NULL ) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory( + AcceptEndpoint->LocalAddress, + ListenEndpoint->LocalAddress, + ListenEndpoint->LocalAddressLength + ); + + // + // Find the connection on which the accept is being performed. + // + + connection = AfdGetReturnedConnection( ListenEndpoint, Sequence ); + + if ( connection == NULL ) { + return STATUS_INVALID_PARAMETER; + } + + ASSERT( connection->Type == AfdBlockTypeConnection ); + + // + // Reenable the accept event bit, and if there are additional + // unaccepted connections on the endpoint, post another event. + // + + AfdAcquireSpinLock( &ListenEndpoint->SpinLock, &oldIrql ); + + ListenEndpoint->EventsActive &= ~AFD_POLL_ACCEPT; + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdAcceptCore: Endp %08lX, Active %08lX\n", + ListenEndpoint, + ListenEndpoint->EventsActive + )); + } + + if( !IsListEmpty( &ListenEndpoint->Common.VcListening.UnacceptedConnectionListHead ) ) { + + AfdIndicateEventSelectEvent( + ListenEndpoint, + AFD_POLL_ACCEPT_BIT, + STATUS_SUCCESS + ); + + } + + AfdReleaseSpinLock( &ListenEndpoint->SpinLock, oldIrql ); + + // + // Recheck the state of the accepting endpoint under the guard + // of the endpoint's spinlock. + // + + AfdAcquireSpinLock( &AcceptEndpoint->SpinLock, &oldIrql ); + + if ( AcceptEndpoint->State != AfdEndpointStateOpen || + AcceptEndpoint->EndpointCleanedUp ) { + AfdReleaseSpinLock( &AcceptEndpoint->SpinLock, oldIrql ); + + // + // The accepted endpoint has been closed, so go ahead and + // abort the incoming connection. + // + + AfdAbortConnection( connection ); + return STATUS_INVALID_PARAMETER; + } + + // + // Note that the returned connection structure already has a + // referenced pointer to the listening endpoint. Rather than + // removing the reference here, only to re-add it later, we'll + // just not touch the reference count. + // + + ASSERT( connection->Endpoint == ListenEndpoint ); + + // + // Set up the accept endpoint's type, and remember blocking + // characteristics of the TDI provider. + // + + AcceptEndpoint->Type = AfdBlockTypeVcConnecting; + AcceptEndpoint->TdiBufferring = ListenEndpoint->TdiBufferring; + + // + // Place the connection on the endpoint we'll accept it on. It is + // still referenced from when it was created. + // + + AcceptEndpoint->Common.VcConnecting.Connection = connection; + + // + // Set up a referenced pointer from the connection to the accept + // endpoint. + // + + REFERENCE_ENDPOINT( AcceptEndpoint ); + connection->Endpoint = AcceptEndpoint; + + // + // Set up a referenced pointer to the listening endpoint. This is + // necessary so that the endpoint does not go away until all + // accepted endpoints have gone away. Without this, a connect + // indication could occur on a TDI address object held open + // by an accepted endpoint after the listening endpoint has + // been closed and the memory for it deallocated. + // + // Note that, since we didn't remove the reference above, we don't + // need to add it here. + // + + AcceptEndpoint->Common.VcConnecting.ListenEndpoint = ListenEndpoint; + + // + // Set the endpoint to the connected state. + // + + AcceptEndpoint->State = AfdEndpointStateConnected; + + // + // Set up a referenced pointer in the accepted endpoint to the + // TDI address object. + // + + ObReferenceObject( ListenEndpoint->AddressFileObject ); + AfdRecordAddrRef(); + + AcceptEndpoint->AddressFileObject = ListenEndpoint->AddressFileObject; + AcceptEndpoint->AddressDeviceObject = ListenEndpoint->AddressDeviceObject; + + // + // Setup the active event bits on the accepted endpoint. We'll start with + // the ones in the listening endpoint, as these are the OR of all active + // bits for the listening endpoint and all unaccepted connections. + // + // Note that we don't actually signal the events here, as the DLL will + // invoke WSAEventSelect() if necessary on the socket while processing + // the accept() API. + // + + eventsActive = ListenEndpoint->EventsActive; + + if( eventsActive & AFD_POLL_RECEIVE ) { + + if( !IS_DATA_ON_CONNECTION( connection ) && + ( !AcceptEndpoint->InLine || + !IS_EXPEDITED_DATA_ON_CONNECTION( connection ) ) ) { + + eventsActive &= ~AFD_POLL_RECEIVE; + + } + + } + + if( eventsActive & AFD_POLL_RECEIVE_EXPEDITED ) { + + if( AcceptEndpoint->InLine || + !IS_EXPEDITED_DATA_ON_CONNECTION( connection ) ) { + + eventsActive &= ~AFD_POLL_RECEIVE_EXPEDITED; + + } + + } + + if( eventsActive & AFD_POLL_DISCONNECT ) { + + if( !connection->DisconnectIndicated ) { + + eventsActive &= ~AFD_POLL_DISCONNECT; + } + + } + + if( eventsActive & AFD_POLL_ABORT ) { + + if( !connection->AbortIndicated ) { + + eventsActive &= ~AFD_POLL_ABORT; + + } + + } + + AcceptEndpoint->EventsActive = eventsActive | AFD_POLL_SEND; + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdAcceptCore: Endp %08lX, Active %08lX\n", + AcceptEndpoint, + AcceptEndpoint->EventsActive + )); + } + + AfdReleaseSpinLock( &AcceptEndpoint->SpinLock, oldIrql ); + + return STATUS_SUCCESS; + +} // AfdAcceptCore + + +VOID +AfdInitiateListenBacklogReplenish ( + IN PAFD_ENDPOINT Endpoint + ) + +/*++ + +Routine Description: + + Queues a work item to begin replenishing the listen backlog + on a listening endpoint. + +Arguments: + + Endpoint - the listening endpoint on which to replenish the + backlog. + +Return Value: + + None. + +--*/ + +{ + PAFD_WORK_ITEM afdWorkItem; + + // + // Reference the endpoint so that it won't go away until we're + // done with it. + // + + REFERENCE_ENDPOINT( Endpoint ); + + // + // Queue a work item to an executive worker thread. + // + + afdWorkItem = AfdAllocateWorkItem(); + ASSERT( afdWorkItem != NULL ); + + afdWorkItem->Context = Endpoint; + + AfdQueueWorkItem( + AfdDoListenBacklogReplenish, + afdWorkItem + ); + +} // AfdInitiateListenBacklogReplenish + + +VOID +AfdDoListenBacklogReplenish ( + IN PVOID Context + ) + +/*++ + +Routine Description: + + The worker routine for replenishing the listen backlog on a + listening endpoint. This routine only runs in the context of + an executive worker thread. + +Arguments: + + Context - Points to an AFD_WORK_ITEM structure. The Context field + of this structure points to the endpoint on which to replenish + the listen backlog. + +Return Value: + + None. + +--*/ + +{ + PAFD_ENDPOINT endpoint; + PAFD_WORK_ITEM afdWorkItem; + + PAGED_CODE( ); + + ASSERT( Context != NULL ); + + afdWorkItem = Context; + endpoint = (PAFD_ENDPOINT)afdWorkItem->Context; + + AfdFreeWorkItem( afdWorkItem ); + + ASSERT( endpoint->Type == AfdBlockTypeVcListening ); + + // + // If the endpoint's state changed, don't replenish the backlog. + // + + if ( endpoint->State != AfdEndpointStateListening ) { + DEREFERENCE_ENDPOINT( endpoint ); + return; + } + + // + // Fill up the free connection backlog. + // + + AfdReplenishListenBacklog( endpoint ); + + // + // Clean up and return. + // + + DEREFERENCE_ENDPOINT( endpoint ); + + return; + +} // AfdDoListenBacklogReplenish + + +VOID +AfdReplenishListenBacklog ( + IN PAFD_ENDPOINT Endpoint + ) + +/*++ + +Routine Description: + + Does the actual work of filling up the listen backlog on a listening + endpoint. + +Arguments: + + Endpoint - the listening endpoint on which to replenish the + listen backlog. + +Return Value: + + None--any errors are ignored and the backlog may be refilled + at a later time. + +--*/ + +{ + NTSTATUS status; + LONG result; + + PAGED_CODE( ); + + ASSERT( Endpoint->Type == AfdBlockTypeVcListening ); + + + // + // Decrement the count of failed connection additions. + // + + result = InterlockedDecrement( + &Endpoint->Common.VcListening.FailedConnectionAdds + ); + + // + // Continue opening new free conections until we've hit the + // backlog or a connection open fails. + // + // If the result of the decrement is negative, then we are either + // all set on the connection count or else have available extra + // connection objects on the listening endpoint. These connections + // have been reused from prior connections which have now + // terminated. + // + + while ( result >= 0 ) { + + status = AfdAddFreeConnection( Endpoint ); + + if ( !NT_SUCCESS(status) ) { + + InterlockedIncrement( + &Endpoint->Common.VcListening.FailedConnectionAdds + ); + + IF_DEBUG(ACCEPT) { + KdPrint(( "AfdReplenishListenBacklog: AfdAddFreeConnection failed: %X, " + "fail count = %ld\n", status, + Endpoint->Common.VcListening.FailedConnectionAdds )); + } + + return; + } + + result = InterlockedDecrement( + &Endpoint->Common.VcListening.FailedConnectionAdds + ); + } + + // + // Correct the counter to reflect the number of connections + // we have available. Then just return from here. + // + + InterlockedIncrement( + &Endpoint->Common.VcListening.FailedConnectionAdds + ); + + return; + +} // AfdReplenishListenBacklog + + +NTSTATUS +AfdSuperAccept ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Initial entrypoint for handling super accept IRPs. A super accept + combines several operations for high-performance connection + acceptance. The combined operations are waiting for an incoming + connection, accepting it, retrieving the local and remote socket + addresses, and receiving the first chunk of data on the connection. + + This routine verifies parameters, initializes data structures to be + used for the request, and initiates the I/O. + +Arguments: + + Irp - a pointer to a transmit file IRP. + + IrpSp - Our stack location for this IRP. + +Return Value: + + STATUS_PENDING if the request was initiated successfully, or a + failure status code if there was an error. + +--*/ + +{ + PAFD_ENDPOINT listenEndpoint; + PAFD_ENDPOINT acceptEndpoint; + PFILE_OBJECT acceptFileObject; + PAFD_SUPER_ACCEPT_INFO superAcceptInfo = NULL; + NTSTATUS status; + PIO_STACK_LOCATION nextIrpSp; + + PAGED_CODE( ); + + // + // Set up local variables. + // + + listenEndpoint = IrpSp->FileObject->FsContext; + superAcceptInfo = Irp->AssociatedIrp.SystemBuffer; + + // + // Validate the input information. The input buffer must be large + // enough to hold all the input information, plus some extra to use + // here to hold the local address. The output buffer must be + // non-NULL and large enough to hold the specified information. + // + // + + if ( listenEndpoint->Type != AfdBlockTypeVcListening + + || + + IrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof(AFD_SUPER_ACCEPT_INFO) + + || + + IrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof(AFD_SUPER_ACCEPT_INFO) + superAcceptInfo->LocalAddressLength + + || + + Irp->MdlAddress == NULL + + || + + IrpSp->Parameters.DeviceIoControl.OutputBufferLength < + superAcceptInfo->ReceiveDataLength + + superAcceptInfo->LocalAddressLength + + superAcceptInfo->RemoteAddressLength ) { + +#if DBG + if( listenEndpoint->Type != AfdBlockTypeVcListening ) { + KdPrint(( + "AfdSuperAccept: non-listening endpoint @ %08lX\n", + listenEndpoint + )); + } +#endif + + superAcceptInfo = NULL; + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + // + // Obtain a pointer to the endpoint on which we're going to + // accept the connection. + // + + status = ObReferenceObjectByHandle( + superAcceptInfo->AcceptHandle, + 0L, // DesiredAccess + *IoFileObjectType, + KernelMode, + &superAcceptInfo->AcceptFileObject, + NULL + ); + + if ( !NT_SUCCESS(status) ) { + superAcceptInfo = NULL; + goto complete; + } + + acceptFileObject = superAcceptInfo->AcceptFileObject; + acceptEndpoint = acceptFileObject->FsContext; + + superAcceptInfo->AcceptEndpoint = acceptEndpoint; + + // + // We may have a file object that is not an AFD endpoint. Make sure + // that this is an actual AFD endpoint. + // + + if ( acceptEndpoint->Type != AfdBlockTypeEndpoint ) { + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + ASSERT( InterlockedIncrement( &acceptEndpoint->ObReferenceBias ) > 0 ); + + // + // Add another free connection to replace the one we're accepting. + // Also, add extra to account for past failures in calls to + // AfdAddFreeConnection(). + // + + InterlockedIncrement( + &listenEndpoint->Common.VcListening.FailedConnectionAdds + ); + + AfdReplenishListenBacklog( listenEndpoint ); + + // + // Start off by building a AFD_WAIT_FOR_LISTEN_LIFO IRP, using the current + // IRP and the next stack location on it. + // + + nextIrpSp = IoGetNextIrpStackLocation( Irp ); + + Irp->AssociatedIrp.SystemBuffer = &superAcceptInfo->ListenResponseInfo; + nextIrpSp->FileObject = IrpSp->FileObject; + nextIrpSp->DeviceObject = IoGetRelatedDeviceObject( IrpSp->FileObject ); + nextIrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL; + + + nextIrpSp->Parameters.DeviceIoControl.OutputBufferLength = + sizeof(AFD_LISTEN_RESPONSE_INFO) + superAcceptInfo->RemoteAddressLength; + nextIrpSp->Parameters.DeviceIoControl.InputBufferLength = 0; + nextIrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_AFD_WAIT_FOR_LISTEN_LIFO; + + IoSetCompletionRoutine( + Irp, + AfdRestartSuperAcceptListen, + superAcceptInfo, + TRUE, + TRUE, + TRUE + ); + + // + // Perform the listen wait. We'll continue processing from the + // completion routine. + // + + IoCallDriver( IrpSp->DeviceObject, Irp ); + + return STATUS_PENDING; + +complete: + + if ( superAcceptInfo != NULL ) { + ASSERT( InterlockedDecrement( &acceptEndpoint->ObReferenceBias ) >= 0 ); + + ObDereferenceObject( superAcceptInfo->AcceptFileObject ); + } + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, 0 ); + + return status; + +} // AfdSuperAccept + + +NTSTATUS +AfdRestartSuperAcceptListen ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + The completion routine for the AFD wait for listen IRP portion + of a super accept. + +Arguments: + + DeviceObject - the devoce object on which the request is completing. + + Irp - The super accept IRP. + + Context - points to the super accept request information. + +Return Value: + + STATUS_SUCCESS if the I/O system should complete the super accept + request, or STATUS_MORE_PROCESSING_REQUIRED if the super accept + request is still being processed. + +--*/ + +{ + PAFD_ENDPOINT listenEndpoint; + PAFD_ENDPOINT acceptEndpoint; + PAFD_CONNECTION connection; + PAFD_SUPER_ACCEPT_INFO superAcceptInfo; + NTSTATUS status; + PIO_STACK_LOCATION irpSp; + ULONG bytesCopied; + PMDL mdl; + + // + // Initialize some locals. + // + + superAcceptInfo = Context; + irpSp = IoGetCurrentIrpStackLocation( Irp ); + listenEndpoint = irpSp->FileObject->FsContext; + acceptEndpoint = superAcceptInfo->AcceptEndpoint; + + // + // If pending has been returned for this irp then mark the current + // stack as pending. + // + + if ( Irp->PendingReturned ) { + IoMarkIrpPending( Irp ); + } + + // + // Fix up the system buffer pointer in the IRP. + // + + ASSERT( Irp->AssociatedIrp.SystemBuffer == &superAcceptInfo->ListenResponseInfo ); + Irp->AssociatedIrp.SystemBuffer = superAcceptInfo; + + // + // If the IRP failed, quit processing. + // + + if ( !NT_SUCCESS(Irp->IoStatus.Status) ) { + ASSERT( InterlockedDecrement( &acceptEndpoint->ObReferenceBias ) >= 0 ); + + ObDereferenceObject( superAcceptInfo->AcceptFileObject ); + InterlockedDecrement( + &listenEndpoint->Common.VcListening.FailedConnectionAdds + ); + return Irp->IoStatus.Status; + } + + // + // Copy over the address information to the user's buffer. + // + + status = TdiCopyBufferToMdl( + &superAcceptInfo->ListenResponseInfo.RemoteAddress, + 0, + superAcceptInfo->RemoteAddressLength, + Irp->MdlAddress, + superAcceptInfo->ReceiveDataLength + + superAcceptInfo->LocalAddressLength, + &bytesCopied + ); + if ( !NT_SUCCESS(status) ) { + ASSERT( InterlockedDecrement( &acceptEndpoint->ObReferenceBias ) >= 0 ); + + ObDereferenceObject( superAcceptInfo->AcceptFileObject ); + InterlockedDecrement( + &listenEndpoint->Common.VcListening.FailedConnectionAdds + ); + Irp->IoStatus.Status = status; + return status; + } + + // + // Now do the actual connection acceptance. + // + + status = AfdAcceptCore( + listenEndpoint, + acceptEndpoint, + superAcceptInfo->ListenResponseInfo.Sequence + ); + if ( !NT_SUCCESS(status) ) { + ASSERT( InterlockedDecrement( &acceptEndpoint->ObReferenceBias ) >= 0 ); + + ObDereferenceObject( superAcceptInfo->AcceptFileObject ); + InterlockedDecrement( + &listenEndpoint->Common.VcListening.FailedConnectionAdds + ); + return STATUS_SUCCESS; + } + + // + // The AFD connection object should now be in the endpoiont. + // + + connection = AFD_CONNECTION_FROM_ENDPOINT( acceptEndpoint ); + ASSERT( connection != NULL ); + + // + // The endpoint is now connected and ready to go. Get the local + // address for the connection. We'll need an MDL to map the portion + // of the user buffer that will receive the local address. + // + + mdl = IoAllocateMdl( + (PCHAR)MmGetMdlVirtualAddress( Irp->MdlAddress ) + superAcceptInfo->ReceiveDataLength, + superAcceptInfo->LocalAddressLength, + FALSE, + FALSE, + NULL + ); + if ( mdl == NULL ) { + ASSERT( InterlockedDecrement( &acceptEndpoint->ObReferenceBias ) >= 0 ); + + ObDereferenceObject( superAcceptInfo->AcceptFileObject ); + return STATUS_SUCCESS; + } + + // + // Set up this new MDL to describe the appropriate portion of the + // buffer. + // + + IoBuildPartialMdl( + Irp->MdlAddress, + mdl, + (PCHAR)MmGetMdlVirtualAddress( Irp->MdlAddress ) + superAcceptInfo->ReceiveDataLength, + superAcceptInfo->LocalAddressLength + ); + + // + // Set up the IRP to query the local address of this endpoint. We'll + // hold on to the original MDL value in the AcceptHandle field, which + // we don't need any longer. + // + + superAcceptInfo->AcceptHandle = (HANDLE)Irp->MdlAddress; + Irp->MdlAddress = mdl; + + TdiBuildQueryInformation( + Irp, + connection->DeviceObject, + connection->FileObject, + AfdRestartSuperAcceptGetAddress, + superAcceptInfo, + TDI_QUERY_ADDRESS_INFO, + Irp->MdlAddress + ); + + // + // Perform the local address query. We'll continue processing from + // the completion routine. + // + + IoCallDriver( connection->DeviceObject, Irp ); + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // AfdRestartSuperAcceptListen + + +NTSTATUS +AfdRestartSuperAcceptGetAddress ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + The completion routine for the AFD wait for query local address + portion of a super accept. + +Arguments: + + DeviceObject - the devoce object on which the request is completing. + + Irp - The super accept IRP. + + Context - points to the super accept request information. + +Return Value: + + STATUS_SUCCESS if the I/O system should complete the super accept + request, or STATUS_MORE_PROCESSING_REQUIRED if the super accept + request is still being processed. + +--*/ + +{ + PAFD_SUPER_ACCEPT_INFO superAcceptInfo; + PAFD_ENDPOINT acceptEndpoint; + PIO_STACK_LOCATION nextIrpSp; + PFILE_OBJECT acceptFileObject; + PMDL partialMdl; + + // + // Initialize some locals. + // + + superAcceptInfo = Context; + acceptEndpoint = superAcceptInfo->AcceptEndpoint; + acceptFileObject = superAcceptInfo->AcceptFileObject; + + // + // If pending has been returned for this irp then mark the current + // stack as pending. + // + + if ( Irp->PendingReturned ) { + IoMarkIrpPending( Irp ); + } + + // + // Free the MDL we temporarily allocated and fix up the MDL pointer + // in the IRP. + // + + IoFreeMdl( Irp->MdlAddress ); + + Irp->MdlAddress = (PMDL)superAcceptInfo->AcceptHandle; + + // + // If the caller didn't want to receive any data on the connection, + // or if the query for the local address failed, then we're done + // now. + // + + if ( superAcceptInfo->ReceiveDataLength == 0 || + !NT_SUCCESS( Irp->IoStatus.Status ) ) { + ASSERT( InterlockedDecrement( &acceptEndpoint->ObReferenceBias ) >= 0 ); + + ObDereferenceObject( superAcceptInfo->AcceptFileObject ); + Irp->IoStatus.Information = 0; + return STATUS_SUCCESS; + } + + // + // Create a partial MDL describing the portion of the user's buffer + // to be used for receive data. Note that we cannot use the entire + // user's buffer, as the end of the buffer is used for the local and + // remote addresses. + // + + partialMdl = IoAllocateMdl( + MmGetMdlVirtualAddress( Irp->MdlAddress ), + superAcceptInfo->ReceiveDataLength, + FALSE, // SecondaryBuffer + FALSE, // ChargeQuota + NULL // Irp + ); + + if( partialMdl == NULL ) { + ASSERT( InterlockedDecrement( &acceptEndpoint->ObReferenceBias ) >= 0 ); + + ObDereferenceObject( superAcceptInfo->AcceptFileObject ); + Irp->IoStatus.Information = 0; + return STATUS_SUCCESS; + } + + IoBuildPartialMdl( + Irp->MdlAddress, + partialMdl, + MmGetMdlVirtualAddress( Irp->MdlAddress ), + superAcceptInfo->ReceiveDataLength + ); + + Irp->MdlAddress = partialMdl; + + // + // Prepare the IRP to be used to receive the first chunk of data on + // the connection. + // + // Also note that we send ourselves an IRP_MJ_READ IRP because + // the I/O subsystem has already probed & locked the output buffer, + // which just happens to look just like an IRP_MJ_READ IRP. + // + + nextIrpSp = IoGetNextIrpStackLocation( Irp ); + + nextIrpSp->FileObject = acceptFileObject; + nextIrpSp->DeviceObject = IoGetRelatedDeviceObject( acceptFileObject ); + nextIrpSp->MajorFunction = IRP_MJ_READ; + + nextIrpSp->Parameters.Read.Length = superAcceptInfo->ReceiveDataLength; + nextIrpSp->Parameters.Read.Key = 0; + nextIrpSp->Parameters.Read.ByteOffset.QuadPart = 0; + + IoSetCompletionRoutine( + Irp, + AfdRestartSuperAcceptReceive, + superAcceptInfo, + TRUE, + TRUE, + TRUE + ); + + // + // Perform the receive. We'll continue processing from + // the completion routine. + // + + IoCallDriver( nextIrpSp->DeviceObject, Irp ); + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // AfdRestartSuperAcceptGetAddress + + +NTSTATUS +AfdRestartSuperAcceptReceive ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + The completion routine for the AFD receive portion of a super accept. + +Arguments: + + DeviceObject - the devoce object on which the request is completing. + + Irp - The super accept IRP. + + Context - points to the super accept request information. + +Return Value: + + STATUS_SUCCESS if the I/O system should complete the super accept + request, or STATUS_MORE_PROCESSING_REQUIRED if the super accept + request is still being processed. + +--*/ + +{ + PAFD_SUPER_ACCEPT_INFO superAcceptInfo; + PIO_STACK_LOCATION nextIrpSp; + PFILE_OBJECT acceptFileObject; + + // + // Initialize some locals. + // + + superAcceptInfo = Context; + acceptFileObject = superAcceptInfo->AcceptFileObject; + + // + // If pending has been returned for this irp then mark the current + // stack as pending. + // + + if ( Irp->PendingReturned ) { + IoMarkIrpPending( Irp ); + } + + // + // Free the partial MDL we temporarily allocated and fix up the + // MDL pointer in the IRP. + // + + IoFreeMdl( Irp->MdlAddress ); + + Irp->MdlAddress = (PMDL)superAcceptInfo->AcceptHandle; + + // + // Dereference the accept file object and tell IO to complete this IRP. + // + +#if DBG + { + PAFD_ENDPOINT endpoint; + + endpoint = ((PFILE_OBJECT)superAcceptInfo->AcceptFileObject)->FsContext; + ASSERT( endpoint != NULL ); + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + ASSERT( InterlockedDecrement( &endpoint->ObReferenceBias ) >= 0 ); + } +#endif + + ObDereferenceObject( superAcceptInfo->AcceptFileObject ); + return STATUS_SUCCESS; + +} // AfdRestartSuperAcceptReceive + + +NTSTATUS +AfdDeferAccept ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Defers acceptance of an incoming connection for which an + AFD_WAIT_FOR_LISTEN IOCTL has already completed. The caller + may specify that the connection be deferred for later acceptance + or rejected totally. + +Arguments: + + Irp - a pointer to a transmit file IRP. + + IrpSp - Our stack location for this IRP. + +Return Value: + + STATUS_SUCCESS if the request was completed successfully, or a + failure status code if there was an error. + +--*/ + +{ + NTSTATUS status; + PAFD_DEFER_ACCEPT_INFO deferAcceptInfo; + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + KIRQL oldIrql; + + PAGED_CODE( ); + + // + // Set up local variables. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( endpoint->Type == AfdBlockTypeVcListening ); + deferAcceptInfo = Irp->AssociatedIrp.SystemBuffer; + + Irp->IoStatus.Information = 0; + + // + // Make sure that this request is valid. + // + + if( endpoint->Type != AfdBlockTypeVcListening || + IrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof(AFD_DEFER_ACCEPT_INFO) ) { + + status = STATUS_INVALID_PARAMETER; + goto complete; + + } + + // + // Find the specified connection. If it cannot be found, then this + // is a bogus request. + // + + connection = AfdGetReturnedConnection( + endpoint, + deferAcceptInfo->Sequence + ); + + if( connection == NULL ) { + + status = STATUS_INVALID_PARAMETER; + goto complete; + + } + + ASSERT( connection->Type == AfdBlockTypeConnection ); + + // + // If this is a request to reject the accepted connection, then + // abort the connection. Otherwise (this is a request to defer + // acceptance until later) then insert the connection at the *head* + // of the endpoint's unaccepted connection queue. + // + + if( deferAcceptInfo->Reject ) { + + // + // Abort the connection. + // + + AfdAbortConnection( connection ); + + // + // Reenable the accept event bit, and if there are additional + // unaccepted connections on the endpoint, post another event. + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + endpoint->EventsActive &= ~AFD_POLL_ACCEPT; + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdDeferAccept: Endp %08lX, Active %08lX\n", + endpoint, + endpoint->EventsActive + )); + } + + if( !IsListEmpty( &endpoint->Common.VcListening.UnacceptedConnectionListHead ) ) { + + AfdIndicateEventSelectEvent( + endpoint, + AFD_POLL_ACCEPT_BIT, + STATUS_SUCCESS + ); + + } + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // Add another free connection to replace the one we're rejecting. + // Also, add extra to account for past failures in calls to + // AfdAddFreeConnection(). + // + + InterlockedIncrement( + &endpoint->Common.VcListening.FailedConnectionAdds + ); + + AfdReplenishListenBacklog( endpoint ); + + } else { + + // + // Restore the connection's state before putting it back + // on the queue. + // + + connection->State = AfdConnectionStateUnaccepted; + + ExInterlockedInsertHeadList( + &endpoint->Common.VcListening.UnacceptedConnectionListHead, + &connection->ListEntry, + &AfdSpinLock + ); + + } + + status = STATUS_SUCCESS; + +complete: + + Irp->IoStatus.Status = status; + ASSERT( Irp->CancelRoutine == NULL ); + + IoCompleteRequest( Irp, AfdPriorityBoost ); + + return status; + +} // AfdDeferAccept + diff --git a/private/ntos/afd/afd.rc b/private/ntos/afd/afd.rc new file mode 100644 index 000000000..87cdb1048 --- /dev/null +++ b/private/ntos/afd/afd.rc @@ -0,0 +1,12 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Ancillary Function Driver for WinSock" +#define VER_INTERNALNAME_STR "afd.sys" +#define VER_ORIGINALFILENAME_STR "afd.sys" + +#include "common.ver" + diff --git a/private/ntos/afd/afddata.c b/private/ntos/afd/afddata.c new file mode 100644 index 000000000..115191526 --- /dev/null +++ b/private/ntos/afd/afddata.c @@ -0,0 +1,349 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + afddata.c + +Abstract: + + This module contains global data for AFD. + +Author: + + David Treadwell (davidtr) 21-Feb-1992 + +Revision History: + +--*/ + +#include "afdp.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( INIT, AfdInitializeData ) +#endif + +PDEVICE_OBJECT AfdDeviceObject; + +KSPIN_LOCK AfdSpinLock; + +PERESOURCE AfdResource; + +LIST_ENTRY AfdEndpointListHead; +LIST_ENTRY AfdDisconnectListHead; +LIST_ENTRY AfdPollListHead; +LIST_ENTRY AfdTransportInfoListHead; +LIST_ENTRY AfdConstrainedEndpointListHead; + +PKPROCESS AfdSystemProcess; + +// +// Global lookaside lists. These must always be in nonpaged pool, +// even when the driver is paged out. +// + +PAFD_LOOKASIDE_LISTS AfdLookasideLists; + +// +// Globals for dealing with AFD's executive worker thread. +// + +KSPIN_LOCK AfdWorkQueueSpinLock; +LIST_ENTRY AfdWorkQueueListHead; +BOOLEAN AfdWorkThreadRunning = FALSE; +WORK_QUEUE_ITEM AfdWorkQueueItem; + +// +// Globals to track the buffers used by AFD. +// + +ULONG AfdLargeBufferListDepth; +ULONG AfdMediumBufferListDepth; +ULONG AfdSmallBufferListDepth; + +CLONG AfdLargeBufferSize; // default == AfdBufferLengthForOnePage +CLONG AfdMediumBufferSize = AFD_DEFAULT_MEDIUM_BUFFER_SIZE; +CLONG AfdSmallBufferSize = AFD_DEFAULT_SMALL_BUFFER_SIZE; + +ULONG AfdCacheLineSize; +CLONG AfdBufferLengthForOnePage; + +// +// Globals for tuning TransmitFile(). +// + +LIST_ENTRY AfdQueuedTransmitFileListHead; +ULONG AfdActiveTransmitFileCount; +ULONG AfdMaxActiveTransmitFileCount; + +// +// Various pieces of configuration information, with default values. +// + +CLONG AfdStandardAddressLength = AFD_DEFAULT_STD_ADDRESS_LENGTH; +CCHAR AfdIrpStackSize = AFD_DEFAULT_IRP_STACK_SIZE; +CCHAR AfdPriorityBoost = AFD_DEFAULT_PRIORITY_BOOST; + +ULONG AfdFastSendDatagramThreshold = AFD_FAST_SEND_DATAGRAM_THRESHOLD; + +CLONG AfdReceiveWindowSize; +CLONG AfdSendWindowSize; + +CLONG AfdBufferMultiplier = AFD_DEFAULT_BUFFER_MULTIPLIER; + +CLONG AfdTransmitIoLength; +CLONG AfdMaxFastTransmit = AFD_DEFAULT_MAX_FAST_TRANSMIT; +CLONG AfdMaxFastCopyTransmit = AFD_DEFAULT_MAX_FAST_COPY_TRANSMIT; + +ULONG AfdEndpointsOpened = 0; +ULONG AfdEndpointsCleanedUp = 0; +ULONG AfdEndpointsClosed = 0; + +BOOLEAN AfdIgnorePushBitOnReceives = FALSE; + +BOOLEAN AfdEnableDynamicBacklog = AFD_DEFAULT_ENABLE_DYNAMIC_BACKLOG; +LONG AfdMinimumDynamicBacklog = AFD_DEFAULT_MINIMUM_DYNAMIC_BACKLOG; +LONG AfdMaximumDynamicBacklog = AFD_DEFAULT_MAXIMUM_DYNAMIC_BACKLOG; +LONG AfdDynamicBacklogGrowthDelta = AFD_DEFAULT_DYNAMIC_BACKLOG_GROWTH_DELTA; + +BOOLEAN AfdDisableRawSecurity = FALSE; + +// +// Global which holds AFD's discardable code handle, and a BOOLEAN +// that tells whether AFD is loaded. +// + +PVOID AfdDiscardableCodeHandle; +BOOLEAN AfdLoaded = FALSE; + +FAST_IO_DISPATCH AfdFastIoDispatch = +{ + 11, // SizeOfFastIoDispatch + NULL, // FastIoCheckIfPossible + AfdFastIoRead, // FastIoRead + AfdFastIoWrite, // FastIoWrite + NULL, // FastIoQueryBasicInfo + NULL, // FastIoQueryStandardInfo + NULL, // FastIoLock + NULL, // FastIoUnlockSingle + NULL, // FastIoUnlockAll + NULL, // FastIoUnlockAllByKey + AfdFastIoDeviceControl // FastIoDeviceControl +}; + +#if DBG +ULONG AfdDebug = 0; +ULONG AfdLocksAcquired = 0; +BOOLEAN AfdUsePrivateAssert = FALSE; +#endif + +// +// Some counters used for monitoring performance. These are not enabled +// in the normal build. +// + +#if AFD_PERF_DBG + +CLONG AfdFullReceiveIndications = 0; +CLONG AfdPartialReceiveIndications = 0; + +CLONG AfdFullReceiveDatagramIndications = 0; +CLONG AfdPartialReceiveDatagramIndications = 0; + +CLONG AfdFastPollsSucceeded = 0; +CLONG AfdFastPollsFailed = 0; + +CLONG AfdFastSendsSucceeded = 0; +CLONG AfdFastSendsFailed = 0; +CLONG AfdFastReceivesSucceeded = 0; +CLONG AfdFastReceivesFailed = 0; + +CLONG AfdFastSendDatagramsSucceeded = 0; +CLONG AfdFastSendDatagramsFailed = 0; +CLONG AfdFastReceiveDatagramsSucceeded = 0; +CLONG AfdFastReceiveDatagramsFailed = 0; + +BOOLEAN AfdDisableFastIo = FALSE; +BOOLEAN AfdDisableConnectionReuse = FALSE; + +#endif // AFD_PERF_DBG + +#if AFD_KEEP_STATS + +AFD_QUOTA_STATS AfdQuotaStats; +AFD_HANDLE_STATS AfdHandleStats; +AFD_QUEUE_STATS AfdQueueStats; +AFD_CONNECTION_STATS AfdConnectionStats; + +#endif // AFD_KEEP_STATS + +#if ENABLE_ABORT_TIMER_HACK +LARGE_INTEGER AfdAbortTimerTimeout; +#endif // ENABLE_ABORT_TIMER_HACK + +QOS AfdDefaultQos = + { + { // SendingFlowspec + -1, // TokenRate + -1, // TokenBucketSize + -1, // PeakBandwidth + -1, // Latency + -1, // DelayVariation + BestEffortService, // LevelOfGuarantee + 0, // CostOfCall + 1 // NetworkAvailability + }, + + { // ReceivingFlowspec + -1, // TokenRate + -1, // TokenBucketSize + -1, // PeakBandwidth + -1, // Latency + -1, // DelayVariation + BestEffortService, // LevelOfGuarantee + 0, // CostOfCall + 1 // NetworkAvailability + }, + + { // ProviderSpecific + 0, // len + NULL // buf + } + }; + +BOOLEAN +AfdInitializeData ( + VOID + ) +{ + PAGED_CODE( ); + +#if DBG || REFERENCE_DEBUG + AfdInitializeDebugData( ); +#endif + + // + // Initialize global spin locks and resources used by AFD. + // + + KeInitializeSpinLock( &AfdSpinLock ); + KeInitializeSpinLock( &AfdWorkQueueSpinLock ); + + AfdResource = AFD_ALLOCATE_POOL( + NonPagedPool, + sizeof(*AfdResource), + AFD_RESOURCE_POOL_TAG + ); + + if ( AfdResource == NULL ) { + return FALSE; + } + + ExInitializeResource( AfdResource ); + + // + // Initialize global lists. + // + + InitializeListHead( &AfdEndpointListHead ); + InitializeListHead( &AfdDisconnectListHead ); + InitializeListHead( &AfdPollListHead ); + InitializeListHead( &AfdTransportInfoListHead ); + InitializeListHead( &AfdWorkQueueListHead ); + InitializeListHead( &AfdConstrainedEndpointListHead ); + + InitializeListHead( &AfdQueuedTransmitFileListHead ); + + AfdCacheLineSize= HalGetDmaAlignmentRequirement( ); + + AfdBufferLengthForOnePage = PAGE_SIZE - AfdCalculateBufferSize( 4, 0 ); + AfdLargeBufferSize = AfdBufferLengthForOnePage; + +#if ENABLE_ABORT_TIMER_HACK + // + // Initialize the abort timer timeout value. + // + + AfdAbortTimerTimeout = RtlEnlargedIntegerMultiply( + AFD_ABORT_TIMER_TIMEOUT_VALUE, + -10*1000*1000 + ); +#endif // ENABLE_ABORT_TIMER_HACK + + // + // Set up buffer counts based on machine size. For smaller + // machines, it is OK to take the perf hit of the additional + // allocations in order to save the nonpaged pool overhead. + // + + switch ( MmQuerySystemSize( ) ) { + + case MmSmallSystem: + + AfdReceiveWindowSize = AFD_SM_DEFAULT_RECEIVE_WINDOW; + AfdSendWindowSize = AFD_SM_DEFAULT_SEND_WINDOW; + AfdTransmitIoLength = AFD_SM_DEFAULT_TRANSMIT_IO_LENGTH; + AfdLargeBufferListDepth = AFD_SM_DEFAULT_LARGE_LIST_DEPTH; + AfdMediumBufferListDepth = AFD_SM_DEFAULT_MEDIUM_LIST_DEPTH; + AfdSmallBufferListDepth = AFD_SM_DEFAULT_SMALL_LIST_DEPTH; + break; + + case MmMediumSystem: + + AfdReceiveWindowSize = AFD_MM_DEFAULT_RECEIVE_WINDOW; + AfdSendWindowSize = AFD_MM_DEFAULT_SEND_WINDOW; + AfdTransmitIoLength = AFD_MM_DEFAULT_TRANSMIT_IO_LENGTH; + AfdLargeBufferListDepth = AFD_MM_DEFAULT_LARGE_LIST_DEPTH; + AfdMediumBufferListDepth = AFD_MM_DEFAULT_MEDIUM_LIST_DEPTH; + AfdSmallBufferListDepth = AFD_MM_DEFAULT_SMALL_LIST_DEPTH; + break; + + case MmLargeSystem: + + AfdReceiveWindowSize = AFD_LM_DEFAULT_RECEIVE_WINDOW; + AfdSendWindowSize = AFD_LM_DEFAULT_SEND_WINDOW; + AfdTransmitIoLength = AFD_LM_DEFAULT_TRANSMIT_IO_LENGTH; + AfdLargeBufferListDepth = AFD_LM_DEFAULT_LARGE_LIST_DEPTH; + AfdMediumBufferListDepth = AFD_LM_DEFAULT_MEDIUM_LIST_DEPTH; + AfdSmallBufferListDepth = AFD_LM_DEFAULT_SMALL_LIST_DEPTH; + break; + + default: + + ASSERT( FALSE ); + } + + if( MmIsThisAnNtAsSystem() ) { + + // + // On the NT Server product, there is no maximum active TransmitFile + // count. Setting this counter to zero short-circuits a number of + // tests for queueing TransmitFile IRPs. + // + + AfdMaxActiveTransmitFileCount = 0; + + } else { + + // + // On the workstation product, the TransmitFile default I/O length + // is always a page size. This conserves memory on workstatioons + // and keeps the server product's performance high. + // + + AfdTransmitIoLength = PAGE_SIZE; + + // + // Enforce a maximum active TransmitFile count. + // + + AfdMaxActiveTransmitFileCount = + AFD_DEFAULT_MAX_ACTIVE_TRANSMIT_FILE_COUNT; + + } + + return TRUE; + +} // AfdInitializeData + diff --git a/private/ntos/afd/afddata.h b/private/ntos/afd/afddata.h new file mode 100644 index 000000000..fcb3e3cc0 --- /dev/null +++ b/private/ntos/afd/afddata.h @@ -0,0 +1,201 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + init.c + +Abstract: + + This module declares global data for AFD. + +Author: + + David Treadwell (davidtr) 21-Feb-1992 + +Revision History: + +--*/ + +#ifndef _AFDDATA_ +#define _AFDDATA_ + +extern PDEVICE_OBJECT AfdDeviceObject; + +extern KSPIN_LOCK AfdSpinLock; + +extern PERESOURCE AfdResource; +extern LIST_ENTRY AfdEndpointListHead; +extern LIST_ENTRY AfdDisconnectListHead; +extern LIST_ENTRY AfdPollListHead; +extern LIST_ENTRY AfdTransportInfoListHead; +extern LIST_ENTRY AfdConstrainedEndpointListHead; + +extern PKPROCESS AfdSystemProcess; +extern FAST_IO_DISPATCH AfdFastIoDispatch; + +// +// Global lookaside lists. These must always be in nonpaged pool, +// even when the driver is paged out. +// + +PAFD_LOOKASIDE_LISTS AfdLookasideLists; + +// +// Globals for dealing with AFD's executive worker thread. +// + +extern KSPIN_LOCK AfdWorkQueueSpinLock; +extern LIST_ENTRY AfdWorkQueueListHead; +extern BOOLEAN AfdWorkThreadRunning; +extern WORK_QUEUE_ITEM AfdWorkQueueItem; + +// +// Globals to track the buffers used by AFD. +// + +extern ULONG AfdLargeBufferListDepth; +#define AFD_SM_DEFAULT_LARGE_LIST_DEPTH 0 +#define AFD_MM_DEFAULT_LARGE_LIST_DEPTH 2 +#define AFD_LM_DEFAULT_LARGE_LIST_DEPTH 10 + +extern ULONG AfdMediumBufferListDepth; +#define AFD_SM_DEFAULT_MEDIUM_LIST_DEPTH 4 +#define AFD_MM_DEFAULT_MEDIUM_LIST_DEPTH 8 +#define AFD_LM_DEFAULT_MEDIUM_LIST_DEPTH 24 + +extern ULONG AfdSmallBufferListDepth; +#define AFD_SM_DEFAULT_SMALL_LIST_DEPTH 8 +#define AFD_MM_DEFAULT_SMALL_LIST_DEPTH 16 +#define AFD_LM_DEFAULT_SMALL_LIST_DEPTH 32 + +extern CLONG AfdLargeBufferSize; +// default value is AfdBufferLengthForOnePage + +extern CLONG AfdMediumBufferSize; +#define AFD_DEFAULT_MEDIUM_BUFFER_SIZE 1504 + +extern CLONG AfdSmallBufferSize; +#define AFD_DEFAULT_SMALL_BUFFER_SIZE 128 + +extern CLONG AfdStandardAddressLength; +#define AFD_DEFAULT_STD_ADDRESS_LENGTH sizeof(TA_IP_ADDRESS) + +extern ULONG AfdCacheLineSize; +extern CLONG AfdBufferLengthForOnePage; + +// +// Globals for tuning TransmitFile(). +// + +extern LIST_ENTRY AfdQueuedTransmitFileListHead; +extern ULONG AfdActiveTransmitFileCount; +extern ULONG AfdMaxActiveTransmitFileCount; +#define AFD_DEFAULT_MAX_ACTIVE_TRANSMIT_FILE_COUNT 2 + +// +// Various pieces of configuration information, with default values. +// + +extern CCHAR AfdIrpStackSize; +#define AFD_DEFAULT_IRP_STACK_SIZE 4 + +extern CCHAR AfdPriorityBoost; +#define AFD_DEFAULT_PRIORITY_BOOST 2 + +extern ULONG AfdFastSendDatagramThreshold; +#define AFD_FAST_SEND_DATAGRAM_THRESHOLD 1024 + +extern PVOID AfdDiscardableCodeHandle; +extern BOOLEAN AfdLoaded; + +extern CLONG AfdReceiveWindowSize; +#define AFD_LM_DEFAULT_RECEIVE_WINDOW 8192 +#define AFD_MM_DEFAULT_RECEIVE_WINDOW 8192 +#define AFD_SM_DEFAULT_RECEIVE_WINDOW 4096 + +extern CLONG AfdSendWindowSize; +#define AFD_LM_DEFAULT_SEND_WINDOW 8192 +#define AFD_MM_DEFAULT_SEND_WINDOW 8192 +#define AFD_SM_DEFAULT_SEND_WINDOW 4096 + +extern CLONG AfdBufferMultiplier; +#define AFD_DEFAULT_BUFFER_MULTIPLIER 4 + +extern CLONG AfdTransmitIoLength; +#define AFD_LM_DEFAULT_TRANSMIT_IO_LENGTH 65536 +#define AFD_MM_DEFAULT_TRANSMIT_IO_LENGTH (PAGE_SIZE*2) +#define AFD_SM_DEFAULT_TRANSMIT_IO_LENGTH PAGE_SIZE + +extern CLONG AfdMaxFastTransmit; +#define AFD_DEFAULT_MAX_FAST_TRANSMIT 65536 +extern CLONG AfdMaxFastCopyTransmit; +#define AFD_DEFAULT_MAX_FAST_COPY_TRANSMIT 128 + +extern ULONG AfdEndpointsOpened; +extern ULONG AfdEndpointsCleanedUp; +extern ULONG AfdEndpointsClosed; + +extern BOOLEAN AfdIgnorePushBitOnReceives; + +extern BOOLEAN AfdEnableDynamicBacklog; +#define AFD_DEFAULT_ENABLE_DYNAMIC_BACKLOG FALSE + +extern LONG AfdMinimumDynamicBacklog; +#define AFD_DEFAULT_MINIMUM_DYNAMIC_BACKLOG 0 + +extern LONG AfdMaximumDynamicBacklog; +#define AFD_DEFAULT_MAXIMUM_DYNAMIC_BACKLOG 0 + +extern LONG AfdDynamicBacklogGrowthDelta; +#define AFD_DEFAULT_DYNAMIC_BACKLOG_GROWTH_DELTA 0 + +extern BOOLEAN AfdDisableRawSecurity; + +#if AFD_PERF_DBG + +extern CLONG AfdFullReceiveIndications; +extern CLONG AfdPartialReceiveIndications; + +extern CLONG AfdFullReceiveDatagramIndications; +extern CLONG AfdPartialReceiveDatagramIndications; + +extern CLONG AfdFastPollsSucceeded; +extern CLONG AfdFastPollsFailed; + +extern CLONG AfdFastSendsSucceeded; +extern CLONG AfdFastSendsFailed; +extern CLONG AfdFastReceivesSucceeded; +extern CLONG AfdFastReceivesFailed; + +extern CLONG AfdFastSendDatagramsSucceeded; +extern CLONG AfdFastSendDatagramsFailed; +extern CLONG AfdFastReceiveDatagramsSucceeded; +extern CLONG AfdFastReceiveDatagramsFailed; + +extern BOOLEAN AfdDisableFastIo; +extern BOOLEAN AfdDisableConnectionReuse; + +#endif // if AFD_PERF_DBG + +#if AFD_KEEP_STATS + +extern AFD_QUOTA_STATS AfdQuotaStats; +extern AFD_HANDLE_STATS AfdHandleStats; +extern AFD_QUEUE_STATS AfdQueueStats; +extern AFD_CONNECTION_STATS AfdConnectionStats; + +#endif // if AFD_KEEP_STATS + +#if DBG +extern BOOLEAN AfdUsePrivateAssert; +#endif + +#if ENABLE_ABORT_TIMER_HACK +extern LARGE_INTEGER AfdAbortTimerTimeout; +#endif // ENABLE_ABORT_TIMER_HACK + +extern QOS AfdDefaultQos; + +#endif // ndef _AFDDATA_ diff --git a/private/ntos/afd/afdkd/addr.c b/private/ntos/afd/afdkd/addr.c new file mode 100644 index 000000000..61f9f3aea --- /dev/null +++ b/private/ntos/afd/afdkd/addr.c @@ -0,0 +1,139 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + addr.c + +Abstract: + + Implements the addr command. + +Author: + + Keith Moore (keithmo) 19-Apr-1995 + +Environment: + + User Mode. + +Revision History: + +--*/ + + +#include "afdkdp.h" +#pragma hdrstop + + +// +// Public functions. +// + +DECLARE_API( addr ) + +/*++ + +Routine Description: + + Dumps the TRANSPORT_ADDRESS structure at the specified address. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + + DWORD address = 0; + ULONG result; + UCHAR transportAddress[MAX_TRANSPORT_ADDR]; + PTA_IP_ADDRESS ipAddress = (PTA_IP_ADDRESS)transportAddress; + + // + // Snag the address from the command line. + // + + sscanf( args, "%lx", &address ); + + if( address == 0 ) { + + dprintf( "use: addr address\n" ); + return; + + } + + // + // Assume the address is 22 bytes long (the same size as + // a TA_IP_ADDRESS structure). After we perform this initial + // read, we can examine the structure to determine its actual + // size. If it's really larger than 22 bytes, we'll reread + // the entire address structure. + // + + if( !ReadMemory( + address, + transportAddress, + sizeof(TA_IP_ADDRESS), + &result + ) ) { + + dprintf( + "addr: cannot read TRANSPORT_ADDRESS @ %08lx\n", + address + ); + + return; + + } + + if( ipAddress->TAAddressCount != 1 ) { + + dprintf( + "addr: invalid TRANSPORT_ADDRESS @ %08lx\n", + address + ); + + return; + + } + + if( ipAddress->Address[0].AddressLength > sizeof(TDI_ADDRESS_IP) ) { + + // + // It's a big one. + // + + if( !ReadMemory( + address, + transportAddress, + ipAddress->Address[0].AddressLength + + ( sizeof(TA_IP_ADDRESS) - sizeof(TDI_ADDRESS_IP) ), + &result + ) ) { + + dprintf( + "addr: cannot read TRANSPORT_ADDRESS @ %08lx\n", + address + ); + + return; + + } + + } + + DumpTransportAddress( + "", + (PTRANSPORT_ADDRESS)transportAddress, + address + ); + +} // addr + diff --git a/private/ntos/afd/afdkd/afdkd.def b/private/ntos/afd/afdkd/afdkd.def new file mode 100644 index 000000000..3c79eeefa --- /dev/null +++ b/private/ntos/afd/afdkd/afdkd.def @@ -0,0 +1,25 @@ +LIBRARY AFDKD + +DESCRIPTION 'Kernel Debugger Extensions for AFD.SYS' + +EXPORTS + + CheckVersion + WinDbgExtensionDllInit + ExtensionApiVersion + + help + endp + state + port + conn + addr + cref + eref + gref + version + proc + tranfile + buffer + stats + diff --git a/private/ntos/afd/afdkd/afdkd.rc b/private/ntos/afd/afdkd/afdkd.rc new file mode 100644 index 000000000..8153c5474 --- /dev/null +++ b/private/ntos/afd/afdkd/afdkd.rc @@ -0,0 +1,11 @@ +#include <windows.h> +#include <ntverp.h> + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "AFD.SYS Kernel Debugger Extension" +#define VER_INTERNALNAME_STR "afdkd.dll" +#define VER_ORIGINALFILENAME_STR "afdkd.dll" + +#include <common.ver> + diff --git a/private/ntos/afd/afdkd/afdkdp.h b/private/ntos/afd/afdkd/afdkdp.h new file mode 100644 index 000000000..fb43e7d4e --- /dev/null +++ b/private/ntos/afd/afdkd/afdkdp.h @@ -0,0 +1,104 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + afdkdp.h + +Abstract: + + Master header file for the AFD.SYS Kernel Debugger Extensions. + +Author: + + Keith Moore (keithmo) 19-Apr-1995. + +Environment: + + User Mode. + +--*/ + + +#ifndef _AFDKDP_H_ +#define _AFDKDP_H_ + + +// +// System include files. +// + +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <ntos.h> +#include <ntverp.h> + +#define NOGDICAPMASKS +#define NOVIRTUALKEYCODES +#define NOWINMESSAGES +#define NOWINSTYLES +#define NOSYSMETRICS +#define NOMENUS +#define NOICONS +#define NOKEYSTATES +#define NOSYSCOMMANDS +#define NORASTEROPS +#define NOSHOWWINDOW +#define OEMRESOURCE +#define NOATOM +#define NOCLIPBOARD +#define NOCOLOR +#define NOCTLMGR +#define NODRAWTEXT +#define NOGDI +#define NOKERNEL +#define NOUSER +#define NONLS +#define NOMB +#define NOMEMMGR +#define NOMETAFILE +#define NOMINMAX +#define NOMSG +#define NOOPENFILE +#define NOSCROLL +#define NOSERVICE +#define NOSOUND +#define NOTEXTMETRIC +#define NOWH +#define NOWINOFFSETS +#define NOCOMM +#define NOKANJI +#define NOHELP +#define NOPROFILER +#define NODEFERWINDOWPOS + +#include <windows.h> +#include <wdbgexts.h> + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + + +// +// Project include files. +// + +#define _NTIFS_ +#include <afdp.h> + + +// +// Local include files. +// + +#include <cons.h> +#include <type.h> +#include <data.h> +#include <proc.h> + + +#endif // _AFDKDP_H_ + diff --git a/private/ntos/afd/afdkd/afdutil.c b/private/ntos/afd/afdkd/afdutil.c new file mode 100644 index 000000000..fd899e894 --- /dev/null +++ b/private/ntos/afd/afdkd/afdutil.c @@ -0,0 +1,2221 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + afdutil.c + +Abstract: + + Utility functions for dumping various AFD structures. + +Author: + + Keith Moore (keithmo) 19-Apr-1995 + +Environment: + + User Mode. + +Revision History: + +--*/ + + +#include "afdkdp.h" +#pragma hdrstop + + +// +// Private constants. +// + +#define MAX_SYMBOL_LENGTH 128 + +#define ACTUAL_ADDRESS(a,s,f) \ + ( (DWORD)(a) + ( (PUCHAR)(&(s)->f) - (PUCHAR)(s) ) ) + +#define IS_LIST_EMPTY(a,s,f) \ + ( (DWORD)(s)->f.Flink == ACTUAL_ADDRESS(a,s,f) ) + + +// +// Private globals. +// + +PSTR WeekdayNames[] = + { + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday" + }; + +PSTR MonthNames[] = + { + "", + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December" + }; + + +// +// Private prototypes. +// + +PSTR +StructureTypeToString( + USHORT Type + ); + +PSTR +BooleanToString( + BOOLEAN Flag + ); + +PSTR +EndpointStateToString( + UCHAR State + ); + +PSTR +EndpointTypeToString( + AFD_ENDPOINT_TYPE Type + ); + +PSTR +ConnectionStateToString( + USHORT State + ); + +PSTR +SystemTimeToString( + LONGLONG Value + ); + +PSTR +GroupTypeToString( + AFD_GROUP_TYPE GroupType + ); + +VOID +DumpReferenceDebug( + PAFD_REFERENCE_DEBUG ReferenceDebug, + ULONG CurrentSlot + ); + +BOOL +IsTransmitIrpBusy( + PIRP Irp + ); + + +// +// Public functions. +// + +VOID +DumpAfdEndpoint( + PAFD_ENDPOINT Endpoint, + DWORD ActualAddress + ) + +/*++ + +Routine Description: + + Dumps the specified AFD_ENDPOINT structure. + +Arguments: + + Endpoint - Points to the AFD_ENDPOINT to dump. + + ActualAddress - The actual address where the structure resides on the + debugee. + +Return Value: + + None. + +--*/ + +{ + + AFD_TRANSPORT_INFO transportInfo; + UNICODE_STRING unicodeString; + WCHAR buffer[MAX_PATH]; + UCHAR address[MAX_TRANSPORT_ADDR]; + ULONG result; + + dprintf( + "AFD_ENDPOINT @ %08lx:\n", + ActualAddress + ); + + dprintf( + " Type = %04X (%s)\n", + Endpoint->Type, + StructureTypeToString( Endpoint->Type ) + ); + + dprintf( + " ReferenceCount = %d\n", + Endpoint->ReferenceCount + ); + + dprintf( + " State = %02X (%s)\n", + Endpoint->State, + EndpointStateToString( Endpoint->State ) + ); + + dprintf( + " NonBlocking = %s\n", + BooleanToString( Endpoint->NonBlocking ) + ); + + dprintf( + " InLine = %s\n", + BooleanToString( Endpoint->InLine ) + ); + + dprintf( + " TdiBufferring = %s\n", + BooleanToString( Endpoint->TdiBufferring ) + ); + + dprintf( + " TransportInfo = %08lx\n", + Endpoint->TransportInfo + ); + + if( ReadMemory( + (DWORD)Endpoint->TransportInfo, + &transportInfo, + sizeof(transportInfo), + &result + ) && + ReadMemory( + (DWORD)transportInfo.TransportDeviceName.Buffer, + buffer, + sizeof(buffer), + &result + ) ) { + + unicodeString = transportInfo.TransportDeviceName; + unicodeString.Buffer = buffer; + + dprintf( + " TransportDeviceName = %wZ\n", + &unicodeString + ); + + } + + dprintf( + " EndpointType = %08lx (%s)\n", + Endpoint->EndpointType, + EndpointTypeToString( Endpoint->EndpointType ) + ); + + dprintf( + " AddressHandle = %08lx\n", + Endpoint->AddressHandle + ); + + dprintf( + " AddressFileObject = %08lx\n", + Endpoint->AddressFileObject + ); + + switch( Endpoint->Type ) { + + case AfdBlockTypeVcConnecting : + + dprintf( + " Connection = %08lx\n", + Endpoint->Common.VcConnecting.Connection + ); + + dprintf( + " ConnectStatus = %08lx\n", + Endpoint->Common.VcConnecting.ConnectStatus + ); + + dprintf( + " ListenEndpoint = %08lx\n", + Endpoint->Common.VcConnecting.ListenEndpoint + ); + + break; + + case AfdBlockTypeVcListening : + + if( IS_LIST_EMPTY( + ActualAddress, + Endpoint, + Common.VcListening.FreeConnectionListHead ) ) { + + dprintf( + " FreeConnectionListHead = EMPTY\n" + ); + + } else { + + dprintf( + " FreeConnectionListHead @ %08lx\n", + ACTUAL_ADDRESS( + ActualAddress, + Endpoint, + Common.VcListening.FreeConnectionListHead + ) + ); + + } + + if( IS_LIST_EMPTY( + ActualAddress, + Endpoint, + Common.VcListening.UnacceptedConnectionListHead ) ) { + + dprintf( + " UnacceptedConnectionListHead = EMPTY\n" + ); + + } else { + + dprintf( + " UnacceptedConnectionListHead @ %08lx\n", + ACTUAL_ADDRESS( + ActualAddress, + Endpoint, + Common.VcListening.UnacceptedConnectionListHead + ) + ); + + } + + if( IS_LIST_EMPTY( + ActualAddress, + Endpoint, + Common.VcListening.ReturnedConnectionListHead ) ) { + + dprintf( + " ReturnedConnectionListHead = EMPTY\n" + ); + + } else { + + dprintf( + " ReturnedConnectionListHead @ %08lx\n", + ACTUAL_ADDRESS( + ActualAddress, + Endpoint, + Common.VcListening.ReturnedConnectionListHead + ) + ); + + } + + if( IS_LIST_EMPTY( + ActualAddress, + Endpoint, + Common.VcListening.ListeningIrpListHead ) ) { + + dprintf( + " ListeningIrpListHead = EMPTY\n" + ); + + } else { + + dprintf( + " ListeningIrpListHead @ %08lx\n", + ACTUAL_ADDRESS( + ActualAddress, + Endpoint, + Common.VcListening.ListeningIrpListHead + ) + ); + + } + + dprintf( + " FailedConnectionAdds = %08lx\n", + Endpoint->Common.VcListening.FailedConnectionAdds + ); + + break; + + case AfdBlockTypeDatagram : + + dprintf( + " RemoteAddress = %08lx\n", + Endpoint->Common.Datagram.RemoteAddress + ); + + dprintf( + " RemoteAddressLength = %08lx\n", + Endpoint->Common.Datagram.RemoteAddressLength + ); + + if( IS_LIST_EMPTY( + ActualAddress, + Endpoint, + Common.Datagram.ReceiveIrpListHead ) ) { + + dprintf( + " ReceiveIrpListHead = EMPTY\n" + ); + + } else { + + dprintf( + " ReceiveIrpListHead @ %08lx\n", + ACTUAL_ADDRESS( + ActualAddress, + Endpoint, + Common.Datagram.ReceiveIrpListHead + ) + ); + + } + + if( IS_LIST_EMPTY( + ActualAddress, + Endpoint, + Common.Datagram.PeekIrpListHead ) ) { + + dprintf( + " PeekIrpListHead = EMPTY\n" + ); + + } else { + + dprintf( + " PeekIrpListHead @ %08lx\n", + ACTUAL_ADDRESS( + ActualAddress, + Endpoint, + Common.Datagram.PeekIrpListHead + ) + ); + + } + + if( IS_LIST_EMPTY( + ActualAddress, + Endpoint, + Common.Datagram.ReceiveBufferListHead ) ) { + + dprintf( + " ReceiveBufferListHead = EMPTY\n" + ); + + } else { + + dprintf( + " ReceiveBufferListHead @ %08lx\n", + ACTUAL_ADDRESS( + ActualAddress, + Endpoint, + Common.Datagram.ReceiveBufferListHead + ) + ); + + } + + dprintf( + " BufferredDatagramBytes = %08lx\n", + Endpoint->BufferredDatagramBytes + ); + + dprintf( + " BufferredDatagramCount = %04X\n", + Endpoint->BufferredDatagramCount + ); + + dprintf( + " MaxBufferredReceiveBytes = %08lx\n", + Endpoint->Common.Datagram.MaxBufferredReceiveBytes + ); + + dprintf( + " MaxBufferredSendBytes = %08lx\n", + Endpoint->Common.Datagram.MaxBufferredSendBytes + ); + + dprintf( + " MaxBufferredReceiveCount = %04X\n", + Endpoint->Common.Datagram.MaxBufferredReceiveCount + ); + + dprintf( + " MaxBufferredSendCount = %04X\n", + Endpoint->Common.Datagram.MaxBufferredSendCount + ); + + dprintf( + " CircularQueueing = %s\n", + BooleanToString( Endpoint->Common.Datagram.CircularQueueing ) + ); + + break; + + } + + dprintf( + " DisconnectMode = %08lx\n", + Endpoint->DisconnectMode + ); + + dprintf( + " OutstandingIrpCount = %08lx\n", + Endpoint->OutstandingIrpCount + ); + + dprintf( + " LocalAddress = %08lx\n", + Endpoint->LocalAddress + ); + + dprintf( + " LocalAddressLength = %08lx\n", + Endpoint->LocalAddressLength + ); + + if( Endpoint->LocalAddressLength <= sizeof(address) && + Endpoint->LocalAddress != NULL ) { + + if( ReadMemory( + (DWORD)Endpoint->LocalAddress, + address, + sizeof(address), + &result + ) ) { + + DumpTransportAddress( + " ", + (PTRANSPORT_ADDRESS)address, + (DWORD)Endpoint->LocalAddress + ); + + } + + } + + dprintf( + " Context = %08lx\n", + Endpoint->Context + ); + + dprintf( + " ContextLength = %08lx\n", + Endpoint->ContextLength + ); + + dprintf( + " OwningProcess = %08lx\n", + Endpoint->OwningProcess + ); + + dprintf( + " ConnectDataBuffers = %08lx\n", + Endpoint->ConnectDataBuffers + ); + + dprintf( + " TransmitIrp = %08lx\n", + Endpoint->TransmitIrp + ); + + dprintf( + " TransmitInfo = %08lx\n", + Endpoint->TransmitInfo + ); + + dprintf( + " AddressDeviceObject = %08lx\n", + Endpoint->AddressDeviceObject + ); + + dprintf( + " ConnectOutstanding = %s\n", + BooleanToString( Endpoint->ConnectOutstanding ) + ); + + dprintf( + " SendDisconnected = %s\n", + BooleanToString( Endpoint->SendDisconnected ) + ); + + dprintf( + " EndpointCleanedUp = %s\n", + BooleanToString( Endpoint->EndpointCleanedUp ) + ); + + dprintf( + " TdiMessageMode = %s\n", + BooleanToString( Endpoint->TdiMessageMode ) + ); + +#if !defined(NT351) + dprintf( + " EventObject = %08lx\n", + Endpoint->EventObject + ); + + dprintf( + " EventsEnabled = %08lx\n", + Endpoint->EventsEnabled + ); + + dprintf( + " EventsDisabled = %08lx\n", + Endpoint->EventsDisabled + ); + + dprintf( + " EventsActive = %08lx\n", + Endpoint->EventsActive + ); + + dprintf( + " EventStatus = %08lx\n", + ACTUAL_ADDRESS( ActualAddress, Endpoint, EventStatus[0] ) + ); + + dprintf( + " GroupID = %08lx\n", + Endpoint->GroupID + ); + + dprintf( + " GroupType = %s\n", + GroupTypeToString( Endpoint->GroupType ) + ); +#endif + + if( IsCheckedAfd ) { + + dprintf( + " ReferenceDebug = %08lx\n", + Endpoint->ReferenceDebug + ); + + dprintf( + " CurrentReferenceSlot = %lu\n", + Endpoint->CurrentReferenceSlot % MAX_REFERENCE + ); + + } + + dprintf( "\n" ); + +} // DumpAfdEndpoint + +VOID +DumpAfdConnection( + PAFD_CONNECTION Connection, + DWORD ActualAddress + ) + +/*++ + +Routine Description: + + Dumps the specified AFD_CONNECTION structures. + +Arguments: + + Connection - Points to the AFD_CONNECTION structure to dump. + + ActualAddress - The actual address where the structure resides on the + debugee. + +Return Value: + + None. + +--*/ + +{ + + UCHAR address[MAX_TRANSPORT_ADDR]; + ULONG result; + + dprintf( + "AFD_CONNECTION @ %08lx:\n", + ActualAddress + ); + + dprintf( + " Type = %04X (%s)\n", + Connection->Type, + StructureTypeToString( Connection->Type ) + ); + + dprintf( + " ReferenceCount = %d\n", + Connection->ReferenceCount + ); + + dprintf( + " State = %08X (%s)\n", + Connection->State, + ConnectionStateToString( Connection->State ) + ); + + dprintf( + " Handle = %08lx\n", + Connection->Handle + ); + + dprintf( + " FileObject = %08lx\n", + Connection->FileObject + ); + + dprintf( + " ConnectTime = %s\n", + SystemTimeToString( Connection->ConnectTime ) + ); + + if( Connection->TdiBufferring ) + { + dprintf( + " ReceiveBytesIndicated = %s\n", + LongLongToString( Connection->Common.Bufferring.ReceiveBytesIndicated.QuadPart ) + ); + + dprintf( + " ReceiveBytesTaken = %s\n", + LongLongToString( Connection->Common.Bufferring.ReceiveBytesTaken.QuadPart ) + ); + + dprintf( + " ReceiveBytesOutstanding = %s\n", + LongLongToString( Connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart ) + ); + + dprintf( + " ReceiveExpeditedBytesIndicated = %s\n", + LongLongToString( Connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.QuadPart ) + ); + + dprintf( + " ReceiveExpeditedBytesTaken = %s\n", + LongLongToString( Connection->Common.Bufferring.ReceiveExpeditedBytesTaken.QuadPart ) + ); + + dprintf( + " ReceiveExpeditedBytesOutstanding = %s\n", + LongLongToString( Connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart ) + ); + + dprintf( + " NonBlockingSendPossible = %s\n", + BooleanToString( Connection->Common.Bufferring.NonBlockingSendPossible ) + ); + + dprintf( + " ZeroByteReceiveIndicated = %s\n", + BooleanToString( Connection->Common.Bufferring.ZeroByteReceiveIndicated ) + ); + } + else + { + if( IS_LIST_EMPTY( + ActualAddress, + Connection, + Common.NonBufferring.ReceiveIrpListHead ) ) { + + dprintf( + " ReceiveIrpListHead = EMPTY\n" + ); + + } else { + + dprintf( + " ReceiveIrpListHead @ %08lx\n", + ACTUAL_ADDRESS( + ActualAddress, + Connection, + Common.NonBufferring.ReceiveIrpListHead + ) + ); + + } + + if( IS_LIST_EMPTY( + ActualAddress, + Connection, + Common.NonBufferring.ReceiveBufferListHead ) ) { + + dprintf( + " ReceiveBufferListHead = EMPTY\n" + ); + + } else { + + dprintf( + " ReceiveBufferListHead @ %08lx\n", + ACTUAL_ADDRESS( + ActualAddress, + Connection, + Common.NonBufferring.ReceiveBufferListHead + ) + ); + + } + + if( IS_LIST_EMPTY( + ActualAddress, + Connection, + Common.NonBufferring.SendIrpListHead ) ) { + + dprintf( + " SendIrpListHead = EMPTY\n" + ); + + } else { + + dprintf( + " SendIrpListHead @ %08lx\n", + ACTUAL_ADDRESS( + ActualAddress, + Connection, + Common.NonBufferring.SendIrpListHead + ) + ); + + } + + dprintf( + " BufferredReceiveBytes = %lu\n", + Connection->Common.NonBufferring.BufferredReceiveBytes + ); + + dprintf( + " BufferredExpeditedBytes = %lu\n", + Connection->Common.NonBufferring.BufferredExpeditedBytes + ); + + dprintf( + " BufferredReceiveCount = %u\n", + Connection->Common.NonBufferring.BufferredReceiveCount + ); + + dprintf( + " BufferredExpeditedCount = %u\n", + Connection->Common.NonBufferring.BufferredExpeditedCount + ); + + dprintf( + " ReceiveBytesInTransport = %lu\n", + Connection->Common.NonBufferring.ReceiveBytesInTransport + ); + + dprintf( + " BufferredSendBytes = %lu\n", + Connection->Common.NonBufferring.BufferredSendBytes + ); + + dprintf( + " ReceiveCountInTransport = %u\n", + Connection->Common.NonBufferring.ReceiveCountInTransport + ); + + dprintf( + " BufferredSendCount = %u\n", + Connection->Common.NonBufferring.BufferredSendCount + ); + + dprintf( + " DisconnectIrp = %08lx\n", + Connection->Common.NonBufferring.DisconnectIrp + ); + } + + dprintf( + " Endpoint = %08lx\n", + Connection->Endpoint + ); + + dprintf( + " MaxBufferredReceiveBytes = %lu\n", + Connection->MaxBufferredReceiveBytes + ); + + dprintf( + " MaxBufferredSendBytes = %lu\n", + Connection->MaxBufferredSendBytes + ); + + dprintf( + " MaxBufferredReceiveCount = %u\n", + Connection->MaxBufferredReceiveCount + ); + + dprintf( + " MaxBufferredSendCount = %u\n", + Connection->MaxBufferredSendCount + ); + + dprintf( + " ConnectDataBuffers = %08lx\n", + Connection->ConnectDataBuffers + ); + + dprintf( + " OwningProcess = %08lx\n", + Connection->OwningProcess + ); + + dprintf( + " DeviceObject = %08lx\n", + Connection->DeviceObject + ); + + dprintf( + " RemoteAddress = %08lx\n", + Connection->RemoteAddress + ); + + dprintf( + " RemoteAddressLength = %lu\n", + Connection->RemoteAddressLength + ); + + if( Connection->RemoteAddressLength <= sizeof(address) && + Connection->RemoteAddress != NULL ) { + + if( ReadMemory( + (DWORD)Connection->RemoteAddress, + address, + sizeof(address), + &result + ) ) { + + DumpTransportAddress( + " ", + (PTRANSPORT_ADDRESS)address, + (DWORD)Connection->RemoteAddress + ); + + } + + } + + dprintf( + " DisconnectIndicated = %s\n", + BooleanToString( Connection->DisconnectIndicated ) + ); + + dprintf( + " AbortIndicated = %s\n", + BooleanToString( Connection->AbortIndicated ) + ); + + dprintf( + " TdiBufferring = %s\n", + BooleanToString( Connection->TdiBufferring ) + ); + + dprintf( + " ConnectedReferenceAdded = %s\n", + BooleanToString( Connection->ConnectedReferenceAdded ) + ); + + dprintf( + " SpecialCondition = %s\n", + BooleanToString( Connection->SpecialCondition ) + ); + + dprintf( + " CleanupBegun = %s\n", + BooleanToString( Connection->CleanupBegun ) + ); + + dprintf( + " ClosePendedTransmit = %s\n", + BooleanToString( Connection->ClosePendedTransmit ) + ); + + if( IsCheckedAfd ) { + + dprintf( + " CurrentReferenceSlot = %lu\n", + Connection->CurrentReferenceSlot % MAX_REFERENCE + ); + + dprintf( + " ReferenceDebug = %08lx\n", + ACTUAL_ADDRESS( + ActualAddress, + Connection, + ReferenceDebug + ) + ); + + } + + dprintf( "\n" ); + +} // DumpAfdConnection + +VOID +DumpAfdConnectionReferenceDebug( + PAFD_REFERENCE_DEBUG ReferenceDebug, + DWORD ActualAddress + ) + +/*++ + +Routine Description: + + Dumps the AFD_REFERENCE_DEBUG structures associated with an + AFD_CONNECTION object. + +Arguments: + + ReferenceDebug - Points to an array of AFD_REFERENCE_DEBUG structures. + There are assumed to be MAX_REFERENCE entries in this array. + + ActualAddress - The actual address where the array resides on the + debugee. + +Return Value: + + None. + +--*/ + +{ + + ULONG i; + ULONG result; + LPSTR fileName; + CHAR filePath[MAX_PATH]; + CHAR action[16]; + + dprintf( + "AFD_REFERENCE_DEBUG @ %08lx\n", + ActualAddress + ); + + for( i = 0 ; i < MAX_REFERENCE ; i++, ReferenceDebug++ ) { + + if( CheckControlC() ) { + + break; + + } + + if( ReferenceDebug->Info1 == NULL && + ReferenceDebug->Info2 == NULL && + ReferenceDebug->Action == 0 && + ReferenceDebug->NewCount == 0 ) { + + break; + + } + + if( ReferenceDebug->Action == 0 || + ReferenceDebug->Action == 1 || + ReferenceDebug->Action == (ULONG)-1L ) { + + sprintf( + action, + "%ld", + ReferenceDebug->Action + ); + + } else { + + sprintf( + action, + "%08lx", + ReferenceDebug->Action + ); + + } + + switch( (DWORD)ReferenceDebug->Info1 ) { + + case 0xafdafd02 : + dprintf( + " %3lu: Buffered Send, IRP @ %08lx [%s] -> %lu\n", + i, + ReferenceDebug->Info2, + action, + ReferenceDebug->NewCount + ); + break; + + case 0xafdafd03 : + dprintf( + " %3lu: Nonbuffered Send, IRP @ %08lx [%s] -> %lu\n", + i, + ReferenceDebug->Info2, + action, + ReferenceDebug->NewCount + ); + break; + + case 0xafd11100 : + case 0xafd11101 : + dprintf( + " %3lu: AfdRestartSend (%08lx), IRP @ %08lx [%s] -> %lu\n", + i, + ReferenceDebug->Info1, + ReferenceDebug->Info2, + action, + ReferenceDebug->NewCount + ); + break; + + case 0xafd11102 : + case 0xafd11103 : + case 0xafd11104 : + case 0xafd11105 : + dprintf( + " %3lu: AfdRestartBufferSend (%08lx), IRP @ %08lx [%s] -> %lu\n", + i, + ReferenceDebug->Info1, + ReferenceDebug->Info2, + action, + ReferenceDebug->NewCount + ); + break; + + case 0 : + if( ReferenceDebug->Info2 == NULL ) { + + dprintf( + " %3lu: AfdDeleteConnectedReference (%08lx)\n", + i, + ReferenceDebug->Action + ); + break; + + } else { + + // + // Fall through to default case. + // + + } + + default : + if( ReadMemory( + (DWORD)ReferenceDebug->Info1, + filePath, + sizeof(filePath), + &result + ) ) { + + fileName = strrchr( filePath, '\\' ); + + if( fileName != NULL ) { + + fileName++; + + } else { + + fileName = filePath; + + } + + } else { + + sprintf( + filePath, + "%08lx", + ReferenceDebug->Info1 + ); + + fileName = filePath; + + } + + dprintf( + " %3lu: %s:%lu [%s] -> %lu\n", + i, + fileName, + ReferenceDebug->Info2, + action, + ReferenceDebug->NewCount + ); + break; + + } + + } + +} // DumpAfdConnectionReferenceDebug + +VOID +DumpAfdEndpointReferenceDebug( + PAFD_REFERENCE_DEBUG ReferenceDebug, + DWORD ActualAddress + ) + +/*++ + +Routine Description: + + Dumps the AFD_REFERENCE_DEBUG structures associated with an + AFD_ENDPOINT object. + +Arguments: + + ReferenceDebug - Points to an array of AFD_REFERENCE_DEBUG structures. + There are assumed to be MAX_REFERENCE entries in this array. + + ActualAddress - The actual address where the array resides on the + debugee. + +Return Value: + + None. + +--*/ + +{ + + ULONG i; + ULONG result; + LPSTR fileName; + CHAR filePath[MAX_PATH]; + CHAR action[16]; + + dprintf( + "AFD_REFERENCE_DEBUG @ %08lx\n", + ActualAddress + ); + + for( i = 0 ; i < MAX_REFERENCE ; i++, ReferenceDebug++ ) { + + if( CheckControlC() ) { + + break; + + } + + if( ReferenceDebug->Info1 == NULL && + ReferenceDebug->Info2 == NULL && + ReferenceDebug->Action == 0 && + ReferenceDebug->NewCount == 0 ) { + + break; + + } + + if( ReferenceDebug->Action == 0 || + ReferenceDebug->Action == 1 || + ReferenceDebug->Action == (ULONG)-1L ) { + + sprintf( + action, + "%ld", + ReferenceDebug->Action + ); + + } else { + + sprintf( + action, + "%08lx", + ReferenceDebug->Action + ); + + } + + if( ReadMemory( + (DWORD)ReferenceDebug->Info1, + filePath, + sizeof(filePath), + &result + ) ) { + + fileName = strrchr( filePath, '\\' ); + + if( fileName != NULL ) { + + fileName++; + + } else { + + fileName = filePath; + + } + + } else { + + sprintf( + filePath, + "%08lx", + ReferenceDebug->Info1 + ); + + fileName = filePath; + + } + + dprintf( + " %3lu: %s:%lu [%s] -> %ld\n", + i, + fileName, + ReferenceDebug->Info2, + action, + ReferenceDebug->NewCount + ); + + } + +} // DumpAfdEndpointReferenceDebug + +#if GLOBAL_REFERENCE_DEBUG +BOOL +DumpAfdGlobalReferenceDebug( + PAFD_GLOBAL_REFERENCE_DEBUG ReferenceDebug, + DWORD ActualAddress, + DWORD CurrentSlot, + DWORD StartingSlot, + DWORD NumEntries, + DWORD CompareAddress + ) + +/*++ + +Routine Description: + + Dumps the AFD_GLOBAL_REFERENCE_DEBUG structures. + +Arguments: + + ReferenceDebug - Points to an array of AFD_GLOBAL_REFERENCE_DEBUG + structures. There are assumed to be MAX_GLOBAL_REFERENCE entries + in this array. + + ActualAddress - The actual address where the array resides on the + debugee. + + CurrentSlot - The last slot used. + + CompareAddress - If zero, then dump all records. Otherwise, only dump + those records with a matching connection pointer. + +Return Value: + + None. + +--*/ + +{ + + ULONG result; + LPSTR fileName; + CHAR decoration; + CHAR filePath[MAX_PATH]; + CHAR action[16]; + BOOL foundEnd = FALSE; + ULONG lowTick; + + if( StartingSlot == 0 ) { + + dprintf( + "AFD_GLOBAL_REFERENCE_DEBUG @ %08lx, Current Slot = %lu\n", + ActualAddress, + CurrentSlot + ); + + } + + for( ; NumEntries > 0 ; NumEntries--, StartingSlot++, ReferenceDebug++ ) { + + if( CheckControlC() ) { + + foundEnd = TRUE; + break; + + } + + if( ReferenceDebug->Info1 == NULL && + ReferenceDebug->Info2 == NULL && + ReferenceDebug->Action == 0 && + ReferenceDebug->NewCount == 0 && + ReferenceDebug->Connection == NULL ) { + + foundEnd = TRUE; + break; + + } + + if( CompareAddress != 0 && + ReferenceDebug->Connection != (PVOID)CompareAddress ) { + + continue; + + } + + if( ReferenceDebug->Action == 0 || + ReferenceDebug->Action == 1 || + ReferenceDebug->Action == (ULONG)-1L ) { + + sprintf( + action, + "%ld", + ReferenceDebug->Action + ); + + } else { + + sprintf( + action, + "%08lx", + ReferenceDebug->Action + ); + + } + + decoration = ( StartingSlot == CurrentSlot ) ? '>' : ' '; + lowTick = ReferenceDebug->TickCounter.LowPart; + + switch( (DWORD)ReferenceDebug->Info1 ) { + + case 0xafdafd02 : + dprintf( + "%c %3lu: %08lx (%8lu) Buffered Send, IRP @ %08lx [%s] -> %lu\n", + decoration, + StartingSlot, + ReferenceDebug->Connection, + lowTick, + ReferenceDebug->Info2, + action, + ReferenceDebug->NewCount + ); + break; + + case 0xafdafd03 : + dprintf( + "%c %3lu: %08lx (%8lu) Nonbuffered Send, IRP @ %08lx [%s] -> %lu\n", + decoration, + StartingSlot, + ReferenceDebug->Connection, + lowTick, + ReferenceDebug->Info2, + action, + ReferenceDebug->NewCount + ); + break; + + case 0xafd11100 : + case 0xafd11101 : + dprintf( + "%c %3lu: %08lx (%8lu) AfdRestartSend (%08lx), IRP @ %08lx [%s] -> %lu\n", + decoration, + StartingSlot, + ReferenceDebug->Connection, + lowTick, + ReferenceDebug->Info1, + ReferenceDebug->Info2, + action, + ReferenceDebug->NewCount + ); + break; + + case 0xafd11102 : + case 0xafd11103 : + case 0xafd11104 : + case 0xafd11105 : + dprintf( + "%c %3lu: %08lx (%8lu) AfdRestartBufferSend (%08lx), IRP @ %08lx [%s] -> %lu\n", + decoration, + StartingSlot, + ReferenceDebug->Connection, + lowTick, + ReferenceDebug->Info1, + ReferenceDebug->Info2, + action, + ReferenceDebug->NewCount + ); + break; + + case 0 : + if( ReferenceDebug->Info2 == NULL ) { + + dprintf( + "%c %3lu: %08lx (%8lu) AfdDeleteConnectedReference (%08lx)\n", + decoration, + StartingSlot, + ReferenceDebug->Connection, + lowTick, + ReferenceDebug->Action + ); + break; + + } else { + + // + // Fall through to default case. + // + + } + + default : + if( ReadMemory( + (DWORD)ReferenceDebug->Info1, + filePath, + sizeof(filePath), + &result + ) ) { + + fileName = strrchr( filePath, '\\' ); + + if( fileName != NULL ) { + + fileName++; + + } else { + + fileName = filePath; + + } + + } else { + + sprintf( + filePath, + "%08lx", + ReferenceDebug->Info1 + ); + + fileName = filePath; + + } + + dprintf( + "%c %3lu: %08lx (%8lu) %s:%lu [%s] -> %lu\n", + decoration, + StartingSlot, + ReferenceDebug->Connection, + lowTick, + fileName, + ReferenceDebug->Info2, + action, + ReferenceDebug->NewCount + ); + break; + + } + + } + + return foundEnd; + +} // DumpAfdGlobalReferenceDebug +#endif + +VOID +DumpAfdTransmitInfo( + PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo, + DWORD ActualAddress + ) +{ + + dprintf( + "AFD_TRANSMIT_FILE_INFO_INTERNAL @ %08lx\n", + ActualAddress + ); + + dprintf( + " Offset = %s\n", + LongLongToString( TransmitInfo->Offset ) + ); + + dprintf( + " FileWriteLength = %s\n", + LongLongToString( TransmitInfo->FileWriteLength ) + ); + + dprintf( + " SendPacketLength = %08lx\n", + TransmitInfo->SendPacketLength + ); + + dprintf( + " FileHandle = %08lx\n", + TransmitInfo->FileHandle + ); + + dprintf( + " Head = %08lx\n", + TransmitInfo->Head + ); + + dprintf( + " HeadLength = %08lx\n", + TransmitInfo->HeadLength + ); + + dprintf( + " Tail = %08lx\n", + TransmitInfo->Tail + ); + + dprintf( + " TailLength = %08lx\n", + TransmitInfo->TailLength + ); + + dprintf( + " Flags = %08lx\n", + TransmitInfo->Flags + ); + + dprintf( + " _Dummy = %08lx\n", + TransmitInfo->_Dummy + ); + + dprintf( + " TotalBytesToSend = %s\n", + LongLongToString( TransmitInfo->TotalBytesToSend ) + ); + + dprintf( + " BytesRead = %s\n", + LongLongToString( TransmitInfo->BytesRead ) + ); + + dprintf( + " BytesSent = %s\n", + LongLongToString( TransmitInfo->BytesSent ) + ); + + dprintf( + " FileObject = %08lx\n", + TransmitInfo->FileObject + ); + + dprintf( + " DeviceObject = %08lx\n", + TransmitInfo->DeviceObject + ); + + dprintf( + " TdiFileObject = %08lx\n", + TransmitInfo->TdiFileObject + ); + + dprintf( + " TdiDeviceObject = %08lx\n", + TransmitInfo->TdiDeviceObject + ); + + dprintf( + " TransmitIrp = %08lx\n", + TransmitInfo->TransmitIrp + ); + + dprintf( + " Endpoint = %08lx\n", + TransmitInfo->Endpoint + ); + + dprintf( + " FileMdl = %08lx\n", + TransmitInfo->FileMdl + ); + + dprintf( + " HeadMdl = %08lx\n", + TransmitInfo->HeadMdl + ); + + dprintf( + " TailMdl = %08lx\n", + TransmitInfo->TailMdl + ); + + dprintf( + " FirstFileMdlAfterHead = %08lx\n", + TransmitInfo->FirstFileMdlAfterHead + ); + + dprintf( + " LastFileMdlBeforeTail = %08lx\n", + TransmitInfo->LastFileMdlBeforeTail + ); + + dprintf( + " IrpUsedTOSendTail = %08lx\n", + TransmitInfo->IrpUsedToSendTail + ); + + dprintf( + " FileMdlLength = %08lx\n", + TransmitInfo->FileMdlLength + ); + + dprintf( + " ReadPending = %s\n", + BooleanToString( TransmitInfo->ReadPending ) + ); + + dprintf( + " CompletionPending = %s\n", + BooleanToString( TransmitInfo->CompletionPending ) + ); + + dprintf( + " NeedToSendHead = %s\n", + BooleanToString( TransmitInfo->NeedToSendHead ) + ); + + dprintf( + " Queued = %s\n", + BooleanToString( TransmitInfo->Queued ) + ); + + dprintf( + " Read.Irp = %08lx%s\n", + TransmitInfo->Read.Irp, + IsTransmitIrpBusy( TransmitInfo->Read.Irp ) + ? " (BUSY)" + : "" + ); + + dprintf( + " Read.AfdBuffer = %08lx\n", + TransmitInfo->Read.AfdBuffer + ); + + dprintf( + " Read.Length = %08lx\n", + TransmitInfo->Read.Length + ); + + dprintf( + " Send1.Irp = %08lx%s\n", + TransmitInfo->Send1.Irp, + IsTransmitIrpBusy( TransmitInfo->Send1.Irp ) + ? " (BUSY)" + : "" + ); + + dprintf( + " Send1.AfdBuffer = %08lx\n", + TransmitInfo->Send1.AfdBuffer + ); + + dprintf( + " Send1.Length = %08lx\n", + TransmitInfo->Send1.Length + ); + + dprintf( + " Send2.Irp = %08lx%s\n", + TransmitInfo->Send2.Irp, + IsTransmitIrpBusy( TransmitInfo->Send2.Irp ) + ? " (BUSY)" + : "" + ); + + dprintf( + " Send2.AfdBuffer = %08lx\n", + TransmitInfo->Send2.AfdBuffer + ); + + dprintf( + " Send2.Length = %08lx\n", + TransmitInfo->Send2.Length + ); + + dprintf( "\n" ); + +} // DumpAfdTransmitInfo + +VOID +DumpAfdBuffer( + PAFD_BUFFER Buffer, + DWORD ActualAddress + ) +{ + + dprintf( + "AFD_BUFFER @ %08lx\n", + ActualAddress + ); + + dprintf( + " BufferListHead = %08lx\n", + Buffer->BufferListHead + ); + + dprintf( + " NextBuffer = %08lx\n", + Buffer->NextBuffer + ); + + dprintf( + " Buffer = %08lx\n", + Buffer->Buffer + ); + + dprintf( + " BufferLength = %08lx\n", + Buffer->BufferLength + ); + + dprintf( + " DataLength = %08lx\n", + Buffer->DataLength + ); + + dprintf( + " DataOffset = %08lx\n", + Buffer->DataOffset + ); + + dprintf( + " Irp = %08lx\n", + Buffer->Irp + ); + + dprintf( + " Mdl = %08lx\n", + Buffer->Mdl + ); + + dprintf( + " Context = %08lx\n", + Buffer->Context + ); + + dprintf( + " SourceAddress = %08lx\n", + Buffer->SourceAddress + ); + + dprintf( + " SourceAddressLength = %08lx\n", + Buffer->SourceAddressLength + ); + + dprintf( + " FileObject = %08lx\n", + Buffer->FileObject + ); + + dprintf( + " AllocatedAddressLength = %04X\n", + Buffer->AllocatedAddressLength + ); + + dprintf( + " ExpeditedData = %s\n", + BooleanToString( Buffer->ExpeditedData ) + ); + + dprintf( + " PartialMessage = %s\n", + BooleanToString( Buffer->PartialMessage ) + ); + +#if DBG + if( IsCheckedAfd ) { + + dprintf( + " TotalChainLength = %08lx\n", + Buffer->TotalChainLength + ); + + } +#endif + + dprintf( "\n" ); + +} // DumpAfdBuffer + + +// +// Private functions. +// + +PSTR +StructureTypeToString( + USHORT Type + ) + +/*++ + +Routine Description: + + Maps an AFD structure type to a displayable string. + +Arguments: + + Type - The AFD structure type to map. + +Return Value: + + PSTR - Points to the displayable form of the structure type. + +--*/ + +{ + + switch( Type ) { + + case AfdBlockTypeEndpoint : + return "Endpoint"; + + case AfdBlockTypeVcConnecting : + return "VcConnecting"; + + case AfdBlockTypeVcListening : + return "VcListening"; + + case AfdBlockTypeDatagram : + return "Datagram"; + + case AfdBlockTypeConnection : + return "Connection"; + +#if !defined(NT351) + case AfdBlockTypeHelper : + return "Helper"; +#endif + + } + + return "INVALID"; + +} // StructureTypeToString + +PSTR +BooleanToString( + BOOLEAN Flag + ) + +/*++ + +Routine Description: + + Maps a BOOELEAN to a displayable form. + +Arguments: + + Flag - The BOOLEAN to map. + +Return Value: + + PSTR - Points to the displayable form of the BOOLEAN. + +--*/ + +{ + + return Flag ? "TRUE" : "FALSE"; + +} // BooleanToString + +PSTR +EndpointStateToString( + UCHAR State + ) + +/*++ + +Routine Description: + + Maps an AFD endpoint state to a displayable string. + +Arguments: + + State - The AFD endpoint state to map. + +Return Value: + + PSTR - Points to the displayable form of the AFD endpoint state. + +--*/ + +{ + + switch( State ) { + + case AfdEndpointStateOpen : + return "Open"; + + case AfdEndpointStateBound : + return "Bound"; + + case AfdEndpointStateListening : + return "Listening"; + + case AfdEndpointStateConnected : + return "Connected"; + + case AfdEndpointStateCleanup : + return "Cleanup"; + + case AfdEndpointStateClosing : + return "Closing"; + + case AfdEndpointStateTransmitClosing : + return "Transmit Closing"; + +#if !defined(NT351) + case AfdEndpointStateInvalid : + return "Invalid"; +#endif + + } + + return "INVALID"; + +} // EndpointStateToString + +PSTR +EndpointTypeToString( + AFD_ENDPOINT_TYPE Type + ) + +/*++ + +Routine Description: + + Maps an AFD_ENDPOINT_TYPE to a displayable string. + +Arguments: + + Type - The AFD_ENDPOINT_TYPE to map. + +Return Value: + + PSTR - Points to the displayable form of the AFD_ENDPOINT_TYPE. + +--*/ + +{ + + switch( Type ) { + + case AfdEndpointTypeStream : + return "Stream"; + + case AfdEndpointTypeDatagram : + return "Datagram"; + + case AfdEndpointTypeRaw : + return "Raw"; + + case AfdEndpointTypeSequencedPacket : + return "SequencedPacket"; + + case AfdEndpointTypeReliableMessage : + return "ReliableMessage"; + + case AfdEndpointTypeUnknown : + return "Unknown"; + + } + + return "INVALID"; + +} // EndpointTypeToString + +PSTR +ConnectionStateToString( + USHORT State + ) + +/*++ + +Routine Description: + + Maps an AFD connection state to a displayable string. + +Arguments: + + State - The AFD connection state to map. + +Return Value: + + PSTR - Points to the displayable form of the AFD connection state. + +--*/ + +{ + switch( State ) { + + case AfdConnectionStateFree : + return "Free"; + + case AfdConnectionStateUnaccepted : + return "Unaccepted"; + + case AfdConnectionStateReturned : + return "Returned"; + + case AfdConnectionStateConnected : + return "Connected"; + + case AfdConnectionStateClosing : + return "Closing"; + + } + + return "INVALID"; + +} // ConnectionStateToString + +PSTR +SystemTimeToString( + LONGLONG Value + ) + +/*++ + +Routine Description: + + Maps a LONGLONG representing system time to a displayable string. + +Arguments: + + Value - The LONGLONG time to map. + +Return Value: + + PSTR - Points to the displayable form of the system time. + +Notes: + + This routine is NOT multithread safe! + +--*/ + +{ + + static char buffer[64]; + NTSTATUS status; + LARGE_INTEGER systemTime; + LARGE_INTEGER localTime; + TIME_FIELDS timeFields; + + systemTime.QuadPart = Value; + + status = RtlSystemTimeToLocalTime( &systemTime, &localTime ); + + if( !NT_SUCCESS(status) ) { + + return LongLongToString( Value ); + + } + + RtlTimeToTimeFields( &localTime, &timeFields ); + + sprintf( + buffer, + "%s %s %2d %4d %02d:%02d:%02d.%03d", + WeekdayNames[timeFields.Weekday], + MonthNames[timeFields.Month], + timeFields.Day, + timeFields.Year, + timeFields.Hour, + timeFields.Minute, + timeFields.Second, + timeFields.Milliseconds + ); + + return buffer; + +} // SystemTimeToString + + +BOOL +IsTransmitIrpBusy( + PIRP Irp + ) +{ + IRP localIrp; + ULONG result; + + if( Irp == NULL ) { + + return FALSE; + + } + + if( !ReadMemory( + (DWORD)Irp, + &localIrp, + sizeof(localIrp), + &result + ) ) { + + return FALSE; + + } + + return localIrp.UserIosb != 0; + +} // IsTransmitIrpBusy + +PSTR +GroupTypeToString( + AFD_GROUP_TYPE GroupType + ) + +/*++ + +Routine Description: + + Maps an AFD_GROUP_TYPE to a displayable string. + +Arguments: + + GroupType - The AFD_GROUP_TYPE to map. + +Return Value: + + PSTR - Points to the displayable form of the AFD_GROUP_TYPE. + +--*/ + +{ + + switch( GroupType ) { + + case GroupTypeNeither : + return "Neither"; + + case GroupTypeConstrained : + return "Constrained"; + + case GroupTypeUnconstrained : + return "Unconstrained"; + + } + + return "INVALID"; + +} // GroupTypeToString + diff --git a/private/ntos/afd/afdkd/buffer.c b/private/ntos/afd/afdkd/buffer.c new file mode 100644 index 000000000..dadc0ab90 --- /dev/null +++ b/private/ntos/afd/afdkd/buffer.c @@ -0,0 +1,89 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + buffer.c + +Abstract: + + Implements the buffer command. + +Author: + + Keith Moore (keithmo) 15-Apr-1996 + +Environment: + + User Mode. + +Revision History: + +--*/ + + +#include "afdkdp.h" +#pragma hdrstop + + +// +// Public functions. +// + +DECLARE_API( buffer ) + +/*++ + +Routine Description: + + Dumps the AFD_BUFFER structure at the specified address. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + + DWORD address = 0; + ULONG result; + AFD_BUFFER buffer; + + sscanf( args, "%lx", &address ); + + if( address == 0 ) { + + dprintf( "use: buffer address\n" ); + return; + + } + + if( ReadMemory( + address, + &buffer, + sizeof(buffer), + &result + ) ) { + + DumpAfdBuffer( + &buffer, + address + ); + + } else { + + dprintf( + "buffer: cannot read AFD_BUFFER @ %08lx\n", + address + ); + + } + +} // buffer + diff --git a/private/ntos/afd/afdkd/conn.c b/private/ntos/afd/afdkd/conn.c new file mode 100644 index 000000000..c3e9dccd3 --- /dev/null +++ b/private/ntos/afd/afdkd/conn.c @@ -0,0 +1,93 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + conn.c + +Abstract: + + Implements the conn command. + +Author: + + Keith Moore (keithmo) 19-Apr-1995 + +Environment: + + User Mode. + +Revision History: + +--*/ + + +#include "afdkdp.h" +#pragma hdrstop + + +// +// Public functions. +// + +DECLARE_API( conn ) + +/*++ + +Routine Description: + + Dumps the AFD_CONNECTION structure at the specified address. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + + DWORD address = 0; + ULONG result; + AFD_CONNECTION connection; + + // + // Snag the address from the command line. + // + + sscanf( args, "%lx", &address ); + + if( address == 0 ) { + + dprintf( "use: conn address\n" ); + return; + + } + + if( !ReadMemory( + address, + &connection, + sizeof(connection), + &result + ) ) { + + dprintf( + "conn: cannot read AFD_CONNECTION @ %08lx\n", + address + ); + + return; + + } + + DumpAfdConnection( + &connection, + address + ); + +} // conn + diff --git a/private/ntos/afd/afdkd/cons.h b/private/ntos/afd/afdkd/cons.h new file mode 100644 index 000000000..82543817f --- /dev/null +++ b/private/ntos/afd/afdkd/cons.h @@ -0,0 +1,36 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + cons.h + +Abstract: + + Global constant definitions for the AFD.SYS Kernel Debugger + Extensions. + +Author: + + Keith Moore (keithmo) 19-Apr-1995. + +Environment: + + User Mode. + +--*/ + + +#ifndef _CONS_H_ +#define _CONS_H_ + + +#define MAX_TRANSPORT_ADDR 256 +#define Address00 Address[0].Address[0] +#define UC(x) ((UINT)((x) & 0xFF)) +#define NTOHS(x) ( (UC(x) * 256) + UC((x) >> 8) ) + + +#endif // _CONS_H_ + diff --git a/private/ntos/afd/afdkd/data.h b/private/ntos/afd/afdkd/data.h new file mode 100644 index 000000000..39cf9083a --- /dev/null +++ b/private/ntos/afd/afdkd/data.h @@ -0,0 +1,40 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + data.h + +Abstract: + + Global data definitions for the AFD.SYS Kernel Debugger + Extensions. + +Author: + + Keith Moore (keithmo) 19-Apr-1995. + +Environment: + + User Mode. + +--*/ + + +#ifndef _DATA_H_ +#define _DATA_H_ + + +extern EXT_API_VERSION ApiVersion; +extern WINDBG_EXTENSION_APIS ExtensionApis; +extern ULONG STeip; +extern ULONG STebp; +extern ULONG STesp; +extern USHORT SavedMajorVersion; +extern USHORT SavedMinorVersion; +extern BOOL IsCheckedAfd; + + +#endif // _DATA_H_ + diff --git a/private/ntos/afd/afdkd/dbgutil.c b/private/ntos/afd/afdkd/dbgutil.c new file mode 100644 index 000000000..82bc617e0 --- /dev/null +++ b/private/ntos/afd/afdkd/dbgutil.c @@ -0,0 +1,140 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + dbgutil.c + +Abstract: + + Utility functions for dealing with the kernel debugger. + +Author: + + Keith Moore (keithmo) 19-Apr-1995 + +Environment: + + User Mode. + +Revision History: + +--*/ + + +#include "afdkdp.h" +#pragma hdrstop + + +// +// Public functions. +// + +PSTR +LongLongToString( + LONGLONG Value + ) + +/*++ + +Routine Description: + + Maps a LONGLONG to a displayable string. + +Arguments: + + Value - The LONGLONG to map. + +Return Value: + + PSTR - Points to the displayable form of the LONGLONG. + +Notes: + + This routine is NOT multithread safe! + +--*/ + +{ + + static char buffer[32]; + PSTR p1; + PSTR p2; + char ch; + int digit; + BOOL negative; + + // + // Handling zero specially makes everything else a bit easier. + // + + if( Value == 0 ) { + + return "0"; + + } + + // + // Remember if the value is negative. + // + + if( Value < 0 ) { + + negative = TRUE; + Value = -Value; + + } else { + + negative = FALSE; + + } + + // + // Pull the least signifigant digits off the value and store them + // into the buffer. Note that this will store the digits in the + // reverse order. + // + + p1 = p2 = buffer; + + while( Value != 0 ) { + + digit = (int)( Value % 10 ); + Value = Value / 10; + + *p1++ = '0' + digit; + + } + + // + // Tack on a leading '-' if necessary. + // + + if( negative ) { + + *p1++ = '-'; + + } + + // + // Reverse the digits in the buffer. + // + + *p1-- = '\0'; + + while( p1 > p2 ) { + + ch = *p1; + *p1 = *p2; + *p2 = ch; + + p2++; + p1--; + + } + + return buffer; + +} // LongLongToString + diff --git a/private/ntos/afd/afdkd/endp.c b/private/ntos/afd/afdkd/endp.c new file mode 100644 index 000000000..71611c9d9 --- /dev/null +++ b/private/ntos/afd/afdkd/endp.c @@ -0,0 +1,486 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + endp.c + +Abstract: + + Implements the endp, state, port, and proc commands. + +Author: + + Keith Moore (keithmo) 19-Apr-1995 + +Environment: + + User Mode. + +Revision History: + +--*/ + + +#include "afdkdp.h" +#pragma hdrstop + + +// +// Private prototypes. +// + +BOOL +DumpEndpointCallback( + PAFD_ENDPOINT Endpoint, + DWORD ActualAddress, + LPVOID Context + ); + +BOOL +FindStateCallback( + PAFD_ENDPOINT Endpoint, + DWORD ActualAddress, + LPVOID Context + ); + +BOOL +FindPortCallback( + PAFD_ENDPOINT Endpoint, + DWORD ActualAddress, + LPVOID Context + ); + +BOOL +FindProcessCallback( + PAFD_ENDPOINT Endpoint, + DWORD ActualAddress, + LPVOID Context + ); + + +// +// Public functions. +// + +DECLARE_API( endp ) + +/*++ + +Routine Description: + + Dumps the AFD_ENDPOINT structure at the specified address, if + given or all endpoints. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + + DWORD address = 0; + ULONG result; + AFD_ENDPOINT endpoint; + + // + // Snag the address from the command line. + // + + sscanf( args, "%lx", &address ); + + if( address == 0 ) { + + EnumEndpoints( + DumpEndpointCallback, + NULL + ); + + } else { + + if( !ReadMemory( + address, + &endpoint, + sizeof(endpoint), + &result + ) ) { + + dprintf( + "endp: cannot read AFD_ENDPOINT @ %08lx\n", + address + ); + + return; + + } + + DumpAfdEndpoint( + &endpoint, + address + ); + + } + +} // endp + + +DECLARE_API( state ) + +/*++ + +Routine Description: + + Dumps all AFD_ENDPOINT structures in the given state. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + + DWORD state = 0; + + // + // Snag the state from the command line. + // + + sscanf( args, "%lx", &state ); + + if( state == 0 ) { + + dprintf( "use: state state\n" ); + dprintf( " valid states are:\n" ); + dprintf( " 0 - Open\n" ); + dprintf( " 1 - Bound\n" ); + dprintf( " 2 - Listening\n" ); + dprintf( " 3 - Connected\n" ); + dprintf( " 4 - Cleanup\n" ); + dprintf( " 5 - Closing\n" ); + dprintf( " 6 - TransmitClosing\n" ); + dprintf( " 7 - Invalid\n" ); + + return; + + } + + EnumEndpoints( + FindStateCallback, + (LPVOID)state + ); + +} // state + + +DECLARE_API( port ) + +/*++ + +Routine Description: + + Dumps all AFD_ENDPOINT structures bound to the given port. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + + DWORD port = 0; + + // + // Snag the port from the command line. + // + + sscanf( args, "%lx", &port ); + + if( port == 0 ) { + + dprintf( "use: port port\n" ); + return; + + } + + EnumEndpoints( + FindPortCallback, + (LPVOID)port + ); + +} // port + + +DECLARE_API( proc ) + +/*++ + +Routine Description: + + Dumps all AFD_ENDPOINT structures owned by the given process. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + + DWORD process = 0; + + // + // Snag the port from the command line. + // + + sscanf( args, "%lx", &process ); + + if( process == 0 ) { + + dprintf( "use: process process\n" ); + return; + + } + + EnumEndpoints( + FindProcessCallback, + (LPVOID)process + ); + +} // proc + + +// +// Private prototypes. +// + +BOOL +DumpEndpointCallback( + PAFD_ENDPOINT Endpoint, + DWORD ActualAddress, + LPVOID Context + ) + +/*++ + +Routine Description: + + EnumEndpoints() callback for dumping AFD_ENDPOINTs. + +Arguments: + + Endpoint - The current AFD_ENDPOINT. + + ActualAddress - The actual address where the structure resides on the + debugee. + + Context - The context value passed into EnumEndpoints(). + +Return Value: + + BOOL - TRUE if enumeration should continue, FALSE if it should be + terminated. + +--*/ + +{ + + DumpAfdEndpoint( + Endpoint, + ActualAddress + ); + + return TRUE; + +} // DumpEndpointCallback + +BOOL +FindStateCallback( + PAFD_ENDPOINT Endpoint, + DWORD ActualAddress, + LPVOID Context + ) + +/*++ + +Routine Description: + + EnumEndpoints() callback for finding AFD_ENDPOINTs in a specific state. + +Arguments: + + Endpoint - The current AFD_ENDPOINT. + + ActualAddress - The actual address where the structure resides on the + debugee. + + Context - The context value passed into EnumEndpoints(). + +Return Value: + + BOOL - TRUE if enumeration should continue, FALSE if it should be + terminated. + +--*/ + +{ + + if( (DWORD)Endpoint->State == (DWORD)Context ) { + + DumpAfdEndpoint( + Endpoint, + ActualAddress + ); + + } + + return TRUE; + +} // FindStateCallback + +BOOL +FindPortCallback( + PAFD_ENDPOINT Endpoint, + DWORD ActualAddress, + LPVOID Context + ) + +/*++ + +Routine Description: + + EnumEndpoints() callback for finding AFD_ENDPOINT bound to a specific + port. + +Arguments: + + Endpoint - The current AFD_ENDPOINT. + + ActualAddress - The actual address where the structure resides on the + debugee. + + Context - The context value passed into EnumEndpoints(). + +Return Value: + + BOOL - TRUE if enumeration should continue, FALSE if it should be + terminated. + +--*/ + +{ + + TA_IP_ADDRESS ipAddress; + ULONG result; + DWORD endpointPort; + + if( ( Endpoint->LocalAddressLength != sizeof(ipAddress) ) || + ( Endpoint->LocalAddress == NULL ) ) { + + return TRUE; + + } + + if( !ReadMemory( + (DWORD)Endpoint->LocalAddress, + &ipAddress, + sizeof(ipAddress), + &result + ) ) { + + dprintf( + "port: cannot read Endpoint->LocalAddress @ %08lx\n", + Endpoint->LocalAddress + ); + + return TRUE; + + } + + if( ( ipAddress.TAAddressCount != 1 ) || + ( ipAddress.Address[0].AddressLength != sizeof(TDI_ADDRESS_IP) ) || + ( ipAddress.Address[0].AddressType != TDI_ADDRESS_TYPE_IP ) ) { + + return TRUE; + + } + + endpointPort = (DWORD)NTOHS(ipAddress.Address[0].Address[0].sin_port); + + if( endpointPort == (DWORD)Context ) { + + DumpAfdEndpoint( + Endpoint, + ActualAddress + ); + + } + + return TRUE; + +} // FindPortCallback + +BOOL +FindProcessCallback( + PAFD_ENDPOINT Endpoint, + DWORD ActualAddress, + LPVOID Context + ) + +/*++ + +Routine Description: + + EnumEndpoints() callback for finding AFD_ENDPOINTs owned by a specific + process. + +Arguments: + + Endpoint - The current AFD_ENDPOINT. + + ActualAddress - The actual address where the structure resides on the + debugee. + + Context - The context value passed into EnumEndpoints(). + +Return Value: + + BOOL - TRUE if enumeration should continue, FALSE if it should be + terminated. + +--*/ + +{ + + if( (LPVOID)Endpoint->OwningProcess == Context ) { + + DumpAfdEndpoint( + Endpoint, + ActualAddress + ); + + } + + return TRUE; + +} // FindProcessCallback + diff --git a/private/ntos/afd/afdkd/enumendp.c b/private/ntos/afd/afdkd/enumendp.c new file mode 100644 index 000000000..17164f759 --- /dev/null +++ b/private/ntos/afd/afdkd/enumendp.c @@ -0,0 +1,136 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + enumendp.c + +Abstract: + + Enumerates all AFD_ENDPOINT structures in the system. + +Author: + + Keith Moore (keithmo) 19-Apr-1995 + +Environment: + + User Mode. + +Revision History: + +--*/ + + +#include "afdkdp.h" +#pragma hdrstop + + +// +// Public functions. +// + +VOID +EnumEndpoints( + PENUM_ENDPOINTS_CALLBACK Callback, + LPVOID Context + ) + +/*++ + +Routine Description: + + Enumerates all AFD_ENDPOINT structures in the system, invoking the + specified callback for each endpoint. + +Arguments: + + Callback - Points to the callback to invoke for each AFD_ENDPOINT. + + Context - An uninterpreted context value passed to the callback + routine. + +Return Value: + + None. + +--*/ + +{ + + PAFD_ENDPOINT endpoint; + LIST_ENTRY listEntry; + PLIST_ENTRY nextEntry; + ULONG listHead; + ULONG result; + DWORD i; + AFD_ENDPOINT localEndpoint; + + listHead = GetExpression( "afd!AfdEndpointListHead" ); + + if( listHead == 0 ) { + + dprintf( "cannot find afd!AfdEndpointlistHead\n" ); + return; + + } + + if( !ReadMemory( + (DWORD)listHead, + &listEntry, + sizeof(listEntry), + &result + ) ) { + + dprintf( + "EnumEndpoints: cannot read afd!AfdEndpointlistHead @ %08lx\n", + listHead + ); + + } + + nextEntry = listEntry.Flink; + + while( nextEntry != (PLIST_ENTRY)listHead ) { + + if( CheckControlC() ) { + + break; + + } + + endpoint = CONTAINING_RECORD( + nextEntry, + AFD_ENDPOINT, + GlobalEndpointListEntry + ); + + if( !ReadMemory( + (DWORD)endpoint, + &localEndpoint, + sizeof(localEndpoint), + &result + ) ) { + + dprintf( + "EnumEndpoints: cannot read AFD_ENDPOINT @ %08lx\n", + endpoint + ); + + return; + + } + + nextEntry = localEndpoint.GlobalEndpointListEntry.Flink; + + if( !(Callback)( &localEndpoint, (DWORD)endpoint, Context ) ) { + + break; + + } + + } + +} // EnumEndpoints + diff --git a/private/ntos/afd/afdkd/help.c b/private/ntos/afd/afdkd/help.c new file mode 100644 index 000000000..69adbf01f --- /dev/null +++ b/private/ntos/afd/afdkd/help.c @@ -0,0 +1,81 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + help.c + +Abstract: + + Help for AFD.SYS Kernel Debugger Extensions. + +Author: + + Keith Moore (keithmo) 19-Apr-1995 + +Environment: + + User Mode. + +Revision History: + +--*/ + + +#include "afdkdp.h" +#pragma hdrstop + + +// +// Public functions. +// + +DECLARE_API( help ) + +/*++ + +Routine Description: + + Displays help for the AFD.SYS Kernel Debugger Extensions. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + + dprintf( "? - Displays this list\n" ); + dprintf( "help - Displays this list\n" ); + dprintf( "endp [address...] - Dumps endpoints\n" ); + dprintf( "port port [port...] - Dumps endpoints bound to specific ports\n" ); + dprintf( "state state [state...] - Dumps endpoints in specific states\n" ); + dprintf( " valid states are:\n" ); + dprintf( " 0 - Open\n" ); + dprintf( " 1 - Bound\n" ); + dprintf( " 2 - Listening\n" ); + dprintf( " 3 - Connected\n" ); + dprintf( " 4 - Cleanup\n" ); + dprintf( " 5 - Closing\n" ); + dprintf( " 6 - TransmitClosing\n" ); + dprintf( " 7 - Invalid\n" ); + dprintf( "proc address [address...] - Dumps endpoints owned by a process\n" ); + dprintf( "conn address [address...] - Dumps connections\n" ); + dprintf( "addr address [address...] - Dumps transport addresses\n" ); + dprintf( "cref address - Dumps connection reference debug info\n" ); + dprintf( "eref address - Dumps endpoint reference debug info\n" ); +#if GLOBAL_REFERENCE_DEBUG + dprintf( "gref - Dumps global reference debug info\n" ); +#endif + dprintf( "tranfile address - Dumps TransmitFile info\n" ); + dprintf( "buffer address - Dumps buffer structure\n" ); + dprintf( "stats - Dumps debug-only statistics\n" ); + +} // help + diff --git a/private/ntos/afd/afdkd/kdexts.c b/private/ntos/afd/afdkd/kdexts.c new file mode 100644 index 000000000..80a46f97b --- /dev/null +++ b/private/ntos/afd/afdkd/kdexts.c @@ -0,0 +1,136 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + kdexts.c + +Abstract: + + This file contains the generic routines and initialization code + for the kernel debugger extensions dll. + +Author: + + Wesley Witt (wesw) 26-Aug-1993 + +Environment: + + User Mode + +--*/ + +#include "afdkdp.h" +#pragma hdrstop + +#include <ntverp.h> +#include <imagehlp.h> + +// +// globals +// + +EXT_API_VERSION ApiVersion = { 3, 5, EXT_API_VERSION_NUMBER, 0 }; +WINDBG_EXTENSION_APIS ExtensionApis; +ULONG STeip; +ULONG STebp; +ULONG STesp; +USHORT SavedMajorVersion; +USHORT SavedMinorVersion; +BOOL IsCheckedAfd; + + + +DllInit( + HANDLE hModule, + DWORD dwReason, + DWORD dwReserved + ) +{ + switch (dwReason) { + case DLL_THREAD_ATTACH: + break; + + case DLL_THREAD_DETACH: + break; + + case DLL_PROCESS_DETACH: + break; + + case DLL_PROCESS_ATTACH: + break; + } + + return TRUE; +} + + +VOID +WinDbgExtensionDllInit( + PWINDBG_EXTENSION_APIS lpExtensionApis, + USHORT MajorVersion, + USHORT MinorVersion + ) +{ + ExtensionApis = *lpExtensionApis; + + SavedMajorVersion = MajorVersion; + SavedMinorVersion = MinorVersion; + + // + // Try to get a pointer to afd!AfdDebug. If we can, then we know + // the target machine is running a checked AFD.SYS. + // + + IsCheckedAfd = ( GetExpression( "afd!AfdDebug" ) != 0 ); + + return; +} + +DECLARE_API( version ) +{ +#if DBG + PCHAR DebuggerType = "Checked"; +#else + PCHAR DebuggerType = "Free"; +#endif + + dprintf( "%s Extension dll for Build %d debugging %s kernel for Build %d\n", + DebuggerType, + VER_PRODUCTBUILD, + SavedMajorVersion == 0x0c ? "Checked" : "Free", + SavedMinorVersion + ); + + dprintf( "Running %s AFD.SYS\n", + IsCheckedAfd ? "Checked" : "Free" + ); +} + +VOID +CheckVersion( + VOID + ) +{ +#if DBG + if ((SavedMajorVersion != 0x0c) || (SavedMinorVersion != VER_PRODUCTBUILD)) { + dprintf("\r\n*** Extension DLL(%d Checked) does not match target system(%d %s)\r\n\r\n", + VER_PRODUCTBUILD, SavedMinorVersion, (SavedMajorVersion==0x0f) ? "Free" : "Checked" ); + } +#else + if ((SavedMajorVersion != 0x0f) || (SavedMinorVersion != VER_PRODUCTBUILD)) { + dprintf("\r\n*** Extension DLL(%d Free) does not match target system(%d %s)\r\n\r\n", + VER_PRODUCTBUILD, SavedMinorVersion, (SavedMajorVersion==0x0f) ? "Free" : "Checked" ); + } +#endif +} + +LPEXT_API_VERSION +ExtensionApiVersion( + VOID + ) +{ + return &ApiVersion; +} + diff --git a/private/ntos/afd/afdkd/makefile b/private/ntos/afd/afdkd/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/afd/afdkd/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/afd/afdkd/proc.h b/private/ntos/afd/afdkd/proc.h new file mode 100644 index 000000000..4ab141ea2 --- /dev/null +++ b/private/ntos/afd/afdkd/proc.h @@ -0,0 +1,117 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + proc.h + +Abstract: + + Global procedure declarations for the AFD.SYS Kernel Debugger + Extensions. + +Author: + + Keith Moore (keithmo) 19-Apr-1995. + +Environment: + + User Mode. + +--*/ + + +#ifndef _PROC_H_ +#define _PROC_H_ + + +// +// Functions from AFDUTIL.C. +// + +VOID +DumpAfdEndpoint( + PAFD_ENDPOINT Endpoint, + DWORD ActualAddress + ); + +VOID +DumpAfdConnection( + PAFD_CONNECTION Connection, + DWORD ActualAddress + ); + +VOID +DumpAfdConnectionReferenceDebug( + PAFD_REFERENCE_DEBUG ReferenceDebug, + DWORD ActualAddress + ); + +VOID +DumpAfdEndpointReferenceDebug( + PAFD_REFERENCE_DEBUG ReferenceDebug, + DWORD ActualAddress + ); + +#if GLOBAL_REFERENCE_DEBUG +BOOL +DumpAfdGlobalReferenceDebug( + PAFD_GLOBAL_REFERENCE_DEBUG ReferenceDebug, + DWORD ActualAddress, + DWORD CurrentSlot, + DWORD StartingSlot, + DWORD NumEntries, + DWORD CompareAddress + ); +#endif + +VOID +DumpAfdTransmitInfo( + PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo, + DWORD ActualAddress + ); + +VOID +DumpAfdBuffer( + PAFD_BUFFER Buffer, + DWORD ActualAddress + ); + + +// +// Functions from DBGUTIL.C. +// + +PSTR +LongLongToString( + LONGLONG Value + ); + + +// +// Functions from ENUMENDP.C. +// + +VOID +EnumEndpoints( + PENUM_ENDPOINTS_CALLBACK Callback, + LPVOID Context + ); + + + +// +// Functions from TDIUTIL.C. +// + +VOID +DumpTransportAddress( + PCHAR Prefix, + PTRANSPORT_ADDRESS Address, + DWORD ActualAddress + ); + + +#endif // _PROC_H_ + diff --git a/private/ntos/afd/afdkd/ref.c b/private/ntos/afd/afdkd/ref.c new file mode 100644 index 000000000..3df4ef264 --- /dev/null +++ b/private/ntos/afd/afdkd/ref.c @@ -0,0 +1,336 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + ref.c + +Abstract: + + Implements the cref, eref, and gref commands. + +Author: + + Keith Moore (keithmo) 09-Dec-1995 + +Environment: + + User Mode. + +Revision History: + +--*/ + + +#include "afdkdp.h" +#pragma hdrstop + + +// +// Public functions. +// + +DECLARE_API( cref ) + +/*++ + +Routine Description: + + Dumps the AFD_REFERENCE_DEBUG structure at the specified address. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + + DWORD address = 0; + ULONG result; + AFD_REFERENCE_DEBUG referenceDebug[MAX_REFERENCE]; + + // + // Verify we're running a checked AFD.SYS. + // + + if( !IsCheckedAfd ) { + + dprintf( + "cref: this command only available with CHECKED AFD.SYS!\n" + ); + + return; + + } + + // + // Snag the address from the command line. + // + + sscanf( args, "%lx", &address ); + + if( address == 0 ) { + + dprintf( "use: cref address\n" ); + return; + + } + + if( !ReadMemory( + address, + referenceDebug, + sizeof(referenceDebug), + &result + ) ) { + + dprintf( + "cref: cannot read AFD_REFERENCE_DEBUG @ %08lx\n", + address + ); + + } else { + + DumpAfdConnectionReferenceDebug( + referenceDebug, + address + ); + + } + +} // cref + + +DECLARE_API( eref ) + +/*++ + +Routine Description: + + Dumps the AFD_REFERENCE_DEBUG structure at the specified address. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + + DWORD address = 0; + ULONG result; + AFD_REFERENCE_DEBUG referenceDebug[MAX_REFERENCE]; + + // + // Verify we're running a checked AFD.SYS. + // + + if( !IsCheckedAfd ) { + + dprintf( + "eref: this command only available with CHECKED AFD.SYS!\n" + ); + + return; + + } + + // + // Snag the address from the command line. + // + + sscanf( args, "%lx", &address ); + + if( address == 0 ) { + + dprintf( "use: eref address\n" ); + return; + + } + + if( !ReadMemory( + address, + referenceDebug, + sizeof(referenceDebug), + &result + ) ) { + + dprintf( + "eref: cannot read AFD_REFERENCE_DEBUG @ %08lx\n", + address + ); + + } else { + + DumpAfdEndpointReferenceDebug( + referenceDebug, + address + ); + + } + +} // eref + + +DECLARE_API( gref ) + +/*++ + +Routine Description: + + Dumps the AFD_GLOBAL_REFERENCE_DEBUG structure in the system. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + +#if GLOBAL_REFERENCE_DEBUG + + DWORD address; + DWORD currentSlot; + DWORD slot; + ULONG result; + DWORD compareAddress = 0; + DWORD numEntries; + DWORD maxEntries; + DWORD entriesToRead; + CHAR buffer[sizeof(AFD_GLOBAL_REFERENCE_DEBUG) * 64]; + + // + // Verify we're running a checked AFD.SYS. + // + + if( !IsCheckedAfd ) { + + dprintf( + "gref: this command only available with CHECKED AFD.SYS!\n" + ); + + return; + + } + + // + // Snag the optional "connection compare" address from the command line. + // + + sscanf( args, "%lx", &compareAddress ); + + // + // Find the global reference data. + // + + address = GetExpression( "afd!AfdGlobalReference" ); + + if( address == 0 ) { + + dprintf( "cannot find afd!AfdGlobalReference\n" ); + return; + + } + + currentSlot = GetExpression( "afd!AfdGlobalReferenceSlot" ); + + if( currentSlot == 0 ) { + + dprintf( "cannot find afd!AfdGlobalReferenceSlot\n" ); + return; + + } + + if( !ReadMemory( + currentSlot, + ¤tSlot, + sizeof(currentSlot), + &result + ) ) { + + dprintf( "cannot read afd!AfdGlobalReferenceSlot\n" ); + return; + + } + + if( currentSlot < MAX_GLOBAL_REFERENCE ) { + + numEntries = currentSlot; + + } else { + + numEntries = MAX_GLOBAL_REFERENCE; + + } + + // + // Dump it all. + // + + slot = 0; + maxEntries = sizeof(buffer) / sizeof(AFD_GLOBAL_REFERENCE_DEBUG); + currentSlot %= MAX_GLOBAL_REFERENCE; + + while( numEntries > 0 ) { + + entriesToRead = min( numEntries, maxEntries ); + + if( !ReadMemory( + address, + buffer, + entriesToRead * sizeof(AFD_GLOBAL_REFERENCE_DEBUG), + &result + ) ) { + + dprintf( + "gref: cannot read AFD_GLOBAL_REFERENCE_DEBUG @ %08lx\n", + address + ); + + return; + + } + + if( DumpAfdGlobalReferenceDebug( + (PAFD_GLOBAL_REFERENCE_DEBUG)buffer, + address, + currentSlot, + slot, + entriesToRead, + compareAddress + ) ) { + + break; + + } + + address += entriesToRead * sizeof(AFD_GLOBAL_REFERENCE_DEBUG); + slot += entriesToRead; + numEntries -= entriesToRead; + + } + +#else + + dprintf( + "gref: not yet implemented\n" + ); + +#endif + +} // gref + diff --git a/private/ntos/afd/afdkd/sources b/private/ntos/afd/afdkd/sources new file mode 100644 index 000000000..af98dbc10 --- /dev/null +++ b/private/ntos/afd/afdkd/sources @@ -0,0 +1,45 @@ +MAJORCOMP=sdktools +MINORCOMP=afdkd + +TARGETNAME=afdkd +TARGETPATH=obj +TARGETTYPE=DYNLINK + +SOURCES= \ + addr.c \ + afdutil.c \ + buffer.c \ + conn.c \ + dbgutil.c \ + endp.c \ + enumendp.c \ + help.c \ + kdexts.c \ + ref.c \ + stats.c \ + tdiutil.c \ + tranfile.c \ + afdkd.rc + +INCLUDES=.;\ + $(BASEDIR)\private\inc;\ + $(BASEDIR)\private\ntos\inc;\ + $(BASEDIR)\private\ntos\afd; + +C_DEFINES=$(C_DEFINES) -DREFERENCE_DEBUG=1 + +!IF "$(NT351)" != "" +C_DEFINES=$(C_DEFINES) -DNT351=1 +!ENDIF + +MSC_WARNING_LEVEL=/W3 /WX + +USE_NTDLL=1 + +UMTYPE=windows + +!IF "$(NTNOPCH)" == "" +PRECOMPILED_INCLUDE=afdkdp.h +PRECOMPILED_PCH=afdkdp.pch +PRECOMPILED_OBJ=afdkdp.obj +!ENDIF diff --git a/private/ntos/afd/afdkd/stats.c b/private/ntos/afd/afdkd/stats.c new file mode 100644 index 000000000..4e36bb96c --- /dev/null +++ b/private/ntos/afd/afdkd/stats.c @@ -0,0 +1,335 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + stats.c + +Abstract: + + Implements the stats command. + +Author: + + Keith Moore (keithmo) 06-May-1996 + +Environment: + + User Mode. + +Revision History: + +--*/ + + +#include "afdkdp.h" +#pragma hdrstop + + +// +// Public functions. +// + +DECLARE_API( stats ) + +/*++ + +Routine Description: + + Dumps the debug-only AFD statistic counters. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + AFD_QUOTA_STATS quotaStats; + AFD_HANDLE_STATS handleStats; + AFD_QUEUE_STATS queueStats; + AFD_CONNECTION_STATS connectionStats; + DWORD address; + ULONG result; + + // + // Dump the quota statistics. + // + + address = GetExpression( "afd!AfdQuotaStats" ); + + if( address == 0 ) { + + dprintf( "cannot find afd!AfdQuotaStats\n" ); + + } else { + + if( ReadMemory( + address, + "aStats, + sizeof(quotaStats), + &result + ) ) { + + dprintf( + "AfdQuotaStats:\n" + ); + + dprintf( + " Charged = %s\n", + LongLongToString( quotaStats.Charged.QuadPart ) + ); + + dprintf( + " Returned = %s\n", + LongLongToString( quotaStats.Returned.QuadPart ) + ); + + dprintf( "\n" ); + + } else { + + dprintf( + "stats: cannot read afd!AfdQuotaStats @ %08lx\n", + address + ); + + } + + } + + // + // Dump the handle statistics. + // + + address = GetExpression( "afd!AfdHandleStats" ); + + if( address == 0 ) { + + dprintf( "cannot find afd!AfdHandleStats\n" ); + + } else { + + if( ReadMemory( + address, + &handleStats, + sizeof(handleStats), + &result + ) ) { + + dprintf( + "AfdHandleStats:\n" + ); + + dprintf( + " AddrOpened = %lu\n", + handleStats.AddrOpened + ); + + dprintf( + " AddrClosed = %lu\n", + handleStats.AddrClosed + ); + + dprintf( + " AddrRef = %lu\n", + handleStats.AddrRef + ); + + dprintf( + " AddrDeref = %lu\n", + handleStats.AddrDeref + ); + + dprintf( + " ConnOpened = %lu\n", + handleStats.ConnOpened + ); + + dprintf( + " ConnClosed = %lu\n", + handleStats.ConnClosed + ); + + dprintf( + " ConnRef = %lu\n", + handleStats.ConnRef + ); + + dprintf( + " ConnDeref = %lu\n", + handleStats.ConnDeref + ); + + dprintf( + " FileRef = %lu\n", + handleStats.FileRef + ); + + dprintf( + " FileDeref = %lu\n", + handleStats.FileDeref + ); + + dprintf( "\n" ); + + } else { + + dprintf( + "stats: cannot read afd!AfdHandleStats @ %08lx\n", + address + ); + + } + + } + + // + // Dump the queue statistics. + // + + address = GetExpression( "afd!AfdQueueStats" ); + + if( address == 0 ) { + + dprintf( "cannot find afd!AfdQueueStats\n" ); + + } else { + + if( ReadMemory( + address, + &queueStats, + sizeof(queueStats), + &result + ) ) { + + dprintf( + "AfdQueueStats:\n" + ); + + dprintf( + " AfdWorkItemsQueued = %lu\n", + queueStats.AfdWorkItemsQueued + ); + + dprintf( + " ExWorkItemsQueued = %lu\n", + queueStats.ExWorkItemsQueued + ); + + dprintf( + " WorkerEnter = %lu\n", + queueStats.WorkerEnter + ); + + dprintf( + " WorkerLeave = %lu\n", + queueStats.WorkerLeave + ); + + dprintf( + " AfdWorkItemsProcessed = %lu\n", + queueStats.AfdWorkItemsProcessed + ); + + dprintf( + " AfdWorkerThread = %08lx\n", + queueStats.AfdWorkerThread + ); + + dprintf( "\n" ); + + } else { + + dprintf( + "stats: cannot read afd!AfdQueueStats @ %08lx\n", + address + ); + + } + + } + + // + // Dump the queue statistics. + // + + address = GetExpression( "afd!AfdConnectionStats" ); + + if( address == 0 ) { + + dprintf( "cannot find afd!AfdConnectionStats\n" ); + + } else { + + if( ReadMemory( + address, + &connectionStats, + sizeof(connectionStats), + &result + ) ) { + + dprintf( + "AfdConnectionStats:\n" + ); + + dprintf( + " ConnectedReferencesAdded = %lu\n", + connectionStats.ConnectedReferencesAdded + ); + + dprintf( + " ConnectedReferencesDeleted = %lu\n", + connectionStats.ConnectedReferencesDeleted + ); + + dprintf( + " GracefulDisconnectsInitiated = %lu\n", + connectionStats.GracefulDisconnectsInitiated + ); + + dprintf( + " GracefulDisconnectsCompleted = %lu\n", + connectionStats.GracefulDisconnectsCompleted + ); + + dprintf( + " GracefulDisconnectIndications = %lu\n", + connectionStats.GracefulDisconnectIndications + ); + + dprintf( + " AbortiveDisconnectsInitiated = %lu\n", + connectionStats.AbortiveDisconnectsInitiated + ); + + dprintf( + " AbortiveDisconnectsCompleted = %lu\n", + connectionStats.AbortiveDisconnectsCompleted + ); + + dprintf( + " AbortiveDisconnectIndications = %lu\n", + connectionStats.AbortiveDisconnectIndications + ); + + dprintf( "\n" ); + + } else { + + dprintf( + "stats: cannot read afd!AfdConnectionStats @ %08lx\n", + address + ); + + } + + } + +} // stats + diff --git a/private/ntos/afd/afdkd/tdiutil.c b/private/ntos/afd/afdkd/tdiutil.c new file mode 100644 index 000000000..01d60bbf4 --- /dev/null +++ b/private/ntos/afd/afdkd/tdiutil.c @@ -0,0 +1,367 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + tdiutil.c + +Abstract: + + Utility functions for dumping various TDI structures. + +Author: + + Keith Moore (keithmo) 19-Apr-1995 + +Environment: + + User Mode. + +Revision History: + +--*/ + + +#include "afdkdp.h" +#pragma hdrstop + + +// +// Private prototypes. +// + +PSTR +TransportAddressTypeToString( + USHORT AddressType + ); + +PSTR +NetbiosNameTypeToString( + USHORT NetbiosNameType + ); + + +// +// Public functions. +// + +VOID +DumpTransportAddress( + PCHAR Prefix, + PTRANSPORT_ADDRESS Address, + DWORD ActualAddress + ) + +/*++ + +Routine Description: + + Dumps the specified TRANSPORT_ADDRESS structure. + +Arguments: + + Prefix - A character string prefix to display before each line. Used + to make things pretty. + + Address - Points to the TRANSPORT_ADDRESS to dump. + + ActualAddress - The actual address where the structure resides on the + debugee. + +Return Value: + + None. + +--*/ + +{ + + dprintf( + "%sTRANSPORT_ADDRESS @ %08lx\n", + Prefix, + ActualAddress + ); + + dprintf( + "%s AddressLength = %u\n", + Prefix, + Address->Address[0].AddressLength + ); + + dprintf( + "%s AddressType = %u (%s)\n", + Prefix, + Address->Address[0].AddressType, + TransportAddressTypeToString( Address->Address[0].AddressType ) + ); + + switch( Address->Address[0].AddressType ) { + + case TDI_ADDRESS_TYPE_IP : { + + PTA_IP_ADDRESS ipAddress; + + ipAddress = (PTA_IP_ADDRESS)Address; + + dprintf( + "%s sin_port = %u\n", + Prefix, + NTOHS(ipAddress->Address00.sin_port) + ); + + dprintf( + "%s in_addr = %d.%d.%d.%d\n", + Prefix, + UC(ipAddress->Address00.in_addr >> 0), + UC(ipAddress->Address00.in_addr >> 8), + UC(ipAddress->Address00.in_addr >> 16), + UC(ipAddress->Address00.in_addr >> 24) + ); + + } + break; + + case TDI_ADDRESS_TYPE_IPX : { + + PTA_IPX_ADDRESS ipxAddress; + + ipxAddress = (PTA_IPX_ADDRESS)Address; + + dprintf( + "%s NetworkAddress = %08lx\n", + Prefix, + ipxAddress->Address00.NetworkAddress + ); + + dprintf( + "%s NodeAddress = %02X-%02X-%02X-%02X-%02X-%02X\n", + Prefix, + ipxAddress->Address00.NodeAddress[0], + ipxAddress->Address00.NodeAddress[1], + ipxAddress->Address00.NodeAddress[2], + ipxAddress->Address00.NodeAddress[3], + ipxAddress->Address00.NodeAddress[4], + ipxAddress->Address00.NodeAddress[5] + ); + + dprintf( + "%s Socket = %04X\n", + Prefix, + ipxAddress->Address00.Socket + ); + + } + break; + + case TDI_ADDRESS_TYPE_NETBIOS : { + + PTA_NETBIOS_ADDRESS netbiosAddress; + UCHAR netbiosName[17]; + + netbiosAddress = (PTA_NETBIOS_ADDRESS)Address; + + dprintf( + "%s NetbiosNameType = %04X (%s)\n", + Prefix, + netbiosAddress->Address00.NetbiosNameType, + NetbiosNameTypeToString( netbiosAddress->Address00.NetbiosNameType ) + ); + + RtlCopyMemory( + netbiosName, + netbiosAddress->Address00.NetbiosName, + 16 + ); + + netbiosName[16] = '\0'; + + dprintf( + "%s NetbiosName = %s\n", + Prefix, + netbiosName + ); + + } + break; + + default : + + dprintf( + "%s Unsupported address type\n", + Prefix + ); + + break; + + } + +} // DumpAfdEndpoint + + +// +// Private functions. +// + +PSTR +TransportAddressTypeToString( + USHORT AddressType + ) + +/*++ + +Routine Description: + + Maps a transport address type to a displayable string. + +Arguments: + + AddressType - The transport address type to map. + +Return Value: + + PSTR - Points to the displayable form of the tranport address type. + +--*/ + +{ + + switch( AddressType ) { + + case TDI_ADDRESS_TYPE_UNSPEC : + + return "Unspecified"; + + case TDI_ADDRESS_TYPE_UNIX : + + return "Unix"; + + case TDI_ADDRESS_TYPE_IP : + + return "Ip"; + + case TDI_ADDRESS_TYPE_IMPLINK : + + return "Implink"; + + case TDI_ADDRESS_TYPE_PUP : + + return "Pup"; + + case TDI_ADDRESS_TYPE_CHAOS : + + return "Chaos"; + + case TDI_ADDRESS_TYPE_IPX : + + return "Ipx"; + + case TDI_ADDRESS_TYPE_NBS : + + return "Nbs"; + + case TDI_ADDRESS_TYPE_ECMA : + + return "Ecma"; + + case TDI_ADDRESS_TYPE_DATAKIT : + + return "Datakit"; + + case TDI_ADDRESS_TYPE_CCITT : + + return "Ccitt"; + + case TDI_ADDRESS_TYPE_SNA : + + return "Sna"; + + case TDI_ADDRESS_TYPE_DECnet : + + return "Decnet"; + + case TDI_ADDRESS_TYPE_DLI : + + return "Dli"; + + case TDI_ADDRESS_TYPE_LAT : + + return "Lat"; + + case TDI_ADDRESS_TYPE_HYLINK : + + return "Hylink"; + + case TDI_ADDRESS_TYPE_APPLETALK : + + return "Appletalk"; + + case TDI_ADDRESS_TYPE_NETBIOS : + + return "Netbios"; + + case TDI_ADDRESS_TYPE_8022 : + + return "8022"; + + case TDI_ADDRESS_TYPE_OSI_TSAP : + + return "Osi Trap"; + + case TDI_ADDRESS_TYPE_NETONE : + + return "Netone"; + + } + + return "UNKNOWN"; + +} // TransportAddressTypeToString + +PSTR +NetbiosNameTypeToString( + USHORT NetbiosNameType + ) + +/*++ + +Routine Description: + + Maps a NetBIOS name type to a displayable string. + +Arguments: + + NetbiosNameType - The NetBIOS name type to map. + +Return Value: + + PSTR - Points to the displayable form of the NetBIOS name type. + +--*/ + +{ + + switch( NetbiosNameType ) { + + case TDI_ADDRESS_NETBIOS_TYPE_UNIQUE : + + return "Unique"; + + case TDI_ADDRESS_NETBIOS_TYPE_GROUP : + + return "Group"; + + case TDI_ADDRESS_NETBIOS_TYPE_QUICK_UNIQUE : + + return "Quick Unique"; + + case TDI_ADDRESS_NETBIOS_TYPE_QUICK_GROUP : + + return "Quick Group"; + + } + + return "UNKNOWN"; + +} // NetbiosNameTypeToString + diff --git a/private/ntos/afd/afdkd/tranfile.c b/private/ntos/afd/afdkd/tranfile.c new file mode 100644 index 000000000..ade0679b8 --- /dev/null +++ b/private/ntos/afd/afdkd/tranfile.c @@ -0,0 +1,94 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + tranfile.c + +Abstract: + + Implements the tranfile command. + +Author: + + Keith Moore (keithmo) 15-Apr-1996 + +Environment: + + User Mode. + +Revision History: + +--*/ + + +#include "afdkdp.h" +#pragma hdrstop + + +// +// Public functions. +// + +DECLARE_API( tranfile ) + +/*++ + +Routine Description: + + Dumps the AFD_TRANSMIT_FILE_INFO_INTERNAL structure at the specified + address. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + + DWORD address = 0; + ULONG result; + AFD_TRANSMIT_FILE_INFO_INTERNAL transmitInfo; + + // + // Snag the address from the command line. + // + + sscanf( args, "%lx", &address ); + + if( address == 0 ) { + + dprintf( "use: tranfile address\n" ); + return; + + } + + if( ReadMemory( + address, + &transmitInfo, + sizeof(transmitInfo), + &result + ) ) { + + DumpAfdTransmitInfo( + &transmitInfo, + address + ); + + } else { + + dprintf( + "tranfile: cannot read AFD_TRANSMIT_FILE_INFO_INTERNAL @ %08lx\n", + address + ); + + } + +} // tranfile + diff --git a/private/ntos/afd/afdkd/type.h b/private/ntos/afd/afdkd/type.h new file mode 100644 index 000000000..ec5bee58a --- /dev/null +++ b/private/ntos/afd/afdkd/type.h @@ -0,0 +1,39 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + type.h + +Abstract: + + Global type definitions for the AFD.SYS Kernel Debugger + Extensions. + +Author: + + Keith Moore (keithmo) 19-Apr-1995. + +Environment: + + User Mode. + +--*/ + + +#ifndef _TYPE_H_ +#define _TYPE_H_ + + +typedef +BOOL +(* PENUM_ENDPOINTS_CALLBACK)( + PAFD_ENDPOINT Endpoint, + DWORD ActualAddress, + LPVOID Context + ); + + +#endif // _TYPE_H_ + diff --git a/private/ntos/afd/afdp.h b/private/ntos/afd/afdp.h new file mode 100644 index 000000000..2bff8f50a --- /dev/null +++ b/private/ntos/afd/afdp.h @@ -0,0 +1,361 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + afd.h + +Abstract: + + This is the local header file for AFD. It includes all other + necessary header files for AFD. + +Author: + + David Treadwell (davidtr) 21-Feb-1992 + +Revision History: + +--*/ + +#ifndef _AFDP_ +#define _AFDP_ + +#include <ntos.h> +#include <zwapi.h> +#include <fsrtl.h> +#include <tdikrnl.h> + +#ifndef _AFDKDP_H_ +extern POBJECT_TYPE *IoFileObjectType; +extern POBJECT_TYPE *ExEventObjectType; +#endif // _AFDKDP_H_ + +#define IS_DGRAM_ENDPOINT(endp) \ + ((endp)->EndpointType == AfdEndpointTypeDatagram) + + +#if DBG +#define AFD_PERF_DBG 1 +#define AFD_KEEP_STATS 1 +#else +#define AFD_PERF_DBG 0 +#define AFD_KEEP_STATS 0 +#endif + +// +// Hack-O-Rama. TDI has a fundamental flaw in that it is often impossible +// to determine exactly when a TDI protocol is "done" with a connection +// object. The biggest problem here is that AFD may get a suprious TDI +// indication *after* an abort request has completed. As a temporary work- +// around, whenever an abort request completes, we'll start a timer. AFD +// will defer further processing on the connection until that timer fires. +// +// If the following symbol is defined, then our timer hack is enabled. +// + +#define ENABLE_ABORT_TIMER_HACK 1 + +// +// The following constant defines the relative time interval (in seconds) +// for the "post abort request complete" timer. +// + +#define AFD_ABORT_TIMER_TIMEOUT_VALUE 5 // seconds + +// +// Goodies stolen from other header files. +// + +#ifndef FAR +#define FAR +#endif + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +typedef unsigned short u_short; + +#ifndef SG_UNCONSTRAINED_GROUP +#define SG_UNCONSTRAINED_GROUP 0x01 +#endif + +#ifndef SG_CONSTRAINED_GROUP +#define SG_CONSTRAINED_GROUP 0x02 +#endif + + +#include <afd.h> +#include "afdstr.h" +#include "afddata.h" +#include "afdprocs.h" + +// +// Set this to a non-zero value when NTOSKRNL starts exporting the +// ExFreePoolWithTag() API. +// +// N.B. On RETAIL builds, AFD_DATA_BUFFER_POOL_TAG and AFD_WORK_ITEM_POOL_TAG +// cannot be protected as these items are allocated from within the EX +// lookaside list package and the lookaside list package does not use +// ExFreePoolWithTag(). +// + + +#ifdef NT351 +#define FREE_POOL_WITH_TAG_SUPPORTED 0 +#else +#define FREE_POOL_WITH_TAG_SUPPORTED 0 +#endif + +#if FREE_POOL_WITH_TAG_SUPPORTED + +#define AFD_EA_POOL_TAG ( (ULONG)'AdfA' | PROTECTED_POOL ) +#define AFD_APC_POOL_TAG ( (ULONG)'adfA' | PROTECTED_POOL ) +#if DBG +#define AFD_DATA_BUFFER_POOL_TAG ( (ULONG)'BdfA' | PROTECTED_POOL ) +#else +#define AFD_DATA_BUFFER_POOL_TAG ( (ULONG)'BdfA' ) +#endif +#define AFD_CONNECTION_POOL_TAG ( (ULONG)'CdfA' | PROTECTED_POOL ) +#define AFD_CONNECT_DATA_POOL_TAG ( (ULONG)'cdfA' | PROTECTED_POOL ) +#define AFD_DEBUG_POOL_TAG ( (ULONG)'DdfA' | PROTECTED_POOL ) +#define AFD_DISCONNECT_POOL_TAG ( (ULONG)'ddfA' | PROTECTED_POOL ) +#define AFD_ENDPOINT_POOL_TAG ( (ULONG)'EdfA' | PROTECTED_POOL ) +#define AFD_TRANSMIT_INFO_POOL_TAG ( (ULONG)'FdfA' | PROTECTED_POOL ) +#define AFD_TRANSMIT_DEBUG_POOL_TAG ( (ULONG)'fdfA' | PROTECTED_POOL ) +#define AFD_GROUP_POOL_TAG ( (ULONG)'GdfA' | PROTECTED_POOL ) +#define AFD_ABORT_TIMER_HACK_POOL_TAG ( (ULONG)'HdfA' | PROTECTED_POOL ) +#define AFD_TDI_POOL_TAG ( (ULONG)'IdfA' | PROTECTED_POOL ) +#define AFD_INLINE_POOL_TAG ( (ULONG)'idfA' | PROTECTED_POOL ) +#define AFD_LOCAL_ADDRESS_POOL_TAG ( (ULONG)'LdfA' | PROTECTED_POOL ) +#define AFD_LOOKASIDE_LISTS_POOL_TAG ( (ULONG)'ldfA' | PROTECTED_POOL ) +#define AFD_MDL_COMPLETION_CONTEXT_POOL_TAG ( (ULONG)'MdfA' | PROTECTED_POOL ) +#define AFD_POLL_POOL_TAG ( (ULONG)'PdfA' | PROTECTED_POOL ) +#define AFD_WORK_QUEUE_POOL_TAG ( (ULONG)'QdfA' | PROTECTED_POOL ) +#define AFD_REMOTE_ADDRESS_POOL_TAG ( (ULONG)'RdfA' | PROTECTED_POOL ) +#define AFD_RESOURCE_POOL_TAG ( (ULONG)'rdfA' | PROTECTED_POOL ) +#define AFD_SECURITY_POOL_TAG ( (ULONG)'SdfA' | PROTECTED_POOL ) +#define AFD_TRANSPORT_INFO_POOL_TAG ( (ULONG)'TdfA' | PROTECTED_POOL ) +#if DBG +#define AFD_WORK_ITEM_POOL_TAG ( (ULONG)'WdfA' | PROTECTED_POOL ) +#else +#define AFD_WORK_ITEM_POOL_TAG ( (ULONG)'WdfA' ) +#endif +#define AFD_CONTEXT_POOL_TAG ( (ULONG)'XdfA' | PROTECTED_POOL ) +#define MyFreePoolWithTag(a,t) ExFreePoolWithTag(a,t) + +#else + +#define AFD_EA_POOL_TAG 'AdfA' +#define AFD_APC_POOL_TAG 'adfA' +#define AFD_DATA_BUFFER_POOL_TAG 'BdfA' +#define AFD_CONNECTION_POOL_TAG 'CdfA' +#define AFD_CONNECT_DATA_POOL_TAG 'cdfA' +#define AFD_DEBUG_POOL_TAG 'DdfA' +#define AFD_DISCONNECT_POOL_TAG 'ddfA' +#define AFD_ENDPOINT_POOL_TAG 'EdfA' +#define AFD_TRANSMIT_INFO_POOL_TAG 'FdfA' +#define AFD_TRANSMIT_DEBUG_POOL_TAG 'fdfA' +#define AFD_GROUP_POOL_TAG 'GdfA' +#define AFD_ABORT_TIMER_HACK_POOL_TAG 'HdfA' +#define AFD_TDI_POOL_TAG 'IdfA' +#define AFD_INLINE_POOL_TAG 'idfA' +#define AFD_LOCAL_ADDRESS_POOL_TAG 'LdfA' +#define AFD_LOOKASIDE_LISTS_POOL_TAG 'ldfA' +#define AFD_MDL_COMPLETION_CONTEXT_POOL_TAG 'MdfA' +#define AFD_POLL_POOL_TAG 'PdfA' +#define AFD_WORK_QUEUE_POOL_TAG 'QdfA' +#define AFD_REMOTE_ADDRESS_POOL_TAG 'RdfA' +#define AFD_RESOURCE_POOL_TAG 'rdfA' +#define AFD_SECURITY_POOL_TAG 'SdfA' +#define AFD_TRANSPORT_INFO_POOL_TAG 'TdfA' +#define AFD_WORK_ITEM_POOL_TAG 'WdfA' +#define AFD_CONTEXT_POOL_TAG 'XdfA' + +#define MyFreePoolWithTag(a,t) ExFreePool(a) + +#endif + +#ifdef NT351 +#undef ObReferenceObject +#define ObReferenceObject(_p) ObReferenceObjectByPointer( _p, 0L, *IoFileObjectType, KernelMode ) +#undef RtlEqualMemory +#define RtlEqualMemory(_a,_b,_c) (RtlCompareMemory((_a),(_b),(_c)) == (_c)) +#endif + +#if DBG + +extern ULONG AfdDebug; +extern ULONG AfdLocksAcquired; + +#undef IF_DEBUG +#define IF_DEBUG(a) if ( (AFD_DEBUG_ ## a & AfdDebug) != 0 ) + +#define AFD_DEBUG_OPEN_CLOSE 0x00000001 +#define AFD_DEBUG_ENDPOINT 0x00000002 +#define AFD_DEBUG_CONNECTION 0x00000004 +#define AFD_DEBUG_EVENT_SELECT 0x00000008 + +#define AFD_DEBUG_BIND 0x00000010 +#define AFD_DEBUG_CONNECT 0x00000020 +#define AFD_DEBUG_LISTEN 0x00000040 +#define AFD_DEBUG_ACCEPT 0x00000080 + +#define AFD_DEBUG_SEND 0x00000100 +#define AFD_DEBUG_10 0x00000200 +#define AFD_DEBUG_RECEIVE 0x00000400 +#define AFD_DEBUG_11 0x00000800 + +#define AFD_DEBUG_POLL 0x00001000 +#define AFD_DEBUG_FAST_IO 0x00002000 + +#define DEBUG + +#define AFD_ALLOCATE_POOL(a,b,t) AfdAllocatePool( a,b,t,__FILE__,__LINE__,FALSE ) +#define AFD_ALLOCATE_POOL_WITH_QUOTA(a,b,t) AfdAllocatePool( a,b,t,__FILE__,__LINE__,TRUE ) +PVOID +AfdAllocatePool ( + IN POOL_TYPE PoolType, + IN ULONG NumberOfBytes, + IN ULONG Tag, + IN PCHAR FileName, + IN ULONG LineNumber, + IN BOOLEAN WithQuota + ); + +#define AFD_FREE_POOL(a,t) AfdFreePool(a,t) +VOID +AfdFreePool ( + IN PVOID Pointer, + IN ULONG Tag + ); + +#define AfdIoCallDriver(a,b,c) AfdIoCallDriverDebug(a,b,c,__FILE__,__LINE__) +#define AfdCompleteOutstandingIrp(a,b) AfdCompleteOutstandingIrpDebug(a,b) + +NTSTATUS +AfdIoCallDriverDebug ( + IN PAFD_ENDPOINT Endpoint, + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PCHAR FileName, + IN ULONG LineNumber + ); + +VOID +AfdCompleteOutstandingIrpDebug ( + IN PAFD_ENDPOINT Endpoint, + IN PIRP Irp + ); + +#ifdef AFDDBG_QUOTA +VOID +AfdRecordQuotaHistory( + IN PEPROCESS Process, + IN LONG Bytes, + IN PSZ Type, + IN PVOID Block + ); +#else +#define AfdRecordQuotaHistory(a,b,c,d) +#endif + +#define AfdAcquireSpinLock(a,b) \ + ASSERT( AfdLoaded ); KeAcquireSpinLock((a),(b)); AfdLocksAcquired++ + +#define AfdReleaseSpinLock(a,b) \ + AfdLocksAcquired--; ASSERT( AfdLoaded ); KeReleaseSpinLock((a),(b)) + +// +// Define our own assert so that we can actually catch assertion failures +// when running a checked AFD on a free kernel. +// + +VOID +AfdAssert( + PVOID FailedAssertion, + PVOID FileName, + ULONG LineNumber, + PCHAR Message + ); + +#undef ASSERT +#define ASSERT( exp ) \ + if (!(exp)) \ + AfdAssert( #exp, __FILE__, __LINE__, NULL ) + +#undef ASSERTMSG +#define ASSERTMSG( msg, exp ) \ + if (!(exp)) \ + AfdAssert( #exp, __FILE__, __LINE__, msg ) + +#else // !DBG + +#undef IF_DEBUG +#define IF_DEBUG(a) if (FALSE) +#define DEBUG if ( FALSE ) + +#define AFD_ALLOCATE_POOL(a,b,t) ExAllocatePoolWithTag(a,b,t) +#define AFD_ALLOCATE_POOL_WITH_QUOTA(a,b,t) ExAllocatePoolWithQuotaTag(a,b,t) +#define AFD_FREE_POOL(a,t) MyFreePoolWithTag(a,t) + +#define AfdIoCallDriver(a,b,c) AfdIoCallDriverFree(a,b,c) +#define AfdCompleteOutstandingIrp(a,b) AfdCompleteOutstandingIrpFree(a,b) + +NTSTATUS +AfdIoCallDriverFree ( + IN PAFD_ENDPOINT Endpoint, + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +AfdCompleteOutstandingIrpFree ( + IN PAFD_ENDPOINT Endpoint, + IN PIRP Irp + ); + +#define AfdRecordQuotaHistory(a,b,c,d) + +#define AfdAcquireSpinLock(a,b) KeAcquireSpinLock((a),(b)) +#define AfdReleaseSpinLock(a,b) KeReleaseSpinLock((a),(b)) + +#endif // def DBG + +#if DBG || REFERENCE_DEBUG +VOID +AfdInitializeDebugData( + VOID + ); +#endif + +// +// Various poll events disabled based on socket state. +// + +#define AFD_DISABLED_LISTENING_POLL_EVENTS ( \ + AFD_POLL_RECEIVE | \ + AFD_POLL_RECEIVE_EXPEDITED | \ + AFD_POLL_SEND | \ + AFD_POLL_CONNECT | \ + AFD_POLL_CONNECT_FAIL | \ + AFD_POLL_DISCONNECT | \ + AFD_POLL_ABORT | \ + AFD_POLL_QOS | \ + AFD_POLL_GROUP_QOS \ + ) + +// +// Make some of the receive code a bit prettier. +// + +#define TDI_RECEIVE_EITHER ( TDI_RECEIVE_NORMAL | TDI_RECEIVE_EXPEDITED ) + +#endif // ndef _AFDP_ + diff --git a/private/ntos/afd/afdprocs.h b/private/ntos/afd/afdprocs.h new file mode 100644 index 000000000..c805902e8 --- /dev/null +++ b/private/ntos/afd/afdprocs.h @@ -0,0 +1,1073 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + afdprocs.h + +Abstract: + + This module contains routine prototypes for AFD. + +Author: + + David Treadwell (davidtr) 21-Feb-1992 + +Revision History: + +--*/ + +#ifndef _AFDPROCS_ +#define _AFDPROCS_ + +NTSTATUS +DriverEntry ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +NTSTATUS +AfdAccept ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdSuperAccept ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdDeferAccept ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +PMDL +AfdAdvanceMdlChain( + IN PMDL Mdl, + IN ULONG Offset + ); + +NTSTATUS +AfdAllocateMdlChain( + IN PIRP Irp, + IN LPWSABUF BufferArray, + IN ULONG BufferCount, + IN LOCK_OPERATION Operation, + OUT PULONG TotalByteCount + ); + +BOOLEAN +AfdAreTransportAddressesEqual ( + IN PTRANSPORT_ADDRESS EndpointAddress, + IN ULONG EndpointAddressLength, + IN PTRANSPORT_ADDRESS RequestAddress, + IN ULONG RequestAddressLength, + IN BOOLEAN HonorWildcardIpPortInEndpointAddress + ); + +NTSTATUS +AfdBeginAbort ( + IN PAFD_CONNECTION Connection + ); + +NTSTATUS +AfdBeginDisconnect ( + IN PAFD_ENDPOINT Endpoint, + IN PLARGE_INTEGER Timeout OPTIONAL, + OUT PIRP *DisconnectIrp OPTIONAL + ); + +NTSTATUS +AfdBind ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +ULONG +AfdCalcBufferArrayByteLengthRead( + IN LPWSABUF BufferArray, + IN ULONG BufferCount + ); + +ULONG +AfdCalcBufferArrayByteLengthWrite( + IN LPWSABUF BufferArray, + IN ULONG BufferCount + ); + +VOID +AfdCancelReceiveDatagram ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +AfdCancelTransmit ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +AfdCleanup ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdClose ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +VOID +AfdCompleteIrpList ( + IN PLIST_ENTRY IrpListHead, + IN PKSPIN_LOCK SpinLock, + IN NTSTATUS Status, + IN PAFD_IRP_CLEANUP_ROUTINE CleanupRoutine OPTIONAL + ); + +VOID +AfdCompleteClosePendedTransmit ( + IN PAFD_ENDPOINT Endpoint + ); + +NTSTATUS +AfdConnect ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdConnectEventHandler ( + IN PVOID TdiEventContext, + IN int RemoteAddressLength, + IN PVOID RemoteAddress, + IN int UserDataLength, + IN PVOID UserData, + IN int OptionsLength, + IN PVOID Options, + OUT CONNECTION_CONTEXT *ConnectionContext, + OUT PIRP *AcceptIrp + ); + +ULONG +AfdCopyBufferArrayToBuffer( + IN PVOID Destination, + IN ULONG DestinationLength, + IN LPWSABUF BufferArray, + IN ULONG BufferCount + ); + +ULONG +AfdCopyBufferToBufferArray( + IN LPWSABUF BufferArray, + IN ULONG Offset, + IN ULONG BufferCount, + IN PVOID Source, + IN ULONG SourceLength + ); + +NTSTATUS +AfdCreate ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +VOID +AfdDestroyMdlChain ( + IN PIRP Irp + ); + +NTSTATUS +AfdDisconnectEventHandler ( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN int DisconnectDataLength, + IN PVOID DisconnectData, + IN int DisconnectInformationLength, + IN PVOID DisconnectInformation, + IN ULONG DisconnectFlags + ); + +NTSTATUS +AfdDispatch ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +AfdEnumNetworkEvents ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdErrorEventHandler ( + IN PVOID TdiEventContext, + IN NTSTATUS Status + ); + +NTSTATUS +AfdEventSelect ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +BOOLEAN +AfdFastTransmitFile ( + IN struct _FILE_OBJECT *FileObject, + IN PVOID InputBuffer OPTIONAL, + IN ULONG InputBufferLength, + OUT PIO_STATUS_BLOCK IoStatus + ); + +VOID +AfdFreeConnectDataBuffers ( + IN PAFD_CONNECT_DATA_BUFFERS ConnectDataBuffers + ); + +VOID +AfdFreeQueuedConnections ( + IN PAFD_ENDPOINT Endpoint + ); + +NTSTATUS +AfdGetAddress ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdGetContext ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdGetContextLength ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdGetInformation ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +VOID +AfdIndicateEventSelectEvent ( + IN PAFD_ENDPOINT Endpoint, + IN ULONG PollEventBit, + IN NTSTATUS Status + ); + +VOID +AfdIndicatePollEvent ( + IN PAFD_ENDPOINT Endpoint, + IN ULONG PollEventBit, + IN NTSTATUS Status + ); + +VOID +AfdInitiateListenBacklogReplenish ( + IN PAFD_ENDPOINT Endpoint + ); + +BOOLEAN +AfdInitializeData ( + VOID + ); + +NTSTATUS +AfdIssueDeviceControl ( + IN HANDLE FileHandle OPTIONAL, + IN PFILE_OBJECT FileObject OPTIONAL, + IN PVOID IrpParameters, + IN ULONG IrpParametersLength, + IN PVOID MdlBuffer, + IN ULONG MdlBufferLength, + IN UCHAR MinorFunction + ); + + +VOID +AfdIncrementLockCount ( + VOID + ); + +VOID +AfdDecrementLockCount ( + VOID + ); + +VOID +AfdInsertNewEndpointInList ( + IN PAFD_ENDPOINT Endpoint + ); + +VOID +AfdRemoveEndpointFromList ( + IN PAFD_ENDPOINT Endpoint + ); + +VOID +AfdInterlockedRemoveEntryList ( + IN PLIST_ENTRY ListEntry, + IN PKSPIN_LOCK SpinLock + ); + +NTSTATUS +AfdOpenConnection ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdPartialDisconnect ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdPoll ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +PAFD_WORK_ITEM +AfdAllocateWorkItem( + VOID + ); + +VOID +AfdQueueWorkItem ( + IN PWORKER_THREAD_ROUTINE AfdWorkerRoutine, + IN PAFD_WORK_ITEM AfdWorkItem + ); + +VOID +AfdFreeWorkItem( + IN PAFD_WORK_ITEM AfdWorkItem + ); + +#if DBG +PVOID +NTAPI +AfdAllocateWorkItemPool( + IN POOL_TYPE PoolType, + IN ULONG NumberOfBytes, + IN ULONG Tag + ); + +VOID +NTAPI +AfdFreeWorkItemPool( + IN PVOID Block + ); +#endif + +NTSTATUS +AfdQueryHandles ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdQueryReceiveInformation ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdQueueUserApc ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdSetContext ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdSetEventHandler ( + IN PFILE_OBJECT FileObject, + IN ULONG EventType, + IN PVOID EventHandler, + IN PVOID EventContext + ); + +NTSTATUS +AfdSetInLineMode ( + IN PAFD_CONNECTION Connection, + IN BOOLEAN InLine + ); + +NTSTATUS +AfdReceive ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdBReceive ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp, + IN ULONG RecvFlags, + IN ULONG AfdFlags, + IN ULONG RecvLength + ); + +NTSTATUS +AfdReceiveDatagram ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp, + IN ULONG RecvFlags, + IN ULONG AfdFlags + ); + +VOID +AfdCleanupReceiveDatagramIrp( + IN PIRP Irp + ); + +NTSTATUS +AfdReceiveEventHandler ( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *BytesTaken, + IN PVOID Tsdu, + OUT PIRP *IoRequestPacket + ); + +NTSTATUS +AfdBReceiveEventHandler ( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *BytesTaken, + IN PVOID Tsdu, + OUT PIRP *IoRequestPacket + ); + +NTSTATUS +AfdReceiveDatagramEventHandler ( + IN PVOID TdiEventContext, + IN int SourceAddressLength, + IN PVOID SourceAddress, + IN int OptionsLength, + IN PVOID Options, + IN ULONG ReceiveDatagramFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *BytesTaken, + IN PVOID Tsdu, + OUT PIRP *IoRequestPacket + ); + +NTSTATUS +AfdReceiveExpeditedEventHandler ( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *BytesTaken, + IN PVOID Tsdu, + OUT PIRP *IoRequestPacket + ); + +NTSTATUS +AfdBReceiveExpeditedEventHandler ( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *BytesTaken, + IN PVOID Tsdu, + OUT PIRP *IoRequestPacket + ); + +NTSTATUS +AfdRestartBufferReceive ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +AfdRestartAbort ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +AfdSend ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdSendDatagram ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdSendPossibleEventHandler ( + IN PVOID TdiEventContext, + IN PVOID ConnectionContext, + IN ULONG BytesAvailable + ); + +NTSTATUS +AfdRestartBufferSend ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +AfdSetInformation ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +BOOLEAN +AfdShouldSendBlock ( + IN PAFD_ENDPOINT Endpoint, + IN PAFD_CONNECTION Connection, + IN ULONG SendLength + ); + +NTSTATUS +AfdStartListen ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdTransmitFile ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdMdlReadComplete( + IN PFILE_OBJECT FileObject, + IN PMDL MdlChain, + IN LONGLONG FileOffset, + IN ULONG Length + ); + +NTSTATUS +AfdWaitForListen ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdSetQos ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdGetQos ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdNoOperation ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdValidateGroup ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +AfdGetUnacceptedConnectData ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +#ifdef NT351 + +NTSTATUS +AfdReferenceEventObjectByHandle( + IN HANDLE Handle, + IN KPROCESSOR_MODE AccessMode, + OUT PVOID *Object + ); + +#else // !NT351 + +#define AfdReferenceEventObjectByHandle(Handle, AccessMode, Object) \ + ObReferenceObjectByHandle( \ + (Handle), \ + 0, \ + *(POBJECT_TYPE *)ExEventObjectType, \ + (AccessMode), \ + (Object), \ + NULL \ + ) + +#endif + +// +// Endpoint handling routines. +// + +NTSTATUS +AfdAllocateEndpoint ( + OUT PAFD_ENDPOINT * NewEndpoint, + IN PUNICODE_STRING TransportDeviceName, + IN LONG GroupID + ); + +VOID +AfdCloseEndpoint ( + IN PAFD_ENDPOINT Endpoint + ); + +#if REFERENCE_DEBUG + +VOID +AfdReferenceEndpoint ( + IN PAFD_ENDPOINT Endpoint, + IN PVOID Info1, + IN PVOID Info2 + ); + +VOID +AfdDereferenceEndpoint ( + IN PAFD_ENDPOINT Endpoint, + IN PVOID Info1, + IN PVOID Info2 + ); + +#define REFERENCE_ENDPOINT(_a) AfdReferenceEndpoint((_a),(PVOID)__FILE__,(PVOID)__LINE__) +#define REFERENCE_ENDPOINT2(_a,_b,_c) AfdReferenceEndpoint((_a),(_b),(_c)) + +#define DEREFERENCE_ENDPOINT(_a) AfdDereferenceEndpoint((_a),(PVOID)__FILE__,(PVOID)__LINE__) +#define DEREFERENCE_ENDPOINT2(_a,_b,_c) AfdDereferenceEndpoint((_a),(PVOID)__FILE__,(PVOID)__LINE__) + +#else + +VOID +AfdDereferenceEndpoint ( + IN PAFD_ENDPOINT Endpoint + ); + +#define REFERENCE_ENDPOINT(_a) InterlockedIncrement( &(_a)->ReferenceCount ) +#define REFERENCE_ENDPOINT2(_a,_b,_c) InterlockedIncrement( &(_a)->ReferenceCount ) + +#define DEREFERENCE_ENDPOINT(_a) AfdDereferenceEndpoint((_a)) +#define DEREFERENCE_ENDPOINT2(_a,_b,_c) AfdDereferenceEndpoint((_a)) + +#endif + +VOID +AfdRefreshEndpoint ( + IN PAFD_ENDPOINT Endpoint + ); + +// +// Connection handling routines. +// + +VOID +AfdAbortConnection ( + IN PAFD_CONNECTION Connection + ); + +NTSTATUS +AfdAddFreeConnection ( + IN PAFD_ENDPOINT Endpoint + ); + +PAFD_CONNECTION +AfdAllocateConnection ( + VOID + ); + +NTSTATUS +AfdCreateConnection ( + IN PUNICODE_STRING TransportDeviceName, + IN HANDLE AddressHandle OPTIONAL, + IN BOOLEAN TdiBufferring, + IN BOOLEAN InLine, + IN PEPROCESS ProcessToCharge, + OUT PAFD_CONNECTION *Connection + ); + +PAFD_CONNECTION +AfdGetFreeConnection ( + IN PAFD_ENDPOINT Endpoint + ); + +PAFD_CONNECTION +AfdGetReturnedConnection ( + IN PAFD_ENDPOINT Endpoint, + IN ULONG Sequence + ); + +PAFD_CONNECTION +AfdGetUnacceptedConnection ( + IN PAFD_ENDPOINT Endpoint + ); + +#if REFERENCE_DEBUG + +VOID +AfdReferenceConnection ( + IN PAFD_CONNECTION Connection, + IN PVOID Info1, + IN PVOID Info2 + ); + +VOID +AfdDereferenceConnection ( + IN PAFD_CONNECTION Connection, + IN PVOID Info1, + IN PVOID Info2 + ); + +#define REFERENCE_CONNECTION(_a) AfdReferenceConnection((_a), (PVOID)__FILE__, (PVOID)__LINE__) +#define REFERENCE_CONNECTION2(_a,_b,_c) AfdReferenceConnection((_a),(_b),(_c)) + +#define DEREFERENCE_CONNECTION(_a) AfdDereferenceConnection((_a), (PVOID)__FILE__, (PVOID)__LINE__ ) +#define DEREFERENCE_CONNECTION2(_a,_b,_c) AfdDereferenceConnection((_a),(_b),(_c)) + +VOID +AfdUpdateConnectionTrack ( + IN PAFD_CONNECTION Connection, + IN LONG NewReferenceCount, + IN PVOID Info1, + IN PVOID Info2, + IN ULONG Action + ); + +#define UPDATE_CONN(_c,_a) \ + if( (_c) != NULL ) { \ + AfdUpdateConnectionTrack( \ + (_c), \ + (_c)->ReferenceCount, \ + __FILE__, \ + (PVOID)__LINE__, \ + (ULONG)(_a) \ + ); \ + } else + +#else + +VOID +AfdDereferenceConnection ( + IN PAFD_CONNECTION Connection + ); + +#define REFERENCE_CONNECTION(_a) InterlockedIncrement( &(_a)->ReferenceCount ) +#define REFERENCE_CONNECTION2(_a,_b,_c) InterlockedIncrement( &(_a)->ReferenceCount ) + +#define DEREFERENCE_CONNECTION(_a) AfdDereferenceConnection((_a)) +#define DEREFERENCE_CONNECTION2(_a,_b,_c) AfdDereferenceConnection((_a)) + +#define UPDATE_CONN(_c,_a) + +#endif + +VOID +AfdAddConnectedReference ( + IN PAFD_CONNECTION Connection + ); + +VOID +AfdDeleteConnectedReference ( + IN PAFD_CONNECTION Connection, + IN BOOLEAN EndpointLockHeld + ); + + +// +// Routines to handle fast IO. +// + +BOOLEAN +AfdFastIoRead ( + IN struct _FILE_OBJECT *FileObject, + IN PLARGE_INTEGER FileOffset, + IN ULONG Length, + IN BOOLEAN Wait, + IN ULONG LockKey, + OUT PVOID Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN struct _DEVICE_OBJECT *DeviceObject + ); + +BOOLEAN +AfdFastIoWrite ( + IN struct _FILE_OBJECT *FileObject, + IN PLARGE_INTEGER FileOffset, + IN ULONG Length, + IN BOOLEAN Wait, + IN ULONG LockKey, + IN PVOID Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN struct _DEVICE_OBJECT *DeviceObject + ); + +BOOLEAN +AfdFastIoDeviceControl ( + IN struct _FILE_OBJECT *FileObject, + IN BOOLEAN Wait, + IN PVOID InputBuffer OPTIONAL, + IN ULONG InputBufferLength, + OUT PVOID OutputBuffer OPTIONAL, + IN ULONG OutputBufferLength, + IN ULONG IoControlCode, + OUT PIO_STATUS_BLOCK IoStatus, + IN struct _DEVICE_OBJECT *DeviceObject + ); + +// +// Routines to handle getting and setting connect data. +// + +NTSTATUS +AfdGetConnectData ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp, + IN ULONG Code + ); + +NTSTATUS +AfdSetConnectData ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp, + IN ULONG Code + ); + +NTSTATUS +AfdSaveReceivedConnectData ( + IN OUT PAFD_CONNECT_DATA_BUFFERS * DataBuffers, + IN ULONG IoControlCode, + IN PVOID Buffer, + IN ULONG BufferLength + ); + +// +// Buffer management routines. +// + +PVOID +AfdAllocateBuffer ( + IN POOL_TYPE PoolType, + IN ULONG NumberOfBytes, + IN ULONG Tag + ); + +CLONG +AfdCalculateBufferSize ( + IN CLONG BufferDataSize, + IN CLONG AddressSize + ); + +PAFD_BUFFER +AfdGetBuffer ( + IN CLONG BufferDataSize, + IN CLONG AddressSize + ); + +PAFD_BUFFER +AfdGetBufferChain ( + IN CLONG BufferDataSize + ); + +VOID +AfdReturnBuffer ( + IN PAFD_BUFFER AfdBuffer + ); + +VOID +AfdReturnBufferChain ( + IN PAFD_BUFFER AfdBuffer + ); + +#if DBG +VOID +NTAPI +AfdFreeBufferPool( + IN PVOID Block + ); +#endif + +// +// Group ID managment routines. +// + +BOOLEAN +AfdInitializeGroup( + VOID + ); + +VOID +AfdTerminateGroup( + VOID + ); + +BOOLEAN +AfdReferenceGroup( + IN LONG Group, + OUT PAFD_GROUP_TYPE GroupType + ); + +BOOLEAN +AfdDereferenceGroup( + IN LONG Group + ); + +BOOLEAN +AfdGetGroup( + IN OUT PLONG Group, + OUT PAFD_GROUP_TYPE GroupType + ); + +#define IS_DATA_ON_CONNECTION_B(conn) \ + ((conn)->Common.Bufferring.ReceiveBytesIndicated.QuadPart > \ + ((conn)->Common.Bufferring.ReceiveBytesTaken.QuadPart + \ + (conn)->Common.Bufferring.ReceiveBytesOutstanding.QuadPart )\ + || \ + (conn)->VcZeroByteReceiveIndicated) + +#define IS_EXPEDITED_DATA_ON_CONNECTION_B(conn) \ + ((conn)->Common.Bufferring.ReceiveExpeditedBytesIndicated.QuadPart > \ + ((conn)->Common.Bufferring.ReceiveExpeditedBytesTaken.QuadPart + \ + (conn)->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart) ) + +#define IS_DATA_ON_CONNECTION_NB(conn) \ + ( (conn)->Common.NonBufferring.BufferredReceiveCount != 0 ) + +#define IS_EXPEDITED_DATA_ON_CONNECTION_NB(conn) \ + ( (conn)->Common.NonBufferring.BufferredExpeditedCount != 0 ) + +#define IS_DATA_ON_CONNECTION(conn) \ + ( (conn)->Endpoint->TdiBufferring ? \ + IS_DATA_ON_CONNECTION_B(conn) : \ + IS_DATA_ON_CONNECTION_NB(conn) ) + +#define IS_EXPEDITED_DATA_ON_CONNECTION(conn) \ + ( (conn)->Endpoint->TdiBufferring ? \ + IS_EXPEDITED_DATA_ON_CONNECTION_B(conn) : \ + IS_EXPEDITED_DATA_ON_CONNECTION_NB(conn) ) + +#define ARE_DATAGRAMS_ON_ENDPOINT(endp) \ + ( (endp)->BufferredDatagramCount != 0 ) + +// +// Debug statistic manipulators. On checked builds these macros update +// their corresponding statistic counter. On retail builds, these macros +// evaluate to nothing. +// + +#if AFD_KEEP_STATS + +#define AfdRecordPoolQuotaCharged( b ) \ + ExInterlockedAddLargeStatistic( \ + &AfdQuotaStats.Charged, \ + (b) \ + ) + +#define AfdRecordPoolQuotaReturned( b ) \ + ExInterlockedAddLargeStatistic( \ + &AfdQuotaStats.Returned, \ + (b) \ + ) + +#define AfdRecordAddrOpened() InterlockedIncrement( &AfdHandleStats.AddrOpened ) +#define AfdRecordAddrClosed() InterlockedIncrement( &AfdHandleStats.AddrClosed ) +#define AfdRecordAddrRef() InterlockedIncrement( &AfdHandleStats.AddrRef ) +#define AfdRecordAddrDeref() InterlockedIncrement( &AfdHandleStats.AddrDeref ) +#define AfdRecordConnOpened() InterlockedIncrement( &AfdHandleStats.ConnOpened ) +#define AfdRecordConnClosed() InterlockedIncrement( &AfdHandleStats.ConnClosed ) +#define AfdRecordConnRef() InterlockedIncrement( &AfdHandleStats.ConnRef ) +#define AfdRecordConnDeref() InterlockedIncrement( &AfdHandleStats.ConnDeref ) +#define AfdRecordFileRef() InterlockedIncrement( &AfdHandleStats.FileRef ) +#define AfdRecordFileDeref() InterlockedIncrement( &AfdHandleStats.FileDeref ) + +#define AfdRecordAfdWorkItemsQueued() InterlockedIncrement( &AfdQueueStats.AfdWorkItemsQueued ) +#define AfdRecordExWorkItemsQueued() InterlockedIncrement( &AfdQueueStats.ExWorkItemsQueued ) +#define AfdRecordWorkerEnter() InterlockedIncrement( &AfdQueueStats.WorkerEnter ) +#define AfdRecordWorkerLeave() InterlockedIncrement( &AfdQueueStats.WorkerLeave ) +#define AfdRecordAfdWorkItemsProcessed() InterlockedIncrement( &AfdQueueStats.AfdWorkItemsProcessed ) + +#define AfdRecordAfdWorkerThread(t) \ + if( 1 ) { \ + ASSERT( AfdQueueStats.AfdWorkerThread == NULL || \ + (t) == NULL ); \ + AfdQueueStats.AfdWorkerThread = (t); \ + } else + +#define AfdRecordConnectedReferencesAdded() InterlockedIncrement( &AfdConnectionStats.ConnectedReferencesAdded ) +#define AfdRecordConnectedReferencesDeleted() InterlockedIncrement( &AfdConnectionStats.ConnectedReferencesDeleted ) +#define AfdRecordGracefulDisconnectsInitiated() InterlockedIncrement( &AfdConnectionStats.GracefulDisconnectsInitiated ) +#define AfdRecordGracefulDisconnectsCompleted() InterlockedIncrement( &AfdConnectionStats.GracefulDisconnectsCompleted ) +#define AfdRecordGracefulDisconnectIndications() InterlockedIncrement( &AfdConnectionStats.GracefulDisconnectIndications ) +#define AfdRecordAbortiveDisconnectsInitiated() InterlockedIncrement( &AfdConnectionStats.AbortiveDisconnectsInitiated ) +#define AfdRecordAbortiveDisconnectsCompleted() InterlockedIncrement( &AfdConnectionStats.AbortiveDisconnectsCompleted ) +#define AfdRecordAbortiveDisconnectIndications() InterlockedIncrement( &AfdConnectionStats.AbortiveDisconnectIndications ) + +#else // !AFD_KEEP_STATS + +#define AfdRecordPoolQuotaCharged(b) +#define AfdRecordPoolQuotaReturned(b) + +#define AfdRecordAddrOpened() +#define AfdRecordAddrClosed() +#define AfdRecordAddrRef() +#define AfdRecordAddrDeref() +#define AfdRecordConnOpened() +#define AfdRecordConnClosed() +#define AfdRecordConnRef() +#define AfdRecordConnDeref() +#define AfdRecordFileRef() +#define AfdRecordFileDeref() + +#define AfdRecordAfdWorkItemsQueued() +#define AfdRecordExWorkItemsQueued() +#define AfdRecordWorkerEnter() +#define AfdRecordWorkerLeave() +#define AfdRecordAfdWorkItemsProcessed() +#define AfdRecordAfdWorkerThread(t) + +#define AfdRecordConnectedReferencesAdded() +#define AfdRecordConnectedReferencesDeleted() +#define AfdRecordGracefulDisconnectsInitiated() +#define AfdRecordGracefulDisconnectsCompleted() +#define AfdRecordGracefulDisconnectIndications() +#define AfdRecordAbortiveDisconnectsInitiated() +#define AfdRecordAbortiveDisconnectsCompleted() +#define AfdRecordAbortiveDisconnectIndications() + +#endif // if AFD_KEEP_STATS + +#endif // ndef _AFDPROCS_ + diff --git a/private/ntos/afd/afdstr.h b/private/ntos/afd/afdstr.h new file mode 100644 index 000000000..813270194 --- /dev/null +++ b/private/ntos/afd/afdstr.h @@ -0,0 +1,591 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + afdstr.h + +Abstract: + + This module contains typedefs for structures used by AFD. + +Author: + + David Treadwell (davidtr) 21-Feb-1992 + +Revision History: + +--*/ + +#ifndef _AFDSTR_ +#define _AFDSTR_ + +#ifndef REFERENCE_DEBUG +#if DBG +#define REFERENCE_DEBUG 1 +#define GLOBAL_REFERENCE_DEBUG 0 +#else +#define REFERENCE_DEBUG 0 +#define GLOBAL_REFERENCE_DEBUG 0 +#endif +#endif + +#if REFERENCE_DEBUG + +#define MAX_REFERENCE 64 + +typedef struct _AFD_REFERENCE_DEBUG { + PVOID Info1; + PVOID Info2; + ULONG Action; + ULONG NewCount; +} AFD_REFERENCE_DEBUG, *PAFD_REFERENCE_DEBUG; + +#if GLOBAL_REFERENCE_DEBUG +#define MAX_GLOBAL_REFERENCE 4096 + +typedef struct _AFD_GLOBAL_REFERENCE_DEBUG { + PVOID Info1; + PVOID Info2; + ULONG Action; + ULONG NewCount; + PVOID Connection; + LARGE_INTEGER TickCounter; + PVOID Dummy; +} AFD_GLOBAL_REFERENCE_DEBUG, *PAFD_GLOBAL_REFERENCE_DEBUG; +#endif + +#endif + +// +// A structure for maintaining work queue information in AFD. +// + +typedef struct _AFD_WORK_ITEM { + LIST_ENTRY WorkItemListEntry; + PWORKER_THREAD_ROUTINE AfdWorkerRoutine; + PVOID Context; +} AFD_WORK_ITEM, *PAFD_WORK_ITEM; + +// +// Structures for holding connect data pointers and lengths. This is +// kept separate from the normal structures to save space in those +// structures for transports that do not support and applications +// which do not use connect data. +// + +typedef struct _AFD_CONNECT_DATA_INFO { + PVOID Buffer; + ULONG BufferLength; +} AFD_CONNECT_DATA_INFO, *PAFD_CONNECT_DATA_INFO; + +typedef struct _AFD_CONNECT_DATA_BUFFERS { + AFD_CONNECT_DATA_INFO SendConnectData; + AFD_CONNECT_DATA_INFO SendConnectOptions; + AFD_CONNECT_DATA_INFO ReceiveConnectData; + AFD_CONNECT_DATA_INFO ReceiveConnectOptions; + AFD_CONNECT_DATA_INFO SendDisconnectData; + AFD_CONNECT_DATA_INFO SendDisconnectOptions; + AFD_CONNECT_DATA_INFO ReceiveDisconnectData; + AFD_CONNECT_DATA_INFO ReceiveDisconnectOptions; + TDI_CONNECTION_INFORMATION TdiConnectionInfo; +} AFD_CONNECT_DATA_BUFFERS, *PAFD_CONNECT_DATA_BUFFERS; + +// +// Structure used for holding disconnect context information. +// + +struct _AFD_ENDPOINT; +struct _AFD_CONNECTION; + +typedef struct _AFD_DISCONNECT_CONTEXT { + LIST_ENTRY DisconnectListEntry; + struct _AFD_ENDPOINT *Endpoint; + PTDI_CONNECTION_INFORMATION TdiConnectionInformation; + LARGE_INTEGER Timeout; + struct _AFD_CONNECTION *Connection; + PIRP Irp; +} AFD_DISCONNECT_CONTEXT, *PAFD_DISCONNECT_CONTEXT; + +// +// Endpoint and connection structures and related informaion. +// + +#define AfdBlockTypeEndpoint 0xAFD0 +#define AfdBlockTypeVcConnecting 0xAFD1 +#define AfdBlockTypeVcListening 0xAFD2 +#define AfdBlockTypeDatagram 0xAFD3 +#define AfdBlockTypeConnection 0xAFD4 +#define AfdBlockTypeHelper 0xAFD5 + +#define IS_AFD_ENDPOINT_TYPE( endpoint ) \ + ( (endpoint)->Type == AfdBlockTypeEndpoint || \ + (endpoint)->Type == AfdBlockTypeVcConnecting || \ + (endpoint)->Type == AfdBlockTypeVcListening || \ + (endpoint)->Type == AfdBlockTypeDatagram || \ + (endpoint)->Type == AfdBlockTypeHelper ) + +#define AfdConnectionStateFree 0 +#define AfdConnectionStateUnaccepted 1 +#define AfdConnectionStateReturned 2 +#define AfdConnectionStateConnected 3 +#define AfdConnectionStateClosing 4 + +typedef struct _AFD_CONNECTION { + + USHORT Type; + USHORT State; + LONG ReferenceCount; + + LIST_ENTRY ListEntry; + HANDLE Handle; + PFILE_OBJECT FileObject; + LONGLONG ConnectTime; + + union { + + struct { + LARGE_INTEGER ReceiveBytesIndicated; + LARGE_INTEGER ReceiveBytesTaken; + LARGE_INTEGER ReceiveBytesOutstanding; + + LARGE_INTEGER ReceiveExpeditedBytesIndicated; + LARGE_INTEGER ReceiveExpeditedBytesTaken; + LARGE_INTEGER ReceiveExpeditedBytesOutstanding; + BOOLEAN NonBlockingSendPossible; + BOOLEAN ZeroByteReceiveIndicated; + } Bufferring; + + struct { + LIST_ENTRY ReceiveIrpListHead; + LIST_ENTRY ReceiveBufferListHead; + LIST_ENTRY SendIrpListHead; + + CLONG BufferredReceiveBytes; + CLONG BufferredExpeditedBytes; + CSHORT BufferredReceiveCount; + CSHORT BufferredExpeditedCount; + + CLONG ReceiveBytesInTransport; + CLONG BufferredSendBytes; + CSHORT ReceiveCountInTransport; + CSHORT BufferredSendCount; + + PIRP DisconnectIrp; + } NonBufferring; + + } Common; + + struct _AFD_ENDPOINT *Endpoint; + + CLONG MaxBufferredReceiveBytes; + CLONG MaxBufferredSendBytes; + CSHORT MaxBufferredReceiveCount; + CSHORT MaxBufferredSendCount; + + PAFD_CONNECT_DATA_BUFFERS ConnectDataBuffers; + PEPROCESS OwningProcess; + PDEVICE_OBJECT DeviceObject; + PTRANSPORT_ADDRESS RemoteAddress; + ULONG RemoteAddressLength; + BOOLEAN DisconnectIndicated; + BOOLEAN AbortIndicated; + BOOLEAN TdiBufferring; + BOOLEAN ConnectedReferenceAdded; + BOOLEAN SpecialCondition; + BOOLEAN CleanupBegun; + BOOLEAN ClosePendedTransmit; + + AFD_DISCONNECT_CONTEXT DisconnectContext; + AFD_WORK_ITEM WorkItem; + +#if ENABLE_ABORT_TIMER_HACK + struct _AFD_ABORT_TIMER_INFO * AbortTimerInfo; +#endif // ENABLE_ABORT_TIMER_HACK + +#if REFERENCE_DEBUG + LONG CurrentReferenceSlot; + ULONG Dummy1; + ULONG Dummy2; + AFD_REFERENCE_DEBUG ReferenceDebug[MAX_REFERENCE]; +#endif + +} AFD_CONNECTION, *PAFD_CONNECTION; + +// +// Some macros that make code more readable. +// + +#define VcNonBlockingSendPossible Common.Bufferring.NonBlockingSendPossible +#define VcZeroByteReceiveIndicated Common.Bufferring.ZeroByteReceiveIndicated + +#define VcReceiveIrpListHead Common.NonBufferring.ReceiveIrpListHead +#define VcReceiveBufferListHead Common.NonBufferring.ReceiveBufferListHead +#define VcSendIrpListHead Common.NonBufferring.SendIrpListHead + +#define VcBufferredReceiveBytes Common.NonBufferring.BufferredReceiveBytes +#define VcBufferredExpeditedBytes Common.NonBufferring.BufferredExpeditedBytes +#define VcBufferredReceiveCount Common.NonBufferring.BufferredReceiveCount +#define VcBufferredExpeditedCount Common.NonBufferring.BufferredExpeditedCount + +#define VcReceiveBytesInTransport Common.NonBufferring.ReceiveBytesInTransport +#define VcReceiveCountInTransport Common.NonBufferring.ReceiveCountInTransport + +#define VcBufferredSendBytes Common.NonBufferring.BufferredSendBytes +#define VcBufferredSendCount Common.NonBufferring.BufferredSendCount + +#define VcDisconnectIrp Common.NonBufferring.DisconnectIrp + +// +// Information stored about each transport device name for which there +// is an open endpoint. +// + +typedef struct _AFD_TRANSPORT_INFO { + LIST_ENTRY TransportInfoListEntry; + UNICODE_STRING TransportDeviceName; + TDI_PROVIDER_INFO ProviderInfo; + //WCHAR TransportDeviceNameStructure; +} AFD_TRANSPORT_INFO, *PAFD_TRANSPORT_INFO; + +// +// Endpoint state definitions. +// + +#define AfdEndpointStateOpen 0 +#define AfdEndpointStateBound 1 +#define AfdEndpointStateListening 2 +#define AfdEndpointStateConnected 3 +#define AfdEndpointStateCleanup 4 +#define AfdEndpointStateClosing 5 +#define AfdEndpointStateTransmitClosing 6 +#define AfdEndpointStateInvalid 7 + +typedef struct _AFD_ENDPOINT { + + USHORT Type; + UCHAR State; + + LONG ReferenceCount; + + BOOLEAN NonBlocking; + BOOLEAN InLine; + BOOLEAN TdiBufferring; + + LIST_ENTRY GlobalEndpointListEntry; + LIST_ENTRY ConstrainedEndpointListEntry; + AFD_ENDPOINT_TYPE EndpointType; + HANDLE AddressHandle; + PFILE_OBJECT AddressFileObject; + + // + // Use a union to overlap the fields that are exclusive to datagram + // connecting, or listening endpoints. Since many fields are + // relevant to only one type of socket, it makes no sense to + // maintain the fields for all sockets--instead, save some nonpaged + // pool by combining them. + // + + union { + + // + // Information for circuit-based connected endpoints. + // + + struct { + PAFD_CONNECTION Connection; + NTSTATUS ConnectStatus; + struct _AFD_ENDPOINT *ListenEndpoint; + } VcConnecting; + + // + // Information for circuit-based listening endpoints. + // + + struct { + LIST_ENTRY FreeConnectionListHead; + LIST_ENTRY UnacceptedConnectionListHead; + LIST_ENTRY ReturnedConnectionListHead; + LIST_ENTRY ListeningIrpListHead; + LONG FailedConnectionAdds; + LONG FreeConnectionCount; + LONG TdiAcceptPendingCount; + BOOLEAN EnableDynamicBacklog; + } VcListening; + + // + // Information for datagram endpoints. Note that different + // information is kept depending on whether the underlying + // transport buffers internally. + // + + struct { + PTRANSPORT_ADDRESS RemoteAddress; + ULONG RemoteAddressLength; + + LIST_ENTRY ReceiveIrpListHead; + LIST_ENTRY PeekIrpListHead; + LIST_ENTRY ReceiveBufferListHead; + CLONG BufferredDatagramBytes; + CSHORT BufferredDatagramCount; + + CLONG MaxBufferredReceiveBytes; + CLONG MaxBufferredSendBytes; + CSHORT MaxBufferredReceiveCount; + CSHORT MaxBufferredSendCount; + + BOOLEAN CircularQueueing; + } Datagram; + + } Common; + + CLONG DisconnectMode; + CLONG OutstandingIrpCount; + PTRANSPORT_ADDRESS LocalAddress; + ULONG LocalAddressLength; + PVOID Context; + CLONG ContextLength; + KSPIN_LOCK SpinLock; + PEPROCESS OwningProcess; + PAFD_CONNECT_DATA_BUFFERS ConnectDataBuffers; + PIRP TransmitIrp; + struct _AFD_TRANSMIT_FILE_INFO_INTERNAL * TransmitInfo; + PDEVICE_OBJECT AddressDeviceObject; + PAFD_TRANSPORT_INFO TransportInfo; + BOOLEAN ConnectOutstanding; + BOOLEAN SendDisconnected; + BOOLEAN EndpointCleanedUp; + BOOLEAN TdiMessageMode; + BOOLEAN PollCalled; + + AFD_WORK_ITEM WorkItem; + + // + // EventSelect info. + // + + PKEVENT EventObject; + ULONG EventsEnabled; + ULONG EventsDisabled; + ULONG EventsActive; + NTSTATUS EventStatus[AFD_NUM_POLL_EVENTS]; + + // + // Socket grouping. + // + + LONG GroupID; + AFD_GROUP_TYPE GroupType; + + // + // Debug stuff. + // + +#if REFERENCE_DEBUG + PAFD_REFERENCE_DEBUG ReferenceDebug; + LONG CurrentReferenceSlot; +#endif + +#if DBG + LIST_ENTRY OutstandingIrpListHead; + LONG ObReferenceBias; +#endif + +} AFD_ENDPOINT, *PAFD_ENDPOINT; + +// +// A couple of useful manifests that make code more readable. +// + +#define ReceiveDatagramIrpListHead Common.Datagram.ReceiveIrpListHead +#define PeekDatagramIrpListHead Common.Datagram.PeekIrpListHead +#define ReceiveDatagramBufferListHead Common.Datagram.ReceiveBufferListHead +#define BufferredDatagramCount Common.Datagram.BufferredDatagramCount +#define BufferredDatagramBytes Common.Datagram.BufferredDatagramBytes + +#define AFD_CONNECTION_FROM_ENDPOINT( endpoint ) \ + ( (endpoint)->Type == AfdBlockTypeVcConnecting ? \ + (endpoint)->Common.VcConnecting.Connection : NULL ) + +// +// A structure which describes buffers used by AFD to perform bufferring +// for TDI providers which do not perform internal bufferring. +// + +typedef struct _AFD_BUFFER { + union { + SINGLE_LIST_ENTRY SList; // for buffer lookaside lists + LIST_ENTRY BufferListEntry;// to place these structures on lists + }; + PLIST_ENTRY BufferListHead; // the global list this buffer belongs to + struct _AFD_BUFFER *NextBuffer;// next buffer in chain + PVOID Buffer; // a pointer to the actual data buffer + CLONG BufferLength; // amount of space allocated for the buffer + CLONG DataLength; // actual data in the buffer + CLONG DataOffset; // offset in buffer to start of unread data + PIRP Irp; // pointer to the IRP associated w/the buffer + PMDL Mdl; // pointer to an MDL describing the buffer + PVOID Context; // stores context info + PVOID SourceAddress; // pointer to address of datagram sender + CLONG SourceAddressLength; // length of datagram sender's address + PFILE_OBJECT FileObject; // for fast-path TransmitFile + LONGLONG FileOffset; // for fast-path TransmitFile + ULONG ReadLength; // for fast-path TransmitFile + USHORT AllocatedAddressLength; // length allocated for address + BOOLEAN ExpeditedData; // TRUE if the buffer contains expedited data + BOOLEAN PartialMessage; // TRUE if this is a partial message + TDI_CONNECTION_INFORMATION TdiInputInfo; // holds info for TDI requests + TDI_CONNECTION_INFORMATION TdiOutputInfo; // holds info for TDI requests +#if DBG + CLONG TotalChainLength; + LIST_ENTRY DebugListEntry; + PVOID Caller; + PVOID CallersCaller; +#endif + // IRP Irp; // the IRP follows this structure + // MDL Mdl; // the MDL follows the IRP + // UCHAR Address[]; // address of datagram sender + // UCHAR Buffer[BufferLength]; // the actual data buffer is last +} AFD_BUFFER, *PAFD_BUFFER; + +// +// Macros for making it easier to deal with the debug-only TotalChainLength +// AFD_BUFFER field. +// + +#if DBG +#define SET_CHAIN_LENGTH(b,l) ((b)->TotalChainLength = (l)) +#define RESET_CHAIN_LENGTH(b) ((b)->TotalChainLength = (b)->BufferLength) +#else +#define SET_CHAIN_LENGTH(b,l) +#define RESET_CHAIN_LENGTH(b) +#endif + +// +// Internal information for the transmit file IOCTL. Note that +// this must be the same size as AFD_TRANSMIT_FILE_INFO in afd.h!!!! +// + +typedef struct _AFD_TRANSMIT_IRP_INFO { + PIRP Irp; + PAFD_BUFFER AfdBuffer; + ULONG Length; +} AFD_TRANSMIT_IRP_INFO, *PAFD_TRANSMIT_IRP_INFO; + +typedef struct _AFD_TRANSMIT_FILE_INFO_INTERNAL { + LONGLONG Offset; + LONGLONG FileWriteLength; + ULONG SendPacketLength; + HANDLE FileHandle; + PVOID Head; + ULONG HeadLength; + PVOID Tail; + ULONG TailLength; + ULONG Flags; + PVOID _Dummy; + LONGLONG TotalBytesToSend; + LONGLONG BytesRead; + LONGLONG BytesSent; + PFILE_OBJECT FileObject; + PDEVICE_OBJECT DeviceObject; + PFILE_OBJECT TdiFileObject; + PDEVICE_OBJECT TdiDeviceObject; + PIRP TransmitIrp; + PAFD_ENDPOINT Endpoint; + PMDL FileMdl; + PMDL HeadMdl; + PMDL TailMdl; + PMDL FirstFileMdlAfterHead; + PMDL LastFileMdlBeforeTail; + PIRP IrpUsedToSendTail; + ULONG FileMdlLength; + BOOLEAN ReadPending; + BOOLEAN CompletionPending; + BOOLEAN NeedToSendHead; + BOOLEAN Queued; + + AFD_TRANSMIT_IRP_INFO Read; + AFD_TRANSMIT_IRP_INFO Send1; + AFD_TRANSMIT_IRP_INFO Send2; + + WORK_QUEUE_ITEM WorkQueueItem; + +#if DBG + BOOLEAN Completed; + ULONG ReadPendingLastSetTrueLine; + ULONG ReadPendingLastSetFalseLine; + PVOID Debug1; + PVOID Debug2; +#endif + +} AFD_TRANSMIT_FILE_INFO_INTERNAL, *PAFD_TRANSMIT_FILE_INFO_INTERNAL; + +// +// Pointer to an IRP cleanup routine. This is used as a parameter to +// AfdCompleteIrpList(). +// + +typedef +VOID +(NTAPI * PAFD_IRP_CLEANUP_ROUTINE)( + IN PIRP Irp + ); + +// +// Debug statistics. +// + +typedef struct _AFD_QUOTA_STATS { + LARGE_INTEGER Charged; + LARGE_INTEGER Returned; +} AFD_QUOTA_STATS; + +typedef struct _AFD_HANDLE_STATS { + LONG AddrOpened; + LONG AddrClosed; + LONG AddrRef; + LONG AddrDeref; + LONG ConnOpened; + LONG ConnClosed; + LONG ConnRef; + LONG ConnDeref; + LONG FileRef; + LONG FileDeref; +} AFD_HANDLE_STATS; + +typedef struct _AFD_QUEUE_STATS { + LONG AfdWorkItemsQueued; + LONG ExWorkItemsQueued; + LONG WorkerEnter; + LONG WorkerLeave; + LONG AfdWorkItemsProcessed; + PETHREAD AfdWorkerThread; +} AFD_QUEUE_STATS; + +typedef struct _AFD_CONNECTION_STATS { + LONG ConnectedReferencesAdded; + LONG ConnectedReferencesDeleted; + LONG GracefulDisconnectsInitiated; + LONG GracefulDisconnectsCompleted; + LONG GracefulDisconnectIndications; + LONG AbortiveDisconnectsInitiated; + LONG AbortiveDisconnectsCompleted; + LONG AbortiveDisconnectIndications; +} AFD_CONNECTION_STATS; + +// +// Buffer for lookaside list descriptors. Lookaside list descriptors +// cannot be statically allocated, as they need to ALWAYS be nonpageable, +// even when the entire driver is paged out. +// + +typedef struct _AFD_LOOKASIDE_LISTS { + NPAGED_LOOKASIDE_LIST WorkQueueList; + NPAGED_LOOKASIDE_LIST LargeBufferList; + NPAGED_LOOKASIDE_LIST MediumBufferList; + NPAGED_LOOKASIDE_LIST SmallBufferList; +} AFD_LOOKASIDE_LISTS, *PAFD_LOOKASIDE_LISTS; + +#endif // ndef _AFDSTR_ + diff --git a/private/ntos/afd/bind.c b/private/ntos/afd/bind.c new file mode 100644 index 000000000..ac799bfde --- /dev/null +++ b/private/ntos/afd/bind.c @@ -0,0 +1,828 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + bind.c + +Abstract: + + Contains AfdBind for binding an endpoint to a transport address. + +Author: + + David Treadwell (davidtr) 25-Feb-1992 + +Revision History: + +--*/ + +#include "afdp.h" + +NTSTATUS +AfdRestartGetAddress ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, AfdBind ) +#pragma alloc_text( PAGE, AfdGetAddress ) +#pragma alloc_text( PAGEAFD, AfdAreTransportAddressesEqual ) +#pragma alloc_text( PAGEAFD, AfdRestartGetAddress ) +#endif + + +NTSTATUS +AfdBind ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Handles the IOCTL_AFD_BIND IOCTL. + +Arguments: + + Irp - Pointer to I/O request packet. + + IrpSp - pointer to the IO stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + NTSTATUS status; + OBJECT_ATTRIBUTES objectAttributes; + IO_STATUS_BLOCK iosb; + + PTRANSPORT_ADDRESS transportAddress; + PTRANSPORT_ADDRESS requestedAddress; + ULONG requestedAddressLength; + PAFD_ENDPOINT endpoint; + + PFILE_FULL_EA_INFORMATION ea; + ULONG eaBufferLength; + + PAGED_CODE( ); + + // + // Set up local pointers. + // + + requestedAddress = Irp->AssociatedIrp.SystemBuffer; + requestedAddressLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + // + // Bomb off if this is a helper endpoint. + // + + if ( endpoint->Type == AfdBlockTypeHelper ) { + return STATUS_INVALID_PARAMETER; + } + + // + // If the client wants a unique address, make sure that there are no + // other sockets with this address. + + ExAcquireResourceExclusive( AfdResource, TRUE ); + + if ( IrpSp->Parameters.DeviceIoControl.OutputBufferLength != 0 ) { + + PLIST_ENTRY listEntry; + + // + // Walk the global list of endpoints, + // and compare this address againat the address on each endpoint. + // + + for ( listEntry = AfdEndpointListHead.Flink; + listEntry != &AfdEndpointListHead; + listEntry = listEntry->Flink ) { + + PAFD_ENDPOINT compareEndpoint; + + compareEndpoint = CONTAINING_RECORD( + listEntry, + AFD_ENDPOINT, + GlobalEndpointListEntry + ); + + ASSERT( IS_AFD_ENDPOINT_TYPE( compareEndpoint ) ); + + // + // Check whether the endpoint has a local address, whether + // the endpoint has been disconnected, and whether the + // endpoint is in the process of closing. If any of these + // is true, don't compare addresses with this endpoint. + // + + if ( compareEndpoint->LocalAddress != NULL && + ( (compareEndpoint->DisconnectMode & + (AFD_PARTIAL_DISCONNECT_SEND | + AFD_ABORTIVE_DISCONNECT) ) == 0 ) && + (compareEndpoint->State != AfdEndpointStateClosing) ) { + + // + // Compare the bits in the endpoint's address and the + // address we're attempting to bind to. Note that we + // also compare the transport device names on the + // endpoints, as it is legal to bind to the same address + // on different transports (e.g. bind to same port in + // TCP and UDP). We can just compare the transport + // device name pointers because unique names are stored + // globally. + // + + if ( compareEndpoint->LocalAddressLength == + IrpSp->Parameters.DeviceIoControl.InputBufferLength + + && + + AfdAreTransportAddressesEqual( + compareEndpoint->LocalAddress, + compareEndpoint->LocalAddressLength, + requestedAddress, + requestedAddressLength, + FALSE + ) + + && + + endpoint->TransportInfo == + compareEndpoint->TransportInfo ) { + + // + // The addresses are equal. Fail the request. + // + + ExReleaseResource( AfdResource ); + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_SHARING_VIOLATION; + + return STATUS_SHARING_VIOLATION; + } + } + } + } + + // + // Store the address to which the endpoint is bound. + // + + endpoint->LocalAddress = AFD_ALLOCATE_POOL( + NonPagedPool, + requestedAddressLength, + AFD_LOCAL_ADDRESS_POOL_TAG + ); + + if ( endpoint->LocalAddress == NULL ) { + + ExReleaseResource( AfdResource ); + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + return STATUS_INSUFFICIENT_RESOURCES; + } + + endpoint->LocalAddressLength = + IrpSp->Parameters.DeviceIoControl.InputBufferLength; + + RtlMoveMemory( + endpoint->LocalAddress, + requestedAddress, + endpoint->LocalAddressLength + ); + + ExReleaseResource( AfdResource ); + + // + // Allocate memory to hold the EA buffer we'll use to specify the + // transport address to NtCreateFile. + // + + eaBufferLength = sizeof(FILE_FULL_EA_INFORMATION) - 1 + + TDI_TRANSPORT_ADDRESS_LENGTH + 1 + + IrpSp->Parameters.DeviceIoControl.InputBufferLength; + +#if DBG + ea = AFD_ALLOCATE_POOL( + NonPagedPool, + eaBufferLength, + AFD_EA_POOL_TAG + ); +#else + ea = AFD_ALLOCATE_POOL( + PagedPool, + eaBufferLength, + AFD_EA_POOL_TAG + ); +#endif + + if ( ea == NULL ) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Initialize the EA. + // + + ea->NextEntryOffset = 0; + ea->Flags = 0; + ea->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH; + ea->EaValueLength = (USHORT)IrpSp->Parameters.DeviceIoControl.InputBufferLength; + + RtlMoveMemory( + ea->EaName, + TdiTransportAddress, + ea->EaNameLength + 1 + ); + + transportAddress = (PTRANSPORT_ADDRESS)(&ea->EaName[ea->EaNameLength + 1]); + + RtlMoveMemory( + transportAddress, + requestedAddress, + ea->EaValueLength + ); + + // + // Prepare for opening the address object. + // + + InitializeObjectAttributes( + &objectAttributes, + &endpoint->TransportInfo->TransportDeviceName, + OBJ_CASE_INSENSITIVE, // attributes + NULL, + NULL + ); + + // + // Perform the actual open of the address object. + // + + KeAttachProcess( AfdSystemProcess ); + + status = ZwCreateFile( + &endpoint->AddressHandle, + GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, + &objectAttributes, + &iosb, // returned status information. + 0, // block size (unused). + 0, // file attributes. + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_CREATE, // create disposition. + 0, // create options. + ea, + eaBufferLength + ); + + AFD_FREE_POOL( + ea, + AFD_EA_POOL_TAG + ); + + if ( !NT_SUCCESS(status) ) { + + // + // We store the local address in a local before freeing it to + // avoid a timing window. + // + + PVOID localAddress = endpoint->LocalAddress; + + endpoint->LocalAddress = NULL; + endpoint->LocalAddressLength = 0; + AFD_FREE_POOL( + localAddress, + AFD_LOCAL_ADDRESS_POOL_TAG + ); + + KeDetachProcess( ); + + return status; + } + + AfdRecordAddrOpened(); + + // + // Get a pointer to the file object of the address. + // + + status = ObReferenceObjectByHandle( + endpoint->AddressHandle, + 0L, // DesiredAccess + NULL, + KernelMode, + (PVOID *)&endpoint->AddressFileObject, + NULL + ); + + ASSERT( NT_SUCCESS(status) ); + + AfdRecordAddrRef(); + + IF_DEBUG(BIND) { + KdPrint(( "AfdBind: address file object for endpoint %lx at %lx\n", + endpoint, endpoint->AddressFileObject )); + } + + // + // Remember the device object to which we need to give requests for + // this address object. We can't just use the + // fileObject->DeviceObject pointer because there may be a device + // attached to the transport protocol. + // + + endpoint->AddressDeviceObject = + IoGetRelatedDeviceObject( endpoint->AddressFileObject ); + + // + // Determine whether the TDI provider supports data bufferring. + // If the provider doesn't, then we have to do it. + // + + if ( (endpoint->TransportInfo->ProviderInfo.ServiceFlags & + TDI_SERVICE_INTERNAL_BUFFERING) != 0 ) { + endpoint->TdiBufferring = TRUE; + } else { + endpoint->TdiBufferring = FALSE; + } + + // + // Determine whether the TDI provider is message or stream oriented. + // + + if ( (endpoint->TransportInfo->ProviderInfo.ServiceFlags & + TDI_SERVICE_MESSAGE_MODE) != 0 ) { + endpoint->TdiMessageMode = TRUE; + } else { + endpoint->TdiMessageMode = FALSE; + } + + // + // Remember that the endpoint has been bound to a transport address. + // + + endpoint->State = AfdEndpointStateBound; + + // + // Set up indication handlers on the address object. Only set up + // appropriate event handlers--don't set unnecessary event handlers. + // + + status = AfdSetEventHandler( + endpoint->AddressFileObject, + TDI_EVENT_ERROR, + AfdErrorEventHandler, + endpoint + ); +#if DBG + if ( !NT_SUCCESS(status) ) { + DbgPrint( "AFD: Setting TDI_EVENT_ERROR failed: %lx\n", status ); + } +#endif + + if ( IS_DGRAM_ENDPOINT(endpoint) ) { + + endpoint->EventsActive = AFD_POLL_SEND; + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdBind: Endp %08lX, Active %08lX\n", + endpoint, + endpoint->EventsActive + )); + } + + status = AfdSetEventHandler( + endpoint->AddressFileObject, + TDI_EVENT_RECEIVE_DATAGRAM, + AfdReceiveDatagramEventHandler, + endpoint + ); +#if DBG + if ( !NT_SUCCESS(status) ) { + DbgPrint( "AFD: Setting TDI_EVENT_RECEIVE_DATAGRAM failed: %lx\n", status ); + } +#endif + + } else { + + status = AfdSetEventHandler( + endpoint->AddressFileObject, + TDI_EVENT_DISCONNECT, + AfdDisconnectEventHandler, + endpoint + ); +#if DBG + if ( !NT_SUCCESS(status) ) { + DbgPrint( "AFD: Setting TDI_EVENT_DISCONNECT failed: %lx\n", status ); + } +#endif + + if ( endpoint->TdiBufferring ) { + + status = AfdSetEventHandler( + endpoint->AddressFileObject, + TDI_EVENT_RECEIVE, + AfdReceiveEventHandler, + endpoint + ); +#if DBG + if ( !NT_SUCCESS(status) ) { + DbgPrint( "AFD: Setting TDI_EVENT_RECEIVE failed: %lx\n", status ); + } +#endif + + status = AfdSetEventHandler( + endpoint->AddressFileObject, + TDI_EVENT_RECEIVE_EXPEDITED, + AfdReceiveExpeditedEventHandler, + endpoint + ); +#if DBG + if ( !NT_SUCCESS(status) ) { + DbgPrint( "AFD: Setting TDI_EVENT_RECEIVE_EXPEDITED failed: %lx\n", status ); + } +#endif + + status = AfdSetEventHandler( + endpoint->AddressFileObject, + TDI_EVENT_SEND_POSSIBLE, + AfdSendPossibleEventHandler, + endpoint + ); +#if DBG + if ( !NT_SUCCESS(status) ) { + DbgPrint( "AFD: Setting TDI_EVENT_SEND_POSSIBLE failed: %lx\n", status ); + } +#endif + + } else { + + status = AfdSetEventHandler( + endpoint->AddressFileObject, + TDI_EVENT_RECEIVE, + AfdBReceiveEventHandler, + endpoint + ); +#if DBG + if ( !NT_SUCCESS(status) ) { + DbgPrint( "AFD: Setting TDI_EVENT_RECEIVE failed: %lx\n", status ); + } +#endif + + // + // Only attempt to set the expedited event handler if the + // TDI provider supports expedited data. + // + + if ( (endpoint->TransportInfo->ProviderInfo.ServiceFlags & + TDI_SERVICE_EXPEDITED_DATA) != 0 ) { + status = AfdSetEventHandler( + endpoint->AddressFileObject, + TDI_EVENT_RECEIVE_EXPEDITED, + AfdBReceiveExpeditedEventHandler, + endpoint + ); +#if DBG + if ( !NT_SUCCESS(status) ) { + DbgPrint( "AFD: Setting TDI_EVENT_RECEIVE_EXPEDITED failed: %lx\n", status ); + } +#endif + } + } + } + + KeDetachProcess( ); + + Irp->IoStatus.Information = 0; + return STATUS_SUCCESS; + +} // AfdBind + + +NTSTATUS +AfdGetAddress ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Handles the IOCTL_AFD_BIND IOCTL. + +Arguments: + + Irp - Pointer to I/O request packet. + + IrpSp - pointer to the IO stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + NTSTATUS status; + PAFD_ENDPOINT endpoint; + PFILE_OBJECT fileObject; + PDEVICE_OBJECT deviceObject; + + PAGED_CODE( ); + + Irp->IoStatus.Information = 0; + + // + // Make sure that the endpoint is in the correct state. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + if ( endpoint->AddressFileObject == NULL && + endpoint->State != AfdEndpointStateConnected ) { + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + // + // If the endpoint is connected, use the connection's file object. + // Otherwise, use the address file object. Don't use the connection + // file object if this is a Netbios endpoint because NETBT cannot + // support this TDI feature. + // + + if ( endpoint->Type == AfdBlockTypeVcConnecting && + endpoint->Common.VcConnecting.Connection != NULL && + endpoint->LocalAddress->Address[0].AddressType != + TDI_ADDRESS_TYPE_NETBIOS ) { + ASSERT( endpoint->Common.VcConnecting.Connection->Type == AfdBlockTypeConnection ); + fileObject = endpoint->Common.VcConnecting.Connection->FileObject; + deviceObject = endpoint->Common.VcConnecting.Connection->DeviceObject; + } else { + fileObject = endpoint->AddressFileObject; + deviceObject = endpoint->AddressDeviceObject; + } + + // + // Set up the query info to the TDI provider. + // + + ASSERT( Irp->MdlAddress != NULL ); + + TdiBuildQueryInformation( + Irp, + deviceObject, + fileObject, + AfdRestartGetAddress, + endpoint, + TDI_QUERY_ADDRESS_INFO, + Irp->MdlAddress + ); + + // + // Call the TDI provider to get the address. + // + + return AfdIoCallDriver( endpoint, deviceObject, Irp ); + +complete: + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + return status; + +} // AfdGetAddress + + +NTSTATUS +AfdRestartGetAddress ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + NTSTATUS status; + PAFD_ENDPOINT endpoint = Context; + KIRQL oldIrql; + PMDL mdl; + ULONG addressLength; + + // + // If the request succeeded, save the address in the endpoint so + // we can use it to handle address sharing. + // + + if ( NT_SUCCESS(Irp->IoStatus.Status) ) { + + // + // First determine the length of the address by walking the MDL + // chain. + // + + mdl = Irp->MdlAddress; + ASSERT( mdl != NULL ); + + addressLength = 0; + + do { + + addressLength += MmGetMdlByteCount( mdl ); + mdl = mdl->Next; + + } while ( mdl != NULL ); + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + // + // If the new address is longer than the original address, allocate + // a new local address buffer. The +4 accounts for the ActivityCount + // field that is returned by a query address but is not part + // of a TRANSPORT_ADDRESS. + // + + if ( addressLength > endpoint->LocalAddressLength + 4 ) { + + PVOID newAddress; + + newAddress = AFD_ALLOCATE_POOL( + NonPagedPool, + addressLength-4, + AFD_LOCAL_ADDRESS_POOL_TAG + ); + + if ( newAddress == NULL ) { + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + AFD_FREE_POOL( + endpoint->LocalAddress, + AFD_LOCAL_ADDRESS_POOL_TAG + ); + + endpoint->LocalAddress = newAddress; + endpoint->LocalAddressLength = addressLength-4; + } + + status = TdiCopyMdlToBuffer( + Irp->MdlAddress, + 4, + endpoint->LocalAddress, + 0, + endpoint->LocalAddressLength, + &endpoint->LocalAddressLength + ); + ASSERT( NT_SUCCESS(status) ); + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + } + + AfdCompleteOutstandingIrp( endpoint, Irp ); + + // + // If pending has been returned for this irp then mark the current + // stack as pending. + // + + if ( Irp->PendingReturned ) { + IoMarkIrpPending( Irp ); + } + + return STATUS_SUCCESS; + +} // AfdRestartGetAddress + +CHAR ZeroNodeAddress[6]; + + +BOOLEAN +AfdAreTransportAddressesEqual ( + IN PTRANSPORT_ADDRESS EndpointAddress, + IN ULONG EndpointAddressLength, + IN PTRANSPORT_ADDRESS RequestAddress, + IN ULONG RequestAddressLength, + IN BOOLEAN HonorWildcardIpPortInEndpointAddress + ) +{ + if ( EndpointAddress->Address[0].AddressType == TDI_ADDRESS_TYPE_IP && + RequestAddress->Address[0].AddressType == TDI_ADDRESS_TYPE_IP ) { + + TDI_ADDRESS_IP UNALIGNED *ipEndpointAddress; + TDI_ADDRESS_IP UNALIGNED *ipRequestAddress; + + // + // They are both IP addresses. If the ports are the same, and + // the IP addresses are or _could_be_ the same, then the addresses + // are equal. The "cound be" part is true if either IP address + // is 0, the "wildcard" IP address. + // + + ipEndpointAddress = (TDI_ADDRESS_IP UNALIGNED *)&EndpointAddress->Address[0].Address[0]; + ipRequestAddress = (TDI_ADDRESS_IP UNALIGNED *)&RequestAddress->Address[0].Address[0]; + + if ( ( ipEndpointAddress->sin_port == ipRequestAddress->sin_port || + ( HonorWildcardIpPortInEndpointAddress && + ipEndpointAddress->sin_port == 0 ) ) && + ( ipEndpointAddress->in_addr == ipRequestAddress->in_addr || + ipEndpointAddress->in_addr == 0 || ipRequestAddress->in_addr == 0 ) ) { + + return TRUE; + } + + // + // The addresses are not equal. + // + + return FALSE; + } + + if ( EndpointAddress->Address[0].AddressType == TDI_ADDRESS_TYPE_IPX && + RequestAddress->Address[0].AddressType == TDI_ADDRESS_TYPE_IPX ) { + + TDI_ADDRESS_IPX UNALIGNED *ipxEndpointAddress; + TDI_ADDRESS_IPX UNALIGNED *ipxRequestAddress; + + ipxEndpointAddress = (TDI_ADDRESS_IPX UNALIGNED *)&EndpointAddress->Address[0].Address[0]; + ipxRequestAddress = (TDI_ADDRESS_IPX UNALIGNED *)&RequestAddress->Address[0].Address[0]; + + // + // They are both IPX addresses. Check the network addresses + // first--if they don't match and both != 0, the addresses + // are different. + // + + if ( ipxEndpointAddress->NetworkAddress != ipxRequestAddress->NetworkAddress && + ipxEndpointAddress->NetworkAddress != 0 && + ipxRequestAddress->NetworkAddress != 0 ) { + return FALSE; + } + + // + // Now check the node addresses. Again, if they don't match + // and neither is 0, the addresses don't match. + // + + ASSERT( ZeroNodeAddress[0] == 0 ); + ASSERT( ZeroNodeAddress[1] == 0 ); + ASSERT( ZeroNodeAddress[2] == 0 ); + ASSERT( ZeroNodeAddress[3] == 0 ); + ASSERT( ZeroNodeAddress[4] == 0 ); + ASSERT( ZeroNodeAddress[5] == 0 ); + + if ( !RtlEqualMemory( + ipxEndpointAddress->NodeAddress, + ipxRequestAddress->NodeAddress, + 6 ) && + !RtlEqualMemory( + ipxEndpointAddress->NodeAddress, + ZeroNodeAddress, + 6 ) && + !RtlEqualMemory( + ipxRequestAddress->NodeAddress, + ZeroNodeAddress, + 6 ) ) { + return FALSE; + } + + // + // Finally, make sure the socket numbers match. + // + + if ( ipxEndpointAddress->Socket != ipxRequestAddress->Socket ) { + return FALSE; + } + + return TRUE; + + } + + // + // If either address is not of a known address type, then do a + // simple memory compare. + // + + return ( EndpointAddressLength == RtlCompareMemory( + EndpointAddress, + RequestAddress, + RequestAddressLength ) ); +} // AfdAreTransportAddressesEqual diff --git a/private/ntos/afd/blkconn.c b/private/ntos/afd/blkconn.c new file mode 100644 index 000000000..0135836d3 --- /dev/null +++ b/private/ntos/afd/blkconn.c @@ -0,0 +1,1453 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + blkconn.c + +Abstract: + + This module contains allocate, free, close, reference, and dereference + routines for AFD connections. + +Author: + + David Treadwell (davidtr) 10-Mar-1992 + +Revision History: + +--*/ + +#include "afdp.h" + +VOID +AfdFreeConnection ( + IN PVOID Context + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGEAFD, AfdAbortConnection ) +#pragma alloc_text( PAGE, AfdAddFreeConnection ) +#pragma alloc_text( PAGE, AfdAllocateConnection ) +#pragma alloc_text( PAGE, AfdCreateConnection ) +#pragma alloc_text( PAGE, AfdFreeConnection ) +#pragma alloc_text( PAGEAFD, AfdDereferenceConnection ) +#if REFERENCE_DEBUG +#pragma alloc_text( PAGEAFD, AfdReferenceConnection ) +#endif +#pragma alloc_text( PAGEAFD, AfdGetFreeConnection ) +#pragma alloc_text( PAGEAFD, AfdGetReturnedConnection ) +#pragma alloc_text( PAGEAFD, AfdGetUnacceptedConnection ) +#pragma alloc_text( PAGEAFD, AfdAddConnectedReference ) +#pragma alloc_text( PAGEAFD, AfdDeleteConnectedReference ) +#endif + +#if GLOBAL_REFERENCE_DEBUG +AFD_GLOBAL_REFERENCE_DEBUG AfdGlobalReference[MAX_GLOBAL_REFERENCE]; +LONG AfdGlobalReferenceSlot = -1; +#endif + +#if AFD_PERF_DBG +#define CONNECTION_REUSE_DISABLED (AfdDisableConnectionReuse) +#else +#define CONNECTION_REUSE_DISABLED (FALSE) +#endif + + +VOID +AfdAbortConnection ( + IN PAFD_CONNECTION Connection + ) +{ + + NTSTATUS status; + + ASSERT( Connection != 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. + // + + Connection->CleanupBegun = TRUE; + status = AfdBeginAbort( Connection ); + + if( !NT_SUCCESS(status) ) { + + Connection->AbortIndicated = TRUE; + AfdDeleteConnectedReference( Connection, FALSE ); + + } + + // + // Remove the active reference. + // + + DEREFERENCE_CONNECTION( Connection ); + +} // AfdAbortConnection + + +NTSTATUS +AfdAddFreeConnection ( + IN PAFD_ENDPOINT Endpoint + ) + +/*++ + +Routine Description: + + Adds a connection object to an endpoints pool of connections available + to satisfy a connect indication. + +Arguments: + + Endpoint - a pointer to the endpoint to which to add a connection. + +Return Value: + + NTSTATUS -- Indicates the status of the request. + +--*/ + +{ + PAFD_CONNECTION connection; + NTSTATUS status; + + PAGED_CODE( ); + + ASSERT( Endpoint->Type == AfdBlockTypeVcListening ); + + // + // Create a new connection block and associated connection object. + // + + status = AfdCreateConnection( + &Endpoint->TransportInfo->TransportDeviceName, + Endpoint->AddressHandle, + Endpoint->TdiBufferring, + Endpoint->InLine, + Endpoint->OwningProcess, + &connection + ); + + if ( !NT_SUCCESS(status) ) { + return status; + } + + ASSERT( Endpoint->TdiBufferring == connection->TdiBufferring ); + + // + // Set up the handle in the listening connection structure and place + // the connection on the endpoint's list of listening connections. + // + + ExInterlockedInsertTailList( + &Endpoint->Common.VcListening.FreeConnectionListHead, + &connection->ListEntry, + &AfdSpinLock + ); + + InterlockedIncrement( + &Endpoint->Common.VcListening.FreeConnectionCount + ); + + return STATUS_SUCCESS; + +} // AfdAddFreeConnection + + +PAFD_CONNECTION +AfdAllocateConnection ( + VOID + ) +{ + PAFD_CONNECTION connection; + + PAGED_CODE( ); + + // + // Allocate a buffer to hold the endpoint structure. + // + + connection = AFD_ALLOCATE_POOL( + NonPagedPool, + sizeof(AFD_CONNECTION), + AFD_CONNECTION_POOL_TAG + ); + + if ( connection == NULL ) { + return NULL; + } + + RtlZeroMemory( connection, sizeof(AFD_CONNECTION) ); + + // + // Initialize the reference count to 1 to account for the caller's + // reference. Connection blocks are temporary--as soon as the last + // reference goes away, so does the connection. There is no active + // reference on a connection block. + // + + connection->ReferenceCount = 1; + + // + // Initialize the connection structure. + // + + connection->Type = AfdBlockTypeConnection; + connection->State = AfdConnectionStateFree; + //connection->Handle = NULL; + //connection->FileObject = NULL; + //connection->RemoteAddress = NULL; + //connection->Endpoint = NULL; + //connection->ReceiveBytesIndicated = 0; + //connection->ReceiveBytesTaken = 0; + //connection->ReceiveBytesOutstanding = 0; + //connection->ReceiveExpeditedBytesIndicated = 0; + //connection->ReceiveExpeditedBytesTaken = 0; + //connection->ReceiveExpeditedBytesOutstanding = 0; + //connection->ConnectDataBuffers = NULL; + //connection->DisconnectIndicated = FALSE; + //connection->AbortIndicated = FALSE; + //connection->ConnectedReferenceAdded = FALSE; + //connection->SpecialCondition = FALSE; + //connection->CleanupBegun = FALSE; + //connection->OwningProcess = NULL; + //connection->ClosePendedTransmit = FALSE; + +#if REFERENCE_DEBUG + connection->CurrentReferenceSlot = -1; + RtlZeroMemory( + &connection->ReferenceDebug, + sizeof(AFD_REFERENCE_DEBUG) * MAX_REFERENCE + ); +#endif + + // + // Return a pointer to the new connection to the caller. + // + + IF_DEBUG(CONNECTION) { + KdPrint(( "AfdAllocateConnection: connection at %lx\n", connection )); + } + + return connection; + +} // AfdAllocateConnection + + +NTSTATUS +AfdCreateConnection ( + IN PUNICODE_STRING TransportDeviceName, + IN HANDLE AddressHandle, + IN BOOLEAN TdiBufferring, + IN BOOLEAN InLine, + IN PEPROCESS ProcessToCharge, + OUT PAFD_CONNECTION *Connection + ) + +/*++ + +Routine Description: + + Allocates a connection block and creates a connection object to + go with the block. This routine also associates the connection + with the specified address handle (if any). + +Arguments: + + TransportDeviceName - Name to use when creating the connection object. + + AddressHandle - a handle to an address object for the specified + transport. If specified (non NULL), the connection object that + is created is associated with the address object. + + TdiBufferring - whether the TDI provider supports data bufferring. + Only passed so that it can be stored in the connection + structure. + + InLine - if TRUE, the endpoint should be created in OOB inline + mode. + + ProcessToCharge - the process which should be charged the quota + for this connection. + + Connection - receives a pointer to the new connection. + +Return Value: + + NTSTATUS -- Indicates the status of the request. + +--*/ + +{ + NTSTATUS status; + IO_STATUS_BLOCK ioStatusBlock; + OBJECT_ATTRIBUTES objectAttributes; + CHAR eaBuffer[sizeof(FILE_FULL_EA_INFORMATION) - 1 + + TDI_CONNECTION_CONTEXT_LENGTH + 1 + + sizeof(CONNECTION_CONTEXT)]; + PFILE_FULL_EA_INFORMATION ea; + CONNECTION_CONTEXT UNALIGNED *ctx; + PAFD_CONNECTION connection; + + PAGED_CODE( ); + + // + // Attempt to charge this process quota for the data bufferring we + // will do on its behalf. + // + + try { + + PsChargePoolQuota( + ProcessToCharge, + NonPagedPool, + AfdReceiveWindowSize + AfdSendWindowSize + ); + + } except ( EXCEPTION_EXECUTE_HANDLER ) { + +#if DBG + DbgPrint( "AfdCreateConnection: PsChargePoolQuota failed.\n" ); +#endif + + return STATUS_QUOTA_EXCEEDED; + } + + // + // Allocate a connection block. + // + + connection = AfdAllocateConnection( ); + + if ( connection == NULL ) { + PsReturnPoolQuota( + ProcessToCharge, + NonPagedPool, + AfdReceiveWindowSize + AfdSendWindowSize + ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + AfdRecordQuotaHistory( + ProcessToCharge, + (LONG)(AfdReceiveWindowSize+AfdSendWindowSize), + "CreateConn ", + connection + ); + + AfdRecordPoolQuotaCharged( AfdReceiveWindowSize + AfdSendWindowSize ); + + // + // Remember the process that got charged the pool quota for this + // connection object. Also reference the process to which we're + // going to charge the quota so that it is still around when we + // return the quota. + // + + ASSERT( connection->OwningProcess == NULL ); + connection->OwningProcess = ProcessToCharge; + + ObReferenceObject( ProcessToCharge ); + + // + // If the provider does not buffer, initialize appropriate lists in + // the connection object. + // + + connection->TdiBufferring = TdiBufferring; + + if ( !TdiBufferring ) { + + InitializeListHead( &connection->VcReceiveIrpListHead ); + InitializeListHead( &connection->VcSendIrpListHead ); + InitializeListHead( &connection->VcReceiveBufferListHead ); + + connection->VcBufferredReceiveBytes = 0; + connection->VcBufferredExpeditedBytes = 0; + connection->VcBufferredReceiveCount = 0; + connection->VcBufferredExpeditedCount = 0; + + connection->VcReceiveBytesInTransport = 0; + connection->VcReceiveCountInTransport = 0; + + connection->VcBufferredSendBytes = 0; + connection->VcBufferredSendCount = 0; + + } else { + + connection->VcNonBlockingSendPossible = TRUE; + connection->VcZeroByteReceiveIndicated = FALSE; + } + + // + // Set up the send and receive window with default maximums. + // + + connection->MaxBufferredReceiveBytes = AfdReceiveWindowSize; + connection->MaxBufferredReceiveCount = + (CSHORT)(AfdReceiveWindowSize / AfdBufferMultiplier); + + connection->MaxBufferredSendBytes = AfdSendWindowSize; + connection->MaxBufferredSendCount = + (CSHORT)(AfdSendWindowSize / AfdBufferMultiplier); + + // + // We need to open a connection object to the TDI provider for this + // endpoint. First create the EA for the connection context and the + // object attributes structure which will be used for all the + // connections we open here. + // + + ea = (PFILE_FULL_EA_INFORMATION)eaBuffer; + ea->NextEntryOffset = 0; + ea->Flags = 0; + ea->EaNameLength = TDI_CONNECTION_CONTEXT_LENGTH; + ea->EaValueLength = sizeof(CONNECTION_CONTEXT); + + RtlMoveMemory( ea->EaName, TdiConnectionContext, ea->EaNameLength + 1 ); + + // + // Use the pointer to the connection block as the connection context. + // + + ctx = (CONNECTION_CONTEXT UNALIGNED *)&ea->EaName[ea->EaNameLength + 1]; + *ctx = (CONNECTION_CONTEXT)connection; + + InitializeObjectAttributes( + &objectAttributes, + TransportDeviceName, + OBJ_CASE_INSENSITIVE, // attributes + NULL, + NULL + ); + + // + // Do the actual open of the connection object. + // + + KeAttachProcess( AfdSystemProcess ); + + status = ZwCreateFile( + &connection->Handle, + GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, + &objectAttributes, + &ioStatusBlock, + NULL, // AllocationSize + 0, // FileAttributes + 0, // ShareAccess + 0, // CreateDisposition + 0, // CreateOptions + eaBuffer, + FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0] ) + + ea->EaNameLength + 1 + ea->EaValueLength + ); + + if ( NT_SUCCESS(status) ) { + status = ioStatusBlock.Status; + } + if ( !NT_SUCCESS(status) ) { + KeDetachProcess( ); + DEREFERENCE_CONNECTION( connection ); + return status; + } + + AfdRecordConnOpened(); + + // + // Reference the connection's file object. + // + + status = ObReferenceObjectByHandle( + connection->Handle, + 0, + (POBJECT_TYPE) NULL, + KernelMode, + (PVOID *)&connection->FileObject, + NULL + ); + + ASSERT( NT_SUCCESS(status) ); + + IF_DEBUG(OPEN_CLOSE) { + KdPrint(( "AfdCreateConnection: file object for connection %lx at " + "%lx\n", connection, connection->FileObject )); + } + + AfdRecordConnRef(); + + // + // Remember the device object to which we need to give requests for + // this connection object. We can't just use the + // fileObject->DeviceObject pointer because there may be a device + // attached to the transport protocol. + // + + connection->DeviceObject = + IoGetRelatedDeviceObject( connection->FileObject ); + + // + // Associate the connection with the address object on the endpoint if + // an address handle was specified. + // + + if ( AddressHandle != NULL ) { + + TDI_REQUEST_USER_ASSOCIATE associateRequest; + + associateRequest.AddressHandle = AddressHandle; + + status = ZwDeviceIoControlFile( + connection->Handle, + NULL, // EventHandle + NULL, // APC Routine + NULL, // APC Context + &ioStatusBlock, + IOCTL_TDI_ASSOCIATE_ADDRESS, + (PVOID)&associateRequest, // InputBuffer + sizeof(associateRequest), // InputBufferLength + NULL, // OutputBuffer + 0 // OutputBufferLength + ); + + if ( status == STATUS_PENDING ) { + status = ZwWaitForSingleObject( connection->Handle, TRUE, NULL ); + ASSERT( NT_SUCCESS(status) ); + status = ioStatusBlock.Status; + } + } + + KeDetachProcess( ); + + // + // If requested, set the connection to be inline. + // + + if ( InLine ) { + status = AfdSetInLineMode( connection, TRUE ); + if ( !NT_SUCCESS(status) ) { + DEREFERENCE_CONNECTION( connection ); + return status; + } + } + + // + // Set up the connection pointer and return. + // + + *Connection = connection; + + UPDATE_CONN( connection, connection->FileObject ); + + return STATUS_SUCCESS; + +} // AfdCreateConnection + + +VOID +AfdFreeConnection ( + IN PVOID Context + ) +{ + NTSTATUS status; + PAFD_CONNECTION connection; + BOOLEAN reuseConnection; + PAFD_ENDPOINT listeningEndpoint; + + PAGED_CODE( ); + + ASSERT( Context != NULL ); + + connection = CONTAINING_RECORD( + Context, + AFD_CONNECTION, + WorkItem + ); + + ASSERT( connection->ReferenceCount == 0 ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + // + // Determine whether we can reuse this connection object. We reuse + // connection objects to assist performance when the connection + // object is from a listening endpoint. + // + + if ( connection->Endpoint != NULL && + !CONNECTION_REUSE_DISABLED && + !connection->Endpoint->EndpointCleanedUp && + connection->Endpoint->Type == AfdBlockTypeVcConnecting && + connection->Endpoint->Common.VcConnecting.ListenEndpoint != NULL ) { + + UPDATE_CONN( connection, 0 ); + + // + // Reference the listening endpoint so that it does not + // go away while we are cleaning up this connection object + // for reuse. + // + + listeningEndpoint = connection->Endpoint->Common.VcConnecting.ListenEndpoint; + ASSERT( listeningEndpoint->Type == AfdBlockTypeVcListening ); + + REFERENCE_ENDPOINT( listeningEndpoint ); + + reuseConnection = TRUE; + + } else { + + UPDATE_CONN( connection, 0 ); + + reuseConnection = FALSE; + + // + // Free and dereference the various objects on the connection. + // Close and dereference the TDI connection object on the endpoint, + // if any. + // + + if ( connection->Handle != NULL ) { + + IO_STATUS_BLOCK ioStatusBlock; + HANDLE handle; + + KeAttachProcess( AfdSystemProcess ); + handle = connection->Handle; + connection->Handle = NULL; + + // + // Disassociate this connection object from the address object. + // + + status = ZwDeviceIoControlFile( + handle, // FileHandle + NULL, // Event + NULL, // ApcRoutine + NULL, // ApcContext + &ioStatusBlock, // IoStatusBlock + IOCTL_TDI_DISASSOCIATE_ADDRESS, // IoControlCode + NULL, // InputBuffer + 0, // InputBufferLength + NULL, // OutputBuffer + 0 // OutputBufferLength + ); + + if( status == STATUS_PENDING ) { + + status = ZwWaitForSingleObject( + handle, + TRUE, + NULL + ); + + ASSERT( NT_SUCCESS(status) ); + status = ioStatusBlock.Status; + + } + + // ASSERT( NT_SUCCESS(status) ); + + // + // Close the handle. + // + + status = ZwClose( handle ); + ASSERT( NT_SUCCESS(status) ); + AfdRecordConnClosed(); + KeDetachProcess( ); + + } + + if ( connection->FileObject != NULL ) { + + ObDereferenceObject( connection->FileObject ); + connection->FileObject = NULL; + AfdRecordConnDeref(); + + } + + // + // Return the quota we charged to this process when we allocated + // the connection object. + // + + PsReturnPoolQuota( + connection->OwningProcess, + NonPagedPool, + connection->MaxBufferredReceiveBytes + connection->MaxBufferredSendBytes + ); + AfdRecordQuotaHistory( + connection->OwningProcess, + -(LONG)(connection->MaxBufferredReceiveBytes + connection->MaxBufferredSendBytes), + "ConnDealloc ", + connection + ); + AfdRecordPoolQuotaReturned( + connection->MaxBufferredReceiveBytes + connection->MaxBufferredSendBytes + ); + + // + // Dereference the process that got the quota charge. + // + + ASSERT( connection->OwningProcess != NULL ); + ObDereferenceObject( connection->OwningProcess ); + connection->OwningProcess = NULL; + } + + if ( !connection->TdiBufferring && connection->VcDisconnectIrp != NULL ) { + IoFreeIrp( connection->VcDisconnectIrp ); + connection->VcDisconnectIrp = NULL; + } + + // + // If we're going to reuse this connection, don't free the remote + // address structure--we'll reuse it as well. + // + + if ( connection->RemoteAddress != NULL && !reuseConnection ) { + AFD_FREE_POOL( + connection->RemoteAddress, + AFD_REMOTE_ADDRESS_POOL_TAG + ); + connection->RemoteAddress = NULL; + } + + if ( connection->ConnectDataBuffers != NULL ) { + AfdFreeConnectDataBuffers( connection->ConnectDataBuffers ); + connection->ConnectDataBuffers = NULL; + } + + // + // If this is a bufferring connection, remove all the AFD buffers + // from the connection's lists and free them. + // + + if ( !connection->TdiBufferring ) { + + PAFD_BUFFER afdBuffer; + PLIST_ENTRY listEntry; + + ASSERT( IsListEmpty( &connection->VcReceiveIrpListHead ) ); + ASSERT( IsListEmpty( &connection->VcSendIrpListHead ) ); + + while ( !IsListEmpty( &connection->VcReceiveBufferListHead ) ) { + + listEntry = RemoveHeadList( &connection->VcReceiveBufferListHead ); + afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry ); + + afdBuffer->DataOffset = 0; + afdBuffer->ExpeditedData = FALSE; + + AfdReturnBuffer( afdBuffer ); + } + } + + if ( connection->Endpoint != NULL ) { + + // + // If there is a transmit file IRP on the endpoint, complete it. + // + + if ( connection->ClosePendedTransmit ) { + AfdCompleteClosePendedTransmit( connection->Endpoint ); + } + + DEREFERENCE_ENDPOINT( connection->Endpoint ); + connection->Endpoint = NULL; + } + + // + // Either free the actual connection block or put it back on the + // listening endpoint's list of available connection objects. + // + + if ( reuseConnection ) { + + // + // Reinitialize various fields in the connection object. + // + + connection->ReferenceCount = 1; + ASSERT( connection->Type == AfdBlockTypeConnection ); + connection->State = AfdConnectionStateFree; + + connection->DisconnectIndicated = FALSE; + connection->AbortIndicated = FALSE; + connection->ConnectedReferenceAdded = FALSE; + connection->SpecialCondition = FALSE; + connection->CleanupBegun = FALSE; + connection->ClosePendedTransmit = FALSE; + + if ( !connection->TdiBufferring ) { + + ASSERT( IsListEmpty( &connection->VcReceiveIrpListHead ) ); + ASSERT( IsListEmpty( &connection->VcSendIrpListHead ) ); + ASSERT( IsListEmpty( &connection->VcReceiveBufferListHead ) ); + + connection->VcBufferredReceiveBytes = 0; + connection->VcBufferredExpeditedBytes = 0; + connection->VcBufferredReceiveCount = 0; + connection->VcBufferredExpeditedCount = 0; + + connection->VcReceiveBytesInTransport = 0; + connection->VcReceiveCountInTransport = 0; + + connection->VcBufferredSendBytes = 0; + connection->VcBufferredSendCount = 0; + + } else { + + connection->VcNonBlockingSendPossible = TRUE; + connection->VcZeroByteReceiveIndicated = FALSE; + } + + // + // Place the connection on the listening endpoint's list of + // available connections. + // + + ExInterlockedInsertHeadList( + &listeningEndpoint->Common.VcListening.FreeConnectionListHead, + &connection->ListEntry, + &AfdSpinLock + ); + + // + // Reduce the count of failed connection adds on the listening + // endpoint to account for this connection object which we're + // adding back onto the queue. + // + + InterlockedDecrement( + &listeningEndpoint->Common.VcListening.FailedConnectionAdds + ); + + InterlockedIncrement( + &listeningEndpoint->Common.VcListening.FreeConnectionCount + ); + + // + // Get rid of the reference we added to the listening endpoint + // above. + // + + DEREFERENCE_ENDPOINT( listeningEndpoint ); + + } else { + +#if ENABLE_ABORT_TIMER_HACK + // + // Free any attached abort timer. + // + + if( connection->AbortTimerInfo != NULL ) { + + AFD_FREE_POOL( + connection->AbortTimerInfo, + AFD_ABORT_TIMER_HACK_POOL_TAG + ); + + } +#endif // ENABLE_ABORT_TIMER_HACK + + // + // Free the space that holds the connection itself. + // + + IF_DEBUG(CONNECTION) { + KdPrint(( "AfdFreeConnection: Freeing connection at %lx\n", connection )); + } + + connection->Type = 0xAFDF; + + AFD_FREE_POOL( + connection, + AFD_CONNECTION_POOL_TAG + ); + } + +} // AfdFreeConnection + + +#if REFERENCE_DEBUG +VOID +AfdDereferenceConnection ( + IN PAFD_CONNECTION Connection, + IN PVOID Info1, + IN PVOID Info2 + ) +#else +VOID +AfdDereferenceConnection ( + IN PAFD_CONNECTION Connection + ) +#endif +{ + LONG result; + KIRQL oldIrql; + + ASSERT( Connection->Type == AfdBlockTypeConnection ); + ASSERT( Connection->ReferenceCount > 0 ); + ASSERT( Connection->ReferenceCount != 0xD1000000 ); + + IF_DEBUG(CONNECTION) { + KdPrint(( "AfdDereferenceConnection: connection %lx, new refcnt %ld\n", + Connection, Connection->ReferenceCount-1 )); + } + + // + // Note that if we're tracking refcnts, we *must* call + // AfdUpdateConnectionTrack before doing the dereference. This is + // because the connection object might go away in another thread as + // soon as we do the dereference. However, because of this, + // the refcnt we store with this may sometimes be incorrect. + // + +#if REFERENCE_DEBUG + AfdUpdateConnectionTrack( + Connection, + Connection->ReferenceCount - 1, + Info1, + Info2, + 0xFFFFFFFF + ); +#endif + + // + // We must hold AfdSpinLock while doing the dereference and check + // for free. This is because some code makes the assumption that + // the connection structure will not go away while AfdSpinLock is + // held, and that code references the endpoint before releasing + // AfdSpinLock. If we did the InterlockedDecrement() without the + // lock held, our count may go to zero, that code may reference the + // connection, and then a double free might occur. + // + // It is still valuable to use the interlocked routines for + // increment and decrement of structures because it allows us to + // avoid having to hold the spin lock for a reference. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + // + // Perform the actual decrement of the refernce count. Note that we + // use the intrinsic functions for this, because of their + // performance benefit. + // + + result = InterlockedDecrement( &Connection->ReferenceCount ); + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + // + // If the reference count is now 0, free the connection in an + // executive worker thread. + // + + if ( result == 0 ) { + + AfdQueueWorkItem( + AfdFreeConnection, + &Connection->WorkItem + ); + + } + +} // AfdDereferenceConnection + + +PAFD_CONNECTION +AfdGetFreeConnection ( + IN PAFD_ENDPOINT Endpoint + ) + +/*++ + +Routine Description: + + Takes a connection off of the endpoint's queue of listening + connections. + +Arguments: + + Endpoint - a pointer to the endpoint from which to get a connection. + +Return Value: + + AFD_CONNECTION - a pointer to an AFD connection block. + +--*/ + +{ + PAFD_CONNECTION connection; + PLIST_ENTRY listEntry; + + ASSERT( Endpoint->Type == AfdBlockTypeVcListening ); + + // + // Remove the first entry from the list. If the list is empty, + // return NULL. + // + + listEntry = ExInterlockedRemoveHeadList( + &Endpoint->Common.VcListening.FreeConnectionListHead, + &AfdSpinLock + ); + + if ( listEntry == NULL ) { + return NULL; + } + + InterlockedDecrement( + &Endpoint->Common.VcListening.FreeConnectionCount + ); + + // + // Find the connection pointer from the list entry and return a + // pointer to the connection object. + // + + connection = CONTAINING_RECORD( + listEntry, + AFD_CONNECTION, + ListEntry + ); + + return connection; + +} // AfdGetFreeConnection + + +PAFD_CONNECTION +AfdGetReturnedConnection ( + IN PAFD_ENDPOINT Endpoint, + IN ULONG Sequence + ) + +/*++ + +Routine Description: + + Takes a connection off of the endpoint's queue of returned + connections. + +Arguments: + + Endpoint - a pointer to the endpoint from which to get a connection. + + Sequence - the sequence the connection must match. This is actually + a pointer to the connection. If NULL, the first returned + connection is used. + +Return Value: + + AFD_CONNECTION - a pointer to an AFD connection block. + +--*/ + +{ + PAFD_CONNECTION connection; + PLIST_ENTRY listEntry; + KIRQL oldIrql; + + ASSERT( Endpoint->Type == AfdBlockTypeVcListening ); + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + // + // Walk the endpoint's list of returned connections until we reach + // the end or until we find one with a matching sequence. + // + + for ( listEntry = Endpoint->Common.VcListening.ReturnedConnectionListHead.Flink; + listEntry != &Endpoint->Common.VcListening.ReturnedConnectionListHead; + listEntry = listEntry->Flink ) { + + + connection = CONTAINING_RECORD( + listEntry, + AFD_CONNECTION, + ListEntry + ); + + if ( Sequence == (ULONG)connection || Sequence == 0 ) { + + // + // Found the connection we were looking for. Remove + // the connection from the list, release the spin lock, + // and return the connection. + // + + RemoveEntryList( listEntry ); + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + return connection; + } + } + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + return NULL; + +} // AfdGetReturnedConnection + + +PAFD_CONNECTION +AfdGetUnacceptedConnection ( + IN PAFD_ENDPOINT Endpoint + ) + +/*++ + +Routine Description: + + Takes a connection of the endpoint's queue of unaccpted connections. + + *** NOTE: This routine must be called with AfdSpinLock held!! + +Arguments: + + Endpoint - a pointer to the endpoint from which to get a connection. + +Return Value: + + AFD_CONNECTION - a pointer to an AFD connection block. + +--*/ + +{ + PAFD_CONNECTION connection; + PLIST_ENTRY listEntry; + + ASSERT( Endpoint->Type == AfdBlockTypeVcListening ); + ASSERT( KeGetCurrentIrql( ) == DISPATCH_LEVEL ); + + if ( IsListEmpty( &Endpoint->Common.VcListening.UnacceptedConnectionListHead ) ) { + return NULL; + } + + // + // Dequeue a listening connection and remember its handle. + // + + listEntry = RemoveHeadList( &Endpoint->Common.VcListening.UnacceptedConnectionListHead ); + connection = CONTAINING_RECORD( listEntry, AFD_CONNECTION, ListEntry ); + + return connection; + +} // AfdGetUnacceptedConnection + + +#if REFERENCE_DEBUG +VOID +AfdReferenceConnection ( + IN PAFD_CONNECTION Connection, + IN PVOID Info1, + IN PVOID Info2 + ) +{ + + LONG result; + + ASSERT( Connection->Type == AfdBlockTypeConnection ); + ASSERT( Connection->ReferenceCount > 0 ); + ASSERT( Connection->ReferenceCount != 0xD1000000 ); + + IF_DEBUG(CONNECTION) { + KdPrint(( "AfdReferenceConnection: connection %lx, new refcnt %ld\n", + Connection, Connection->ReferenceCount+1 )); + } + + // + // Do the actual increment of the reference count. + // + + result = InterlockedIncrement( &Connection->ReferenceCount ); + +#if REFERENCE_DEBUG + AfdUpdateConnectionTrack( + Connection, + result, + Info1, + Info2, + 1 + ); +#endif + +} // AfdReferenceConnection +#endif + + +VOID +AfdAddConnectedReference ( + IN PAFD_CONNECTION Connection + ) + +/*++ + +Routine Description: + + Adds the connected reference to an AFD connection block. The + connected reference is special because it prevents the connection + object from being freed until we receive a disconnect event, or know + through some other means that the virtual circuit is disconnected. + +Arguments: + + Connection - a pointer to an AFD connection block. + +Return Value: + + None. + +--*/ + +{ + KIRQL oldIrql; + + AfdAcquireSpinLock( &Connection->Endpoint->SpinLock, &oldIrql ); + + IF_DEBUG(CONNECTION) { + KdPrint(( "AfdAddConnectedReference: connection %lx, new refcnt %ld\n", + Connection, Connection->ReferenceCount+1 )); + } + + ASSERT( !Connection->ConnectedReferenceAdded ); + ASSERT( Connection->Type == AfdBlockTypeConnection ); + + // + // Increment the reference count and remember that the connected + // reference has been placed on the connection object. + // + + Connection->ConnectedReferenceAdded = TRUE; + AfdRecordConnectedReferencesAdded(); + + AfdReleaseSpinLock( &Connection->Endpoint->SpinLock, oldIrql ); + + REFERENCE_CONNECTION( Connection ); + +} // AfdAddConnectedReference + + +VOID +AfdDeleteConnectedReference ( + IN PAFD_CONNECTION Connection, + IN BOOLEAN EndpointLockHeld + ) + +/*++ + +Routine Description: + + Removes the connected reference to an AFD connection block. If the + connected reference has already been removed, this routine does + nothing. The connected reference should be removed as soon as we + know that it is OK to close the connection object handle, but not + before. Removing this reference too soon could abort a connection + which shouldn't get aborted. + +Arguments: + + Connection - a pointer to an AFD connection block. + + EndpointLockHeld - TRUE if the caller already has the endpoint + spin lock. The lock remains held on exit. + +Return Value: + + None. + +--*/ + +{ + KIRQL oldIrql; + PAFD_ENDPOINT endpoint; +#if REFERENCE_DEBUG + PVOID caller, callersCaller; + + RtlGetCallersAddress( &caller, &callersCaller ); +#endif + + endpoint = Connection->Endpoint; + + if ( !EndpointLockHeld ) { + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + } + + // + // Only do a dereference if the connected reference is still active + // on the connectiuon object. + // + + if ( Connection->ConnectedReferenceAdded ) { + + // + // Three things must be true before we can remove the connected + // reference: + // + // 1) There must be no sends outstanding on the connection if + // the TDI provider does not support bufferring. This is + // because AfdRestartBufferSend() looks at the connection + // object. + // + // 2) Cleanup must have started on the endpoint. Until we get a + // cleanup IRP on the endpoint, we could still get new sends. + // + // 3) We have been indicated with a disconnect on the + // connection. We want to keep the connection object around + // until we get a disconnect indication in order to avoid + // premature closes on the connection object resulting in an + // unintended abort. If the transport does not support + // orderly release, then this condition is not necessary. + // + + if ( (Connection->TdiBufferring || + Connection->VcBufferredSendCount == 0) + + && + + Connection->CleanupBegun + + && + + (Connection->AbortIndicated || Connection->DisconnectIndicated || + ( (endpoint->TransportInfo->ProviderInfo.ServiceFlags & + TDI_SERVICE_ORDERLY_RELEASE) == 0)) ) { + + IF_DEBUG(CONNECTION) { + KdPrint(( "AfdDeleteConnectedReference: connection %lx, " + "new refcnt %ld\n", + Connection, Connection->ReferenceCount-1 )); + } + + // + // Be careful about the order of things here. We must FIRST + // reset the flag, then release the spin lock and call + // AfdDereferenceConnection(). Note that it is illegal to + // call AfdDereferenceConnection() with a spin lock held. + // + + Connection->ConnectedReferenceAdded = FALSE; + AfdRecordConnectedReferencesDeleted(); + + if ( !EndpointLockHeld ) { + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + } + + DEREFERENCE_CONNECTION( Connection ); + + } else { + + IF_DEBUG(CONNECTION) { + KdPrint(( "AfdDeleteConnectedReference: connection %lx, " + "%ld sends pending\n", + Connection, Connection->VcBufferredSendCount )); + } + +#if REFERENCE_DEBUG + { + ULONG action; + + action = 0; + + if ( !Connection->TdiBufferring && + Connection->VcBufferredSendCount != 0 ) { + action |= 0xA0000000; + } + + if ( !Connection->CleanupBegun ) { + action |= 0x0B000000; + } + + if ( !Connection->AbortIndicated && !Connection->DisconnectIndicated ) { + action |= 0x00C00000; + } + + UPDATE_CONN( Connection, action ); + } +#endif + // + // Remember that the connected reference deletion is still + // pending, i.e. there is a special condition on the + // endpoint. This will cause AfdRestartBufferSend() to do + // the actual dereference when the last send completes. + // + + Connection->SpecialCondition = TRUE; + + if ( !EndpointLockHeld ) { + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + } + } + + } else { + + IF_DEBUG(CONNECTION) { + KdPrint(( "AfdDeleteConnectedReference: already removed on " + " connection %lx, refcnt %ld\n", + Connection, Connection->ReferenceCount )); + } + + if ( !EndpointLockHeld ) { + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + } + } + + return; + +} // AfdDeleteConnectedReference + +#if REFERENCE_DEBUG + + +VOID +AfdUpdateConnectionTrack ( + IN PAFD_CONNECTION Connection, + IN LONG NewReferenceCount, + IN PVOID Info1, + IN PVOID Info2, + IN ULONG Action + ) +{ + PAFD_REFERENCE_DEBUG slot; + LONG newSlot; + + newSlot = InterlockedIncrement( &Connection->CurrentReferenceSlot ); + slot = &Connection->ReferenceDebug[newSlot % MAX_REFERENCE]; + + slot->Info1 = Info1; + slot->Info2 = Info2; + slot->Action = Action; + slot->NewCount = NewReferenceCount; + +#if GLOBAL_REFERENCE_DEBUG + { + PAFD_GLOBAL_REFERENCE_DEBUG globalSlot; + + newSlot = InterlockedIncrement( &AfdGlobalReferenceSlot ); + globalSlot = &AfdGlobalReference[newSlot % MAX_GLOBAL_REFERENCE]; + + globalSlot->Info1 = Info1; + globalSlot->Info2 = Info2; + globalSlot->Action = Action; + globalSlot->NewCount = NewReferenceCount; + globalSlot->Connection = Connection; + KeQueryTickCount( &globalSlot->TickCounter ); + } +#endif + +} // AfdUpdateConnectionTrack + +#endif + diff --git a/private/ntos/afd/blkendp.c b/private/ntos/afd/blkendp.c new file mode 100644 index 000000000..ceff093ee --- /dev/null +++ b/private/ntos/afd/blkendp.c @@ -0,0 +1,1010 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + blkendp.c + +Abstract: + + This module contains allocate, free, close, reference, and dereference + routines for AFD endpoints. + +Author: + + David Treadwell (davidtr) 10-Mar-1992 + +Revision History: + +--*/ + +#include "afdp.h" + +VOID +AfdFreeEndpoint ( + IN PVOID Context + ); + +PAFD_TRANSPORT_INFO +AfdGetTransportInfo ( + IN PUNICODE_STRING TransportDeviceName + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, AfdAllocateEndpoint ) +#pragma alloc_text( PAGE, AfdCloseEndpoint ) +#pragma alloc_text( PAGE, AfdFreeEndpoint ) +#pragma alloc_text( PAGE, AfdGetTransportInfo ) +#pragma alloc_text( PAGE, AfdRefreshEndpoint ) +#pragma alloc_text( PAGEAFD, AfdDereferenceEndpoint ) +#if REFERENCE_DEBUG +#pragma alloc_text( PAGEAFD, AfdReferenceEndpoint ) +#endif +#pragma alloc_text( PAGEAFD, AfdFreeQueuedConnections ) +#endif + + +NTSTATUS +AfdAllocateEndpoint ( + OUT PAFD_ENDPOINT * NewEndpoint, + IN PUNICODE_STRING TransportDeviceName, + IN LONG GroupID + ) + +/*++ + +Routine Description: + + Allocates and initializes a new AFD endpoint structure. + +Arguments: + + NewEndpoint - Receives a pointer to the new endpoint structure if + successful. + + TransportDeviceName - the name of the TDI transport provider + corresponding to the endpoint structure. + + GroupID - Identifies the group ID for the new endpoint. + +Return Value: + + NTSTATUS - The completion status. + +--*/ + +{ + PAFD_ENDPOINT endpoint; + PAFD_TRANSPORT_INFO transportInfo; + NTSTATUS status; + AFD_GROUP_TYPE groupType; + + PAGED_CODE( ); + + DEBUG *NewEndpoint = NULL; + + if ( TransportDeviceName != NULL ) { + // + // First, make sure that the transport device name is stored globally + // for AFD. Since there will typically only be a small number of + // transport device names, we store the name strings once globally + // for access by all endpoints. + // + + transportInfo = AfdGetTransportInfo( TransportDeviceName ); + + if ( transportInfo == NULL ) { + return STATUS_INSUFFICIENT_RESOURCES; + } + } + + // + // Validate the incoming group ID, allocate a new one if necessary. + // + + if( !AfdGetGroup( &GroupID, &groupType ) ) { + return STATUS_INVALID_PARAMETER; + } + + // + // Allocate a buffer to hold the endpoint structure. + // + + endpoint = AFD_ALLOCATE_POOL( + NonPagedPool, + sizeof(AFD_ENDPOINT), + AFD_ENDPOINT_POOL_TAG + ); + + if ( endpoint == NULL ) { + if( GroupID != 0 ) { + AfdDereferenceGroup( GroupID ); + } + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory( endpoint, sizeof(AFD_ENDPOINT) ); + + // + // Initialize the reference count to 2--one for the caller's + // reference, one for the active reference. + // + + endpoint->ReferenceCount = 2; + + // + // Initialize the endpoint structure. + // + + if ( TransportDeviceName == NULL ) { + endpoint->Type = AfdBlockTypeHelper; + endpoint->State = AfdEndpointStateInvalid; + endpoint->EndpointType = AfdEndpointTypeUnknown; + } else { + endpoint->Type = AfdBlockTypeEndpoint; + endpoint->State = AfdEndpointStateOpen; + endpoint->TransportInfo = transportInfo; + } + + endpoint->GroupID = GroupID; + endpoint->GroupType = groupType; + + KeInitializeSpinLock( &endpoint->SpinLock ); + +#if REFERENCE_DEBUG + { + PAFD_REFERENCE_DEBUG referenceDebug; + + referenceDebug = AFD_ALLOCATE_POOL( + NonPagedPool, + sizeof(AFD_REFERENCE_DEBUG) * MAX_REFERENCE, + AFD_DEBUG_POOL_TAG + ); + + if ( referenceDebug != NULL ) { + RtlZeroMemory( referenceDebug, sizeof(AFD_REFERENCE_DEBUG) * MAX_REFERENCE ); + } + + endpoint->CurrentReferenceSlot = -1; + endpoint->ReferenceDebug = referenceDebug; + } +#endif + +#if DBG + InitializeListHead( &endpoint->OutstandingIrpListHead ); +#endif + + // + // Remember the process which opened the endpoint. We'll use this to + // charge quota to the process as necessary. Reference the process + // so that it does not go away until we have returned all charged + // quota to the process. + // + + endpoint->OwningProcess = IoGetCurrentProcess( ); + + ObReferenceObject(endpoint->OwningProcess); + + // + // Insert the endpoint on the global list. + // + + AfdInsertNewEndpointInList( endpoint ); + + // + // Return a pointer to the new endpoint to the caller. + // + + IF_DEBUG(ENDPOINT) { + KdPrint(( "AfdAllocateEndpoint: new endpoint at %lx\n", endpoint )); + } + + *NewEndpoint = endpoint; + return STATUS_SUCCESS; + +} // AfdAllocateEndpoint + + +VOID +AfdCloseEndpoint ( + IN PAFD_ENDPOINT Endpoint + ) + +/*++ + +Routine Description: + + Initiates the closing of an AFD endpoint structure. + +Arguments: + + Endpoint - a pointer to the AFD endpoint structure. + +Return Value: + + None. + +--*/ + +{ + PAFD_CONNECTION connection; + + PAGED_CODE( ); + + IF_DEBUG(ENDPOINT) { + KdPrint(( "AfdCloseEndpoint: closing endpoint at %lx\n", Endpoint )); + } + + if ( Endpoint->State == AfdEndpointStateClosing ) { + return; + } + + // + // Set the state of the endpoint to closing and dereference to + // get rid of the active reference. + // + + Endpoint->State = AfdEndpointStateClosing; + + // + // If there is a connection on this endpoint, dereference it here + // rather than in AfdDereferenceEndpoint, because the connection + // has a referenced pointer to the endpoint which must be removed + // before the endpoint can dereference the connection. + // + + connection = AFD_CONNECTION_FROM_ENDPOINT( Endpoint ); + if ( connection != NULL ) { + DEREFERENCE_CONNECTION( Endpoint->Common.VcConnecting.Connection ); + } + + // + // Dereference the endpoint to get rid of the active reference. + // This will result in the endpoint storage being freed as soon + // as all other references go away. + // + + DEREFERENCE_ENDPOINT( Endpoint ); + +} // AfdCloseEndpoint + + +VOID +AfdFreeQueuedConnections ( + IN PAFD_ENDPOINT Endpoint + ) + +/*++ + +Routine Description: + + Frees queued connection objects on a listening AFD endpoint. + +Arguments: + + Endpoint - a pointer to the AFD endpoint structure. + +Return Value: + + None. + +--*/ + +{ + KIRQL oldIrql; + PAFD_CONNECTION connection; + NTSTATUS status; + + ASSERT( Endpoint->Type == AfdBlockTypeVcListening ); + + // + // Free the unaccepted connections. + // + // We must hold AfdSpinLock to call AfdGetUnacceptedConnection, + // but we may not hold it when calling AfdDereferenceConnection. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + while ( (connection = AfdGetUnacceptedConnection( Endpoint )) != NULL ) { + + ASSERT( connection->Endpoint == Endpoint ); + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + AfdAbortConnection( connection ); + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + } + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + // + // Free the returned connections. + // + + while ( (connection = AfdGetReturnedConnection( Endpoint, 0 )) != NULL ) { + + ASSERT( connection->Endpoint == Endpoint ); + AfdAbortConnection( connection ); + + } + + // + // And finally, purge the free connection queue. + // + + while ( (connection = AfdGetFreeConnection( Endpoint )) != NULL ) { + + ASSERT( connection->Endpoint == NULL ); + DEREFERENCE_CONNECTION( connection ); + + } + + return; + +} // AfdFreeQueuedConnections + + +VOID +AfdFreeEndpoint ( + IN PVOID Context + ) + +/*++ + +Routine Description: + + Does the actual work to deallocate an AFD endpoint structure and + associated structures. Note that all other references to the + endpoint structure must be gone before this routine is called, since + it frees the endpoint and assumes that nobody else will be looking + at the endpoint. + +Arguments: + + Context - Actually points to the endpoint's embedded AFD_WORK_ITEM + structure. From this we can determine the endpoint to free. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS status; + PAFD_ENDPOINT endpoint; + PLIST_ENTRY listEntry; + PAFD_CONNECTION connection; + + PAGED_CODE( ); + + ASSERT( Context != NULL ); + + endpoint = CONTAINING_RECORD( + Context, + AFD_ENDPOINT, + WorkItem + ); + + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + ASSERT( endpoint->ReferenceCount == 0 ); + ASSERT( endpoint->State == AfdEndpointStateClosing ); + ASSERT( endpoint->ObReferenceBias == 0 ); + ASSERT( KeGetCurrentIrql( ) == 0 ); + + // + // If this is a listening endpoint, then purge the endpoint of all + // queued connections. + // + + if ( endpoint->Type == AfdBlockTypeVcListening ) { + + AfdFreeQueuedConnections( endpoint ); + + } + + // + // Dereference any group ID associated with this endpoint. + // + + if( endpoint->GroupID != 0 ) { + + AfdDereferenceGroup( endpoint->GroupID ); + + } + + // + // If we set up an owning process for the endpoint, dereference the + // process. + // + + if ( endpoint->OwningProcess != NULL ) { + ObDereferenceObject( endpoint->OwningProcess ); + endpoint->OwningProcess = NULL; + } + + // + // If this is a bufferring datagram endpoint, remove all the + // bufferred datagrams from the endpoint's list and free them. + // + + if ( endpoint->Type == AfdBlockTypeDatagram && + endpoint->ReceiveDatagramBufferListHead.Flink != NULL ) { + + while ( !IsListEmpty( &endpoint->ReceiveDatagramBufferListHead ) ) { + + PAFD_BUFFER afdBuffer; + + listEntry = RemoveHeadList( &endpoint->ReceiveDatagramBufferListHead ); + afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry ); + + AfdReturnBuffer( afdBuffer ); + } + } + + // + // Close and dereference the TDI address object on the endpoint, if + // any. + // + + if ( endpoint->AddressFileObject != NULL ) { + ObDereferenceObject( endpoint->AddressFileObject ); + endpoint->AddressFileObject = NULL; + AfdRecordAddrDeref(); + } + + if ( endpoint->AddressHandle != NULL ) { + KeAttachProcess( AfdSystemProcess ); + status = ZwClose( endpoint->AddressHandle ); + ASSERT( NT_SUCCESS(status) ); + KeDetachProcess( ); + endpoint->AddressHandle = NULL; + AfdRecordAddrClosed(); + } + + // + // Remove the endpoint from the global list. Do this before any + // deallocations to prevent someone else from seeing an endpoint in + // an invalid state. + // + + AfdRemoveEndpointFromList( endpoint ); + + // + // Dereference the listening endpoint on the endpoint, if + // any. + // + + if ( endpoint->Type == AfdBlockTypeVcConnecting && + endpoint->Common.VcConnecting.ListenEndpoint != NULL ) { + ASSERT( endpoint->Common.VcConnecting.ListenEndpoint->Type == AfdBlockTypeVcListening ); + DEREFERENCE_ENDPOINT( endpoint->Common.VcConnecting.ListenEndpoint ); + endpoint->Common.VcConnecting.ListenEndpoint = NULL; + } + + // + // Free local and remote address buffers. + // + + if ( endpoint->LocalAddress != NULL ) { + AFD_FREE_POOL( + endpoint->LocalAddress, + AFD_LOCAL_ADDRESS_POOL_TAG + ); + endpoint->LocalAddress = NULL; + } + + if ( endpoint->Type == AfdBlockTypeDatagram && + endpoint->Common.Datagram.RemoteAddress != NULL ) { + AFD_FREE_POOL( + endpoint->Common.Datagram.RemoteAddress, + AFD_REMOTE_ADDRESS_POOL_TAG + ); + endpoint->Common.Datagram.RemoteAddress = NULL; + } + + // + // Free context and connect data buffers. + // + + if ( endpoint->Context != NULL ) { + + AFD_FREE_POOL( + endpoint->Context, + AFD_CONTEXT_POOL_TAG + ); + endpoint->Context = NULL; + + } + + if ( endpoint->ConnectDataBuffers != NULL ) { + AfdFreeConnectDataBuffers( endpoint->ConnectDataBuffers ); + } + + // + // If there's an active EventSelect() on this endpoint, dereference + // the associated event object. + // + + if( endpoint->EventObject != NULL ) { + ObDereferenceObject( endpoint->EventObject ); + endpoint->EventObject = NULL; + } + + // + // Free any reusable TransmitFile info attached to the endpoint. + // + + if( endpoint->TransmitInfo != NULL ) { + + AFD_FREE_POOL( + endpoint->TransmitInfo, + AFD_TRANSMIT_INFO_POOL_TAG + ); + + } + + // + // Free the space that holds the endpoint itself. + // + + IF_DEBUG(ENDPOINT) { + KdPrint(( "AfdFreeEndpoint: freeing endpoint at %lx\n", endpoint )); + } + + endpoint->Type = 0xAFDE; + +#if REFERENCE_DEBUG + if ( endpoint->ReferenceDebug != NULL ) { + AFD_FREE_POOL( + endpoint->ReferenceDebug, + AFD_DEBUG_POOL_TAG + ); + } +#endif + + // + // Free the pool used for the endpoint itself. + // + + AFD_FREE_POOL( + endpoint, + AFD_ENDPOINT_POOL_TAG + ); + +} // AfdFreeEndpoint + + +#if REFERENCE_DEBUG +VOID +AfdDereferenceEndpoint ( + IN PAFD_ENDPOINT Endpoint, + IN PVOID Info1, + IN PVOID Info2 + ) +#else +VOID +AfdDereferenceEndpoint ( + IN PAFD_ENDPOINT Endpoint + ) +#endif + +/*++ + +Routine Description: + + Dereferences an AFD endpoint and calls the routine to free it if + appropriate. + +Arguments: + + Endpoint - a pointer to the AFD endpoint structure. + +Return Value: + + None. + +--*/ + +{ + LONG result; + KIRQL oldIrql; + +#if REFERENCE_DEBUG + PAFD_REFERENCE_DEBUG slot; + LONG newSlot; +#endif + +#if REFERENCE_DEBUG + IF_DEBUG(ENDPOINT) { + KdPrint(( "AfdDereferenceEndpoint: endpoint at %lx, new refcnt %ld\n", + Endpoint, Endpoint->ReferenceCount-1 )); + } + + ASSERT( IS_AFD_ENDPOINT_TYPE( Endpoint ) ); + ASSERT( Endpoint->ReferenceCount > 0 ); + ASSERT( Endpoint->ReferenceCount != 0xDAADF00D ); + + if ( Endpoint->ReferenceDebug != NULL ) { + newSlot = InterlockedIncrement( &Endpoint->CurrentReferenceSlot ); + slot = &Endpoint->ReferenceDebug[newSlot % MAX_REFERENCE]; + + slot->Action = 0xFFFFFFFF; + slot->NewCount = Endpoint->ReferenceCount - 1; + slot->Info1 = Info1; + slot->Info2 = Info2; + } + +#endif + + // + // We must hold AfdSpinLock while doing the dereference and check + // for free. This is because some code makes the assumption that + // the endpoint structure will not go away while AfdSpinLock is + // held, and that code references the endpoint before releasing + // AfdSpinLock. If we did the InterlockedDecrement() without the + // lock held, our count may go to zero, that code may reference the + // endpoint, and then a double free might occur. + // + // It is still valuable to use the interlocked routines for + // increment and decrement of structures because it allows us to + // avoid having to hold the spin lock for a reference. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + // + // Decrement the reference count; if it is 0, free the endpoint. + // + + result = InterlockedDecrement( &Endpoint->ReferenceCount ); + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + if ( result == 0 ) { + + ASSERT( Endpoint->State == AfdEndpointStateClosing ); + + // + // We're going to do this by queueing a request to an executive + // worker thread. We do this for several reasons: to ensure + // that we're at IRQL 0 so we can free pageable memory, and to + // ensure that we're in a legitimate context for a close + // operation. + // + + AfdQueueWorkItem( + AfdFreeEndpoint, + &Endpoint->WorkItem + ); + + } + +} // AfdDereferenceEndpoint + +#if REFERENCE_DEBUG + +VOID +AfdReferenceEndpoint ( + IN PAFD_ENDPOINT Endpoint, + IN PVOID Info1, + IN PVOID Info2 + ) + +/*++ + +Routine Description: + + References an AFD endpoint. + +Arguments: + + Endpoint - a pointer to the AFD endpoint structure. + +Return Value: + + None. + +--*/ + +{ + + PAFD_REFERENCE_DEBUG slot; + LONG newSlot; + LONG result; + + ASSERT( Endpoint->ReferenceCount > 0 ); + + if( Endpoint->ReferenceDebug != NULL ) { + newSlot = InterlockedIncrement( &Endpoint->CurrentReferenceSlot ); + slot = &Endpoint->ReferenceDebug[newSlot % MAX_REFERENCE]; + + slot->Action = 1; + slot->NewCount = Endpoint->ReferenceCount + 1; + slot->Info1 = Info1; + slot->Info2 = Info2; + } + + IF_DEBUG(ENDPOINT) { + KdPrint(( "AfdReferenceEndpoint: endpoint at %lx, new refcnt %ld\n", + Endpoint, Endpoint->ReferenceCount+1 )); + } + + ASSERT( Endpoint->ReferenceCount < 0xFFFF ); + + result = InterlockedIncrement( &Endpoint->ReferenceCount ); + +} // AfdReferenceEndpoint +#endif + + +VOID +AfdRefreshEndpoint ( + IN PAFD_ENDPOINT Endpoint + ) + +/*++ + +Routine Description: + + Prepares an AFD endpoint structure to be reused. All other + references to the endpoint must be freed before this routine is + called, since this routine assumes that nobody will access the old + information in the endpoint structure. + +Arguments: + + Endpoint - a pointer to the AFD endpoint structure. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS status; + + PAGED_CODE( ); + + // + // This routine must be called at low IRQL. At initial + // implementation, it is only called through AfdFreeConnection in an + // executive worker thread. + // + + ASSERT( Endpoint->Type == AfdBlockTypeVcConnecting ); + ASSERT( Endpoint->Common.VcConnecting.Connection == NULL ); + ASSERT( KeGetCurrentIrql( ) < DISPATCH_LEVEL ); + + // + // Dereference the listening endpoint and its address object. + // + + if ( Endpoint->Common.VcConnecting.ListenEndpoint != NULL ) { + DEREFERENCE_ENDPOINT( Endpoint->Common.VcConnecting.ListenEndpoint ); + Endpoint->Common.VcConnecting.ListenEndpoint = NULL; + } + + // + // Close and dereference the TDI address object on the endpoint, if + // any. + // + + if ( Endpoint->AddressFileObject != NULL ) { + ObDereferenceObject( Endpoint->AddressFileObject ); + Endpoint->AddressFileObject = NULL; + AfdRecordAddrDeref(); + } + + if ( Endpoint->AddressHandle != NULL ) { + KeAttachProcess( AfdSystemProcess ); + status = ZwClose( Endpoint->AddressHandle ); + ASSERT( NT_SUCCESS(status) ); + KeDetachProcess( ); + Endpoint->AddressHandle = NULL; + AfdRecordAddrClosed(); + } + + // + // Reinitialize the endpoint structure. + // + + Endpoint->Type = AfdBlockTypeEndpoint; + Endpoint->State = AfdEndpointStateOpen; + Endpoint->DisconnectMode = 0; + Endpoint->PollCalled = FALSE; + + return; + +} // AfdRefreshEndpoint + + +PAFD_TRANSPORT_INFO +AfdGetTransportInfo ( + IN PUNICODE_STRING TransportDeviceName + ) + +/*++ + +Routine Description: + + Returns a transport information structure corresponding to the + specified TDI transport provider. Each unique transport string gets + a single provider structure, so that multiple endpoints for the same + transport share the same transport information structure. + +Arguments: + + TransportDeviceName - the name of the TDI transport provider. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY listEntry; + PAFD_TRANSPORT_INFO transportInfo; + ULONG structureLength; + NTSTATUS status; + HANDLE controlChannel; + OBJECT_ATTRIBUTES objectAttributes; + IO_STATUS_BLOCK iosb; + TDI_REQUEST_KERNEL_QUERY_INFORMATION kernelQueryInfo; + + PAGED_CODE( ); + + // + // First walk the list of transport device names looking for an + // identical name. + // + + ExAcquireResourceExclusive( AfdResource, TRUE ); + + for ( listEntry = AfdTransportInfoListHead.Flink; + listEntry != &AfdTransportInfoListHead; + listEntry = listEntry->Flink ) { + + transportInfo = CONTAINING_RECORD( + listEntry, + AFD_TRANSPORT_INFO, + TransportInfoListEntry + ); + + if ( RtlCompareUnicodeString( + &transportInfo->TransportDeviceName, + TransportDeviceName, + TRUE ) == 0 ) { + + // + // We found an exact match. Return a pointer to the + // UNICODE_STRING field of this structure. + // + + ExReleaseResource( AfdResource ); + return transportInfo; + } + } + + // + // There were no matches, so this is a new transport device name + // which we've never seen before. Allocate a structure to hold the + // new name and place the name on the global list. + // + + + structureLength = sizeof(AFD_TRANSPORT_INFO) + + TransportDeviceName->Length + sizeof(WCHAR); + + transportInfo = AFD_ALLOCATE_POOL( + NonPagedPool, + structureLength, + AFD_TRANSPORT_INFO_POOL_TAG + ); + + if ( transportInfo == NULL ) { + ExReleaseResource( AfdResource ); + return NULL; + } + + // + // Set up the IRP stack location information to query the TDI + // provider information. + // + + kernelQueryInfo.QueryType = TDI_QUERY_PROVIDER_INFORMATION; + kernelQueryInfo.RequestConnectionInformation = NULL; + + // + // Open a control channel to the TDI provider. + // + + InitializeObjectAttributes( + &objectAttributes, + TransportDeviceName, + OBJ_CASE_INSENSITIVE, // attributes + NULL, + NULL + ); + + status = ZwCreateFile( + &controlChannel, + GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, + &objectAttributes, + &iosb, // returned status information. + 0, // block size (unused). + 0, // file attributes. + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_CREATE, // create disposition. + 0, // create options. + NULL, + 0 + ); + if ( !NT_SUCCESS(status) ) { + ExReleaseResource( AfdResource ); + AFD_FREE_POOL( + transportInfo, + AFD_TRANSPORT_INFO_POOL_TAG + ); + return NULL; + } + + // + // Get the TDI provider information for the transport. + // + + status = AfdIssueDeviceControl( + controlChannel, + NULL, + &kernelQueryInfo, + sizeof(kernelQueryInfo), + &transportInfo->ProviderInfo, + sizeof(transportInfo->ProviderInfo), + TDI_QUERY_INFORMATION + ); + if ( !NT_SUCCESS(status) ) { + ExReleaseResource( AfdResource ); + AFD_FREE_POOL( + transportInfo, + AFD_TRANSPORT_INFO_POOL_TAG + ); + ZwClose( controlChannel ); + return NULL; + } + + // + // Fill in the transport device name. + // + + transportInfo->TransportDeviceName.MaximumLength = + TransportDeviceName->Length + sizeof(WCHAR); + transportInfo->TransportDeviceName.Buffer = + (PWSTR)(transportInfo + 1); + + RtlCopyUnicodeString( + &transportInfo->TransportDeviceName, + TransportDeviceName + ); + + // + // Place the transport info structure on the global list. + // + + InsertTailList( + &AfdTransportInfoListHead, + &transportInfo->TransportInfoListEntry + ); + + // + // Return the transport info structure to the caller. + // + + ExReleaseResource( AfdResource ); + ZwClose( controlChannel ); + + return transportInfo; + +} // AfdGetTransportInfo diff --git a/private/ntos/afd/buffer.c b/private/ntos/afd/buffer.c new file mode 100644 index 000000000..11d672360 --- /dev/null +++ b/private/ntos/afd/buffer.c @@ -0,0 +1,651 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + buffer.c + +Abstract: + + This module contains routines for handling non-bufferring TDI + providers. The AFD interface assumes that bufferring will be done + below AFD; if the TDI provider doesn't buffer, then AFD must. + +Author: + + David Treadwell (davidtr) 21-Feb-1992 + +Revision History: + +--*/ + +#include "afdp.h" + +VOID +AfdInitializeBuffer ( + IN PAFD_BUFFER AfdBuffer, + IN CLONG BufferDataSize, + IN CLONG AddressSize + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGEAFD, AfdAllocateBuffer ) +#pragma alloc_text( PAGEAFD, AfdCalculateBufferSize ) +#pragma alloc_text( PAGEAFD, AfdInitializeBuffer ) +#pragma alloc_text( PAGEAFD, AfdGetBuffer ) +#pragma alloc_text( PAGEAFD, AfdGetBufferChain ) +#pragma alloc_text( PAGEAFD, AfdReturnBuffer ) +#pragma alloc_text( PAGEAFD, AfdReturnBufferChain ) +#if DBG +#pragma alloc_text( PAGEAFD, AfdFreeBufferPool ) +#endif +#endif + + +PVOID +AfdAllocateBuffer ( + IN POOL_TYPE PoolType, + IN ULONG NumberOfBytes, + IN ULONG Tag + ) + +/*++ + +Routine Description: + + Used by the lookaside list allocation function to allocate a new + AFD buffer structure. The returned structure will be fully + initialized. + +Arguments: + + PoolType - passed to ExAllocatePoolWithTag. + + NumberOfBytes - the number of bytes required for the data buffer + portion of the AFD buffer. + + Tag - passed to ExAllocatePoolWithTag. + +Return Value: + + PVOID - a fully initialized PAFD_BUFFER, or NULL if the allocation + attempt fails. + +--*/ + +{ + PAFD_BUFFER afdBuffer; + ULONG bytesRequired; + + // + // The requested length must be the same as one of the standard + // AFD buffer sizes. + // + + ASSERT( NumberOfBytes == AfdSmallBufferSize || + NumberOfBytes == AfdMediumBufferSize || + NumberOfBytes == AfdLargeBufferSize ); + + // + // Determine how much data we'll actually need for the buffer. + // + + bytesRequired = AfdCalculateBufferSize( + NumberOfBytes, + AfdStandardAddressLength + ); + + // + // Get nonpaged pool for the buffer. + // + + afdBuffer = AFD_ALLOCATE_POOL( PoolType, bytesRequired, Tag ); + if ( afdBuffer == NULL ) { + return NULL; + } + + // + // Initialize the buffer and return a pointer to it. + // + + AfdInitializeBuffer( afdBuffer, NumberOfBytes, AfdStandardAddressLength ); + + return afdBuffer; + + +} // AfdAllocateBuffer + + +CLONG +AfdCalculateBufferSize ( + IN CLONG BufferDataSize, + IN CLONG AddressSize + ) + +/*++ + +Routine Description: + + Determines the size of an AFD buffer structure given the amount of + data that the buffer contains. + +Arguments: + + BufferDataSize - data length of the buffer. + + AddressSize - length of address structure for the buffer. + +Return Value: + + Number of bytes needed for an AFD_BUFFER structure for data of + this size. + +--*/ + +{ + CLONG irpSize; + CLONG mdlSize; + CLONG bufferSize; + + ASSERT( BufferDataSize != 0 ); + ASSERT( AfdCacheLineSize < 100 ); + + // + // Determine the sizes of the various components of an AFD_BUFFER + // structure. Note that these are all worst-case calculations-- + // actual sizes of the MDL and the buffer may be smaller. + // + + irpSize = IoSizeOfIrp( AfdIrpStackSize ) + 8; + bufferSize = BufferDataSize + AfdCacheLineSize; + mdlSize = MmSizeOfMdl( (PVOID)(PAGE_SIZE-1), bufferSize ); + + return ( (sizeof(AFD_BUFFER) + irpSize + mdlSize + + AddressSize + bufferSize + 3) & ~3); + +} // AfdCalculateBufferSize + + +PAFD_BUFFER +AfdGetBuffer ( + IN CLONG BufferDataSize, + IN CLONG AddressSize + ) + +/*++ + +Routine Description: + + Obtains a buffer of the appropriate size for the caller. Uses + the preallocated buffers if possible, or else allocates a new buffer + structure if required. + +Arguments: + + BufferDataSize - the size of the data buffer that goes along with the + buffer structure. + + AddressSize - size of the address field required for the buffer. + +Return Value: + + PAFD_BUFFER - a pointer to an AFD_BUFFER structure, or NULL if one + was not available or could not be allocated. + +--*/ + +{ + PAFD_BUFFER afdBuffer; + CLONG bufferSize; + PLIST_ENTRY listEntry; + PNPAGED_LOOKASIDE_LIST lookasideList; + + // + // If possible, allocate the buffer from one of the lookaside lists. + // + + if ( AddressSize <= AfdStandardAddressLength && + BufferDataSize <= AfdLargeBufferSize ) { + + if ( BufferDataSize <= AfdSmallBufferSize ) { + + lookasideList = &AfdLookasideLists->SmallBufferList; + BufferDataSize = AfdSmallBufferSize; + + } else if ( BufferDataSize <= AfdMediumBufferSize ) { + + lookasideList = &AfdLookasideLists->MediumBufferList; + BufferDataSize = AfdMediumBufferSize; + + } else { + + lookasideList = &AfdLookasideLists->LargeBufferList; + BufferDataSize = AfdLargeBufferSize; + } + + afdBuffer = ExAllocateFromNPagedLookasideList( lookasideList ); +#if DBG + if ( afdBuffer != NULL ) { + + RtlGetCallersAddress( + &afdBuffer->Caller, + &afdBuffer->CallersCaller + ); + } +#endif + + return afdBuffer; + + } + + // + // Couldn't find an appropriate buffer that was preallocated. + // Allocate one manually. If the buffer size requested was + // zero bytes, give them four bytes. This is because some of + // the routines like MmSizeOfMdl() cannot handle getting passed + // in a length of zero. + // + // !!! It would be good to ROUND_TO_PAGES for this allocation + // if appropriate, then use entire buffer size. + // + + if ( BufferDataSize == 0 ) { + BufferDataSize = sizeof(ULONG); + } + + bufferSize = AfdCalculateBufferSize( BufferDataSize, AddressSize ); + + afdBuffer = AFD_ALLOCATE_POOL( + NonPagedPool, + bufferSize, + AFD_DATA_BUFFER_POOL_TAG + ); + + if ( afdBuffer == NULL ) { + return NULL; + } + + // + // Initialize the AFD buffer structure and return it. + // + + AfdInitializeBuffer( afdBuffer, BufferDataSize, AddressSize ); + + return afdBuffer; + +} // AfdGetBuffer + + +PAFD_BUFFER +AfdGetBufferChain ( + IN CLONG BufferDataSize + ) +{ + PAFD_BUFFER afdBuffer; + PAFD_BUFFER bufferChain; + PAFD_BUFFER *bufferChainTarget; + PMDL mdlChain; + PMDL *mdlChainTarget; + CLONG currentBufferSize; +#if DBG + CLONG totalChainLength = BufferDataSize; +#endif + + // + // Sanity check. + // + + ASSERT( BufferDataSize > AfdBufferLengthForOnePage ); + + // + // Setup so we know how to cleanup. + // + + bufferChain = NULL; + mdlChain = NULL; + bufferChainTarget = &bufferChain; + mdlChainTarget = &mdlChain; + + // + // Loop, acquiring & chaining the buffers. + // + + while ( BufferDataSize > 0 ) { + + // + // Acquire a new buffer. If this fails, we're toast. + // + + currentBufferSize = max( BufferDataSize, AfdBufferLengthForOnePage ); + + afdBuffer = AfdGetBuffer( currentBufferSize, 0 ); + + if ( afdBuffer == NULL ) { + + break; + + } + + // + // Chain it on. + // + + *bufferChainTarget = afdBuffer; + bufferChainTarget = &afdBuffer->NextBuffer; + + *mdlChainTarget = afdBuffer->Mdl; + mdlChainTarget = &afdBuffer->Mdl->Next; + + BufferDataSize -= currentBufferSize; + + } + + if ( BufferDataSize == 0 ) { + + ASSERT( bufferChain != NULL ); + ASSERT( afdBuffer != NULL ); + ASSERT( currentBufferSize > 0 ); + ASSERT( currentBufferSize <= AfdBufferLengthForOnePage ); + + // + // Set the byte count in the final MDL in the chain. + // + + afdBuffer->Mdl->ByteCount = currentBufferSize; + SET_CHAIN_LENGTH( afdBuffer, totalChainLength ); + + return bufferChain; + + } + + // + // Error, time to cleanup. + // + + while( bufferChain != NULL ) { + + afdBuffer = bufferChain->NextBuffer; + + bufferChain->Mdl->Next = NULL; + bufferChain->NextBuffer = NULL; + RESET_CHAIN_LENGTH( bufferChain ); + AfdReturnBuffer( bufferChain ); + + bufferChain = afdBuffer; + + } + + return NULL; + +} // AfdGetBufferChain + + +VOID +AfdReturnBuffer ( + IN PAFD_BUFFER AfdBuffer + ) + +/*++ + +Routine Description: + + Returns an AFD buffer to the appropriate global list, or frees + it if necessary. + +Arguments: + + AfdBuffer - points to the AFD_BUFFER structure to return or free. + +Return Value: + + None. + +--*/ + +{ + PNPAGED_LOOKASIDE_LIST lookasideList; + + // + // Most of the AFD buffer must be zeroed when returning the buffer. + // + + ASSERT( AfdBuffer->DataOffset == 0 ); + ASSERT( !AfdBuffer->ExpeditedData ); + ASSERT( AfdBuffer->TdiInputInfo.UserDataLength == 0 ); + ASSERT( AfdBuffer->TdiInputInfo.UserData == NULL ); + ASSERT( AfdBuffer->TdiInputInfo.OptionsLength == 0 ); + ASSERT( AfdBuffer->TdiInputInfo.Options == NULL ); + ASSERT( AfdBuffer->TdiInputInfo.RemoteAddressLength == 0 ); + ASSERT( AfdBuffer->TdiInputInfo.RemoteAddress == NULL ); + ASSERT( AfdBuffer->TdiOutputInfo.UserDataLength == 0 ); + ASSERT( AfdBuffer->TdiOutputInfo.UserData == NULL ); + ASSERT( AfdBuffer->TdiOutputInfo.OptionsLength == 0 ); + ASSERT( AfdBuffer->TdiOutputInfo.Options == NULL ); + ASSERT( AfdBuffer->TdiOutputInfo.RemoteAddressLength == 0 ); + ASSERT( AfdBuffer->TdiOutputInfo.RemoteAddress == NULL ); + + ASSERT( AfdBuffer->Mdl->ByteCount == AfdBuffer->BufferLength ); + ASSERT( AfdBuffer->Mdl->Next == NULL ); + ASSERT( AfdBuffer->FileObject == NULL ); + ASSERT( AfdBuffer->NextBuffer == NULL ); + ASSERT( AfdBuffer->TotalChainLength == AfdBuffer->BufferLength ); + +#if DBG + AfdBuffer->Caller = NULL; + AfdBuffer->CallersCaller = NULL; +#endif + + // + // If appropriate, return the buffer to one of the AFD buffer + // lookaside lists. + // + + if ( AfdBuffer->AllocatedAddressLength == AfdStandardAddressLength && + AfdBuffer->BufferLength <= AfdLargeBufferSize ) { + + if ( AfdBuffer->BufferLength == AfdSmallBufferSize ) { + + lookasideList = &AfdLookasideLists->SmallBufferList; + + } else if ( AfdBuffer->BufferLength == AfdMediumBufferSize ) { + + lookasideList = &AfdLookasideLists->MediumBufferList; + + } else { + + ASSERT( AfdBuffer->BufferLength == AfdLargeBufferSize ); + lookasideList = &AfdLookasideLists->LargeBufferList; + } + + ExFreeToNPagedLookasideList( lookasideList, AfdBuffer ); + + return; + } + + // + // The buffer was not from a lookaside list allocation, so just free + // the pool we used for it. + // + + AFD_FREE_POOL( + AfdBuffer, + AFD_DATA_BUFFER_POOL_TAG + ); + + return; + +} // AfdReturnBuffer + +VOID +AfdReturnBufferChain ( + IN PAFD_BUFFER AfdBuffer + ) + +/*++ + +Routine Description: + + Returns an AFD buffer chain to the appropriate global list, or frees + it if necessary. + +Arguments: + + AfdBuffer - points to the AFD_BUFFER structure to return or free. + +Return Value: + + None. + +--*/ + +{ + PAFD_BUFFER nextBuffer; + + while ( AfdBuffer != NULL ) { + + nextBuffer = AfdBuffer->NextBuffer; + + AfdBuffer->NextBuffer = NULL; + AfdBuffer->Mdl->Next = NULL; + AfdBuffer->Mdl->ByteCount = AfdBuffer->BufferLength; + RESET_CHAIN_LENGTH( AfdBuffer ); + AfdReturnBuffer( AfdBuffer ); + + AfdBuffer = nextBuffer; + + } + +} // AfdReturnBufferChain + + +VOID +AfdInitializeBuffer ( + IN PAFD_BUFFER AfdBuffer, + IN CLONG BufferDataSize, + IN CLONG AddressSize + ) + +/*++ + +Routine Description: + + Initializes an AFD buffer. Sets up fields in the actual AFD_BUFFER + structure and initializes the IRP and MDL associated with the + buffer. This routine assumes that the caller has properly allocated + sufficient space for all this. + +Arguments: + + AfdBuffer - points to the AFD_BUFFER structure to initialize. + + BufferDataSize - the size of the data buffer that goes along with the + buffer structure. + + AddressSize - the size of data allocated for the address buffer. + + ListHead - the global list this buffer belongs to, or NULL if it + doesn't belong on any list. This routine does NOT place the + buffer structure on the list. + +Return Value: + + None. + +--*/ + +{ + CLONG irpSize; + CLONG mdlSize; + + // + // Initialize the IRP pointer and the IRP itself. + // + + AfdBuffer->Irp = (PIRP)(( ((ULONG)(AfdBuffer + 1)) + 7) & ~7); + irpSize = IoSizeOfIrp( AfdIrpStackSize ); + + IoInitializeIrp( AfdBuffer->Irp, (USHORT)irpSize, AfdIrpStackSize ); + + // + // Set up the MDL pointer but don't build it yet. We have to wait + // until after the data buffer is built to build the MDL. + // + + mdlSize = MmSizeOfMdl( (PVOID)(PAGE_SIZE-1), BufferDataSize ); + + AfdBuffer->Mdl = (PMDL)( (PCHAR)AfdBuffer->Irp + irpSize ); + + // + // Set up the address buffer pointer. + // + + AfdBuffer->SourceAddress = (PCHAR)AfdBuffer->Mdl + mdlSize; + AfdBuffer->AllocatedAddressLength = (USHORT)AddressSize; + + // + // Initialize the TDI information structures. + // + + RtlZeroMemory( &AfdBuffer->TdiInputInfo, sizeof(AfdBuffer->TdiInputInfo) ); + RtlZeroMemory( &AfdBuffer->TdiOutputInfo, sizeof(AfdBuffer->TdiOutputInfo) ); + + // + // Set up the data buffer pointer and length. Note that the buffer + // MUST begin on a cache line boundary so that we can use the fast + // copy routines like RtlCopyMemory on the buffer. + // + + AfdBuffer->Buffer = (PVOID) + ( ( (ULONG)AfdBuffer->SourceAddress + AddressSize + + AfdCacheLineSize - 1 ) & ~(AfdCacheLineSize - 1) ); + + AfdBuffer->BufferLength = BufferDataSize; + RESET_CHAIN_LENGTH( AfdBuffer ); + + // + // Now build the MDL and set up a pointer to the MDL in the IRP. + // + + MmInitializeMdl( AfdBuffer->Mdl, AfdBuffer->Buffer, BufferDataSize ); + MmBuildMdlForNonPagedPool( AfdBuffer->Mdl ); + + AfdBuffer->Irp->MdlAddress = AfdBuffer->Mdl; + AfdBuffer->DataOffset = 0; + AfdBuffer->ExpeditedData = FALSE; + AfdBuffer->PartialMessage = FALSE; + AfdBuffer->FileObject = NULL; + + // + // By default, buffers are not part of a chain. + // + + AfdBuffer->NextBuffer = NULL; + + +#if DBG + AfdBuffer->BufferListEntry.Flink = (PVOID)0xE0E1E2E3; + AfdBuffer->BufferListEntry.Blink = (PVOID)0xE4E5E6E7; + AfdBuffer->Caller = NULL; + AfdBuffer->CallersCaller = NULL; +#endif + +} // AfdInitializeBuffer + + +#if DBG +VOID +NTAPI +AfdFreeBufferPool( + IN PVOID Block + ) +{ + + AFD_FREE_POOL( + Block, + AFD_DATA_BUFFER_POOL_TAG + ); + +} // AfdFreeBufferPool +#endif + diff --git a/private/ntos/afd/close.c b/private/ntos/afd/close.c new file mode 100644 index 000000000..0845c4e6d --- /dev/null +++ b/private/ntos/afd/close.c @@ -0,0 +1,488 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + close.c + +Abstract: + + This module contains code for cleanup and close IRPs. + +Author: + + David Treadwell (davidtr) 18-Mar-1992 + +Revision History: + +--*/ + +#include "afdp.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, AfdClose ) +#pragma alloc_text( PAGEAFD, AfdCleanup ) +#endif + + +NTSTATUS +AfdCleanup ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This is the routine that handles Cleanup IRPs in AFD. + +Arguments: + + Irp - Pointer to I/O request packet. + + IrpSp - pointer to the IO stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + NTSTATUS status; + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + KIRQL oldIrql1, oldIrql2; + KIRQL cancelIrql; + PLIST_ENTRY listEntry; + LARGE_INTEGER processExitTime; + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + IF_DEBUG(OPEN_CLOSE) { + KdPrint(( + "AfdCleanup: cleanup on file object %lx, endpoint %lx, connection %lx\n", + IrpSp->FileObject, + endpoint, + AFD_CONNECTION_FROM_ENDPOINT( endpoint ) + )); + } + + // + // Get the process exit time while still at low IRQL. + // + + processExitTime = PsGetProcessExitTime( ); + + // + // Indicate that there was a local close on this endpoint. If there + // are any outstanding polls on this endpoint, they will be + // completed now. + // + AfdIndicatePollEvent( + endpoint, + AFD_POLL_LOCAL_CLOSE_BIT, + STATUS_SUCCESS + ); + + // + // Remember that the endpoint has been cleaned up. This is important + // because it allows AfdRestartAccept to know that the endpoint has + // been cleaned up and that it should toss the connection. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql1 ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql2 ); + + ASSERT( endpoint->EndpointCleanedUp == FALSE ); + endpoint->EndpointCleanedUp = TRUE; + + connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint ); + ASSERT( connection == NULL || connection->Type == AfdBlockTypeConnection ); + + // + // Complete any outstanding wait for listen IRPs on the endpoint. + // + + if ( endpoint->Type == AfdBlockTypeVcListening ) { + + while ( !IsListEmpty( &endpoint->Common.VcListening.ListeningIrpListHead ) ) { + + PIRP waitForListenIrp; + + listEntry = RemoveHeadList( &endpoint->Common.VcListening.ListeningIrpListHead ); + waitForListenIrp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + + // + // Release the AFD spin lock so that we can complete the + // wait for listen IRP. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); + AfdReleaseSpinLock( &AfdSpinLock, oldIrql1 ); + + // + // Cancel the IRP. + // + + waitForListenIrp->IoStatus.Status = STATUS_CANCELLED; + waitForListenIrp->IoStatus.Information = 0; + + // + // Reset the cancel routine in the IRP. + // + + IoAcquireCancelSpinLock( &cancelIrql ); + IoSetCancelRoutine( waitForListenIrp, NULL ); + IoReleaseCancelSpinLock( cancelIrql ); + + IoCompleteRequest( waitForListenIrp, AfdPriorityBoost ); + + // + // Reacquire the AFD spin lock for the next pass through the + // loop. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql1 ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql2 ); + } + + // + // Free all queued (free, unaccepted, and returned) connections + // on the endpoint. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); + AfdReleaseSpinLock( &AfdSpinLock, oldIrql1 ); + AfdFreeQueuedConnections( endpoint ); + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql1 ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql2 ); + endpoint->Common.VcListening.FailedConnectionAdds = 0; + } + + UPDATE_CONN( connection, 0 ); + + // + // If this is a connected non-datagram socket and the send side has + // not been disconnected and there is no outstanding data to be + // received, begin a graceful disconnect on the connection. If there + // is unreceived data out outstanding IO, abort the connection. + // + + if ( endpoint->State == AfdEndpointStateConnected && connection != NULL + + && + + !IS_DGRAM_ENDPOINT(endpoint) + + && + + ( (endpoint->DisconnectMode & AFD_ABORTIVE_DISCONNECT) == 0) + + && + + ( (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND) == 0 || + ( !endpoint->TdiBufferring && + connection->Common.NonBufferring.ReceiveBytesInTransport > 0 ) ) + + && + + !connection->AbortIndicated ) { + + ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ); + + if ( IS_DATA_ON_CONNECTION( connection ) + + || + + IS_EXPEDITED_DATA_ON_CONNECTION( connection ) + + || + + processExitTime.QuadPart != 0 + + || + + endpoint->OutstandingIrpCount != 0 + + || + + ( !endpoint->TdiBufferring && + (!IsListEmpty( &connection->VcReceiveIrpListHead ) || + !IsListEmpty( &connection->VcSendIrpListHead )) ) + + ) { + +#if DBG + if ( IS_DATA_ON_CONNECTION( connection ) ) { + KdPrint(( "AfdCleanup: unrecv'd data on endp %lx, aborting. " + "%ld ind, %ld taken, %ld out\n", + endpoint, + connection->Common.Bufferring.ReceiveBytesIndicated, + connection->Common.Bufferring.ReceiveBytesTaken, + connection->Common.Bufferring.ReceiveBytesOutstanding )); + } + + if ( IS_EXPEDITED_DATA_ON_CONNECTION( connection ) ) { + KdPrint(( "AfdCleanup: unrecv'd exp data on endp %lx, aborting. " + "%ld ind, %ld taken, %ld out\n", + endpoint, + connection->Common.Bufferring.ReceiveExpeditedBytesIndicated, + connection->Common.Bufferring.ReceiveExpeditedBytesTaken, + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding )); + } + + if ( processExitTime.QuadPart != 0 ) { + KdPrint(( "AfdCleanup: process exiting w/o closesocket, " + "aborting endp %lx\n", endpoint )); + } + + if ( endpoint->OutstandingIrpCount != 0 ) { + KdPrint(( "AfdCleanup: 3 IRPs outstanding on endpoint %lx, " + "aborting.\n", endpoint )); + } +#endif + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); + AfdReleaseSpinLock( &AfdSpinLock, oldIrql1 ); + + (VOID)AfdBeginAbort( connection ); + + } else { + + endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_RECEIVE; + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); + AfdReleaseSpinLock( &AfdSpinLock, oldIrql1 ); + + (VOID)AfdBeginDisconnect( endpoint, NULL, NULL ); + } + + } else { + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); + AfdReleaseSpinLock( &AfdSpinLock, oldIrql1 ); + } + + // + // If this a datagram endpoint, cancel all IRPs and free any buffers + // of data. Note that if the state of the endpoint is just "open" + // (not bound, etc.) then we can't have any pended IRPs or datagrams + // on the endpoint. Also, the lists of IRPs and datagrams may not + // yet been initialized if the state is just open. + // + + if ( endpoint->State != AfdEndpointStateOpen && + endpoint->Type == AfdBlockTypeDatagram ) { + + // + // Reset the counts of datagrams bufferred on the endpoint. + // This prevents anyone from thinking that there is bufferred + // data on the endpoint. + // + + endpoint->BufferredDatagramCount = 0; + endpoint->BufferredDatagramBytes = 0; + + // + // Cancel all receive datagram and peek datagram IRPs on the + // endpoint. + // + + AfdCompleteIrpList( + &endpoint->ReceiveDatagramIrpListHead, + &endpoint->SpinLock, + STATUS_CANCELLED, + AfdCleanupReceiveDatagramIrp + ); + + AfdCompleteIrpList( + &endpoint->PeekDatagramIrpListHead, + &endpoint->SpinLock, + STATUS_CANCELLED, + AfdCleanupReceiveDatagramIrp + ); + } + + // + // If this is a datagram endpoint, return the process quota which we + // charged when the endpoint was created. + // + + if ( endpoint->Type == AfdBlockTypeDatagram ) { + + PsReturnPoolQuota( + endpoint->OwningProcess, + NonPagedPool, + endpoint->Common.Datagram.MaxBufferredSendBytes + + endpoint->Common.Datagram.MaxBufferredReceiveBytes + ); + AfdRecordQuotaHistory( + endpoint->OwningProcess, + -(LONG)(endpoint->Common.Datagram.MaxBufferredSendBytes + + endpoint->Common.Datagram.MaxBufferredReceiveBytes), + "Cleanup dgrm", + endpoint + ); + AfdRecordPoolQuotaReturned( + endpoint->Common.Datagram.MaxBufferredSendBytes + + endpoint->Common.Datagram.MaxBufferredReceiveBytes + ); + } + + // + // If this is a connected VC endpoint on a nonbufferring TDI provider, + // cancel all outstanding send and receive IRPs. + // + + if ( connection != NULL ) { + + if ( !endpoint->TdiBufferring ) { + + AfdCompleteIrpList( + &connection->VcReceiveIrpListHead, + &endpoint->SpinLock, + STATUS_CANCELLED, + NULL + ); + + AfdCompleteIrpList( + &connection->VcSendIrpListHead, + &endpoint->SpinLock, + STATUS_CANCELLED, + NULL + ); + } + + // + // Remember that we have started cleanup on this connection. + // We know that we'll never get a request on the connection + // after we start cleanup on the connection. + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql2 ); + connection->CleanupBegun = TRUE; + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); + + // + // Attempt to remove the connected reference. + // + + AfdDeleteConnectedReference( connection, FALSE ); + } + + // + // If there is a transmit IRP on the endpoint, cancel it. + // + + IoAcquireCancelSpinLock( &cancelIrql ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql2 ); + + if ( endpoint->TransmitIrp != NULL ) { + endpoint->TransmitIrp->CancelIrql = cancelIrql; + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); + AfdCancelTransmit( NULL, endpoint->TransmitIrp ); + } else { + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); + IoReleaseCancelSpinLock( cancelIrql ); + } + + // + // Remember the new state of the endpoint. + // + + //endpoint->State = AfdEndpointStateCleanup; + + // + // Reset relevent event handlers on the endpoint. This prevents + // getting indications after we free the endpoint and connection + // objects. We should not be able to get new connects after this + // handle has been cleaned up. + // + // Note that these calls can fail if, for example, DHCP changes the + // host's IP address while the endpoint is active. + // + + if ( endpoint->AddressHandle != NULL ) { + + if ( endpoint->State == AfdEndpointStateListening ) { + status = AfdSetEventHandler( + endpoint->AddressFileObject, + TDI_EVENT_CONNECT, + NULL, + NULL + ); + //ASSERT( NT_SUCCESS(status) ); + } + + if ( IS_DGRAM_ENDPOINT(endpoint) ) { + status = AfdSetEventHandler( + endpoint->AddressFileObject, + TDI_EVENT_RECEIVE_DATAGRAM, + NULL, + NULL + ); + //ASSERT( NT_SUCCESS(status) ); + } + + } + + InterlockedIncrement( + &AfdEndpointsCleanedUp + ); + + return STATUS_SUCCESS; + +} // AfdCleanup + + +NTSTATUS +AfdClose ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This is the routine that handles Close IRPs in AFD. It + dereferences the endpoint specified in the IRP, which will result in + the endpoint being freed when all other references go away. + +Arguments: + + Irp - Pointer to I/O request packet. + + IrpSp - pointer to the IO stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PAFD_ENDPOINT endpoint; + + PAGED_CODE( ); + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + IF_DEBUG(OPEN_CLOSE) { + KdPrint(( "AfdClose: closing file object %lx, endpoint %lx\n", + IrpSp->FileObject, endpoint )); + } + + AfdCloseEndpoint( endpoint ); + + return STATUS_SUCCESS; + +} // AfdClose diff --git a/private/ntos/afd/connect.c b/private/ntos/afd/connect.c new file mode 100644 index 000000000..3fdea91f9 --- /dev/null +++ b/private/ntos/afd/connect.c @@ -0,0 +1,659 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + connect.c + +Abstract: + + This module contains the code for passing on connect IRPs to + TDI providers. + +Author: + + David Treadwell (davidtr) 2-Mar-1992 + +Revision History: + +--*/ + +#include "afdp.h" + +NTSTATUS +AfdDoDatagramConnect ( + IN PAFD_ENDPOINT Endpoint, + IN PIRP Irp + ); + +NTSTATUS +AfdRestartConnect ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +VOID +AfdSetupConnectDataBuffers ( + IN PAFD_ENDPOINT Endpoint, + IN PAFD_CONNECTION Connection, + IN PTDI_CONNECTION_INFORMATION RequestConnectionInformation, + IN PTDI_CONNECTION_INFORMATION ReturnConnectionInformation + ); + +VOID +AfdEnableFailedConnectEvent( + IN PAFD_ENDPOINT Endpoint + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, AfdConnect ) +#pragma alloc_text( PAGEAFD, AfdDoDatagramConnect ) +#pragma alloc_text( PAGEAFD, AfdRestartConnect ) +#pragma alloc_text( PAGEAFD, AfdSetupConnectDataBuffers ) +#pragma alloc_text( PAGEAFD, AfdEnableFailedConnectEvent ) +#endif + + +NTSTATUS +AfdConnect ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Handles the IOCTL_AFD_CONNECT IOCTL. + +Arguments: + + Irp - Pointer to I/O request packet. + + IrpSp - pointer to the IO stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + NTSTATUS status; + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + PTDI_REQUEST_CONNECT tdiRequest; + PTDI_CONNECTION_INFORMATION requestConnectionInformation; + PTDI_CONNECTION_INFORMATION returnConnectionInformation; + ULONG offset; + + PAGED_CODE( ); + + // + // Make sure that the endpoint is in the correct state. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( endpoint->Type == AfdBlockTypeEndpoint || + endpoint->Type == AfdBlockTypeDatagram ); + + IF_DEBUG(CONNECT) { + KdPrint(( "AfdConnect: starting connect on endpoint %lx\n", endpoint )); + } + + // + // If the endpoint is not bound or if there is another connect + // outstanding on this endpoint, then this is an invalid request. + // If this is a datagram endpoint, it is legal to reconnect + // the endpoint. + // + + if ( ( endpoint->Type != AfdBlockTypeEndpoint && + endpoint->Type != AfdBlockTypeDatagram ) || + endpoint->State != AfdEndpointStateBound || + endpoint->ConnectOutstanding ) { + + if ( !IS_DGRAM_ENDPOINT(endpoint) || + endpoint->State != AfdEndpointStateConnected ) { + status = STATUS_INVALID_PARAMETER; + goto complete; + } + } + + // + // Determine where in the system buffer the request and return + // connection information structures exist. Pass pointers to + // these locations instead of the user-mode pointers in the + // tdiRequest structure so that the memory will be nonpageable. + // + + tdiRequest = Irp->AssociatedIrp.SystemBuffer; + + offset = (ULONG)tdiRequest->RequestConnectionInformation - + (ULONG)Irp->UserBuffer; + + if( (LONG)offset < 0 || + ( offset + sizeof(*requestConnectionInformation) ) > + IrpSp->Parameters.DeviceIoControl.InputBufferLength ) { + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + requestConnectionInformation = (PVOID)( (ULONG)tdiRequest + offset ); + + offset = (ULONG)tdiRequest->ReturnConnectionInformation - + (ULONG)Irp->UserBuffer; + + if( (LONG)offset < 0 || + ( offset + sizeof(*returnConnectionInformation) ) > + IrpSp->Parameters.DeviceIoControl.InputBufferLength ) { + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + returnConnectionInformation = (PVOID)( (ULONG)tdiRequest + offset ); + + offset = (ULONG)requestConnectionInformation->RemoteAddress - + (ULONG)Irp->UserBuffer; + + if( (LONG)offset < 0 || + ( offset + requestConnectionInformation->RemoteAddressLength ) > + IrpSp->Parameters.DeviceIoControl.InputBufferLength ) { + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + requestConnectionInformation->RemoteAddress = + (PVOID)( (ULONG)tdiRequest + offset ); + + // + // If this is a datagram endpoint, simply remember the specified + // address so that we can use it on sends, receives, writes, and + // reads. + // + + if ( IS_DGRAM_ENDPOINT(endpoint) ) { + tdiRequest->RequestConnectionInformation = requestConnectionInformation; + tdiRequest->ReturnConnectionInformation = returnConnectionInformation; + + return AfdDoDatagramConnect( endpoint, Irp ); + } + + // + // Create a connection object to use for the connect operation. + // + + status = AfdCreateConnection( + &endpoint->TransportInfo->TransportDeviceName, + endpoint->AddressHandle, + endpoint->TdiBufferring, + endpoint->InLine, + endpoint->OwningProcess, + &connection + ); + + if ( !NT_SUCCESS(status) ) { + goto complete; + } + + // + // Set up a referenced pointer from the connection to the endpoint. + // Note that we set up the connection's pointer to the endpoint + // BEFORE the endpoint's pointer to the connection so that AfdPoll + // doesn't try to back reference the endpoint from the connection. + // + + REFERENCE_ENDPOINT( endpoint ); + connection->Endpoint = endpoint; + + // + // Remember that this is now a connecting type of endpoint, and set + // up a pointer to the connection in the endpoint. This is + // implicitly a referenced pointer. + // + + endpoint->Type = AfdBlockTypeVcConnecting; + endpoint->Common.VcConnecting.Connection = connection; + + ASSERT( endpoint->TdiBufferring == connection->TdiBufferring ); + + // + // Add an additional reference to the connection. This prevents the + // connection from being closed until the disconnect event handler + // is called. + // + + AfdAddConnectedReference( connection ); + + // + // Remember that there is a connect operation outstanding on this + // endpoint. This allows us to correctly block a send poll on the + // endpoint until the connect completes. + // + + endpoint->ConnectOutstanding = TRUE; + + // + // If there are connect data buffers, move them from the endpoint + // structure to the connection structure and set up the necessary + // pointers in the connection request we're going to give to the TDI + // provider. Do this in a subroutine so this routine can be pageable. + // + + AfdSetupConnectDataBuffers( + endpoint, + connection, + requestConnectionInformation, + returnConnectionInformation + ); + + // + // Save a pointer to the return connection information structure + // so we can access it in the restart routine. + // + + IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = returnConnectionInformation; + + // + // Since we may be reissuing a connect after a previous failed connect, + // reenable the failed connect event bit. + // + + AfdEnableFailedConnectEvent( endpoint ); + + // + // Build a TDI kernel-mode connect request in the next stack location + // of the IRP. + // + + TdiBuildConnect( + Irp, + connection->DeviceObject, + connection->FileObject, + AfdRestartConnect, + endpoint, + &tdiRequest->Timeout, + requestConnectionInformation, + returnConnectionInformation + ); + + // + // Reset the connect status to success so that the poll code will + // know if a connect failure occurs. + // + + endpoint->Common.VcConnecting.ConnectStatus = STATUS_SUCCESS; + + // + // Call the transport to actually perform the connect operation. + // + + return AfdIoCallDriver( endpoint, connection->DeviceObject, Irp ); + +complete: + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + return status; + +} // AfdConnect + + +NTSTATUS +AfdDoDatagramConnect ( + IN PAFD_ENDPOINT Endpoint, + IN PIRP Irp + ) +{ + PTRANSPORT_ADDRESS inputAddress; + KIRQL oldIrql; + NTSTATUS status; + PTDI_REQUEST_CONNECT tdiRequest; + + tdiRequest = Irp->AssociatedIrp.SystemBuffer; + + // + // Save the remote address on the endpoint. We'll use this to + // send datagrams in the future and to compare received datagram's + // source addresses. + // + + inputAddress = tdiRequest->RequestConnectionInformation->RemoteAddress; + + AfdAcquireSpinLock( &Endpoint->SpinLock, &oldIrql ); + + if ( Endpoint->Common.Datagram.RemoteAddress != NULL ) { + AFD_FREE_POOL( + Endpoint->Common.Datagram.RemoteAddress, + AFD_REMOTE_ADDRESS_POOL_TAG + ); + } + + Endpoint->Common.Datagram.RemoteAddress = + AFD_ALLOCATE_POOL( + NonPagedPool, + tdiRequest->RequestConnectionInformation->RemoteAddressLength, + AFD_REMOTE_ADDRESS_POOL_TAG + ); + + if ( Endpoint->Common.Datagram.RemoteAddress == NULL ) { + AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql ); + status = STATUS_INSUFFICIENT_RESOURCES; + goto complete; + } + + RtlCopyMemory( + Endpoint->Common.Datagram.RemoteAddress, + inputAddress, + tdiRequest->RequestConnectionInformation->RemoteAddressLength + ); + + Endpoint->Common.Datagram.RemoteAddressLength = + tdiRequest->RequestConnectionInformation->RemoteAddressLength; + Endpoint->State = AfdEndpointStateConnected; + + Endpoint->DisconnectMode = 0; + + // + // Indicate that the connect completed. Implicitly, the + // successful completion of a connect also means that the caller + // can do a send on the socket. + // + + AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql ); + + AfdIndicatePollEvent( + Endpoint, + AFD_POLL_CONNECT_BIT, + STATUS_SUCCESS + ); + + AfdIndicatePollEvent( + Endpoint, + AFD_POLL_SEND_BIT, + STATUS_SUCCESS + ); + + status = STATUS_SUCCESS; + +complete: + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + return status; + +} // AfdDoDatagramConnect + + +VOID +AfdSetupConnectDataBuffers ( + IN PAFD_ENDPOINT Endpoint, + IN PAFD_CONNECTION Connection, + IN PTDI_CONNECTION_INFORMATION RequestConnectionInformation, + IN PTDI_CONNECTION_INFORMATION ReturnConnectionInformation + ) +{ + KIRQL oldIrql; + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + if ( Endpoint->ConnectDataBuffers != NULL ) { + + ASSERT( Connection->ConnectDataBuffers == NULL ); + + Connection->ConnectDataBuffers = Endpoint->ConnectDataBuffers; + Endpoint->ConnectDataBuffers = NULL; + + RequestConnectionInformation->UserData = + Connection->ConnectDataBuffers->SendConnectData.Buffer; + RequestConnectionInformation->UserDataLength = + Connection->ConnectDataBuffers->SendConnectData.BufferLength; + RequestConnectionInformation->Options = + Connection->ConnectDataBuffers->SendConnectOptions.Buffer; + RequestConnectionInformation->OptionsLength = + Connection->ConnectDataBuffers->SendConnectOptions.BufferLength; + ReturnConnectionInformation->UserData = + Connection->ConnectDataBuffers->ReceiveConnectData.Buffer; + ReturnConnectionInformation->UserDataLength = + Connection->ConnectDataBuffers->ReceiveConnectData.BufferLength; + ReturnConnectionInformation->Options = + Connection->ConnectDataBuffers->ReceiveConnectOptions.Buffer; + ReturnConnectionInformation->OptionsLength = + Connection->ConnectDataBuffers->ReceiveConnectOptions.BufferLength; + + } + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + +} // AfdSetupConnectDataBuffers + + +NTSTATUS +AfdRestartConnect ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + Handles the IOCTL_AFD_CONNECT IOCTL. + +Arguments: + + Irp - Pointer to I/O request packet. + + IrpSp - pointer to the IO stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + KIRQL oldIrql; + + endpoint = Context; + ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ); + + connection = endpoint->Common.VcConnecting.Connection; + ASSERT( connection != NULL ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + IF_DEBUG(CONNECT) { + KdPrint(( "AfdRestartConnect: connect completed, status = %X, " + "endpoint = %lx\n", Irp->IoStatus.Status, endpoint )); + } + + endpoint->Common.VcConnecting.ConnectStatus = Irp->IoStatus.Status; + + // + // Remember that there is no longer a connect operation outstanding + // on this endpoint. We must do this BEFORE the AfdIndicatePoll() + // in case a poll comes in while we are doing the indicate of setting + // the ConnectOutstanding bit. + // + + endpoint->ConnectOutstanding = FALSE; + + // + // If there are connect buffers on this endpoint, remember the + // size of the return connect data. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + if ( connection->ConnectDataBuffers != NULL ) { + + PIO_STACK_LOCATION irpSp; + PTDI_CONNECTION_INFORMATION returnConnectionInformation; + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + returnConnectionInformation = + irpSp->Parameters.DeviceIoControl.Type3InputBuffer; + + ASSERT( returnConnectionInformation != NULL ); + ASSERT( connection->ConnectDataBuffers->ReceiveConnectData.BufferLength >= + (ULONG)returnConnectionInformation->UserDataLength ); + ASSERT( connection->ConnectDataBuffers->ReceiveConnectOptions.BufferLength >= + (ULONG)returnConnectionInformation->OptionsLength ); + + connection->ConnectDataBuffers->ReceiveConnectData.BufferLength = + returnConnectionInformation->UserDataLength; + connection->ConnectDataBuffers->ReceiveConnectOptions.BufferLength = + returnConnectionInformation->OptionsLength; + } + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + // + // Indicate that the connect completed. Implicitly, the successful + // completion of a connect also means that the caller can do a send + // on the socket. + // + + if ( NT_SUCCESS(Irp->IoStatus.Status) ) { + + AfdIndicatePollEvent( + endpoint, + AFD_POLL_CONNECT_BIT, + Irp->IoStatus.Status + ); + + // + // If the request succeeded, set the endpoint to the connected + // state. The endpoint type has already been set to + // AfdBlockTypeVcConnecting. + // + + endpoint->State = AfdEndpointStateConnected; + ASSERT( endpoint->Type = AfdBlockTypeVcConnecting ); + + // + // Remember the time that the connection started. + // + + KeQuerySystemTime( (PLARGE_INTEGER)&connection->ConnectTime ); + + } else { + + AfdIndicatePollEvent( + endpoint, + AFD_POLL_CONNECT_FAIL_BIT, + Irp->IoStatus.Status + ); + + // + // Manually delete the connected reference if somebody else + // hasn't already done so. We can't use + // AfdDeleteConnectedReference() because it refuses to delete + // the connected reference until the endpoint has been cleaned + // up. + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + // + // The connect failed, so reset the type to open. + // + + endpoint->Type = AfdBlockTypeEndpoint; + + if ( connection->ConnectedReferenceAdded ) { + connection->ConnectedReferenceAdded = FALSE; + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + DEREFERENCE_CONNECTION( connection ); + } else { + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + } + + // + // Dereference the connection block stored on the endpoint. + // This should cause the connection object reference count to go + // to zero to the connection object can be deleted. + // + + DEREFERENCE_CONNECTION( connection ); + endpoint->Common.VcConnecting.Connection = NULL; + } + + if ( NT_SUCCESS(Irp->IoStatus.Status ) ) { + + AfdIndicatePollEvent( + endpoint, + AFD_POLL_SEND_BIT, + STATUS_SUCCESS + ); + + } + + // + // If pending has be returned for this irp then mark the current + // stack as pending. + // + + if ( Irp->PendingReturned ) { + IoMarkIrpPending(Irp); + } + + AfdCompleteOutstandingIrp( endpoint, Irp ); + + return STATUS_SUCCESS; + +} // AfdRestartConnect + + +VOID +AfdEnableFailedConnectEvent( + IN PAFD_ENDPOINT Endpoint + ) + +/*++ + +Routine Description: + + Reenables the failed connect poll bit on the specified endpoint. + This is off in a separate (nonpageable) routine so that the bulk + of AfdConnect() can remain pageable. + +Arguments: + + Endpoint - The endpoint to enable. + +Return Value: + + None. + +--*/ + +{ + KIRQL oldIrql; + + AfdAcquireSpinLock( &Endpoint->SpinLock, &oldIrql ); + + ASSERT( ( Endpoint->EventsActive & AFD_POLL_CONNECT ) == 0 ); + Endpoint->EventsActive &= ~AFD_POLL_CONNECT_FAIL; + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdConnect: Endp %08lX, Active %08lX\n", + Endpoint, + Endpoint->EventsActive + )); + } + + AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql ); + +} // AfdEnableFailedConnectEvent + diff --git a/private/ntos/afd/create.c b/private/ntos/afd/create.c new file mode 100644 index 000000000..7b2cf5a39 --- /dev/null +++ b/private/ntos/afd/create.c @@ -0,0 +1,298 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dispatch.c + +Abstract: + + This module contains code for opening a handle to AFD. + +Author: + + David Treadwell (davidtr) 21-Feb-1992 + +Revision History: + +--*/ + +#include "afdp.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, AfdCreate ) +#endif + +extern PSECURITY_DESCRIPTOR AfdRawSecurityDescriptor; + + + +NTSTATUS +AfdCreate ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This is the routine that handles Create IRPs in AFD. If creates an + AFD_ENDPOINT structure and fills it in with the information + specified in the open packet. + +Arguments: + + Irp - Pointer to I/O request packet. + + IrpSp - pointer to the IO stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PAFD_OPEN_PACKET openPacket; + PAFD_ENDPOINT endpoint; + PFILE_FULL_EA_INFORMATION eaBuffer; + UNICODE_STRING transportDeviceName; + NTSTATUS status; + + PAGED_CODE( ); + + DEBUG endpoint = NULL; + + // + // Find the open packet from the EA buffer in the system buffer of + // the associated IRP. Fail the request if there was no EA + // buffer specified. + // + + eaBuffer = Irp->AssociatedIrp.SystemBuffer; + + if ( eaBuffer == NULL ) { + + // + // Allocate an AFD "helper" endpoint. + // + + status = AfdAllocateEndpoint( + &endpoint, + NULL, + 0 + ); + + if( !NT_SUCCESS(status) ) { + return status; + } + + } else { + + openPacket = (PAFD_OPEN_PACKET)(eaBuffer->EaName + + eaBuffer->EaNameLength + 1); + + // + // Validate parameters in the open packet. + // + + if ( openPacket->EndpointType < MIN_AFD_ENDPOINT_TYPE || + openPacket->EndpointType > MAX_AFD_ENDPOINT_TYPE ) { + return STATUS_INVALID_PARAMETER; + } + + // + // Make sure that the transport address fits within the specified + // EA buffer. + // + + if ( eaBuffer->EaValueLength < + sizeof(AFD_OPEN_PACKET) + openPacket->TransportDeviceNameLength ) { + return STATUS_ACCESS_VIOLATION; + } + + // + // Set up a string that describes the transport device name. + // + + transportDeviceName.Buffer = openPacket->TransportDeviceName; + transportDeviceName.Length = (USHORT)openPacket->TransportDeviceNameLength; + transportDeviceName.MaximumLength = + transportDeviceName.Length + sizeof(WCHAR); + + // + // If this is an open of a raw endpoint, perform an access check. + // + if ( ( openPacket->EndpointType == AfdEndpointTypeRaw ) && + !AfdDisableRawSecurity + ) + { + BOOLEAN accessGranted; + PACCESS_STATE accessState; + PIO_SECURITY_CONTEXT securityContext; + NTSTATUS status; + PPRIVILEGE_SET privileges = NULL; + ACCESS_MASK grantedAccess; + + + securityContext = IrpSp->Parameters.Create.SecurityContext; + accessState = securityContext->AccessState; + + SeLockSubjectContext(&accessState->SubjectSecurityContext); + + accessGranted = SeAccessCheck( + AfdRawSecurityDescriptor, + &accessState->SubjectSecurityContext, + TRUE, + IrpSp->Parameters.Create.SecurityContext->DesiredAccess, + 0, + &privileges, + IoGetFileObjectGenericMapping(), + UserMode, + &grantedAccess, + &status + ); + + + if (privileges) { + (VOID) SeAppendPrivileges( + accessState, + privileges + ); + SeFreePrivileges(privileges); + } + + if (accessGranted) { + accessState->PreviouslyGrantedAccess |= grantedAccess; + accessState->RemainingDesiredAccess &= ~( grantedAccess | MAXIMUM_ALLOWED ); + } + + SeUnlockSubjectContext(&accessState->SubjectSecurityContext); + + if (!accessGranted) { + return STATUS_ACCESS_DENIED; + } + } + + // + // Allocate an AFD endpoint. + // + + status = AfdAllocateEndpoint( + &endpoint, + &transportDeviceName, + openPacket->GroupID + ); + + if( !NT_SUCCESS(status) ) { + return status; + } + } + + ASSERT( endpoint != NULL ); + + // + // Set up a pointer to the endpoint in the file object so that we + // can find the endpoint in future calls. + // + + IrpSp->FileObject->FsContext = endpoint; + + IF_DEBUG(OPEN_CLOSE) { + KdPrint(( "AfdCreate: opened file object = %lx, endpoint = %lx\n", + IrpSp->FileObject, endpoint )); + + } + + // + // Remember the type of endpoint that this is. If this is a datagram + // endpoint, change the block type to reflect this. + // + + if ( eaBuffer != NULL ) { + if (openPacket->EndpointType == AfdEndpointTypeRaw) { + // + // There is no other distinction between a raw endpoint and + // a datagram endpoint, so we mark them all as datagram. + // + endpoint->EndpointType = AfdEndpointTypeDatagram; + } + else { + endpoint->EndpointType = openPacket->EndpointType; + } + } + + if ( IS_DGRAM_ENDPOINT(endpoint) ) { + + if ( eaBuffer == NULL ) { + + DEREFERENCE_ENDPOINT( endpoint ); + AfdCloseEndpoint( endpoint ); + + return STATUS_INVALID_PARAMETER; + } + + endpoint->Type = AfdBlockTypeDatagram; + + // + // Initialize lists which exist only in datagram endpoints. + // + + InitializeListHead( &endpoint->ReceiveDatagramIrpListHead ); + InitializeListHead( &endpoint->PeekDatagramIrpListHead ); + InitializeListHead( &endpoint->ReceiveDatagramBufferListHead ); + + // + // Charge quota for the endpoint to account for the data + // bufferring we'll do on behalf of the process. + // + + try { + + PsChargePoolQuota( + endpoint->OwningProcess, + NonPagedPool, + AfdReceiveWindowSize + AfdSendWindowSize + ); + AfdRecordQuotaHistory( + endpoint->OwningProcess, + (LONG)(AfdReceiveWindowSize + AfdSendWindowSize), + "Create dgram", + endpoint + ); + AfdRecordPoolQuotaCharged( + AfdReceiveWindowSize + AfdSendWindowSize + ); + + } except ( EXCEPTION_EXECUTE_HANDLER ) { + +#if DBG + DbgPrint( "AfdCreate: PsChargePoolQuota failed.\n" ); +#endif + + DEREFERENCE_ENDPOINT( endpoint ); + AfdCloseEndpoint( endpoint ); + + return STATUS_QUOTA_EXCEEDED; + } + + endpoint->Common.Datagram.MaxBufferredReceiveBytes = AfdReceiveWindowSize; + endpoint->Common.Datagram.MaxBufferredReceiveCount = + (CSHORT)(AfdReceiveWindowSize / AfdBufferMultiplier); + endpoint->Common.Datagram.MaxBufferredSendBytes = AfdSendWindowSize; + endpoint->Common.Datagram.MaxBufferredSendCount = + (CSHORT)(AfdSendWindowSize / AfdBufferMultiplier); + } + + // + // The open worked. Dereference the endpoint and return success. + // + + DEREFERENCE_ENDPOINT( endpoint ); + + return STATUS_SUCCESS; + +} // AfdCreate + diff --git a/private/ntos/afd/daytona/afd.prf b/private/ntos/afd/daytona/afd.prf new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/private/ntos/afd/daytona/afd.prf diff --git a/private/ntos/afd/daytona/makefile b/private/ntos/afd/daytona/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/afd/daytona/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/afd/daytona/sources b/private/ntos/afd/daytona/sources new file mode 100644 index 000000000..aab27ef2e --- /dev/null +++ b/private/ntos/afd/daytona/sources @@ -0,0 +1,32 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +NT_UP=0 + +TARGETPATH=$(BASEDIR)\public\sdk\lib + +!INCLUDE ..\sources.inc + +NTPROFILEINPUT=yes + diff --git a/private/ntos/afd/dirs b/private/ntos/afd/dirs new file mode 100644 index 000000000..ae7c99ccb --- /dev/null +++ b/private/ntos/afd/dirs @@ -0,0 +1,29 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + + +Author: + + Steve Wood (stevewo) 17-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl + +!ENDIF + +DIRS= \ + daytona \ + afdkd \ + +OPTIONAL_DIRS= \ + nt351 \ + diff --git a/private/ntos/afd/disconn.c b/private/ntos/afd/disconn.c new file mode 100644 index 000000000..61b5bbbd8 --- /dev/null +++ b/private/ntos/afd/disconn.c @@ -0,0 +1,1183 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + disconn.c + +Abstract: + + This module contains the dispatch routines for AFD. + +Author: + + David Treadwell (davidtr) 31-Mar-1992 + +Revision History: + +--*/ + +#include "afdp.h" + + +#if ENABLE_ABORT_TIMER_HACK +// +// Hack-O-Rama. TDI has a fundamental flaw in that it is often impossible +// to determine exactly when a TDI protocol is "done" with a connection +// object. The biggest problem here is that AFD may get a suprious TDI +// indication *after* an abort request has completed. As a temporary work- +// around, whenever an abort request completes, we'll start a timer. AFD +// will defer further processing on the connection until that timer fires. +// + +typedef struct _AFD_ABORT_TIMER_INFO { + + KDPC Dpc; + KTIMER Timer; + +} AFD_ABORT_TIMER_INFO, *PAFD_ABORT_TIMER_INFO; + +VOID +AfdAbortTimerHack( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); +#endif // ENABLE_ABORT_TIMER_HACK + +NTSTATUS +AfdRestartAbort( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +VOID +AfdRestartAbortHelper( + IN PAFD_CONNECTION Connection + ); + +NTSTATUS +AfdRestartDisconnect( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +typedef struct _AFD_ABORT_CONTEXT { + PAFD_CONNECTION Connection; +} AFD_ABORT_CONTEXT, *PAFD_ABORT_CONTEXT; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGEAFD, AfdPartialDisconnect ) +#pragma alloc_text( PAGEAFD, AfdDisconnectEventHandler ) +#pragma alloc_text( PAGEAFD, AfdBeginAbort ) +#pragma alloc_text( PAGEAFD, AfdRestartAbort ) +#pragma alloc_text( PAGEAFD, AfdRestartAbortHelper ) +#pragma alloc_text( PAGEAFD, AfdBeginDisconnect ) +#pragma alloc_text( PAGEAFD, AfdRestartDisconnect ) +#if ENABLE_ABORT_TIMER_HACK +#pragma alloc_text( PAGEAFD, AfdAbortTimerHack ) +#endif // ENABLE_ABORT_TIMER_HACK +#endif + + +NTSTATUS +AfdPartialDisconnect( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) +{ + NTSTATUS status; + KIRQL oldIrql; + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + PAFD_PARTIAL_DISCONNECT_INFO disconnectInfo; + + Irp->IoStatus.Information = 0; + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + disconnectInfo = Irp->AssociatedIrp.SystemBuffer; + + IF_DEBUG(CONNECT) { + KdPrint(( "AfdPartialDisconnect: disconnecting endpoint %lx, " + "mode %lx, endp mode %lx\n", + endpoint, disconnectInfo->DisconnectMode, + endpoint->DisconnectMode )); + } + + // + // If this is a datagram endpoint, just remember how the endpoint + // was shut down, don't actually do anything. Note that it is legal + // to do a shutdown() on an unconnected datagram socket, so the + // test that the socket must be connected is after this case. + // + + if ( IS_DGRAM_ENDPOINT(endpoint) ) { + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + if ( (disconnectInfo->DisconnectMode & AFD_ABORTIVE_DISCONNECT) != 0 ) { + endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_RECEIVE; + endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_SEND; + endpoint->DisconnectMode |= AFD_ABORTIVE_DISCONNECT; + } + + if ( (disconnectInfo->DisconnectMode & AFD_PARTIAL_DISCONNECT_RECEIVE) != 0 ) { + endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_RECEIVE; + } + + if ( (disconnectInfo->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND) != 0 ) { + endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_SEND; + } + + if ( (disconnectInfo->DisconnectMode & AFD_UNCONNECT_DATAGRAM) != 0 ) { + if( endpoint->Common.Datagram.RemoteAddress != NULL ) { + AFD_FREE_POOL( + endpoint->Common.Datagram.RemoteAddress, + AFD_REMOTE_ADDRESS_POOL_TAG + ); + } + endpoint->Common.Datagram.RemoteAddress = NULL; + endpoint->Common.Datagram.RemoteAddressLength = 0; + endpoint->State = AfdEndpointStateBound; + } + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + status = STATUS_SUCCESS; + goto complete; + } + + // + // Make sure that the endpoint is in the correct state. + // + + if ( endpoint->Type != AfdBlockTypeVcConnecting || + endpoint->State != AfdEndpointStateConnected ) { + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + connection = endpoint->Common.VcConnecting.Connection; + ASSERT( connection != NULL ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + // + // If we're doing an abortive disconnect, remember that the receive + // side is shut down and issue a disorderly release. + // + + if ( (disconnectInfo->DisconnectMode & AFD_ABORTIVE_DISCONNECT) != 0 ) { + + IF_DEBUG(CONNECT) { + KdPrint(( "AfdPartialDisconnect: abortively disconnecting endp %lx\n", + endpoint )); + } + + status = AfdBeginAbort( connection ); + if ( status == STATUS_PENDING ) { + status = STATUS_SUCCESS; + } + + goto complete; + } + + // + // If the receive side of the connection is being shut down, + // remember the fact in the endpoint. If there is pending data on + // the VC, do a disorderly release on the endpoint. If the receive + // side has already been shut down, do nothing. + // + + if ( (disconnectInfo->DisconnectMode & AFD_PARTIAL_DISCONNECT_RECEIVE) != 0 && + (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_RECEIVE) == 0 ) { + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + // + // Determine whether there is pending data. + // + + if ( IS_DATA_ON_CONNECTION( connection ) || + IS_EXPEDITED_DATA_ON_CONNECTION( connection ) ) { + + // + // There is unreceived data. Abort the connection. + // + + IF_DEBUG(CONNECT) { + KdPrint(( "AfdPartialDisconnect: unreceived data on endp %lx," + "conn %lx, aborting.\n", + endpoint, connection )); + } + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + (VOID)AfdBeginAbort( connection ); + + status = STATUS_SUCCESS; + goto complete; + + } else { + + IF_DEBUG(CONNECT) { + KdPrint(( "AfdPartialDisconnect: disconnecting recv for endp %lx\n", + endpoint )); + } + + // + // Remember that the receive side is shut down. This will cause + // the receive indication handlers to dump any data that + // arrived. + // + // !!! This is a minor violation of RFC1122 4.2.2.13. We + // should really do an abortive disconnect if data + // arrives after a receive shutdown. + // + + endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_RECEIVE; + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + } + } + + // + // If the send side is being shut down, remember it in the endpoint + // and pass the request on to the TDI provider for a graceful + // disconnect. If the send side is already shut down, do nothing here. + // + + if ( (disconnectInfo->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND) != 0 && + (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND) == 0 ) { + + status = AfdBeginDisconnect( endpoint, &disconnectInfo->Timeout, NULL ); + if ( !NT_SUCCESS(status) ) { + goto complete; + } + if ( status == STATUS_PENDING ) { + status = STATUS_SUCCESS; + } + } + + status = STATUS_SUCCESS; + +complete: + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + return status; + +} // AfdPartialDisconnect + + +NTSTATUS +AfdDisconnectEventHandler( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN int DisconnectDataLength, + IN PVOID DisconnectData, + IN int DisconnectInformationLength, + IN PVOID DisconnectInformation, + IN ULONG DisconnectFlags + ) +{ + PAFD_CONNECTION connection = ConnectionContext; + PAFD_ENDPOINT endpoint; + KIRQL oldIrql; + NTSTATUS status; + + ASSERT( connection != NULL ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + endpoint = connection->Endpoint; + ASSERT( endpoint->Type == AfdBlockTypeVcConnecting || + endpoint->Type == AfdBlockTypeVcListening ); + + IF_DEBUG(CONNECT) { + KdPrint(( "AfdDisconnectEventHandler called for endpoint %lx, " + "connection %lx\n", + connection->Endpoint, connection )); + } + + UPDATE_CONN( connection, DisconnectFlags ); + + // + // Reference the connection object so that it does not go away while + // we're processing it inside this function. Without this + // reference, the user application could close the endpoint object, + // the connection reference count could go to zero, and the + // AfdDeleteConnectedReference call at the end of this function + // could cause a crash if the AFD connection object has been + // completely cleaned up. + // + + REFERENCE_CONNECTION( connection ); + + // + // Set up in the connection the fact that the remote side has + // disconnected or aborted. + // + + if ( (DisconnectFlags & TDI_DISCONNECT_ABORT) != 0 ) { + connection->AbortIndicated = TRUE; + status = STATUS_REMOTE_DISCONNECT; + AfdRecordAbortiveDisconnectIndications(); + } else { + connection->DisconnectIndicated = TRUE; + status = STATUS_SUCCESS; + AfdRecordGracefulDisconnectIndications(); + } + + // + // If this is a nonbufferring transport, complete any pended receives. + // + + if ( !connection->TdiBufferring ) { + + AfdCompleteIrpList( + &connection->VcReceiveIrpListHead, + &endpoint->SpinLock, + status, + NULL + ); + + // + // If this is an abort indication, complete all pended sends and + // discard any bufferred receive data. + // + + if ( connection->AbortIndicated ) { + + AfdCompleteIrpList( + &connection->VcSendIrpListHead, + &endpoint->SpinLock, + status, + NULL + ); + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + connection->VcBufferredReceiveBytes = 0; + connection->VcBufferredReceiveCount = 0; + connection->VcBufferredExpeditedBytes = 0; + connection->VcBufferredExpeditedCount = 0; + connection->VcReceiveBytesInTransport = 0; + connection->VcReceiveCountInTransport = 0; + + while ( !IsListEmpty( &connection->VcReceiveBufferListHead ) ) { + + PAFD_BUFFER afdBuffer; + PLIST_ENTRY listEntry; + + listEntry = RemoveHeadList( &connection->VcReceiveBufferListHead ); + afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry ); + + afdBuffer->ExpeditedData = FALSE; + afdBuffer->DataOffset = 0; + + AfdReturnBuffer( afdBuffer ); + } + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + } + } + + // + // If we got disconnect data or options, save it. + // + + if( ( DisconnectData != NULL && DisconnectDataLength > 0 ) || + ( DisconnectInformation != NULL && DisconnectInformationLength > 0 ) ) { + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + if( DisconnectData != NULL & DisconnectDataLength > 0 ) { + + status = AfdSaveReceivedConnectData( + &connection->ConnectDataBuffers, + IOCTL_AFD_SET_DISCONNECT_DATA, + DisconnectData, + DisconnectDataLength + ); + + if( !NT_SUCCESS(status) ) { + + // + // We hit an allocation failure, but press on regardless. + // + + KdPrint(( + "AfdSaveReceivedConnectData failed: %08lx\n", + status + )); + + } + + } + + if( DisconnectInformation != NULL & DisconnectInformationLength > 0 ) { + + status = AfdSaveReceivedConnectData( + &connection->ConnectDataBuffers, + IOCTL_AFD_SET_DISCONNECT_DATA, + DisconnectInformation, + DisconnectInformationLength + ); + + if( !NT_SUCCESS(status) ) { + + // + // We hit an allocation failure, but press on regardless. + // + + KdPrint(( + "AfdSaveReceivedConnectData failed: %08lx\n", + status + )); + + } + + } + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + } + + // + // Call AfdIndicatePollEvent in case anyone is polling on this + // connection getting disconnected or aborted. + // + + if ( (DisconnectFlags & TDI_DISCONNECT_ABORT) != 0 ) { + + AfdIndicatePollEvent( + endpoint, + AFD_POLL_ABORT_BIT, + STATUS_SUCCESS + ); + + } else { + + AfdIndicatePollEvent( + endpoint, + AFD_POLL_DISCONNECT_BIT, + STATUS_SUCCESS + ); + + } + + // + // Remove the connected reference on the connection object. We must + // do this AFTER setting up the flag which remembers the disconnect + // type that occurred. We must also do this AFTER we have finished + // handling everything in the endpoint, since the endpoint structure + // may no longer have any information about the connection object if + // a transmit request with AFD_TF_REUSE_SOCKET happenned on it. + // + + AfdDeleteConnectedReference( connection, FALSE ); + + // + // Dereference the connection from the reference added above. + // + + DEREFERENCE_CONNECTION( connection ); + + return STATUS_SUCCESS; + +} // AfdDisconnectEventHandler + + +NTSTATUS +AfdBeginAbort( + IN PAFD_CONNECTION Connection + ) +{ + PAFD_ENDPOINT endpoint = Connection->Endpoint; + PIRP irp; + PFILE_OBJECT fileObject; + PDEVICE_OBJECT deviceObject; + KIRQL oldIrql; + + IF_DEBUG(CONNECT) { + KdPrint(( "AfdBeginAbort: aborting on endpoint %lx\n", endpoint )); + } + + // Yet another hack to keep it from crashing + // Reduce the timing window were this connection can be removed + // from under us. (VadimE) + + REFERENCE_CONNECTION( Connection ); + + // + // Build an IRP to reset the connection. First get the address + // of the target device object. + // + + ASSERT( Connection->Type == AfdBlockTypeConnection ); + fileObject = Connection->FileObject; + ASSERT( fileObject != NULL ); + deviceObject = IoGetRelatedDeviceObject( fileObject ); + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + // + // If the endpoint has already been abortively disconnected, + // or if has been gracefully disconnected and the transport + // does not support orderly (i.e. two-phase) release, then just + // succeed this request. + // + // Note that, since the abort completion routine (AfdRestartAbort) + // will not be called, we must delete the connected reference + // ourselves. + // + + if ( (endpoint->DisconnectMode & AFD_ABORTIVE_DISCONNECT) != 0 || + Connection->AbortIndicated || + (Connection->DisconnectIndicated && + (endpoint->TransportInfo->ProviderInfo.ServiceFlags & + TDI_SERVICE_ORDERLY_RELEASE) == 0) ) { + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + AfdDeleteConnectedReference( Connection, FALSE ); + + // Accounts for the reference hack above (VadimE) + DEREFERENCE_CONNECTION( Connection ); + return STATUS_SUCCESS; + } + +#if ENABLE_ABORT_TIMER_HACK + + // + // Allocate a new abort timer if necessary. + // + + if( Connection->AbortTimerInfo == NULL ) { + + Connection->AbortTimerInfo = AFD_ALLOCATE_POOL( + NonPagedPoolMustSucceed, + sizeof(AFD_ABORT_TIMER_INFO), + AFD_ABORT_TIMER_HACK_POOL_TAG + ); + + } + + ASSERT( Connection->AbortTimerInfo != NULL ); + +#endif // ENABLE_ABORT_TIMER_HACK + + // + // Remember that the connection has been aborted. + // + + if ( endpoint->Type != AfdBlockTypeVcListening ) { + endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_RECEIVE; + endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_SEND; + endpoint->DisconnectMode |= AFD_ABORTIVE_DISCONNECT; + } + + Connection->AbortIndicated = TRUE; + + // + // Set the BytesTaken fields equal to the BytesIndicated fields so + // that no more AFD_POLL_RECEIVE or AFD_POLL_RECEIVE_EXPEDITED + // events get completed. + // + + if ( endpoint->TdiBufferring ) { + + Connection->Common.Bufferring.ReceiveBytesTaken = + Connection->Common.Bufferring.ReceiveBytesIndicated; + Connection->Common.Bufferring.ReceiveExpeditedBytesTaken = + Connection->Common.Bufferring.ReceiveExpeditedBytesIndicated; + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + } else if ( endpoint->Type != AfdBlockTypeVcListening ) { + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + // + // Complete all of the connection's pended sends and receives. + // + + AfdCompleteIrpList( + &Connection->VcReceiveIrpListHead, + &endpoint->SpinLock, + STATUS_LOCAL_DISCONNECT, + NULL + ); + + AfdCompleteIrpList( + &Connection->VcSendIrpListHead, + &endpoint->SpinLock, + STATUS_LOCAL_DISCONNECT, + NULL + ); + + } else { + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + } + + // + // Allocate an IRP. The stack size is one higher than that of the + // target device, to allow for the caller's completion routine. + // + + irp = IoAllocateIrp( (CCHAR)(deviceObject->StackSize), FALSE ); + + if ( irp == NULL ) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Initialize the IRP for an abortive disconnect. + // + + irp->MdlAddress = NULL; + + irp->Flags = 0; + irp->RequestorMode = KernelMode; + irp->PendingReturned = FALSE; + + irp->UserIosb = NULL; + irp->UserEvent = NULL; + + irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL; + + irp->AssociatedIrp.SystemBuffer = NULL; + irp->UserBuffer = NULL; + + irp->Tail.Overlay.Thread = PsGetCurrentThread(); + irp->Tail.Overlay.OriginalFileObject = fileObject; + irp->Tail.Overlay.AuxiliaryBuffer = NULL; + + TdiBuildDisconnect( + irp, + deviceObject, + fileObject, + AfdRestartAbort, + Connection, + NULL, + TDI_DISCONNECT_ABORT, + NULL, + NULL + ); + + // + // Reference the connection object so that it does not go away + // until the abort completes. + // + + // REFERENCE_CONNECTION( Connection ); Done above (VadimE) + + AfdRecordAbortiveDisconnectsInitiated(); + + // + // Pass the request to the transport provider. + // + + return IoCallDriver( deviceObject, irp ); + +} // AfdBeginAbort + + +#if ENABLE_ABORT_TIMER_HACK +NTSTATUS +AfdRestartAbort( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + + PAFD_CONNECTION connection; + PAFD_ABORT_TIMER_INFO timerInfo; + + connection = Context; + ASSERT( connection != NULL ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + UPDATE_CONN( connection, Irp->IoStatus.Status ); + + timerInfo = connection->AbortTimerInfo; + ASSERT( timerInfo != NULL ); + + IF_DEBUG(CONNECT) { + + KdPrint(( + "AfdRestartAbort: abort completed, status = %X, endpoint = %lx\n", + Irp->IoStatus.Status, + connection->Endpoint + )); + + } + + // + // Setup a timer so we know it's safe to free the connection. + // + + KeInitializeDpc( + &timerInfo->Dpc, + AfdAbortTimerHack, + connection + ); + + KeInitializeTimer( + &timerInfo->Timer + ); + + KeSetTimer( + &timerInfo->Timer, + AfdAbortTimerTimeout, + &timerInfo->Dpc + ); + + // + // Free the IRP now since it is no longer needed. + // + + IoFreeIrp( Irp ); + + // + // Return STATUS_MORE_PROCESSING_REQUIRED so that IoCompleteRequest + // will stop working on the IRP. + // + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // AfdRestartAbort + +VOID +AfdAbortTimerHack( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) +{ + + PAFD_CONNECTION connection; + + connection = DeferredContext; + ASSERT( connection != NULL ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + // + // Let the helper do the dirty work. + // + + AfdRestartAbortHelper( connection ); + +} // AfdAbortTimerHack + +#else // !ENABLE_ABORT_TIMER_HACK + + +NTSTATUS +AfdRestartAbort( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + + PAFD_CONNECTION connection; + + connection = Context; + ASSERT( connection != NULL ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + IF_DEBUG(CONNECT) { + + KdPrint(( + "AfdRestartAbort: abort completed, status = %X, endpoint = %lx\n", + Irp->IoStatus.Status, + connection->Endpoint + )); + + } + + // + // Let the helper do the dirty work. + // + + AfdRestartAbortHelper( connection ); + + // + // Free the IRP now since it is no longer needed. + // + + IoFreeIrp( Irp ); + + // + // Return STATUS_MORE_PROCESSING_REQUIRED so that IoCompleteRequest + // will stop working on the IRP. + // + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // AfdRestartAbort + +#endif // ENABLE_ABORT_TIMER_HACK + + +VOID +AfdRestartAbortHelper( + IN PAFD_CONNECTION Connection + ) +{ + + PAFD_ENDPOINT endpoint; + + ASSERT( Connection != NULL ); + ASSERT( Connection->Type == AfdBlockTypeConnection ); + + endpoint = Connection->Endpoint; + + UPDATE_CONN( Connection, 0 ); + AfdRecordAbortiveDisconnectsCompleted(); + + // + // Remember that the connection has been aborted, and indicate if + // necessary. + // + + if( endpoint->Type != AfdBlockTypeVcListening ) { + + AfdIndicatePollEvent( + endpoint, + AFD_POLL_ABORT_BIT, + STATUS_SUCCESS + ); + + } + + if( !Connection->TdiBufferring ) { + + // + // Complete all of the connection's pended sends and receives. + // + + AfdCompleteIrpList( + &Connection->VcReceiveIrpListHead, + &endpoint->SpinLock, + STATUS_LOCAL_DISCONNECT, + NULL + ); + + AfdCompleteIrpList( + &Connection->VcSendIrpListHead, + &endpoint->SpinLock, + STATUS_LOCAL_DISCONNECT, + NULL + ); + + } + + // + // Remove the connected reference from the connection, since we + // know that the connection will not be active any longer. + // + + AfdDeleteConnectedReference( Connection, FALSE ); + + // + // Dereference the AFD connection object. + // + + DEREFERENCE_CONNECTION( Connection ); + +} // AfdRestartAbortHelper + + +NTSTATUS +AfdBeginDisconnect( + IN PAFD_ENDPOINT Endpoint, + IN PLARGE_INTEGER Timeout OPTIONAL, + OUT PIRP *DisconnectIrp OPTIONAL + ) +{ + PTDI_CONNECTION_INFORMATION requestConnectionInformation = NULL; + PTDI_CONNECTION_INFORMATION returnConnectionInformation = NULL; + PAFD_CONNECTION connection; + KIRQL oldIrql; + PFILE_OBJECT fileObject; + PDEVICE_OBJECT deviceObject; + PAFD_DISCONNECT_CONTEXT disconnectContext; + PIRP irp; + + ASSERT( Endpoint->Type == AfdBlockTypeVcConnecting ); + + connection = Endpoint->Common.VcConnecting.Connection; + ASSERT( connection != NULL ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + fileObject = connection->FileObject; + ASSERT( fileObject != NULL ); + deviceObject = IoGetRelatedDeviceObject( fileObject ); + + UPDATE_CONN( connection, 0 ); + + if ( DisconnectIrp != NULL ) { + *DisconnectIrp = NULL; + } + + // + // Allocate and initialize a disconnect IRP. + // + + irp = IoAllocateIrp( (CCHAR)(deviceObject->StackSize), FALSE ); + if ( irp == NULL ) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Initialize the IRP. + // + + irp->MdlAddress = NULL; + + irp->Flags = 0; + irp->RequestorMode = KernelMode; + irp->PendingReturned = FALSE; + + irp->UserIosb = NULL; + irp->UserEvent = NULL; + + irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL; + + irp->AssociatedIrp.SystemBuffer = NULL; + irp->UserBuffer = NULL; + + irp->Tail.Overlay.Thread = PsGetCurrentThread(); + irp->Tail.Overlay.OriginalFileObject = fileObject; + irp->Tail.Overlay.AuxiliaryBuffer = NULL; + + // + // If the endpoint has already been abortively disconnected, + // just succeed this request. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + if ( (Endpoint->DisconnectMode & AFD_ABORTIVE_DISCONNECT) != 0 || + connection->AbortIndicated ) { + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + IoFreeIrp( irp ); + return STATUS_SUCCESS; + } + + // + // If this connection has already been disconnected, just succeed. + // + + if ( (Endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND) != 0 ) { + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + IoFreeIrp( irp ); + return STATUS_SUCCESS; + } + + // + // Use the disconnect context space in the connection structure. + // + + disconnectContext = &connection->DisconnectContext; + + disconnectContext->Endpoint = Endpoint; + disconnectContext->Connection = connection; + disconnectContext->TdiConnectionInformation = NULL; + disconnectContext->Irp = irp; + + InsertHeadList( + &AfdDisconnectListHead, + &disconnectContext->DisconnectListEntry + ); + + // + // Remember that the send side has been disconnected. + // + + Endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_SEND; + + // + // If there are disconnect data buffers, allocate request + // and return connection information structures and copy over + // pointers to the structures. + // + + if ( connection->ConnectDataBuffers != NULL ) { + + requestConnectionInformation = + AFD_ALLOCATE_POOL( + NonPagedPool, + sizeof(*requestConnectionInformation) + + sizeof(*returnConnectionInformation), + AFD_CONNECT_DATA_POOL_TAG + ); + + if ( requestConnectionInformation != NULL ) { + + returnConnectionInformation = + requestConnectionInformation + 1; + + requestConnectionInformation->UserData = + connection->ConnectDataBuffers->SendDisconnectData.Buffer; + requestConnectionInformation->UserDataLength = + connection->ConnectDataBuffers->SendDisconnectData.BufferLength; + requestConnectionInformation->Options = + connection->ConnectDataBuffers->SendDisconnectOptions.Buffer; + requestConnectionInformation->OptionsLength = + connection->ConnectDataBuffers->SendDisconnectOptions.BufferLength; + returnConnectionInformation->UserData = + connection->ConnectDataBuffers->ReceiveDisconnectData.Buffer; + returnConnectionInformation->UserDataLength = + connection->ConnectDataBuffers->ReceiveDisconnectData.BufferLength; + returnConnectionInformation->Options = + connection->ConnectDataBuffers->ReceiveDisconnectOptions.Buffer; + returnConnectionInformation->OptionsLength = + connection->ConnectDataBuffers->ReceiveDisconnectOptions.BufferLength; + } + + disconnectContext->TdiConnectionInformation = + requestConnectionInformation; + } + + // + // Set up the timeout for the disconnect. + // + + disconnectContext->Timeout = RtlConvertLongToLargeInteger( -1 ); + + // + // Build a disconnect Irp to pass to the TDI provider. + // + + TdiBuildDisconnect( + irp, + connection->DeviceObject, + connection->FileObject, + AfdRestartDisconnect, + disconnectContext, + &disconnectContext->Timeout, + TDI_DISCONNECT_RELEASE, + requestConnectionInformation, + returnConnectionInformation + ); + + IF_DEBUG(CONNECT) { + KdPrint(( "AfdBeginDisconnect: disconnecting endpoint %lx\n", + Endpoint )); + } + + // + // Reference the endpoint and connection so the space stays + // allocated until the disconnect completes. + // + + REFERENCE_ENDPOINT( Endpoint ); + REFERENCE_CONNECTION( connection ); + + // + // If there are still outstanding sends and this is a nonbufferring + // TDI transport which does not support orderly release, pend the + // IRP until all the sends have completed. + // + + if ( (Endpoint->TransportInfo->ProviderInfo.ServiceFlags & + TDI_SERVICE_ORDERLY_RELEASE) == 0 && + !Endpoint->TdiBufferring && connection->VcBufferredSendCount != 0 ) { + + ASSERT( connection->VcDisconnectIrp == NULL ); + + connection->VcDisconnectIrp = irp; + connection->SpecialCondition = TRUE; + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + return STATUS_PENDING; + } + + AfdRecordGracefulDisconnectsInitiated(); + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + // + // Pass the disconnect request on to the TDI provider. + // + + if ( DisconnectIrp == NULL ) { + return IoCallDriver( connection->DeviceObject, irp ); + } else { + *DisconnectIrp = irp; + return STATUS_SUCCESS; + } + +} // AfdBeginDisconnect + + +NTSTATUS +AfdRestartDisconnect( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + PAFD_DISCONNECT_CONTEXT disconnectContext = Context; + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + KIRQL oldIrql; + + endpoint = disconnectContext->Endpoint; + connection = disconnectContext->Connection; + + UPDATE_CONN( connection, 0 ); + AfdRecordGracefulDisconnectsCompleted(); + + ASSERT( connection != NULL ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + IF_DEBUG(CONNECT) { + KdPrint(( "AfdRestartDisconnect: disconnect completed, status = %X, " + "endpoint = %lx\n", Irp->IoStatus.Status, endpoint )); + } + + // + // Free context structures. + // + + if ( disconnectContext->TdiConnectionInformation != NULL ) { + AFD_FREE_POOL( + disconnectContext->TdiConnectionInformation, + AFD_CONNECT_DATA_POOL_TAG + ); + } + + // + // Remove the request from the list of disconnect requests and + // Dereference the connection and endpoint. We must remove it from + // the list before dereferencing the endpoint because when we do the + // dereference AFD might get unloaded, and we cannot acquire a spin + // lock after AFD gets unloaded. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + RemoveEntryList( &disconnectContext->DisconnectListEntry ); + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + DEREFERENCE_ENDPOINT( endpoint ); + DEREFERENCE_CONNECTION( connection ); + + // + // Free the IRP and return a status code so that the IO system will + // stop working on the IRP. + // + + IoFreeIrp( Irp ); + return STATUS_MORE_PROCESSING_REQUIRED; + +} // AfdRestartDisconnect + diff --git a/private/ntos/afd/dispatch.c b/private/ntos/afd/dispatch.c new file mode 100644 index 000000000..80cce6922 --- /dev/null +++ b/private/ntos/afd/dispatch.c @@ -0,0 +1,604 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dispatch.c + +Abstract: + + This module contains the dispatch routines for AFD. + +Author: + + David Treadwell (davidtr) 21-Feb-1992 + +Revision History: + +--*/ + +#include "afdp.h" + +NTSTATUS +AfdDispatchDeviceControl ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGEAFD, AfdDispatch ) +#pragma alloc_text( PAGEAFD, AfdDispatchDeviceControl ) +#endif + +// +// Lookup table to verify incoming IOCTL codes. +// + +ULONG AfdIoctlTable[] = + { + IOCTL_AFD_BIND, + IOCTL_AFD_CONNECT, + IOCTL_AFD_START_LISTEN, + IOCTL_AFD_WAIT_FOR_LISTEN, + IOCTL_AFD_ACCEPT, + IOCTL_AFD_RECEIVE, + IOCTL_AFD_RECEIVE_DATAGRAM, + IOCTL_AFD_SEND, + IOCTL_AFD_SEND_DATAGRAM, + IOCTL_AFD_POLL, + IOCTL_AFD_PARTIAL_DISCONNECT, + IOCTL_AFD_GET_ADDRESS, + IOCTL_AFD_QUERY_RECEIVE_INFO, + IOCTL_AFD_QUERY_HANDLES, + IOCTL_AFD_SET_INFORMATION, + IOCTL_AFD_GET_CONTEXT_LENGTH, + IOCTL_AFD_GET_CONTEXT, + IOCTL_AFD_SET_CONTEXT, + IOCTL_AFD_SET_CONNECT_DATA, + IOCTL_AFD_SET_CONNECT_OPTIONS, + IOCTL_AFD_SET_DISCONNECT_DATA, + IOCTL_AFD_SET_DISCONNECT_OPTIONS, + IOCTL_AFD_GET_CONNECT_DATA, + IOCTL_AFD_GET_CONNECT_OPTIONS, + IOCTL_AFD_GET_DISCONNECT_DATA, + IOCTL_AFD_GET_DISCONNECT_OPTIONS, + IOCTL_AFD_SIZE_CONNECT_DATA, + IOCTL_AFD_SIZE_CONNECT_OPTIONS, + IOCTL_AFD_SIZE_DISCONNECT_DATA, + IOCTL_AFD_SIZE_DISCONNECT_OPTIONS, + IOCTL_AFD_GET_INFORMATION, + IOCTL_AFD_TRANSMIT_FILE, + IOCTL_AFD_SUPER_ACCEPT, + IOCTL_AFD_EVENT_SELECT, + IOCTL_AFD_ENUM_NETWORK_EVENTS, + IOCTL_AFD_DEFER_ACCEPT, + IOCTL_AFD_WAIT_FOR_LISTEN_LIFO, + IOCTL_AFD_SET_QOS, + IOCTL_AFD_GET_QOS, + IOCTL_AFD_NO_OPERATION, + IOCTL_AFD_VALIDATE_GROUP, + IOCTL_AFD_GET_UNACCEPTED_CONNECT_DATA + +#ifdef NT351 + ,IOCTL_AFD_QUEUE_APC +#endif + }; + +#define NUM_AFD_IOCTLS ( sizeof(AfdIoctlTable) / sizeof(AfdIoctlTable[0]) ) + + +NTSTATUS +AfdDispatch ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the dispatch routine for AFD. + +Arguments: + + DeviceObject - Pointer to device object for target device + + Irp - Pointer to I/O request packet + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PIO_STACK_LOCATION irpSp; + NTSTATUS status; +#if DBG + KIRQL oldIrql; + + oldIrql = KeGetCurrentIrql( ); +#endif + + DeviceObject; // prevent compiler warnings + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + switch ( irpSp->MajorFunction ) { + + case IRP_MJ_DEVICE_CONTROL: + + return AfdDispatchDeviceControl( Irp, irpSp ); + + case IRP_MJ_WRITE: + + // + // Make the IRP look like a send IRP. + // + + ASSERT( FIELD_OFFSET( IO_STACK_LOCATION, Parameters.Write.Length ) == + FIELD_OFFSET( IO_STACK_LOCATION, Parameters.DeviceIoControl.OutputBufferLength ) ); + ASSERT( FIELD_OFFSET( IO_STACK_LOCATION, Parameters.Write.Key ) == + FIELD_OFFSET( IO_STACK_LOCATION, Parameters.DeviceIoControl.InputBufferLength ) ); + irpSp->Parameters.Write.Key = 0; + + status = AfdSend( Irp, irpSp ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case IRP_MJ_READ: + + // + // Make the IRP look like a receive IRP. + // + + ASSERT( FIELD_OFFSET( IO_STACK_LOCATION, Parameters.Read.Length ) == + FIELD_OFFSET( IO_STACK_LOCATION, Parameters.DeviceIoControl.OutputBufferLength ) ); + ASSERT( FIELD_OFFSET( IO_STACK_LOCATION, Parameters.Read.Key ) == + FIELD_OFFSET( IO_STACK_LOCATION, Parameters.DeviceIoControl.InputBufferLength ) ); + irpSp->Parameters.Read.Key = 0; + + status = AfdReceive( Irp, irpSp ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case IRP_MJ_CREATE: + + status = AfdCreate( Irp, irpSp ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + return status; + + case IRP_MJ_CLEANUP: + + status = AfdCleanup( Irp, irpSp ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case IRP_MJ_CLOSE: + + status = AfdClose( Irp, irpSp ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + default: + KdPrint(( "AfdDispatch: Invalid major function %lx\n", + irpSp->MajorFunction )); + Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + return STATUS_NOT_IMPLEMENTED; + } + +} // AfdDispatch + + +NTSTATUS +AfdDispatchDeviceControl ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This is the dispatch routine for AFD IOCTLs. + +Arguments: + + Irp - Pointer to I/O request packet. + + IrpSp - pointer to the stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + ULONG code; + ULONG request; + NTSTATUS status; +#if DBG + KIRQL oldIrql; + + oldIrql = KeGetCurrentIrql( ); +#endif + + + // + // Extract the IOCTL control code and process the request. + // + + code = IrpSp->Parameters.DeviceIoControl.IoControlCode; + request = _AFD_REQUEST(code); + + if( _AFD_BASE(code) == FSCTL_AFD_BASE && + request < NUM_AFD_IOCTLS && + AfdIoctlTable[request] == code ) { + + switch( request ) { + + case AFD_SEND: + + status = AfdSend( Irp, IrpSp ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_SEND_DATAGRAM: + + status = AfdSendDatagram( Irp, IrpSp ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_RECEIVE: + + status = AfdReceive( Irp, IrpSp ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_RECEIVE_DATAGRAM: + + status = AfdReceiveDatagram( Irp, IrpSp, 0, 0 ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_TRANSMIT_FILE: + + status = AfdTransmitFile( Irp, IrpSp ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_BIND: + + status = AfdBind( Irp, IrpSp ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_CONNECT: + + return AfdConnect( Irp, IrpSp ); + + case AFD_START_LISTEN: + + status = AfdStartListen( Irp, IrpSp ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_WAIT_FOR_LISTEN: + case AFD_WAIT_FOR_LISTEN_LIFO: + + status = AfdWaitForListen( Irp, IrpSp ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_ACCEPT: + + status = AfdAccept( Irp, IrpSp ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_PARTIAL_DISCONNECT: + + status = AfdPartialDisconnect( Irp, IrpSp ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_GET_ADDRESS: + + status = AfdGetAddress( Irp, IrpSp ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_POLL: + + status = AfdPoll( Irp, IrpSp ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_QUERY_RECEIVE_INFO: + + status = AfdQueryReceiveInformation( Irp, IrpSp ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_QUERY_HANDLES: + + status = AfdQueryHandles( Irp, IrpSp ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_GET_CONTEXT_LENGTH: + + status = AfdGetContextLength( Irp, IrpSp ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_GET_CONTEXT: + + status = AfdGetContext( Irp, IrpSp ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_SET_CONTEXT: + + status = AfdSetContext( Irp, IrpSp ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_SET_INFORMATION: + + status = AfdSetInformation( Irp, IrpSp ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_GET_INFORMATION: + + status = AfdGetInformation( Irp, IrpSp ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_SET_CONNECT_DATA: + case AFD_SET_CONNECT_OPTIONS: + case AFD_SET_DISCONNECT_DATA: + case AFD_SET_DISCONNECT_OPTIONS: + case AFD_SIZE_CONNECT_DATA: + case AFD_SIZE_CONNECT_OPTIONS: + case AFD_SIZE_DISCONNECT_DATA: + case AFD_SIZE_DISCONNECT_OPTIONS: + + status = AfdSetConnectData( Irp, IrpSp, code ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_GET_CONNECT_DATA: + case AFD_GET_CONNECT_OPTIONS: + case AFD_GET_DISCONNECT_DATA: + case AFD_GET_DISCONNECT_OPTIONS: + + status = AfdGetConnectData( Irp, IrpSp, code ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_SUPER_ACCEPT: + + status = AfdSuperAccept( Irp, IrpSp ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_EVENT_SELECT : + + status = AfdEventSelect( Irp, IrpSp ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql() == LOW_LEVEL ); + + return status; + + case AFD_ENUM_NETWORK_EVENTS : + + status = AfdEnumNetworkEvents( Irp, IrpSp ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql() == LOW_LEVEL ); + + return status; + + case AFD_DEFER_ACCEPT: + + status = AfdDeferAccept( Irp, IrpSp ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_SET_QOS : + + status = AfdSetQos( Irp, IrpSp ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_GET_QOS : + + status = AfdGetQos( Irp, IrpSp ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_NO_OPERATION : + + status = AfdNoOperation( Irp, IrpSp ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_VALIDATE_GROUP : + + status = AfdValidateGroup( Irp, IrpSp ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + + case AFD_GET_UNACCEPTED_CONNECT_DATA : + + status = AfdGetUnacceptedConnectData( Irp, IrpSp ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql( ) == oldIrql ); + + return status; + +#ifdef NT351 + case AFD_QUEUE_APC : + + status = AfdQueueUserApc( Irp, IrpSp ); + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + ASSERT( KeGetCurrentIrql() == LOW_LEVEL ); + + return status; +#endif // NT351 + + } + + } + + // + // If we made it this far, then the ioctl is invalid. + // + + KdPrint(( + "AfdDispatchDeviceControl: invalid IOCTL %08lX\n", + code + )); + + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + return STATUS_INVALID_DEVICE_REQUEST; + +} // AfdDispatchDeviceControl + diff --git a/private/ntos/afd/eventsel.c b/private/ntos/afd/eventsel.c new file mode 100644 index 000000000..b7f0ab1ea --- /dev/null +++ b/private/ntos/afd/eventsel.c @@ -0,0 +1,478 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + eventsel.c + +Abstract: + + This module contains routines for supporting the WinSock 2.0 + WSAEventSelect() and WSAEnumNetworkEvents() APIs. + +Author: + + Keith Moore (keithmo) 02-Aug-1995 + +Revision History: + +--*/ + +#include "afdp.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, AfdEventSelect ) +#pragma alloc_text( PAGE, AfdEnumNetworkEvents ) +#endif + + + +NTSTATUS +AfdEventSelect ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Associates an event object with the socket such that the event object + will be signalled when any of the specified network events becomes + active. + +Arguments: + + Irp - Pointer to I/O request packet. + + IrpSp - pointer to the IO stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates whether the APC was successfully queued. + +--*/ + +{ + + NTSTATUS status; + PAFD_ENDPOINT endpoint; + PAFD_EVENT_SELECT_INFO eventInfo; + KIRQL oldIrql; + PKEVENT eventObject; + ULONG eventMask; + + PAGED_CODE( ); + + // + // Validate the parameters. + // + + eventInfo = Irp->AssociatedIrp.SystemBuffer; + + if( eventInfo == NULL || + IrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof(*eventInfo) || + ( eventInfo->Event == NULL ^ + eventInfo->PollEvents == 0 ) ) { + + return STATUS_INVALID_PARAMETER; + + } + + // + // Reference the target event object. + // + + + eventObject = NULL; + + if( eventInfo->Event != NULL ) { + + status = AfdReferenceEventObjectByHandle( + eventInfo->Event, + Irp->RequestorMode, + (PVOID *)&eventObject + ); + + if( !NT_SUCCESS(status) ) { + + return status; + + } + + ASSERT( eventObject != NULL ); + + } + + // + // Grab the endpoint from the socket handle. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + // + // Acquire the spinlock protecting the endpoint. + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + // + // If this endpoint has an active EventSelect, dereference the + // associated event object. + // + + if( endpoint->EventObject != NULL ) { + + ObDereferenceObject( endpoint->EventObject ); + + } + + // + // Fill in the info. + // + + endpoint->EventObject = eventObject; + endpoint->EventsEnabled = eventInfo->PollEvents; + + if( endpoint->State == AfdEndpointStateListening ) { + + endpoint->EventsDisabled = AFD_DISABLED_LISTENING_POLL_EVENTS; + + } else { + + endpoint->EventsDisabled = 0; + + } + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdEventSelect:\n" + )); + + KdPrint(( + " Endpoint %08lX\n", + endpoint + )); + + KdPrint(( + " EventObject %08lX\n", + eventObject + )); + + KdPrint(( + " EventsEnabled %08lX\n", + endpoint->EventsEnabled + )); + + KdPrint(( + " EventsDisabled %08lX\n", + endpoint->EventsDisabled + )); + + KdPrint(( + " EventsActive %08lX\n", + endpoint->EventsActive + )); + } + + // + // While we've got the spinlock held, determine if any conditions + // are met, and if so, signal the event object. + // + + eventMask = endpoint->EventsActive & endpoint->EventsEnabled & + ~endpoint->EventsDisabled; + + if( eventMask != 0 && eventObject != NULL ) { + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdEventSelect: Setting event %08lX\n", + eventObject + )); + } + + KeSetEvent( + eventObject, + AfdPriorityBoost, + FALSE + ); + + } + + // + // Release the spin lock and return. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + return STATUS_SUCCESS; + +} // AfdEventSelect + + +NTSTATUS +AfdEnumNetworkEvents ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Retrieves event select information from the socket. + +Arguments: + + Irp - Pointer to I/O request packet. + + IrpSp - pointer to the IO stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates whether the APC was successfully queued. + +--*/ + +{ + + NTSTATUS status; + PAFD_ENDPOINT endpoint; + PAFD_ENUM_NETWORK_EVENTS_INFO eventInfo; + KIRQL oldIrql; + PKEVENT eventObject; + ULONG pollEvents; + + PAGED_CODE( ); + + // + // Validate the parameters. + // + + eventInfo = Irp->AssociatedIrp.SystemBuffer; + + if( eventInfo == NULL || + IrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof(*eventInfo) || + IrpSp->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(*eventInfo) ) { + + return STATUS_INVALID_PARAMETER; + + } + + // + // Reference the target event object. + // + + eventObject = NULL; + + if( eventInfo->Event != NULL ) { + + status = AfdReferenceEventObjectByHandle( + eventInfo->Event, + Irp->RequestorMode, + (PVOID *)&eventObject + ); + + if( !NT_SUCCESS(status) ) { + + return status; + + } + + ASSERT( eventObject != NULL ); + + } + + // + // Grab the endpoint from the socket handle. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + // + // Acquire the spinlock protecting the endpoint. + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdEnumNetworkEvents:\n" + )); + + KdPrint(( + " Endpoint %08lX\n", + endpoint + )); + + KdPrint(( + " EventObject %08lX\n", + eventObject + )); + + KdPrint(( + " EventsEnabled %08lX\n", + endpoint->EventsEnabled + )); + + KdPrint(( + " EventsDisabled %08lX\n", + endpoint->EventsDisabled + )); + + KdPrint(( + " EventsActive %08lX\n", + endpoint->EventsActive + )); + } + + // + // Copy the data to the user's structure. + // + + pollEvents = endpoint->EventsActive & endpoint->EventsEnabled & + ~endpoint->EventsDisabled; + eventInfo->PollEvents = pollEvents; + + RtlCopyMemory( + eventInfo->EventStatus, + endpoint->EventStatus, + sizeof(endpoint->EventStatus) + ); + + // + // If there was an event object handle passed in with this + // request, reset and dereference it. + // + + if( eventObject != NULL ) { + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdEnumNetworkEvents: Resetting event %08lX\n", + eventObject + )); + } + + KeResetEvent( eventObject ); + ObDereferenceObject( eventObject ); + + } + + // + // Release the spin lock and return. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // Before returning, tell the I/O subsystem how may bytes to copy + // to the user's output buffer. + // + + Irp->IoStatus.Information = sizeof(*eventInfo); + + return STATUS_SUCCESS; + +} // AfdEnumNetworkEvents + + +VOID +AfdIndicateEventSelectEvent ( + IN PAFD_ENDPOINT Endpoint, + IN ULONG PollEventBit, + IN NTSTATUS Status + ) +{ + ULONG event; + ULONG oldEventsActive; + + // + // Sanity check. + // + + ASSERT( IS_AFD_ENDPOINT_TYPE( Endpoint ) ); + ASSERT( PollEventBit < AFD_NUM_POLL_EVENTS ); + ASSERT( KeGetCurrentIrql() >= DISPATCH_LEVEL ); + + // + // Calculate the actual event bit. + // + + event = 1 << PollEventBit; + + oldEventsActive = Endpoint->EventsActive; + Endpoint->EventsActive |= event; + Endpoint->EventStatus[PollEventBit] = Status; + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdIndicateEventSelectEvent:\n" + )); + + KdPrint(( + " Endpoint %08lX\n", + Endpoint + )); + + KdPrint(( + " EventObject %08lX\n", + Endpoint->EventObject + )); + + KdPrint(( + " EventsEnabled %08lX\n", + Endpoint->EventsEnabled + )); + + KdPrint(( + " EventsDisabled %08lX\n", + Endpoint->EventsDisabled + )); + + KdPrint(( + " EventsActive %08lX\n", + Endpoint->EventsActive + )); + + KdPrint(( + " Indicated Event %08lX\n", + event + )); + } + + // + // Only signal the endpoint's event object if the current event + // is enabled, AND the current event was not already active, AND + // there is an event object associated with this endpoint. + // + + event &= Endpoint->EventsEnabled & ~Endpoint->EventsDisabled & + ~oldEventsActive; + + if( event != 0 && Endpoint->EventObject != NULL ) { + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdIndicateEventSelectEvent: Setting event %08lX\n", + Endpoint->EventObject + )); + } + + KeSetEvent( + Endpoint->EventObject, + AfdPriorityBoost, + FALSE + ); + + } + +} // AfdIndicateEventSelectEvent + diff --git a/private/ntos/afd/fastio.c b/private/ntos/afd/fastio.c new file mode 100644 index 000000000..e32cd4065 --- /dev/null +++ b/private/ntos/afd/fastio.c @@ -0,0 +1,2095 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + fastio.c + +Abstract: + + This module contains routines for handling fast ("turbo") IO + in AFD. + +Author: + + David Treadwell (davidtr) 12-Oct-1992 + +Revision History: + +--*/ + +#include "afdp.h" + +BOOLEAN +AfdFastDatagramIo ( + IN struct _FILE_OBJECT *FileObject, + IN PVOID InputBuffer OPTIONAL, + IN ULONG InputBufferLength, + IN ULONG IoControlCode, + OUT PIO_STATUS_BLOCK IoStatus + ); + +BOOLEAN +AfdFastDatagramReceive ( + IN PAFD_ENDPOINT Endpoint, + IN ULONG ReceiveFlags, + IN ULONG AfdFlags, + IN LPWSABUF BufferArray, + IN ULONG BufferCount, + IN ULONG ReceiveBufferLength, + OUT PVOID SourceAddress, + OUT PULONG SourceAddressLength, + OUT PIO_STATUS_BLOCK IoStatus + ); + +BOOLEAN +AfdFastDatagramSend ( + IN PAFD_BUFFER AfdBuffer, + IN PAFD_ENDPOINT Endpoint, + OUT PIO_STATUS_BLOCK IoStatus + ); + +NTSTATUS +AfdRestartFastSendDatagram ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +PAFD_BUFFER +CopyAddressToBuffer ( + IN PAFD_ENDPOINT Endpoint, + IN ULONG OutputBufferLength + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, AfdFastDatagramIo ) +#pragma alloc_text( PAGE, AfdFastDatagramSend ) +#pragma alloc_text( PAGE, AfdFastIoRead ) +#pragma alloc_text( PAGE, AfdFastIoWrite ) +#pragma alloc_text( PAGEAFD, AfdFastIoDeviceControl ) +#pragma alloc_text( PAGEAFD, AfdFastDatagramReceive ) +#pragma alloc_text( PAGEAFD, AfdRestartFastSendDatagram ) +#pragma alloc_text( PAGEAFD, CopyAddressToBuffer ) +#pragma alloc_text( PAGEAFD, AfdShouldSendBlock ) +#endif + +BOOLEAN +AfdFastIoRead ( + IN struct _FILE_OBJECT *FileObject, + IN PLARGE_INTEGER FileOffset, + IN ULONG Length, + IN BOOLEAN Wait, + IN ULONG LockKey, + OUT PVOID Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN struct _DEVICE_OBJECT *DeviceObject + ) +{ + + AFD_RECV_INFO recvInfo; + WSABUF wsaBuf; + + PAGED_CODE( ); + + UNREFERENCED_PARAMETER( FileOffset ); + UNREFERENCED_PARAMETER( LockKey ); + + // + // Build the (one and only) WSABUF. + // + + wsaBuf.buf = Buffer; + wsaBuf.len = Length; + + // + // Setup the AFD_RECV_INFO structure. + // + + recvInfo.BufferArray = &wsaBuf; + recvInfo.BufferCount = 1; + recvInfo.AfdFlags = AFD_OVERLAPPED; + recvInfo.TdiFlags = TDI_RECEIVE_NORMAL; + + // + // Fake an ioctl. + // + + return AfdFastIoDeviceControl( + FileObject, + Wait, + &recvInfo, + sizeof(recvInfo), + NULL, + 0, + IOCTL_AFD_RECEIVE, + IoStatus, + DeviceObject + ); + +} // AfdFastIoRead + +BOOLEAN +AfdFastIoWrite ( + IN struct _FILE_OBJECT *FileObject, + IN PLARGE_INTEGER FileOffset, + IN ULONG Length, + IN BOOLEAN Wait, + IN ULONG LockKey, + IN PVOID Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN struct _DEVICE_OBJECT *DeviceObject + ) +{ + + AFD_SEND_INFO sendInfo; + WSABUF wsaBuf; + + PAGED_CODE( ); + + UNREFERENCED_PARAMETER( FileOffset ); + UNREFERENCED_PARAMETER( LockKey ); + + // + // Build the (one and only) WSABUF. + // + + wsaBuf.buf = Buffer; + wsaBuf.len = Length; + + // + // Setup the AFD_SEND_INFO structure. + // + + sendInfo.BufferArray = &wsaBuf; + sendInfo.BufferCount = 1; + sendInfo.AfdFlags = AFD_OVERLAPPED; + sendInfo.TdiFlags = 0; + + // + // Fake an ioctl. + // + + return AfdFastIoDeviceControl( + FileObject, + Wait, + &sendInfo, + sizeof(sendInfo), + NULL, + 0, + IOCTL_AFD_SEND, + IoStatus, + DeviceObject + ); + +} // AfdFastIoWrite + +#if AFD_PERF_DBG + +BOOLEAN +AfdFastIoDeviceControlReal ( + IN struct _FILE_OBJECT *FileObject, + IN BOOLEAN Wait, + IN PVOID InputBuffer OPTIONAL, + IN ULONG InputBufferLength, + OUT PVOID OutputBuffer OPTIONAL, + IN ULONG OutputBufferLength, + IN ULONG IoControlCode, + OUT PIO_STATUS_BLOCK IoStatus + ); + + +BOOLEAN +AfdFastIoDeviceControl ( + IN struct _FILE_OBJECT *FileObject, + IN BOOLEAN Wait, + IN PVOID InputBuffer OPTIONAL, + IN ULONG InputBufferLength, + OUT PVOID OutputBuffer OPTIONAL, + IN ULONG OutputBufferLength, + IN ULONG IoControlCode, + OUT PIO_STATUS_BLOCK IoStatus, + IN struct _DEVICE_OBJECT *DeviceObject + ) +{ + BOOLEAN success; + + if ( AfdDisableFastIo ) { + return FALSE; + } + + ASSERT( KeGetCurrentIrql( ) == LOW_LEVEL ); + + success = AfdFastIoDeviceControlReal ( + FileObject, + Wait, + InputBuffer, + InputBufferLength, + OutputBuffer, + OutputBufferLength, + IoControlCode, + IoStatus + ); + + ASSERT( KeGetCurrentIrql( ) == LOW_LEVEL ); + + switch ( IoControlCode ) { + + case IOCTL_AFD_SEND: + + if ( success ) { + AfdFastSendsSucceeded++; + } else { + AfdFastSendsFailed++; + } + break; + + case IOCTL_AFD_RECEIVE: + + if ( success ) { + AfdFastReceivesSucceeded++; + } else { + AfdFastReceivesFailed++; + } + break; + + case IOCTL_AFD_SEND_DATAGRAM: + + if ( success ) { + AfdFastSendDatagramsSucceeded++; + } else { + AfdFastSendDatagramsFailed++; + } + break; + + case IOCTL_AFD_RECEIVE_DATAGRAM: + + if ( success ) { + AfdFastReceiveDatagramsSucceeded++; + } else { + AfdFastReceiveDatagramsFailed++; + } + break; + + case IOCTL_AFD_POLL: + + if ( success ) { + AfdFastPollsSucceeded++; + } else { + AfdFastPollsFailed++; + } + break; + } + + return success; + +} // AfdFastIoDeviceControl + +BOOLEAN +AfdFastIoDeviceControlReal ( + IN struct _FILE_OBJECT *FileObject, + IN BOOLEAN Wait, + IN PVOID InputBuffer OPTIONAL, + IN ULONG InputBufferLength, + OUT PVOID OutputBuffer OPTIONAL, + IN ULONG OutputBufferLength, + IN ULONG IoControlCode, + OUT PIO_STATUS_BLOCK IoStatus + ) +#else + +BOOLEAN +AfdFastIoDeviceControl ( + IN struct _FILE_OBJECT *FileObject, + IN BOOLEAN Wait, + IN PVOID InputBuffer OPTIONAL, + IN ULONG InputBufferLength, + OUT PVOID OutputBuffer OPTIONAL, + IN ULONG OutputBufferLength, + IN ULONG IoControlCode, + OUT PIO_STATUS_BLOCK IoStatus, + IN struct _DEVICE_OBJECT *DeviceObject + ) +#endif +{ + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + KIRQL oldIrql; + PAFD_BUFFER afdBuffer; + NTSTATUS status; + PMDL MdlChain; + + // + // All we want to do is pass the request through to the TDI provider + // if possible. First get the endpoint and connection pointers. + // + + endpoint = FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + // + // If the endpoint is shut down in any way, bail out of fast IO. + // + + if ( endpoint->DisconnectMode != 0 ) { + return FALSE; + } + + // + // If an OutputBuffer parameter was specified, then this is a more + // complicated IO request, so bail on fast IO. + // + + if( OutputBuffer != NULL ) { + return FALSE; + } + + // + // Handle datagram fast IO in a subroutine. This keeps this routine + // cleaner and faster. + // + + if ( IS_DGRAM_ENDPOINT(endpoint) ) { + return AfdFastDatagramIo( + FileObject, + InputBuffer, + InputBufferLength, + IoControlCode, + IoStatus + ); + } + + // + // If the endpoint isn't connected yet, then we don't want to + // attempt fast IO on it. + // + + if ( endpoint->State != AfdEndpointStateConnected ) { + return FALSE; + } + + ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ); + ASSERT( endpoint->Common.VcConnecting.Connection != NULL ); + + // + // If the TDI provider for this endpoint supports bufferring, + // don't use fast IO. + // + + if ( endpoint->TdiBufferring ) { + return FALSE; + } + + connection = endpoint->Common.VcConnecting.Connection; + ASSERT( connection->Type == AfdBlockTypeConnection ); + + if( connection->CleanupBegun ) { + return FALSE; + } + + IF_DEBUG(FAST_IO) { + KdPrint(( "AfdFastIoDeviceControl: attempting fast IO on endp %lx, " + "conn %lx, code %lx\n", + endpoint, connection, IoControlCode )); + } + + // + // Based on whether this is a send or receive, attempt to perform + // fast IO. + // + + switch ( IoControlCode ) { + + case IOCTL_AFD_SEND: { + + PAFD_SEND_INFO sendInfo; + ULONG sendLength; + ULONG afdFlags; + + // + // If the connection has been aborted, then we don't want to try + // fast IO on it. + // + + if ( connection->AbortIndicated ) { + return FALSE; + } + + // + // If the input structure isn't large enough, bail on fast IO. + // + + if( InputBufferLength < sizeof(*sendInfo) ) { + return FALSE; + } + + try { + + // + // Make a quick preliminary check of the input buffer. If it's + // bogus (or if Fast IO is disabled), then fail the request. + // + + sendInfo = (PAFD_SEND_INFO)InputBuffer; + afdFlags = sendInfo->AfdFlags; + + if( (afdFlags & AFD_NO_FAST_IO) != 0 || + sendInfo->TdiFlags != 0 || + sendInfo->BufferArray == NULL || + sendInfo->BufferCount == 0 ) { + + return FALSE; + + } + + // + // Calculate the length of the send buffer. + // + + sendLength = AfdCalcBufferArrayByteLengthRead( + sendInfo->BufferArray, + sendInfo->BufferCount + ); + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + return FALSE; + } + + // + // Determine whether we can do fast IO with this send. In order + // to perform fast IO, there must be no other sends pended on this + // connection and there must be enough space left for bufferring + // the requested amount of data. + // + + if ( AfdShouldSendBlock( endpoint, connection, sendLength ) ) { + + // + // If this is a nonblocking endpoint, fail the request here and + // save going through the regular path. + // + + if ( endpoint->NonBlocking && !( afdFlags & AFD_OVERLAPPED ) ) { + IoStatus->Status = STATUS_DEVICE_NOT_READY; + return TRUE; + } + + return FALSE; + } + + // + // Next get an AFD buffer structure that contains an IRP and a + // buffer to hold the data. + // + + afdBuffer = AfdGetBuffer( sendLength, 0 ); + + if ( afdBuffer == NULL ) { + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + connection->VcBufferredSendBytes -= sendLength; + connection->VcBufferredSendCount -= 1; + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + return FALSE; + } + + // + // We have to rebuild the MDL in the AFD buffer structure to + // represent exactly the number of bytes we're going to be + // sending. + // + + afdBuffer->Mdl->ByteCount = sendLength; + SET_CHAIN_LENGTH( afdBuffer, sendLength ); + + // + // Remember the endpoint in the AFD buffer structure. We need + // this in order to access the endpoint in the restart routine. + // + + afdBuffer->Context = endpoint; + + // + // Copy the user's data into the AFD buffer. + // + + if( sendLength > 0 ) { + + try { + + AfdCopyBufferArrayToBuffer( + afdBuffer->Buffer, + sendLength, + sendInfo->BufferArray, + sendInfo->BufferCount + ); + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + afdBuffer->Mdl->ByteCount = afdBuffer->BufferLength; + RESET_CHAIN_LENGTH( afdBuffer ); + AfdReturnBuffer( afdBuffer ); + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + connection->VcBufferredSendBytes -= sendLength; + connection->VcBufferredSendCount -= 1; + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + return FALSE; + + } + + } + + // + // Use the IRP in the AFD buffer structure to give to the TDI + // provider. Build the TDI send request. + // + + TdiBuildSend( + afdBuffer->Irp, + connection->DeviceObject, + connection->FileObject, + AfdRestartBufferSend, + afdBuffer, + afdBuffer->Mdl, + 0, + sendLength + ); + + // + // Add a reference to the connection object since the send + // request will complete asynchronously. + // + + REFERENCE_CONNECTION( connection ); + + // + // Call the transport to actually perform the send. + // + + status = IoCallDriver( + connection->DeviceObject, + afdBuffer->Irp + ); + + // + // Complete the user's IRP as appropriate. Note that we change the + // status code from what was returned by the TDI provider into + // STATUS_SUCCESS. This is because we don't want to complete + // the IRP with STATUS_PENDING etc. + // + + if ( NT_SUCCESS(status) ) { + IoStatus->Information = sendLength; + IoStatus->Status = STATUS_SUCCESS; + return TRUE; + } + + // + // The call failed for some reason. Fail fast IO. + // + + return FALSE; + + } + + case IOCTL_AFD_RECEIVE: { + + PLIST_ENTRY listEntry; + PAFD_RECV_INFO recvInfo; + ULONG recvLength; + ULONG totalOffset; + + // + // If the input structure isn't large enough, bail on fast IO. + // + + if( InputBufferLength < sizeof(*recvInfo) ) { + return FALSE; + } + + try { + + // + // Make a quick preliminary check of the input buffer. If it's + // bogus (or if Fast IO is disabled), then fail the request. + // + + recvInfo = (PAFD_RECV_INFO)InputBuffer; + + if( (recvInfo->AfdFlags & AFD_NO_FAST_IO) != 0 || + recvInfo->TdiFlags != TDI_RECEIVE_NORMAL || + recvInfo->BufferArray == NULL || + recvInfo->BufferCount == 0 ) { + + return FALSE; + + } + + // + // Calculate the length of the receive buffer. + // + + recvLength = AfdCalcBufferArrayByteLengthWrite( + recvInfo->BufferArray, + recvInfo->BufferCount + ); + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + return FALSE; + } + + // + // Determine whether we'll be able to perform fast IO. In order + // to do fast IO, there must be some bufferred data on the + // connection, there must not be any pended receives on the + // connection, and there must not be any bufferred expedited + // data on the connection. This last requirement is for + // the sake of simplicity only. + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + if ( connection->VcBufferredReceiveCount == 0 || + !IsListEmpty( &connection->VcReceiveIrpListHead ) || + connection->VcBufferredExpeditedCount != 0 ) { + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + return FALSE; + } + + ASSERT( !IsListEmpty( &connection->VcReceiveBufferListHead ) ); + + // + // Get a pointer to the first bufferred AFD buffer structure on + // the connection. + // + + afdBuffer = CONTAINING_RECORD( + connection->VcReceiveBufferListHead.Flink, + AFD_BUFFER, + BufferListEntry + ); + + ASSERT( !afdBuffer->ExpeditedData ); + + // + // If the buffer contains a partial message, bail out of the + // fast path. We don't want the added complexity of handling + // partial messages in the fast path. + // + + if ( afdBuffer->PartialMessage && + endpoint->EndpointType != AfdEndpointTypeStream ) { + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + return FALSE; + } + + // + // For simplicity, act differently based on whether the user's + // buffer is large enough to hold the entire first AFD buffer + // worth of data. + // + + if ( recvLength >= afdBuffer->DataLength ) { + + LIST_ENTRY bufferListHead; + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = 0; + + InitializeListHead( &bufferListHead ); + + // + // Loop getting AFD buffers that will fill in the user's + // buffer with as much data as will fit, or else with a + // single buffer if this is not a stream endpoint. We don't + // actually do the copy within this loop because this loop + // must occur while holding a lock, and we cannot hold a + // lock while copying the data into the user's buffer + // because the user's buffer is not locked and we cannot + // take a page fault at raised IRQL. + // + + do { + + // + // Update the count of bytes on the connection. + // + + ASSERT( connection->VcBufferredReceiveBytes >= afdBuffer->DataLength ); + ASSERT( connection->VcBufferredReceiveCount > 0 ); + + connection->VcBufferredReceiveBytes -= afdBuffer->DataLength; + connection->VcBufferredReceiveCount -= 1; + IoStatus->Information += afdBuffer->DataLength; + + // + // Remove the AFD buffer from the connection's list of + // buffers and place it on our local list of buffers. + // + + RemoveEntryList( &afdBuffer->BufferListEntry ); + InsertTailList( &bufferListHead, &afdBuffer->BufferListEntry ); + + // + // If this is a stream endpoint and all of the data in + // the next AFD buffer will fit in the user's buffer, + // use this buffer for the IO as well. + // + + if ( !IsListEmpty( &connection->VcReceiveBufferListHead ) ) { + + afdBuffer = CONTAINING_RECORD( + connection->VcReceiveBufferListHead.Flink, + AFD_BUFFER, + BufferListEntry + ); + + ASSERT( !afdBuffer->ExpeditedData ); + ASSERT( afdBuffer->DataOffset == 0 ); + + if ( endpoint->EndpointType == AfdEndpointTypeStream && + IoStatus->Information + afdBuffer->DataLength <= + recvLength ) { + continue; + } else { + break; + } + + } else { + + break; + } + + } while ( TRUE ); + + // + // If there is indicated but unreceived data in the TDI provider, + // and we have available buffer space, fire off an IRP to receive + // the data. + // + + if ( connection->VcReceiveCountInTransport > 0 + + && + + connection->VcBufferredReceiveBytes < + connection->MaxBufferredReceiveBytes + + && + + connection->VcBufferredReceiveCount < + connection->MaxBufferredReceiveCount ) { + + CLONG bytesToReceive; + + // + // Remember the count of data that we're going to receive, + // then reset the fields in the connection where we keep + // track of how much data is available in the transport. + // We reset it here before releasing the lock so that + // another thread doesn't try to receive the data at the + // same time as us. + // + + if ( connection->VcReceiveBytesInTransport > AfdLargeBufferSize ) { + bytesToReceive = connection->VcReceiveBytesInTransport; + } else { + bytesToReceive = AfdLargeBufferSize; + } + + ASSERT( connection->VcReceiveCountInTransport == 1 ); + connection->VcReceiveBytesInTransport = 0; + connection->VcReceiveCountInTransport = 0; + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // Get an AFD buffer structure to hold the data. + // + + afdBuffer = AfdGetBuffer( bytesToReceive, 0 ); + + if ( afdBuffer == NULL ) { + + // + // If we were unable to get a buffer, abort the + // circuit. + // + + AfdBeginAbort( connection ); + + } else { + + // + // We need to remember the connection in the AFD buffer + // because we'll need to access it in the completion + // routine. + // + + afdBuffer->Context = connection; + + // + // Finish building the receive IRP to give to the TDI provider. + // + + TdiBuildReceive( + afdBuffer->Irp, + connection->DeviceObject, + connection->FileObject, + AfdRestartBufferReceive, + afdBuffer, + afdBuffer->Mdl, + TDI_RECEIVE_NORMAL, + bytesToReceive + ); + + // + // Hand off the IRP to the TDI provider. + // + + (VOID)IoCallDriver( + connection->DeviceObject, + afdBuffer->Irp + ); + } + + } else { + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + } + + // + // We have in a local list all the data we'll use for this + // IO. Start copying data to the user buffer. + // + + totalOffset = 0; + + try { + + while ( !IsListEmpty( &bufferListHead ) ) { + + // + // Take the first buffer from the list. + // + + listEntry = RemoveHeadList( &bufferListHead ); + afdBuffer = CONTAINING_RECORD( + listEntry, + AFD_BUFFER, + BufferListEntry + ); + + if( afdBuffer->DataLength > 0 ) { + + // + // Copy the data in the buffer to the user buffer. + // + + AfdCopyBufferToBufferArray( + recvInfo->BufferArray, + totalOffset, + recvInfo->BufferCount, + (PCHAR)afdBuffer->Buffer + afdBuffer->DataOffset, + afdBuffer->DataLength + ); + + totalOffset += afdBuffer->DataLength; + + } + + // + // We're done with the AFD buffer. + // + + afdBuffer->DataOffset = 0; + RESET_CHAIN_LENGTH( afdBuffer ); + AfdReturnBuffer( afdBuffer ); + } + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + // + // If an exception is hit, there is the possibility of + // data corruption. However, it is nearly impossible to + // avoid this in all cases, so just throw out the + // remainder of the data that we would have copied to + // the user buffer. + // + + afdBuffer->DataOffset = 0; + RESET_CHAIN_LENGTH( afdBuffer ); + AfdReturnBuffer( afdBuffer ); + + while ( !IsListEmpty( &bufferListHead ) ) { + listEntry = RemoveHeadList( &bufferListHead ); + afdBuffer = CONTAINING_RECORD( + listEntry, + AFD_BUFFER, + BufferListEntry + ); + RESET_CHAIN_LENGTH( afdBuffer ); + AfdReturnBuffer( afdBuffer ); + } + + return FALSE; + } + + // + // Clear the receive data active bit. If there's more data + // available, set the corresponding event. + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + endpoint->EventsActive &= ~AFD_POLL_RECEIVE; + + if( !IsListEmpty( &connection->VcReceiveBufferListHead ) ) { + + AfdIndicateEventSelectEvent( + endpoint, + AFD_POLL_RECEIVE_BIT, + STATUS_SUCCESS + ); + + } + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // Fast IO succeeded! + // + + ASSERT( IoStatus->Information <= recvLength ); + + return TRUE; + + } else { + + PAFD_BUFFER tempAfdBuffer; + + // + // If this is not a stream endpoint and the user's buffer + // is insufficient to hold the entire message, then we cannot + // use fast IO because this IO will need to fail with + // STATUS_BUFFER_OVERFLOW. + // + + if ( endpoint->EndpointType != AfdEndpointTypeStream || + recvLength == 0 ) { + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + return FALSE; + } + + // + // This is a stream endpoint and the user buffer is + // insufficient for the amount of data stored in the first + // buffer. So that we can perform fast IO and still + // preserve data ordering, we're going to allocate a new AFD + // buffer structure, copy the appropriate amount of data + // into that buffer, then release locks and copy the data + // into the user buffer. + // + // The extra copy incurred is still less than the IRP + // overhead incurred in the normal IO path, so using fast IO + // here is a win. Also, applications which force us through + // this path will typically be using very small receives, + // like one or four bytes, so the copy will be short. + // + // First allocate an AFD buffer to hold the data we're + // eventually going to give to the user. + // + + tempAfdBuffer = AfdGetBuffer( recvLength, 0 ); + if ( tempAfdBuffer == NULL ) { + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + return FALSE; + } + + // + // Copy the first part of the bufferred data into our local + // AFD buffer. + // + + RtlCopyMemory( + tempAfdBuffer->Buffer, + (PCHAR)afdBuffer->Buffer + afdBuffer->DataOffset, + recvLength + ); + + // + // Update the data length and offset in the data bufferred + // on the connection. + // + + afdBuffer->DataLength -= recvLength; + afdBuffer->DataOffset += recvLength; + connection->VcBufferredReceiveBytes -= recvLength; + + // + // If there is indicated but unreceived data in the TDI provider, + // and we have available buffer space, fire off an IRP to receive + // the data. + // + + if ( connection->VcReceiveBytesInTransport > 0 + + && + + connection->VcBufferredReceiveBytes < + connection->MaxBufferredReceiveBytes + + && + + connection->VcBufferredReceiveCount < + connection->MaxBufferredReceiveCount ) { + + CLONG bytesInTransport; + + // + // Remember the count of data that we're going to receive, + // then reset the fields in the connection where we keep + // track of how much data is available in the transport. + // We reset it here before releasing the lock so that + // another thread doesn't try to receive the data at the + // same time as us. + // + + bytesInTransport = connection->VcReceiveBytesInTransport; + + ASSERT( connection->VcReceiveCountInTransport == 1 ); + connection->VcReceiveBytesInTransport = 0; + connection->VcReceiveCountInTransport = 0; + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // Get an AFD buffer structure to hold the data. + // + + afdBuffer = AfdGetBuffer( bytesInTransport, 0 ); + + if ( afdBuffer == NULL ) { + + // + // If we were unable to get a buffer, abort the + // circuit. + // + + AfdBeginAbort( connection ); + + } else { + + // + // We need to remember the connection in the AFD buffer + // because we'll need to access it in the completion + // routine. + // + + afdBuffer->Context = connection; + + // + // Finish building the receive IRP to give to the TDI provider. + // + + TdiBuildReceive( + afdBuffer->Irp, + connection->DeviceObject, + connection->FileObject, + AfdRestartBufferReceive, + afdBuffer, + afdBuffer->Mdl, + TDI_RECEIVE_NORMAL, + bytesInTransport + ); + + // + // Hand off the IRP to the TDI provider. + // + + (VOID)IoCallDriver( + connection->DeviceObject, + afdBuffer->Irp + ); + } + + } else { + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + } + + // + // Now copy the data to the user buffer inside an exception + // handler. + // + + try { + + AfdCopyBufferToBufferArray( + recvInfo->BufferArray, + 0, + recvInfo->BufferCount, + tempAfdBuffer->Buffer, + recvLength + ); + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + RESET_CHAIN_LENGTH( tempAfdBuffer ); + AfdReturnBuffer( tempAfdBuffer ); + return FALSE; + } + + // + // Clear the receive data active bit. If there's more data + // available, set the corresponding event. + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + endpoint->EventsActive &= ~AFD_POLL_RECEIVE; + + if( !IsListEmpty( &connection->VcReceiveBufferListHead ) ) { + + AfdIndicateEventSelectEvent( + endpoint, + AFD_POLL_RECEIVE_BIT, + STATUS_SUCCESS + ); + + } + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // Fast IO succeeded! + // + + RESET_CHAIN_LENGTH( tempAfdBuffer ); + AfdReturnBuffer( tempAfdBuffer ); + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = recvLength; + + return TRUE; + } + } + + case IOCTL_AFD_TRANSMIT_FILE: + + return AfdFastTransmitFile( + FileObject, + InputBuffer, + InputBufferLength, + IoStatus + ); + + + default: + + return FALSE; + } + + return FALSE; + +} // AfdFastDeviceIoControlFile + + +BOOLEAN +AfdFastDatagramIo ( + IN struct _FILE_OBJECT *FileObject, + IN PVOID InputBuffer OPTIONAL, + IN ULONG InputBufferLength, + IN ULONG IoControlCode, + OUT PIO_STATUS_BLOCK IoStatus + ) +{ + PAFD_ENDPOINT endpoint; + PAFD_BUFFER afdBuffer; + + PAGED_CODE( ); + + // + // All we want to do is pass the request through to the TDI provider + // if possible. First get the endpoint pointer. + // + + endpoint = FileObject->FsContext; + ASSERT( endpoint->Type == AfdBlockTypeDatagram ); + + IF_DEBUG(FAST_IO) { + KdPrint(( "AfdFastDatagramIo: attempting fast IO on endp %lx, " + "code %lx\n", + endpoint, IoControlCode )); + } + + switch ( IoControlCode ) { + + case IOCTL_AFD_SEND: { + + PAFD_SEND_INFO sendInfo; + ULONG sendLength; + + // + // If the input structure isn't large enough, bail on fast IO. + // + + if( InputBufferLength < sizeof(*sendInfo) ) { + return FALSE; + } + + try { + + // + // Make a quick preliminary check of the input buffer. If it's + // bogus (or if Fast IO is disabled), then fail the request. + // + + sendInfo = (PAFD_SEND_INFO)InputBuffer; + + if( (sendInfo->AfdFlags & AFD_NO_FAST_IO) != 0 || + sendInfo->TdiFlags != 0 || + sendInfo->BufferArray == NULL || + sendInfo->BufferCount == 0 ) { + + return FALSE; + + } + + // + // Calculate the length of the send buffer. + // + + sendLength = AfdCalcBufferArrayByteLengthRead( + sendInfo->BufferArray, + sendInfo->BufferCount + ); + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + return FALSE; + } + + // + // If this is a send for more than the threshold number of + // bytes, don't use the fast path. We don't allow larger sends + // in the fast path because of the extra data copy it entails, + // which is more expensive for large buffers. For smaller + // buffers, however, the cost of the copy is small compared to + // the IO system overhead of the slow path. + // + + if ( sendLength > AfdFastSendDatagramThreshold ) { + return FALSE; + } + + // + // In a subroutine, copy the destination address to the AFD + // buffer. We do this in a subroutine because it needs to + // acquire a spin lock and we want this routine to be pageable. + // + + afdBuffer = CopyAddressToBuffer( endpoint, sendLength ); + if ( afdBuffer == NULL ) { + return FALSE; + } + + // + // Store the length of the data we're going to send. + // + + afdBuffer->DataLength = sendLength; + + // + // Copy the output buffer to the AFD buffer. + // + + try { + + AfdCopyBufferArrayToBuffer( + afdBuffer->Buffer, + sendLength, + sendInfo->BufferArray, + sendInfo->BufferCount + ); + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + RESET_CHAIN_LENGTH( afdBuffer ); + AfdReturnBuffer( afdBuffer ); + return FALSE; + } + + // + // Call a subroutine to complete the work of the fast path. + // + + return AfdFastDatagramSend( afdBuffer, endpoint, IoStatus ); + + } + + case IOCTL_AFD_SEND_DATAGRAM: { + + PTDI_REQUEST_SEND_DATAGRAM tdiRequest; + ULONG destinationAddressLength; + PAFD_SEND_DATAGRAM_INFO sendInfo; + ULONG sendLength; + + // + // If the input structure isn't large enough, bail on fast IO. + // + + if( InputBufferLength < sizeof(*sendInfo) ) { + return FALSE; + } + + try { + + // + // Make a quick preliminary check of the input buffer. If it's + // bogus (or if Fast IO is disabled), then fail the request. + // + + sendInfo = (PAFD_SEND_DATAGRAM_INFO)InputBuffer; + + if( (sendInfo->AfdFlags & AFD_NO_FAST_IO) != 0 || + sendInfo->BufferArray == NULL || + sendInfo->BufferCount == 0 ) { + + return FALSE; + + } + + // + // Calculate the length of the send buffer. + // + + sendLength = AfdCalcBufferArrayByteLengthRead( + sendInfo->BufferArray, + sendInfo->BufferCount + ); + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + return FALSE; + } + + // + // If this is a send for more than the threshold number of + // bytes, don't use the fast path. We don't allow larger sends + // in the fast path because of the extra data copy it entails, + // which is more expensive for large buffers. For smaller + // buffers, however, the cost of the copy is small compared to + // the IO system overhead of the slow path. + // + + if ( sendLength > AfdFastSendDatagramThreshold ) { + return FALSE; + } + + // + // If the endpoint is not bound, fail. + // + + if ( endpoint->State != AfdEndpointStateBound ) { + return FALSE; + } + + tdiRequest = &sendInfo->TdiRequest; + + try { + destinationAddressLength = + tdiRequest->SendDatagramInformation->RemoteAddressLength ; + } except( EXCEPTION_EXECUTE_HANDLER ) { + return FALSE; + } + + // + // Get an AFD buffer to use for the request. We'll copy the + // user's data to the AFD buffer then submit the IRP in the AFD + // buffer to the TDI provider. + // + + afdBuffer = AfdGetBuffer( sendLength, destinationAddressLength ); + if ( afdBuffer == NULL ) { + return FALSE; + } + + // + // Store the length of the data and the address we're going to + // send. + // + + afdBuffer->DataLength = sendLength; + afdBuffer->SourceAddressLength = destinationAddressLength; + + // + // Copy the destination address and the output buffer to the + // AFD buffer. + // + + try { + + AfdCopyBufferArrayToBuffer( + afdBuffer->Buffer, + sendLength, + sendInfo->BufferArray, + sendInfo->BufferCount + ); + + RtlCopyMemory( + afdBuffer->SourceAddress, + tdiRequest->SendDatagramInformation->RemoteAddress, + destinationAddressLength + ); + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + RESET_CHAIN_LENGTH( afdBuffer ); + AfdReturnBuffer( afdBuffer ); + return FALSE; + } + + // + // Call a subroutine to complete the work of the fast path. + // + + return AfdFastDatagramSend( afdBuffer, endpoint, IoStatus ); + + } + + case IOCTL_AFD_RECEIVE: { + + AFD_RECV_INFO recvInfo; + ULONG recvLength; + + // + // If the input structure isn't large enough, bail on fast IO. + // + + if( InputBufferLength < sizeof(recvInfo) ) { + return FALSE; + } + + // + // Capture the input structure. + // + + try { + + recvInfo = *(PAFD_RECV_INFO)InputBuffer; + + // + // Make a quick preliminary check of the input buffer. If it's + // bogus (or if Fast IO is disabled), then fail the request. + // + + if( (recvInfo.AfdFlags & AFD_NO_FAST_IO) != 0 || + recvInfo.TdiFlags != TDI_RECEIVE_NORMAL || + recvInfo.BufferArray == NULL || + recvInfo.BufferCount == 0 ) { + + return FALSE; + + } + + // + // Calculate the length of the receive buffer. + // + + recvLength = AfdCalcBufferArrayByteLengthWrite( + recvInfo.BufferArray, + recvInfo.BufferCount + ); + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + return FALSE; + + } + + // + // Attempt to perform fast IO on the endpoint. + // + + return AfdFastDatagramReceive( + endpoint, + recvInfo.TdiFlags, + recvInfo.AfdFlags, + recvInfo.BufferArray, + recvInfo.BufferCount, + recvLength, + NULL, + NULL, + IoStatus + ); + + } + + case IOCTL_AFD_RECEIVE_DATAGRAM: { + + AFD_RECV_DATAGRAM_INFO recvInfo; + ULONG recvLength; + + // + // If the input structure isn't large enough, bail on fast IO. + // + + if( InputBufferLength < sizeof(recvInfo) ) { + return FALSE; + } + + // + // Capture the input structure. + // + + try { + + recvInfo = *(PAFD_RECV_DATAGRAM_INFO)InputBuffer; + + // + // Make a quick preliminary check of the input buffer. If it's + // bogus (or if Fast IO is disabled), then fail the request. + // + + if( (recvInfo.AfdFlags & AFD_NO_FAST_IO) != 0 || + recvInfo.TdiFlags != TDI_RECEIVE_NORMAL || + recvInfo.BufferArray == NULL || + recvInfo.BufferCount == 0 ) { + + return FALSE; + + } + + // + // Calculate the length of the receive buffer. + // + + recvLength = AfdCalcBufferArrayByteLengthWrite( + recvInfo.BufferArray, + recvInfo.BufferCount + ); + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + return FALSE; + + } + + // + // Attempt to perform fast IO on the endpoint. + // + + return AfdFastDatagramReceive( + endpoint, + recvInfo.TdiFlags, + recvInfo.AfdFlags, + recvInfo.BufferArray, + recvInfo.BufferCount, + recvLength, + recvInfo.Address, + recvInfo.AddressLength, + IoStatus + ); + + } + + default: + + return FALSE; + } + +} // AfdFastDatagramIo + + +BOOLEAN +AfdFastDatagramReceive ( + IN PAFD_ENDPOINT Endpoint, + IN ULONG ReceiveFlags, + IN ULONG AfdFlags, + IN LPWSABUF BufferArray, + IN ULONG BufferCount, + IN ULONG ReceiveBufferLength, + OUT PVOID SourceAddress, + OUT PULONG SourceAddressLength, + OUT PIO_STATUS_BLOCK IoStatus + ) +{ + KIRQL oldIrql; + PLIST_ENTRY listEntry; + PAFD_BUFFER afdBuffer; + PTRANSPORT_ADDRESS tdiAddress; + ULONG addressLength; + + // + // If the receive flags has any unexpected bits set, fail fast IO. + // We don't handle peeks or expedited data here. + // + + if( ReceiveFlags != TDI_RECEIVE_NORMAL ) { + return FALSE; + } + + // + // If the endpoint is neither bound nor connected, fail. + // + + if ( Endpoint->State != AfdEndpointStateBound && + Endpoint->State != AfdEndpointStateConnected ) { + return FALSE; + } + + // + // If there are no datagrams available to be received, don't + // bother with the fast path. + // + + AfdAcquireSpinLock( &Endpoint->SpinLock, &oldIrql ); + + if ( !ARE_DATAGRAMS_ON_ENDPOINT( Endpoint ) ) { + + // + // If this is a nonblocking endpoint, fail the request here and + // save going through the regular path. + // + + if ( Endpoint->NonBlocking && !( AfdFlags & AFD_OVERLAPPED ) ) { + Endpoint->EventsActive &= ~AFD_POLL_SEND; + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdFastDatagramReceive: Endp %08lX, Active %08lX\n", + Endpoint, + Endpoint->EventsActive + )); + } + + IoStatus->Status = STATUS_DEVICE_NOT_READY; + AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql ); + return TRUE; + } + + AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql ); + return FALSE; + } + + // + // There is at least one datagram bufferred on the endpoint. Use it + // for this receive. + // + + listEntry = RemoveHeadList( &Endpoint->ReceiveDatagramBufferListHead ); + afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry ); + + // + // If the datagram is too large, fail fast IO. + // + + if ( afdBuffer->DataLength > ReceiveBufferLength ) { + InsertHeadList( + &Endpoint->ReceiveDatagramBufferListHead, + &afdBuffer->BufferListEntry + ); + AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql ); + return FALSE; + } + + // + // Update counts of bufferred datagrams and bytes on the endpoint. + // + + Endpoint->BufferredDatagramCount--; + Endpoint->BufferredDatagramBytes -= afdBuffer->DataLength; + + // + // Release the lock and copy the datagram into the user buffer. We + // can't continue to hold the lock, because it is not legal to take + // an exception at raised IRQL. Releasing the lock may result in a + // misordered datagram if there is an exception in copying to the + // user's buffer, but that is the user's fault for giving us a bogus + // pointer. Besides, datagram order is not guaranteed. + // + + AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql ); + + try { + + AfdCopyBufferToBufferArray( + BufferArray, + 0, + BufferCount, + afdBuffer->Buffer, + afdBuffer->DataLength + ); + + // + // If we need to return the source address, copy it to the + // user's output buffer. + // + + if ( SourceAddress != NULL ) { + + tdiAddress = afdBuffer->SourceAddress; + + addressLength = tdiAddress->Address[0].AddressLength + + sizeof(u_short); // sa_family + + if( *SourceAddressLength < addressLength ) { + + ExRaiseAccessViolation(); + + } + + RtlCopyMemory( + SourceAddress, + &tdiAddress->Address[0].AddressType, + addressLength + ); + + *SourceAddressLength = addressLength; + + } + + IoStatus->Information = afdBuffer->DataLength; + IoStatus->Status = STATUS_SUCCESS; + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + // + // Put the buffer back on the endpoint's list. + // + + AfdAcquireSpinLock( &Endpoint->SpinLock, &oldIrql ); + + InsertHeadList( + &Endpoint->ReceiveDatagramBufferListHead, + &afdBuffer->BufferListEntry + ); + + Endpoint->BufferredDatagramCount++; + Endpoint->BufferredDatagramBytes += afdBuffer->DataLength; + + AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql ); + + return FALSE; + } + + // + // Clear the receive data active bit. If there's more data + // available, set the corresponding event. + // + + AfdAcquireSpinLock( &Endpoint->SpinLock, &oldIrql ); + + Endpoint->EventsActive &= ~AFD_POLL_RECEIVE; + + if( ARE_DATAGRAMS_ON_ENDPOINT( Endpoint ) ) { + + AfdIndicateEventSelectEvent( + Endpoint, + AFD_POLL_RECEIVE_BIT, + STATUS_SUCCESS + ); + + } + + AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql ); + + // + // The fast IO worked! Clean up and return to the user. + // + + RESET_CHAIN_LENGTH( afdBuffer ); + AfdReturnBuffer( afdBuffer ); + + return TRUE; + +} // AfdFastDatagramReceive + + +BOOLEAN +AfdFastDatagramSend ( + IN PAFD_BUFFER AfdBuffer, + IN PAFD_ENDPOINT Endpoint, + OUT PIO_STATUS_BLOCK IoStatus + ) +{ + NTSTATUS status; + ULONG sendLength; + + PAGED_CODE( ); + + // + // Set up the input TDI information to point to the destination + // address. + // + + AfdBuffer->TdiInputInfo.RemoteAddressLength = AfdBuffer->SourceAddressLength; + AfdBuffer->TdiInputInfo.RemoteAddress = AfdBuffer->SourceAddress; + + sendLength = AfdBuffer->DataLength; + + // + // Initialize the IRP in the AFD buffer to do a fast datagram send. + // + + TdiBuildSendDatagram( + AfdBuffer->Irp, + Endpoint->AddressDeviceObject, + Endpoint->AddressFileObject, + AfdRestartFastSendDatagram, + AfdBuffer, + AfdBuffer->Irp->MdlAddress, + sendLength, + &AfdBuffer->TdiInputInfo + ); + + // + // Change the MDL in the AFD buffer to specify only the number + // of bytes we're actually sending. This is a requirement of TDI-- + // the MDL chain cannot describe a longer buffer than the send + // request. + // + + AfdBuffer->Mdl->ByteCount = sendLength; + + // + // Reference the endpoint so that it does not go away until the send + // completes. This is necessary to ensure that a send which takes a + // very long time and lasts longer than the process will not cause a + // crash when the send datragram finally completes. + // + + REFERENCE_ENDPOINT( Endpoint ); + AfdBuffer->Context = Endpoint; + + // + // Give the IRP to the TDI provider. If the request fails + // immediately, then fail fast IO. If the request fails later on, + // there's nothing we can do about it. + // + + status = IoCallDriver( + Endpoint->AddressDeviceObject, + AfdBuffer->Irp + ); + + if ( NT_SUCCESS(status) ) { + IoStatus->Information = sendLength; + IoStatus->Status = STATUS_SUCCESS; + return TRUE; + } else { + return FALSE; + } + +} // AfdFastDatagramSend + + +NTSTATUS +AfdRestartFastSendDatagram ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + PAFD_BUFFER afdBuffer; + PAFD_ENDPOINT endpoint; + + afdBuffer = Context; + + // + // Find the endpoint used for this request. + // + + endpoint = afdBuffer->Context; + ASSERT( endpoint->Type == AfdBlockTypeDatagram ); + + // + // Reset and free the AFD buffer structure. + // + + ASSERT( afdBuffer->Irp == Irp ); + + afdBuffer->TdiInputInfo.RemoteAddressLength = 0; + afdBuffer->TdiInputInfo.RemoteAddress = NULL; + + afdBuffer->Mdl->ByteCount = afdBuffer->BufferLength; + + RESET_CHAIN_LENGTH( afdBuffer ); + AfdReturnBuffer( afdBuffer ); + + // + // Get rid of the reference we put on the endpoint when we started + // this I/O. + // + + DEREFERENCE_ENDPOINT( endpoint ); + + // + // Tell the IO system to stop processing this IRP. + // + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // AfdRestartFastSendDatagram + + +PAFD_BUFFER +CopyAddressToBuffer ( + IN PAFD_ENDPOINT Endpoint, + IN ULONG OutputBufferLength + ) +{ + KIRQL oldIrql; + PAFD_BUFFER afdBuffer; + + ASSERT( Endpoint->Type == AfdBlockTypeDatagram ); + + AfdAcquireSpinLock( &Endpoint->SpinLock, &oldIrql ); + + // + // If the endpoint is not connected, fail. + // + + if ( Endpoint->State != AfdEndpointStateConnected ) { + AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql ); + return NULL; + } + + // + // Get an AFD buffer to use for the request. We'll copy the + // user to the AFD buffer then submit the IRP in the AFD + // buffer to the TDI provider. + // + + afdBuffer = AfdGetBuffer( + OutputBufferLength, + Endpoint->Common.Datagram.RemoteAddressLength + ); + if ( afdBuffer == NULL ) { + return NULL; + } + + ASSERT( Endpoint->Common.Datagram.RemoteAddress != NULL ); + ASSERT( afdBuffer->AllocatedAddressLength >= + Endpoint->Common.Datagram.RemoteAddressLength ); + + // + // Copy the address to the AFD buffer. + // + + RtlCopyMemory( + afdBuffer->SourceAddress, + Endpoint->Common.Datagram.RemoteAddress, + Endpoint->Common.Datagram.RemoteAddressLength + ); + + afdBuffer->SourceAddressLength = Endpoint->Common.Datagram.RemoteAddressLength; + + AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql ); + + return afdBuffer; + +} // CopyAddressToBuffer + + +BOOLEAN +AfdShouldSendBlock ( + IN PAFD_ENDPOINT Endpoint, + IN PAFD_CONNECTION Connection, + IN ULONG SendLength + ) + +/*++ + +Routine Description: + + Determines whether a nonblocking send can be performed on the + connection, and if the send is possible, updates the connection's + send tracking information. + +Arguments: + + Endpoint - the AFD endpoint for the send. + + Connection - the AFD connection for the send. + + SendLength - the number of bytes that the caller wants to send. + +Return Value: + + TRUE if the there is not too much data on the endpoint to perform + the send; FALSE otherwise. + +--*/ + +{ + KIRQL oldIrql; + + // + // Determine whether we can do fast IO with this send. In order + // to perform fast IO, there must be no other sends pended on this + // connection and there must be enough space left for bufferring + // the requested amount of data. + // + + AfdAcquireSpinLock( &Endpoint->SpinLock, &oldIrql ); + + if ( !IsListEmpty( &Connection->VcSendIrpListHead ) + + || + + Connection->VcBufferredSendBytes >= Connection->MaxBufferredSendBytes + + || + + Connection->VcBufferredSendCount >= Connection->MaxBufferredSendCount + + ) { + + // + // If this is a nonblocking endpoint, fail the request here and + // save going through the regular path. + // + + if ( Endpoint->NonBlocking ) { + Endpoint->EventsActive &= ~AFD_POLL_SEND; + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdFastIoDeviceControl: Endp %08lX, Active %08lX\n", + Endpoint, + Endpoint->EventsActive + )); + } + } + + AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql ); + return TRUE; + } + + // + // Update count of send bytes pending on the connection. + // + + Connection->VcBufferredSendBytes += SendLength; + Connection->VcBufferredSendCount += 1; + + AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql ); + + // + // Indicate to the caller that it is OK to proceed with the send. + // + + return FALSE; + +} // AfdShouldSendBlock + diff --git a/private/ntos/afd/group.c b/private/ntos/afd/group.c new file mode 100644 index 000000000..90a3c7fa3 --- /dev/null +++ b/private/ntos/afd/group.c @@ -0,0 +1,537 @@ +/*++ + +Copyright (c) 1996 Microsoft Corporation + +Module Name: + + group.c + +Abstract: + + This module contains Group ID managment routines. + + Group IDs identify an AFD_GROUP_ENTRY structure in a lookup table. + Each AFD_GROUP_ENTRY contains a reference count and a type (either + GroupTypeConstrained or GroupTypeUnconstrained). Free group IDs are + linked together in a doubly-linked list. As group IDs are allocated, + they are removed from this list. Once the free list becomes empty, + the lookup table is grown appropriately. + +Author: + + Keith Moore (keithmo) 06-Jun-1996 + +Revision History: + +--*/ + +#include "afdp.h" + + +// +// Private constants. +// + +#define AFD_GROUP_TABLE_GROWTH 32 // entries + + +// +// Private types. +// + +typedef struct _AFD_GROUP_ENTRY { + union { + LIST_ENTRY ListEntry; + struct { + AFD_GROUP_TYPE GroupType; + LONG ReferenceCount; + }; + }; +} AFD_GROUP_ENTRY, *PAFD_GROUP_ENTRY; + + +// +// Private globals. +// + +PERESOURCE AfdGroupTableResource; +PAFD_GROUP_ENTRY AfdGroupTable; +LIST_ENTRY AfdFreeGroupList; +LONG AfdGroupTableSize; + + +// +// Private functions. +// + +PAFD_GROUP_ENTRY +AfdMapGroupToEntry( + IN LONG Group + ); + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( INIT, AfdInitializeGroup ) +#pragma alloc_text( PAGE, AfdTerminateGroup ) +#pragma alloc_text( PAGE, AfdReferenceGroup ) +#pragma alloc_text( PAGE, AfdDereferenceGroup ) +#pragma alloc_text( PAGE, AfdGetGroup ) +#endif + + +BOOLEAN +AfdInitializeGroup( + VOID + ) + +/*++ + +Routine Description: + + Initializes any globals necessary for the group ID package. + +Return Value: + + BOOLEAN - TRUE if successful, FALSE otherwise. + +--*/ + +{ + + // + // Initialize the group globals. + // + + AfdGroupTableResource = AFD_ALLOCATE_POOL( + NonPagedPool, + sizeof(*AfdGroupTableResource), + AFD_RESOURCE_POOL_TAG + ); + + if( AfdGroupTableResource == NULL ) { + + return FALSE; + + } + + ExInitializeResourceLite( AfdGroupTableResource ); + + AfdGroupTable = NULL; + InitializeListHead( &AfdFreeGroupList ); + AfdGroupTableSize = 0; + + return TRUE; + +} // AfdInitializeGroup + + +VOID +AfdTerminateGroup( + VOID + ) + +/*++ + +Routine Description: + + Destroys any globals created for the group ID package. + +--*/ + +{ + + if( AfdGroupTableResource != NULL ) { + + ExDeleteResourceLite( AfdGroupTableResource ); + + AFD_FREE_POOL( + AfdGroupTableResource, + AFD_RESOURCE_POOL_TAG + ); + + AfdGroupTableResource = NULL; + + } + + if( AfdGroupTable != NULL ) { + + AFD_FREE_POOL( + AfdGroupTable, + AFD_GROUP_POOL_TAG + ); + + AfdGroupTable = NULL; + + } + + InitializeListHead( &AfdFreeGroupList ); + AfdGroupTableSize = 0; + +} // AfdTerminateGroup + + +BOOLEAN +AfdReferenceGroup( + IN LONG Group, + OUT PAFD_GROUP_TYPE GroupType + ) + +/*++ + +Routine Description: + + Bumps the reference count associated with the given group ID. + +Arguments: + + Group - The group ID to reference. + + GroupType - Returns the type of the group. + +Returns: + + BOOLEAN - TRUE if the group ID was valid, FALSE otherwise. + +--*/ + +{ + + PAFD_GROUP_ENTRY groupEntry; + AFD_GROUP_TYPE groupType; + + groupEntry = AfdMapGroupToEntry( Group ); + + if( groupEntry != NULL ) { + + groupType = groupEntry->GroupType; + + if( groupType == GroupTypeConstrained || + groupType == GroupTypeUnconstrained ) { + + groupEntry->ReferenceCount++; + *GroupType = groupType; + + } else { + + groupEntry = NULL; + + } + + ExReleaseResourceLite( AfdGroupTableResource ); + + } + + return (BOOLEAN)( groupEntry != NULL ); + +} // AfdReferenceGroup + + +BOOLEAN +AfdDereferenceGroup( + IN LONG Group + ) + +/*++ + +Routine Description: + + Decrements the reference count associated with the given group ID. + If the ref count drops to zero, the group ID is freed. + +Arguments: + + Group - The group ID to dereference. + +Returns: + + BOOLEAN - TRUE if the group ID was valid, FALSE otherwise. + +--*/ + +{ + + PAFD_GROUP_ENTRY groupEntry; + AFD_GROUP_TYPE groupType; + + groupEntry = AfdMapGroupToEntry( Group ); + + if( groupEntry != NULL ) { + + groupType = groupEntry->GroupType; + + if( groupType == GroupTypeConstrained || + groupType == GroupTypeUnconstrained ) { + + ASSERT( groupEntry->ReferenceCount > 0 ); + groupEntry->ReferenceCount--; + + if( groupEntry->ReferenceCount == 0 ) { + + InsertTailList( + &AfdFreeGroupList, + &groupEntry->ListEntry + ); + + } + + } else { + + groupEntry = NULL; + + } + + ExReleaseResourceLite( AfdGroupTableResource ); + + } + + return (BOOLEAN)( groupEntry != NULL ); + +} // AfdDereferenceGroup + + +BOOLEAN +AfdGetGroup( + IN OUT PLONG Group, + OUT PAFD_GROUP_TYPE GroupType + ) + +/*++ + +Routine Description: + + Examines the incoming group. If is zero, then nothing is done. If it + is SG_CONSTRAINED_GROUP, then a new constrained group ID is created. + If it is SG_UNCONSTRAINED_GROUP, then a new unconstrained group ID is + created. Otherwise, it must identify an existing group, so that group + is referenced. + +Arguments: + + Group - Points to the group ID to examine/modify. + + GroupType - Returns the type of the group. + +Return Value: + + BOOLEAN - TRUE if successful, FALSE otherwise. + +--*/ + +{ + + LONG groupValue; + PAFD_GROUP_ENTRY groupEntry; + PAFD_GROUP_ENTRY newGroupTable; + LONG newGroupTableSize; + LONG i; + PLIST_ENTRY listEntry; + + groupValue = *Group; + + // + // Zero means "no group", so just ignore it. + // + + if( groupValue == 0 ) { + + *GroupType = GroupTypeNeither; + return TRUE; + + } + + // + // If we're being asked to create a new group, do it. + // + + if( groupValue == SG_CONSTRAINED_GROUP || + groupValue == SG_UNCONSTRAINED_GROUP ) { + + // + // Lock the table. + // + + ExAcquireResourceExclusiveLite( AfdGroupTableResource, TRUE ); + + // + // See if there's room at the inn. + // + + if( IsListEmpty( &AfdFreeGroupList ) ) { + + // + // No room, we'll need to create/expand the table. + // + + newGroupTableSize = AfdGroupTableSize + AFD_GROUP_TABLE_GROWTH; + + newGroupTable = AFD_ALLOCATE_POOL( + PagedPool, + newGroupTableSize * sizeof(AFD_GROUP_ENTRY), + AFD_GROUP_POOL_TAG + ); + + if( newGroupTable == NULL ) { + + ExReleaseResourceLite( AfdGroupTableResource ); + return FALSE; + + } + + if( AfdGroupTable == NULL ) { + + // + // This is the initial table allocation, so reserve the + // first three entries (0, SG_UNCONSTRAINED_GROUP, and + // SG_CONSTRAINED_GROUP). + // + + for( ; + AfdGroupTableSize <= SG_CONSTRAINED_GROUP || + AfdGroupTableSize <= SG_UNCONSTRAINED_GROUP ; + AfdGroupTableSize++ ) { + + newGroupTable[AfdGroupTableSize].ReferenceCount = 0; + newGroupTable[AfdGroupTableSize].GroupType = GroupTypeNeither; + + } + + } else { + + // + // Copy the old table into the new table, then free the + // old table. + // + + RtlCopyMemory( + newGroupTable, + AfdGroupTable, + AfdGroupTableSize * sizeof(AFD_GROUP_ENTRY) + ); + + AFD_FREE_POOL( + AfdGroupTable, + AFD_GROUP_POOL_TAG + ); + + } + + // + // Add the new entries to the free list. + // + + for( i = newGroupTableSize - 1 ; i >= AfdGroupTableSize ; i-- ) { + + InsertHeadList( + &AfdFreeGroupList, + &newGroupTable[i].ListEntry + ); + + } + + AfdGroupTable = newGroupTable; + AfdGroupTableSize = newGroupTableSize; + + } + + // + // Pull the next free entry off the list. + // + + ASSERT( !IsListEmpty( &AfdFreeGroupList ) ); + + listEntry = RemoveHeadList( &AfdFreeGroupList ); + + groupEntry = CONTAINING_RECORD( + listEntry, + AFD_GROUP_ENTRY, + ListEntry + ); + + groupEntry->ReferenceCount = 1; + groupEntry->GroupType = (AFD_GROUP_TYPE)groupValue; + + *Group = ( groupEntry - AfdGroupTable ); + *GroupType = groupEntry->GroupType; + + ExReleaseResourceLite( AfdGroupTableResource ); + return TRUE; + + } + + // + // Otherwise, just reference the group. + // + + return AfdReferenceGroup( groupValue, GroupType ); + +} // AfdGetGroup + + +PAFD_GROUP_ENTRY +AfdMapGroupToEntry( + IN LONG Group + ) + +/*++ + +Routine Description: + + Maps the given group ID to the corresponding AFD_GROUP_ENTRY structure. + + N.B. This routine returns with AfdGroupTableResource held if successful. + +Arguments: + + Group - The group ID to map. + +Return Value: + + PAFD_GROUP_ENTRY - The entry corresponding to the group ID if successful, + NULL otherwise. + +--*/ + +{ + + PAFD_GROUP_ENTRY groupEntry; + + // + // Lock the table. + // + + ExAcquireResourceExclusiveLite( AfdGroupTableResource, TRUE ); + + // + // Validate the group ID. + // + + if( Group > 0 && Group < AfdGroupTableSize ) { + + groupEntry = AfdGroupTable + Group; + + // + // The group ID is within legal range. Ensure it's in use. + // In the AFD_GROUP_ENTRY structure, the GroupType field is + // overlayed with ListEntry.Flink due to the internal union. + // We can use this knowledge to quickly validate that this + // entry is in use. + // + + if( groupEntry->GroupType == GroupTypeConstrained || + groupEntry->GroupType == GroupTypeUnconstrained ) { + + return groupEntry; + + } + + } + + // + // Invalid group ID, fail it. + // + + ExReleaseResourceLite( AfdGroupTableResource ); + return NULL; + +} // AfdMapGroupToEntry + diff --git a/private/ntos/afd/init.c b/private/ntos/afd/init.c new file mode 100644 index 000000000..95b5ff579 --- /dev/null +++ b/private/ntos/afd/init.c @@ -0,0 +1,1056 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + init.c + +Abstract: + + This module performs initialization for the AFD device driver. + +Author: + + David Treadwell (davidtr) 21-Feb-1992 + +Revision History: + +--*/ + +#include "afdp.h" + +#define REGISTRY_PARAMETERS L"Parameters" +#define REGISTRY_AFD_INFORMATION L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Afd" +#define REGISTRY_IRP_STACK_SIZE L"IrpStackSize" +#define REGISTRY_PRIORITY_BOOST L"PriorityBoost" +#define REGISTRY_IGNORE_PUSH_BIT L"IgnorePushBitOnReceives" +#define REGISTRY_NO_RAW_SECURITY L"DisableRawSecurity" +#define REGISTRY_MAX_ACTIVE_TRANSMIT_FILE_COUNT L"MaxActiveTransmitFileCount" +#define REGISTRY_ENABLE_DYNAMIC_BACKLOG L"EnableDynamicBacklog" + +#if DBG +#define REGISTRY_DEBUG_FLAGS L"DebugFlags" +#define REGISTRY_BREAK_ON_STARTUP L"BreakOnStartup" +#define REGISTRY_ENABLE_UNLOAD L"EnableUnload" +#define REGISTRY_USE_PRIVATE_ASSERT L"UsePrivateAssert" +#endif + +#if AFD_PERF_DBG +#define REGISTRY_DISABLE_FAST_IO L"DisableFastIO" +#define REGISTRY_DISABLE_CONN_REUSE L"DisableConnectionReuse" +#endif + +// +// A list of longwords that are configured by the registry. +// + +struct _AfdConfigInfo { + PWCHAR RegistryValueName; + PULONG Variable; +} AfdConfigInfo[] = { + { L"LargeBufferSize", &AfdLargeBufferSize }, + { L"LargeBufferListDepth", &AfdLargeBufferListDepth }, + { L"MediumBufferSize", &AfdMediumBufferSize }, + { L"MediumBufferListDepth", &AfdMediumBufferListDepth }, + { L"SmallBufferSize", &AfdSmallBufferSize }, + { L"SmallBufferListDepth", &AfdSmallBufferListDepth }, + { L"FastSendDatagramThreshold", &AfdFastSendDatagramThreshold }, + { L"StandardAddressLength", &AfdStandardAddressLength }, + { L"DefaultReceiveWindow", &AfdReceiveWindowSize }, + { L"DefaultSendWindow", &AfdSendWindowSize }, + { L"BufferMultiplier", &AfdBufferMultiplier }, + { L"TransmitIoLength", &AfdTransmitIoLength }, + { L"MaxFastTransmit", &AfdMaxFastTransmit }, + { L"MaxFastCopyTransmit", &AfdMaxFastCopyTransmit }, + { L"MinimumDynamicBacklog", &AfdMinimumDynamicBacklog }, + { L"MaximumDynamicBacklog", &AfdMaximumDynamicBacklog }, + { L"DynamicBacklogGrowthDelta", &AfdDynamicBacklogGrowthDelta } +}; + +#define AFD_CONFIG_VAR_COUNT (sizeof(AfdConfigInfo) / sizeof(AfdConfigInfo[0])) + +PSECURITY_DESCRIPTOR AfdRawSecurityDescriptor = NULL; + +#if DBG +BOOLEAN AfdEnableUnload = FALSE; +#endif + +ULONG +AfdReadSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN LONG DefaultValue + ); + +NTSTATUS +AfdOpenRegistry( + IN PUNICODE_STRING BaseName, + OUT PHANDLE ParametersHandle + ); + +VOID +AfdReadRegistry ( + VOID + ); + +#if DBG +VOID +AfdUnload ( + IN PDRIVER_OBJECT DriverObject + ); +#endif + +NTSTATUS +DriverEntry ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +NTSTATUS +AfdCreateRawSecurityDescriptor( + VOID + ); + +NTSTATUS +AfdBuildDeviceAcl( + OUT PACL *DeviceAcl + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( INIT, DriverEntry ) +#pragma alloc_text( INIT, AfdReadSingleParameter ) +#pragma alloc_text( INIT, AfdOpenRegistry ) +#pragma alloc_text( INIT, AfdReadRegistry ) +#pragma alloc_text( INIT, AfdCreateRawSecurityDescriptor ) +#pragma alloc_text( INIT, AfdBuildDeviceAcl ) +#if DBG +#pragma alloc_text( PAGE, AfdUnload ) +#endif +#endif + + +NTSTATUS +DriverEntry ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This is the initialization routine for the AFD device driver. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + NTSTATUS status; + UNICODE_STRING deviceName; + CLONG i; + BOOLEAN success; + + PAGED_CODE( ); + + // + // Create the device object. (IoCreateDevice zeroes the memory + // occupied by the object.) + // + // !!! Apply an ACL to the device object. + // + + RtlInitUnicodeString( &deviceName, AFD_DEVICE_NAME ); + + status = IoCreateDevice( + DriverObject, // DriverObject + 0, // DeviceExtension + &deviceName, // DeviceName + FILE_DEVICE_NAMED_PIPE, // DeviceType + 0, // DeviceCharacteristics + FALSE, // Exclusive + &AfdDeviceObject // DeviceObject + ); + + + if ( !NT_SUCCESS(status) ) { + KdPrint(( "AFD DriverEntry: unable to create device object: %X\n", status )); + return status; + } + + // + // Create the security descriptor used for raw socket access checks. + // + status = AfdCreateRawSecurityDescriptor(); + + if (!NT_SUCCESS(status)) { + IoDeleteDevice(AfdDeviceObject); + return status; + } + + AfdDeviceObject->Flags |= DO_DIRECT_IO; + + // + // Initialize the driver object for this file system driver. + // + + for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) { + DriverObject->MajorFunction[i] = AfdDispatch; + } + + DriverObject->FastIoDispatch = &AfdFastIoDispatch; + DriverObject->DriverUnload = NULL; + + // + // Initialize global data. + // + + success = AfdInitializeData( ); + if ( !success ) { + IoDeleteDevice(AfdDeviceObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Initialize group ID manager. + // + + success = AfdInitializeGroup(); + if ( !success ) { + IoDeleteDevice(AfdDeviceObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Read registry information. + // + + AfdReadRegistry( ); + +#if DBG + if( AfdEnableUnload ) { + KdPrint(( "AFD: DriverUnload enabled\n" )); + DriverObject->DriverUnload = AfdUnload; + } +#endif + + // + // Initialize the lookaside lists. + // + + AfdLookasideLists = AFD_ALLOCATE_POOL( + NonPagedPool, + sizeof(*AfdLookasideLists), + AFD_LOOKASIDE_LISTS_POOL_TAG + ); + + if( AfdLookasideLists == NULL ) { + IoDeleteDevice(AfdDeviceObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Initialize the work queue item lookaside list. + // + + ExInitializeNPagedLookasideList( + &AfdLookasideLists->WorkQueueList, +#if DBG + AfdAllocateWorkItemPool, + AfdFreeWorkItemPool, +#else + NULL, + NULL, +#endif + NonPagedPoolMustSucceed, + sizeof( AFD_WORK_ITEM ), + AFD_WORK_ITEM_POOL_TAG, + 12 + ); + + // + // Initialize the AFD buffer lookaside lists. These must be + // initialized *after* the registry data has been read. + // + + ExInitializeNPagedLookasideList( + &AfdLookasideLists->LargeBufferList, + AfdAllocateBuffer, +#if DBG + AfdFreeBufferPool, +#else + NULL, +#endif + 0, + AfdLargeBufferSize, + AFD_DATA_BUFFER_POOL_TAG, + (USHORT)AfdLargeBufferListDepth + ); + + ExInitializeNPagedLookasideList( + &AfdLookasideLists->MediumBufferList, + AfdAllocateBuffer, +#if DBG + AfdFreeBufferPool, +#else + NULL, +#endif + 0, + AfdMediumBufferSize, + AFD_DATA_BUFFER_POOL_TAG, + (USHORT)AfdMediumBufferListDepth + ); + + ExInitializeNPagedLookasideList( + &AfdLookasideLists->SmallBufferList, + AfdAllocateBuffer, +#if DBG + AfdFreeBufferPool, +#else + NULL, +#endif + 0, + AfdSmallBufferSize, + AFD_DATA_BUFFER_POOL_TAG, + (USHORT)AfdSmallBufferListDepth + ); + + // + // Initialize our device object. + // + + AfdDeviceObject->StackSize = AfdIrpStackSize; + + // + // Remember a pointer to the system process. We'll use this pointer + // for KeAttachProcess() calls so that we can open handles in the + // context of the system process. + // + + AfdSystemProcess = (PKPROCESS)IoGetCurrentProcess(); + + // + // Tell MM that it can page all of AFD it is desires. We will reset + // to normal paging of AFD code as soon as an AFD endpoint is + // opened. + // + + AfdLoaded = FALSE; + + MmPageEntireDriver( DriverEntry ); + + return (status); + +} // DriverEntry + + +#if DBG +VOID +AfdUnload ( + IN PDRIVER_OBJECT DriverObject + ) +{ + + PLIST_ENTRY listEntry; + PAFD_TRANSPORT_INFO transportInfo; + + UNREFERENCED_PARAMETER( DriverObject ); + + PAGED_CODE( ); + + KdPrint(( "AfdUnload called.\n" )); + + // + // Kill the transport info list. + // + + while( !IsListEmpty( &AfdTransportInfoListHead ) ) { + + listEntry = RemoveHeadList( &AfdTransportInfoListHead ); + + transportInfo = CONTAINING_RECORD( + listEntry, + AFD_TRANSPORT_INFO, + TransportInfoListEntry + ); + + AFD_FREE_POOL( + transportInfo, + AFD_TRANSPORT_INFO_POOL_TAG + ); + + } + + // + // Kill the resource that protects the executive worker thread queue. + // + + if( AfdResource != NULL ) { + + ExDeleteResource( AfdResource ); + + AFD_FREE_POOL( + AfdResource, + AFD_RESOURCE_POOL_TAG + ); + + } + + // + // Destroy the lookaside lists. + // + + if( AfdLookasideLists != NULL ) { + + ExDeleteNPagedLookasideList( &AfdLookasideLists->WorkQueueList ); + ExDeleteNPagedLookasideList( &AfdLookasideLists->LargeBufferList ); + ExDeleteNPagedLookasideList( &AfdLookasideLists->MediumBufferList ); + ExDeleteNPagedLookasideList( &AfdLookasideLists->SmallBufferList ); + + AFD_FREE_POOL( + AfdLookasideLists, + AFD_LOOKASIDE_LISTS_POOL_TAG + ); + + } + + // + // Terminate the group ID manager. + // + + AfdTerminateGroup(); + + // + // Delete our device object. + // + + IoDeleteDevice( AfdDeviceObject ); + +} // AfdUnload +#endif + + +VOID +AfdReadRegistry ( + VOID + ) + +/*++ + +Routine Description: + + Reads the AFD section of the registry. Any values listed in the + registry override defaults. + +Arguments: + + None. + +Return Value: + + None -- if anything fails, the default value is used. + +--*/ +{ + HANDLE parametersHandle; + NTSTATUS status; + ULONG stackSize; + ULONG priorityBoost; + UNICODE_STRING registryPath; + CLONG i; + + PAGED_CODE( ); + + RtlInitUnicodeString( ®istryPath, REGISTRY_AFD_INFORMATION ); + + status = AfdOpenRegistry( ®istryPath, ¶metersHandle ); + + if (status != STATUS_SUCCESS) { + return; + } + +#if DBG + // + // Read the debug flags from the registry. + // + + AfdDebug = AfdReadSingleParameter( + parametersHandle, + REGISTRY_DEBUG_FLAGS, + AfdDebug + ); + + // + // Force a breakpoint if so requested. + // + + if( AfdReadSingleParameter( + parametersHandle, + REGISTRY_BREAK_ON_STARTUP, + 0 ) != 0 ) { + DbgBreakPoint(); + } + + // + // Enable driver unload if requested. + // + + AfdEnableUnload = AfdReadSingleParameter( + parametersHandle, + REGISTRY_ENABLE_UNLOAD, + (LONG)AfdEnableUnload + ) != 0; + + // + // Enable private assert function if requested. + // + + AfdUsePrivateAssert = AfdReadSingleParameter( + parametersHandle, + REGISTRY_USE_PRIVATE_ASSERT, + (LONG)AfdUsePrivateAssert + ) != 0; +#endif + +#if AFD_PERF_DBG + // + // Read a flag from the registry that allows us to disable Fast IO. + // + + AfdDisableFastIo = AfdReadSingleParameter( + parametersHandle, + REGISTRY_DISABLE_FAST_IO, + (LONG)AfdDisableFastIo + ) != 0; + + if( AfdDisableFastIo ) { + + KdPrint(( "AFD: Fast IO disabled\n" )); + + } + + // + // Read a flag from the registry that allows us to disable connection + // reuse. + // + + AfdDisableConnectionReuse = AfdReadSingleParameter( + parametersHandle, + REGISTRY_DISABLE_CONN_REUSE, + (LONG)AfdDisableConnectionReuse + ) != 0; + + if( AfdDisableConnectionReuse ) { + + KdPrint(( "AFD: Connection Reuse disabled\n" )); + + } +#endif + + // + // Read the stack size and priority boost values from the registry. + // + + stackSize = AfdReadSingleParameter( + parametersHandle, + REGISTRY_IRP_STACK_SIZE, + (ULONG)AfdIrpStackSize + ); + + if ( stackSize > 255 ) { + stackSize = 255; + } + + AfdIrpStackSize = (CCHAR)stackSize; + + priorityBoost = AfdReadSingleParameter( + parametersHandle, + REGISTRY_PRIORITY_BOOST, + (ULONG)AfdPriorityBoost + ); + + if ( priorityBoost > 16 ) { + priorityBoost = AFD_DEFAULT_PRIORITY_BOOST; + } + + AfdPriorityBoost = (CCHAR)priorityBoost; + + // + // Read other config variables from the registry. + // + + for ( i = 0; i < AFD_CONFIG_VAR_COUNT; i++ ) { + + *AfdConfigInfo[i].Variable = + AfdReadSingleParameter( + parametersHandle, + AfdConfigInfo[i].RegistryValueName, + *AfdConfigInfo[i].Variable + ); + } + + AfdIgnorePushBitOnReceives = AfdReadSingleParameter( + parametersHandle, + REGISTRY_IGNORE_PUSH_BIT, + (LONG)AfdIgnorePushBitOnReceives + ) != 0; + + AfdDisableRawSecurity = AfdReadSingleParameter( + parametersHandle, + REGISTRY_NO_RAW_SECURITY, + (LONG)AfdDisableRawSecurity + ) != 0; + + if( MmIsThisAnNtAsSystem() ) { + + // + // On the NT Server product, make the maximum active TransmitFile + // count configurable. This value is fixed (not configurable) on + // the NT Workstation product. + // + + AfdMaxActiveTransmitFileCount = AfdReadSingleParameter( + parametersHandle, + REGISTRY_MAX_ACTIVE_TRANSMIT_FILE_COUNT, + (LONG)AfdMaxActiveTransmitFileCount + ); + + // + // Dynamic backlog is only possible on NT Server. + // + + AfdEnableDynamicBacklog = AfdReadSingleParameter( + parametersHandle, + REGISTRY_ENABLE_DYNAMIC_BACKLOG, + (LONG)AfdEnableDynamicBacklog + ) != 0; + + } else { + + AfdEnableDynamicBacklog = FALSE; + + } + + ZwClose( parametersHandle ); + + return; + +} // AfdReadRegistry + + +NTSTATUS +AfdOpenRegistry( + IN PUNICODE_STRING BaseName, + OUT PHANDLE ParametersHandle + ) + +/*++ + +Routine Description: + + This routine is called by AFD to open the registry. If the registry + tree exists, then it opens it and returns an error. If not, it + creates the appropriate keys in the registry, opens it, and + returns STATUS_SUCCESS. + +Arguments: + + BaseName - Where in the registry to start looking for the information. + + LinkageHandle - Returns the handle used to read linkage information. + + ParametersHandle - Returns the handle used to read other + parameters. + +Return Value: + + The status of the request. + +--*/ +{ + + HANDLE configHandle; + NTSTATUS status; + PWSTR parametersString = REGISTRY_PARAMETERS; + UNICODE_STRING parametersKeyName; + OBJECT_ATTRIBUTES objectAttributes; + ULONG disposition; + + PAGED_CODE( ); + + // + // Open the registry for the initial string. + // + + InitializeObjectAttributes( + &objectAttributes, + BaseName, // name + OBJ_CASE_INSENSITIVE, // attributes + NULL, // root + NULL // security descriptor + ); + + status = ZwCreateKey( + &configHandle, + KEY_WRITE, + &objectAttributes, + 0, // title index + NULL, // class + 0, // create options + &disposition // disposition + ); + + if (!NT_SUCCESS(status)) { + return STATUS_UNSUCCESSFUL; + } + + // + // Now open the parameters key. + // + + RtlInitUnicodeString (¶metersKeyName, parametersString); + + InitializeObjectAttributes( + &objectAttributes, + ¶metersKeyName, // name + OBJ_CASE_INSENSITIVE, // attributes + configHandle, // root + NULL // security descriptor + ); + + status = ZwOpenKey( + ParametersHandle, + KEY_READ, + &objectAttributes + ); + if (!NT_SUCCESS(status)) { + + ZwClose( configHandle ); + return status; + } + + // + // All keys successfully opened or created. + // + + ZwClose( configHandle ); + return STATUS_SUCCESS; + +} // AfdOpenRegistry + + +ULONG +AfdReadSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN LONG DefaultValue + ) + +/*++ + +Routine Description: + + This routine is called by AFD to read a single parameter + from the registry. If the parameter is found it is stored + in Data. + +Arguments: + + ParametersHandle - A pointer to the open registry. + + ValueName - The name of the value to search for. + + DefaultValue - The default value. + +Return Value: + + The value to use; will be the default if the value is not + found or is not in the correct range. + +--*/ + +{ + static ULONG informationBuffer[32]; // declare ULONG to get it aligned + PKEY_VALUE_FULL_INFORMATION information = + (PKEY_VALUE_FULL_INFORMATION)informationBuffer; + UNICODE_STRING valueKeyName; + ULONG informationLength; + LONG returnValue; + NTSTATUS status; + + PAGED_CODE( ); + + RtlInitUnicodeString( &valueKeyName, ValueName ); + + status = ZwQueryValueKey( + ParametersHandle, + &valueKeyName, + KeyValueFullInformation, + (PVOID)information, + sizeof (informationBuffer), + &informationLength + ); + + if ((status == STATUS_SUCCESS) && (information->DataLength == sizeof(ULONG))) { + + RtlMoveMemory( + (PVOID)&returnValue, + ((PUCHAR)information) + information->DataOffset, + sizeof(ULONG) + ); + + if (returnValue < 0) { + + returnValue = DefaultValue; + + } + + } else { + + returnValue = DefaultValue; + } + + return returnValue; + +} // AfdReadSingleParameter + + +NTSTATUS +AfdBuildDeviceAcl( + OUT PACL *DeviceAcl + ) + +/*++ + +Routine Description: + + This routine builds an ACL which gives Administrators and LocalSystem + principals full access. All other principals have no access. + +Arguments: + + DeviceAcl - Output pointer to the new ACL. + +Return Value: + + STATUS_SUCCESS or an appropriate error code. + +--*/ + +{ + PGENERIC_MAPPING GenericMapping; + PSID AdminsSid; + PSID SystemSid; + ULONG AclLength; + NTSTATUS Status; + ACCESS_MASK AccessMask = GENERIC_ALL; + PACL NewAcl; + + // + // Enable access to all the globally defined SIDs + // + + GenericMapping = IoGetFileObjectGenericMapping(); + + RtlMapGenericMask( &AccessMask, GenericMapping ); + + SeEnableAccessToExports(); + + AdminsSid = SeExports->SeAliasAdminsSid; + SystemSid = SeExports->SeLocalSystemSid; + + AclLength = sizeof( ACL ) + + 2 * sizeof( ACCESS_ALLOWED_ACE ) + + RtlLengthSid( AdminsSid ) + + RtlLengthSid( SystemSid ) - + 2 * sizeof( ULONG ); + + NewAcl = AFD_ALLOCATE_POOL( + PagedPool, + AclLength, + AFD_SECURITY_POOL_TAG + ); + + if (NewAcl == NULL) { + return( STATUS_INSUFFICIENT_RESOURCES ); + } + + Status = RtlCreateAcl (NewAcl, AclLength, ACL_REVISION ); + + if (!NT_SUCCESS( Status )) { + AFD_FREE_POOL( + NewAcl, + AFD_SECURITY_POOL_TAG + ); + return( Status ); + } + + Status = RtlAddAccessAllowedAce ( + NewAcl, + ACL_REVISION2, + AccessMask, + AdminsSid + ); + + ASSERT( NT_SUCCESS( Status )); + + Status = RtlAddAccessAllowedAce ( + NewAcl, + ACL_REVISION2, + AccessMask, + SystemSid + ); + + ASSERT( NT_SUCCESS( Status )); + + *DeviceAcl = NewAcl; + + return( STATUS_SUCCESS ); + +} // AfdBuildDeviceAcl + + +NTSTATUS +AfdCreateRawSecurityDescriptor( + VOID + ) + +/*++ + +Routine Description: + + This routine creates a security descriptor which gives access + only to Administrtors and LocalSystem. This descriptor is used + to access check raw endpoint opens. + +Arguments: + + None. + +Return Value: + + STATUS_SUCCESS or an appropriate error code. + +--*/ + +{ + PACL rawAcl; + NTSTATUS status; + BOOLEAN memoryAllocated = FALSE; + PSECURITY_DESCRIPTOR afdSecurityDescriptor; + ULONG afdSecurityDescriptorLength; + CHAR buffer[SECURITY_DESCRIPTOR_MIN_LENGTH]; + PSECURITY_DESCRIPTOR localSecurityDescriptor = + (PSECURITY_DESCRIPTOR) &buffer; + SECURITY_INFORMATION securityInformation = DACL_SECURITY_INFORMATION; + + + // + // Get a pointer to the security descriptor from the AFD device object. + // + status = ObGetObjectSecurity( + AfdDeviceObject, + &afdSecurityDescriptor, + &memoryAllocated + ); + + if (!NT_SUCCESS(status)) { + KdPrint(( + "AFD: Unable to get security descriptor, error: %x\n", + status + )); + ASSERT(memoryAllocated == FALSE); + return(status); + } + + // + // Build a local security descriptor with an ACL giving only + // administrators and system access. + // + status = AfdBuildDeviceAcl(&rawAcl); + + if (!NT_SUCCESS(status)) { + KdPrint(("AFD: Unable to create Raw ACL, error: %x\n", status)); + goto error_exit; + } + + (VOID) RtlCreateSecurityDescriptor( + localSecurityDescriptor, + SECURITY_DESCRIPTOR_REVISION + ); + + (VOID) RtlSetDaclSecurityDescriptor( + localSecurityDescriptor, + TRUE, + rawAcl, + FALSE + ); + + // + // Make a copy of the AFD descriptor. This copy will be the raw descriptor. + // + afdSecurityDescriptorLength = RtlLengthSecurityDescriptor( + afdSecurityDescriptor + ); + + AfdRawSecurityDescriptor = AFD_ALLOCATE_POOL( + PagedPool, + afdSecurityDescriptorLength, + AFD_SECURITY_POOL_TAG + ); + + if (AfdRawSecurityDescriptor == NULL) { + KdPrint(("AFD: couldn't allocate security descriptor\n")); + goto error_exit; + } + + RtlMoveMemory( + AfdRawSecurityDescriptor, + afdSecurityDescriptor, + afdSecurityDescriptorLength + ); + + // + // Now apply the local descriptor to the raw descriptor. + // + status = SeSetSecurityDescriptorInfo( + NULL, + &securityInformation, + localSecurityDescriptor, + &AfdRawSecurityDescriptor, + PagedPool, + IoGetFileObjectGenericMapping() + ); + + if (!NT_SUCCESS(status)) { + KdPrint(("AFD: SeSetSecurity failed, %lx\n", status)); + AFD_FREE_POOL( + AfdRawSecurityDescriptor, + AFD_SECURITY_POOL_TAG + ); + AFD_FREE_POOL( + rawAcl, + AFD_SECURITY_POOL_TAG + ); + AfdRawSecurityDescriptor = NULL; + goto error_exit; + } + + status = STATUS_SUCCESS; + +error_exit: + + ObReleaseObjectSecurity( + afdSecurityDescriptor, + memoryAllocated + ); + + return(status); +} diff --git a/private/ntos/afd/listen.c b/private/ntos/afd/listen.c new file mode 100644 index 000000000..1995fa686 --- /dev/null +++ b/private/ntos/afd/listen.c @@ -0,0 +1,1435 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + listen.c + +Abstract: + + This module contains the hyandling for IOCTL_AFD_START_LISTEN + and IOCTL_AFD_WAIT_FOR_LISTEN. + +Author: + + David Treadwell (davidtr) 21-Feb-1992 + +Revision History: + +--*/ + +#include "afdp.h" + +VOID +AfdCancelWaitForListen ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +AfdRestartAccept ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +PAFD_CONNECT_DATA_BUFFERS +CopyConnectDataBuffers ( + IN PAFD_CONNECT_DATA_BUFFERS OriginalConnectDataBuffers + ); + +BOOLEAN +CopySingleConnectDataBuffer ( + IN PAFD_CONNECT_DATA_INFO InConnectDataInfo, + OUT PAFD_CONNECT_DATA_INFO OutConnectDataInfo + ); + +VOID +AfdEnableDynamicBacklogOnEndpoint( + IN PAFD_ENDPOINT Endpoint, + IN LONG ListenBacklog + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGEAFD, AfdStartListen ) +#pragma alloc_text( PAGEAFD, AfdWaitForListen ) +#pragma alloc_text( PAGEAFD, AfdCancelWaitForListen ) +#pragma alloc_text( PAGEAFD, AfdConnectEventHandler ) +#pragma alloc_text( PAGEAFD, AfdRestartAccept ) +#pragma alloc_text( PAGEAFD, CopyConnectDataBuffers ) +#pragma alloc_text( PAGEAFD, CopySingleConnectDataBuffer ) +#pragma alloc_text( PAGEAFD, AfdEnableDynamicBacklogOnEndpoint ) +#endif + + +NTSTATUS +AfdStartListen ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine handles the IOCTL_AFD_START_LISTEN IRP, which starts + listening for connections on an AFD endpoint. + +Arguments: + + Irp - Pointer to I/O request packet + + IrpSp - pointer to the IO stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates the status of the request. + +--*/ + +{ + ULONG i; + NTSTATUS status; + PAFD_LISTEN_INFO afdListenInfo; + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + + // + // Set up local variables. + // + + afdListenInfo = Irp->AssociatedIrp.SystemBuffer; + endpoint = IrpSp->FileObject->FsContext; + ASSERT( endpoint->Type == AfdBlockTypeEndpoint ); + + // + // Make sure that the endpoint is in the correct state. + // + + if ( endpoint->State != AfdEndpointStateBound ) { + return STATUS_INVALID_PARAMETER; + } + + // + // Set the type and state of the endpoint to listening. + // + + endpoint->Type = AfdBlockTypeVcListening; + endpoint->State = AfdEndpointStateListening; + endpoint->EventsDisabled = AFD_DISABLED_LISTENING_POLL_EVENTS; + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdStartListen: Disabled %08lX events on endpoint %08lX\n", + endpoint->EventsDisabled, + endpoint + )); + } + + // + // Initialize lists which are specific to listening endpoints. + // + + InitializeListHead( &endpoint->Common.VcListening.FreeConnectionListHead ); + InitializeListHead( &endpoint->Common.VcListening.UnacceptedConnectionListHead ); + InitializeListHead( &endpoint->Common.VcListening.ReturnedConnectionListHead ); + InitializeListHead( &endpoint->Common.VcListening.ListeningIrpListHead ); + + // + // Initialize the tracking data for implementing dynamic backlog. + // + + endpoint->Common.VcListening.FreeConnectionCount = 0; + endpoint->Common.VcListening.TdiAcceptPendingCount = 0; + + AfdEnableDynamicBacklogOnEndpoint( + endpoint, + (LONG)afdListenInfo->MaximumConnectionQueue + ); + + // + // Open a pool of connections on the specified endpoint. The + // connect indication handler will use these connections when + // connect indications come in. + // + + for ( i = 0; i < afdListenInfo->MaximumConnectionQueue; i++ ) { + + status = AfdAddFreeConnection( endpoint ); + + if ( !NT_SUCCESS(status) ) { + goto error_exit; + } + } + + // + // Set up a connect indication handler on the specified endpoint. + // + + status = AfdSetEventHandler( + endpoint->AddressFileObject, + TDI_EVENT_CONNECT, + AfdConnectEventHandler, + endpoint + ); + + if ( !NT_SUCCESS(status) ) { + goto error_exit; + } + + // + // We're done, return to the user. + // + + return STATUS_SUCCESS; + +error_exit: + + // + // Take all the connection handles off the endpoint and close them. + // + + while ( (connection = AfdGetFreeConnection( endpoint )) != NULL ) { + ASSERT( connection->Type == AfdBlockTypeConnection ); + DEREFERENCE_CONNECTION( connection ); + } + + // + // Reset the type and state of the endpoint. + // + + endpoint->Type = AfdBlockTypeEndpoint; + endpoint->State = AfdEndpointStateBound; + + return status; + +} // AfdStartListen + + +NTSTATUS +AfdWaitForListen ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine handles the IOCTL_AFD_WAIT_FOR_LISTEN IRP, which either + immediately passes back to the caller a completed connection or + waits for a connection attempt. + +Arguments: + + Irp - Pointer to I/O request packet + + IrpSp - pointer to the IO stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates the status of the request. + +--*/ + +{ + KIRQL oldIrql1, oldIrql2; + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + PAFD_LISTEN_RESPONSE_INFO listenResponse; + + // + // Set up local variables. + // + + endpoint = IrpSp->FileObject->FsContext; + listenResponse = Irp->AssociatedIrp.SystemBuffer; + + // !!! need to check that the output buffer is large enough! + + // + // Make sure that the endpoint is in the correct state. + // + + if ( endpoint->State != AfdEndpointStateListening ) { + return STATUS_INVALID_PARAMETER; + } + + // + // Check if there is already an unaccepted connection on the + // endpoint. If there isn't, then we must wait until a connect + // attempt arrives before completing this IRP. + // + // Note that we hold the AfdSpinLock withe doing this checking-- + // this is necessary to synchronize with out indication handler. + // The cancel spin lock must also be held in order to preserve + // the correct order of spin lock acquisitions. + // + + IoAcquireCancelSpinLock( &oldIrql1 ); + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql2 ); + + connection = AfdGetUnacceptedConnection( endpoint ); + + if ( connection == NULL ) { + + // + // There were no outstanding unaccepted connections. Set up the + // cancel routine in the IRP. If the IRP has already been + // canceled, call our cancel routine. + // + + if ( Irp->Cancel ) { + + // + // The IRP has already been canceled. Just return + // STATUS_CANCELLED. + // + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql2 ); + IoReleaseCancelSpinLock( oldIrql1 ); + + Irp->IoStatus.Status = STATUS_CANCELLED; + Irp->IoStatus.Information = 0; + + IoCompleteRequest( Irp, 0 ); + + return STATUS_CANCELLED; + + } else { + + IoSetCancelRoutine( Irp, AfdCancelWaitForListen ); + } + + // + // Put this IRP on the endpoint's list of listening IRPs and + // return pending. We must hold the Cancel spin lock while + // we do this to prevent the IRP from being cancelled before + // we place it on the endpoint's list. If this were to happen + // then the IRP would never get cancelled. + // + + IoMarkIrpPending( Irp ); + + if( IrpSp->Parameters.DeviceIoControl.IoControlCode == + IOCTL_AFD_WAIT_FOR_LISTEN_LIFO ) { + + InsertHeadList( + &endpoint->Common.VcListening.ListeningIrpListHead, + &Irp->Tail.Overlay.ListEntry + ); + + } else { + + InsertTailList( + &endpoint->Common.VcListening.ListeningIrpListHead, + &Irp->Tail.Overlay.ListEntry + ); + + } + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql2 ); + IoReleaseCancelSpinLock( oldIrql1 ); + + return STATUS_PENDING; + } + + ASSERT( connection->Type == AfdBlockTypeConnection ); + + // + // There was a connection to use. Set up the return buffer. + // + + listenResponse->Sequence = (ULONG)connection; + + ASSERT( connection->State == AfdConnectionStateUnaccepted ); + + if( connection->RemoteAddressLength > + IrpSp->Parameters.DeviceIoControl.OutputBufferLength ) { + + // + // The specified remote address buffer is too small. Put + // the connection back at the head of the queue and fail + // the request. + // + + InsertHeadList( + &endpoint->Common.VcListening.UnacceptedConnectionListHead, + &connection->ListEntry + ); + + Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql2 ); + IoReleaseCancelSpinLock( oldIrql1 ); + + return STATUS_BUFFER_TOO_SMALL; + + } + + RtlMoveMemory( + &listenResponse->RemoteAddress, + connection->RemoteAddress, + connection->RemoteAddressLength + ); + + Irp->IoStatus.Information = + sizeof(*listenResponse) - sizeof(TRANSPORT_ADDRESS) + + connection->RemoteAddressLength; + + // + // Place the connection we're going to use on the endpoint's list of + // returned connections. + // + + InsertTailList( + &endpoint->Common.VcListening.ReturnedConnectionListHead, + &connection->ListEntry + ); + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql2 ); + IoReleaseCancelSpinLock( oldIrql1 ); + + // + // Indicate in the state of this connection that it has been + // returned to the user. + // + + connection->State = AfdConnectionStateReturned; + + // + // Complete the IRP. + // + + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + return STATUS_SUCCESS; + +} // AfdWaitForListen + + +VOID +AfdCancelWaitForListen ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +{ + KIRQL oldIrql; + PLIST_ENTRY endpointListEntry, irpListEntry; + + IF_DEBUG(LISTEN) { + KdPrint(( "AfdCancelWaitForListen: called on IRP %lx\n", Irp )); + } + + // + // While holding the AFD spin lock, search all listening endpoints + // for this IRP. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + for ( endpointListEntry = AfdEndpointListHead.Flink; + endpointListEntry != &AfdEndpointListHead; + endpointListEntry = endpointListEntry->Flink ) { + + PAFD_ENDPOINT endpoint = CONTAINING_RECORD( + endpointListEntry, + AFD_ENDPOINT, + GlobalEndpointListEntry + ); + + // + // If this is not a listening endpoint, then don't look at it. + // + + if ( endpoint->Type != AfdBlockTypeVcListening ) { + continue; + } + + // + // Search all listening IRPs on the endpoint to see if any are + // the one we're supposed to be cancelling. + // + + for ( irpListEntry = endpoint->Common.VcListening.ListeningIrpListHead.Flink; + irpListEntry != &endpoint->Common.VcListening.ListeningIrpListHead; + irpListEntry = irpListEntry->Flink ) { + + PIRP testIrp = CONTAINING_RECORD( + irpListEntry, + IRP, + Tail.Overlay.ListEntry + ); + + if ( testIrp == Irp ) { + + IF_DEBUG(LISTEN) { + KdPrint(( "AfdCancelWaitForListen: found IRP on " + "endpoint %lx\n", endpoint )); + } + + // + // We found the IRP. Remove it from this endpoint's + // list of listening IRPs. + // + + RemoveEntryList( &Irp->Tail.Overlay.ListEntry ); + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + // + // Complete the IRP with STATUS_CANCELLED and return. + // + + Irp->IoStatus.Status = STATUS_CANCELLED; + Irp->IoStatus.Information = 0; + + + IoCompleteRequest( Irp, AfdPriorityBoost ); + + return; + } + } + } + + // + // We didn't find the IRP. Is this possible? + // + + ASSERT( FALSE ); + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + return; + +} // AfdCancelWaitForListen + + +NTSTATUS +AfdConnectEventHandler ( + IN PVOID TdiEventContext, + IN int RemoteAddressLength, + IN PVOID RemoteAddress, + IN int UserDataLength, + IN PVOID UserData, + IN int OptionsLength, + IN PVOID Options, + OUT CONNECTION_CONTEXT *ConnectionContext, + OUT PIRP *AcceptIrp + ) + +/*++ + +Routine Description: + + This is the connect event handler for listening AFD endpoints. + It attempts to get a connection, and if successful checks whether + there are outstanding IOCTL_WAIT_FOR_LISTEN IRPs. If so, the + first one is completed; if not, the connection is queued in a list of + available, unaccepted but connected connection objects. + +Arguments: + + TdiEventContext - the endpoint on which the connect attempt occurred. + +Return Value: + + NTSTATUS -- Indicates the status of the request. + +--*/ + +{ + PAFD_CONNECTION connection; + PAFD_ENDPOINT endpoint; + PIRP irp; + PDEVICE_OBJECT deviceObject; + PFILE_OBJECT fileObject; + KIRQL oldIrql; + PAFD_CONNECT_DATA_BUFFERS connectDataBuffers; + PTDI_CONNECTION_INFORMATION requestConnectionInformation; + + IF_DEBUG(LISTEN) { + KdPrint(( "AfdConnectEventHandler: called on endpoint %lx\n", + TdiEventContext )); + } + + endpoint = TdiEventContext; + ASSERT( endpoint != NULL ); + ASSERT( endpoint->Type == AfdBlockTypeVcListening ); + + // + // If the endpoint is closing, refuse to accept the connection. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + if ( endpoint->State == AfdEndpointStateClosing || + endpoint->EndpointCleanedUp ) { + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + return STATUS_INSUFFICIENT_RESOURCES; + + } + + // + // Reference the endpoint while holding AfdSpinLock so that the + // endpoint doesn't go away beneath us. + // + + REFERENCE_ENDPOINT( endpoint ); + + // + // If there are connect data buffers on the listening endpoint, + // create equivalent buffers that we'll use for the connection. + // + + connectDataBuffers = NULL; + + if( endpoint->ConnectDataBuffers != NULL ) { + + connectDataBuffers = CopyConnectDataBuffers( + endpoint->ConnectDataBuffers + ); + + if( connectDataBuffers == NULL ) { + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + DEREFERENCE_ENDPOINT( endpoint ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } + + // + // If we got connect data and/or options, save them on the connection. + // + + if( UserData != NULL && UserDataLength > 0 ) { + + NTSTATUS status; + + status = AfdSaveReceivedConnectData( + &connectDataBuffers, + IOCTL_AFD_SET_CONNECT_DATA, + UserData, + UserDataLength + ); + + if( !NT_SUCCESS(status) ) { + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + DEREFERENCE_ENDPOINT( endpoint ); + AfdFreeConnectDataBuffers( connectDataBuffers ); + return status; + + } + + } + + if( Options != NULL && OptionsLength > 0 ) { + + NTSTATUS status; + + status = AfdSaveReceivedConnectData( + &connectDataBuffers, + IOCTL_AFD_SET_CONNECT_OPTIONS, + Options, + OptionsLength + ); + + if( !NT_SUCCESS(status) ) { + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + DEREFERENCE_ENDPOINT( endpoint ); + AfdFreeConnectDataBuffers( connectDataBuffers ); + return status; + + } + + } + + if( connectDataBuffers != NULL ) { + + // + // We allocated extra space at the end of the connect data + // buffers structure. We'll use this for the + // TDI_CONNECTION_INFORMATION structure that holds response + // connect data and options. Not pretty, but the fastest + // and easiest way to accomplish this. + // + + requestConnectionInformation = + &connectDataBuffers->TdiConnectionInfo; + + RtlZeroMemory( + requestConnectionInformation, + sizeof(*requestConnectionInformation) + ); + + requestConnectionInformation->UserData = + connectDataBuffers->SendConnectData.Buffer; + requestConnectionInformation->UserDataLength = + connectDataBuffers->SendConnectData.BufferLength; + requestConnectionInformation->Options = + connectDataBuffers->SendConnectOptions.Buffer; + requestConnectionInformation->OptionsLength = + connectDataBuffers->SendConnectOptions.BufferLength; + + } else { + + requestConnectionInformation = NULL; + + } + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + // + // Enforce dynamic backlog if enabled. + // + + if( endpoint->Common.VcListening.EnableDynamicBacklog ) { + + LONG freeCount; + LONG acceptCount; + LONG failedCount; + + // + // If the free connection count has dropped below the configured + // minimum, the number of "quasi-free" connections is less than + // the configured maximum, and we haven't already queued enough + // requests to take us past the maximum, then add new free + // connections to the endpoint. "Quasi-free" is defined as the + // sum of the free connection count and the count of pending TDI + // accepts. + // + + freeCount = endpoint->Common.VcListening.FreeConnectionCount; + acceptCount = endpoint->Common.VcListening.TdiAcceptPendingCount; + failedCount = endpoint->Common.VcListening.FailedConnectionAdds; + + if( freeCount < AfdMinimumDynamicBacklog && + ( freeCount + acceptCount ) < AfdMaximumDynamicBacklog && + failedCount < AfdMaximumDynamicBacklog ) { + + InterlockedExchangeAdd( + &endpoint->Common.VcListening.FailedConnectionAdds, + AfdMaximumDynamicBacklog + ); + + AfdInitiateListenBacklogReplenish( endpoint ); + + } + + } + + // + // Attempt to get a pre-allocated connection object to handle the + // connection. + // + + connection = AfdGetFreeConnection( endpoint ); + + IF_DEBUG(LISTEN) { + KdPrint(( "AfdConnectEventHandler: using connection %lx\n", + connection )); + } + + // + // If there are no free connections on the endpoint, fail the + // connect attempt. + // + + if ( connection == NULL ) { + + if ( connectDataBuffers != NULL ) { + AfdFreeConnectDataBuffers( connectDataBuffers ); + } + + // + // If there have been failed connection additions, kick off + // a request to an executive worker thread to attempt to add + // some additional free connections. + // + + if ( endpoint->Common.VcListening.FailedConnectionAdds != 0 ) { + AfdInitiateListenBacklogReplenish( endpoint ); + } + + DEREFERENCE_ENDPOINT( endpoint ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + ASSERT( connection->Type == AfdBlockTypeConnection ); + + // + // Save a pointer to the connect data buffers, if any. + // + + connection->ConnectDataBuffers = connectDataBuffers; + + // + // Get the address of the target device object. + // + + fileObject = connection->FileObject; + ASSERT( fileObject != NULL ); + deviceObject = connection->DeviceObject; + + // + // 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 ) { + + // + // Unable to allocate an IRP. Free resources and inform the + // caller. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + if ( connection->ConnectDataBuffers != NULL ) { + AFD_FREE_POOL( + connection->ConnectDataBuffers, + AFD_CONNECT_DATA_POOL_TAG + ); + connection->ConnectDataBuffers = NULL; + } + + InsertTailList( + &endpoint->Common.VcListening.FreeConnectionListHead, + &connection->ListEntry + ); + + InterlockedIncrement( + &endpoint->Common.VcListening.FreeConnectionCount + ); + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + DEREFERENCE_ENDPOINT( endpoint ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Initialize the IRP for an accept operation. + // + + 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; + + TdiBuildAccept( + irp, + deviceObject, + fileObject, + AfdRestartAccept, + connection, + requestConnectionInformation, + NULL + ); + + IoSetNextIrpStackLocation( irp ); + + // + // Set the return IRP so the transport processes this accept IRP. + // + + *AcceptIrp = irp; + + // + // Set up the connection context as a pointer to the connection block + // we're going to use for this connect request. This allows the + // TDI provider to which connection object to use. + // + + *ConnectionContext = (CONNECTION_CONTEXT)connection; + + // + // Set the block state of this connection. + // + + connection->State = AfdConnectionStateUnaccepted; + + // + // We need to store the remote address in the connection. If the + // connection object already has a remote address block that is + // sufficient, use it. Otherwise, allocate a new one. + // + + if ( connection->RemoteAddress != NULL && + connection->RemoteAddressLength < (ULONG)RemoteAddressLength ) { + + AFD_FREE_POOL( + connection->RemoteAddress, + AFD_REMOTE_ADDRESS_POOL_TAG + ); + connection->RemoteAddress = NULL; + } + + if ( connection->RemoteAddress == NULL ) { + + connection->RemoteAddress = AFD_ALLOCATE_POOL( + NonPagedPoolMustSucceed, + RemoteAddressLength, + AFD_REMOTE_ADDRESS_POOL_TAG + ); + } + + connection->RemoteAddressLength = RemoteAddressLength; + + RtlMoveMemory( + connection->RemoteAddress, + RemoteAddress, + RemoteAddressLength + ); + + // + // Save the address endpoint pointer in the connection. + // + + connection->Endpoint = endpoint; + + // + // Add an additional reference to the connection. This prevents + // the connection from being closed until the disconnect event + // handler is called. + // + + AfdAddConnectedReference( connection ); + + // + // Remember that we have another TDI accept pending on this endpoint. + // + + InterlockedIncrement( + &endpoint->Common.VcListening.TdiAcceptPendingCount + ); + + // + // Indicate to the TDI provider that we allocated a connection to + // service this connect attempt. + // + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // AfdConnectEventHandler + + +NTSTATUS +AfdRestartAccept ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + KIRQL oldIrql1, oldIrql2; + LIST_ENTRY irpCompletionList; + PLIST_ENTRY listEntry; + PIRP waitForListenIrp; + BOOLEAN successfulCompletion; + + connection = (PAFD_CONNECTION)Context; + ASSERT( connection != NULL ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + endpoint = connection->Endpoint; + ASSERT( endpoint != NULL ); + ASSERT( endpoint->Type == AfdBlockTypeVcListening ); + + UPDATE_CONN( connection, Irp->IoStatus.Status ); + + IF_DEBUG(ACCEPT) { + KdPrint(( "AfdRestartAccept: accept completed, status = %X, " + "endpoint = %lx, connection = %lx\n", + Irp->IoStatus.Status, endpoint, + endpoint->Common.VcConnecting.Connection )); + } + + // + // Remember that a TDI accept has completed on this endpoint. + // + + InterlockedDecrement( + &endpoint->Common.VcListening.TdiAcceptPendingCount + ); + + // + // If there are outstanding polls waiting for a connection on this + // endpoint, complete them. + // + + AfdIndicatePollEvent( + endpoint, + AFD_POLL_ACCEPT_BIT, + STATUS_SUCCESS + ); + + // + // If the accept failed, treat it like an abortive disconnect. + // This way the application still gets a new endpoint, but it gets + // told about the reset. + // + + if ( !NT_SUCCESS(Irp->IoStatus.Status) ) { + AfdDisconnectEventHandler( + NULL, + connection, + 0, + NULL, + 0, + NULL, + TDI_DISCONNECT_ABORT + ); + } + + // + // Remember the time that the connection started. + // + + KeQuerySystemTime( (PLARGE_INTEGER)&connection->ConnectTime ); + + // + // Check whether the endpoint has been cleaned up yet. If so, just + // throw out this connection, since it cannot be accepted any more. + // Also, this closes a hole between the endpoint being cleaned up + // and all the connections that reference it being deleted. + // + + IoAcquireCancelSpinLock( &oldIrql2 ); + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql1 ); + + if ( endpoint->EndpointCleanedUp ) { + + // + // First release the locks. + // + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql1 ); + IoReleaseCancelSpinLock( oldIrql2 ); + + // + // Abort the connection. + // + + AfdAbortConnection( 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; + + } + + // + // Initialize the local list of IRPs to complete. + // + + InitializeListHead( &irpCompletionList ); + + successfulCompletion = FALSE; + + // + // Scan the list of pending IOCTL_AFD_WAIT_FOR_LISTEN IRPs. Remove IRPs + // from the pending list and append them the local completion list. + // We'll stop our scan of the pending list as soon as either a) the + // pending list is exhausted, or b) we find a pended IRP with sufficient + // listen response buffer that it can be completed successfully. In + // either case, the IRPs are fully setup for completion (status code, + // etc.) before being appended to the completion list. + // + // This may seem like a lot of trouble (and it is). We must do this + // because we may have multiple IRPs to complete, we must hold the + // spinlock while traversing the pended IRP list, and we cannot hold + // the spinlock when calling IoCompleteRequest(). + // + + while( !IsListEmpty( &endpoint->Common.VcListening.ListeningIrpListHead ) ) { + + PIO_STACK_LOCATION irpSp; + PAFD_LISTEN_RESPONSE_INFO listenResponse; + + ASSERT( !successfulCompletion ); + + // + // Take the first IRP off the listening list. + // + + listEntry = RemoveHeadList( + &endpoint->Common.VcListening.ListeningIrpListHead + ); + + // + // Get a pointer to the current IRP, reset its cancel routine, + // and get a pointer to the current stack lockation. + // + + waitForListenIrp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + + IF_DEBUG(LISTEN) { + KdPrint(( "AfdRestartAccept: completing IRP %lx\n", + waitForListenIrp )); + } + + IoSetCancelRoutine( waitForListenIrp, NULL ); + + irpSp = IoGetCurrentIrpStackLocation( waitForListenIrp ); + + // + // Check the listen response buffer. If it's insufficient, + // set its completion status to STATUS_BUFFER_TOO_SMALL and + // append it to the completion list. + // + + listenResponse = waitForListenIrp->AssociatedIrp.SystemBuffer; + + if( connection->RemoteAddressLength > + ( irpSp->Parameters.DeviceIoControl.OutputBufferLength - + sizeof(listenResponse->Sequence) ) ) { + + // + // Setup the IRP completion status. + // + + waitForListenIrp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + waitForListenIrp->IoStatus.Information = 0; + + // + // Append to the IRP completion list. + // + + InsertTailList( + &irpCompletionList, + &waitForListenIrp->Tail.Overlay.ListEntry + ); + + } else { + + // + // This is an IRP we can actually complete successfully. + // Setup the sequence number (actually the pointer to the + // connection) and the address of the remote client. + // + + listenResponse->Sequence = (ULONG)connection; + + RtlMoveMemory( + &listenResponse->RemoteAddress, + connection->RemoteAddress, + connection->RemoteAddressLength + ); + + // + // Setup the IRP completion status. + // + + waitForListenIrp->IoStatus.Status = STATUS_SUCCESS; + waitForListenIrp->IoStatus.Information = + sizeof(*listenResponse) - sizeof(TRANSPORT_ADDRESS) + + connection->RemoteAddressLength; + + // + // Place the connection we're going to use on the endpoint's + // list of returned connections. + // + + InsertTailList( + &endpoint->Common.VcListening.ReturnedConnectionListHead, + &connection->ListEntry + ); + + // + // Indicate in the state of this connection that it has been + // returned to the user. + // + + connection->State = AfdConnectionStateReturned; + + // + // Append to the IRP completion list, then bail out of the + // scan loop. + // + + InsertTailList( + &irpCompletionList, + &waitForListenIrp->Tail.Overlay.ListEntry + ); + + successfulCompletion = TRUE; + break; + + } + + } + + // + // At this point, we still hold the AFD and I/O cancel spinlocks. + // We have a (potentially empty) list of IRPs that we need to + // complete. + // + // If we didn't manage to find a queued IRP to complete successfully, + // then enqueue the connection onto the endpoint's list of unaccepted, + // unreturned connections. We must do this *before* releasing the + // spinlocks. + // + + if( !successfulCompletion ) { + + InsertTailList( + &endpoint->Common.VcListening.UnacceptedConnectionListHead, + &connection->ListEntry + ); + + } + + // + // We can now safely release the spinlocks and start completing + // IRPs. + // + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql1 ); + IoReleaseCancelSpinLock( oldIrql2 ); + + // + // Scan the IRP completion list, and complete them all. + // + + while( !IsListEmpty( &irpCompletionList ) ) { + + listEntry = RemoveHeadList( &irpCompletionList ); + + // + // Get a pointer to the current IRP and complete it. Note that + // all completion information (IoStatus.Information and + // IoStatus.Status) was set before appending the IRP to this + // list. + // + + waitForListenIrp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + + IoCompleteRequest( + waitForListenIrp, + AfdPriorityBoost + ); + + } + + // + // 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; + +} // AfdRestartAccept + + +PAFD_CONNECT_DATA_BUFFERS +CopyConnectDataBuffers ( + IN PAFD_CONNECT_DATA_BUFFERS OriginalConnectDataBuffers + ) +{ + PAFD_CONNECT_DATA_BUFFERS connectDataBuffers; + + connectDataBuffers = AFD_ALLOCATE_POOL( + NonPagedPool, + sizeof(*connectDataBuffers), + AFD_CONNECT_DATA_POOL_TAG + ); + + if ( connectDataBuffers == NULL ) { + return NULL; + } + + RtlZeroMemory( connectDataBuffers, sizeof(*connectDataBuffers) ); + + if ( !CopySingleConnectDataBuffer( + &OriginalConnectDataBuffers->SendConnectData, + &connectDataBuffers->SendConnectData ) ) { + AfdFreeConnectDataBuffers( connectDataBuffers ); + return NULL; + } + + if ( !CopySingleConnectDataBuffer( + &OriginalConnectDataBuffers->SendConnectOptions, + &connectDataBuffers->SendConnectOptions ) ) { + AfdFreeConnectDataBuffers( connectDataBuffers ); + return NULL; + } + + if ( !CopySingleConnectDataBuffer( + &OriginalConnectDataBuffers->ReceiveConnectData, + &connectDataBuffers->ReceiveConnectData ) ) { + AfdFreeConnectDataBuffers( connectDataBuffers ); + return NULL; + } + + if ( !CopySingleConnectDataBuffer( + &OriginalConnectDataBuffers->ReceiveConnectOptions, + &connectDataBuffers->ReceiveConnectOptions ) ) { + AfdFreeConnectDataBuffers( connectDataBuffers ); + return NULL; + } + + if ( !CopySingleConnectDataBuffer( + &OriginalConnectDataBuffers->SendDisconnectData, + &connectDataBuffers->SendDisconnectData ) ) { + AfdFreeConnectDataBuffers( connectDataBuffers ); + return NULL; + } + + if ( !CopySingleConnectDataBuffer( + &OriginalConnectDataBuffers->SendDisconnectOptions, + &connectDataBuffers->SendDisconnectOptions ) ) { + AfdFreeConnectDataBuffers( connectDataBuffers ); + return NULL; + } + + if ( !CopySingleConnectDataBuffer( + &OriginalConnectDataBuffers->ReceiveDisconnectData, + &connectDataBuffers->ReceiveDisconnectData ) ) { + AfdFreeConnectDataBuffers( connectDataBuffers ); + return NULL; + } + + if ( !CopySingleConnectDataBuffer( + &OriginalConnectDataBuffers->ReceiveDisconnectOptions, + &connectDataBuffers->ReceiveDisconnectOptions ) ) { + AfdFreeConnectDataBuffers( connectDataBuffers ); + return NULL; + } + + return connectDataBuffers; + +} // CopyConnectDataBuffers + + +BOOLEAN +CopySingleConnectDataBuffer ( + IN PAFD_CONNECT_DATA_INFO InConnectDataInfo, + OUT PAFD_CONNECT_DATA_INFO OutConnectDataInfo + ) +{ + + if ( InConnectDataInfo->Buffer != NULL && + InConnectDataInfo->BufferLength != 0 ) { + + OutConnectDataInfo->BufferLength = InConnectDataInfo->BufferLength; + + OutConnectDataInfo->Buffer = AFD_ALLOCATE_POOL( + NonPagedPool, + OutConnectDataInfo->BufferLength, + AFD_CONNECT_DATA_POOL_TAG + ); + + if ( OutConnectDataInfo->Buffer == NULL ) { + return FALSE; + } + + RtlCopyMemory( + OutConnectDataInfo->Buffer, + InConnectDataInfo->Buffer, + InConnectDataInfo->BufferLength + ); + + } else { + + OutConnectDataInfo->Buffer = NULL; + OutConnectDataInfo->BufferLength = 0; + } + + return TRUE; + +} // CopySingleConnectDataBuffer + + +VOID +AfdEnableDynamicBacklogOnEndpoint( + IN PAFD_ENDPOINT Endpoint, + IN LONG ListenBacklog + ) + +/*++ + +Routine Description: + + Determine if dynamic backlog should be enabled for the given + endpoint using the specified listen() backlog. + +Arguments: + + Endpoint - The endpoint to manipulate. + + ListenBacklog - The backlog passed into the listen() API. + +Return Value: + + None. + +--*/ + +{ + + // + // CODEWORK: For IP endpoints we could conditionally enable + // dynamic backlog by looking up the IP Port number in a + // database read from the registry. + // + + if( AfdEnableDynamicBacklog && + ListenBacklog > AfdMinimumDynamicBacklog ) { + + Endpoint->Common.VcListening.EnableDynamicBacklog = TRUE; + + } else { + + Endpoint->Common.VcListening.EnableDynamicBacklog = FALSE; + + } + +} // AfdEnableDynamicBacklogOnEndpoint + diff --git a/private/ntos/afd/misc.c b/private/ntos/afd/misc.c new file mode 100644 index 000000000..cbd9663c7 --- /dev/null +++ b/private/ntos/afd/misc.c @@ -0,0 +1,4804 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + misc.c + +Abstract: + + This module contains the miscellaneous AFD routines. + +Author: + + David Treadwell (davidtr) 13-Nov-1992 + +Revision History: + +--*/ + +#include "afdp.h" +#define TL_INSTANCE 0 +#include <ipexport.h> +#include <tdiinfo.h> +#include <tcpinfo.h> +#include <ntddtcp.h> + + +VOID +AfdDoWork ( + IN PVOID Context + ); + +NTSTATUS +AfdRestartDeviceControl ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +VOID +AfdUnlockDriver ( + IN PVOID Context + ); + +#ifdef NT351 +typedef struct _AFD_APC { + KAPC Apc; +} AFD_APC, *PAFD_APC; + +VOID +AfdSpecialApc ( + struct _KAPC *Apc, + PKNORMAL_ROUTINE *NormalRoutine, + PVOID *NormalContext, + PVOID *SystemArgument1, + PVOID *SystemArgument2 + ); + +VOID +AfdSpecialApcRundown ( + struct _KAPC *Apc + ); +#endif // NT351 + +BOOLEAN +AfdCompareAddresses( + IN PTRANSPORT_ADDRESS Address1, + IN ULONG Address1Length, + IN PTRANSPORT_ADDRESS Address2, + IN ULONG Address2Length + ); + +PAFD_CONNECTION +AfdFindReturnedConnection( + IN PAFD_ENDPOINT Endpoint, + IN ULONG Sequence + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, AfdCalcBufferArrayByteLengthRead ) +#pragma alloc_text( PAGE, AfdCalcBufferArrayByteLengthWrite ) +#pragma alloc_text( PAGE, AfdCopyBufferArrayToBuffer ) +#pragma alloc_text( PAGE, AfdCopyBufferToBufferArray ) +#pragma alloc_text( PAGEAFD, AfdAdvanceMdlChain ) +#pragma alloc_text( PAGEAFD, AfdAllocateMdlChain ) +#pragma alloc_text( PAGE, AfdQueryHandles ) +#pragma alloc_text( PAGE, AfdGetInformation ) +#pragma alloc_text( PAGE, AfdSetInformation ) +#pragma alloc_text( PAGE, AfdSetInLineMode ) +#pragma alloc_text( PAGE, AfdGetContext ) +#pragma alloc_text( PAGE, AfdGetContextLength ) +#pragma alloc_text( PAGE, AfdSetContext ) +#pragma alloc_text( PAGE, AfdIssueDeviceControl ) +#pragma alloc_text( PAGE, AfdSetEventHandler ) +#pragma alloc_text( PAGE, AfdInsertNewEndpointInList ) +#pragma alloc_text( PAGE, AfdRemoveEndpointFromList ) +#pragma alloc_text( PAGEAFD, AfdCompleteIrpList ) +#pragma alloc_text( PAGEAFD, AfdErrorEventHandler ) +//#pragma alloc_text( PAGEAFD, AfdRestartDeviceControl ) // can't ever be paged! +#pragma alloc_text( PAGEAFD, AfdGetConnectData ) +#pragma alloc_text( PAGEAFD, AfdSetConnectData ) +#pragma alloc_text( PAGEAFD, AfdFreeConnectDataBuffers ) +#pragma alloc_text( PAGEAFD, AfdSaveReceivedConnectData ) +//#pragma alloc_text( PAGEAFD, AfdDoWork ) +#pragma alloc_text( PAGEAFD, AfdAllocateWorkItem ) +#pragma alloc_text( PAGEAFD, AfdQueueWorkItem ) +#pragma alloc_text( PAGEAFD, AfdFreeWorkItem ) +#if DBG +#pragma alloc_text( PAGEAFD, AfdIoCallDriverDebug ) +#pragma alloc_text( PAGEAFD, AfdAllocateWorkItemPool ) +#pragma alloc_text( PAGEAFD, AfdFreeWorkItemPool ) +#else +#pragma alloc_text( PAGEAFD, AfdIoCallDriverFree ) +#endif +#ifdef NT351 +#pragma alloc_text( PAGE, AfdReferenceEventObjectByHandle ) +#pragma alloc_text( PAGE, AfdQueueUserApc ) +#pragma alloc_text( PAGE, AfdSpecialApc ) +#pragma alloc_text( PAGE, AfdSpecialApcRundown ) +#endif +#pragma alloc_text( PAGE, AfdSetQos ) +#pragma alloc_text( PAGE, AfdGetQos ) +#pragma alloc_text( PAGE, AfdNoOperation ) +#pragma alloc_text( PAGE, AfdValidateGroup ) +#pragma alloc_text( PAGE, AfdCompareAddresses ) +#pragma alloc_text( PAGE, AfdGetUnacceptedConnectData ) +#pragma alloc_text( PAGE, AfdFindReturnedConnection ) +#endif + + +VOID +AfdCompleteIrpList ( + IN PLIST_ENTRY IrpListHead, + IN PKSPIN_LOCK SpinLock, + IN NTSTATUS Status, + IN PAFD_IRP_CLEANUP_ROUTINE CleanupRoutine OPTIONAL + ) + +/*++ + +Routine Description: + + Completes a list of IRPs with the specified status. + +Arguments: + + IrpListHead - the head of the list of IRPs to complete. + + SpinLock - a lock which protects the list of IRPs. + + Status - the status to use for completing the IRPs. + + CleanupRoutine - a pointer to an optional IRP cleanup routine called + before the IRP is completed. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY listEntry; + PIRP irp; + KIRQL oldIrql; + KIRQL cancelIrql; + + IoAcquireCancelSpinLock( &cancelIrql ); + AfdAcquireSpinLock( SpinLock, &oldIrql ); + + while ( !IsListEmpty( IrpListHead ) ) { + + // + // Remove the first IRP from the list, get a pointer to + // the IRP and reset the cancel routine in the IRP. The + // IRP is no longer cancellable. + // + + listEntry = RemoveHeadList( IrpListHead ); + irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry ); + IoSetCancelRoutine( irp, NULL ); + + // + // If we have a cleanup routine, call it. + // + + if( CleanupRoutine != NULL ) { + + (CleanupRoutine)( irp ); + + } + + // + // We must release the locks in order to actually + // complete the IRP. It is OK to release these locks + // because we don't maintain any absolute pointer into + // the list; the loop termination condition is just + // whether the list is completely empty. + // + + AfdReleaseSpinLock( SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + // + // Complete the IRP. + // + + irp->IoStatus.Status = Status; + irp->IoStatus.Information = 0; + + IoCompleteRequest( irp, AfdPriorityBoost ); + + // + // Reacquire the locks and continue completing IRPs. + // + + IoAcquireCancelSpinLock( &cancelIrql ); + AfdAcquireSpinLock( SpinLock, &oldIrql ); + } + + AfdReleaseSpinLock( SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + return; + +} // AfdCompleteIrpList + + +NTSTATUS +AfdErrorEventHandler ( + IN PVOID TdiEventContext, + IN NTSTATUS Status + ) +{ + + IF_DEBUG(CONNECT) { + KdPrint(( "AfdErrorEventHandler called for endpoint %lx\n", + TdiEventContext )); + + } + + return STATUS_SUCCESS; + +} // AfdErrorEventHandler + + +VOID +AfdInsertNewEndpointInList ( + IN PAFD_ENDPOINT Endpoint + ) + +/*++ + +Routine Description: + + Inserts a new endpoint in the global list of AFD endpoints. If this + is the first endpoint, then this routine does various allocations to + prepare AFD for usage. + +Arguments: + + Endpoint - the endpoint being added. + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE( ); + + // + // Acquire a lock which prevents other threads from performing this + // operation. + // + + ExAcquireResourceExclusive( AfdResource, TRUE ); + + InterlockedIncrement( + &AfdEndpointsOpened + ); + + // + // If the list of endpoints is empty, do some allocations. + // + + if ( IsListEmpty( &AfdEndpointListHead ) ) { + + // + // Tell MM to revert to normal paging semantics. + // + + MmResetDriverPaging( DriverEntry ); + + // + // Lock down the AFD section that cannot be pagable if any + // sockets are open. + // + + ASSERT( AfdDiscardableCodeHandle == NULL ); + + AfdDiscardableCodeHandle = MmLockPagableCodeSection( AfdGetBuffer ); + ASSERT( AfdDiscardableCodeHandle != NULL ); + + AfdLoaded = TRUE; + } + + // + // Add the endpoint to the list(s). + // + + ExInterlockedInsertHeadList( + &AfdEndpointListHead, + &Endpoint->GlobalEndpointListEntry, + &AfdSpinLock + ); + + if( Endpoint->GroupType == GroupTypeConstrained ) { + ExInterlockedInsertHeadList( + &AfdConstrainedEndpointListHead, + &Endpoint->ConstrainedEndpointListEntry, + &AfdSpinLock + ); + } + + // + // Release the lock and return. + // + + ExReleaseResource( AfdResource ); + + return; + +} // AfdInsertNewEndpointInList + + +VOID +AfdRemoveEndpointFromList ( + IN PAFD_ENDPOINT Endpoint + ) + +/*++ + +Routine Description: + + Removes a new endpoint from the global list of AFD endpoints. If + this is the last endpoint in the list, then this routine does + various deallocations to save resource utilization. + +Arguments: + + Endpoint - the endpoint being removed. + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE( ); + + // + // Acquire a lock which prevents other threads from performing this + // operation. + // + + ExAcquireResourceExclusive( AfdResource, TRUE ); + + InterlockedIncrement( + &AfdEndpointsClosed + ); + + // + // Remove the endpoint from the list(s). + // + + AfdInterlockedRemoveEntryList( + &Endpoint->GlobalEndpointListEntry, + &AfdSpinLock + ); + + if( Endpoint->GroupType == GroupTypeConstrained ) { + AfdInterlockedRemoveEntryList( + &Endpoint->ConstrainedEndpointListEntry, + &AfdSpinLock + ); + } + + // + // If the list of endpoints is now empty, do some deallocations. + // + + if ( IsListEmpty( &AfdEndpointListHead ) ) { + + PAFD_WORK_ITEM afdWorkItem; + + // + // Unlock the AFD section that can be pagable when no sockets + // are open. + // + + ASSERT( IsListEmpty( &AfdConstrainedEndpointListHead ) ); + ASSERT( AfdDiscardableCodeHandle != NULL ); + + MmUnlockPagableImageSection( AfdDiscardableCodeHandle ); + + AfdDiscardableCodeHandle = NULL; + + // + // Queue off an executive worker thread to unlock AFD. We do + // this using special hacks in the AFD worker thread code so + // that we don't need to acuire a spin lock after the unlock. + // + + afdWorkItem = AfdAllocateWorkItem(); + ASSERT( afdWorkItem != NULL ); + + AfdQueueWorkItem( AfdUnlockDriver, afdWorkItem ); + } + + // + // Release the lock and return. + // + + ExReleaseResource( AfdResource ); + + return; + +} // AfdRemoveEndpointFromList + + +VOID +AfdUnlockDriver ( + IN PVOID Context + ) +{ + // + // Free the work item allocated in AdfRemoveEndpointFromList(). + // + + AfdFreeWorkItem( (PAFD_WORK_ITEM)Context ); + + // + // Acquire a lock which prevents other threads from performing this + // operation. + // + + ExAcquireResourceExclusive( AfdResource, TRUE ); + + // + // Test whether the endpoint list remains empty. If it is still + // empty, we can proceed with unlocking the driver. If a new + // endpoint has been placed on the list, then do not make AFD + // pagable. + // + + if ( IsListEmpty( &AfdEndpointListHead ) ) { + + // + // Tell MM that it can page all of AFD as it desires. + // + + AfdLoaded = FALSE; + MmPageEntireDriver( DriverEntry ); + } + + ExReleaseResource( AfdResource ); + +} // AfdUnlockDriver + + +VOID +AfdInterlockedRemoveEntryList ( + IN PLIST_ENTRY ListEntry, + IN PKSPIN_LOCK SpinLock + ) +{ + KIRQL oldIrql; + + // + // Our own routine since EX doesn't have a version of this.... + // + + AfdAcquireSpinLock( SpinLock, &oldIrql ); + RemoveEntryList( ListEntry ); + AfdReleaseSpinLock( SpinLock, oldIrql ); + +} // AfdInterlockedRemoveEntryList + + +NTSTATUS +AfdQueryHandles ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Returns information about the TDI handles corresponding to an AFD + endpoint. NULL is returned for either the connection handle or the + address handle (or both) if the endpoint does not have that particular + object. + +Arguments: + + Irp - Pointer to I/O request packet. + + IrpSp - pointer to the IO stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PAFD_ENDPOINT endpoint; + PAFD_HANDLE_INFO handleInfo; + ULONG getHandleInfo; + NTSTATUS status; + + PAGED_CODE( ); + + // + // Set up local pointers. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + handleInfo = Irp->AssociatedIrp.SystemBuffer; + + // + // Make sure that the input and output buffers are large enough. + // + + if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof(getHandleInfo) || + IrpSp->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(*handleInfo) ) { + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Determine which handles we need to get. + // + + getHandleInfo = *(PULONG)Irp->AssociatedIrp.SystemBuffer; + + // + // If no handle information or invalid handle information was + // requested, fail. + // + + if ( (getHandleInfo & + ~(AFD_QUERY_ADDRESS_HANDLE | AFD_QUERY_CONNECTION_HANDLE)) != 0 || + getHandleInfo == 0 ) { + return STATUS_INVALID_PARAMETER; + } + + // + // Initialize the output buffer. + // + + handleInfo->TdiAddressHandle = NULL; + handleInfo->TdiConnectionHandle = NULL; + + // + // If the caller requested a TDI address handle and we have an + // address handle for this endpoint, dupe the address handle to the + // user process. + // + + if ( (getHandleInfo & AFD_QUERY_ADDRESS_HANDLE) != 0 && + endpoint->State != AfdEndpointStateOpen && + endpoint->AddressHandle != NULL ) { + + ASSERT( endpoint->AddressFileObject != NULL ); + + status = ObOpenObjectByPointer( + endpoint->AddressFileObject, + OBJ_CASE_INSENSITIVE, + NULL, + GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, + *IoFileObjectType, + KernelMode, + &handleInfo->TdiAddressHandle + ); + if ( !NT_SUCCESS(status) ) { + return status; + } + } + + // + // If the caller requested a TDI connection handle and we have a + // connection handle for this endpoint, dupe the connection handle + // to the user process. + // + + if ( (getHandleInfo & AFD_QUERY_CONNECTION_HANDLE) != 0 && + endpoint->Type == AfdBlockTypeVcConnecting && + endpoint->Common.VcConnecting.Connection != NULL && + endpoint->Common.VcConnecting.Connection->Handle != NULL ) { + + ASSERT( endpoint->Common.VcConnecting.Connection->Type == AfdBlockTypeConnection ); + ASSERT( endpoint->Common.VcConnecting.Connection->FileObject != NULL ); + + status = ObOpenObjectByPointer( + endpoint->Common.VcConnecting.Connection->FileObject, + OBJ_CASE_INSENSITIVE, + NULL, + GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, + *IoFileObjectType, + KernelMode, + &handleInfo->TdiConnectionHandle + ); + if ( !NT_SUCCESS(status) ) { + if ( handleInfo->TdiAddressHandle != NULL ) { + ZwClose( handleInfo->TdiAddressHandle ); + } + return status; + } + } + + Irp->IoStatus.Information = sizeof(*handleInfo); + + return STATUS_SUCCESS; + +} // AfdQueryHandles + + +NTSTATUS +AfdGetInformation ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Gets information in the endpoint. + +Arguments: + + Irp - Pointer to I/O request packet. + + IrpSp - pointer to the IO stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + PAFD_INFORMATION afdInfo; + PVOID additionalInfo; + ULONG additionalInfoLength; + TDI_REQUEST_KERNEL_QUERY_INFORMATION kernelQueryInfo; + TDI_CONNECTION_INFORMATION connectionInfo; + NTSTATUS status; + LONGLONG currentTime; + LONGLONG connectTime; + + PAGED_CODE( ); + + // + // Set up local pointers. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + afdInfo = Irp->AssociatedIrp.SystemBuffer; + + // + // Make sure that the input and output buffers are large enough. + // + + if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof(*afdInfo) || + IrpSp->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(*afdInfo) ) { + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Figure out the additional information, if any. + // + + additionalInfo = afdInfo + 1; + additionalInfoLength = + IrpSp->Parameters.DeviceIoControl.InputBufferLength - sizeof(*afdInfo); + + // + // Set up appropriate information in the endpoint. + // + + switch ( afdInfo->InformationType ) { + + case AFD_MAX_PATH_SEND_SIZE: + + // + // Set up a query to the TDI provider to obtain the largest + // datagram that can be sent to a particular address. + // + + kernelQueryInfo.QueryType = TDI_QUERY_MAX_DATAGRAM_INFO; + kernelQueryInfo.RequestConnectionInformation = &connectionInfo; + + connectionInfo.UserDataLength = 0; + connectionInfo.UserData = NULL; + connectionInfo.OptionsLength = 0; + connectionInfo.Options = NULL; + connectionInfo.RemoteAddressLength = additionalInfoLength; + connectionInfo.RemoteAddress = additionalInfo; + + // + // Ask the TDI provider for the information. + // + + status = AfdIssueDeviceControl( + NULL, + endpoint->AddressFileObject, + &kernelQueryInfo, + sizeof(kernelQueryInfo), + &afdInfo->Information.Ulong, + sizeof(afdInfo->Information.Ulong), + TDI_QUERY_INFORMATION + ); + + // + // If the request succeeds, use this information. Otherwise, + // fall through and use the transport's global information. + // This is done because not all transports support this + // particular TDI request, and for those which do not the + // global information is a reasonable approximation. + // + + if ( NT_SUCCESS(status) ) { + break; + } + + case AFD_MAX_SEND_SIZE: + + // + // Return the MaxSendSize or MaxDatagramSendSize from the + // TDI_PROVIDER_INFO based on whether or not this is a datagram + // endpoint. + // + + if ( IS_DGRAM_ENDPOINT(endpoint) ) { + afdInfo->Information.Ulong = + endpoint->TransportInfo->ProviderInfo.MaxDatagramSize; + } else { + afdInfo->Information.Ulong = + endpoint->TransportInfo->ProviderInfo.MaxSendSize; + } + + break; + + case AFD_SENDS_PENDING: + + // + // If this is an endpoint on a bufferring transport, no sends + // are pending in AFD. If it is on a nonbufferring transport, + // return the count of sends pended in AFD. + // + + if ( endpoint->TdiBufferring || endpoint->Type != AfdBlockTypeVcConnecting ) { + afdInfo->Information.Ulong = 0; + } else { + afdInfo->Information.Ulong = + endpoint->Common.VcConnecting.Connection->VcBufferredSendCount; + } + + break; + + case AFD_RECEIVE_WINDOW_SIZE: + + // + // Return the default receive window. + // + + afdInfo->Information.Ulong = AfdReceiveWindowSize; + break; + + case AFD_SEND_WINDOW_SIZE: + + // + // Return the default send window. + // + + afdInfo->Information.Ulong = AfdSendWindowSize; + break; + + case AFD_CONNECT_TIME: + + // + // If the endpoint is not yet connected, return -1. Otherwise, + // calculate the number of seconds that the connection has been + // active. + // + + if ( endpoint->State != AfdEndpointStateConnected || + endpoint->EndpointType == AfdEndpointTypeDatagram ) { + + afdInfo->Information.Ulong = 0xFFFFFFFF; + + } else { + + connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint ); + ASSERT( connection != NULL ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + // + // Calculate how long the connection has been active by + // subtracting the time at which the connection started from + // the current time. Note that we convert the units of the + // time value from 100s of nanoseconds to seconds. + // + + KeQuerySystemTime( (PLARGE_INTEGER)¤tTime ); + + connectTime = (currentTime - connection->ConnectTime); + connectTime /= 10*1000*1000; + + // + // We can safely convert this to a ULONG because it takes + // 127 years to overflow a ULONG counting seconds. The + // bizarre conversion to a LARGE_INTEGER is required to + // prevent the compiler from optimizing out the full 64-bit + // division above. Without this, the compiler would do only + // a 32-bit division and lose some information. + // + + //afdInfo->Information.Ulong = (ULONG)connectTime; + afdInfo->Information.Ulong = ((PLARGE_INTEGER)&connectTime)->LowPart; + } + + break; + + case AFD_GROUP_ID_AND_TYPE : { + + PAFD_GROUP_INFO groupInfo; + + groupInfo = (PAFD_GROUP_INFO)&afdInfo->Information.LargeInteger; + + // + // Return the endpoint's group ID and group type. + // + + groupInfo->GroupID = endpoint->GroupID; + groupInfo->GroupType = endpoint->GroupType; + + } + break; + + default: + + return STATUS_INVALID_PARAMETER; + } + + Irp->IoStatus.Information = sizeof(*afdInfo); + Irp->IoStatus.Status = STATUS_SUCCESS; + + return STATUS_SUCCESS; + +} // AfdGetInformation + + +NTSTATUS +AfdSetInformation ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Sets information in the endpoint. + +Arguments: + + Irp - Pointer to I/O request packet. + + IrpSp - pointer to the IO stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + PAFD_INFORMATION afdInfo; + NTSTATUS status; + + PAGED_CODE( ); + + // + // Set up local pointers. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + afdInfo = Irp->AssociatedIrp.SystemBuffer; + + // + // Make sure that the input buffer is large enough. + // + + if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*afdInfo) ) { + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Set up appropriate information in the endpoint. + // + + switch ( afdInfo->InformationType ) { + + case AFD_NONBLOCKING_MODE: + + // + // Set the blocking mode of the endpoint. If TRUE, send and receive + // calls on the endpoint will fail if they cannot be completed + // immediately. + // + + endpoint->NonBlocking = afdInfo->Information.Boolean; + break; + + case AFD_CIRCULAR_QUEUEING: + + // + // Enables circular queuing on the endpoint. + // + + if( !IS_DGRAM_ENDPOINT( endpoint ) ) { + + return STATUS_INVALID_PARAMETER; + + } + + endpoint->Common.Datagram.CircularQueueing = afdInfo->Information.Boolean; + break; + + case AFD_INLINE_MODE: + + // + // Set the inline mode of the endpoint. If TRUE, a receive for + // normal data will be completed with either normal data or + // expedited data. If the endpoint is connected, we need to + // tell the TDI provider that the endpoint is inline so that it + // delivers data to us in order. If the endpoint is not yet + // connected, then we will set the inline mode when we create + // the TDI connection object. + // + + if ( endpoint->Type == AfdBlockTypeVcConnecting ) { + status = AfdSetInLineMode( + AFD_CONNECTION_FROM_ENDPOINT( endpoint ), + afdInfo->Information.Boolean + ); + if ( !NT_SUCCESS(status) ) { + return status; + } + } + + endpoint->InLine = afdInfo->Information.Boolean; + break; + + case AFD_RECEIVE_WINDOW_SIZE: + case AFD_SEND_WINDOW_SIZE: { + + LONG newBytes; + PCLONG maxBytes; + CLONG requestedCount; + PCSHORT maxCount; +#ifdef AFDDBG_QUOTA + PVOID chargeBlock; + PSZ chargeType; +#endif + + // + // First determine where the appropriate limits are stored in the + // connection or endpoint. We do this so that we can use common + // code to charge quota and set the new counters. + // + + if ( endpoint->Type == AfdBlockTypeVcConnecting ) { + + connection = endpoint->Common.VcConnecting.Connection; + + if ( afdInfo->InformationType == AFD_SEND_WINDOW_SIZE ) { + maxBytes = &connection->MaxBufferredSendBytes; + maxCount = &connection->MaxBufferredSendCount; + } else { + maxBytes = &connection->MaxBufferredReceiveBytes; + maxCount = &connection->MaxBufferredReceiveCount; + } + +#ifdef AFDDBG_QUOTA + chargeBlock = connection; + chargeType = "SetInfo vcnb"; +#endif + + } else if ( endpoint->Type == AfdBlockTypeDatagram ) { + + if ( afdInfo->InformationType == AFD_SEND_WINDOW_SIZE ) { + maxBytes = &endpoint->Common.Datagram.MaxBufferredSendBytes; + maxCount = &endpoint->Common.Datagram.MaxBufferredSendCount; + } else { + maxBytes = &endpoint->Common.Datagram.MaxBufferredReceiveBytes; + maxCount = &endpoint->Common.Datagram.MaxBufferredReceiveCount; + } + +#ifdef AFDDBG_QUOTA + chargeBlock = endpoint; + chargeType = "SetInfo dgrm"; +#endif + + } else { + + return STATUS_INVALID_PARAMETER; + } + + // + // Make sure that we always allow at least one message to be + // bufferred on an endpoint. + // + + requestedCount = afdInfo->Information.Ulong / AfdBufferMultiplier; + + if ( requestedCount == 0 ) { + + // + // Don't allow the max receive bytes to go to zero, but + // max send bytes IS allowed to go to zero because it has + // special meaning: specifically, do not buffer sends. + // + + if ( afdInfo->InformationType == AFD_RECEIVE_WINDOW_SIZE ) { + afdInfo->Information.Ulong = AfdBufferMultiplier; + requestedCount = 1; + } + } + + // + // If the count will overflow the field we use to set the max + // count, just use the max count as the limit. + // + + if ( requestedCount > 0x7FFF ) { + requestedCount = 0x7FFF; + } + + // + // Charge or return quota to the process making this request. + // + + newBytes = afdInfo->Information.Ulong - (ULONG)(*maxBytes); + + if ( newBytes > 0 ) { + + try { + + PsChargePoolQuota( + endpoint->OwningProcess, + NonPagedPool, + newBytes + ); + + } except ( EXCEPTION_EXECUTE_HANDLER ) { +#if DBG + DbgPrint( "AfdSetInformation: PsChargePoolQuota failed.\n" ); +#endif + return STATUS_QUOTA_EXCEEDED; + } + + AfdRecordQuotaHistory( + endpoint->OwningProcess, + newBytes, + chargeType, + chargeBlock + ); + AfdRecordPoolQuotaCharged( newBytes ); + + } else { + + PsReturnPoolQuota( + endpoint->OwningProcess, + NonPagedPool, + -1 * newBytes + ); + AfdRecordQuotaHistory( + endpoint->OwningProcess, + newBytes, + chargeType, + chargeBlock + ); + AfdRecordPoolQuotaCharged( -1 * newBytes ); + } + + // + // Set up the new information in the AFD internal structure. + // + + *maxBytes = (CLONG)afdInfo->Information.Ulong; + *maxCount = (CSHORT)requestedCount; + + break; + } + + default: + + return STATUS_INVALID_PARAMETER; + } + + return STATUS_SUCCESS; + +} // AfdSetInformation + + +NTSTATUS +AfdSetInLineMode ( + IN PAFD_CONNECTION Connection, + IN BOOLEAN InLine + ) + +/*++ + +Routine Description: + + Sets a connection to be in inline mode. In inline mode, urgent data + is delivered in the order in which it is received. We must tell the + TDI provider about this so that it indicates data in the proper + order. + +Arguments: + + Connection - the AFD connection to set as inline. + + InLine - TRUE to enable inline mode, FALSE to disable inline mode. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully + performed. + +--*/ + +{ + NTSTATUS status; + PTCP_REQUEST_SET_INFORMATION_EX setInfoEx; + PIO_STATUS_BLOCK ioStatusBlock; + HANDLE event; + + PAGED_CODE( ); + + // + // Allocate space to hold the TDI set information buffers and the IO + // status block. + // + + ioStatusBlock = AFD_ALLOCATE_POOL( + NonPagedPool, + sizeof(*ioStatusBlock) + sizeof(*setInfoEx) + + sizeof(TCPSocketOption), + AFD_INLINE_POOL_TAG + ); + + if ( ioStatusBlock == NULL ) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Initialize the TDI information buffers. + // + + setInfoEx = (PTCP_REQUEST_SET_INFORMATION_EX)(ioStatusBlock + 1); + + setInfoEx->ID.toi_entity.tei_entity = CO_TL_ENTITY; + setInfoEx->ID.toi_entity.tei_instance = TL_INSTANCE; + setInfoEx->ID.toi_class = INFO_CLASS_PROTOCOL; + setInfoEx->ID.toi_type = INFO_TYPE_CONNECTION; + setInfoEx->ID.toi_id = TCP_SOCKET_OOBINLINE; + + *(PULONG)setInfoEx->Buffer = (ULONG)InLine; + setInfoEx->BufferSize = sizeof(ULONG); + + KeAttachProcess( AfdSystemProcess ); + + status = ZwCreateEvent( + &event, + EVENT_ALL_ACCESS, + NULL, + SynchronizationEvent, + FALSE + ); + if ( !NT_SUCCESS(status) ) { + KeDetachProcess( ); + AFD_FREE_POOL( + ioStatusBlock, + AFD_INLINE_POOL_TAG + ); + return status; + } + + // + // Make the actual TDI set information call. + // + + status = ZwDeviceIoControlFile( + Connection->Handle, + event, + NULL, + NULL, + ioStatusBlock, + IOCTL_TCP_SET_INFORMATION_EX, + setInfoEx, + sizeof(*setInfoEx) + setInfoEx->BufferSize, + NULL, + 0 + ); + + if ( status == STATUS_PENDING ) { + status = ZwWaitForSingleObject( event, FALSE, NULL ); + ASSERT( NT_SUCCESS(status) ); + status = ioStatusBlock->Status; + } + + ZwClose( event ); + + KeDetachProcess( ); + AFD_FREE_POOL( + ioStatusBlock, + AFD_INLINE_POOL_TAG + ); + + // + // Since this option is only supported for TCP/IP, always return success. + // + + return STATUS_SUCCESS; + +} // AfdSetInLineMode + + +NTSTATUS +AfdGetContext ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) +{ + PAFD_ENDPOINT endpoint; + + PAGED_CODE( ); + + // + // Set up local pointers. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + // + // Make sure that the output buffer is large enough to hold all the + // context information for this socket. + // + + if ( IrpSp->Parameters.DeviceIoControl.OutputBufferLength < + endpoint->ContextLength ) { + return STATUS_BUFFER_TOO_SMALL; + } + + // + // If there is no context, return nothing. + // + + if ( endpoint->Context == NULL ) { + Irp->IoStatus.Information = 0; + return STATUS_SUCCESS; + } + + // + // Return the context information we have stored for this endpoint. + // + + RtlCopyMemory( + Irp->AssociatedIrp.SystemBuffer, + endpoint->Context, + endpoint->ContextLength + ); + + Irp->IoStatus.Information = endpoint->ContextLength; + + return STATUS_SUCCESS; + +} // AfdGetContext + + +NTSTATUS +AfdGetContextLength ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) +{ + PAFD_ENDPOINT endpoint; + + PAGED_CODE( ); + + // + // Set up local pointers. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + // + // Make sure that the output buffer is large enough to hold the + // context buffer length. + // + + if ( IrpSp->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(endpoint->ContextLength) ) { + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Return the length of the context information we have stored for + // this endpoint. + // + + *(PULONG)Irp->AssociatedIrp.SystemBuffer = endpoint->ContextLength; + + Irp->IoStatus.Information = sizeof(endpoint->ContextLength); + + return STATUS_SUCCESS; + +} // AfdGetContextLength + + +NTSTATUS +AfdSetContext ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) +{ + PAFD_ENDPOINT endpoint; + ULONG newContextLength; + + PAGED_CODE( ); + + // + // Set up local pointers. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + newContextLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; + + // + // If there is no context buffer on the endpoint, or if the context + // buffer is too small, allocate a new context buffer from paged pool. + // + + if ( endpoint->Context == NULL || + endpoint->ContextLength < newContextLength ) { + + PVOID newContext; + + // + // Allocate a new context buffer. + // + + try { + newContext = AFD_ALLOCATE_POOL_WITH_QUOTA( + PagedPool, + newContextLength, + AFD_CONTEXT_POOL_TAG + ); + + } except( EXCEPTION_EXECUTE_HANDLER ) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + if ( newContext == NULL ) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Free the old context buffer, if there was one. + // + + if ( endpoint->Context != NULL ) { + + AFD_FREE_POOL( + endpoint->Context, + AFD_CONTEXT_POOL_TAG + ); + + } + + endpoint->Context = newContext; + } + + // + // Store the passed-in context buffer. + // + + endpoint->ContextLength = newContextLength; + + RtlCopyMemory( + endpoint->Context, + Irp->AssociatedIrp.SystemBuffer, + newContextLength + ); + + Irp->IoStatus.Information = 0; + + return STATUS_SUCCESS; + +} // AfdSetContext + + +NTSTATUS +AfdSetEventHandler ( + IN PFILE_OBJECT FileObject, + IN ULONG EventType, + IN PVOID EventHandler, + IN PVOID EventContext + ) + +/*++ + +Routine Description: + + Sets up a TDI indication handler on a connection or address object + (depending on the file handle). This is done synchronously, which + shouldn't usually be an issue since TDI providers can usually complete + indication handler setups immediately. + +Arguments: + + FileObject - a pointer to the file object for an open connection or + address object. + + EventType - the event for which the indication handler should be + called. + + EventHandler - the routine to call when tghe specified event occurs. + + EventContext - context which is passed to the indication routine. + +Return Value: + + NTSTATUS -- Indicates the status of the request. + +--*/ + +{ + TDI_REQUEST_KERNEL_SET_EVENT parameters; + + PAGED_CODE( ); + + parameters.EventType = EventType; + parameters.EventHandler = EventHandler; + parameters.EventContext = EventContext; + + return AfdIssueDeviceControl( + NULL, + FileObject, + ¶meters, + sizeof(parameters), + NULL, + 0, + TDI_SET_EVENT_HANDLER + ); + +} // AfdSetEventHandler + + +NTSTATUS +AfdIssueDeviceControl ( + IN HANDLE FileHandle OPTIONAL, + IN PFILE_OBJECT FileObject OPTIONAL, + IN PVOID IrpParameters, + IN ULONG IrpParametersLength, + IN PVOID MdlBuffer, + IN ULONG MdlBufferLength, + IN UCHAR MinorFunction + ) + +/*++ + +Routine Description: + + Issues a device control returst to a TDI provider and waits for the + request to complete. + + Note that while FileHandle and FileObject are both marked as optional, + in reality exactly one of these must be specified. + +Arguments: + + FileHandle - a TDI handle. + + FileObject - a pointer to the file object corresponding to a TDI + handle + + IrpParameters - information to write to the parameters section of the + stack location of the IRP. + + IrpParametersLength - length of the parameter information. Cannot be + greater than 16. + + MdlBuffer - if non-NULL, a buffer of nonpaged pool to be mapped + into an MDL and placed in the MdlAddress field of the IRP. + + MdlBufferLength - the size of the buffer pointed to by MdlBuffer. + + MinorFunction - the minor function code for the request. + +Return Value: + + NTSTATUS -- Indicates the status of the request. + +--*/ + +{ + NTSTATUS status; + PFILE_OBJECT fileObject; + PIRP irp; + PIO_STACK_LOCATION irpSp; + KEVENT event; + IO_STATUS_BLOCK ioStatusBlock; + PDEVICE_OBJECT deviceObject; + PMDL mdl; + + PAGED_CODE( ); + + // + // Initialize the kernel event that will signal I/O completion. + // + + KeInitializeEvent( &event, SynchronizationEvent, FALSE ); + + if( FileHandle != NULL ) { + + ASSERT( FileObject == NULL ); + + // + // Get the file object corresponding to the directory's handle. + // Referencing the file object every time is necessary because the + // IO completion routine dereferences it. + // + + status = ObReferenceObjectByHandle( + FileHandle, + 0L, // DesiredAccess + NULL, // ObjectType + KernelMode, + (PVOID *)&fileObject, + NULL + ); + if ( !NT_SUCCESS(status) ) { + return status; + } + + } else { + + ASSERT( FileObject != NULL ); + + // + // Reference the passed in file object. This is necessary because + // the IO completion routine dereferences it. + // + + ObReferenceObject( FileObject ); + + fileObject = FileObject; + + } + + // + // Set the file object event to a non-signaled state. + // + + (VOID) KeResetEvent( &fileObject->Event ); + + // + // Attempt to allocate and initialize the I/O Request Packet (IRP) + // for this operation. + // + + deviceObject = IoGetRelatedDeviceObject ( fileObject ); + + irp = IoAllocateIrp( (deviceObject)->StackSize, TRUE ); + if ( irp == NULL ) { + ObDereferenceObject( fileObject ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Fill in the service independent parameters in the IRP. + // + + irp->Flags = (LONG)IRP_SYNCHRONOUS_API; + irp->RequestorMode = KernelMode; + irp->PendingReturned = FALSE; + + irp->UserIosb = &ioStatusBlock; + irp->UserEvent = &event; + + 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; + + DEBUG ioStatusBlock.Status = STATUS_UNSUCCESSFUL; + DEBUG ioStatusBlock.Information = (ULONG)-1; + + // + // If an MDL buffer was specified, get an MDL, map the buffer, + // and place the MDL pointer in the IRP. + // + + if ( MdlBuffer != NULL ) { + + mdl = IoAllocateMdl( + MdlBuffer, + MdlBufferLength, + FALSE, + FALSE, + irp + ); + if ( mdl == NULL ) { + IoFreeIrp( irp ); + ObDereferenceObject( fileObject ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + MmBuildMdlForNonPagedPool( mdl ); + + } else { + + irp->MdlAddress = NULL; + } + + // + // Put the file object pointer in the stack location. + // + + irpSp = IoGetNextIrpStackLocation( irp ); + irpSp->FileObject = fileObject; + irpSp->DeviceObject = deviceObject; + + // + // Fill in the service-dependent parameters for the request. + // + + ASSERT( IrpParametersLength <= sizeof(irpSp->Parameters) ); + RtlCopyMemory( &irpSp->Parameters, IrpParameters, IrpParametersLength ); + + irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + irpSp->MinorFunction = MinorFunction; + + // + // Set up a completion routine which we'll use to free the MDL + // allocated previously. + // + + IoSetCompletionRoutine( irp, AfdRestartDeviceControl, NULL, TRUE, TRUE, TRUE ); + + // + // Queue the IRP to the thread and pass it to the driver. + // + + IoEnqueueIrp( irp ); + + status = IoCallDriver( deviceObject, irp ); + + // + // If necessary, wait for the I/O to complete. + // + + if ( status == STATUS_PENDING ) { + KeWaitForSingleObject( (PVOID)&event, UserRequest, KernelMode, FALSE, NULL ); + } + + // + // If the request was successfully queued, get the final I/O status. + // + + if ( NT_SUCCESS(status) ) { + status = ioStatusBlock.Status; + } + + return status; + +} // AfdIssueDeviceControl + + +NTSTATUS +AfdRestartDeviceControl ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + // + // N.B. This routine can never be demand paged because it can be + // called before any endpoints have been placed on the global + // list--see AfdAllocateEndpoint() and it's call to + // AfdGetTransportInfo(). + // + + // + // If there was an MDL in the IRP, free it and reset the pointer to + // NULL. The IO system can't handle a nonpaged pool MDL being freed + // in an IRP, which is why we do it here. + // + + if ( Irp->MdlAddress != NULL ) { + IoFreeMdl( Irp->MdlAddress ); + Irp->MdlAddress = NULL; + } + + return STATUS_SUCCESS; + +} // AfdRestartDeviceControl + + +NTSTATUS +AfdGetConnectData ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp, + IN ULONG Code + ) +{ + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + PAFD_CONNECT_DATA_BUFFERS connectDataBuffers; + PAFD_CONNECT_DATA_INFO connectDataInfo; + KIRQL oldIrql; + + // + // Set up local pointers. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint ); + ASSERT( connection == NULL || connection->Type == AfdBlockTypeConnection ); + + // + // If there is a connection on this endpoint, use the data buffers + // on the connection. Otherwise, use the data buffers from the + // endpoint. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + if ( connection != NULL ) { + connectDataBuffers = connection->ConnectDataBuffers; + } else { + connectDataBuffers = endpoint->ConnectDataBuffers; + } + + // + // If there are no connect data buffers on the endpoint, complete + // the IRP with no bytes. + // + + if ( connectDataBuffers == NULL ) { + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + Irp->IoStatus.Information = 0; + return STATUS_SUCCESS; + } + + // + // Determine what sort of data we're handling and where it should + // come from. + // + + switch ( Code ) { + + case IOCTL_AFD_GET_CONNECT_DATA: + connectDataInfo = &connectDataBuffers->ReceiveConnectData; + break; + + case IOCTL_AFD_GET_CONNECT_OPTIONS: + connectDataInfo = &connectDataBuffers->ReceiveConnectOptions; + break; + + case IOCTL_AFD_GET_DISCONNECT_DATA: + connectDataInfo = &connectDataBuffers->ReceiveDisconnectData; + break; + + case IOCTL_AFD_GET_DISCONNECT_OPTIONS: + connectDataInfo = &connectDataBuffers->ReceiveDisconnectOptions; + break; + + default: + ASSERT(FALSE); + } + + // + // If there is none of the requested data type, again complete + // the IRP with no bytes. + // + + if ( connectDataInfo->Buffer == NULL || + connectDataInfo->BufferLength == 0 ) { + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + Irp->IoStatus.Information = 0; + return STATUS_SUCCESS; + } + + // + // If the output buffer is too small, fail. + // + + if ( IrpSp->Parameters.DeviceIoControl.OutputBufferLength < + connectDataInfo->BufferLength ) { + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Copy over the buffer and return the number of bytes copied. + // + + RtlCopyMemory( + Irp->AssociatedIrp.SystemBuffer, + connectDataInfo->Buffer, + connectDataInfo->BufferLength + ); + + Irp->IoStatus.Information = connectDataInfo->BufferLength; + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + return STATUS_SUCCESS; + +} // AfdGetConnectData + + +NTSTATUS +AfdSetConnectData ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp, + IN ULONG Code + ) +{ + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + PAFD_CONNECT_DATA_BUFFERS connectDataBuffers; + PAFD_CONNECT_DATA_BUFFERS * connectDataBuffersTarget; + PAFD_CONNECT_DATA_INFO connectDataInfo; + KIRQL oldIrql; + ULONG bufferLength; + BOOLEAN size = FALSE; + + // + // Set up local pointers. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint ); + ASSERT( connection == NULL || connection->Type == AfdBlockTypeConnection ); + + // + // If there is a connection on this endpoint, use the data buffers + // on the connection. Otherwise, use the data buffers from the + // endpoint. Also, if there is no connect data buffer structure + // yet, allocate one. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + if( connection != NULL ) { + + connectDataBuffersTarget = &connection->ConnectDataBuffers; + + } else { + + connectDataBuffersTarget = &endpoint->ConnectDataBuffers; + + } + + connectDataBuffers = *connectDataBuffersTarget; + + if( connectDataBuffers == NULL ) { + + try { + + connectDataBuffers = AFD_ALLOCATE_POOL_WITH_QUOTA( + NonPagedPool, + sizeof(*connectDataBuffers), + AFD_CONNECT_DATA_POOL_TAG + ); + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + return STATUS_INSUFFICIENT_RESOURCES; + + } + + if( connectDataBuffers == NULL ) { + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + return STATUS_INSUFFICIENT_RESOURCES; + + } + + RtlZeroMemory( + connectDataBuffers, + sizeof(*connectDataBuffers) + ); + + *connectDataBuffersTarget = connectDataBuffers; + + } + + // + // If there is a connect outstanding on this endpoint or if it + // has already been shut down, fail this request. This prevents + // the connect code from accessing buffers which may be freed soon. + // + + if( endpoint->ConnectOutstanding || + (endpoint->DisconnectMode & ~AFD_PARTIAL_DISCONNECT_RECEIVE) != 0 ) { + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + return STATUS_INVALID_PARAMETER; + } + + // + // Determine what sort of data we're handling and where it should + // go. + // + + switch( Code ) { + + case IOCTL_AFD_SET_CONNECT_DATA: + connectDataInfo = &connectDataBuffers->SendConnectData; + break; + + case IOCTL_AFD_SET_CONNECT_OPTIONS: + connectDataInfo = &connectDataBuffers->SendConnectOptions; + break; + + case IOCTL_AFD_SET_DISCONNECT_DATA: + connectDataInfo = &connectDataBuffers->SendDisconnectData; + break; + + case IOCTL_AFD_SET_DISCONNECT_OPTIONS: + connectDataInfo = &connectDataBuffers->SendDisconnectOptions; + break; + + case IOCTL_AFD_SIZE_CONNECT_DATA: + connectDataInfo = &connectDataBuffers->ReceiveConnectData; + size = TRUE; + break; + + case IOCTL_AFD_SIZE_CONNECT_OPTIONS: + connectDataInfo = &connectDataBuffers->ReceiveConnectOptions; + size = TRUE; + break; + + case IOCTL_AFD_SIZE_DISCONNECT_DATA: + connectDataInfo = &connectDataBuffers->ReceiveDisconnectData; + size = TRUE; + break; + + case IOCTL_AFD_SIZE_DISCONNECT_OPTIONS: + connectDataInfo = &connectDataBuffers->ReceiveDisconnectOptions; + size = TRUE; + break; + + default: + ASSERT(FALSE); + } + + // + // Determine the buffer size based on whether we're setting a buffer + // into which data will be received, in which case the size is + // in the four bytes of input buffer, or setting a buffer which we're + // going to send, in which case the size is the length of the input + // buffer. + // + + if( size ) { + + if( IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG) ) { + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + return STATUS_INVALID_PARAMETER; + + } + + bufferLength = *(PULONG)Irp->AssociatedIrp.SystemBuffer; + + } else { + + bufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; + } + + // + // If there's not currently a buffer of the requested type, or there is + // such a buffer and it's smaller than the requested size, free it + // and allocate a new one. + // + + if( connectDataInfo->Buffer == NULL || + connectDataInfo->BufferLength < bufferLength ) { + + if( connectDataInfo->Buffer != NULL ) { + + AFD_FREE_POOL( + connectDataInfo->Buffer, + AFD_CONNECT_DATA_POOL_TAG + ); + + } + + connectDataInfo->Buffer = NULL; + connectDataInfo->BufferLength = 0; + + try { + + connectDataInfo->Buffer = AFD_ALLOCATE_POOL_WITH_QUOTA( + NonPagedPool, + bufferLength, + AFD_CONNECT_DATA_POOL_TAG + ); + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + return STATUS_INSUFFICIENT_RESOURCES; + + } + + if ( connectDataInfo->Buffer == NULL ) { + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + return STATUS_INSUFFICIENT_RESOURCES; + + } + + } + + // + // If this wasn't simply a "size" request, copy the data into the buffer. + // + + if( !size ) { + + RtlCopyMemory( + connectDataInfo->Buffer, + Irp->AssociatedIrp.SystemBuffer, + bufferLength + ); + + } + + connectDataInfo->BufferLength = bufferLength; + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + Irp->IoStatus.Information = 0; + + return STATUS_SUCCESS; + +} // AfdSetConnectData + + +NTSTATUS +AfdSaveReceivedConnectData ( + IN OUT PAFD_CONNECT_DATA_BUFFERS * DataBuffers, + IN ULONG IoControlCode, + IN PVOID Buffer, + IN ULONG BufferLength + ) + +/*++ + +Routine Description: + + This helper routine stores the specified *received* connect/disconnect + data/options on the specified endpoint/connection. + + N.B. This routine MUST be called with AfdSpinLock held! + + N.B. Unlike AfdSetConnectData(), this routine cannot allocate the + AFD_CONNECT_DATA_BUFFERS structure with quota, as it may be + called from AfdDisconnectEventHandler() in an unknown thread + context. + +Arguments: + + DataBuffers -Points to a pointer to the connect data buffers structure. + If the value pointed to by DataBuffers is NULL, then a new structure + is allocated, otherwise the existing structure is used. + + IoControlCode - Specifies the type of data to save. + + Buffer - Points to the buffer containing the data. + + BufferLength - The length of Buffer. + +Return Value: + + NTSTATUS - The completion status. + +--*/ + +{ + PAFD_CONNECT_DATA_BUFFERS connectDataBuffers; + PAFD_CONNECT_DATA_INFO connectDataInfo; + + ASSERT( KeGetCurrentIrql() >= DISPATCH_LEVEL ); + + // + // If there's no connect data buffer structure, allocate one now. + // + + connectDataBuffers = *DataBuffers; + + if( connectDataBuffers == NULL ) { + + connectDataBuffers = AFD_ALLOCATE_POOL( + NonPagedPool, + sizeof(*connectDataBuffers), + AFD_CONNECT_DATA_POOL_TAG + ); + + if( connectDataBuffers == NULL ) { + + return STATUS_INSUFFICIENT_RESOURCES; + + } + + RtlZeroMemory( + connectDataBuffers, + sizeof(*connectDataBuffers) + ); + + *DataBuffers = connectDataBuffers; + + } + + // + // Determine what sort of data we're handling and where it should + // go. + // + + switch( IoControlCode ) { + + case IOCTL_AFD_SET_CONNECT_DATA: + connectDataInfo = &connectDataBuffers->ReceiveConnectData; + break; + + case IOCTL_AFD_SET_CONNECT_OPTIONS: + connectDataInfo = &connectDataBuffers->ReceiveConnectOptions; + break; + + case IOCTL_AFD_SET_DISCONNECT_DATA: + connectDataInfo = &connectDataBuffers->ReceiveDisconnectData; + break; + + case IOCTL_AFD_SET_DISCONNECT_OPTIONS: + connectDataInfo = &connectDataBuffers->ReceiveDisconnectOptions; + break; + + default: + ASSERT(FALSE); + } + + // + // If there was previously a buffer of the requested type, free it. + // + + if( connectDataInfo->Buffer != NULL ) { + + AFD_FREE_POOL( + connectDataInfo->Buffer, + AFD_CONNECT_DATA_POOL_TAG + ); + + connectDataInfo->Buffer = NULL; + + } + + // + // Allocate a new buffer for the data and copy in the data we're to + // send. + // + + connectDataInfo->Buffer = AFD_ALLOCATE_POOL( + NonPagedPool, + BufferLength, + AFD_CONNECT_DATA_POOL_TAG + ); + + if( connectDataInfo->Buffer == NULL ) { + + return STATUS_INSUFFICIENT_RESOURCES; + + } + + RtlCopyMemory( + connectDataInfo->Buffer, + Buffer, + BufferLength + ); + + connectDataInfo->BufferLength = BufferLength; + return STATUS_SUCCESS; + +} // AfdSaveReceivedConnectData + + +VOID +AfdFreeConnectDataBuffers ( + IN PAFD_CONNECT_DATA_BUFFERS ConnectDataBuffers + ) +{ + if ( ConnectDataBuffers->SendConnectData.Buffer != NULL ) { + AFD_FREE_POOL( + ConnectDataBuffers->SendConnectData.Buffer, + AFD_CONNECT_DATA_POOL_TAG + ); + } + + if ( ConnectDataBuffers->ReceiveConnectData.Buffer != NULL ) { + AFD_FREE_POOL( + ConnectDataBuffers->ReceiveConnectData.Buffer, + AFD_CONNECT_DATA_POOL_TAG + ); + } + + if ( ConnectDataBuffers->SendConnectOptions.Buffer != NULL ) { + AFD_FREE_POOL( + ConnectDataBuffers->SendConnectOptions.Buffer, + AFD_CONNECT_DATA_POOL_TAG + ); + } + + if ( ConnectDataBuffers->ReceiveConnectOptions.Buffer != NULL ) { + AFD_FREE_POOL( + ConnectDataBuffers->ReceiveConnectOptions.Buffer, + AFD_CONNECT_DATA_POOL_TAG + ); + } + + if ( ConnectDataBuffers->SendDisconnectData.Buffer != NULL ) { + AFD_FREE_POOL( + ConnectDataBuffers->SendDisconnectData.Buffer, + AFD_CONNECT_DATA_POOL_TAG + ); + } + + if ( ConnectDataBuffers->ReceiveDisconnectData.Buffer != NULL ) { + AFD_FREE_POOL( + ConnectDataBuffers->ReceiveDisconnectData.Buffer, + AFD_CONNECT_DATA_POOL_TAG + ); + } + + if ( ConnectDataBuffers->SendDisconnectOptions.Buffer != NULL ) { + AFD_FREE_POOL( + ConnectDataBuffers->SendDisconnectOptions.Buffer, + AFD_CONNECT_DATA_POOL_TAG + ); + } + + if ( ConnectDataBuffers->ReceiveDisconnectOptions.Buffer != NULL ) { + AFD_FREE_POOL( + ConnectDataBuffers->ReceiveDisconnectOptions.Buffer, + AFD_CONNECT_DATA_POOL_TAG + ); + } + + AFD_FREE_POOL( + ConnectDataBuffers, + AFD_CONNECT_DATA_POOL_TAG + ); + + return; + +} // AfdFreeConnectDataBuffers + + +PAFD_WORK_ITEM +AfdAllocateWorkItem( + VOID + ) +{ + + PAFD_WORK_ITEM afdWorkItem; + + afdWorkItem = ExAllocateFromNPagedLookasideList( + &AfdLookasideLists->WorkQueueList + ); + + ASSERT( afdWorkItem != NULL ); + + return afdWorkItem; + +} // AfdAllocateWorkItem + + +VOID +AfdQueueWorkItem ( + IN PWORKER_THREAD_ROUTINE AfdWorkerRoutine, + IN PAFD_WORK_ITEM AfdWorkItem + ) +{ + KIRQL oldIrql; + + ASSERT( AfdWorkerRoutine != NULL ); + ASSERT( AfdWorkItem != NULL ); + + AfdWorkItem->AfdWorkerRoutine = AfdWorkerRoutine; + + // + // Insert the work item at the tail of AFD's list of work itrems. + // + + AfdAcquireSpinLock( &AfdWorkQueueSpinLock, &oldIrql ); + + InsertTailList( &AfdWorkQueueListHead, &AfdWorkItem->WorkItemListEntry ); + + AfdRecordAfdWorkItemsQueued(); + + // + // If there is no executive worker thread working on AFD work, fire + // off an executive worker thread to start servicing the list. + // + + if ( !AfdWorkThreadRunning ) { + + // + // Remember that the work thread is running and release the + // lock. Note that we must release the lock before queuing the + // work because the worker thread may unlock AFD and we can't + // hold a lock when AFD is unlocked. + // + + AfdRecordExWorkItemsQueued(); + + AfdWorkThreadRunning = TRUE; + AfdReleaseSpinLock( &AfdWorkQueueSpinLock, oldIrql ); + + ExInitializeWorkItem( &AfdWorkQueueItem, AfdDoWork, NULL ); + ExQueueWorkItem( &AfdWorkQueueItem, DelayedWorkQueue ); + + } else { + + AfdReleaseSpinLock( &AfdWorkQueueSpinLock, oldIrql ); + } + + return; + +} // AfdQueueWorkItem + + +VOID +AfdFreeWorkItem( + IN PAFD_WORK_ITEM AfdWorkItem + ) +{ + + ExFreeToNPagedLookasideList( + &AfdLookasideLists->WorkQueueList, + AfdWorkItem + ); + +} // AfdFreeWorkItem + + +#if DBG +PVOID +NTAPI +AfdAllocateWorkItemPool( + IN POOL_TYPE PoolType, + IN ULONG NumberOfBytes, + IN ULONG Tag + ) +{ + + ASSERT( Tag == AFD_WORK_ITEM_POOL_TAG ); + + return AFD_ALLOCATE_POOL( + PoolType, + NumberOfBytes, + Tag + ); + +} + +VOID +NTAPI +AfdFreeWorkItemPool( + IN PVOID Block + ) +{ + + AFD_FREE_POOL( + Block, + AFD_WORK_ITEM_POOL_TAG + ); + +} +#endif + + +VOID +AfdDoWork ( + IN PVOID Context + ) +{ + PAFD_WORK_ITEM afdWorkItem; + PLIST_ENTRY listEntry; + KIRQL oldIrql; + PWORKER_THREAD_ROUTINE workerRoutine; + + ASSERT( AfdWorkThreadRunning ); + + // + // Empty the queue of AFD work items. + // + + AfdAcquireSpinLock( &AfdWorkQueueSpinLock, &oldIrql ); + + AfdRecordWorkerEnter(); + AfdRecordAfdWorkerThread( PsGetCurrentThread() ); + + while ( !IsListEmpty( &AfdWorkQueueListHead ) ) { + + // + // Take the first item from the queue and find the address + // of the AFD work item structure. + // + + listEntry = RemoveHeadList( &AfdWorkQueueListHead ); + afdWorkItem = CONTAINING_RECORD( + listEntry, + AFD_WORK_ITEM, + WorkItemListEntry + ); + + AfdRecordAfdWorkItemsProcessed(); + + // + // Capture the worker thread routine from the item. + // + + workerRoutine = afdWorkItem->AfdWorkerRoutine; + + // + // If this work item is going to unlock AFD, then remember that + // the worker thread is no longer running. This closes the + // window where AFD gets unloaded at the same time as new work + // comes in and gets put on the work queue. Note that we + // must reset this boolean BEFORE releasing the spin lock. + // + + if( workerRoutine == AfdUnlockDriver ) { + + AfdWorkThreadRunning = FALSE; + + AfdRecordAfdWorkerThread( NULL ); + AfdRecordWorkerLeave(); + + } + + // + // Release the lock and then call the AFD worker routine. + // + + AfdReleaseSpinLock( &AfdWorkQueueSpinLock, oldIrql ); + + workerRoutine( afdWorkItem ); + + // + // If the purpose of this work item was to unload AFD, then + // we know that there is no more work to do and we CANNOT + // acquire a spin lock. Quit servicing the list and return. + // + + if( workerRoutine == AfdUnlockDriver ) { + return; + } + + // + // Reacquire the spin lock and continue servicing the list. + // + + AfdAcquireSpinLock( &AfdWorkQueueSpinLock, &oldIrql ); + } + + // + // Remember that we're no longer servicing the list and release the + // spin lock. + // + + AfdRecordAfdWorkerThread( NULL ); + AfdRecordWorkerLeave(); + + AfdWorkThreadRunning = FALSE; + AfdReleaseSpinLock( &AfdWorkQueueSpinLock, oldIrql ); + +} // AfdDoWork + +#if DBG + +typedef struct _AFD_OUTSTANDING_IRP { + LIST_ENTRY OutstandingIrpListEntry; + PIRP OutstandingIrp; + PCHAR FileName; + ULONG LineNumber; +} AFD_OUTSTANDING_IRP, *PAFD_OUTSTANDING_IRP; + + +NTSTATUS +AfdIoCallDriverDebug ( + IN PAFD_ENDPOINT Endpoint, + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PCHAR FileName, + IN ULONG LineNumber + ) +{ + PAFD_OUTSTANDING_IRP outstandingIrp; + KIRQL oldIrql; + + // + // Get an outstanding IRP structure to hold the IRP. + // + + outstandingIrp = AFD_ALLOCATE_POOL( + NonPagedPool, + sizeof(AFD_OUTSTANDING_IRP), + AFD_DEBUG_POOL_TAG + ); + + if ( outstandingIrp == NULL ) { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + IoSetNextIrpStackLocation( Irp ); + IoCompleteRequest( Irp, AfdPriorityBoost ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Initialize the structure and place it on the endpoint's list of + // outstanding IRPs. + // + + outstandingIrp->OutstandingIrp = Irp; + outstandingIrp->FileName = FileName; + outstandingIrp->LineNumber = LineNumber; + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + InsertTailList( + &Endpoint->OutstandingIrpListHead, + &outstandingIrp->OutstandingIrpListEntry + ); + Endpoint->OutstandingIrpCount++; + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + // + // Pass the IRP to the TDI provider. + // + + return IoCallDriver( DeviceObject, Irp ); + +} // AfdIoCallDriverDebug + + +VOID +AfdCompleteOutstandingIrpDebug ( + IN PAFD_ENDPOINT Endpoint, + IN PIRP Irp + ) +{ + PAFD_OUTSTANDING_IRP outstandingIrp; + KIRQL oldIrql; + PLIST_ENTRY listEntry; + + // + // First find the IRP on the endpoint's list of outstanding IRPs. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + for ( listEntry = Endpoint->OutstandingIrpListHead.Flink; + listEntry != &Endpoint->OutstandingIrpListHead; + listEntry = listEntry->Flink ) { + + outstandingIrp = CONTAINING_RECORD( + listEntry, + AFD_OUTSTANDING_IRP, + OutstandingIrpListEntry + ); + if ( outstandingIrp->OutstandingIrp == Irp ) { + RemoveEntryList( listEntry ); + ASSERT( Endpoint->OutstandingIrpCount != 0 ); + Endpoint->OutstandingIrpCount--; + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + AFD_FREE_POOL( + outstandingIrp, + AFD_DEBUG_POOL_TAG + ); + return; + } + } + + // + // The corresponding outstanding IRP structure was not found. This + // should never happen unless an allocate for an outstanding IRP + // structure failed above. + // + + KdPrint(( "AfdCompleteOutstandingIrp: Irp %lx not found on endpoint %lx\n", + Irp, Endpoint )); + + ASSERT( Endpoint->OutstandingIrpCount != 0 ); + + Endpoint->OutstandingIrpCount--; + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + return; + +} // AfdCompleteOutstandingIrpDebug + +#else + + +NTSTATUS +AfdIoCallDriverFree ( + IN PAFD_ENDPOINT Endpoint, + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +{ + // + // Increment the count of IRPs outstanding on the endpoint. This + // allows the cleanup code to abort the VC if there is outstanding + // IO when a cleanup occurs. + // + + InterlockedIncrement( + &Endpoint->OutstandingIrpCount + ); + + // + // Pass the IRP to the TDI provider. + // + + return IoCallDriver( DeviceObject, Irp ); + +} // AfdIoCallDriverFree + + +VOID +AfdCompleteOutstandingIrpFree ( + IN PAFD_ENDPOINT Endpoint, + IN PIRP Irp + ) +{ + // + // Decrement the count of IRPs on the endpoint. + // + + InterlockedDecrement( + &Endpoint->OutstandingIrpCount + ); + + return; + +} // AfdCompleteOutstandingIrpFree + +#endif + + +#if DBG || REFERENCE_DEBUG + +VOID +AfdInitializeDebugData ( + VOID + ) +{ + // + // Empty for now. + // + +} // AfdInitializeDebugData + +#endif + +#if DBG + +#undef ExAllocatePool +#undef ExFreePool + +ULONG AfdTotalAllocations = 0; +ULONG AfdTotalFrees = 0; +LARGE_INTEGER AfdTotalBytesAllocated; +LARGE_INTEGER AfdTotalBytesFreed; + +// +// N.B. This structure MUST be quadword aligned! +// + +typedef struct _AFD_POOL_HEADER { + PCHAR FileName; + ULONG LineNumber; + ULONG Size; + ULONG InUse; +#if !FREE_POOL_WITH_TAG_SUPPORTED + LONG Tag; + LONG Dummy; // for proper alignment +#endif +} AFD_POOL_HEADER, *PAFD_POOL_HEADER; + + +PVOID +AfdAllocatePool ( + IN POOL_TYPE PoolType, + IN ULONG NumberOfBytes, + IN ULONG Tag, + IN PCHAR FileName, + IN ULONG LineNumber, + IN BOOLEAN WithQuota + ) +{ + + PAFD_POOL_HEADER header; + KIRQL oldIrql; + + ASSERT( PoolType == NonPagedPool || + PoolType == NonPagedPoolMustSucceed || + PoolType == PagedPool ); + + if ( WithQuota ) { + try { + header = ExAllocatePoolWithQuotaTag( + PoolType, + NumberOfBytes + sizeof(*header), + Tag + ); + } except( EXCEPTION_EXECUTE_HANDLER ) { + return NULL; + } + } else { + header = ExAllocatePoolWithTag( + PoolType, + NumberOfBytes + sizeof(*header), + Tag + ); + } + + if ( header == NULL ) { + return NULL; + } + + header->FileName = FileName; + header->LineNumber = LineNumber; + header->Size = NumberOfBytes; + header->InUse = 1; +#if !FREE_POOL_WITH_TAG_SUPPORTED + header->Tag = (LONG)Tag; +#endif + + InterlockedIncrement( + &AfdTotalAllocations + ); + + ExInterlockedAddLargeStatistic( + &AfdTotalBytesAllocated, + header->Size + ); + + return (PVOID)(header + 1); + +} // AfdAllocatePool + + +VOID +AfdFreePool ( + IN PVOID Pointer, + IN ULONG Tag + ) +{ + + KIRQL oldIrql; + PAFD_POOL_HEADER header = (PAFD_POOL_HEADER)Pointer - 1; + + InterlockedIncrement( + &AfdTotalFrees + ); + + ExInterlockedAddLargeStatistic( + &AfdTotalBytesFreed, + header->Size + ); + + header->InUse = 0; + +#if !FREE_POOL_WITH_TAG_SUPPORTED + ASSERT( InterlockedExchange( &header->Tag, 0 ) == (LONG)Tag ); +#endif + + MyFreePoolWithTag( + (PVOID)header, + Tag + ); + +} // AfdFreePool + +#ifdef AFDDBG_QUOTA +typedef struct { + union { + ULONG Bytes; + struct { + UCHAR Reserved[3]; + UCHAR Sign; + } ; + } ; + UCHAR Location[12]; + PVOID Block; + PVOID Process; + PVOID Reserved2[2]; +} QUOTA_HISTORY, *PQUOTA_HISTORY; +#define QUOTA_HISTORY_LENGTH 512 +QUOTA_HISTORY AfdQuotaHistory[QUOTA_HISTORY_LENGTH]; +LONG AfdQuotaHistoryIndex = 0; + +VOID +AfdRecordQuotaHistory( + IN PEPROCESS Process, + IN LONG Bytes, + IN PSZ Type, + IN PVOID Block + ) +{ + + KIRQL oldIrql; + LONG index; + PQUOTA_HISTORY history; + + index = InterlockedIncrement( &AfdQuotaHistoryIndex ); + index &= QUOTA_HISTORY_LENGTH - 1; + history = &AfdQuotaHistory[index]; + + history->Bytes = Bytes; + history->Sign = Bytes < 0 ? '-' : '+'; + RtlCopyMemory( history->Location, Type, 12 ); + history->Block = Block; + history->Process = Process; + +} // AfdRecordQuotaHistory +#endif +#endif + + +PMDL +AfdAdvanceMdlChain( + IN PMDL Mdl, + IN ULONG Offset + ) + +/*++ + +Routine Description: + + Accepts a pointer to an existing MDL chain and offsets that chain + by a specified number of bytes. This may involve the creation + of a partial MDL for the first entry in the new chain. + +Arguments: + + Mdl - Pointer to the MDL chain to advance. + + Offset - The number of bytes to offset the chain. + +Return Value: + + NTSTATUS -- Indicates the status of the request. + +--*/ + +{ + PMDL partialMdl; + PVOID virtualAddress; + + // + // Sanity check. + // + + ASSERT( Mdl != NULL ); + ASSERT( Offset > 0 ); + + // + // Scan past any fully completed MDLs. + // + + while ( Offset > MmGetMdlByteCount( Mdl ) ) { + + Offset -= MmGetMdlByteCount( Mdl ); + ASSERT( Mdl->Next != NULL ); + Mdl = Mdl->Next; + + } + + // + // Tautology of the day: Offset will either be zero (meaning that + // we've advanced to a clean boundary between MDLs) or non-zero + // (meaning we need to now build a partial MDL). + // + + if ( Offset > 0 ) { + + // + // Compute the virtual address for the new MDL. + // + + virtualAddress = (PVOID)((PUCHAR)MmGetMdlVirtualAddress( Mdl ) + Offset); + + // + // Allocate the partial MDL. + // + + partialMdl = IoAllocateMdl( + virtualAddress, + MmGetMdlByteCount( Mdl ) - Offset, + FALSE, // SecondaryBuffer + FALSE, // ChargeQuota + NULL // Irp + ); + + if ( partialMdl != NULL ) { + + // + // Map part of the existing MDL into the parital MDL. + // + + IoBuildPartialMdl( + Mdl, + partialMdl, + virtualAddress, + MmGetMdlByteCount( Mdl ) - Offset + ); + + } + + // + // Return the parital MDL. + // + + Mdl = partialMdl; + + } + + return Mdl; + +} // AfdAdvanceMdlChain + + +NTSTATUS +AfdAllocateMdlChain( + IN PIRP Irp, + IN LPWSABUF BufferArray, + IN ULONG BufferCount, + IN LOCK_OPERATION Operation, + OUT PULONG TotalByteCount + ) + +/*++ + +Routine Description: + + Allocates a MDL chain describing the WSABUF array and attaches + the chain to the specified IRP. + +Arguments: + + Irp - The IRP that will receive the MDL chain. + + BufferArray - Points to an array of WSABUF structures describing + the user's buffers. + + BufferCount - Contains the number of WSABUF structures in the + array. + + Operation - Specifies the type of operation being performed (either + IoReadAccess or IoWriteAccess). + + TotalByteCount - Will receive the total number of BYTEs described + by the WSABUF array. + +Return Value: + + NTSTATUS -- Indicates the status of the request. + +--*/ + +{ + NTSTATUS status; + PMDL currentMdl; + PMDL * chainTarget; + KPROCESSOR_MODE previousMode; + ULONG totalLength; + PVOID bufferPointer; + ULONG bufferLength; + + // + // Sanity check. + // + + ASSERT( Irp != NULL ); + ASSERT( Irp->MdlAddress == NULL ); + ASSERT( BufferArray != NULL ); + ASSERT( BufferCount > 0 ); + ASSERT( ( Operation == IoReadAccess ) || ( Operation == IoWriteAccess ) ); + ASSERT( TotalByteCount != NULL ); + + // + // Get the previous processor mode. + // + + previousMode = Irp->RequestorMode; + + // + // Get into a known state. + // + + status = STATUS_SUCCESS; + currentMdl = NULL; + chainTarget = &Irp->MdlAddress; + totalLength = 0; + + // + // Walk the array of WSABUF structures, creating the MDLs and + // probing & locking the pages. + // + + try { + + if( previousMode != KernelMode ) { + + // + // Probe the WSABUF array. + // + + ProbeForRead( + BufferArray, // Address + BufferCount * sizeof(WSABUF), // Length + sizeof(ULONG) // Alignment + ); + + } + + // + // Scan the array. + // + + do { + + bufferPointer = BufferArray->buf; + bufferLength = BufferArray->len; + + if( bufferPointer != NULL && + bufferLength > 0 ) { + + // + // Update the total byte counter. + // + + totalLength += bufferLength; + + // + // Create a new MDL. + // + + currentMdl = IoAllocateMdl( + bufferPointer, // VirtualAddress + bufferLength, // Length + FALSE, // SecondaryBuffer + TRUE, // ChargeQuota + NULL // Irp + ); + + if( currentMdl != NULL ) { + + // + // Lock the pages. This will raise an exception + // if the operation fails. + // + + MmProbeAndLockPages( + currentMdl, // MemoryDescriptorList + previousMode, // AccessMode + Operation // Operation + ); + + // + // Chain the MDL onto the IRP. In theory, we could + // do this by passing the IRP into IoAllocateMdl(), + // but IoAllocateMdl() does a linear scan on the MDL + // chain to find the last one in the chain. + // + // We can do much better. + // + + *chainTarget = currentMdl; + chainTarget = ¤tMdl->Next; + + // + // Advance to the next WSABUF structure. + // + + BufferArray++; + + } else { + + // + // Cannot allocate new MDL, return appropriate error. + // + + status = STATUS_INSUFFICIENT_RESOURCES; + break; + + } + + } + + } while( --BufferCount ); + + // + // Ensure the MDL chain is NULL terminated. + // + + ASSERT( *chainTarget == NULL ); + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + // + // Bad news. Snag the exception code. + // + + status = GetExceptionCode(); + + // + // currentMdl will only be non-NULL at this point if an MDL + // has been created, but MmProbeAndLockPages() raised an + // exception. If this is true, then free the MDL. + // + + if( currentMdl != NULL ) { + + IoFreeMdl( currentMdl ); + + } + + } + + // + // Return the total buffer count. + // + + *TotalByteCount = totalLength; + + return status; + +} // AfdAllocateMdlChain + + +VOID +AfdDestroyMdlChain ( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + Unlocks & frees the MDLs in the MDL chain attached to the given IRP. + +Arguments: + + Irp - The IRP that owns the MDL chain to destroy. + +Return Value: + + None. + +--*/ + +{ + + PMDL mdl; + PMDL nextMdl; + + mdl = Irp->MdlAddress; + Irp->MdlAddress = NULL; + + while( mdl != NULL ) { + + nextMdl = mdl->Next; + MmUnlockPages( mdl ); + IoFreeMdl( mdl ); + mdl = nextMdl; + + } + +} // AfdDestroyMdlChain + + +ULONG +AfdCalcBufferArrayByteLengthRead( + IN LPWSABUF BufferArray, + IN ULONG BufferCount + ) + +/*++ + +Routine Description: + + Calculates the total size (in bytes) of the buffers described by the + specified WSABUF array and probes those buffers for read access. + +Arguments: + + BufferArray - Points to an array of WSABUF structures. + + BufferCount - The number of entries in BufferArray. + +Return Value: + + ULONG - The total size (in bytes) of the buffers described by the + WSABUF array. Will raise an exception & return -1 if the total + size is obviously too large. + +--*/ + +{ + + LARGE_INTEGER totalLength; + KPROCESSOR_MODE previousMode; + + PAGED_CODE( ); + + // + // Sanity check. + // + + ASSERT( BufferArray != NULL ); + ASSERT( BufferCount > 0 ); + + previousMode = ExGetPreviousMode(); + + if( previousMode != KernelMode ) { + + // + // Probe the WSABUF array. + // + + ProbeForRead( + BufferArray, // Address + BufferCount * sizeof(WSABUF), // Length + sizeof(ULONG) // Alignment + ); + + } + + // + // Scan the array & sum the lengths. + // + + totalLength.QuadPart = 0; + + while( BufferCount-- ) { + + if( previousMode != KernelMode ) { + + ProbeForRead( + BufferArray->buf, // Address + BufferArray->len, // Length + sizeof(UCHAR) // Alignment + ); + + } + + totalLength.QuadPart += (LONGLONG)BufferArray->len; + BufferArray++; + + } + + if( totalLength.HighPart == 0 && + ( totalLength.LowPart & 0x80000000 ) == 0 ) { + + return totalLength.LowPart; + + } + + ExRaiseAccessViolation(); + return (ULONG)-1L; + +} // AfdCalcBufferArrayByteLengthRead + + +ULONG +AfdCalcBufferArrayByteLengthWrite( + IN LPWSABUF BufferArray, + IN ULONG BufferCount + ) + +/*++ + +Routine Description: + + Calculates the total size (in bytes) of the buffers described by the + specified WSABUF array and probes those buffers for write access. + +Arguments: + + BufferArray - Points to an array of WSABUF structures. + + BufferCount - The number of entries in BufferArray. + +Return Value: + + ULONG - The total size (in bytes) of the buffers described by the + WSABUF array. Will raise an exception & return -1 if the total + size is obviously too large. + +--*/ + +{ + + LARGE_INTEGER totalLength; + KPROCESSOR_MODE previousMode; + + PAGED_CODE( ); + + // + // Sanity check. + // + + ASSERT( BufferArray != NULL ); + ASSERT( BufferCount > 0 ); + + previousMode = ExGetPreviousMode(); + + if( previousMode != KernelMode ) { + + // + // Probe the WSABUF array. + // + + ProbeForRead( + BufferArray, // Address + BufferCount * sizeof(WSABUF), // Length + sizeof(ULONG) // Alignment + ); + + } + + // + // Scan the array & sum the lengths. + // + + totalLength.QuadPart = 0; + + while( BufferCount-- ) { + + if( previousMode != KernelMode ) { + + ProbeForWrite( + BufferArray->buf, // Address + BufferArray->len, // Length + sizeof(UCHAR) // Alignment + ); + + } + + totalLength.QuadPart += (LONGLONG)BufferArray->len; + BufferArray++; + + } + + if( totalLength.HighPart == 0 && + ( totalLength.LowPart & 0x80000000 ) == 0 ) { + + return totalLength.LowPart; + + } + + ExRaiseAccessViolation(); + return (ULONG)-1L; + +} // AfdCalcBufferArrayByteLengthWrite + + +ULONG +AfdCopyBufferArrayToBuffer( + IN PVOID Destination, + IN ULONG DestinationLength, + IN LPWSABUF BufferArray, + IN ULONG BufferCount + ) + +/*++ + +Routine Description: + + Copies data from a WSABUF array to a linear buffer. + +Arguments: + + Destination - Points to the linear destination of the data. + + DestinationLength - The length of Destination. + + BufferArray - Points to an array of WSABUF structures describing the + source for the copy. + + BufferCount - The number of entries in BufferArray. + +Return Value: + + ULONG - The number of bytes copied. + +--*/ + +{ + + PVOID destinationStart; + ULONG bytesToCopy; + + PAGED_CODE( ); + + // + // Sanity check. + // + + ASSERT( Destination != NULL ); + ASSERT( BufferArray != NULL ); + ASSERT( BufferCount > 0 ); + + // + // Remember this so we can calc number of bytes copied. + // + + destinationStart = Destination; + + // + // Scan the array & copy to the linear buffer. + // + + while( BufferCount-- && DestinationLength > 0 ) { + + bytesToCopy = min( DestinationLength, BufferArray->len ); + + RtlCopyMemory( + Destination, + BufferArray->buf, + bytesToCopy + ); + + Destination = (PCHAR)Destination + bytesToCopy; + DestinationLength -= bytesToCopy; + BufferArray++; + + } + + // + // Return number of bytes copied. + // + + return (PCHAR)Destination - (PCHAR)destinationStart; + +} // AfdCopyBufferArrayToBuffer + + +ULONG +AfdCopyBufferToBufferArray( + IN LPWSABUF BufferArray, + IN ULONG Offset, + IN ULONG BufferCount, + IN PVOID Source, + IN ULONG SourceLength + ) + +/*++ + +Routine Description: + + Copies data from a linear buffer to a WSABUF array. + +Arguments: + + BufferArray - Points to an array of WSABUF structures describing the + destination for the copy. + + Offset - An offset within the buffer array at which the data should + be copied. + + BufferCount - The number of entries in BufferArray. + + Source - Points to the linear source of the data. + + SourceLength - The length of Source. + +Return Value: + + ULONG - The number of bytes copied. + +--*/ + +{ + + PVOID sourceStart; + ULONG bytesToCopy; + + PAGED_CODE( ); + + // + // Sanity check. + // + + ASSERT( BufferArray != NULL ); + ASSERT( BufferCount > 0 ); + ASSERT( Source != NULL ); + + // + // Remember this so we can return the number of bytes copied. + // + + sourceStart = Source; + + // + // Handle the offset if one was specified. + // + + if( Offset > 0 ) { + + // + // Skip whole entries if necessary. + // + + while( BufferCount > 0 && Offset >= BufferArray->len ) { + + Offset -= BufferArray->len; + BufferArray++; + BufferCount--; + + } + + // + // If there's a fragmented portion remaining, and we still + // have buffers left, then copy the fragment here to keep + // the loop below fast. + // + + if( Offset > 0 && BufferCount > 0 ) { + + ASSERT( Offset < BufferArray->len ); + + bytesToCopy = min( SourceLength, BufferArray->len - Offset ); + + RtlCopyMemory( + BufferArray->buf + Offset, + Source, + bytesToCopy + ); + + Source = (PCHAR)Source + bytesToCopy; + SourceLength -= bytesToCopy; + BufferArray++; + BufferCount--; + + } + + } + + // + // Scan the array & copy from the linear buffer. + // + + while( BufferCount-- && SourceLength > 0 ) { + + bytesToCopy = min( SourceLength, BufferArray->len ); + + RtlCopyMemory( + BufferArray->buf, + Source, + bytesToCopy + ); + + Source = (PCHAR)Source + bytesToCopy; + SourceLength -= bytesToCopy; + BufferArray++; + + } + + // + // Return number of bytes copied. + // + + return (PCHAR)Source - (PCHAR)sourceStart; + +} // AfdCopyBufferToBufferArray + + +#ifdef NT351 + +NTSTATUS +AfdReferenceEventObjectByHandle( + IN HANDLE Handle, + IN KPROCESSOR_MODE AccessMode, + OUT PVOID *Object + ) + +/*++ + +Routine Description: + + References an event object by handle, returning a pointer to the + object. + +Arguments: + + Handle - The event handle to reference. + + AccessMode - The requestor mode (user or kernel). + + Object - Receives a pointer to the event object. + +Return Value: + + NTSTATUS - Completion status. + +--*/ + +{ + + NTSTATUS status; + PKEVENT eventObject; + + PAGED_CODE( ); + + // + // Reference the event object, but pass a NULL ObjectType descriptor + // to ObReferenceObjectByHandle(). We must do this because the NT 3.51 + // kernel does not export the ExEventObjectType descriptor. + // + + status = ObReferenceObjectByHandle( + Handle, // Handle + 0, // DesiredAccess + NULL, // ObjectType + AccessMode, // AccessMode + Object, // Object, + NULL // HandleInformation + ); + + if( NT_SUCCESS(status) ) { + + eventObject = (PKEVENT)*Object; + + // + // Since we had to pass in a NULL object type descriptor, OB + // couldn't validate the object type for us. In an attempt to + // ensure we don't just blindly use the resulting object, we'll + // make a few sanity checks here. + // + + if( ( eventObject->Header.Type == NotificationEvent || + eventObject->Header.Type == SynchronizationEvent ) && + eventObject->Header.Size == sizeof(*eventObject ) && + eventObject->Header.Inserted == 0 && + (ULONG)eventObject->Header.SignalState <= 1 ) { + + return STATUS_SUCCESS; + + } + + // + // Object type mismatch. + // + + ObDereferenceObject( eventObject ); + return STATUS_OBJECT_TYPE_MISMATCH; + + } + + // + // ObReferenceObjectByHandle() failure. + // + + return status; + +} // AfdReferenceEventObjectByHandle + + +NTSTATUS +AfdQueueUserApc ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Queues a user-mode APC. + +Arguments: + + Irp - Pointer to I/O request packet. + + IrpSp - pointer to the IO stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates whether the APC was successfully queued. + +--*/ + +{ + PAFD_QUEUE_APC_INFO apcInfo; + PAFD_APC afdApc; + PETHREAD threadObject; + NTSTATUS status; + + PAGED_CODE( ); + + // + // Set up local pointers. + // + + apcInfo = Irp->AssociatedIrp.SystemBuffer; + + // + // Validate the parameters. + // + + if( apcInfo == NULL || + IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*apcInfo) || + apcInfo->Thread == NULL || + apcInfo->ApcRoutine == NULL ) { + return STATUS_INVALID_PARAMETER; + } + + // + // Reference the target thread object. + // + + status = ObReferenceObjectByHandle( + apcInfo->Thread, // Handle + 0, // DesiredAccess + *(POBJECT_TYPE *)PsThreadType, // ObjectType + Irp->RequestorMode, // AccessMode + (PVOID *)&threadObject, // Object + NULL // HandleInformation + ); + + if ( !NT_SUCCESS(status) ) { + return status; + } + + // + // Create the APC object. + // + + afdApc = AFD_ALLOCATE_POOL( + NonPagedPool, + sizeof(*afdApc), + AFD_APC_POOL_TAG + ); + + if ( afdApc == NULL ) { + ObDereferenceObject(threadObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Initialize the APC object. + // + + KeInitializeApc( + &afdApc->Apc, // Apc + &threadObject->Tcb, // Thread + CurrentApcEnvironment, // Environment + AfdSpecialApc, // KernelRoutine + AfdSpecialApcRundown, // RundownRoutine + (PKNORMAL_ROUTINE)apcInfo->ApcRoutine, // NormalRoutine + Irp->RequestorMode, // ProcessorMode + apcInfo->ApcContext // NormalContext + ); + + // + // Insert the APC into the thread's queue. + // + + KeInsertQueueApc( + &afdApc->Apc, // Apc + apcInfo->SystemArgument1, // SystemArgument1 + apcInfo->SystemArgument2, // SystemArgument2 + 0 // Increment + ); + + // + // Dereference the target thread. Note that the AFD_APC structure + // will be freed in either AfdSpecialApc (if the APC was successfully + // delivered to the target thread) or AfdSpecialApcRundown (if the + // target thread was destroyed before the APC could be delivered). + // + + ObDereferenceObject(threadObject); + + Irp->IoStatus.Information = 0; + + return STATUS_SUCCESS; + +} // AfdQueueUserApc + + +VOID +AfdSpecialApc ( + struct _KAPC *Apc, + PKNORMAL_ROUTINE *NormalRoutine, + PVOID *NormalContext, + PVOID *SystemArgument1, + PVOID *SystemArgument2 + ) + +/*++ + +Routine Description: + + This is the kernel apc routine. + + The only real work needed here is to free the AFD_APC structure + allocated in AfdQueueUserApc(). + +Arguments: + + Apc - pointer to apc object + + NormalRoutine - Will be called when we return. + + NormalContext - will be 1st argument to normal routine. + + SystemArgument1 - Uninterpreted. + + SystemArgument2 - Uninterpreted. + +Return Value: + + NONE. + +--*/ + +{ + PAFD_APC afdApc; + + PAGED_CODE(); + + ASSERT( Apc != NULL ); + + // + // Grab a pointer to the APC's containing AFD_APC structure, + // then free it. + // + + afdApc = CONTAINING_RECORD( Apc, AFD_APC, Apc ); + + AFD_FREE_POOL( + afdApc, + AFD_APC_POOL_TAG + ); + +} // AfdSpecialApc + + +VOID +AfdSpecialApcRundown ( + struct _KAPC *Apc + ) + +/*++ + +Routine Description: + + This routine is called to clear away apcs in the apc queue + of a thread that has been terminated. + + The only real work needed here is to free the AFD_APC structure + allocated in AfdQueueUserApc(). + +Arguments: + + Apc - pointer to apc object + +Return Value: + + NONE. + +--*/ + +{ + PAFD_APC afdApc; + + PAGED_CODE(); + + ASSERT( Apc != NULL ); + + // + // Grab a pointer to the APC's containing AFD_APC structure, + // then free it. + // + + afdApc = CONTAINING_RECORD( Apc, AFD_APC, Apc ); + + AFD_FREE_POOL( + afdApc, + AFD_APC_POOL_TAG + ); + +} // AfdSpecialApcRundown + +#endif // NT351 + +#if DBG + +VOID +AfdAssert( + IN PVOID FailedAssertion, + IN PVOID FileName, + IN ULONG LineNumber, + IN PCHAR Message OPTIONAL + ) +{ + + if( AfdUsePrivateAssert ) { + + DbgPrint( + "\n*** Assertion failed: %s%s\n*** Source File: %s, line %ld\n\n", + Message + ? Message + : "", + FailedAssertion, + FileName, + LineNumber + ); + + DbgBreakPoint(); + + } else { + + RtlAssert( + FailedAssertion, + FileName, + LineNumber, + Message + ); + + } + +} // AfdAssert + +#endif // DBG + + +NTSTATUS +AfdSetQos( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine sets the QOS for the given endpoint. Note that, since + we don't really (yet) support QOS, we just ignore the incoming + data and issue a AFD_POLL_QOS or AFD_POLL_GROUP_QOS event as + appropriate. + +Arguments: + + Irp - Pointer to I/O request packet. + + IrpSp - pointer to the IO stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + + PAFD_ENDPOINT endpoint; + PAFD_QOS_INFO qosInfo; + + PAGED_CODE(); + + // + // Set up local pointers. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + qosInfo = Irp->AssociatedIrp.SystemBuffer; + + // + // Make sure that the input buffer is large enough. + // + + if( IrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof(*qosInfo) ) { + + return STATUS_BUFFER_TOO_SMALL; + + } + + // + // If the incoming data doesn't match the default QOS, + // indicate the appropriate event. + // + + if( !RtlEqualMemory( + &qosInfo->Qos, + &AfdDefaultQos, + sizeof(QOS) + ) ) { + + AfdIndicatePollEvent( + endpoint, + qosInfo->GroupQos + ? AFD_POLL_GROUP_QOS_BIT + : AFD_POLL_QOS_BIT, + STATUS_SUCCESS + ); + + } + + // + // Complete the IRP. + // + + Irp->IoStatus.Information = 0; + return STATUS_SUCCESS; + +} // AfdSetQos + + +NTSTATUS +AfdGetQos( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine gets the QOS for the given endpoint. + +Arguments: + + Irp - Pointer to I/O request packet. + + IrpSp - pointer to the IO stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + + PAFD_ENDPOINT endpoint; + PAFD_QOS_INFO qosInfo; + + PAGED_CODE(); + + // + // Set up local pointers. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + qosInfo = Irp->AssociatedIrp.SystemBuffer; + + // + // Make sure that the output buffer is large enough. + // + + if( IrpSp->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(*qosInfo) ) { + + return STATUS_BUFFER_TOO_SMALL; + + } + + // + // Just return the default data. + // + + RtlCopyMemory( + &qosInfo->Qos, + &AfdDefaultQos, + sizeof(QOS) + ); + + // + // Complete the IRP. + // + + Irp->IoStatus.Information = sizeof(*qosInfo); + return STATUS_SUCCESS; + +} // AfdGetQos + + +NTSTATUS +AfdNoOperation( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine does nothing but complete the IRP. + +Arguments: + + Irp - Pointer to I/O request packet. + + IrpSp - pointer to the IO stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + + PAFD_ENDPOINT endpoint; + + PAGED_CODE(); + + // + // Set up local pointers. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + // + // Complete the IRP. + // + + Irp->IoStatus.Information = 0; + return STATUS_SUCCESS; + +} // AfdNoOperation + + +NTSTATUS +AfdValidateGroup( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine examines a group ID. If the ID is for a "constrained" + group, then all endpoints are scanned to validate the given address + is consistent with the constrained group. + + +Arguments: + + Irp - Pointer to I/O request packet. + + IrpSp - pointer to the IO stack location to use for this request. + +Return Value: + + NTSTATUS -- Indicates whether the request was successfully queued. + +--*/ + +{ + + PAFD_ENDPOINT endpoint; + PAFD_ENDPOINT compareEndpoint; + PAFD_CONNECTION connection; + PLIST_ENTRY listEntry; + PAFD_VALIDATE_GROUP_INFO validateInfo; + AFD_GROUP_TYPE groupType; + PTRANSPORT_ADDRESS requestAddress; + ULONG requestAddressLength; + KIRQL oldIrql; + BOOLEAN result; + LONG groupId; + + PAGED_CODE(); + + // + // Set up local pointers. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + validateInfo = Irp->AssociatedIrp.SystemBuffer; + + // + // Make sure that the input buffer is large enough. + // + + if( IrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof(*validateInfo) ) { + + return STATUS_BUFFER_TOO_SMALL; + + } + + if( validateInfo->RemoteAddress.TAAddressCount != 1 ) { + + return STATUS_INVALID_PARAMETER; + + } + + if( IrpSp->Parameters.DeviceIoControl.InputBufferLength < + ( sizeof(*validateInfo) - + sizeof(TRANSPORT_ADDRESS) + + validateInfo->RemoteAddress.Address[0].AddressLength ) ) { + + return STATUS_BUFFER_TOO_SMALL; + + } + + // + // Start by referencing the group so it doesn't go away unexpectedly. + // This will also validate the group ID, and give us the group type. + // + + groupId = validateInfo->GroupID; + + if( !AfdReferenceGroup( groupId, &groupType ) ) { + + return STATUS_INVALID_PARAMETER; + + } + + // + // If it's not a constrained group ID, we can just complete the IRP + // successfully right now. + // + + if( groupType != GroupTypeConstrained ) { + + AfdDereferenceGroup( validateInfo->GroupID ); + + Irp->IoStatus.Information = 0; + return STATUS_SUCCESS; + + } + + // + // Calculate the size of the incoming TDI address. + // + + requestAddress = &validateInfo->RemoteAddress; + + requestAddressLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength - + sizeof(AFD_VALIDATE_GROUP_INFO) + + sizeof(TRANSPORT_ADDRESS); + + // + // OK, it's a constrained group. Scan the list of constrained endpoints, + // find those that are either datagram endpoints or have associated + // connections, and validate the remote addresses. + // + + result = TRUE; + + ExAcquireResourceShared( AfdResource, TRUE ); + + for( listEntry = AfdConstrainedEndpointListHead.Flink ; + listEntry != &AfdConstrainedEndpointListHead ; + listEntry = listEntry->Flink ) { + + compareEndpoint = CONTAINING_RECORD( + listEntry, + AFD_ENDPOINT, + ConstrainedEndpointListEntry + ); + + ASSERT( IS_AFD_ENDPOINT_TYPE( compareEndpoint ) ); + ASSERT( compareEndpoint->GroupType == GroupTypeConstrained ); + + // + // Skip this endpoint if the group IDs don't match. + // + + if( groupId != compareEndpoint->GroupID ) { + + continue; + + } + + // + // If this is a datagram endpoint, check it's remote address. + // + + if( IS_DGRAM_ENDPOINT( compareEndpoint ) ) { + + AfdAcquireSpinLock( &compareEndpoint->SpinLock, &oldIrql ); + + if( compareEndpoint->Common.Datagram.RemoteAddress != NULL && + compareEndpoint->Common.Datagram.RemoteAddressLength == + requestAddressLength ) { + + result = AfdCompareAddresses( + compareEndpoint->Common.Datagram.RemoteAddress, + compareEndpoint->Common.Datagram.RemoteAddressLength, + requestAddress, + requestAddressLength + ); + + } + + AfdReleaseSpinLock( &compareEndpoint->SpinLock, oldIrql ); + + if( !result ) { + break; + } + + } else { + + // + // Not a datagram. If it's a connected endpoint, still has + // a connection object, and that object has a remote address, + // then compare the addresses. + // + + AfdAcquireSpinLock( &compareEndpoint->SpinLock, &oldIrql ); + + connection = AFD_CONNECTION_FROM_ENDPOINT( compareEndpoint ); + + if( compareEndpoint->State == AfdEndpointStateConnected && + connection != NULL ) { + + REFERENCE_CONNECTION( connection ); + + AfdReleaseSpinLock( &compareEndpoint->SpinLock, oldIrql ); + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + if( connection->RemoteAddress != NULL && + connection->RemoteAddressLength == requestAddressLength ) { + + result = AfdCompareAddresses( + connection->RemoteAddress, + connection->RemoteAddressLength, + requestAddress, + requestAddressLength + ); + + } + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + DEREFERENCE_CONNECTION( connection ); + + if( !result ) { + break; + } + + } else { + + AfdReleaseSpinLock( &compareEndpoint->SpinLock, oldIrql ); + + } + + } + + } + + ExReleaseResource( AfdResource ); + AfdDereferenceGroup( validateInfo->GroupID ); + + if( !result ) { + return STATUS_INVALID_PARAMETER; + } + + // + // Success! + // + + Irp->IoStatus.Information = 0; + return STATUS_SUCCESS; + +} // AfdValidateGroup + +BOOLEAN +AfdCompareAddresses( + IN PTRANSPORT_ADDRESS Address1, + IN ULONG Address1Length, + IN PTRANSPORT_ADDRESS Address2, + IN ULONG Address2Length + ) + +/*++ + +Routine Description: + + This routine compares two addresses in a special way to support + constrained socket groups. This routine will return TRUE if the + two addresses represent the same "interface". By "interface", I + mean something like an IP address or an IPX address. Note that for + some address types (such as IP) certain portions of the address + should be ignored (such as the port). + + I really hate hard-coded knowledge of "select" address types, but + there's no easy way around it. Ideally, this should be the protocol + driver's responsibility. We could really use a standard "compare + these addresses" IOCTL in TDI. + +Arguments: + + Address1 - The first address. + + Address1Length - The length of Address1. + + Address2 - The second address. + + Address2Length - The length of Address2. + +Return Value: + + BOOLEAN - TRUE if the addresses reference the same interface, FALSE + otherwise. + +--*/ + +{ + + USHORT addressType; + + addressType = Address1->Address[0].AddressType; + + if( addressType != Address2->Address[0].AddressType ) { + + // + // If they're not the same address type, they can't be the + // same address... + // + + return FALSE; + + } + + // + // Special case a few addresses. + // + + switch( addressType ) { + + case TDI_ADDRESS_TYPE_IP : { + + TDI_ADDRESS_IP UNALIGNED * ip1; + TDI_ADDRESS_IP UNALIGNED * ip2; + + ip1 = (PVOID)&Address1->Address[0].Address[0]; + ip2 = (PVOID)&Address2->Address[0].Address[0]; + + // + // IP addresses. Compare the address portion (ignoring + // the port). + // + + if( ip1->in_addr == ip2->in_addr ) { + return TRUE; + } + + } + return FALSE; + + case TDI_ADDRESS_TYPE_IPX : { + + TDI_ADDRESS_IPX UNALIGNED * ipx1; + TDI_ADDRESS_IPX UNALIGNED * ipx2; + + ipx1 = (PVOID)&Address1->Address[0].Address[0]; + ipx2 = (PVOID)&Address2->Address[0].Address[0]; + + // + // IPX addresses. Compare the network and node addresses. + // + + if( ipx1->NetworkAddress == ipx2->NetworkAddress && + RtlEqualMemory( + ipx1->NodeAddress, + ipx2->NodeAddress, + sizeof(ipx1->NodeAddress) + ) ) { + return TRUE; + } + + } + return FALSE; + + case TDI_ADDRESS_TYPE_APPLETALK : { + + TDI_ADDRESS_APPLETALK UNALIGNED * atalk1; + TDI_ADDRESS_APPLETALK UNALIGNED * atalk2; + + atalk1 = (PVOID)&Address1->Address[0].Address[0]; + atalk2 = (PVOID)&Address2->Address[0].Address[0]; + + // + // APPLETALK address. Compare the network and node + // addresses. + // + + if( atalk1->Network == atalk2->Network && + atalk1->Node == atalk2->Node ) { + return TRUE; + } + + } + return FALSE; + + case TDI_ADDRESS_TYPE_VNS : { + + TDI_ADDRESS_VNS UNALIGNED * vns1; + TDI_ADDRESS_VNS UNALIGNED * vns2; + + vns1 = (PVOID)&Address1->Address[0].Address[0]; + vns2 = (PVOID)&Address2->Address[0].Address[0]; + + // + // VNS addresses. Compare the network and subnet addresses. + // + + if( RtlEqualMemory( + vns1->net_address, + vns2->net_address, + sizeof(vns1->net_address) + ) && + RtlEqualMemory( + vns1->subnet_addr, + vns2->subnet_addr, + sizeof(vns1->subnet_addr) + ) ) { + return TRUE; + } + + } + return FALSE; + + default : + + // + // Unknown address type. Do a simple memory compare. + // + + return (BOOLEAN)RtlEqualMemory( + Address1, + Address2, + Address2Length + ); + + } + +} // AfdCompareAddresses + +PAFD_CONNECTION +AfdFindReturnedConnection( + IN PAFD_ENDPOINT Endpoint, + IN ULONG Sequence + ) + +/*++ + +Routine Description: + + Scans the endpoints queue of returned connections looking for one + with the specified sequence number. + +Arguments: + + Endpoint - A pointer to the endpoint from which to get a connection. + + Sequence - The sequence the connection must match. This is actually + a pointer to the connection. + +Return Value: + + AFD_CONNECTION - A pointer to an AFD connection block if successful, + NULL if not. + +--*/ + +{ + + PAFD_CONNECTION connection; + PLIST_ENTRY listEntry; + + ASSERT( Endpoint != NULL ); + ASSERT( IS_AFD_ENDPOINT_TYPE( Endpoint ) ); + + // + // Walk the endpoint's list of returned connections until we reach + // the end or until we find one with a matching sequence. + // + + for( listEntry = Endpoint->Common.VcListening.ReturnedConnectionListHead.Flink; + listEntry != &Endpoint->Common.VcListening.ReturnedConnectionListHead; + listEntry = listEntry->Flink ) { + + connection = CONTAINING_RECORD( + listEntry, + AFD_CONNECTION, + ListEntry + ); + + if( Sequence == (ULONG)connection ) { + + return connection; + + } + + } + + return NULL; + +} // AfdFindReturnedConnection + +NTSTATUS +AfdGetUnacceptedConnectData ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) +{ + + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + PAFD_CONNECT_DATA_BUFFERS connectDataBuffers; + PAFD_UNACCEPTED_CONNECT_DATA_INFO connectInfo; + KIRQL oldIrql; + ULONG dataLength; + + // + // Set up local pointers. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + connectInfo = Irp->AssociatedIrp.SystemBuffer; + + // + // Validate the request. + // + + if( endpoint->Type != AfdBlockTypeVcListening || + IrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof(*connectInfo) ) { + + return STATUS_INVALID_PARAMETER; + + } + + if( connectInfo->LengthOnly && + IrpSp->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(*connectInfo) ) { + + return STATUS_INVALID_PARAMETER; + + } + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + // + // Find the specified connection. + // + + connection = AfdFindReturnedConnection( + endpoint, + connectInfo->Sequence + ); + + if( connection == NULL ) { + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + return STATUS_INVALID_PARAMETER; + + } + + // + // Determine the length of any received connect data. + // + + dataLength = 0; + connectDataBuffers = connection->ConnectDataBuffers; + + if( connectDataBuffers != NULL && + connectDataBuffers->ReceiveConnectData.Buffer != NULL ) { + + dataLength = connectDataBuffers->ReceiveConnectData.BufferLength; + + } + + // + // If the caller is just interested in the data length, return it. + // + + if( connectInfo->LengthOnly ) { + + connectInfo->ConnectDataLength = dataLength; + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + Irp->IoStatus.Information = sizeof(*connectInfo); + return STATUS_SUCCESS; + + } + + // + // If there is no connect data, complete the IRP with no bytes. + // + + if( dataLength == 0 ) { + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + Irp->IoStatus.Information = 0; + return STATUS_SUCCESS; + + } + + // + // If the output buffer is too small, fail. + // + + if( IrpSp->Parameters.DeviceIoControl.OutputBufferLength < dataLength ) { + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + return STATUS_BUFFER_TOO_SMALL; + + } + + // + // Copy over the buffer and return the number of bytes copied. + // + + RtlCopyMemory( + Irp->AssociatedIrp.SystemBuffer, + connectDataBuffers->ReceiveConnectData.Buffer, + dataLength + ); + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + Irp->IoStatus.Information = dataLength; + return STATUS_SUCCESS; + +} // AfdGetUnacceptedConnectData + diff --git a/private/ntos/afd/nt351/makefile b/private/ntos/afd/nt351/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/afd/nt351/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/afd/nt351/place351.inc b/private/ntos/afd/nt351/place351.inc new file mode 100644 index 000000000..435741542 --- /dev/null +++ b/private/ntos/afd/nt351/place351.inc @@ -0,0 +1,19 @@ +# +# Places NT 3.51 binaries into the NT 3.51 tree. +# + +!ifdef _NT386TREE +_NT386TREE=$(_NT386TREE)\nt351 +!endif + +!ifdef _NTMIPSTREE +_NTMIPSTREE=$(_NTMIPSTREE)\nt351 +!endif + +!ifdef _NTALPHATREE +_NTALPHATREE=$(_NTALPHATREE)\nt351 +!endif + +!ifdef _NTPPCTREE +_NTPPCTREE=$(_NTPPCTREE)\nt351 +!endif diff --git a/private/ntos/afd/nt351/sources b/private/ntos/afd/nt351/sources new file mode 100644 index 000000000..838e90f41 --- /dev/null +++ b/private/ntos/afd/nt351/sources @@ -0,0 +1,33 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + + +!INCLUDE place351.inc + +BUILD_FOR_3_51=1 + +TARGETPATH=obj + +!INCLUDE ..\sources.inc + diff --git a/private/ntos/afd/poll.c b/private/ntos/afd/poll.c new file mode 100644 index 000000000..18047ba90 --- /dev/null +++ b/private/ntos/afd/poll.c @@ -0,0 +1,1265 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + poll.c + +Abstract: + + Contains AfdPoll to handle IOCTL_AFD_POLL. + +Author: + + David Treadwell (davidtr) 4-Apr-1992 + +Revision History: + +--*/ + +#include "afdp.h" + +typedef struct _AFD_POLL_ENDPOINT_INFO { + PAFD_ENDPOINT Endpoint; + PFILE_OBJECT FileObject; + HANDLE Handle; + ULONG PollEvents; +} AFD_POLL_ENDPOINT_INFO, *PAFD_POLL_ENDPOINT_INFO; + +typedef struct _AFD_POLL_INFO_INTERNAL { + LIST_ENTRY PollListEntry; + ULONG NumberOfEndpoints; + PIRP Irp; + KDPC Dpc; + KTIMER Timer; + BOOLEAN Unique; + BOOLEAN TimerStarted; + AFD_POLL_ENDPOINT_INFO EndpointInfo[1]; +} AFD_POLL_INFO_INTERNAL, *PAFD_POLL_INFO_INTERNAL; + +VOID +AfdCancelPoll ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +AfdFreePollInfo ( + IN PAFD_POLL_INFO_INTERNAL PollInfoInternal + ); + +VOID +AfdTimeoutPoll ( + IN struct _KDPC *Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGEAFD, AfdPoll ) +#pragma alloc_text( PAGEAFD, AfdCancelPoll ) +#pragma alloc_text( PAGEAFD, AfdFreePollInfo ) +#pragma alloc_text( PAGEAFD, AfdTimeoutPoll ) +#endif + + +NTSTATUS +AfdPoll ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) +{ + NTSTATUS status; + PAFD_POLL_INFO pollInfo; + PAFD_POLL_HANDLE_INFO pollHandleInfo; + PAFD_POLL_INFO_INTERNAL pollInfoInternal; + PAFD_POLL_INFO_INTERNAL freePollInfo = NULL; + ULONG pollInfoInternalSize; + PAFD_POLL_ENDPOINT_INFO pollEndpointInfo; + ULONG i; + KIRQL oldIrql1, oldIrql2; + + // + // Set up locals. + // + + pollInfo = Irp->AssociatedIrp.SystemBuffer; + + IF_DEBUG(POLL) { + KdPrint(( "AfdPoll: poll IRP %lx, IrpSp %lx, handles %ld, " + "TO %lx,%lx\n", + Irp, IrpSp, + pollInfo->NumberOfHandles, + pollInfo->Timeout.HighPart, pollInfo->Timeout.LowPart )); + } + + Irp->IoStatus.Information = 0; + + // + // Determine how large the internal poll information structure will + // be and allocate space for it from nonpaged pool. It must be + // nonpaged since this will be accesses in event handlers. + // + + pollInfoInternalSize = sizeof(AFD_POLL_INFO_INTERNAL) + + (pollInfo->NumberOfHandles + 1) * sizeof(AFD_POLL_ENDPOINT_INFO); + + pollInfoInternal = AFD_ALLOCATE_POOL( + NonPagedPool, + pollInfoInternalSize, + AFD_POLL_POOL_TAG + ); + + if ( pollInfoInternal == NULL ) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto complete; + } + + // + // Initialize the internal information buffer. + // + + pollInfoInternal->Irp = Irp; + pollInfoInternal->NumberOfEndpoints = 0; + pollInfoInternal->Unique = pollInfo->Unique; + + pollHandleInfo = pollInfo->Handles; + pollEndpointInfo = pollInfoInternal->EndpointInfo; + + for ( i = 0; i < pollInfo->NumberOfHandles; i++ ) { + + status = ObReferenceObjectByHandle( + pollHandleInfo->Handle, + 0L, // DesiredAccess + *IoFileObjectType, + KernelMode, + (PVOID *)&pollEndpointInfo->FileObject, + NULL + ); + + if ( !NT_SUCCESS(status) ) { + AfdFreePollInfo( pollInfoInternal ); + goto complete; + } + + // + // Make sure that this is an AFD endpoint and not some other + // random file handle. + // + + if ( pollEndpointInfo->FileObject->DeviceObject != AfdDeviceObject ) { + + ObDereferenceObject( pollEndpointInfo->FileObject ); + status = STATUS_INVALID_HANDLE; + AfdFreePollInfo( pollInfoInternal ); + goto complete; + } + + pollEndpointInfo->PollEvents = pollHandleInfo->PollEvents; + pollEndpointInfo->Handle = pollHandleInfo->Handle; + pollEndpointInfo->Endpoint = pollEndpointInfo->FileObject->FsContext; + + ASSERT( InterlockedIncrement( &pollEndpointInfo->Endpoint->ObReferenceBias ) > 0 ); + + // + // Remember that there has been a poll on this endpoint. This flag + // allows us to optimize AfdIndicatePollEvent() for endpoints that have + // never been polled, which is a common case. + // + + pollEndpointInfo->Endpoint->PollCalled = TRUE; + + IF_DEBUG(POLL) { + KdPrint(( "AfdPoll: event %lx, endp %lx, conn %lx, handle %lx, " + "info %lx\n", + pollEndpointInfo->PollEvents, + pollEndpointInfo->Endpoint, + AFD_CONNECTION_FROM_ENDPOINT( pollEndpointInfo->Endpoint ), + pollEndpointInfo->Handle, + pollEndpointInfo )); + } + + REFERENCE_ENDPOINT( pollEndpointInfo->Endpoint ); + + // + // Increment pointers in the poll info structures. + // + + pollHandleInfo++; + pollEndpointInfo++; + pollInfoInternal->NumberOfEndpoints++; + } + + // + // Set up a cancel routine in the IRP so that the IRP will be + // completed correctly if it gets canceled. First check whether the + // IRP has already been canceled. + // + + IoAcquireCancelSpinLock( &oldIrql1 ); + + if ( Irp->Cancel ) { + + // + // The IRP has already been canceled. Free the internal + // poll information structure and complete the IRP. + // + + IoReleaseCancelSpinLock( oldIrql1 ); + + AfdFreePollInfo( pollInfoInternal ); + + status = STATUS_CANCELLED; + goto complete; + + } else { + + IoSetCancelRoutine( Irp, AfdCancelPoll ); + } + + // + // Hold the AFD spin lock while we check for endpoints that already + // satisfy a condition to synchronize between this operation and + // a call to AfdIndicatePollEvent. We release the spin lock + // after all the endpoints have been checked and the internal + // poll info structure is on the global list so AfdIndicatePollEvent + // can find it if necessary. + // + // Note that we continue to hold the cancel spin lock while we do + // this in order to prevent the IRP from being cancelled before it + // is on the global list of poll IRPs. If we didn't do this, the + // IRP could get cancelled, but AfdCancelPoll wouldn't find it and + // the IO would never get cancelled. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql2 ); + + // + // If this is a unique poll, determine whether there is another + // unique poll on this endpoint. If there is an existing unique + // poll, cancel it. This request will supercede the existing + // request. + // + + if ( pollInfo->Unique ) { + + PLIST_ENTRY listEntry; + + for ( listEntry = AfdPollListHead.Flink; + listEntry != &AfdPollListHead; + listEntry = listEntry->Flink ) { + + PAFD_POLL_INFO_INTERNAL testInfo; + BOOLEAN timerCancelSucceeded; + + testInfo = CONTAINING_RECORD( + listEntry, + AFD_POLL_INFO_INTERNAL, + PollListEntry + ); + + if ( testInfo->Unique && + testInfo->EndpointInfo[0].FileObject == + pollInfoInternal->EndpointInfo[0].FileObject ) { + + IF_DEBUG(POLL) { + KdPrint(( "AfdPoll: found existing unique poll IRP %lx " + "for file object %lx, context %lx, cancelling.\n", + testInfo->Irp, + testInfo->EndpointInfo[0].FileObject, + testInfo )); + } + + // + // Cancel the IRP manually rather than calling + // AfdCancelPoll because we already hold the + // AfdSpinLock, we can't acquire it recursively, and we + // don't want to release it. Remove the poll structure + // from the global list. + // + + RemoveEntryList( &testInfo->PollListEntry ); + + // + // Cancel the timer. + // + + if ( testInfo->TimerStarted ) { + timerCancelSucceeded = KeCancelTimer( &testInfo->Timer ); + } else { + timerCancelSucceeded = TRUE; + } + + // + // Complete the IRP with STATUS_CANCELLED as the status. + // + + testInfo->Irp->IoStatus.Information = 0; + testInfo->Irp->IoStatus.Status = STATUS_CANCELLED; + + IoSetCancelRoutine( testInfo->Irp, NULL ); + + IoCompleteRequest( testInfo->Irp, AfdPriorityBoost ); + + // + // Remember the poll info structure so that we'll free + // before we exit. We cannot free it now because we're + // holding the AfdSpinLock. Note that if cancelling the + // timer failed, then the timer is already running and + // it will free the poll info structure, but not + // complete the IRP since we complete it and NULL it + // here. + // + + if ( timerCancelSucceeded ) { + freePollInfo = testInfo; + } else { + pollInfoInternal->Irp = NULL; + } + + // + // There should be only one outstanding unique poll IRP + // on any given file object, so quit looking for another + // now that we've found one. + // + + break; + } + } + } + + // + // We're done with the input structure provided by the caller. Now + // walk through the internal structure and determine whether any of + // the specified endpoints are ready for the specified condition. + // + + pollInfo->NumberOfHandles = 0; + + pollHandleInfo = pollInfo->Handles; + pollEndpointInfo = pollInfoInternal->EndpointInfo; + + for ( i = 0; i < pollInfoInternal->NumberOfEndpoints; i++ ) { + + BOOLEAN found; + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + + found = FALSE; + endpoint = pollEndpointInfo->Endpoint; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint ); + ASSERT( connection == NULL || connection->Type == AfdBlockTypeConnection ); + + pollHandleInfo->PollEvents = 0; + pollHandleInfo->Status = STATUS_SUCCESS; + + // + // Check each possible event and, if it is being polled, whether + // the endpoint is ready for that event. If the endpoint is + // ready, write information about the endpoint into the output + // buffer. + // + + if ( (pollEndpointInfo->PollEvents & AFD_POLL_RECEIVE) != 0 ) { + + // + // For most endpoints, a receive poll is completed when + // data arrived that does not have a posted receive. + // For listening endpoints, however, a receive poll + // completes when there is a connection available to be + // accepted. + // + + if ( endpoint->State != AfdEndpointStateListening ) { + + if ( (connection != NULL && + IS_DATA_ON_CONNECTION( connection )) || + + (IS_DGRAM_ENDPOINT(endpoint) && + ARE_DATAGRAMS_ON_ENDPOINT( endpoint )) ) { + + pollHandleInfo->Handle = pollEndpointInfo->Handle; + pollHandleInfo->PollEvents |= AFD_POLL_RECEIVE; + found = TRUE; + } + + // + // If the endpoint is set up for inline reception of + // expedited data, then any expedited data should + // be indicated as normal data. + // + + if ( connection != NULL && endpoint->InLine && + IS_EXPEDITED_DATA_ON_CONNECTION( connection ) ) { + pollHandleInfo->Handle = pollEndpointInfo->Handle; + pollHandleInfo->PollEvents |= AFD_POLL_RECEIVE; + found = TRUE; + } + + } else { + + // + // This is really a poll to see whether a connection is + // available for an immediate accept. Convert the events + // and do the check below in the accept poll handling. + // + + pollEndpointInfo->PollEvents &= ~AFD_POLL_RECEIVE; + pollEndpointInfo->PollEvents |= AFD_POLL_ACCEPT; + } + } + + if ( (pollEndpointInfo->PollEvents & AFD_POLL_RECEIVE_EXPEDITED) != 0 ) { + + // + // If the endpoint is set up for inline reception of + // expedited data, do not indicate as expedited data. + // + + if ( connection != NULL && !endpoint->InLine && + IS_EXPEDITED_DATA_ON_CONNECTION( connection ) ) { + pollHandleInfo->Handle = pollEndpointInfo->Handle; + pollHandleInfo->PollEvents |= AFD_POLL_RECEIVE_EXPEDITED; + found = TRUE; + } + } + + if ( (pollEndpointInfo->PollEvents & AFD_POLL_SEND) != 0 ) { + + // + // For unconnected non-datagram endpoints, a send poll + // should complete when a connect operation completes. + // Therefore, if this is an non-datagram endpoint which is + // not connected, do not complete the poll until the connect + // completes. + // + + if ( endpoint->State == AfdEndpointStateConnected || + IS_DGRAM_ENDPOINT(endpoint) ) { + + // + // It should always be possible to do a nonblocking send + // on a datagram endpoint. For nonbufferring VC + // endpoints, check whether a blocking error has + // occurred. If so, it will not be possible to do a + // nonblocking send until a send possible indication + // arrives. + // + // For bufferring endpoints (TDI provider does not + // buffer), check whether we have too much send data + // outstanding. + // + + if ( IS_DGRAM_ENDPOINT(endpoint) + + || + + ( endpoint->TdiBufferring && + connection->VcNonBlockingSendPossible ) + + || + + ( !endpoint->TdiBufferring && + connection->VcBufferredSendBytes < + connection->MaxBufferredSendBytes && + connection->VcBufferredSendCount < + connection->MaxBufferredSendCount ) + + || + + connection->AbortIndicated ) { + + pollHandleInfo->Handle = pollEndpointInfo->Handle; + pollHandleInfo->PollEvents |= AFD_POLL_SEND; + found = TRUE; + + } + } + } + + if ( (pollEndpointInfo->PollEvents & AFD_POLL_ACCEPT) != 0 ) { + + if ( endpoint->Type == AfdBlockTypeVcListening && + !IsListEmpty( &endpoint->Common.VcListening.UnacceptedConnectionListHead ) ) { + pollHandleInfo->Handle = pollEndpointInfo->Handle; + pollHandleInfo->PollEvents |= AFD_POLL_ACCEPT; + found = TRUE; + } + } + + if ( (pollEndpointInfo->PollEvents & AFD_POLL_CONNECT) != 0 ) { + + // + // If the endpoint is now connected, complete this event. + // + + if ( endpoint->State == AfdEndpointStateConnected ) { + + ASSERT( NT_SUCCESS(endpoint->Common.VcConnecting.ConnectStatus) ); + pollHandleInfo->Handle = pollEndpointInfo->Handle; + pollHandleInfo->PollEvents |= AFD_POLL_CONNECT; + found = TRUE; + } + } + + if ( (pollEndpointInfo->PollEvents & AFD_POLL_CONNECT_FAIL) != 0 ) { + + // + // This is a poll to see whether a connect has failed + // recently. If the connect status indicates an error, + // then complete the poll. + // + + if ( endpoint->State == AfdEndpointStateBound && + !NT_SUCCESS(endpoint->Common.VcConnecting.ConnectStatus) ) { + + pollHandleInfo->Handle = pollEndpointInfo->Handle; + pollHandleInfo->PollEvents |= AFD_POLL_CONNECT_FAIL; + pollHandleInfo->Status = + endpoint->Common.VcConnecting.ConnectStatus; + found = TRUE; + } + } + + if ( (pollEndpointInfo->PollEvents & AFD_POLL_DISCONNECT) != 0 ) { + + if ( connection != NULL && connection->DisconnectIndicated ) { + pollHandleInfo->Handle = pollEndpointInfo->Handle; + pollHandleInfo->PollEvents |= AFD_POLL_DISCONNECT; + found = TRUE; + } + } + + if ( (pollEndpointInfo->PollEvents & AFD_POLL_ABORT) != 0 ) { + + if ( connection != NULL && connection->AbortIndicated ) { + pollHandleInfo->Handle = pollEndpointInfo->Handle; + pollHandleInfo->PollEvents |= AFD_POLL_ABORT; + found = TRUE; + } + } + + + // + // If the handle had a current event that was requested, update + // the count of handles in the output buffer and increment the + // pointer to the output buffer. + // + + if ( found ) { + pollInfo->NumberOfHandles++; + pollHandleInfo++; + } + + pollEndpointInfo++; + } + + // + // If we found any endpoints that are ready, free the poll information + // structure and complete the request. + // + + if ( pollInfo->NumberOfHandles > 0 ) { + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql2 ); + IoReleaseCancelSpinLock( oldIrql1 ); + AfdFreePollInfo( pollInfoInternal ); + + Irp->IoStatus.Information = (ULONG)pollHandleInfo - (ULONG)pollInfo; + status = STATUS_SUCCESS; + goto complete; + } + + // + // None of the endpoints are in the correct state. If a timeout was + // specified, place the poll information on the global list and set + // up a DPC and timer so that we know when to complete the IRP. + // + + if ( pollInfo->Timeout.LowPart != 0 && pollInfo->Timeout.HighPart != 0 ) { + + IF_DEBUG(POLL) { + KdPrint(( "AfdPoll: no current events for poll IRP %lx, " + "info %lx\n", Irp, pollInfoInternal )); + } + + // + // Set up the information field of the IO status block to indicate + // that an output buffer with no handles should be returned. + // AfdIndicatePollEvent will modify this if necessary. + // + + Irp->IoStatus.Information = (ULONG)pollHandleInfo - (ULONG)pollInfo; + + // + // Put a pointer to the internal poll info struct into the IRP + // so that the cancel routine can find it. + // + + IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = pollInfoInternal; + + // + // Place the internal poll info struct on the global list. + // + + InsertTailList( &AfdPollListHead, &pollInfoInternal->PollListEntry ); + + // + // If the timeout is infinite, then don't set up a timer and + // DPC. Otherwise, set up a timer so we can timeout the poll + // request if appropriate. + // + + if ( pollInfo->Timeout.HighPart != 0x7FFFFFFF ) { + + pollInfoInternal->TimerStarted = TRUE; + + KeInitializeDpc( + &pollInfoInternal->Dpc, + AfdTimeoutPoll, + pollInfoInternal + ); + + KeInitializeTimer( &pollInfoInternal->Timer ); + + KeSetTimer( + &pollInfoInternal->Timer, + pollInfo->Timeout, + &pollInfoInternal->Dpc + ); + + } else { + + pollInfoInternal->TimerStarted = FALSE; + } + + } else { + + // + // A timeout equal to 0 was specified; free the internal + // structure and complete the request with no endpoints in the + // output buffer. + // + + IF_DEBUG(POLL) { + KdPrint(( "AfdPoll: zero timeout on poll IRP %lx and no " + "current events--completing.\n", Irp )); + } + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql2 ); + IoReleaseCancelSpinLock( oldIrql1 ); + AfdFreePollInfo( pollInfoInternal ); + + Irp->IoStatus.Information = (ULONG)pollHandleInfo - (ULONG)pollInfo; + status = STATUS_SUCCESS; + goto complete; + } + + // + // Mark the IRP pending and release the spin locks. At this + // point the IRP may get completed or cancelled. + // + + IoMarkIrpPending( Irp ); + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql2 ); + IoReleaseCancelSpinLock( oldIrql1 ); + + // + // If we need to free a cancelled poll info structure, do it now. + // + + if ( freePollInfo != NULL ) { + AfdFreePollInfo( freePollInfo ); + } + + // + // Return pending. The IRP will be completed when an appropriate + // event is indicated by the TDI provider, when the timeout is hit, + // or when the IRP is cancelled. + // + + return STATUS_PENDING; + +complete: + + // + // If we need to free a cancelled poll info structure, do it now. + // + + if ( freePollInfo != NULL ) { + AfdFreePollInfo( freePollInfo ); + } + + Irp->IoStatus.Status = status; + + IoAcquireCancelSpinLock( &oldIrql1 ); + IoSetCancelRoutine( Irp, NULL ); + IoReleaseCancelSpinLock( oldIrql1 ); + + IoCompleteRequest( Irp, AfdPriorityBoost ); + + return status; + +} // AfdPoll + + +VOID +AfdCancelPoll ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +{ + PAFD_POLL_INFO_INTERNAL pollInfoInternal; + PLIST_ENTRY listEntry; + KIRQL oldIrql; + BOOLEAN found = FALSE; + BOOLEAN timerCancelSucceeded; + PIO_STACK_LOCATION irpSp; + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + pollInfoInternal = + (PAFD_POLL_INFO_INTERNAL)irpSp->Parameters.DeviceIoControl.Type3InputBuffer; + + IF_DEBUG(POLL) { + KdPrint(( "AfdCancelPoll called for IRP %lx\n", Irp )); + } + + // + // Just release the cancel spin lock--we don't need it for + // synchronization because of the mechanism we use below. + // + + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + // + // Get the AFD spin lock and attempt to find the poll structure on + // the list of outstanding polls. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + for ( listEntry = AfdPollListHead.Flink; + listEntry != &AfdPollListHead; + listEntry = listEntry->Flink ) { + + PAFD_POLL_INFO_INTERNAL testInfo; + + testInfo = CONTAINING_RECORD( + listEntry, + AFD_POLL_INFO_INTERNAL, + PollListEntry + ); + + if ( testInfo == pollInfoInternal ) { + found = TRUE; + break; + } + } + + // + // If we didn't find the poll structure on the list, then the + // indication handler got called prior to the spinlock acquisition + // above and it is already off the list. Just return and do + // nothing, as the indication handler completed the IRP. + // + + if ( !found ) { + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + IF_DEBUG(POLL) { + KdPrint(( "AfdCancelPoll: poll info %lx not found on list.\n", + pollInfoInternal )); + } + return; + } + + // + // Remove the poll structure from the global list. + // + + IF_DEBUG(POLL) { + KdPrint(( "AfdCancelPoll: poll info %lx found on list, completing.\n", + pollInfoInternal )); + } + + RemoveEntryList( &pollInfoInternal->PollListEntry ); + + // + // Cancel the timer and reset the IRP pointer in the internal + // poll information structure. NULLing the IRP field + // prevents the timer routine from completing the IRP. + // + + if ( pollInfoInternal->TimerStarted ) { + timerCancelSucceeded = KeCancelTimer( &pollInfoInternal->Timer ); + } else { + timerCancelSucceeded = TRUE; + } + + pollInfoInternal->Irp = NULL; + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + // + // Complete the IRP with STATUS_CANCELLED as the status. + // + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_CANCELLED; + + IoCompleteRequest( Irp, AfdPriorityBoost ); + + // + // Free the poll information structure if the cancel succeeded. If + // the cancel of the timer did not succeed, then the timer is + // already running and the timer DPC will free the internal + // poll info. + // + + if ( timerCancelSucceeded ) { + AfdFreePollInfo( pollInfoInternal ); + } + + return; + +} // AfdCancelPoll + + +VOID +AfdFreePollInfo ( + IN PAFD_POLL_INFO_INTERNAL PollInfoInternal + ) +{ + ULONG i; + PAFD_POLL_ENDPOINT_INFO pollEndpointInfo; + + IF_DEBUG(POLL) { + KdPrint(( "AfdFreePollInfo: freeing info struct at %lx\n", + PollInfoInternal )); + } + + // *** Note that this routine does not remove the poll information + // structure from the global list--that is the responsibility + // of the caller! + + // + // Walk the list of endpoints in the poll information structure and + // dereference each one. + // + + pollEndpointInfo = PollInfoInternal->EndpointInfo; + + for ( i = 0; i < PollInfoInternal->NumberOfEndpoints; i++ ) { + ASSERT( InterlockedDecrement( &pollEndpointInfo->Endpoint->ObReferenceBias ) >= 0 ); + + DEREFERENCE_ENDPOINT( pollEndpointInfo->Endpoint ); + ObDereferenceObject( pollEndpointInfo->FileObject ); + pollEndpointInfo++; + } + + // + // Free the structure itself and return. + // + + AFD_FREE_POOL( + PollInfoInternal, + AFD_POLL_POOL_TAG + ); + + return; + +} // AfdFreePollInfo + + +VOID +AfdIndicatePollEvent ( + IN PAFD_ENDPOINT Endpoint, + IN ULONG PollEventBit, + IN NTSTATUS Status + ) + +/*++ + +Routine Description: + + Called to complete polls with a specific event or events. + +Arguments: + + Endpoint - the endpoint on which the action occurred. + + PollEventBit - the event which occurred. Note that this is one of + the AFD_POLL_*_BIT values, and therefore specifies exactly one event. + + Status - the status of the event, if any. + +Return Value: + + None. + +--*/ + +{ + LIST_ENTRY completePollListHead; + PLIST_ENTRY listEntry; + KIRQL oldIrql; + PAFD_POLL_INFO_INTERNAL pollInfoInternal; + PAFD_POLL_INFO pollInfo; + PIRP irp; + PIO_STACK_LOCATION irpSp; + ULONG eventSelectEvent; + ULONG pollEvent; + + // + // Compute the actual poll event bitmask. + // + // Note that AFD_POLL_ABORT_BIT implies AFD_POLL_SEND_BIT. + // + + pollEvent = 1 << PollEventBit; + eventSelectEvent = pollEvent; + + if( PollEventBit == AFD_POLL_ABORT_BIT ) { + pollEvent |= AFD_POLL_SEND; + } + + // + // If we have never had a poll IRP on this endpoint, skip over the + // expensive looping below. + // + + if ( Endpoint->PollCalled ) { + + // + // Initialize the list of poll info structures that we'll be + // completing for this event. + // + + InitializeListHead( &completePollListHead ); + + // + // Walk the global list of polls, searching for any the are waiting + // for the specified event on the specified endpoint. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + for ( listEntry = AfdPollListHead.Flink; + listEntry != &AfdPollListHead; + listEntry = listEntry->Flink ) { + + PAFD_POLL_ENDPOINT_INFO pollEndpointInfo; + ULONG i; + ULONG foundCount = 0; + + pollInfoInternal = CONTAINING_RECORD( + listEntry, + AFD_POLL_INFO_INTERNAL, + PollListEntry + ); + + pollInfo = pollInfoInternal->Irp->AssociatedIrp.SystemBuffer; + + IF_DEBUG(POLL) { + KdPrint(( "AfdIndicatePollEvent: pollInfoInt %lx " + "IRP %lx pollInfo %lx event %lx status %lx\n", + pollInfoInternal, pollInfoInternal->Irp, pollInfo, + pollEvent, Status )); + } + + // + // Walk the poll structure looking for matching endpoints. + // + + pollEndpointInfo = pollInfoInternal->EndpointInfo; + + for ( i = 0; i < pollInfoInternal->NumberOfEndpoints; i++ ) { + + IF_DEBUG(POLL) { + KdPrint(( "AfdIndicatePollEvent: pollEndpointInfo = %lx, " + "comparing %lx, %lx\n", + pollEndpointInfo, pollEndpointInfo->Endpoint, + Endpoint )); + } + + // + // Regardless of whether the caller requested to be told about + // local closes, we'll complete the IRP if an endpoint + // is being closed. When they close an endpoint, all IO on + // the endpoint must be completed. + // + + if ( Endpoint == pollEndpointInfo->Endpoint && + ( (pollEvent & pollEndpointInfo->PollEvents) != 0 + || + PollEventBit == AFD_POLL_LOCAL_CLOSE_BIT ) ) { + + ASSERT( pollInfo->NumberOfHandles == foundCount ); + + IF_DEBUG(POLL) { + KdPrint(( "AfdIndicatePollEvent: endpoint %lx found " + " for event %lx\n", + pollEndpointInfo->Endpoint, pollEvent )); + } + + pollInfo->NumberOfHandles++; + + pollInfo->Handles[foundCount].Handle = pollEndpointInfo->Handle; + pollInfo->Handles[foundCount].PollEvents = + (pollEvent & + (pollEndpointInfo->PollEvents | AFD_POLL_LOCAL_CLOSE)); + pollInfo->Handles[foundCount].Status = Status; + + foundCount++; + } + + pollEndpointInfo++; + } + + // + // If we found any matching endpoints, remove the poll information + // structure from the global list, complete the IRP, and free the + // poll information structure. + // + + if ( foundCount != 0 ) { + + BOOLEAN timerCancelSucceeded; + + // + // We need to release the spin lock to call AfdFreePollInfo, + // since it calls AfdDereferenceEndpoint which in turn needs + // to acquire the spin lock, and recursive spin lock + // acquisitions result in deadlock. However, we can't + // release the lock of else the state of the poll list could + // change, e.g. the next entry could get freed. Remove + // this entry from the global list and place it on a local + // list. We'll complete all the poll IRPs after walking + // the entire list. + // + + RemoveEntryList( &pollInfoInternal->PollListEntry ); + + irp = pollInfoInternal->Irp; + irpSp = IoGetCurrentIrpStackLocation( irp ); + + InsertTailList( + &completePollListHead, + &irp->Tail.Overlay.ListEntry + ); + + // + // Cancel the timer on the poll so that it does not fire. + // + + if ( pollInfoInternal->TimerStarted ) { + timerCancelSucceeded = KeCancelTimer( &pollInfoInternal->Timer ); + } else { + timerCancelSucceeded = TRUE; + } + + // + // If the cancel of the timer failed, then we don't want to + // free this structure since the timer routine is running. + // Let the timer routine free the structure. + // + + if ( timerCancelSucceeded ) { + irpSp->Parameters.DeviceIoControl.IoControlCode = + (ULONG)pollInfoInternal; + } else { + irpSp->Parameters.DeviceIoControl.IoControlCode = (ULONG)NULL; + } + + // + // Also reset the IRP field of the internal poll info + // structure so that the timer routine will not attempt to + // complete the IRP. + // + + pollInfoInternal->Irp = NULL; + + // + // Set up the IRP for completion now, since we have all needed + // information here. + // + + irp->IoStatus.Information = + (ULONG)&pollInfo->Handles[foundCount] - (ULONG)pollInfo; + + irp->IoStatus.Status = STATUS_SUCCESS; + } + } + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + // + // Now walk the list of polls we need to actually complete. Free + // the poll info structures as we go. + // + + while ( !IsListEmpty( &completePollListHead ) ) { + + listEntry = RemoveHeadList( &completePollListHead ); + ASSERT( listEntry != &completePollListHead ); + + irp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + irpSp = IoGetCurrentIrpStackLocation( irp ); + + pollInfoInternal = + (PAFD_POLL_INFO_INTERNAL)irpSp->Parameters.DeviceIoControl.IoControlCode; + + IoAcquireCancelSpinLock( &oldIrql ); + IoSetCancelRoutine( irp, NULL ); + IoReleaseCancelSpinLock( oldIrql ); + + IoCompleteRequest( irp, AfdPriorityBoost ); + + // + // Free the poll info structure, if necessary. + // + + if ( pollInfoInternal != NULL ) { + AfdFreePollInfo( pollInfoInternal ); + } + } + } + + // + // Acquire the lock protecting the endpoint. + // + + AfdAcquireSpinLock( &Endpoint->SpinLock, &oldIrql ); + + // + // Signal the associated event object. + // + + AfdIndicateEventSelectEvent( + Endpoint, + PollEventBit, + Status + ); + + AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql ); + + return; + +} // AfdIndicatePollEvent + + +VOID +AfdTimeoutPoll ( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) +{ + PAFD_POLL_INFO_INTERNAL pollInfoInternal = DeferredContext; + PIRP irp; + PLIST_ENTRY listEntry; + KIRQL oldIrql; + BOOLEAN found = FALSE; + + // + // Get the AFD spin lock and attempt to find the poll structure on + // the list of outstanding polls. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + for ( listEntry = AfdPollListHead.Flink; + listEntry != &AfdPollListHead; + listEntry = listEntry->Flink ) { + + PAFD_POLL_INFO_INTERNAL testInfo; + + testInfo = CONTAINING_RECORD( + listEntry, + AFD_POLL_INFO_INTERNAL, + PollListEntry + ); + + if ( testInfo == pollInfoInternal ) { + found = TRUE; + break; + } + } + + ASSERT( pollInfoInternal->TimerStarted ); + + // + // If we didn't find the poll structure on the list, then the + // indication handler got called prior to the spinlock acquisition + // above and it is already off the list. Just return and do + // nothing, as the indication handler completed the IRP. + // + // We must free the internal information structure in this case, + // since the indication handler will not free it. The indication + // handler cannot free the structure because the structure contains + // the timer object, which must remain intact until this routine + // is entered. + // + + if ( !found ) { + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + IF_DEBUG(POLL) { + KdPrint(( "AfdTimeoutPoll: poll info %lx not found on list.\n", + pollInfoInternal )); + } + ASSERT( pollInfoInternal->Irp == NULL ); + AfdFreePollInfo( pollInfoInternal ); + return; + } + + // + // The IRP should not have been completed at this point. + // + + ASSERT( pollInfoInternal->Irp != NULL ); + irp = pollInfoInternal->Irp; + + // + // Remove the poll structure from the global list. + // + + IF_DEBUG(POLL) { + KdPrint(( "AfdTimeoutPoll: poll info %lx found on list, completing.\n", + pollInfoInternal )); + } + + RemoveEntryList( &pollInfoInternal->PollListEntry ); + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + // + // Complete the IRP pointed to in the poll structure. The + // Information field has already been set up by AfdPoll, as well as + // the output buffer. + // + + IoAcquireCancelSpinLock( &oldIrql ); + IoSetCancelRoutine( irp, NULL ); + IoReleaseCancelSpinLock( oldIrql ); + + IoCompleteRequest( irp, AfdPriorityBoost ); + + // + // Free the poll information structure. + // + + AfdFreePollInfo( pollInfoInternal ); + + return; + +} // AfdTimeoutPoll + diff --git a/private/ntos/afd/receive.c b/private/ntos/afd/receive.c new file mode 100644 index 000000000..83e918430 --- /dev/null +++ b/private/ntos/afd/receive.c @@ -0,0 +1,1245 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + receive.c + +Abstract: + + This module contains the code for passing on receive IRPs to + TDI providers. + +Author: + + David Treadwell (davidtr) 13-Mar-1992 + +Revision History: + +--*/ + +#include "afdp.h" + +NTSTATUS +AfdRestartReceive ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGEAFD, AfdReceive ) +#pragma alloc_text( PAGEAFD, AfdRestartReceive ) +#pragma alloc_text( PAGEAFD, AfdReceiveEventHandler ) +#pragma alloc_text( PAGEAFD, AfdReceiveExpeditedEventHandler ) +#pragma alloc_text( PAGEAFD, AfdQueryReceiveInformation ) +#endif + + +NTSTATUS +AfdReceive ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) +{ + NTSTATUS status; + KIRQL oldIrql; + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + PTDI_REQUEST_RECEIVE receiveRequest; + BOOLEAN allocatedReceiveRequest = FALSE; + BOOLEAN peek; + LARGE_INTEGER bytesExpected; + BOOLEAN isDataOnConnection; + BOOLEAN isExpeditedDataOnConnection; + PAFD_RECV_INFO recvInfo; + ULONG recvFlags; + ULONG afdFlags; + ULONG recvLength; + + // + // Make sure that the endpoint is in the correct state. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + if ( endpoint->State != AfdEndpointStateConnected ) { + status = STATUS_INVALID_CONNECTION; + goto complete; + } + + // + // If receive has been shut down or the endpoint aborted, fail. + // + + if ( (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_RECEIVE) ) { + status = STATUS_PIPE_DISCONNECTED; + goto complete; + } + + if ( (endpoint->DisconnectMode & AFD_ABORTIVE_DISCONNECT) ) { + status = STATUS_LOCAL_DISCONNECT; + goto complete; + } + + // + // If this is an IOCTL_AFD_RECEIVE, then grab the parameters from the + // supplied AFD_RECV_INFO structure, build an MDL chain describing + // the WSABUF array, and attach the MDL chain to the IRP. + // + // If this is an IRP_MJ_READ IRP, just grab the length from the IRP + // and set the flags to zero. + // + + if ( IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL ) { + + // + // Sanity check. + // + + ASSERT( IrpSp->Parameters.DeviceIoControl.IoControlCode == + IOCTL_AFD_RECEIVE ); + + if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength >= + sizeof(*recvInfo) ) { + + try { + + // + // Probe the input structure. + // + + recvInfo = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; + + if( Irp->RequestorMode != KernelMode ) { + + ProbeForRead( + recvInfo, + sizeof(*recvInfo), + sizeof(ULONG) + ); + + } + + // + // Snag the receive flags. + // + + recvFlags = recvInfo->TdiFlags; + afdFlags = recvInfo->AfdFlags; + + // + // Validate the receive flags & WSABUF parameters. + // Note that either TDI_RECEIVE_NORMAL or + // TDI_RECEIVE_EXPEDITED (but not both) must be set + // in the receive flags. + // + + if ( ( recvFlags & TDI_RECEIVE_EITHER ) != 0 && + ( recvFlags & TDI_RECEIVE_EITHER ) != TDI_RECEIVE_EITHER && + recvInfo->BufferArray != NULL && + recvInfo->BufferCount > 0 ) { + + // + // Create the MDL chain describing the WSABUF array. + // + + status = AfdAllocateMdlChain( + Irp, + recvInfo->BufferArray, + recvInfo->BufferCount, + IoWriteAccess, + &recvLength + ); + + } else { + + // + // Invalid receive flags, BufferArray, or + // BufferCount fields. + // + + status = STATUS_INVALID_PARAMETER; + + } + + } except ( EXCEPTION_EXECUTE_HANDLER ) { + + // + // Exception accessing input structure. + // + + status = GetExceptionCode(); + + } + + } else { + + // + // Invalid input buffer length. + // + + status = STATUS_INVALID_PARAMETER; + + } + + if( !NT_SUCCESS(status) ) { + + goto complete; + + } + + } else { + + ASSERT( IrpSp->MajorFunction == IRP_MJ_READ ); + + recvFlags = TDI_RECEIVE_NORMAL; + afdFlags = AFD_OVERLAPPED; + recvLength = IrpSp->Parameters.Read.Length; + + // + // Convert this stack location to a proper one for a receive + // request. + // + + IrpSp->Parameters.DeviceIoControl.OutputBufferLength = + IrpSp->Parameters.Read.Length; + IrpSp->Parameters.DeviceIoControl.InputBufferLength = + sizeof(*receiveRequest); + + } + + // + // If this is a datagram endpoint, format up a receive datagram request + // and pass it on to the TDI provider. + // + + if ( IS_DGRAM_ENDPOINT(endpoint) ) { + return AfdReceiveDatagram( Irp, IrpSp, recvFlags, afdFlags ); + } + + // + // If this is an endpoint on a nonbufferring transport, use another + // routine to handle the request. + // + + if ( !endpoint->TdiBufferring ) { + return AfdBReceive( Irp, IrpSp, recvFlags, afdFlags, recvLength ); + } + + // + // Allocate a buffer for the receive request structure. + // + + receiveRequest = AFD_ALLOCATE_POOL( + NonPagedPool, + sizeof(TDI_REQUEST_RECEIVE), + AFD_TDI_POOL_TAG + ); + + if ( receiveRequest == NULL ) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto complete; + } + + allocatedReceiveRequest = TRUE; + + // + // Set up the receive request structure. + // + + RtlZeroMemory( + receiveRequest, + sizeof(*receiveRequest) + ); + + receiveRequest->ReceiveFlags = (USHORT)recvFlags; + + connection = endpoint->Common.VcConnecting.Connection; + ASSERT( connection != NULL ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + // + // If this endpoint is set up for inline reception of expedited data, + // change the receive flags to use either normal or expedited data. + // + + if ( endpoint->InLine ) { + receiveRequest->ReceiveFlags |= TDI_RECEIVE_EITHER; + } + + // + // Determine whether this is a request to just peek at the data. + // + + peek = (BOOLEAN)( (receiveRequest->ReceiveFlags & TDI_RECEIVE_PEEK) != 0 ); + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + if ( endpoint->NonBlocking ) { + isDataOnConnection = IS_DATA_ON_CONNECTION( connection ); + isExpeditedDataOnConnection = IS_EXPEDITED_DATA_ON_CONNECTION( connection ); + } + + if ( endpoint->InLine ) { + + // + // If the endpoint is nonblocking, check whether the receive can + // be performed immediately. Note that if the endpoint is set + // up for inline reception of expedited data we don't fail just + // yet--there may be expedited data available to be read. + // + + if ( endpoint->NonBlocking && !( afdFlags & AFD_OVERLAPPED ) ) { + + if ( !isDataOnConnection && + !isExpeditedDataOnConnection && + !connection->AbortIndicated && + !connection->DisconnectIndicated ) { + + IF_DEBUG(RECEIVE) { + KdPrint(( "AfdReceive: failing nonblocking IL receive, ind %ld, " + "taken %ld, out %ld\n", + connection->Common.Bufferring.ReceiveBytesIndicated.LowPart, + connection->Common.Bufferring.ReceiveBytesTaken.LowPart, + connection->Common.Bufferring.ReceiveBytesOutstanding.LowPart )); + KdPrint(( " EXP ind %ld, taken %ld, out %ld\n", + connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.LowPart, + connection->Common.Bufferring.ReceiveExpeditedBytesTaken.LowPart, + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.LowPart )); + } + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + status = STATUS_DEVICE_NOT_READY; + goto complete; + } + } + + // + // If this is a nonblocking endpoint for a message-oriented + // transport, limit the number of bytes that can be received to the + // amount that has been indicated. This prevents the receive + // from blocking in the case where only part of a message has been + // received. + // + + if ( endpoint->EndpointType != AfdEndpointTypeStream && + endpoint->NonBlocking ) { + + LARGE_INTEGER expBytesExpected; + + bytesExpected.QuadPart = + connection->Common.Bufferring.ReceiveBytesIndicated.QuadPart - + (connection->Common.Bufferring.ReceiveBytesTaken.QuadPart + + connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart); + ASSERT( bytesExpected.HighPart == 0 ); + + expBytesExpected.QuadPart = + connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.QuadPart - + (connection->Common.Bufferring.ReceiveExpeditedBytesTaken.QuadPart + + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart); + ASSERT( expBytesExpected.HighPart == 0 ); + + IF_DEBUG(RECEIVE) { + KdPrint(( "AfdReceive: %lx normal bytes expected, %ld exp bytes expected", + bytesExpected.LowPart, expBytesExpected.LowPart )); + } + + // + // If expedited data exists on the connection, use the lower + // count between the available expedited and normal receive + // data. + // + + if ( (isExpeditedDataOnConnection && + bytesExpected.LowPart > expBytesExpected.LowPart) || + !isDataOnConnection ) { + bytesExpected = expBytesExpected; + } + + // + // If the request is for more bytes than are available, cut back + // the number of bytes requested to what we know is actually + // available. + // + + if ( recvLength > bytesExpected.LowPart ) { + recvLength = bytesExpected.LowPart; + } + } + + // + // Increment the count of posted receive bytes outstanding. + // This count is used for polling and nonblocking receives. + // Note that we do not increment this count if this is only + // a PEEK receive, since peeks do not actually take any data + // they should not affect whether data is available to be read + // on the endpoint. + // + + IF_DEBUG(RECEIVE) { + KdPrint(( "AfdReceive: conn %lx for %ld bytes, ind %ld, " + "taken %ld, out %ld %s\n", + connection, + recvLength, + connection->Common.Bufferring.ReceiveBytesIndicated.LowPart, + connection->Common.Bufferring.ReceiveBytesTaken.LowPart, + connection->Common.Bufferring.ReceiveBytesOutstanding.LowPart, + peek ? "PEEK" : "" )); + KdPrint(( " EXP ind %ld, taken %ld, out %ld\n", + connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.LowPart, + connection->Common.Bufferring.ReceiveExpeditedBytesTaken.LowPart, + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.LowPart )); + } + + if ( !peek ) { + + connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart = + connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart + + recvLength; + + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart = + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart + + recvLength; + } + } + + if ( !endpoint->InLine && + (receiveRequest->ReceiveFlags & TDI_RECEIVE_NORMAL) != 0 ) { + + // + // If the endpoint is nonblocking, check whether the receive can + // be performed immediately. + // + + if ( endpoint->NonBlocking && !( afdFlags & AFD_OVERLAPPED ) ) { + + if ( !isDataOnConnection && + !connection->AbortIndicated && + !connection->DisconnectIndicated ) { + + IF_DEBUG(RECEIVE) { + KdPrint(( "AfdReceive: failing nonblocking receive, ind %ld, " + "taken %ld, out %ld\n", + connection->Common.Bufferring.ReceiveBytesIndicated.LowPart, + connection->Common.Bufferring.ReceiveBytesTaken.LowPart, + connection->Common.Bufferring.ReceiveBytesOutstanding.LowPart )); + } + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + status = STATUS_DEVICE_NOT_READY; + goto complete; + } + } + + // + // If this is a nonblocking endpoint for a message-oriented + // transport, limit the number of bytes that can be received to the + // amount that has been indicated. This prevents the receive + // from blocking in the case where only part of a message has been + // received. + // + + if ( endpoint->EndpointType != AfdEndpointTypeStream && + endpoint->NonBlocking ) { + + bytesExpected.QuadPart = + connection->Common.Bufferring.ReceiveBytesIndicated.QuadPart - + (connection->Common.Bufferring.ReceiveBytesTaken.QuadPart + + connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart); + + ASSERT( bytesExpected.HighPart == 0 ); + + // + // If the request is for more bytes than are available, cut back + // the number of bytes requested to what we know is actually + // available. + // + + if ( recvLength > bytesExpected.LowPart ) { + recvLength = bytesExpected.LowPart; + } + } + + // + // Increment the count of posted receive bytes outstanding. + // This count is used for polling and nonblocking receives. + // Note that we do not increment this count if this is only + // a PEEK receive, since peeks do not actually take any data + // they should not affect whether data is available to be read + // on the endpoint. + // + + IF_DEBUG(RECEIVE) { + KdPrint(( "AfdReceive: conn %lx for %ld bytes, ind %ld, " + "taken %ld, out %ld %s\n", + connection, + recvLength, + connection->Common.Bufferring.ReceiveBytesIndicated.LowPart, + connection->Common.Bufferring.ReceiveBytesTaken.LowPart, + connection->Common.Bufferring.ReceiveBytesOutstanding.LowPart, + peek ? "PEEK" : "" )); + } + + if ( !peek ) { + + connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart = + connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart + + recvLength; + } + } + + if ( !endpoint->InLine && + (receiveRequest->ReceiveFlags & TDI_RECEIVE_EXPEDITED) != 0 ) { + + if ( endpoint->NonBlocking && !( afdFlags & AFD_OVERLAPPED ) && + !isExpeditedDataOnConnection && + !connection->AbortIndicated && + !connection->DisconnectIndicated ) { + + IF_DEBUG(RECEIVE) { + KdPrint(( "AfdReceive: failing nonblocking EXP receive, ind %ld, " + "taken %ld, out %ld\n", + connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.LowPart, + connection->Common.Bufferring.ReceiveExpeditedBytesTaken.LowPart, + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.LowPart )); + } + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + status = STATUS_DEVICE_NOT_READY; + goto complete; + } + + // + // If this is a nonblocking endpoint for a message-oriented + // transport, limit the number of bytes that can be received to the + // amount that has been indicated. This prevents the receive + // from blocking in the case where only part of a message has been + // received. + // + + if ( endpoint->EndpointType != AfdEndpointTypeStream && + endpoint->NonBlocking && + IS_EXPEDITED_DATA_ON_CONNECTION( connection ) ) { + + bytesExpected.QuadPart = + connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.QuadPart - + (connection->Common.Bufferring.ReceiveExpeditedBytesTaken.QuadPart + + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart); + + ASSERT( bytesExpected.HighPart == 0 ); + ASSERT( bytesExpected.LowPart != 0 ); + + // + // If the request is for more bytes than are available, cut back + // the number of bytes requested to what we know is actually + // available. + // + + if ( recvLength > bytesExpected.LowPart ) { + recvLength = bytesExpected.LowPart; + } + } + + // + // Increment the count of posted expedited receive bytes + // outstanding. This count is used for polling and nonblocking + // receives. Note that we do not increment this count if this + // is only a PEEK receive. + // + + IF_DEBUG(RECEIVE) { + KdPrint(( "AfdReceive: conn %lx for %ld bytes, ind %ld, " + "taken %ld, out %ld EXP %s\n", + connection, + recvLength, + connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.LowPart, + connection->Common.Bufferring.ReceiveExpeditedBytesTaken.LowPart, + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.LowPart, + peek ? "PEEK" : "" )); + } + + if ( !peek ) { + + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart = + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart + + recvLength; + } + } + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + // + // Build the TDI receive request. + // + + TdiBuildReceive( + Irp, + connection->DeviceObject, + connection->FileObject, + AfdRestartReceive, + endpoint, + Irp->MdlAddress, + receiveRequest->ReceiveFlags, + recvLength + ); + + // + // Save a pointer to the receive request structure so that we + // can free it in our restart routine. + // + + IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = receiveRequest; + IrpSp->Parameters.DeviceIoControl.OutputBufferLength = recvLength; + + + // + // Call the transport to actually perform the connect operation. + // + + return AfdIoCallDriver( endpoint, connection->DeviceObject, Irp ); + +complete: + + if ( allocatedReceiveRequest ) { + AFD_FREE_POOL( + receiveRequest, + AFD_TDI_POOL_TAG + ); + } + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + return status; + +} // AfdReceive + + +NTSTATUS +AfdRestartReceive ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + PAFD_ENDPOINT endpoint = Context; + PAFD_CONNECTION connection; + PIO_STACK_LOCATION irpSp; + LARGE_INTEGER actualBytes; + LARGE_INTEGER requestedBytes; + KIRQL oldIrql1, oldIrql2; + ULONG receiveFlags; + ULONG eventMask; + BOOLEAN expedited; + PTDI_REQUEST_RECEIVE receiveRequest; + + ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ); + ASSERT( endpoint->Common.VcConnecting.Connection != NULL ); + ASSERT( endpoint->TdiBufferring ); + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + actualBytes = RtlConvertUlongToLargeInteger( Irp->IoStatus.Information ); + requestedBytes = RtlConvertUlongToLargeInteger( + irpSp->Parameters.DeviceIoControl.OutputBufferLength + ); + + // + // Determine whether we received normal or expedited data. + // + + receiveRequest = irpSp->Parameters.DeviceIoControl.Type3InputBuffer; + receiveFlags = receiveRequest->ReceiveFlags; + + if ( Irp->IoStatus.Status == STATUS_RECEIVE_EXPEDITED || + Irp->IoStatus.Status == STATUS_RECEIVE_PARTIAL_EXPEDITED ) { + expedited = TRUE; + } else { + expedited = FALSE; + } + + // + // Free the receive request structure. + // + + AFD_FREE_POOL( + receiveRequest, + AFD_TDI_POOL_TAG + ); + + // + // If this was a PEEK receive, don't update the counts of received + // data, just return. + // + + if ( (receiveFlags & TDI_RECEIVE_PEEK) != 0 ) { + + IF_DEBUG(RECEIVE) { + KdPrint(( "AfdRestartReceive: IRP %lx, endpoint %lx, conn %lx, " + "status %X\n", + Irp, endpoint, endpoint->Common.VcConnecting.Connection, + Irp->IoStatus.Status )); + KdPrint(( " %s data, PEEKed only.\n", + expedited ? "expedited" : "normal" )); + } + + AfdCompleteOutstandingIrp( endpoint, Irp ); + return STATUS_SUCCESS; + } + + // + // Update the count of bytes actually received on the connection. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql1 ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql2 ); + + if( expedited ) { + eventMask = endpoint->InLine + ? (ULONG)~AFD_POLL_RECEIVE + : (ULONG)~AFD_POLL_RECEIVE_EXPEDITED; + } else { + eventMask = (ULONG)~AFD_POLL_RECEIVE; + } + + endpoint->EventsActive &= eventMask; + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdReceive: Endp %08lX, Active %08lX\n", + endpoint, + endpoint->EventsActive + )); + } + + connection = endpoint->Common.VcConnecting.Connection; + ASSERT( connection->Type == AfdBlockTypeConnection ); + + if ( !expedited ) { + + if ( actualBytes.LowPart == 0 ) { + ASSERT( actualBytes.HighPart == 0 ); + connection->VcZeroByteReceiveIndicated = FALSE; + } else { + connection->Common.Bufferring.ReceiveBytesTaken.QuadPart = + actualBytes.QuadPart + + connection->Common.Bufferring.ReceiveBytesTaken.QuadPart; + } + + // + // If the number taken exceeds the number indicated, then this + // receive got some unindicated bytes because the receive was + // posted when the indication arrived. If this is the case, set + // the amount indicated equal to the amount received. + // + + if ( connection->Common.Bufferring.ReceiveBytesTaken.QuadPart > + connection->Common.Bufferring.ReceiveBytesIndicated.QuadPart ) { + + connection->Common.Bufferring.ReceiveBytesIndicated = + connection->Common.Bufferring.ReceiveBytesTaken; + } + + // + // Decrement the count of outstanding receive bytes on this connection + // by the receive size that was requested. + // + + connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart = + connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart - + requestedBytes.QuadPart; + + // + // If the endpoint is inline, decrement the count of outstanding + // expedited bytes. + // + + if ( endpoint->InLine ) { + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart = + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart - + requestedBytes.QuadPart; + } + + if( connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart > 0 || + ( endpoint->InLine && + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart > 0 ) ) { + + AfdIndicateEventSelectEvent( + endpoint, + AFD_POLL_RECEIVE_BIT, + STATUS_SUCCESS + ); + + } + + IF_DEBUG(RECEIVE) { + KdPrint(( "AfdRestartReceive: IRP %lx, endpoint %lx, conn %lx, " + "status %X\n", + Irp, endpoint, connection, + Irp->IoStatus.Status )); + KdPrint(( " req. bytes %ld, actual %ld, ind %ld, " + " taken %ld, out %ld\n", + requestedBytes.LowPart, actualBytes.LowPart, + connection->Common.Bufferring.ReceiveBytesIndicated.LowPart, + connection->Common.Bufferring.ReceiveBytesTaken.LowPart, + connection->Common.Bufferring.ReceiveBytesOutstanding.LowPart + )); + } + + } else { + + connection->Common.Bufferring.ReceiveExpeditedBytesTaken.QuadPart = + actualBytes.QuadPart + + connection->Common.Bufferring.ReceiveExpeditedBytesTaken.QuadPart; + + // + // If the number taken exceeds the number indicated, then this + // receive got some unindicated bytes because the receive was + // posted when the indication arrived. If this is the case, set + // the amount indicated equal to the amount received. + // + + if ( connection->Common.Bufferring.ReceiveExpeditedBytesTaken.QuadPart > + connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.QuadPart ) { + + connection->Common.Bufferring.ReceiveExpeditedBytesIndicated = + connection->Common.Bufferring.ReceiveExpeditedBytesTaken; + } + + // + // Decrement the count of outstanding receive bytes on this connection + // by the receive size that was requested. + // + + ASSERT( connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.LowPart > 0 || + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.HighPart > 0 || + requestedBytes.LowPart == 0 ); + + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart = + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart - + requestedBytes.QuadPart; + + // + // If the endpoint is inline, decrement the count of outstanding + // normal bytes. + // + + if ( endpoint->InLine ) { + + connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart = + connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart - + requestedBytes.QuadPart; + + if( connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart > 0 || + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart > 0 ) { + + AfdIndicateEventSelectEvent( + endpoint, + AFD_POLL_RECEIVE_BIT, + STATUS_SUCCESS + ); + + } + + } else { + + if( connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart > 0 ) { + + AfdIndicateEventSelectEvent( + endpoint, + AFD_POLL_RECEIVE_EXPEDITED_BIT, + STATUS_SUCCESS + ); + + } + + } + + IF_DEBUG(RECEIVE) { + KdPrint(( "AfdRestartReceive: (exp) IRP %lx, endpoint %lx, conn %lx, " + "status %X\n", + Irp, endpoint, connection, + Irp->IoStatus.Status )); + KdPrint(( " req. bytes %ld, actual %ld, ind %ld, " + " taken %ld, out %ld\n", + requestedBytes.LowPart, actualBytes.LowPart, + connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.LowPart, + connection->Common.Bufferring.ReceiveExpeditedBytesTaken.LowPart, + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.LowPart + )); + } + + } + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); + AfdReleaseSpinLock( &AfdSpinLock, oldIrql1 ); + + AfdCompleteOutstandingIrp( endpoint, Irp ); + + // + // If pending has be returned for this irp then mark the current + // stack as pending. + // + + if ( Irp->PendingReturned ) { + IoMarkIrpPending(Irp); + } + + return STATUS_SUCCESS; + +} // AfdRestartReceive + + +NTSTATUS +AfdReceiveEventHandler ( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *BytesTaken, + IN PVOID Tsdu, + OUT PIRP *IoRequestPacket + ) +{ + PAFD_CONNECTION connection; + PAFD_ENDPOINT endpoint; + KIRQL oldIrql; + + connection = (PAFD_CONNECTION)ConnectionContext; + ASSERT( connection != NULL ); + + endpoint = connection->Endpoint; + ASSERT( endpoint != NULL ); + + ASSERT( connection->Type == AfdBlockTypeConnection ); + ASSERT( endpoint->Type == AfdBlockTypeVcConnecting || + endpoint->Type == AfdBlockTypeVcListening ); + ASSERT( !connection->DisconnectIndicated ); + ASSERT( !connection->AbortIndicated ); + ASSERT( endpoint->TdiBufferring ); + + // + // Bump the count of bytes indicated on the connection to account for + // the bytes indicated by this event. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + if ( BytesAvailable == 0 ) { + + connection->VcZeroByteReceiveIndicated = TRUE; + + } else { + + connection->Common.Bufferring.ReceiveBytesIndicated.QuadPart = + connection->Common.Bufferring.ReceiveBytesIndicated.QuadPart + + BytesAvailable; + } + + IF_DEBUG(RECEIVE) { + KdPrint(( "AfdReceiveEventHandler: conn %lx, bytes %ld, " + "ind %ld, taken %ld, out %ld\n", + connection, BytesAvailable, + connection->Common.Bufferring.ReceiveBytesIndicated.LowPart, + connection->Common.Bufferring.ReceiveBytesTaken.LowPart, + connection->Common.Bufferring.ReceiveBytesOutstanding.LowPart )); + } + + // + // If the receive side of the endpoint has been shut down, tell the + // provider that we took all the data and reset the connection. + // Also, account for these bytes in our count of bytes taken from + // the transport. + // + + if ( (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_RECEIVE) != 0 ) { + +#if DBG + DbgPrint( "AfdReceiveEventHandler: receive shutdown, " + "%ld bytes, aborting endp %lx\n", + BytesAvailable, endpoint ); +#endif + + connection->Common.Bufferring.ReceiveBytesTaken.QuadPart = + connection->Common.Bufferring.ReceiveBytesTaken.QuadPart + + BytesAvailable; + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + *BytesTaken = BytesAvailable; + + // + // Abort the connection. Note that if the abort attempt fails + // we can't do anything about it. + // + + (VOID)AfdBeginAbort( connection ); + + return STATUS_SUCCESS; + + } else { + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + // + // Note to the TDI provider that we didn't take any of the data here. + // + // !!! needs bufferring for non-bufferring transports! + + *BytesTaken = 0; + + // + // If there are any outstanding poll IRPs for this endpoint/ + // event, complete them. + // + + AfdIndicatePollEvent( + endpoint, + AFD_POLL_RECEIVE_BIT, + STATUS_SUCCESS + ); + + return STATUS_DATA_NOT_ACCEPTED; + } + +} // AfdReceiveEventHandler + + +NTSTATUS +AfdReceiveExpeditedEventHandler ( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *BytesTaken, + IN PVOID Tsdu, + OUT PIRP *IoRequestPacket + ) +{ + PAFD_CONNECTION connection; + PAFD_ENDPOINT endpoint; + KIRQL oldIrql; + + connection = (PAFD_CONNECTION)ConnectionContext; + ASSERT( connection != NULL ); + + endpoint = connection->Endpoint; + ASSERT( endpoint != NULL ); + + ASSERT( connection->Type == AfdBlockTypeConnection ); + + // + // Bump the count of bytes indicated on the connection to account for + // the expedited bytes indicated by this event. + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.QuadPart = + connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.QuadPart + + BytesAvailable; + + IF_DEBUG(RECEIVE) { + KdPrint(( "AfdReceiveExpeditedEventHandler: conn %lx, bytes %ld, " + "ind %ld, taken %ld, out %ld, offset %ld\n", + connection, BytesAvailable, + connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.LowPart, + connection->Common.Bufferring.ReceiveExpeditedBytesTaken.LowPart, + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.LowPart )); + } + + // + // If the receive side of the endpoint has been shut down, tell + // the provider that we took all the data. Also, account for these + // bytes in our count of bytes taken from the transport. + // + // + + if ( (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_RECEIVE) != 0 ) { + + IF_DEBUG(RECEIVE) { + KdPrint(( "AfdReceiveExpeditedEventHandler: receive shutdown, " + "%ld bytes dropped.\n", BytesAvailable )); + } + + connection->Common.Bufferring.ReceiveExpeditedBytesTaken.QuadPart = + connection->Common.Bufferring.ReceiveExpeditedBytesTaken.QuadPart + + BytesAvailable; + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + *BytesTaken = BytesAvailable; + + // + // Abort the connection. Note that if the abort attempt fails + // we can't do anything about it. + // + + (VOID)AfdBeginAbort( connection ); + + } else { + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + // + // Note to the TDI provider that we didn't take any of the data here. + // + // !!! needs bufferring for non-bufferring transports! + + *BytesTaken = 0; + + // + // If there are any outstanding poll IRPs for this endpoint/ + // event, complete them. Indicate this data as normal data if + // this endpoint is set up for inline reception of expedited + // data. + // + + AfdIndicatePollEvent( + endpoint, + endpoint->InLine + ? AFD_POLL_RECEIVE_BIT + : AFD_POLL_RECEIVE_EXPEDITED_BIT, + STATUS_SUCCESS + ); + } + + return STATUS_DATA_NOT_ACCEPTED; + +} // AfdReceiveExpeditedEventHandler + + +NTSTATUS +AfdQueryReceiveInformation ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) +{ + PAFD_RECEIVE_INFORMATION receiveInformation; + PAFD_ENDPOINT endpoint; + KIRQL oldIrql; + LARGE_INTEGER result; + PAFD_CONNECTION connection; + + // + // Make sure that the output buffer is large enough. + // + + if ( IrpSp->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(AFD_RECEIVE_INFORMATION) ) { + return STATUS_BUFFER_TOO_SMALL; + } + + // + // If this endpoint has a connection block, use the connection block's + // information, else use the information from the endpoint itself. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + receiveInformation = Irp->AssociatedIrp.SystemBuffer; + + if ( endpoint->TdiBufferring ) { + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + } else { + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + } + + connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint ); + + if ( connection != NULL ) { + + ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + if ( !endpoint->TdiBufferring ) { + + receiveInformation->BytesAvailable = + connection->VcBufferredReceiveBytes; + receiveInformation->ExpeditedBytesAvailable = + connection->VcBufferredExpeditedBytes; + + } else { + + // + // Determine the number of bytes available to be read. + // + + result.QuadPart = + connection->Common.Bufferring.ReceiveBytesIndicated.QuadPart - + (connection->Common.Bufferring.ReceiveBytesTaken.QuadPart + + connection->Common.Bufferring.ReceiveBytesOutstanding.QuadPart); + + ASSERT( result.HighPart == 0 ); + + receiveInformation->BytesAvailable = result.LowPart; + + // + // Determine the number of expedited bytes available to be read. + // + + result.QuadPart = + connection->Common.Bufferring.ReceiveExpeditedBytesIndicated.QuadPart - + (connection->Common.Bufferring.ReceiveExpeditedBytesTaken.QuadPart + + connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding.QuadPart); + + ASSERT( result.HighPart == 0 ); + + receiveInformation->ExpeditedBytesAvailable = result.LowPart; + } + + } else { + + // + // Determine the number of bytes available to be read. + // + + if ( IS_DGRAM_ENDPOINT(endpoint) ) { + + // + // Return the amount of bytes of datagrams that are + // bufferred on the endpoint. + // + + receiveInformation->BytesAvailable = endpoint->BufferredDatagramBytes; + + } else { + + // + // This is an unconnected endpoint, hence no bytes are + // available to be read. + // + + receiveInformation->BytesAvailable = 0; + } + + // + // Whether this is a datagram endpoint or just unconnected, + // there are no expedited bytes available. + // + + receiveInformation->ExpeditedBytesAvailable = 0; + } + + if ( endpoint->TdiBufferring ) { + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + } else { + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + } + + Irp->IoStatus.Information = sizeof(AFD_RECEIVE_INFORMATION); + + return STATUS_SUCCESS; + +} // AfdQueryReceiveInformation + + diff --git a/private/ntos/afd/recvdg.c b/private/ntos/afd/recvdg.c new file mode 100644 index 000000000..cc029652d --- /dev/null +++ b/private/ntos/afd/recvdg.c @@ -0,0 +1,1653 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + recvdg.c + +Abstract: + + This module contains routines for handling data receive for datagram + endpoints. + +Author: + + David Treadwell (davidtr) 7-Oct-1993 + +Revision History: + +--*/ + +#include "afdp.h" + +NTSTATUS +AfdSetupReceiveDatagramIrp ( + IN PIRP Irp, + IN PVOID DatagramBuffer OPTIONAL, + IN ULONG DatagramLength, + IN PVOID SourceAddress, + IN ULONG SourceAddressLength + ); + +NTSTATUS +AfdRestartBufferReceiveDatagram ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGEAFD, AfdReceiveDatagram ) +#pragma alloc_text( PAGEAFD, AfdReceiveDatagramEventHandler ) +#pragma alloc_text( PAGEAFD, AfdSetupReceiveDatagramIrp ) +#pragma alloc_text( PAGEAFD, AfdRestartBufferReceiveDatagram ) +#pragma alloc_text( PAGEAFD, AfdCancelReceiveDatagram ) +#pragma alloc_text( PAGEAFD, AfdCleanupReceiveDatagramIrp ) +#endif + +// +// Macros to make the receive datagram code more maintainable. +// + +#define AfdRecvDatagramInfo DeviceIoControl + +#define AfdRecvAddressLength InputBufferLength +#define AfdRecvAddressPointer Type3InputBuffer + + +NTSTATUS +AfdReceiveDatagram ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp, + IN ULONG RecvFlags, + IN ULONG AfdFlags + ) +{ + NTSTATUS status; + KIRQL oldIrql; + PAFD_ENDPOINT endpoint; + PLIST_ENTRY listEntry; + BOOLEAN peek; + PAFD_BUFFER afdBuffer; + ULONG recvFlags; + ULONG afdFlags; + ULONG recvLength; + PVOID addressPointer; + PULONG addressLength; + PMDL addressMdl; + PMDL lengthMdl; + + // + // Set up some local variables. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( endpoint->Type == AfdBlockTypeDatagram ); + + Irp->IoStatus.Information = 0; + + addressMdl = NULL; + lengthMdl = NULL; + + // + // If receive has been shut down or the endpoint aborted, fail. + // + // !!! Do we care if datagram endpoints get aborted? + // + + if ( (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_RECEIVE) ) { + status = STATUS_PIPE_DISCONNECTED; + goto complete; + } + +#if 0 + if ( (endpoint->DisconnectMode & AFD_ABORTIVE_DISCONNECT) ) { + status = STATUS_LOCAL_DISCONNECT; + goto complete; + } +#endif + + // + // Do some special processing based on whether this is a receive + // datagram IRP, a receive IRP, or a read IRP. + // + + if ( IrpSp->Parameters.DeviceIoControl.IoControlCode == + IOCTL_AFD_RECEIVE_DATAGRAM && + IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL ) { + + PAFD_RECV_DATAGRAM_INFO recvInfo; + + // + // Make sure that the endpoint is in the correct state. + // + + if ( endpoint->State != AfdEndpointStateBound ) { + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + // + // Grab the parameters from the input structure. + // + + if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength >= + sizeof(*recvInfo) ) { + + try { + + // + // Probe the input structure. + // + + recvInfo = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; + + if( Irp->RequestorMode != KernelMode ) { + + ProbeForRead( + recvInfo, + sizeof(*recvInfo), + sizeof(ULONG) + ); + + } + + // + // Snag the receive flags. + // + + recvFlags = recvInfo->TdiFlags; + afdFlags = recvInfo->AfdFlags; + + // + // Setup the address fields so we can return the datagram + // address to the user. + // + + addressPointer = recvInfo->Address; + addressLength = recvInfo->AddressLength; + + // + // Validate the WSABUF parameters. + // + + if ( recvInfo->BufferArray != NULL && + recvInfo->BufferCount > 0 ) { + + // + // Create the MDL chain describing the WSABUF array. + // + + status = AfdAllocateMdlChain( + Irp, + recvInfo->BufferArray, + recvInfo->BufferCount, + IoWriteAccess, + &recvLength + ); + + } else { + + // + // Zero-length input buffer. This is OK for datagrams. + // + + ASSERT( Irp->MdlAddress == NULL ); + status = STATUS_SUCCESS; + + } + + } except ( EXCEPTION_EXECUTE_HANDLER ) { + + // + // Exception accessing input structure. + // + + status = GetExceptionCode(); + + } + + } else { + + // + // Invalid input buffer length. + // + + status = STATUS_INVALID_PARAMETER; + + } + + // + // If only one of addressPointer or addressLength are NULL, then + // fail the request. + // + + if( (addressPointer == NULL) ^ (addressLength == NULL) ) { + + status = STATUS_INVALID_PARAMETER; + goto complete; + + } + + if( !NT_SUCCESS(status) ) { + + goto complete; + + } + + // + // If the user wants the source address from the receive datagram, + // then create MDLs for the address & address length, then probe + // and lock the MDLs. + // + + if( addressPointer != NULL ) { + + ASSERT( addressLength != NULL ); + + // + // Setup so we know how to cleanup after the try/except block. + // + + status = STATUS_SUCCESS; + + try { + + // + // Bomb off if the user is trying to do something stupid, like + // specify a zero-length address, or one that's unreasonably + // huge. Here, we (arbitrarily) define "unreasonably huge" as + // anything 64K or greater. + // + + if( *addressLength == 0 || + *addressLength >= 65536 ) { + + ExRaiseStatus( STATUS_INVALID_PARAMETER ); + + } + + // + // Create a MDL describing the address buffer, then probe + // it for write access. + // + + addressMdl = IoAllocateMdl( + addressPointer, // VirtualAddress + *addressLength, // Length + FALSE, // SecondaryBuffer + TRUE, // ChargeQuota + NULL // Irp + ); + + if( addressMdl == NULL ) { + + ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); + + } + + MmProbeAndLockPages( + addressMdl, // MemoryDescriptorList + Irp->RequestorMode, // AccessMode + IoWriteAccess // Operation + ); + + // + // Create a MDL describing the length buffer, then probe it + // for write access. + // + + lengthMdl = IoAllocateMdl( + addressLength, // VirtualAddress + sizeof(*addressLength), // Length + FALSE, // SecondaryBuffer + TRUE, // ChargeQuota + NULL // Irp + ); + + if( lengthMdl == NULL ) { + + ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); + + } + + MmProbeAndLockPages( + lengthMdl, // MemoryDescriptorList + Irp->RequestorMode, // AccessMode + IoWriteAccess // Operation + ); + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + status = GetExceptionCode(); + + } + + if( !NT_SUCCESS(status) ) { + + goto complete; + + } + + ASSERT( addressMdl != NULL ); + ASSERT( lengthMdl != NULL ); + + } else { + + ASSERT( addressMdl == NULL ); + ASSERT( lengthMdl == NULL ); + + } + + // + // Validate the receive flags. + // + + if( ( recvFlags & TDI_RECEIVE_EITHER ) != TDI_RECEIVE_NORMAL ) { + status = STATUS_NOT_SUPPORTED; + goto complete; + } + + peek = (BOOLEAN)( (recvFlags & TDI_RECEIVE_PEEK) != 0 ); + + } else { + + ASSERT( (Irp->Flags & IRP_INPUT_OPERATION) == 0 ); + + if ( IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL ) { + + // + // Grab the input parameters from the IRP. + // + + ASSERT( IrpSp->Parameters.DeviceIoControl.IoControlCode == + IOCTL_AFD_RECEIVE ); + + recvFlags = RecvFlags; + afdFlags = AfdFlags; + + // + // It is illegal to attempt to receive expedited data on a + // datagram endpoint. + // + + if ( (recvFlags & TDI_RECEIVE_EXPEDITED) != 0 ) { + status = STATUS_NOT_SUPPORTED; + goto complete; + } + + ASSERT( ( recvFlags & TDI_RECEIVE_EITHER ) == TDI_RECEIVE_NORMAL ); + + peek = (BOOLEAN)( (recvFlags & TDI_RECEIVE_PEEK) != 0 ); + + } else { + + // + // This must be a read IRP. There are no special options + // for a read IRP. + // + + ASSERT( IrpSp->MajorFunction == IRP_MJ_READ ); + + recvFlags = TDI_RECEIVE_NORMAL; + afdFlags = AFD_OVERLAPPED; + peek = FALSE; + + } + + ASSERT( addressMdl == NULL ); + ASSERT( lengthMdl == NULL ); + + } + + // + // Save the address & length MDLs in the current IRP stack location. + // These will be used later in SetupReceiveDatagramIrp(). Note that + // they should either both be NULL or both be non-NULL. + // + + IoAcquireCancelSpinLock( &Irp->CancelIrql ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + ASSERT( !( ( addressMdl == NULL ) ^ ( lengthMdl == NULL ) ) ); + + IrpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressPointer = + (PVOID)addressMdl; + IrpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressLength = + (ULONG)lengthMdl; + + // + // Determine whether there are any datagrams already bufferred on + // this endpoint. If there is a bufferred datagram, we'll use it to + // complete the IRP. + // + + if ( endpoint->BufferredDatagramCount != 0 ) { + + KIRQL saveIrql; + + // + // There is at least one datagram bufferred on the endpoint. + // Use it for this receive. + // + + ASSERT( !IsListEmpty( &endpoint->ReceiveDatagramBufferListHead ) ); + + listEntry = endpoint->ReceiveDatagramBufferListHead.Flink; + afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry ); + + // + // Prepare the user's IRP for completion. + // + + status = AfdSetupReceiveDatagramIrp ( + Irp, + afdBuffer->Buffer, + afdBuffer->DataLength, + afdBuffer->SourceAddress, + afdBuffer->SourceAddressLength + ); + + // + // Release the cancel spin lock, since we don't need it. + // However, be careful about the IRQLs because we're releasing + // locks in a different order than we acquired them. + // + + saveIrql = Irp->CancelIrql; + IoReleaseCancelSpinLock( oldIrql ); + oldIrql = saveIrql; + + // + // If this wasn't a peek IRP, remove the buffer from the endpoint's + // list of bufferred datagrams. + // + + if ( !peek ) { + + RemoveHeadList( &endpoint->ReceiveDatagramBufferListHead ); + + // + // Update the counts of bytes and datagrams on the endpoint. + // + + endpoint->BufferredDatagramCount--; + endpoint->BufferredDatagramBytes -= afdBuffer->DataLength; + endpoint->EventsActive &= ~AFD_POLL_RECEIVE; + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdReceiveDatagram: Endp %08lX, Active %08lX\n", + endpoint, + endpoint->EventsActive + )); + } + + if( endpoint->BufferredDatagramCount > 0 ) { + + AfdIndicateEventSelectEvent( + endpoint, + AFD_POLL_RECEIVE_BIT, + STATUS_SUCCESS + ); + + } + } + + // + // We've set up all return information. Clean up and complete + // the IRP. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + if ( !peek ) { + AfdReturnBuffer( afdBuffer ); + } + + IoCompleteRequest( Irp, 0 ); + + return status; + } + + // + // There were no datagrams bufferred on the endpoint. If this is a + // nonblocking endpoint and the request was a normal receive (as + // opposed to a read IRP), fail the request. We don't fail reads + // under the asumption that if the application is doing reads they + // don't want nonblocking behavior. + // + + if ( endpoint->NonBlocking && !ARE_DATAGRAMS_ON_ENDPOINT( endpoint ) && + !( afdFlags & AFD_OVERLAPPED ) ) { + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + status = STATUS_DEVICE_NOT_READY; + goto complete; + } + + // + // We'll have to pend the IRP. Place the IRP on the appropriate IRP + // list in the endpoint. + // + + if ( peek ) { + InsertTailList( + &endpoint->PeekDatagramIrpListHead, + &Irp->Tail.Overlay.ListEntry + ); + } else { + InsertTailList( + &endpoint->ReceiveDatagramIrpListHead, + &Irp->Tail.Overlay.ListEntry + ); + } + + IoMarkIrpPending( Irp ); + + // + // Set up the cancellation routine in the IRP. If the IRP has already + // been cancelled, just call the cancellation routine here. + // + + if ( Irp->Cancel ) { + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + AfdCancelReceiveDatagram( IrpSp->DeviceObject, Irp ); + status = STATUS_CANCELLED; + goto complete; + } + + IoSetCancelRoutine( Irp, AfdCancelReceiveDatagram ); + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + return STATUS_PENDING; + +complete: + + ASSERT( !NT_SUCCESS(status) ); + + if( addressMdl != NULL ) { + if( (addressMdl->MdlFlags & MDL_PAGES_LOCKED) != 0 ) { + MmUnlockPages( addressMdl ); + } + IoFreeMdl( addressMdl ); + } + + if( lengthMdl != NULL ) { + if( (lengthMdl->MdlFlags & MDL_PAGES_LOCKED) != 0 ) { + MmUnlockPages( lengthMdl ); + } + IoFreeMdl( lengthMdl ); + } + + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, 0 ); + + return status; + +} // AfdReceiveDatagram + + +NTSTATUS +AfdReceiveDatagramEventHandler ( + IN PVOID TdiEventContext, + IN int SourceAddressLength, + IN PVOID SourceAddress, + IN int OptionsLength, + IN PVOID Options, + IN ULONG ReceiveDatagramFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *BytesTaken, + IN PVOID Tsdu, + OUT PIRP *IoRequestPacket + ) + +/*++ + +Routine Description: + + Handles receive datagram events for nonbufferring transports. + +Arguments: + + +Return Value: + + +--*/ + +{ + KIRQL oldIrql; + KIRQL cancelIrql; + PAFD_ENDPOINT endpoint; + PLIST_ENTRY listEntry; + PAFD_BUFFER afdBuffer; + PIRP irp; + ULONG requiredAfdBufferSize; + BOOLEAN userIrp; + + endpoint = TdiEventContext; + ASSERT( endpoint != NULL ); + ASSERT( endpoint->Type == AfdBlockTypeDatagram ); + +#if AFD_PERF_DBG + if ( BytesAvailable == BytesIndicated ) { + AfdFullReceiveDatagramIndications++; + } else { + AfdPartialReceiveDatagramIndications++; + } +#endif + + // + // If this endpoint is connected and the datagram is for a different + // address than the one the endpoint is connected to, drop the + // datagram. Also, if we're in the process of connecting the + // endpoint to a remote address, the MaximumDatagramCount field will + // be 0, in which case we shoul drop the datagram. + // + + IoAcquireCancelSpinLock( &cancelIrql ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + if ( (endpoint->State == AfdEndpointStateConnected && + !AfdAreTransportAddressesEqual( + endpoint->Common.Datagram.RemoteAddress, + endpoint->Common.Datagram.RemoteAddressLength, + SourceAddress, + SourceAddressLength, + TRUE )) || + (endpoint->Common.Datagram.MaxBufferredReceiveCount == 0) ) { + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + *BytesTaken = BytesAvailable; + return STATUS_SUCCESS; + } + + // + // Check whether there are any IRPs waiting on the endpoint. If + // there is such an IRP, use it to receive the datagram. + // + + if ( !IsListEmpty( &endpoint->ReceiveDatagramIrpListHead ) ) { + + ASSERT( *BytesTaken == 0 ); + ASSERT( endpoint->BufferredDatagramCount == 0 ); + ASSERT( endpoint->BufferredDatagramBytes == 0 ); + + listEntry = RemoveHeadList( &endpoint->ReceiveDatagramIrpListHead ); + + // + // Get a pointer to the IRP and reset the cancel routine in + // the IRP. The IRP is no longer cancellable. + // + + irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry ); + IoSetCancelRoutine( irp, NULL ); + + // + // If the entire datagram is being indicated to us here, just + // copy the information to the MDL in the IRP and return. + // + // Note that we'll also take the entire datagram if the user + // has pended a zero-byte datagram receive (detectable as a + // NULL Irp->MdlAddress). We'll eat the datagram and fall + // through to AfdSetupReceiveDatagramIrp(), which will store + // an error status in the IRP since the user's buffer is + // insufficient to hold the datagram. + // + + if( BytesIndicated == BytesAvailable || + irp->MdlAddress == NULL ) { + + // + // Set BytesTaken to indicate that we've taken all the + // data. We do it here because we already have + // BytesAvailable in a register, which probably won't + // be true after making function calls. + // + + *BytesTaken = BytesAvailable; + + // + // Copy the datagram and source address to the IRP. This + // prepares the IRP to be completed. + // + // !!! do we need a special version of this routine to + // handle special RtlCopyMemory, like for + // TdiCopyLookaheadBuffer? + // + + (VOID)AfdSetupReceiveDatagramIrp ( + irp, + Tsdu, + BytesAvailable, + SourceAddress, + SourceAddressLength + ); + + // + // The IRP is off the endpoint's list and is no longer + // cancellable. We can release the locks we hold. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + // + // Complete the IRP. We've already set BytesTaken + // to tell the provider that we have taken all the data. + // + + IoCompleteRequest( irp, AfdPriorityBoost ); + + return STATUS_SUCCESS; + } + + // + // Some of the datagram was not indicated, so remember that we + // want to pass back this IRP to the TDI provider. Passing back + // this IRP directly is good because it avoids having to copy + // the data from one of our buffers into the user's buffer. + // + + userIrp = TRUE; + requiredAfdBufferSize = 0; + + } else { + + userIrp = FALSE; + requiredAfdBufferSize = BytesAvailable; + } + + // + // There were no IRPs available to take the datagram, so we'll have + // to buffer it. First make sure that we're not over the limit + // of bufferring that we can do. If we're over the limit, toss + // this datagram. + // + + if ( endpoint->BufferredDatagramCount >= + endpoint->Common.Datagram.MaxBufferredReceiveCount || + endpoint->BufferredDatagramBytes >= + endpoint->Common.Datagram.MaxBufferredReceiveBytes ) { + + // + // If circular queueing is not enabled, then just drop the + // datagram on the floor. + // + + + if( !endpoint->Common.Datagram.CircularQueueing ) { + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + *BytesTaken = BytesAvailable; + return STATUS_SUCCESS; + + } + + // + // Circular queueing is enabled, so drop packets at the head of + // the receive queue until we're below the receive limit. + // + + while( endpoint->BufferredDatagramCount >= + endpoint->Common.Datagram.MaxBufferredReceiveCount || + endpoint->BufferredDatagramBytes >= + endpoint->Common.Datagram.MaxBufferredReceiveBytes ) { + + listEntry = RemoveHeadList( &endpoint->ReceiveDatagramBufferListHead ); + afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry ); + + endpoint->BufferredDatagramCount--; + endpoint->BufferredDatagramBytes -= afdBuffer->DataLength; + + AfdReturnBuffer( afdBuffer ); + + } + + // + // Proceed to accept the incoming packet. + // + + } + + // + // We're able to buffer the datagram. Now acquire a buffer of + // appropriate size. + // + + afdBuffer = AfdGetBuffer( requiredAfdBufferSize, SourceAddressLength ); + + if ( afdBuffer == NULL ) { + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + *BytesTaken = BytesAvailable; + return STATUS_SUCCESS; + } + + // + // If the entire datagram is being indicated to us, just copy it + // here. + // + + if ( BytesIndicated == BytesAvailable ) { + + ASSERT( !userIrp ); + + // + // If there is a peek IRP on the endpoint, remove it from the + // list and prepare to complete it. We can't complete it now + // because we hold a spin lock. + // + + if ( !IsListEmpty( &endpoint->PeekDatagramIrpListHead ) ) { + + // + // Remove the first peek IRP from the list and get a pointer + // to it. + // + + listEntry = RemoveHeadList( &endpoint->PeekDatagramIrpListHead ); + irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry ); + + // + // Reset the cancel routine in the IRP. The IRP is no + // longer cancellable, since we're about to complete it. + // + + IoSetCancelRoutine( irp, NULL ); + + // + // Copy the datagram and source address to the IRP. This + // prepares the IRP to be completed. + // + + (VOID)AfdSetupReceiveDatagramIrp ( + irp, + Tsdu, + BytesAvailable, + SourceAddress, + SourceAddressLength + ); + + } else { + + irp = NULL; + } + + // + // We don't need the cancel spin lock any more, so we can + // release it. However, since we acquired the cancel spin lock + // after the endpoint spin lock and we still need the endpoint + // spin lock, be careful to switch the IRQLs. + // + + IoReleaseCancelSpinLock( oldIrql ); + oldIrql = cancelIrql; + + // + // Use the special function to copy the data instead of + // RtlCopyMemory in case the data is coming from a special place + // (DMA, etc.) which cannot work with RtlCopyMemory. + // + + TdiCopyLookaheadData( + afdBuffer->Buffer, + Tsdu, + BytesAvailable, + ReceiveDatagramFlags + ); + + // + // Store the data length and set the offset to 0. + // + + afdBuffer->DataLength = BytesAvailable; + ASSERT( afdBuffer->DataOffset == 0 ); + + // + // Store the address of the sender of the datagram. + // + + RtlCopyMemory( + afdBuffer->SourceAddress, + SourceAddress, + SourceAddressLength + ); + + afdBuffer->SourceAddressLength = SourceAddressLength; + + // + // Place the buffer on this endpoint's list of bufferred datagrams + // and update the counts of datagrams and datagram bytes on the + // endpoint. + // + + InsertTailList( + &endpoint->ReceiveDatagramBufferListHead, + &afdBuffer->BufferListEntry + ); + + endpoint->BufferredDatagramCount++; + endpoint->BufferredDatagramBytes += BytesAvailable; + + // + // All done. Release the lock and tell the provider that we + // took all the data. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // Indicate that it is possible to receive on the endpoint now. + // + + AfdIndicatePollEvent( + endpoint, + AFD_POLL_RECEIVE_BIT, + STATUS_SUCCESS + ); + + // + // If there was a peek IRP on the endpoint, complete it now. + // + + if ( irp != NULL ) { + IoCompleteRequest( irp, AfdPriorityBoost ); + } + + *BytesTaken = BytesAvailable; + + return STATUS_SUCCESS; + } + + // + // We'll have to format up an IRP and give it to the provider to + // handle. We don't need any locks to do this--the restart routine + // will check whether new receive datagram IRPs were pended on the + // endpoint. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + // + // Use the IRP in the AFD buffer if appropriate. If userIrp is + // TRUE, then the local variable irp will already point to the + // user's IRP which we'll use for this IO. + // + + if ( !userIrp ) { + irp = afdBuffer->Irp; + ASSERT( afdBuffer->Mdl == irp->MdlAddress ); + } + + // + // Tell the TDI provider where to put the source address. + // + + afdBuffer->TdiOutputInfo.RemoteAddressLength = afdBuffer->AllocatedAddressLength; + afdBuffer->TdiOutputInfo.RemoteAddress = afdBuffer->SourceAddress; + + // + // We need to remember the endpoint in the AFD buffer because we'll + // need to access it in the completion routine. + // + + afdBuffer->Context = endpoint; + + // + // Finish building the receive datagram request. + // + + TdiBuildReceiveDatagram( + irp, + endpoint->AddressDeviceObject, + endpoint->AddressFileObject, + AfdRestartBufferReceiveDatagram, + afdBuffer, + irp->MdlAddress, + BytesAvailable, + &afdBuffer->TdiInputInfo, + &afdBuffer->TdiOutputInfo, + 0 + ); + + // + // Make the next stack location current. Normally IoCallDriver would + // do this, but since we're bypassing that, we do it directly. + // + + IoSetNextIrpStackLocation( irp ); + + *IoRequestPacket = irp; + *BytesTaken = 0; + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // AfdReceiveDatagramEventHandler + + +NTSTATUS +AfdRestartBufferReceiveDatagram ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + Handles completion of bufferred datagram receives that were started + in the datagram indication handler. + +Arguments: + + DeviceObject - not used. + + Irp - the IRP that is completing. + + Context - the endpoint which received the datagram. + +Return Value: + + NTSTATUS - if this is our IRP, then always + STATUS_MORE_PROCESSING_REQUIRED to indicate to the IO system that we + own the IRP and the IO system should stop processing the it. + + If this is a user's IRP, then STATUS_SUCCESS to indicate that + IO completion should continue. + +--*/ + +{ + PAFD_ENDPOINT endpoint; + KIRQL oldIrql; + KIRQL cancelIrql; + PAFD_BUFFER afdBuffer; + PIRP pendedIrp; + PLIST_ENTRY listEntry; + + ASSERT( NT_SUCCESS(Irp->IoStatus.Status) ); + + afdBuffer = Context; + + endpoint = afdBuffer->Context; + ASSERT( endpoint->Type == AfdBlockTypeDatagram ); + + // + // Remember the length of the received datagram and the length + // of the source address. + // + + afdBuffer->DataLength = Irp->IoStatus.Information; + afdBuffer->SourceAddressLength = afdBuffer->TdiOutputInfo.RemoteAddressLength; + + // + // Zero the fields of the TDI info structures in the AFD buffer + // that we used. They must be zero when we return the buffer. + // + + afdBuffer->TdiOutputInfo.RemoteAddressLength = 0; + afdBuffer->TdiOutputInfo.RemoteAddress = NULL; + + // + // If the IRP being completed is actually a user's IRP, set it up + // for completion and allow IO completion to finish. + // + + if ( Irp != afdBuffer->Irp ) { + + // + // Set up the IRP for completion. + // + + IoAcquireCancelSpinLock( &cancelIrql ); + + (VOID)AfdSetupReceiveDatagramIrp ( + Irp, + NULL, + Irp->IoStatus.Information, + afdBuffer->SourceAddress, + afdBuffer->SourceAddressLength + ); + + IoReleaseCancelSpinLock( cancelIrql ); + + // + // Free the AFD buffer we've been using to track this request. + // + + AfdReturnBuffer( afdBuffer ); + + // + // If pending has be returned for this irp then mark the current + // stack as pending. + // + + if ( Irp->PendingReturned ) { + IoMarkIrpPending(Irp); + } + + // + // Tell the IO system that it is OK to continue with IO + // completion. + // + + return STATUS_SUCCESS; + } + + // + // If the IO failed, then just return the AFD buffer to our buffer + // pool. + // + + if ( !NT_SUCCESS(Irp->IoStatus.Status) ) { + AfdReturnBuffer( afdBuffer ); + return STATUS_MORE_PROCESSING_REQUIRED; + } + + // + // If there are any pended IRPs on the endpoint, complete as + // appropriate with the new information. + // + + IoAcquireCancelSpinLock( &cancelIrql ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + if ( !IsListEmpty( &endpoint->ReceiveDatagramIrpListHead ) ) { + + // + // There was a pended receive datagram IRP. Remove it from the + // head of the list. + // + + listEntry = RemoveHeadList( &endpoint->ReceiveDatagramIrpListHead ); + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // Get a pointer to the IRP and reset the cancel routine in + // the IRP. The IRP is no longer cancellable. + // + + pendedIrp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry ); + + IoSetCancelRoutine( pendedIrp, NULL ); + + // + // Set up the user's IRP for completion. + // + + (VOID)AfdSetupReceiveDatagramIrp ( + pendedIrp, + afdBuffer->Buffer, + afdBuffer->DataLength, + afdBuffer->SourceAddress, + afdBuffer->SourceAddressLength + ); + + IoReleaseCancelSpinLock( cancelIrql ); + + // + // Complete the user's IRP, free the AFD buffer we used for + // the request, and tell the IO system that we're done + // processing this request. + // + + IoCompleteRequest( pendedIrp, AfdPriorityBoost ); + + AfdReturnBuffer( afdBuffer ); + + return STATUS_MORE_PROCESSING_REQUIRED; + } + + // + // If there are any pended peek IRPs on the endpoint, complete + // one with this datagram. + // + + if ( !IsListEmpty( &endpoint->PeekDatagramIrpListHead ) ) { + + // + // There was a pended peek receive datagram IRP. Remove it from + // the head of the list. + // + + listEntry = RemoveHeadList( &endpoint->PeekDatagramIrpListHead ); + + // + // Get a pointer to the IRP and reset the cancel routine in + // the IRP. The IRP is no longer cancellable. + // + + pendedIrp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry ); + + IoSetCancelRoutine( pendedIrp, NULL ); + + // + // Set up the user's IRP for completion. + // + + (VOID)AfdSetupReceiveDatagramIrp ( + pendedIrp, + afdBuffer->Buffer, + afdBuffer->DataLength, + afdBuffer->SourceAddress, + afdBuffer->SourceAddressLength + ); + + // + // Don't complete the pended peek IRP yet, since we still hold + // locks. Wait until it is safe to release the locks. + // + + } else { + + pendedIrp = NULL; + } + + // + // Place the datagram at the end of the endpoint's list of bufferred + // datagrams, and update counts of datagrams on the endpoint. + // + + InsertTailList( + &endpoint->ReceiveDatagramBufferListHead, + &afdBuffer->BufferListEntry + ); + + endpoint->BufferredDatagramCount++; + endpoint->BufferredDatagramBytes += afdBuffer->DataLength; + + // + // Release locks and indicate that there are bufferred datagrams + // on the endpoint. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + AfdIndicatePollEvent( + endpoint, + AFD_POLL_RECEIVE_BIT, + STATUS_SUCCESS + ); + + // + // If there was a pended peek IRP to complete, complete it now. + // + + if ( pendedIrp != NULL ) { + IoCompleteRequest( pendedIrp, 2 ); + } + + // + // Tell the IO system to stop processing this IRP, since we now own + // it as part of the AFD buffer. + // + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // AfdRestartBufferReceiveDatagram + + +VOID +AfdCancelReceiveDatagram ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + Cancels a receive datagram IRP that is pended in AFD. + +Arguments: + + DeviceObject - not used. + + Irp - the IRP to cancel. + +Return Value: + + None. + +--*/ + +{ + PIO_STACK_LOCATION irpSp; + PAFD_ENDPOINT endpoint; + KIRQL oldIrql; + PMDL mdl; + + // + // Get the endpoint pointer from our IRP stack location. + // + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + endpoint = irpSp->FileObject->FsContext; + + ASSERT( endpoint->Type == AfdBlockTypeDatagram ); + + // + // Remove the IRP from the endpoint's IRP list, synchronizing with + // the endpoint lock which protects the lists. Note that the + // IRP *must* be on one of the endpoint's lists if we are getting + // called here--anybody that removes the IRP from the list must + // do so while holding the cancel spin lock and reset the cancel + // routine to NULL before releasing the cancel spin lock. + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + RemoveEntryList( &Irp->Tail.Overlay.ListEntry ); + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // Reset the cancel routine in the IRP. + // + + IoSetCancelRoutine( Irp, NULL ); + + // + // Free any MDL chains attached to the IRP stack location. + // + + AfdCleanupReceiveDatagramIrp( Irp ); + + // + // Release the cancel spin lock and complete the IRP with a + // cancellation status code. + // + + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_CANCELLED; + + IoCompleteRequest( Irp, AfdPriorityBoost ); + + return; + +} // AfdCancelReceiveDatagram + + +VOID +AfdCleanupReceiveDatagramIrp( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + Performs any cleanup specific to receive datagram IRPs. + +Arguments: + + Irp - the IRP to cleanup. + +Return Value: + + None. + +Notes: + + This routine may be called at raised IRQL from AfdCompleteIrpList(). + +--*/ + +{ + PIO_STACK_LOCATION irpSp; + PMDL mdl; + + // + // Get the endpoint pointer from our IRP stack location. + // + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Free any MDL chains attached to the IRP stack location. + // + + mdl = (PMDL)irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressPointer; + + if( mdl != NULL ) { + irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressPointer = NULL; + MmUnlockPages( mdl ); + IoFreeMdl( mdl ); + } + + mdl = (PMDL)irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressLength; + + if( mdl != NULL ) { + irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressLength = 0; + MmUnlockPages( mdl ); + IoFreeMdl( mdl ); + } + +} // AfdCleanupReceiveDatagramIrp + + +NTSTATUS +AfdSetupReceiveDatagramIrp ( + IN PIRP Irp, + IN PVOID DatagramBuffer OPTIONAL, + IN ULONG DatagramLength, + IN PVOID SourceAddress, + IN ULONG SourceAddressLength + ) + +/*++ + +Routine Description: + + Copies the datagram to the MDL in the IRP and the datagram sender's + address to the appropriate place in the system buffer. + + NOTE: This function MUST be called with the I/O cancel spinlock held! + +Arguments: + + Irp - the IRP to prepare for completion. + + DatagramBuffer - datagram to copy into the IRP. If NULL, then + there is no need to copy the datagram to the IRP's MDL, the + datagram has already been copied there. + + DatagramLength - the length of the datagram to copy. + + SourceAddress - address of the sender of the datagram. + + SourceAddressLength - length of the source address. + +Return Value: + + NTSTATUS - The status code placed into the IRP. + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION irpSp; + ULONG bytesCopied; + PMDL addressPointer; + PMDL addressLength; + PTRANSPORT_ADDRESS tdiAddress; + ULONG addressBytesCopied; + NTSTATUS status2; + KIRQL cancelIrql; + + ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL ); + + // + // If necessary, copy the datagram in the buffer to the MDL in the + // user's IRP. If there is no MDL in the buffer, then fail if the + // datagram is larger than 0 bytes. + // + + if ( ARGUMENT_PRESENT( DatagramBuffer ) ) { + + if ( Irp->MdlAddress == NULL ) { + + if ( DatagramLength != 0 ) { + status = STATUS_BUFFER_OVERFLOW; + } else { + status = STATUS_SUCCESS; + } + + bytesCopied = 0; + + } else { + + status = TdiCopyBufferToMdl( + DatagramBuffer, + 0, + DatagramLength, + Irp->MdlAddress, + 0, + &bytesCopied + ); + } + + } else { + + // + // The information was already copied to the MDL chain in the + // IRP. Just remember the IO status block so we can do the + // right thing with it later. + // + + status = Irp->IoStatus.Status; + bytesCopied = Irp->IoStatus.Information; + } + + // + // To determine how to complete setting up the IRP for completion, + // figure out whether this IRP was for regular datagram information, + // in which case we need to return an address, or for data only, in + // which case we will not return the source address. NtReadFile() + // and recv() on connected datagram sockets will result in the + // latter type of IRP. + // + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + addressPointer = + (PMDL)irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressPointer; + addressLength = + (PMDL)irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressLength; + + irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressPointer = NULL; + irpSp->Parameters.AfdRecvDatagramInfo.AfdRecvAddressLength = 0; + + if( addressPointer != NULL ) { + + ASSERT( irpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_AFD_RECEIVE_DATAGRAM ); + + ASSERT( addressPointer->Next == NULL ); + ASSERT( ( addressPointer->MdlFlags & MDL_PAGES_LOCKED ) != 0 ); + ASSERT( addressPointer->Size > 0 ); + + ASSERT( addressLength != NULL ); + ASSERT( addressLength->Next == NULL ); + ASSERT( ( addressLength->MdlFlags & MDL_PAGES_LOCKED ) != 0 ); + ASSERT( addressLength->Size > 0 ); + + // + // Extract the real SOCKADDR structure from the TDI address. + // This duplicates MSAFD.DLL's SockBuildSockaddr() function. + // + + tdiAddress = SourceAddress; + + ASSERT( sizeof(tdiAddress->Address[0].AddressType) == sizeof(u_short) ); + ASSERT( FIELD_OFFSET( TA_ADDRESS, AddressLength ) == 0 ); + ASSERT( FIELD_OFFSET( TA_ADDRESS, AddressType ) == sizeof(USHORT) ); + ASSERT( FIELD_OFFSET( TRANSPORT_ADDRESS, Address[0] ) == sizeof(int) ); + ASSERT( SourceAddressLength >= + (tdiAddress->Address[0].AddressLength + sizeof(u_short)) ); + + SourceAddressLength = tdiAddress->Address[0].AddressLength + + sizeof(u_short); // sa_family + SourceAddress = &tdiAddress->Address[0].AddressType; + + // + // Copy the address to the user's buffer, then unlock and + // free the MDL describing the user's buffer. + // + + status2 = TdiCopyBufferToMdl( + SourceAddress, + 0, + SourceAddressLength, + addressPointer, + 0, + &addressBytesCopied + ); + + MmUnlockPages( addressPointer ); + IoFreeMdl( addressPointer ); + + // + // If the above TdiCopyBufferToMdl was successful, then + // copy the address length to the user's buffer, then unlock + // and free the MDL describing the user's buffer. + // + + if( NT_SUCCESS(status2) ) { + + status2 = TdiCopyBufferToMdl( + &SourceAddressLength, + 0, + sizeof(SourceAddressLength), + addressLength, + 0, + &addressBytesCopied + ); + + } + + MmUnlockPages( addressLength ); + IoFreeMdl( addressLength ); + + // + // If either of the above TdiCopyBufferToMdl calls failed, + // then use its status code as the completion code. + // + + if( !NT_SUCCESS(status2) ) { + + status = status2; + + } + + } else { + + ASSERT( addressLength == NULL ); + + } + + // + // Set up the IRP for completion. + // + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = bytesCopied; + + return status; + +} // AfdSetupReceiveDatagramIrp + diff --git a/private/ntos/afd/recvvc.c b/private/ntos/afd/recvvc.c new file mode 100644 index 000000000..f246c64ea --- /dev/null +++ b/private/ntos/afd/recvvc.c @@ -0,0 +1,1884 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + recvvc.c + +Abstract: + + This module contains routines for handling data receive for connection- + oriented endpoints. + +Author: + + David Treadwell (davidtr) 21-Oct-1993 + +Revision History: + +--*/ + +#include "afdp.h" + +VOID +AfdCancelReceive ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +PIRP +AfdGetPendedReceiveIrp ( + IN PAFD_CONNECTION Connection, + IN BOOLEAN Expedited + ); + +PAFD_BUFFER +AfdGetReceiveBuffer ( + IN PAFD_CONNECTION Connection, + IN ULONG ReceiveFlags, + IN PAFD_BUFFER StartingAfdBuffer OPTIONAL + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGEAFD, AfdBReceive ) +#pragma alloc_text( PAGEAFD, AfdBReceiveEventHandler ) +#pragma alloc_text( PAGEAFD, AfdBReceiveExpeditedEventHandler ) +#pragma alloc_text( PAGEAFD, AfdCancelReceive ) +#pragma alloc_text( PAGEAFD, AfdGetPendedReceiveIrp ) +#pragma alloc_text( PAGEAFD, AfdGetReceiveBuffer ) +#pragma alloc_text( PAGEAFD, AfdRestartBufferReceive ) +#endif + + +NTSTATUS +AfdBReceive ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp, + IN ULONG RecvFlags, + IN ULONG AfdFlags, + IN ULONG RecvLength + ) +{ + NTSTATUS status; + KIRQL oldIrql; + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + ULONG bytesReceived; + BOOLEAN peek; + PAFD_BUFFER afdBuffer; + BOOLEAN completeMessage; + BOOLEAN partialReceivePossible; + PAFD_BUFFER newAfdBuffer; + + // + // Set up some local variables. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ); + + connection = endpoint->Common.VcConnecting.Connection; + ASSERT( connection != NULL ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + // + // Determine if this is a peek operation. + // + + ASSERT( ( RecvFlags & TDI_RECEIVE_EITHER ) != 0 ); + ASSERT( ( RecvFlags & TDI_RECEIVE_EITHER ) != TDI_RECEIVE_EITHER ); + + peek = ( RecvFlags & TDI_RECEIVE_PEEK ) != 0; + + // + // Determine whether it is legal to complete this receive with a + // partial message. + // + + if ( endpoint->EndpointType == AfdEndpointTypeStream ) { + + partialReceivePossible = TRUE; + + } else { + + if ( (RecvFlags & TDI_RECEIVE_PARTIAL) != 0 ) { + partialReceivePossible = TRUE; + } else { + partialReceivePossible = FALSE; + } + } + + // + // Reset the InputBufferLength field of our stack location. We'll + // use this to keep track of how much data we've placed into the IRP + // so far. + // + + IrpSp->Parameters.DeviceIoControl.InputBufferLength = 0; + + // + // If this is an inline endpoint, then either type of receive data + // can be used to satisfy this receive. + // + + if ( endpoint->InLine ) { + RecvFlags |= TDI_RECEIVE_EITHER; + } + + // + // Check whether the remote end has aborted the connection, in which + // case we should complete the receive. + // + + if ( connection->AbortIndicated ) { + status = STATUS_CONNECTION_RESET; + goto complete; + } + + // + // Try to get data already bufferred on the connection to satisfy + // this receive. + // + + IoAcquireCancelSpinLock( &Irp->CancelIrql ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + if( RecvFlags & TDI_RECEIVE_EXPEDITED ) { + endpoint->EventsActive &= ~AFD_POLL_RECEIVE_EXPEDITED; + } + + if( RecvFlags & TDI_RECEIVE_NORMAL ) { + endpoint->EventsActive &= ~AFD_POLL_RECEIVE; + } + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdBReceive: Endp %08lX, Active %08lX\n", + endpoint, + endpoint->EventsActive + )); + } + + newAfdBuffer = NULL; + afdBuffer = NULL; + afdBuffer = AfdGetReceiveBuffer( connection, RecvFlags, afdBuffer ); + + while ( afdBuffer != NULL ) { + + // + // Copy the data to the MDL in the IRP. Note that we do not + // handle the case where, for a stream type endpoint, the + // receive IRP is large enough to take multiple data buffers + // worth of information. Our method works fine, albeit a little + // slower. The faster, where the output buffer is filled up as + // much as possible, is done in the fast path. We should only + // be here if we hit a timing window between a fast path attempt + // and a receive indication. + // + + + if ( Irp->MdlAddress != NULL ) { + + status = TdiCopyBufferToMdl( + afdBuffer->Buffer, + afdBuffer->DataOffset, + afdBuffer->DataLength, + Irp->MdlAddress, + IrpSp->Parameters.DeviceIoControl.InputBufferLength, + &bytesReceived + ); + + } else { + + if ( afdBuffer->DataLength == 0 ) { + status = STATUS_SUCCESS; + } else { + status = STATUS_BUFFER_OVERFLOW; + } + + bytesReceived = 0; + } + + ASSERT( status == STATUS_SUCCESS || status == STATUS_BUFFER_OVERFLOW ); + + ASSERT( afdBuffer->PartialMessage == TRUE || afdBuffer->PartialMessage == FALSE ); + + completeMessage = !afdBuffer->PartialMessage; + + // + // If this wasn't a peek IRP, update information on the + // connection based on whether the entire buffer of data was + // taken. + // + + if ( !peek ) { + + // + // If all the data in the buffer was taken, remove the buffer + // from the connection's list and return it to the buffer pool. + // + + if (status == STATUS_SUCCESS) { + + ASSERT(afdBuffer->DataLength == bytesReceived); + + // + // Update the counts of bytes bufferred on the connection. + // + + if ( afdBuffer->ExpeditedData ) { + + ASSERT( connection->VcBufferredExpeditedBytes >= bytesReceived ); + ASSERT( connection->VcBufferredExpeditedCount > 0 ); + + connection->VcBufferredExpeditedBytes -= bytesReceived; + connection->VcBufferredExpeditedCount -= 1; + + } else { + + ASSERT( connection->VcBufferredReceiveBytes >= bytesReceived ); + ASSERT( connection->VcBufferredReceiveCount > 0 ); + + connection->VcBufferredReceiveBytes -= bytesReceived; + connection->VcBufferredReceiveCount -= 1; + } + + RemoveEntryList( &afdBuffer->BufferListEntry ); + + afdBuffer->DataOffset = 0; + afdBuffer->ExpeditedData = FALSE; + + AfdReturnBuffer( afdBuffer ); + + // + // Reset the afdBuffer local so that we know that the + // buffer is gone. + // + + afdBuffer = NULL; + + } else { + + // + // Update the counts of bytes bufferred on the connection. + // + + if ( afdBuffer->ExpeditedData ) { + ASSERT( connection->VcBufferredExpeditedBytes >= bytesReceived ); + connection->VcBufferredExpeditedBytes -= bytesReceived; + } else { + ASSERT( connection->VcBufferredReceiveBytes >= bytesReceived ); + connection->VcBufferredReceiveBytes -= bytesReceived; + } + + // + // Not all of the buffer's data was taken. Update the + // counters in the AFD buffer structure to reflect the + // amount of data that was actually received. + // + ASSERT(afdBuffer->DataLength > bytesReceived); + + afdBuffer->DataOffset += bytesReceived; + afdBuffer->DataLength -= bytesReceived; + + ASSERT( afdBuffer->DataOffset < afdBuffer->BufferLength ); + } + + // + // If there is indicated but unreceived data in the TDI + // provider, and we have available buffer space, fire off an + // IRP to receive the data. + // + + if ( connection->VcReceiveCountInTransport > 0 + + && + + connection->VcBufferredReceiveBytes < + connection->MaxBufferredReceiveBytes + + && + + connection->VcBufferredReceiveCount < + connection->MaxBufferredReceiveCount ) { + + CLONG bytesToReceive; + + // + // Remember the count of data that we're going to + // receive, then reset the fields in the connection + // where we keep track of how much data is available in + // the transport. We reset it here before releasing the + // lock so that another thread doesn't try to receive + // the data at the same time as us. + // + + if ( connection->VcReceiveBytesInTransport > AfdLargeBufferSize ) { + bytesToReceive = connection->VcReceiveBytesInTransport; + } else { + bytesToReceive = AfdLargeBufferSize; + } + + ASSERT( connection->VcReceiveCountInTransport == 1 ); + connection->VcReceiveBytesInTransport = 0; + connection->VcReceiveCountInTransport = 0; + + // + // Get an AFD buffer structure to hold the data. + // + + newAfdBuffer = AfdGetBuffer( bytesToReceive, 0 ); + if ( newAfdBuffer == NULL ) { + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + AfdBeginAbort( connection ); + status = STATUS_LOCAL_DISCONNECT; + goto complete; + } + + // + // We need to remember the connection in the AFD buffer + // because we'll need to access it in the completion + // routine. + // + + newAfdBuffer->Context = connection; + + // + // Finish building the receive IRP to give to the TDI + // provider. + // + + TdiBuildReceive( + newAfdBuffer->Irp, + connection->DeviceObject, + connection->FileObject, + AfdRestartBufferReceive, + newAfdBuffer, + newAfdBuffer->Mdl, + TDI_RECEIVE_NORMAL, + bytesToReceive + ); + + // + // Wait to hand off the IRP until we can safely release + // the endpoint lock. + // + } + } + + // + // For stream type endpoints, it does not make sense to return + // STATUS_BUFFER_OVERFLOW. That status is only sensible for + // message-oriented transports. + // + + if ( endpoint->EndpointType == AfdEndpointTypeStream ) { + status = STATUS_SUCCESS; + } + + // + // We've set up all return information. If we got a full + // message OR if we can complete with a partial message OR if + // the IRP is full of data, clean up and complete the IRP. + // + + if ( completeMessage || partialReceivePossible || + status == STATUS_BUFFER_OVERFLOW ) { + + if( ( RecvFlags & TDI_RECEIVE_NORMAL ) && + IS_DATA_ON_CONNECTION( connection ) ) { + + AfdIndicateEventSelectEvent( + endpoint, + AFD_POLL_RECEIVE_BIT, + STATUS_SUCCESS + ); + + } + + if( ( RecvFlags & TDI_RECEIVE_EXPEDITED ) && + IS_EXPEDITED_DATA_ON_CONNECTION( connection ) ) { + + AfdIndicateEventSelectEvent( + endpoint, + endpoint->InLine + ? AFD_POLL_RECEIVE_BIT + : AFD_POLL_RECEIVE_EXPEDITED_BIT, + STATUS_SUCCESS + ); + + } + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + // + // If there was data bufferred in the transport, fire off + // the IRP to receive it. + // + + if ( newAfdBuffer != NULL ) { + (VOID)IoCallDriver( connection->DeviceObject, newAfdBuffer->Irp ); + } + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = bytesReceived + + IrpSp->Parameters.DeviceIoControl.InputBufferLength; + + + IoCompleteRequest( Irp, 0 ); + + return status; + } + + // + // Update the count of bytes we've received so far into the IRP, + // get another buffer of data, and continue. + // + + IrpSp->Parameters.DeviceIoControl.InputBufferLength += bytesReceived; + afdBuffer = AfdGetReceiveBuffer( connection, RecvFlags, afdBuffer ); + } + + // + // If there was no data bufferred on the endpoint and the connection + // has been disconnected by the remote end, complete the receive + // with 0 bytes read if this is a stream endpoint, or a failure + // code if this is a message endpoint. + // + + if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength == 0 && + connection->DisconnectIndicated ) { + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + if ( endpoint->EndpointType == AfdEndpointTypeStream ) { + status = STATUS_SUCCESS; + } else { + status = STATUS_GRACEFUL_DISCONNECT; + } + + goto complete; + } + + // + // If this is a nonblocking endpoint and the request was a normal + // receive (as opposed to a read IRP), fail the request. We don't + // fail reads under the asumption that if the application is doing + // reads they don't want nonblocking behavior. + // + + if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength == 0 && + endpoint->NonBlocking && !( AfdFlags & AFD_OVERLAPPED ) ) { + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + status = STATUS_DEVICE_NOT_READY; + goto complete; + } + + // + // We'll have to pend the IRP. Remember the receive flags in the + // Type3InputBuffer field of our IO stack location. + // + + IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = (PVOID)RecvFlags; + + // + // Place the IRP on the connection's list of pended receive IRPs and + // mark the IRP ad pended. + // + + InsertTailList( + &connection->VcReceiveIrpListHead, + &Irp->Tail.Overlay.ListEntry + ); + + IoMarkIrpPending( Irp ); + Irp->IoStatus.Status = STATUS_SUCCESS; + + // + // Set up the cancellation routine in the IRP. If the IRP has already + // been cancelled, just call the cancellation routine here. + // + + if ( Irp->Cancel ) { + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + AfdCancelReceive( IrpSp->DeviceObject, Irp ); + return STATUS_CANCELLED; + } + + IoSetCancelRoutine( Irp, AfdCancelReceive ); + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + // + // If there was data bufferred in the transport, fire off the IRP to + // receive it. We have to wait until here because it is not legal + // to do an IoCallDriver() while holding a spin lock. + // + + if ( newAfdBuffer != NULL ) { + (VOID)IoCallDriver( connection->DeviceObject, newAfdBuffer->Irp ); + } + + return STATUS_PENDING; + +complete: + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = 0; + + IoCompleteRequest( Irp, 0 ); + + return status; + +} // AfdBReceive + + +NTSTATUS +AfdBReceiveEventHandler ( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *BytesTaken, + IN PVOID Tsdu, + OUT PIRP *IoRequestPacket + ) + +/*++ + +Routine Description: + + Handles receive events for nonbufferring transports. + +Arguments: + + +Return Value: + + +--*/ + +{ + KIRQL oldIrql; + KIRQL cancelIrql; + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + PLIST_ENTRY listEntry; + PAFD_BUFFER afdBuffer; + PIRP irp; + ULONG requiredAfdBufferSize; + NTSTATUS status; + ULONG receiveLength; + BOOLEAN userIrp; + BOOLEAN expedited; + BOOLEAN completeMessage; + + DEBUG receiveLength = 0xFFFFFFFF; + + connection = (PAFD_CONNECTION)ConnectionContext; + ASSERT( connection != NULL ); + + endpoint = connection->Endpoint; + ASSERT( endpoint != NULL ); + *BytesTaken = 0; + + ASSERT( connection->Type == AfdBlockTypeConnection ); + ASSERT( endpoint->Type == AfdBlockTypeVcConnecting || + endpoint->Type == AfdBlockTypeVcListening ); + ASSERT( !connection->DisconnectIndicated ); + + ASSERT( !endpoint->TdiBufferring ); + ASSERT( endpoint->EndpointType == AfdEndpointTypeStream || + endpoint->EndpointType == AfdEndpointTypeSequencedPacket || + endpoint->EndpointType == AfdEndpointTypeReliableMessage ); + +#if AFD_PERF_DBG + if ( BytesAvailable == BytesIndicated ) { + AfdFullReceiveIndications++; + } else { + AfdPartialReceiveIndications++; + } +#endif + + // + // If the receive side of the endpoint has been shut down, tell the + // provider that we took all the data and reset the connection. + // Also, account for these bytes in our count of bytes taken from + // the transport. + // + + IoAcquireCancelSpinLock( &cancelIrql ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + if ( (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_RECEIVE) != 0 || + endpoint->EndpointCleanedUp ) { + +#if DBG + DbgPrint( "AfdBReceiveEventHandler: receive shutdown, " + "%ld bytes, aborting endp %lx\n", + BytesAvailable, endpoint ); +#endif + + *BytesTaken = BytesAvailable; + + // + // Abort the connection. Note that if the abort attempt fails + // we can't do anything about it. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + (VOID)AfdBeginAbort( connection ); + + return STATUS_SUCCESS; + } + + // + // Figure out whether this is a receive indication for normal + // or expedited data, and whether this is a complete message. + // + + expedited = (BOOLEAN)( (ReceiveFlags & TDI_RECEIVE_EXPEDITED) != 0 ); + + ASSERT( expedited || connection->VcReceiveBytesInTransport == 0 ); + ASSERT( expedited || connection->VcReceiveCountInTransport == 0 ); + + completeMessage = (BOOLEAN)((ReceiveFlags & TDI_RECEIVE_ENTIRE_MESSAGE) != 0); + + // + // Check whether there are any IRPs waiting on the connection. If + // there is such an IRP and normal data is being indicated, use the + // IRP to receive the data. + // + + if ( !IsListEmpty( &connection->VcReceiveIrpListHead ) && !expedited ) { + + PIO_STACK_LOCATION irpSp; + + ASSERT( *BytesTaken == 0 ); + + listEntry = RemoveHeadList( &connection->VcReceiveIrpListHead ); + + // + // Get a pointer to the IRP and reset the cancel routine in + // the IRP. The IRP is no longer cancellable. + // + + irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry ); + IoSetCancelRoutine( irp, NULL ); + + irpSp = IoGetCurrentIrpStackLocation( irp ); + + // + // If the IRP is not large enough to hold the available data, or + // if it is a peek or expedited receive IRP, or if we've already + // placed some data into the IRP, then we'll just buffer the + // data manually and complete the IRP in the receive completion + // routine. + // + + if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength >= + BytesAvailable && + irpSp->Parameters.DeviceIoControl.InputBufferLength == 0 && + (ULONG)irpSp->Parameters.DeviceIoControl.Type3InputBuffer == 0 && + !endpoint->TdiMessageMode ) { + + // + // If all of the data was indicated to us here AND this is a + // complete message in and of itself, then just copy the + // data to the IRP and complete the IRP. + // + + if ( completeMessage && BytesIndicated == BytesAvailable ) { + + // + // The IRP is off the endpoint's list and is no longer + // cancellable. We can release the locks we hold. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + // + // Set BytesTaken to indicate that we've taken all the + // data. We do it here because we already have + // BytesAvailable in a register, which probably won't + // be true after making function calls. + // + + *BytesTaken = BytesAvailable; + + // + // Copy the data to the IRP. + // + + if ( irp->MdlAddress != NULL ) { + + status = TdiCopyBufferToMdl( + Tsdu, + 0, + BytesAvailable, + irp->MdlAddress, + 0, + &irp->IoStatus.Information + ); + + } else { + + ASSERT( BytesAvailable == 0 ); + status = STATUS_SUCCESS; + irp->IoStatus.Information = 0; + } + + // + // We should never get STATUS_BUFFER_OVERFLOW from + // TdiCopyBufferToMdl() because the user's buffer + // should have been large enough to hold all the data. + // + + ASSERT( status == STATUS_SUCCESS ); + + // + // We have already set up the status field of the IRP + // when we pended the IRP, so there's no need to + // set it again here. + // + + ASSERT( irp->IoStatus.Status == STATUS_SUCCESS ); + + // + // Complete the IRP. We've already set BytesTaken + // to tell the provider that we have taken all the data. + // + + IoCompleteRequest( irp, AfdPriorityBoost ); + + return STATUS_SUCCESS; + } + + // + // Some of the data was not indicated, so remember that we + // want to pass back this IRP to the TDI provider. Passing + // back this IRP directly is good because it avoids having + // to copy the data from one of our buffers into the user's + // buffer. + // + + userIrp = TRUE; + requiredAfdBufferSize = 0; + + receiveLength = + AfdIgnorePushBitOnReceives + ? BytesAvailable + : irpSp->Parameters.DeviceIoControl.OutputBufferLength; + + } else { + + // + // The first pended IRP is too tiny to hold all the + // available data or else it is a peek or expedited receive + // IRP. Put the IRP back on the head of the list and buffer + // the data and complete the IRP in the restart routine. + // + + InsertHeadList( + &connection->VcReceiveIrpListHead, + &irp->Tail.Overlay.ListEntry + ); + + userIrp = FALSE; + requiredAfdBufferSize = BytesAvailable; + receiveLength = BytesAvailable; + } + + } else if ( !expedited ) { + + ASSERT( IsListEmpty( &connection->VcReceiveIrpListHead ) ); + + // + // Check whether we've already bufferred the maximum amount of + // data that we'll allow ourselves to buffer for this + // connection. If we're at the limit, then we need to exert + // back pressure by not accepting this indicated data (flow + // control). + // + // Note that we have no flow control mechanisms for expedited + // data. We always accept any expedited data that is indicated + // to us. + // + + if ( connection->VcBufferredReceiveBytes >= + connection->MaxBufferredReceiveBytes + + || + + connection->VcBufferredReceiveCount >= + connection->MaxBufferredReceiveCount ) { + + ASSERT( connection->VcReceiveBytesInTransport == 0 ); + ASSERT( connection->VcReceiveCountInTransport == 0 ); + + // + // Just remember the amount of data that is available. When + // buffer space frees up, we'll actually receive this data. + // + + connection->VcReceiveBytesInTransport = BytesAvailable; + connection->VcReceiveCountInTransport = 1; + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + return STATUS_DATA_NOT_ACCEPTED; + } + + // + // There were no prepended IRPs. We'll have to buffer the data + // here in AFD. If all of the available data is being indicated + // to us AND this is a complete message, just copy the data + // here. + // + + if ( completeMessage && BytesIndicated == BytesAvailable ) { + + // + // We don't need the cancel spin lock any more, so we can + // release it. However, since we acquired the cancel spin lock + // after the endpoint spin lock and we still need the endpoint + // spin lock, be careful to switch the IRQLs. + // + + IoReleaseCancelSpinLock( oldIrql ); + oldIrql = cancelIrql; + + // + // Get an AFD buffer to hold the data. + // + + afdBuffer = AfdGetBuffer( BytesAvailable, 0 ); + + if ( afdBuffer == NULL ) { + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // If we couldn't get a buffer, abort the connection. + // This is pretty brutal, but the only alternative is + // to attempt to receive the data sometime later, which + // is very complicated to implement. + // + + AfdBeginAbort( connection ); + *BytesTaken = BytesAvailable; + return STATUS_SUCCESS; + } + + // + // Use the special function to copy the data instead of + // RtlCopyMemory in case the data is coming from a special + // place (DMA, etc.) which cannot work with RtlCopyMemory. + // + + TdiCopyLookaheadData( + afdBuffer->Buffer, + Tsdu, + BytesAvailable, + ReceiveFlags + ); + + // + // Store the data length and set the offset to 0. + // + + afdBuffer->DataLength = BytesAvailable; + ASSERT( afdBuffer->DataOffset == 0 ); + + afdBuffer->PartialMessage = FALSE; + + // + // Place the buffer on this connection's list of bufferred data + // and update the count of data bytes on the connection. + // + + InsertTailList( + &connection->VcReceiveBufferListHead, + &afdBuffer->BufferListEntry + ); + + connection->VcBufferredReceiveBytes += BytesAvailable; + connection->VcBufferredReceiveCount += 1; + + // + // All done. Release the lock and tell the provider that we + // took all the data. + // + + *BytesTaken = BytesAvailable; + + // + // Indicate that it is possible to receive on the endpoint now. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + AfdIndicatePollEvent( + endpoint, + AFD_POLL_RECEIVE_BIT, + STATUS_SUCCESS + ); + + return STATUS_SUCCESS; + } + + // + // There were no prepended IRPs and not all of the data was + // indicated to us. We'll have to buffer it by handing an IRP + // back to the TDI privider. + // + // Note that in this case we sometimes hand a large buffer to + // the TDI provider. We do this so that it can hold off + // completion of our IRP until it gets EOM or the buffer is + // filled. This reduces the number of receive indications that + // the TDI provider has to perform and also reduces the number + // of kernel/user transitions the application will perform + // because we'll tend to complete receives with larger amounts + // of data. + // + // We do not hand back a "large" AFD buffer if the indicated data + // is greater than the large buffer size or if the TDI provider + // is message mode. The reason for not giving big buffers back + // to message providers is that they will hold on to the buffer + // until a full message is received and this would be incorrect + // behavior on a SOCK_STREAM. + // + + userIrp = FALSE; + + if ( AfdLargeBufferSize >= BytesAvailable && + !AfdIgnorePushBitOnReceives && + !endpoint->TdiMessageMode ) { + requiredAfdBufferSize = AfdLargeBufferSize; + receiveLength = AfdLargeBufferSize; + } else { + requiredAfdBufferSize = BytesAvailable; + receiveLength = BytesAvailable; + } + + } else { + + // + // We're being indicated with expedited data. Buffer it and + // complete any pended IRPs in the restart routine. We always + // buffer expedited data to save complexity and because expedited + // data is not an important performance case. + // + // !!! do we need to perform flow control with expedited data? + // + + userIrp = FALSE; + requiredAfdBufferSize = BytesAvailable; + receiveLength = BytesAvailable; + } + + // + // We're able to buffer the data. First acquire a buffer of + // appropriate size. + // + + afdBuffer = AfdGetBuffer( requiredAfdBufferSize, 0 ); + + if ( afdBuffer == NULL ) { + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + // + // If we couldn't get a buffer, abort the connection. This is + // pretty brutal, but the only alternative is to attempt to + // receive the data sometime later, which is very complicated to + // implement. + // + + AfdBeginAbort( connection ); + + *BytesTaken = BytesAvailable; + return STATUS_SUCCESS; + } + + // + // We'll have to format up an IRP and give it to the provider to + // handle. We don't need any locks to do this--the restart routine + // will check whether new receive IRPs were pended on the endpoint. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + // + // Use the IRP in the AFD buffer if appropriate. If userIrp is + // TRUE, then the local variable irp will already point to the + // user's IRP which we'll use for this IO. + // + + if ( !userIrp ) { + irp = afdBuffer->Irp; + ASSERT( afdBuffer->Mdl == irp->MdlAddress ); + } + + // + // We need to remember the connection in the AFD buffer because + // we'll need to access it in the completion routine. + // + + afdBuffer->Context = connection; + + // + // Remember the type of data that we're receiving. + // + + afdBuffer->ExpeditedData = expedited; + afdBuffer->PartialMessage = !completeMessage; + + // + // Finish building the receive IRP to give to the TDI provider. + // + + ASSERT( receiveLength != 0xFFFFFFFF ); + + TdiBuildReceive( + irp, + connection->DeviceObject, + connection->FileObject, + AfdRestartBufferReceive, + afdBuffer, + irp->MdlAddress, + ReceiveFlags & TDI_RECEIVE_EITHER, + receiveLength + ); + + // + // Make the next stack location current. Normally IoCallDriver would + // do this, but since we're bypassing that, we do it directly. + // + + IoSetNextIrpStackLocation( irp ); + + *IoRequestPacket = irp; + *BytesTaken = 0; + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // AfdBReceiveEventHandler + + +NTSTATUS +AfdBReceiveExpeditedEventHandler ( + IN PVOID TdiEventContext, + IN CONNECTION_CONTEXT ConnectionContext, + IN ULONG ReceiveFlags, + IN ULONG BytesIndicated, + IN ULONG BytesAvailable, + OUT ULONG *BytesTaken, + IN PVOID Tsdu, + OUT PIRP *IoRequestPacket + ) + +/*++ + +Routine Description: + + Handles receive expedited events for nonbufferring transports. + +Arguments: + + +Return Value: + + +--*/ + +{ + return AfdBReceiveEventHandler ( + TdiEventContext, + ConnectionContext, + ReceiveFlags | TDI_RECEIVE_EXPEDITED, + BytesIndicated, + BytesAvailable, + BytesTaken, + Tsdu, + IoRequestPacket + ); + +} // AfdBReceiveExpeditedEventHandler + + +NTSTATUS +AfdRestartBufferReceive ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + Handles completion of bufferred receives that were started in the + receive indication handler. + +Arguments: + + DeviceObject - not used. + + Irp - the IRP that is completing. + + Context - the endpoint which received the data. + +Return Value: + + NTSTATUS - if this is our IRP, then always + STATUS_MORE_PROCESSING_REQUIRED to indicate to the IO system that we + own the IRP and the IO system should stop processing the it. + + If this is a user's IRP, then STATUS_SUCCESS to indicate that + IO completion should continue. + +--*/ + +{ + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + KIRQL oldIrql; + KIRQL cancelIrql; + PAFD_BUFFER afdBuffer; + PLIST_ENTRY listEntry; + LIST_ENTRY completeIrpListHead; + NTSTATUS status; + PIRP userIrp; + BOOLEAN expedited; + NTSTATUS irpStatus; + + afdBuffer = Context; + + connection = afdBuffer->Context; + endpoint = connection->Endpoint; + + ASSERT( connection->Type == AfdBlockTypeConnection ); + ASSERT( endpoint->Type == AfdBlockTypeVcConnecting || + endpoint->Type == AfdBlockTypeVcListening ); + + ASSERT( !endpoint->TdiBufferring ); + ASSERT( endpoint->EndpointType == AfdEndpointTypeStream || + endpoint->EndpointType == AfdEndpointTypeSequencedPacket || + endpoint->EndpointType == AfdEndpointTypeReliableMessage ); + + // + // If the IRP being completed is actually a user's IRP, set it up + // for completion and allow IO completion to finish. + // + + if ( Irp != afdBuffer->Irp ) { + + // + // Free the AFD buffer we've been using to track this request. + // + + AfdReturnBuffer( afdBuffer ); + + // + // If pending has be returned for this IRP then mark the current + // stack as pending. + // + + if ( Irp->PendingReturned ) { + IoMarkIrpPending( Irp ); + } + + // + // Tell the IO system that it is OK to continue with IO + // completion. + // + + return STATUS_SUCCESS; + } + + // + // If the receive failed, abort the connection. + // + + irpStatus = Irp->IoStatus.Status; + + if ( !NT_SUCCESS(irpStatus) ) { + + // + // We treat STATUS_BUFFER_OVERFLOW just like STATUS_RECEIVE_PARTIAL. + // + + if ( irpStatus == STATUS_BUFFER_OVERFLOW ) { + + irpStatus = STATUS_RECEIVE_PARTIAL; + + } else { + + afdBuffer->Mdl->ByteCount = afdBuffer->BufferLength; + AfdReturnBuffer( afdBuffer ); + + // + // !!! We can't abort the connection if the connection has + // not yet been accepted because we'll still be pointing + // at the listening endpoint. We should do something, + // however. How common is this failure? + // + + KdPrint(( "AfdRestartBufferReceive: IRP %lx failed on endp %lx\n", + irpStatus, endpoint )); + + return STATUS_MORE_PROCESSING_REQUIRED; + } + } + + // + // Remember the length of the received data. + // + + afdBuffer->DataLength = Irp->IoStatus.Information; + + // + // Initialize the local list we'll use to complete any receive IRPs. + // We use a list like this because we may need to complete multiple + // IRPs and we usually cannot complete IRPs at any random point due + // to any locks we might hold. + // + + InitializeListHead( &completeIrpListHead ); + + // + // If there are any pended IRPs on the connection, complete as + // appropriate with the new information. Note that we'll try to + // complete as many pended IRPs as possible with this new buffer of + // data. + // + + IoAcquireCancelSpinLock( &cancelIrql ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + expedited = afdBuffer->ExpeditedData; + + while ( afdBuffer != NULL && + (userIrp = AfdGetPendedReceiveIrp( + connection, + expedited )) != NULL ) { + + PIO_STACK_LOCATION irpSp; + ULONG receiveFlags; + ULONG bytesCopied = 0; + BOOLEAN peek; + BOOLEAN partialReceivePossible; + + // + // Set up some locals. + // + + irpSp = IoGetCurrentIrpStackLocation( userIrp ); + + receiveFlags = (ULONG)irpSp->Parameters.DeviceIoControl.Type3InputBuffer; + peek = (BOOLEAN)( (receiveFlags & TDI_RECEIVE_PEEK) != 0 ); + + + if ( endpoint->EndpointType == AfdEndpointTypeStream || + (receiveFlags & TDI_RECEIVE_PARTIAL) != 0 ) { + partialReceivePossible = TRUE; + } else { + partialReceivePossible = FALSE; + } + + // + // We're about to complete the IRP, so reset its cancel routine. + // + + IoSetCancelRoutine( userIrp, NULL ); + + // + // Copy data to the user's IRP. + // + + if ( userIrp->MdlAddress != NULL ) { + + status = TdiCopyBufferToMdl( + afdBuffer->Buffer, + afdBuffer->DataOffset, + afdBuffer->DataLength, + userIrp->MdlAddress, + irpSp->Parameters.DeviceIoControl.InputBufferLength, + &bytesCopied + ); + + userIrp->IoStatus.Information = + irpSp->Parameters.DeviceIoControl.InputBufferLength + bytesCopied; + + } else { + + if ( afdBuffer->DataLength == 0 ) { + status = STATUS_SUCCESS; + } else { + status = STATUS_BUFFER_OVERFLOW; + } + + userIrp->IoStatus.Information = 0; + } + + ASSERT( status == STATUS_SUCCESS || status == STATUS_BUFFER_OVERFLOW ); + + // + // If the IRP was not a peek IRP, update the AFD buffer + // accordingly. If it was a peek IRP then the data should be + // reread, so keep it around. + // + + if ( !peek ) { + + // + // If we copied all of the data from the buffer to the IRP, + // free the AFD buffer structure. + // + + if ( status == STATUS_SUCCESS ) { + + ASSERT(afdBuffer->DataLength == bytesCopied); + + afdBuffer->DataOffset = 0; + afdBuffer->ExpeditedData = FALSE; + + AfdReturnBuffer( afdBuffer ); + afdBuffer = NULL; + + // + // *** NOTE THAT AFTER THIS POINT WE CANNOT TOUCH EITHER + // THE AFD BUFFER OR THE IRP! + // + + } else { + + // + // There is more data left in the buffer. Update counts in + // the AFD buffer structure. + // + + ASSERT(afdBuffer->DataLength > bytesCopied); + + afdBuffer->DataOffset += bytesCopied; + afdBuffer->DataLength -= bytesCopied; + + ASSERT(afdBuffer->DataOffset < afdBuffer->BufferLength); + } + } + + // + // For stream type endpoints, it does not make sense to return + // STATUS_BUFFER_OVERFLOW. That status is only sensible for + // message-oriented transports. We have already set up the + // status field of the IRP when we pended it, so we don't + // need to do it again here. + // + + if ( endpoint->EndpointType == AfdEndpointTypeStream ) { + ASSERT( userIrp->IoStatus.Status == STATUS_SUCCESS ); + } else { + userIrp->IoStatus.Status = status; + } + + // + // We can complete the IRP under any of the following + // conditions: + // + // - the buffer contains a complete message of data. + // + // - it is OK to complete the IRP with a partial message. + // + // - the IRP is already full of data. + // + + if ( irpStatus == STATUS_SUCCESS + + || + + partialReceivePossible + + || + + status == STATUS_BUFFER_OVERFLOW ) { + + // + // Add the IRP to the list of IRPs we'll need to complete once we + // can release locks. + // + + InsertTailList( + &completeIrpListHead, + &userIrp->Tail.Overlay.ListEntry + ); + + } else { + + // + // Update the count of data placed into the IRP thus far. + // + + + irpSp->Parameters.DeviceIoControl.InputBufferLength += bytesCopied; + + // + // Put the IRP back on the connection's list of pended IRPs. + // + + InsertHeadList( + &connection->VcReceiveIrpListHead, + &userIrp->Tail.Overlay.ListEntry + ); + + // + // Stop processing this buffer for now. + // + // !!! This could cause a problem if there is a regular + // receive pended behind a peek IRP! But that is a + // pretty unlikely scenario. + // + + break; + } + } + + // + // If there is any data left, place the buffer at the end of the + // connection's list of bufferred data and update counts of data on + // the connection. + // + + if ( afdBuffer != NULL ) { + + InsertTailList( + &connection->VcReceiveBufferListHead, + &afdBuffer->BufferListEntry + ); + + if ( expedited ) { + connection->VcBufferredExpeditedBytes += afdBuffer->DataLength; + connection->VcBufferredExpeditedCount += 1; + } else { + connection->VcBufferredReceiveBytes += afdBuffer->DataLength; + connection->VcBufferredReceiveCount += 1; + } + + // + // Remember whether we got a full or partial receive in the + // AFD buffer. + // + + if ( irpStatus == STATUS_RECEIVE_PARTIAL || + irpStatus == STATUS_RECEIVE_PARTIAL_EXPEDITED ) { + afdBuffer->PartialMessage = TRUE; + } else { + afdBuffer->PartialMessage = FALSE; + } + } + + // + // Release locks and indicate that there is bufferred data on the + // endpoint. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + // + // If there was leftover data, complete polls as necessary. Indicate + // expedited data if the endpoint is not InLine and expedited data + // was received; otherwise, indicate normal data. + // + + if ( afdBuffer != NULL ) { + + if ( expedited && !endpoint->InLine ) { + + AfdIndicatePollEvent( + endpoint, + AFD_POLL_RECEIVE_EXPEDITED_BIT, + STATUS_SUCCESS + ); + + } else { + + AfdIndicatePollEvent( + endpoint, + AFD_POLL_RECEIVE_BIT, + STATUS_SUCCESS + ); + + } + + } + + // + // Complete IRPs as necessary. + // + + while ( !IsListEmpty( &completeIrpListHead ) ) { + + listEntry = RemoveHeadList( &completeIrpListHead ); + userIrp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry ); + + IoCompleteRequest( userIrp, AfdPriorityBoost ); + } + + // + // Tell the IO system to stop processing the AFD IRP, since we now + // own it as part of the AFD buffer. + // + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // AfdRestartBufferReceive + + +VOID +AfdCancelReceive ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + Cancels a receive IRP that is pended in AFD. + +Arguments: + + DeviceObject - not used. + + Irp - the IRP to cancel. + +Return Value: + + None. + +--*/ + +{ + PIO_STACK_LOCATION irpSp; + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + KIRQL oldIrql; + + // + // Get the endpoint pointer from our IRP stack location and the + // connection pointer from the endpoint. + // + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + endpoint = irpSp->FileObject->FsContext; + ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ); + + connection = endpoint->Common.VcConnecting.Connection; + ASSERT( connection->Type == AfdBlockTypeConnection ); + + // + // Remove the IRP from the connection's IRP list, synchronizing with + // the endpoint lock which protects the lists. Note that the IRP + // *must* be on one of the connection's lists if we are getting + // called here--anybody that removes the IRP from the list must do + // so while holding the cancel spin lock and reset the cancel + // routine to NULL before releasing the cancel spin lock. + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + RemoveEntryList( &Irp->Tail.Overlay.ListEntry ); + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // Reset the cancel routine in the IRP. + // + + IoSetCancelRoutine( Irp, NULL ); + + // + // Release the cancel spin lock and complete the IRP with a + // cancellation status code. + // + + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_CANCELLED; + + IoCompleteRequest( Irp, AfdPriorityBoost ); + + return; + +} // AfdCancelReceive + + +PAFD_BUFFER +AfdGetReceiveBuffer ( + IN PAFD_CONNECTION Connection, + IN ULONG ReceiveFlags, + IN PAFD_BUFFER StartingAfdBuffer OPTIONAL + ) + +/*++ + +Routine Description: + + Returns a pointer to a receive data buffer that contains the + appropriate type of data. Note that this routine DOES NOT remove + the buffer structure from the list it is on. + + This routine MUST be called with the connection's endpoint lock + held! + +Arguments: + + Connection - a pointer to the connection to search for data. + + ReceiveFlags - the type of receive data to look for. + + StartingAfdBuffer - if non-NULL, start looking for a buffer AFTER + this buffer. + +Return Value: + + PAFD_BUFFER - a pointer to an AFD buffer of the appropriate data type, + or NULL if there was no appropriate buffer on the connection. + +--*/ + +{ + PLIST_ENTRY listEntry; + PAFD_BUFFER afdBuffer; + + ASSERT( KeGetCurrentIrql( ) == DISPATCH_LEVEL ); + + // + // Start with the first AFD buffer on the connection. + // + + listEntry = Connection->VcReceiveBufferListHead.Flink; + afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry ); + + // + // If a starting AFD buffer was specified, walk past that buffer in + // the connection list. + // + + if ( ARGUMENT_PRESENT( StartingAfdBuffer ) ) { + + while ( TRUE ) { + + if ( afdBuffer == StartingAfdBuffer ) { + listEntry = listEntry->Flink; + afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry ); + break; + } + + listEntry = listEntry->Flink; + afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry ); + + ASSERT( listEntry != &Connection->VcReceiveBufferListHead ); + } + } + + // + // Act based on the type of data we're trying to get. + // + + switch ( ReceiveFlags & TDI_RECEIVE_EITHER ) { + + case TDI_RECEIVE_NORMAL: + + // + // Walk the connection's list of data buffers until we find the + // first data buffer that is of the appropriate type. + // + + while ( listEntry != &Connection->VcReceiveBufferListHead && + afdBuffer->ExpeditedData ) { + + listEntry = afdBuffer->BufferListEntry.Flink; + afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry ); + } + + if ( listEntry != &Connection->VcReceiveBufferListHead ) { + return afdBuffer; + } else { + return NULL; + } + + case TDI_RECEIVE_EITHER : + + // + // Just return the first buffer, if there is one. + // + + if ( listEntry != &Connection->VcReceiveBufferListHead ) { + return afdBuffer; + } else { + return NULL; + } + + case TDI_RECEIVE_EXPEDITED: + + if ( Connection->VcBufferredExpeditedCount == 0 ) { + return NULL; + } + + // + // Walk the connection's list of data buffers until we find the + // first data buffer that is of the appropriate type. + // + + while ( listEntry != &Connection->VcReceiveBufferListHead && + !afdBuffer->ExpeditedData ) { + + listEntry = afdBuffer->BufferListEntry.Flink; + afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry ); + } + + if ( listEntry != &Connection->VcReceiveBufferListHead ) { + return afdBuffer; + } else { + return NULL; + } + + default: + + ASSERT( !"Invalid ReceiveFlags" ); + return NULL; + } + +} // AfdGetReceiveBuffer + + +PIRP +AfdGetPendedReceiveIrp ( + IN PAFD_CONNECTION Connection, + IN BOOLEAN Expedited + ) + +/*++ + +Routine Description: + + Removes a receive IRP from the connection's list of receive IRPs. + Only returns an IRP which is valid for the specified type of + data, normal or expedited. If there are no IRPs pended or only + IRPs of the wrong type, returns NULL. + + This routine MUST be called with the connection's endpoint lock + held! + +Arguments: + + Connection - a pointer to the connection to search for an IRP. + + Expedited - TRUE if this routine should return a receive IRP which + can receive expedited data. + +Return Value: + + PIRP - a pointer to an IRP which can receive data of the specified + type. The IRP IS removed from the connection's list of pended + receive IRPs. + +--*/ + +{ + PIRP irp; + PIO_STACK_LOCATION irpSp; + ULONG receiveFlags; + PLIST_ENTRY listEntry; + + ASSERT( KeGetCurrentIrql( ) == DISPATCH_LEVEL ); + + // + // Walk the list of pended receive IRPs looking for one which can + // be completed with the specified type of data. + // + + for ( listEntry = Connection->VcReceiveIrpListHead.Flink; + listEntry != &Connection->VcReceiveIrpListHead; + listEntry = listEntry->Flink ) { + + // + // Get a pointer to the IRP and our stack location in the IRP. + // + + irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry ); + irpSp = IoGetCurrentIrpStackLocation( irp ); + + // + // Determine whether this IRP can receive the data type we need. + // + + receiveFlags = (ULONG)irpSp->Parameters.DeviceIoControl.Type3InputBuffer; + receiveFlags &= TDI_RECEIVE_EITHER; + ASSERT( receiveFlags != 0 ); + + if ( receiveFlags == TDI_RECEIVE_NORMAL && !Expedited ) { + + // + // We have a normal receive and normal data. Remove this + // IRP from the connection's list and return it. + // + + RemoveEntryList( listEntry ); + return irp; + } + + if ( receiveFlags == TDI_RECEIVE_EITHER ) { + + // + // This is an "either" receive. It can take the data + // regardless of the data type. + // + + RemoveEntryList( listEntry ); + return irp; + } + + if ( receiveFlags == TDI_RECEIVE_EXPEDITED && Expedited ) { + + // + // We have an expedited receive and expedited data. Remove + // this IRP from the connection's list and return it. + // + + RemoveEntryList( listEntry ); + return irp; + } + + // + // This IRP did not meet our criteria. Continue scanning the + // connection's list of pended IRPs for a good IRP. + // + } + + // + // There were no IRPs which could be completed with the specified + // type of data. + // + + return NULL; + +} // AfdGetPendedReceiveIrp diff --git a/private/ntos/afd/registry.txt b/private/ntos/afd/registry.txt new file mode 100644 index 000000000..614d113c8 --- /dev/null +++ b/private/ntos/afd/registry.txt @@ -0,0 +1,158 @@ +note: for items where i give three default values, the first is for +small machines (<12.5 MB), the second is medium machines (12.5 to 20 +MB) and the third is for large machines (> 20 MB). + +**** AFD is the driver which handles winsock. the following values +may be set under Services\Afd\Parameters: + +LargeBufferSize, REG_DWORD, default = 3876 +the size in bytes of large buffers used by AFD. smaller values use +less memory, larger values can improve performance. + +LargBufferListDepth, REG_DWORD, default = 0/2/10 +the maximum count of large buffers that AFD keeps in reserve. larger +numbers give better performance at the cost of physical memory. + +MediumBufferSize, REG_DWORD, default = 1504 +size of medium buffers. + +MediumBufferListDepth, REG_DWORD, default = 4/8/16 +max count of medium buffers in reserve. + +SmallBufferSize, REG_DWORD, default = 128 + +SmallBufferListDepth, REG_DWORD, default = 8/16/16 + +FastSendDatagramThreshold, REG_DWORD, default = 1024 +datagrams smaller than this get bufferred on send, larger ones are +pended. the default value was found by testing to be the best +overall value for performance. it is unlikely that anyone would want +to change this. + +StandardAddressLength, REG_DWORD, default = 24 +the length of TDI addresses typically used for the machine. if the +customer has a transport protocol like TP4 which uses very long +addresses, then increasing this value will result in a slight +performance improvement. + +DefaultReceiveWindow, REG_DWORD, default = 8192 +the number of receive bytes AFD will buffer on a connection before +imposing flow control. for some applications, a larger value here +will give slightly better performance at the expense of increases +resource utilization. note that applications can modify this value +on a per-socket basis with the SO_RCVBUF socket option. + +DefaultSendWindow, REG_DWORD, default = 8192 +as with DefaultReceiveWindow, but for the send side of connections. + +BufferMultiplier, REG_DWORD, default = 512 +DefaultReceiveWindow and DefaultSendWindow get divided by this value +to determine how many massages can be sent/received before flow +control is imposed. + +PriorityBoost, REG_DWORD, default = 2 +the priority boost AFD gives to a thread when it completes I/O for +that thread. if a multithreaded application experiences starvation +of some threads, reducing this value may remedy the problem. + +IrpStackSize, REG_DWORD, default = 4 +the count of IRP stack locations used by default for AFD. users +shouldn't need to change this. + +TransmitIoLength, REG_DWORD, default = PAGE_SIZE,PAGE_SIZE*2,65536 +the default size for I/O (reads and sends) performed by TransmitFile(). +Note that for the NT workstation product, the default I/O size is +exactly one page. + +IgnorePushBitOnReceives, REG_DWORD, default = 0 +If this value is zero (the default) and the TCP push bit is set on a +receive indication, then a larger then necessary buffer is passed down +to the TCP stack. This often gives a performance boost on receives. +If this value is non-zero, then the TCP push bit is ignored (this was +the behaviour of NT 3.1). + +MaxActiveTransmitFileCount, REG_DWORD, default = 0 +This value controls the maximum number of simultaneous TransmitFile +operations allowed. This registry value is only honored in the NT +Server product; NT Workstations always use a hardcoded (not configurable) +value. + +MaxFastTransmit, REG_DWORD, default = 65536 +This is the threshold count, in bytes, for the TransmitFile fast path to +fail. If the caller requests a send larger than this, it will never +go through the TransmitFile fast path. + +MaxFastCopyTransmit, REG_DWORD, default = 3876 +If a TransmitFile caller requests a send smaller than this size, the +operation is performed by doing a data copy from the file data to a system +buffer. This is slightly faster than direct I/O for small files, but for +larger files the copy overhead overwhelms the inherent efficiency of the +copy operation. + +**** the following keys are used by the RNR (service resolution and +registration) apis in winsock. these are all just "pointers" to +other stuff in the registry. users should never need to change +these. + +under CurrentControlSet\Control\ServiceProvider\Order, values: + +ExcludedProviders: a REG_MULTI_SZ that contains decimal values +corresponding to name space providers that should be excluded. +default is an empty set. some name space provider decimal values +include: + +#define NS_SAP (1) +#define NS_NDS (2) + +#define NS_TCPIP_LOCAL (10) +#define NS_TCPIP_HOSTS (11) +#define NS_DNS (12) +#define NS_NETBT (13) +#define NS_WINS (14) + +#define NS_NBP (20) + +#define NS_MS (30) +#define NS_STDA (31) +#define NS_CAIRO (32) + +#define NS_X500 (40) +#define NS_NIS (41) + +for example, setting ExcludedProviders to "1" "12" means that +GetAddressByName() will not attempt to use SAP or DNS when doing +typical name resolution operations. + +ProviderOrder: a REG_MULTI_SZ that contains strings corresponding to +keys under CurrentControlSet\Services. these keys must have a +ServiceProvider subkey which provides information about the name +space provider, especially Class and ProviderPath values. + +**** the following values are relevent to TCP/IP name resolution +(gethostbyname()) and the GetAddressByName() API. under +Services\Tcpip\ServiceProvider: + +Class, REG_DWORD, default = 8. should never change--this indicates +that TCPIP is a name service provider. + +DnsPriority, REG_DWORD, default = 0x7D0 +HostsPriority, REG_DWORD, default = 0x1F4 +LocalPriority, REG_DWORD, default = 0x1F3 +NetbtPriority, REG_DWORD, default = 0x7D1 +these priority values are used to determine the order of name +resolutions. low priority mechanisms are used first, so the default +order is local, hosts, dns, netbt. if someone wants a different name +resolution order, readjust the priority values as needed. note that +values under 1000 decimal are considered "fast" name resolution +providers, so putting network-based resolution mechanisms like dns +and netbt at values under 1000 may have weird effects. + +Name, REG_SZ, default = "TCP/IP" +no need to change. + +ProviderPath, REG_SZ, default = "%SystemRoot%\System32\wsock32.dll" +points to the dll that does tcpip name resolution. there is no need +to change this. + +**** there are other keys associated with netware name resolution. +chuck chan will comment on their names and meaning. diff --git a/private/ntos/afd/send.c b/private/ntos/afd/send.c new file mode 100644 index 000000000..58a9dd3fe --- /dev/null +++ b/private/ntos/afd/send.c @@ -0,0 +1,1734 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + send.c + +Abstract: + + This module contains the code for passing on send IRPs to + TDI providers. + +Author: + + David Treadwell (davidtr) 13-Mar-1992 + +Revision History: + +--*/ + +#include "afdp.h" + +VOID +AfdCancelSend ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +AfdRestartSend ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +AfdRestartSendConnDatagram ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +AfdRestartSendDatagram ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +typedef struct _AFD_SEND_CONN_DATAGRAM_CONTEXT { + PAFD_ENDPOINT Endpoint; + TDI_CONNECTION_INFORMATION ConnectionInformation; +} AFD_SEND_CONN_DATAGRAM_CONTEXT, *PAFD_SEND_CONN_DATAGRAM_CONTEXT; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGEAFD, AfdSend ) +#pragma alloc_text( PAGEAFD, AfdSendDatagram ) +#pragma alloc_text( PAGEAFD, AfdCancelSend ) +#pragma alloc_text( PAGEAFD, AfdRestartSend ) +#pragma alloc_text( PAGEAFD, AfdRestartBufferSend ) +#pragma alloc_text( PAGEAFD, AfdRestartSendConnDatagram ) +#pragma alloc_text( PAGEAFD, AfdRestartSendDatagram ) +#pragma alloc_text( PAGEAFD, AfdSendPossibleEventHandler ) +#endif + +// +// Macros to make the send restart code more maintainable. +// + +#define AfdRestartSendInfo DeviceIoControl +#define AfdMdlChain Type3InputBuffer +#define AfdSendFlags InputBufferLength +#define AfdOriginalLength OutputBufferLength +#define AfdCurrentLength IoControlCode + + +NTSTATUS +AfdSend ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) +{ + NTSTATUS status; + PAFD_ENDPOINT endpoint; + ULONG sendLength; + PAFD_CONNECTION connection; + BOOLEAN doSendBufferring; + PAFD_BUFFER afdBuffer; + ULONG sendFlags; + ULONG afdFlags; + BOOLEAN pendedIrp = FALSE; + PAFD_SEND_INFO sendInfo; + + // + // Make sure that the endpoint is in the correct state. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + if ( endpoint->State != AfdEndpointStateConnected ) { + status = STATUS_INVALID_CONNECTION; + goto complete; + } + + // + // If send has been shut down on this endpoint, fail. We need to be + // careful about what error code we return here: if the connection + // has been aborted, be sure to return the apprpriate error code. + // + + if ( (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND) != 0 ) { + + if ( (endpoint->DisconnectMode & AFD_ABORTIVE_DISCONNECT) != 0 ) { + status = STATUS_LOCAL_DISCONNECT; + } else { + status = STATUS_PIPE_DISCONNECTED; + } + + goto complete; + } + + // + // Set up the IRP on the assumption that it will complete successfully. + // + + Irp->IoStatus.Status = STATUS_SUCCESS; + + // + // If this is an IOCTL_AFD_SEND, then grab the parameters from the + // supplied AFD_SEND_INFO structure, build an MDL chain describing + // the WSABUF array, and attach the MDL chain to the IRP. + // + // If this is an IRP_MJ_WRITE IRP, just grab the length from the IRP + // and set the flags to zero. + // + + if( IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL ) { + + // + // Sanity check. + // + + ASSERT( IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_AFD_SEND ); + + if( IrpSp->Parameters.DeviceIoControl.InputBufferLength >= + sizeof(*sendInfo) ) { + + try { + + // + // Probe the input structure. + // + + sendInfo = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; + + if( Irp->RequestorMode != KernelMode ) { + + ProbeForRead( + sendInfo, + sizeof(*sendInfo), + sizeof(ULONG) + ); + + } + + // + // Snag the send flags. + // + + sendFlags = sendInfo->TdiFlags; + afdFlags = sendInfo->AfdFlags; + + // + // Validate the WSABUF parameters. + // + + if( sendInfo->BufferArray != NULL && + sendInfo->BufferCount > 0 ) { + + // + // Create the MDL chain describing the WSABUF array. + // + + status = AfdAllocateMdlChain( + Irp, + sendInfo->BufferArray, + sendInfo->BufferCount, + IoReadAccess, + &sendLength + ); + + } else { + + // + // Invalid BufferArray or BufferCount fields. + // + + status = STATUS_INVALID_PARAMETER; + + } + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + // + // Exception accessing input structure. + // + + status = GetExceptionCode(); + + } + + } else { + + // + // Invalid input buffer length. + // + + status = STATUS_INVALID_PARAMETER; + + } + + if( !NT_SUCCESS(status) ) { + + goto complete; + + } + + } else { + + ASSERT( IrpSp->MajorFunction == IRP_MJ_WRITE ); + + sendFlags = 0; + afdFlags = AFD_OVERLAPPED; + sendLength = IrpSp->Parameters.Write.Length; + + } + + // + // AfdSend() will either complete fully or will fail. + // + + Irp->IoStatus.Information = sendLength; + + // + // Setup for possible restart if the transport completes + // the send partially. + // + + IrpSp->Parameters.AfdRestartSendInfo.AfdMdlChain = Irp->MdlAddress; + IrpSp->Parameters.AfdRestartSendInfo.AfdSendFlags = sendFlags; + IrpSp->Parameters.AfdRestartSendInfo.AfdOriginalLength = sendLength; + IrpSp->Parameters.AfdRestartSendInfo.AfdCurrentLength = sendLength; + + // + // Buffer sends if the TDI provider does not buffer. + // + + if ( endpoint->TdiBufferring ) { + + doSendBufferring = FALSE; + + // + // If this is a nonblocking endpoint, set the TDI nonblocking + // send flag so that the request will fail if the send cannot be + // performed immediately. + // + + if ( endpoint->NonBlocking ) { + sendFlags |= TDI_SEND_NON_BLOCKING; + } + + } else { + + doSendBufferring = TRUE; + } + + // + // If this is a datagram endpoint, format up a send datagram request + // and pass it on to the TDI provider. + // + + if ( IS_DGRAM_ENDPOINT(endpoint) ) { + + PAFD_SEND_CONN_DATAGRAM_CONTEXT context; + + // + // It is illegal to send expedited data on a datagram socket. + // + + if ( (sendFlags & TDI_SEND_EXPEDITED) != 0 ) { + status = STATUS_NOT_SUPPORTED; + goto complete; + } + + // + // Allocate space to hold the connection information structure + // we'll use on input. + // + + context = AFD_ALLOCATE_POOL( + NonPagedPool, + sizeof(*context), + AFD_TDI_POOL_TAG + ); + + if ( context == NULL ) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto complete; + } + + context->Endpoint = endpoint; + context->ConnectionInformation.UserDataLength = 0; + context->ConnectionInformation.UserData = NULL; + context->ConnectionInformation.OptionsLength = 0; + context->ConnectionInformation.Options = NULL; + context->ConnectionInformation.RemoteAddressLength = + endpoint->Common.Datagram.RemoteAddressLength; + context->ConnectionInformation.RemoteAddress = + endpoint->Common.Datagram.RemoteAddress; + + // + // Build a send datagram request. + // + + TdiBuildSendDatagram( + Irp, + endpoint->AddressDeviceObject, + endpoint->AddressFileObject, + AfdRestartSendConnDatagram, + context, + Irp->MdlAddress, + sendLength, + &context->ConnectionInformation + ); + + // + // Call the transport to actually perform the send operation. + // + + return AfdIoCallDriver( + endpoint, + endpoint->AddressDeviceObject, + Irp + ); + } + + // + // Get a pointer to the relevent AFD connection structure. + // + + connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint ); + ASSERT( connection != NULL ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + ASSERT( !connection->CleanupBegun ); + + // + // If the connection has been aborted, do not pend the IRP. + // + + if ( connection->AbortIndicated ) { + status = STATUS_CONNECTION_RESET; + goto complete; + } + + // + // If we need to buffer the send, do so. + // + + if ( doSendBufferring && connection->MaxBufferredSendCount != 0 ) { + + KIRQL cancelIrql; + KIRQL oldIrql; + ULONG bytesCopied; + + ASSERT( !endpoint->TdiBufferring ); + ASSERT( !connection->TdiBufferring ); + + // + // First make sure that we don't have too many bytes of send + // data already outstanding and that someone else isn't already + // in the process of completing pended send IRPs. We can't + // issue the send here if someone else is completing pended + // sends because we have to preserve ordering of the sends. + // + // Note that we'll give the send data to the TDI provider even + // if we have exceeded our send buffer limits, but that we don't + // complete the user's IRP until some send buffer space has + // freed up. This effects flow control by blocking the user's + // thread while ensuring that the TDI provider always has lots + // of data available to be sent. + // + + IoAcquireCancelSpinLock( &cancelIrql ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + if ( connection->VcBufferredSendBytes >= connection->MaxBufferredSendBytes + + || + + connection->VcBufferredSendCount >= connection->MaxBufferredSendCount + + ) { + + // + // There is already as much send data bufferred on the + // connection as is allowed. If this is a nonblocking + // endpoint and this is not an overlapped operation, fail the + // request. + // + + if ( endpoint->NonBlocking && !( afdFlags & AFD_OVERLAPPED ) ) { + + // + // Enable the send event. + // + + endpoint->EventsActive &= ~AFD_POLL_SEND; + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdSend: Endp %08lX, Active %08lX\n", + endpoint, + endpoint->EventsActive + )); + } + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + status = STATUS_DEVICE_NOT_READY; + goto complete; + } + + // + // We're going to have to pend the request here in AFD. + // Place the IRP on the connection's list of pended send + // IRPs and mark the IRP as pended. + // + + InsertTailList( + &connection->VcSendIrpListHead, + &Irp->Tail.Overlay.ListEntry + ); + + // + // Set up the cancellation routine in the IRP. If the IRP + // has already been cancelled, just call the cancellation + // routine here. + // + + if ( Irp->Cancel ) { + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + Irp->CancelIrql = cancelIrql; + AfdCancelSend( IrpSp->DeviceObject, Irp ); + return STATUS_CANCELLED; + } + + IoSetCancelRoutine( Irp, AfdCancelSend ); + + // + // Remember that we pended the IRP so that we do not + // complete it later. + // + + pendedIrp = TRUE; + } + + // + // We don't need the IO cancel spin lock any more, so release it + // while being careful to swap the IRQLs. We have to hold on to + // the endpoint spin lock until after we have copied the data + // out of the IRP in order to prevent the IRP from being + // completed in our send completion routine. + // + + IoReleaseCancelSpinLock( oldIrql ); + oldIrql = cancelIrql; + + // + // Next get an AFD buffer structure that contains an IRP and a + // buffer to hold the data. + // + + afdBuffer = AfdGetBuffer( sendLength, 0 ); + + if ( afdBuffer == NULL && sendLength > AfdBufferLengthForOnePage ) { + + IF_DEBUG(SEND) { + KdPrint(( "AfdSend: cannot allocate %lu, trying chain\n", + sendLength )); + } + + afdBuffer = AfdGetBufferChain( sendLength ); + + } + + if ( afdBuffer == NULL ) { + + if ( pendedIrp ) { + RemoveEntryList( &Irp->Tail.Overlay.ListEntry ); + } + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + IoAcquireCancelSpinLock( &cancelIrql ); + IoSetCancelRoutine( Irp, NULL ); + IoReleaseCancelSpinLock( cancelIrql ); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto complete; + } + + // + // If we're pending the user's IRP, then mark it so. + // + + if( pendedIrp ) { + IoMarkIrpPending( Irp ); + } + + // + // Update count of send bytes pending on the connection. + // + + connection->VcBufferredSendBytes += sendLength; + connection->VcBufferredSendCount += 1; + + // + // We have to rebuild the MDL in the AFD buffer structure to + // represent exactly the number of bytes we're going to be + // sending. + // + + if( afdBuffer->NextBuffer == NULL ) { + afdBuffer->Mdl->ByteCount = sendLength; + SET_CHAIN_LENGTH( afdBuffer, sendLength ); + } + + // + // Remember the endpoint in the AFD buffer structure. We need + // this in order to access the endpoint in the restart routine. + // + + afdBuffer->Context = endpoint; + + // + // Copy the user's data into the AFD buffer. If the MDL in the + // IRP is NULL, then don't bother doing the copy--this is a + // send of length 0. + // + + if ( Irp->MdlAddress != NULL ) { + + TdiCopyMdlToBuffer( + Irp->MdlAddress, + 0, + afdBuffer->Buffer, + 0, + sendLength, + &bytesCopied + ); + ASSERT( bytesCopied == sendLength ); + + // + // Now that we've capture the send data, we can free the + // MDL chain associated with the incoming IRP. + // + // !!! Is this really a wise thing to do? + // + + AfdDestroyMdlChain( Irp ); + + } else { + + ASSERT( IrpSp->Parameters.AfdRestartSendInfo.AfdOriginalLength == 0 ); + } + + // + // Release the endpoint lock AFTER we are 100% done with the IRP + // (if pended). This prevents the user's pended IRP from being + // completed in our send completion routine while we're still + // looking at it. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // Use the IRP in the AFD buffer structure to give to the TDI + // provider. Build the TDI send request. + // + + TdiBuildSend( + afdBuffer->Irp, + connection->DeviceObject, + connection->FileObject, + AfdRestartBufferSend, + afdBuffer, + afdBuffer->Mdl, + sendFlags, + sendLength + ); + + // + // Add a reference to the connection object since the send + // request will complete asynchronously. + // + + REFERENCE_CONNECTION2( connection, (PVOID)(0xafdafd02), afdBuffer->Irp ); + + // + // Call the transport to actually perform the send. + // + + status = IoCallDriver( connection->DeviceObject, afdBuffer->Irp ); + + // + // Complete the user's IRP as appropriate if we didn't already + // pend it. Note that we change the status code from what was + // returned by the TDI provider into STATUS_SUCCESS. This is + // because we don't want to complete the IRP with STATUS_PENDING + // etc. + // + + if ( NT_SUCCESS(status) && !pendedIrp ) { + ASSERT( Irp->IoStatus.Information == sendLength ); + IoCompleteRequest( Irp, AfdPriorityBoost ); + return STATUS_SUCCESS; + } + + // + // If we pended the user's IRP, return appropriate status. Note + // that in this case we ignore an IoCallDriver() failure. + // + + if ( pendedIrp ) { + return STATUS_PENDING; + } + + // + // The send request to the TDI provider failed immediately. + // Propagate the failure to the user. + // + + goto complete; + + } else { + + // + // Build the TDI send request. + // + + connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint ); + ASSERT( connection != NULL ); + + TdiBuildSend( + Irp, + connection->DeviceObject, + connection->FileObject, + AfdRestartSend, + endpoint, + Irp->MdlAddress, + sendFlags, + sendLength + ); + + // + // Add a reference to the connection object since the send + // request will complete asynchronously. + // + + REFERENCE_CONNECTION2( connection, (PVOID)(0xafdafd03), Irp ); + + // + // Call the transport to actually perform the send. + // + + return AfdIoCallDriver( endpoint, connection->DeviceObject, Irp ); + } + +complete: + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + return status; + +} // AfdSend + + +NTSTATUS +AfdRestartSend ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + PIO_STACK_LOCATION irpSp; + PAFD_ENDPOINT endpoint = Context; + PAFD_CONNECTION connection; + PMDL mdlChain; + PMDL nextMdl; + NTSTATUS status; + PIRP disconnectIrp; + KIRQL oldIrql; + + ASSERT( endpoint != NULL ); + ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ); + + connection = endpoint->Common.VcConnecting.Connection; + ASSERT( connection != NULL ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + IF_DEBUG(SEND) { + KdPrint(( "AfdRestartSend: send completed for IRP %lx, endpoint %lx, " + "status = %X\n", + Irp, Context, Irp->IoStatus.Status )); + } + + // + // If the request failed indicating that the send would have blocked, + // and the client issues a nonblocking send, remember that nonblocking + // sends won't work until we get a send possible indication. This + // is required for write polls to work correctly. + // + // If the status code is STATUS_REQUEST_NOT_ACCEPTED, then the + // transport does not want us to update our internal variable that + // remembers that nonblocking sends are possible. The transport + // will tell us when sends are or are not possible. + // + // !!! should we also say that nonblocking sends are not possible if + // a send is completed with fewer bytes than were requested? + + if ( Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY ) { + + // + // Reenable the send event. + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + endpoint->EventsActive &= ~AFD_POLL_SEND; + + IF_DEBUG(EVENT_SELECT) { + KdPrint(( + "AfdRestartSend: Endp %08lX, Active %08lX\n", + endpoint, + endpoint->EventsActive + )); + } + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + connection->VcNonBlockingSendPossible = FALSE; + + } + + // + // If this is a send IRP on a nonblocking endpoint and fewer bytes + // were actually sent than were requested to be sent, reissue + // another send for the remaining buffer space. + // + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + if ( !endpoint->NonBlocking && NT_SUCCESS(Irp->IoStatus.Status) && + Irp->IoStatus.Information < + irpSp->Parameters.AfdRestartSendInfo.AfdCurrentLength ) { + + ASSERT( Irp->MdlAddress != NULL ); + + // + // Advance the MDL chain by the number of bytes actually sent. + // + + mdlChain = AfdAdvanceMdlChain( + Irp->MdlAddress, + Irp->IoStatus.Information + ); + + // + // If the first MDL referenced by the IRP has the MDL_PARTIAL + // flag set, then it's one of ours from a previous partial + // send and must be freed. + // + + if ( Irp->MdlAddress->MdlFlags & MDL_PARTIAL ) { + + nextMdl = Irp->MdlAddress->Next; + IoFreeMdl( Irp->MdlAddress ); + Irp->MdlAddress = nextMdl; + + } + + if ( mdlChain != NULL ) { + + Irp->MdlAddress = mdlChain; + + // + // Update our restart info. + // + + irpSp->Parameters.AfdRestartSendInfo.AfdCurrentLength -= + Irp->IoStatus.Information; + + // + // Reissue the send. + // + + TdiBuildSend( + Irp, + connection->FileObject->DeviceObject, + connection->FileObject, + AfdRestartSend, + endpoint, + Irp->MdlAddress, + irpSp->Parameters.AfdRestartSendInfo.AfdSendFlags, + irpSp->Parameters.AfdRestartSendInfo.AfdCurrentLength + ); + + status = AfdIoCallDriver( + endpoint, + connection->FileObject->DeviceObject, + Irp + ); + + IF_DEBUG(SEND) { + if ( !NT_SUCCESS(status) ) { + KdPrint(( + "AfdRestartSend: AfdIoCallDriver returned %lx\n", + status + )); + } + } + + return STATUS_MORE_PROCESSING_REQUIRED; + + } else { + + // + // Bad news, could not allocate a new partial MDL. + // + + + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + disconnectIrp = connection->VcDisconnectIrp; + connection->VcDisconnectIrp = NULL; + + AfdBeginAbort( connection ); + + // + // If there was a disconnect IRP, rather than just freeing it + // give it to the transport. This will cause the correct cleanup + // stuff (dereference objects, free IRP and disconnect context) + // to occur. Note that we do this AFTER starting to abort the + // connection so that we do not confuse the other side. + // + + if ( disconnectIrp != NULL ) { + IoCallDriver( connection->FileObject->DeviceObject, disconnectIrp ); + } + + AfdDeleteConnectedReference( connection, FALSE ); + + // + // Remove the reference added just before calling the transport. + // + + DEREFERENCE_CONNECTION2( connection, (PVOID)(0xafd11105), Irp ); + + return STATUS_MORE_PROCESSING_REQUIRED; + + } + + } + + // + // If the first MDL referenced by the IRP has the MDL_PARTIAL + // flag set, then it's one of ours from a previous partial + // send and must be freed. + // + + if( Irp->MdlAddress != NULL && + Irp->MdlAddress->MdlFlags & MDL_PARTIAL ) { + + IoFreeMdl( Irp->MdlAddress ); + + } + + // + // Restore the IRP to its former glory before completing it. + // + + Irp->MdlAddress = irpSp->Parameters.AfdRestartSendInfo.AfdMdlChain; + Irp->IoStatus.Information = irpSp->Parameters.AfdRestartSendInfo.AfdOriginalLength; + + AfdCompleteOutstandingIrp( endpoint, Irp ); + + // + // If pending has be returned for this irp then mark the current + // stack as pending. + // + + if ( Irp->PendingReturned ) { + IoMarkIrpPending(Irp); + } + + // + // Remove the reference added just before calling the transport. + // + + DEREFERENCE_CONNECTION2( connection, (PVOID)(0xafd11100), Irp ); + + return STATUS_SUCCESS; + +} // AfdRestartSend + + +NTSTATUS +AfdRestartBufferSend ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + PAFD_BUFFER afdBuffer = Context; + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + KIRQL cancelIrql; + KIRQL oldIrql; + PLIST_ENTRY listEntry; + PIRP irp; + ULONG sendCount; + PIRP disconnectIrp; + LIST_ENTRY irpsToComplete; + + endpoint = afdBuffer->Context; + + ASSERT( endpoint != NULL ); + ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ); + ASSERT( !endpoint->TdiBufferring ); + + connection = endpoint->Common.VcConnecting.Connection; + ASSERT( connection != NULL ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + ASSERT( connection->ReferenceCount > 0 ); + + IF_DEBUG(SEND) { + KdPrint(( "AfdRestartBufferSend: send completed for IRP %lx, endpoint %lx, " + "status = %X\n", + Irp, Context, Irp->IoStatus.Status )); + } + + UPDATE_CONN( connection, Irp->IoStatus.Status ); + + // + // Make a special test here to see whether this send was from the + // TransmitFile fast path with MDL file I/O. If there is a file + // object pointer in the AFD buffer structure, then we need to + // return the MDL chain to the cache manager and derefence the file + // object. + // + + if ( afdBuffer->FileObject != NULL ) { + + ASSERT( afdBuffer->NextBuffer == NULL ); + ASSERT( afdBuffer->Mdl->Next != NULL ); + + AfdMdlReadComplete( + afdBuffer->FileObject, + afdBuffer->Mdl->Next, + afdBuffer->FileOffset, + afdBuffer->ReadLength + ); + + afdBuffer->Mdl->Next = NULL; + + ObDereferenceObject( afdBuffer->FileObject ); + afdBuffer->FileObject = NULL; + } + + // + // Update the count of send bytes outstanding on the connection. + // Note that we must do this BEFORE we check to see whether there + // are any pended sends--otherwise, there is a timing window where + // a new send could come in, get pended, and we would not kick + // the sends here. + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + ASSERT( connection->VcBufferredSendBytes >= Irp->IoStatus.Information ); + ASSERT( (connection->VcBufferredSendCount & 0x8000) == 0 ); + ASSERT( connection->VcBufferredSendCount != 0 ); + + connection->VcBufferredSendBytes -= Irp->IoStatus.Information; + connection->VcBufferredSendCount -= 1; + + // + // If the send failed, abort the connection. + // + + if ( !NT_SUCCESS(Irp->IoStatus.Status) ) { + + disconnectIrp = connection->VcDisconnectIrp; + if ( disconnectIrp != NULL ) { + connection->VcDisconnectIrp = NULL; + } + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + if( afdBuffer->NextBuffer == NULL ) { + afdBuffer->Mdl->ByteCount = afdBuffer->BufferLength; + RESET_CHAIN_LENGTH( afdBuffer ); + AfdReturnBuffer( afdBuffer ); + } else { + AfdReturnBufferChain( afdBuffer ); + } + + AfdBeginAbort( connection ); + + // + // If there was a disconnect IRP, rather than just freeing it + // give it to the transport. This will cause the correct cleanup + // stuff (dereferenvce objects, free IRP and disconnect context) + // to occur. Note that we do this AFTER starting to abort the + // connection so that we do not confuse the other side. + // + + if ( disconnectIrp != NULL ) { + IoCallDriver( connection->DeviceObject, disconnectIrp ); + } + + AfdDeleteConnectedReference( connection, FALSE ); + + // + // Remove the reference added just before calling the transport. + // + + DEREFERENCE_CONNECTION2( connection, (PVOID)(0xafd11101), Irp ); + + return STATUS_MORE_PROCESSING_REQUIRED; + } + + // + // Make sure that the TDI provider sent everything we requested that + // he send. + // + + ASSERT( Irp->IoStatus.Information == afdBuffer->TotalChainLength ); + + // + // Return the AFD buffer to our buffer pool. + // + + if( afdBuffer->NextBuffer == NULL ) { + afdBuffer->Mdl->ByteCount = afdBuffer->BufferLength; + RESET_CHAIN_LENGTH( afdBuffer ); + AfdReturnBuffer( afdBuffer ); + } else { + AfdReturnBufferChain( afdBuffer ); + } + + // + // If there are no pended sends on the connection, we're done. Tell + // the IO system to stop processing IO completion for this IRP. + // + + if ( IsListEmpty( &connection->VcSendIrpListHead ) ) { + + // + // If there is no "special condition" on the endpoint, return + // immediately. We use the special condition indication so that + // we need only a single test in the typical case. + // + + if ( !connection->SpecialCondition ) { + + ASSERT( connection->TdiBufferring || connection->VcDisconnectIrp == NULL ); + ASSERT( connection->ConnectedReferenceAdded ); + + // + // There are no sends outstanding on the connection, so indicate + // that the endpoint is writable. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + AfdIndicatePollEvent( + endpoint, + AFD_POLL_SEND_BIT, + STATUS_SUCCESS + ); + + // + // Remove the reference added just before calling the transport. + // + + DEREFERENCE_CONNECTION2( connection, (PVOID)(0xafd11102), Irp ); + + // + // Tell the IO system to stop doing completion processing on + // this IRP. + // + + return STATUS_MORE_PROCESSING_REQUIRED; + } + + // + // Before we release the lock on the endpoint, remember + // the count of sends outstanding in the TDI provider. We must + // grab this while holding the endpoint lock. + // + + sendCount = connection->VcBufferredSendCount; + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // While holding the AFD spin lock, grab the disconnect IRP + // if any. We'll only start the disconnect IRP if there is no + // more pended send data (sendCount == 0). + // + + AfdAcquireSpinLock( &AfdSpinLock, &oldIrql ); + + disconnectIrp = connection->VcDisconnectIrp; + if ( disconnectIrp != NULL && sendCount == 0 ) { + connection->VcDisconnectIrp = NULL; + } else { + disconnectIrp = NULL; + } + + AfdReleaseSpinLock( &AfdSpinLock, oldIrql ); + + // + // There are no sends outstanding on the connection, so indicate + // that the endpoint is writable. + // + + AfdIndicatePollEvent( + endpoint, + AFD_POLL_SEND_BIT, + STATUS_SUCCESS + ); + + // + // If there is a disconnect IRP, give it to the TDI provider. + // + + if ( disconnectIrp != NULL ) { + IoCallDriver( connection->DeviceObject, disconnectIrp ); + } + + // + // If the connected reference delete is pending, attempt to + // remove it. + // + + AfdDeleteConnectedReference( connection, FALSE ); + + // + // Remove the reference added just before calling the transport. + // + + DEREFERENCE_CONNECTION2( connection, (PVOID)(0xafd11103), Irp ); + + // + // Tell the IO system to stop doing completion processing on + // this IRP. + // + + return STATUS_MORE_PROCESSING_REQUIRED; + } + + // + // We have to release the endpoint's spin lock in order to acquire + // the cancel spin lock due to lock ordering restrictions. + // This helps performance in the normal case since we won't have + // to acquire the cancel spin lock. Since we recheck whether + // the list is empty after reacquiring the locks, everything + // will work out. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + IoAcquireCancelSpinLock( &cancelIrql ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + // + // Now loop completing as many pended sends as possible. Note that + // in order to avoid a nasty race condition (between this thread and + // a thread performing sends on this connection) we must build a local + // list of IRPs to complete while holding the cancel and endpoint + // spinlocks. After that list is built then we can release the locks + // and scan the list to actually complete the IRPs. + // + // We complete sends when we fall below the send bufferring limits, OR + // when there is only a single send pended. We want to be agressive + // in completing the send if there is only one because we want to + // give applications every oppurtunity to get data down to us--we + // definitely do not want to incur excessive blocking in the + // application. + // + + InitializeListHead( &irpsToComplete ); + + while ( (connection->VcBufferredSendBytes <= + connection->MaxBufferredSendBytes || + connection->VcSendIrpListHead.Flink == + connection->VcSendIrpListHead.Blink) + + && + + connection->VcBufferredSendCount <= + connection->MaxBufferredSendCount + + && + + !IsListEmpty( &connection->VcSendIrpListHead ) ) { + + // + // Take the first pended user send IRP off the connection's + // list of pended send IRPs. + // + + listEntry = RemoveHeadList( &connection->VcSendIrpListHead ); + irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry ); + + // + // Reset the cancel routine in the user IRP since we're about + // to complete it. + // + + IoSetCancelRoutine( irp, NULL ); + + // + // Append the IRP to the local list. + // + + InsertTailList( + &irpsToComplete, + &irp->Tail.Overlay.ListEntry + ); + + } + + // + // While we're still holding the locks, capture the send count + // from the connection. + // + + sendCount = connection->VcBufferredSendCount; + + // + // Now we can release the locks and scan the local list of IRPs + // we need to complete, and actually complete them. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + while( !IsListEmpty( &irpsToComplete ) ) { + + // + // Remove the first item from the IRP list. + // + + listEntry = RemoveHeadList( &irpsToComplete ); + irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry ); + + // + // Complete the user's IRP with a successful status code. The IRP + // should already be set up with the correct status and bytes + // written count. + // + +#if DBG + if ( irp->IoStatus.Status == STATUS_SUCCESS ) { + PIO_STACK_LOCATION irpSp; + + irpSp = IoGetCurrentIrpStackLocation( irp ); + ASSERT( irp->IoStatus.Information == irpSp->Parameters.AfdRestartSendInfo.AfdOriginalLength ); + } +#endif + + IoCompleteRequest( irp, AfdPriorityBoost ); + + } + + // + // If the list of pended send IRPs is now empty, we'll indicate + // that the endpoint it writable. + // + + if ( sendCount == 0 ) { + + AfdIndicatePollEvent( + endpoint, + AFD_POLL_SEND_BIT, + STATUS_SUCCESS + ); + + } + + // + // Remove the reference added just before calling the transport. + // + + DEREFERENCE_CONNECTION2( connection, (PVOID)(0xafd11104), Irp ); + + // + // Tell the IO system to stop processing IO completion for this IRP. + // + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // AfdRestartBufferSend + + +NTSTATUS +AfdRestartSendConnDatagram ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + PAFD_SEND_CONN_DATAGRAM_CONTEXT context = Context; + + IF_DEBUG(SEND) { + KdPrint(( "AfdRestartSendConnDatagram: send conn completed for " + "IRP %lx, endpoint %lx, status = %X\n", + Irp, context->Endpoint, Irp->IoStatus.Status )); + } + + // + // Free the context structure we allocated earlier. + // + + AfdCompleteOutstandingIrp( context->Endpoint, Irp ); + AFD_FREE_POOL( + context, + AFD_TDI_POOL_TAG + ); + + // + // If pending has be returned for this irp then mark the current + // stack as pending. + // + + if ( Irp->PendingReturned ) { + IoMarkIrpPending(Irp); + } + + return STATUS_SUCCESS; + +} // AfdRestartSendConnDatagram + + +NTSTATUS +AfdSendDatagram ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) +{ + NTSTATUS status; + PAFD_ENDPOINT endpoint; + PAFD_SEND_DATAGRAM_INFO sendInfo; + ULONG destinationAddressLength; + PAFD_BUFFER afdBuffer; + ULONG sendLength; + + // + // Make sure that the endpoint is in the correct state. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( endpoint->Type == AfdBlockTypeDatagram ); + + if ( endpoint->State != AfdEndpointStateBound ) { + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + if( IrpSp->Parameters.DeviceIoControl.InputBufferLength >= + sizeof(*sendInfo) ) { + + try { + + // + // Probe the input structure. + // + + sendInfo = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; + + if( Irp->RequestorMode != KernelMode ) { + + ProbeForRead( + sendInfo, + sizeof(*sendInfo), + sizeof(ULONG) + ); + + } + + // + // Grab the length of the destination address. + // + + destinationAddressLength = + sendInfo->TdiConnInfo.RemoteAddressLength; + + // + // Validate the WSABUF parameters. + // + + if( sendInfo->BufferArray != NULL && + sendInfo->BufferCount > 0 ) { + + // + // Create the MDL chain describing the WSABUF array. + // + + status = AfdAllocateMdlChain( + Irp, + sendInfo->BufferArray, + sendInfo->BufferCount, + IoReadAccess, + &sendLength + ); + + } else { + + // + // Invalid BufferArray or BufferCount fields. + // + + status = STATUS_INVALID_PARAMETER; + + } + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + // + // Exception accessing input structure. + // + + status = GetExceptionCode(); + + } + + } else { + + // + // Invalid input buffer length. + // + + status = STATUS_INVALID_PARAMETER; + + } + + if( !NT_SUCCESS(status) ) { + goto complete; + } + + // + // If send has been shut down on this endpoint, fail. + // + + if ( (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND) ) { + status = STATUS_PIPE_DISCONNECTED; + goto complete; + } + + // + // Get an AFD buffer to use for the request. We need this to + // hold the destination address for the datagram. + // + + afdBuffer = AfdGetBuffer( 0, destinationAddressLength ); + if ( afdBuffer == NULL ) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto complete; + } + + // + // Copy the destination address to the AFD buffer. + // + + try { + + RtlCopyMemory( + afdBuffer->SourceAddress, + sendInfo->TdiConnInfo.RemoteAddress, + destinationAddressLength + ); + + afdBuffer->TdiInputInfo.RemoteAddressLength = destinationAddressLength; + afdBuffer->TdiInputInfo.RemoteAddress = afdBuffer->SourceAddress; + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + AfdReturnBufferChain( afdBuffer ); + status = STATUS_ACCESS_VIOLATION; + goto complete; + } + + // + // Build the request to send the datagram. + // + + afdBuffer->Context = endpoint; + + TdiBuildSendDatagram( + Irp, + endpoint->AddressDeviceObject, + endpoint->AddressFileObject, + AfdRestartSendDatagram, + afdBuffer, + Irp->MdlAddress, + sendLength, + &afdBuffer->TdiInputInfo + ); + + IF_DEBUG(SEND) { + PTDI_REQUEST_SEND_DATAGRAM tdiRequest = &sendInfo->TdiRequest; + + KdPrint(( "AfdSendDatagram: tdiRequest at %lx, SendDataInfo at %lx, len = %lx\n", + tdiRequest, tdiRequest->SendDatagramInformation, + IrpSp->Parameters.DeviceIoControl.InputBufferLength )); + KdPrint(( "AfdSendDatagram: remote address at %lx, len = %lx\n", + tdiRequest->SendDatagramInformation->RemoteAddress, + tdiRequest->SendDatagramInformation->RemoteAddressLength )); + KdPrint(( "AfdSendDatagram: output buffer length = %lx\n", + IrpSp->Parameters.DeviceIoControl.OutputBufferLength )); + } + + // + // Call the transport to actually perform the send datagram. + // + + return AfdIoCallDriver( endpoint, endpoint->AddressDeviceObject, Irp ); + +complete: + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, AfdPriorityBoost ); + + return status; + +} // AfdSendDatagram + + +NTSTATUS +AfdRestartSendDatagram ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + PAFD_BUFFER afdBuffer; + PAFD_ENDPOINT endpoint; + + afdBuffer = Context; + endpoint = afdBuffer->Context; + + ASSERT( endpoint->Type == AfdBlockTypeDatagram ); + + AfdCompleteOutstandingIrp( endpoint, Irp ); + + IF_DEBUG(SEND) { + KdPrint(( "AfdRestartSendDatagram: send datagram completed for " + "IRP %lx, endpoint %lx, status = %X\n", + Irp, Context, Irp->IoStatus.Status )); + } + + // + // If pending has be returned for this irp then mark the current + // stack as pending. + // + + if ( Irp->PendingReturned ) { + IoMarkIrpPending(Irp); + } + + afdBuffer->TdiInputInfo.RemoteAddressLength = 0; + afdBuffer->TdiInputInfo.RemoteAddress = NULL; + + AfdReturnBufferChain( afdBuffer ); + + return STATUS_SUCCESS; + +} // AfdRestartSendDatagram + + +NTSTATUS +AfdSendPossibleEventHandler ( + IN PVOID TdiEventContext, + IN PVOID ConnectionContext, + IN ULONG BytesAvailable + ) +{ + PAFD_CONNECTION connection; + PAFD_ENDPOINT endpoint; + + UNREFERENCED_PARAMETER( TdiEventContext ); + UNREFERENCED_PARAMETER( BytesAvailable ); + + connection = (PAFD_CONNECTION)ConnectionContext; + ASSERT( connection != NULL ); + + endpoint = connection->Endpoint; + ASSERT( endpoint != NULL ); + + ASSERT( connection->Type == AfdBlockTypeConnection ); + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + ASSERT( endpoint->TdiBufferring ); + ASSERT( connection->TdiBufferring ); + + IF_DEBUG(SEND) { + KdPrint(( "AfdSendPossibleEventHandler: send possible on endpoint %lx " + " conn %lx bytes=%ld\n", endpoint, connection, BytesAvailable )); + } + + // + // Remember that it is now possible to do a send on this connection. + // + + if ( BytesAvailable != 0 ) { + + connection->VcNonBlockingSendPossible = TRUE; + + // + // Complete any outstanding poll IRPs waiting for a send poll. + // + + AfdIndicatePollEvent( + endpoint, + AFD_POLL_SEND_BIT, + STATUS_SUCCESS + ); + + } else { + + connection->VcNonBlockingSendPossible = FALSE; + } + + return STATUS_SUCCESS; + +} // AfdSendPossibleEventHandler + + +VOID +AfdCancelSend ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + Cancels a send IRP that is pended in AFD. + +Arguments: + + DeviceObject - not used. + + Irp - the IRP to cancel. + +Return Value: + + None. + +--*/ + +{ + PIO_STACK_LOCATION irpSp; + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + KIRQL oldIrql; + + // + // Get the endpoint pointer from our IRP stack location and the + // connection pointer from the endpoint. + // + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + endpoint = irpSp->FileObject->FsContext; + ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ); + + connection = endpoint->Common.VcConnecting.Connection; + ASSERT( connection->Type == AfdBlockTypeConnection ); + + // + // Remove the IRP from the connection's IRP list, synchronizing with + // the endpoint lock which protects the lists. Note that the IRP + // *must* be on one of the connection's lists if we are getting + // called here--anybody that removes the IRP from the list must do + // so while holding the cancel spin lock and reset the cancel + // routine to NULL before releasing the cancel spin lock. + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + RemoveEntryList( &Irp->Tail.Overlay.ListEntry ); + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // Reset the cancel routine in the IRP. + // + + IoSetCancelRoutine( Irp, NULL ); + + // + // Release the cancel spin lock and complete the IRP with a + // cancellation status code. + // + + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_CANCELLED; + + IoCompleteRequest( Irp, AfdPriorityBoost ); + + return; + +} // AfdCancelSend + diff --git a/private/ntos/afd/sources.inc b/private/ntos/afd/sources.inc new file mode 100644 index 000000000..a8b9684bd --- /dev/null +++ b/private/ntos/afd/sources.inc @@ -0,0 +1,77 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=afd + +TARGETNAME=afd +TARGETTYPE=DRIVER +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib + +INCLUDES=..;..\..\inc;..\..\..\inc + +C_DEFINES=$(C_DEFINES) -DNT -D_NTDRIVER_ + +!IFDEF BUILD_FOR_3_51 +C_DEFINES=$(C_DEFINES) -DNT351 +!ENDIF + +SOURCES= \ + ..\accept.c \ + ..\afddata.c \ + ..\bind.c \ + ..\blkconn.c \ + ..\blkendp.c \ + ..\buffer.c \ + ..\close.c \ + ..\connect.c \ + ..\create.c \ + ..\disconn.c \ + ..\dispatch.c \ + ..\eventsel.c \ + ..\fastio.c \ + ..\group.c \ + ..\init.c \ + ..\listen.c \ + ..\misc.c \ + ..\poll.c \ + ..\receive.c \ + ..\recvdg.c \ + ..\recvvc.c \ + ..\send.c \ + ..\tranfile.c \ + ..\afd.rc \ + +UMRES=obj\*\afd.res + +MSC_WARNING_LEVEL=/W3 /WX + +!IF "$(NTNOPCH)" == "" +PRECOMPILED_INCLUDE=..\afdp.h +PRECOMPILED_PCH=afdp.pch +PRECOMPILED_OBJ=afdp.obj +!ENDIF + +SOURCES_USED=..\sources.inc + diff --git a/private/ntos/afd/spinlock.txt b/private/ntos/afd/spinlock.txt new file mode 100644 index 000000000..6eea232ec --- /dev/null +++ b/private/ntos/afd/spinlock.txt @@ -0,0 +1,440 @@ +AfdSpinLock Usage +~~~~~~~~~~~~~~~~~ + + ACCEPT.C + + Function: AfdDeferAccept + Protects: Endpoint->Common.VcListening.UnacceptedConnectionListHead + Synopsis: Used when deferring an accept (putting a connection back + on the endpoint's unaccepted connection queue). + Strategy: None. Deferring accepted connections is a very low + frequency operation (and is impossible in WinSock 1.1). + + + BIND.C + + Function: AfdRestartGetAddress + Protects: Endpoint->LocalAddress[Length] + Synopsis: Used when allocating an endpoint's local address buffer + to prevent multiple processors from allocating the buffer. + Strategy: None. AfdGetAddress is only called during bind() and + getsockname() APIs. The bind() call is certainly more + interesting than getsockname(), but even bind() is only + called once per socket, so it is unlikely there would be + any benefit in tuning this. + + + BLKCONN.C + + Function: AfdAddFreeConnection + Protects: Endpoint->Common.VcListening.FreeConnectionListHead + Synopsis: Used when adding free connections to the endpoint's + free connection queue. + Strategy: Use SLIST for free connection queue. + + Function: AfdFreeConnection + Protects: Endpoint->Common.VcListening.FreeConnectionListHead + Synopsis: Used when appending a reused connection to the endpoint's + free connection queue. + Strategy: Use SLIST for free connection queue. + + Function: AfdDereferenceConnection + Protects: Connection->ReferenceCount + Synopsis: Synchronizes access to reference count member. + Strategy: Use InterlockedDecrement. If updated value is now zero, + then acquire AfdSpinLock, and recheck the value for zero. + If it's still zero, do the usual dereference stuff. This + will eliminate all spinlock acquisitions on reference/ + dereference except the *last* dereference. + + Function: AfdGetFreeConnection + Protects: Endpoint->Common.VcListening.FreeConnectionListHead + Synopsis: Used when removing a free connection from the endpoint's + free connection queue. + Strategy: Use SLIST for free connection queue. + + Function: AfdGetReturnedConnection + Protects: Endpoint->Common.VcListening.ReturnedConnectionListHead + Synopsis: Used when scanning the returned connection queue for a + specific sequence number. + Strategy: None (for now). + + Function: AfdReferenceConnection + Protects: Connection->ReferenceCount + Synopsis: Synchronizes access to reference count member. + Strategy: Use InterlockedIncrement instead. + + + BLKENDP.C + + Function: AfdFreeQueuedConnections + Protects: Endpoint->Common.VcListening.UnacceptedConnectionListHead + Synopsis: Used when puring the endpoint's unaccepted connection + queue. + Strategy: None (for now). + + Function: AfdDereferenceEndpoint + Protects: Endpoint->ReferenceCount + Synopsis: Synchronizes access to reference count member. + Strategy: Use InterlockedDecrement. If updated value is now zero, + then acquire AfdSpinLock, and recheck the value for zero. + If it's still zero, do the usual dereference stuff. This + will eliminate all spinlock acquisitions on reference/ + dereference except the *last* dereference. + + Function: AfdReferenceEndpoint + Protects: Endpoint->ReferenceCount + Synopsis: Synchronizes access to reference count member. + Strategy: Use InterlockedIncrement instead. + + + CLOSE.C + + Function: AfdCleanup + Protects: + Synopsis: + Strategy: + + + CONNECT.C + + Function: AfdSetupConnectDataBuffers + Protects: Endpoint->ConnectDataBuffers + Synopsis: Used to guard connect data buffers when they're moved + from an endpoint to a connection. + Strategy: Do the "double compare" trick to avoid acquiring the + spinlock if there are no connect data buffers on the + endpoint. + + Function: AfdRestartConnect + Protects: Connection->ConnectDataBuffers + Synopsis: Used to guard connect data buffers after a connect + completes. + Strategy: Do the "double compare" trick to avoid acquiring the + spinlock if there are no connect data buffers on the + connection. + + + DISCONN.C + + Function: AfdPartialDisconnect(1) + Protects: Endpoint->DisconnectMode + Synopsis: Used to guard the disconnect mode bits when shutting + down a datagram endpoint. + Strategy: Test a Bunch-O-Bits in the endpoint, and only if + at least one of these is nonzero acquire the spinlock, + then proceed with the current tests. + + Function: AfdPartialDisconnect(2) + Protects: Connection->Common.Bufferring.Receive[Expedited]BytesTaken + Synopsis: Used to guard access to the byte counters in the connection + when receives are shutdown so that the connection can be + aborted if necessary. + Strategy: None (for now). + + Function: AfdDisconnectEventHandler + Protects: Connection->ConnectDataBuffers + Synopsis: Used to guard disconnect data buffers when a disconnect + indication is received. + Strategy: Do the "double compare" trick to avoid acquiring the + spinlock if there are no connect data buffers on the + connection. + + Function: AfdBeginAbort + Protects: Bunch-O-Crap + Synopsis: + Strategy: + + Function: AfdBeginDisconnect + Protects: Bunch-O-Crap, including disconnect buffers + Synopsis: + Strategy: + + Function: AfdRestartDisconnect + Protects: DisconnectContext->DisconnectListEntry + Synopsis: Guards access to AfdDisconnectListHead + Strategy: AfdDisconnectListHead no longer used. Nuke it! + + + LISTEN.C + + Function: AfdWaitForListen + Protects: + Synopsis: + Strategy: + + Function: AfdCancelWaitForListen + Protects: + Synopsis: + Strategy: + + Function: AfdConnectEventHandler + Protects: + Synopsis: + Strategy: + + Function: AfdRestartAccept + Protects: + Synopsis: + Strategy: + + + MISC.C + + Function: AfdInsertNewEndpointInList + Protects: + Synopsis: + Strategy: + + Function: AfdRemoveEndpointFromList + Protects: + Synopsis: + Strategy: + + Function: AfdInterlockedRemoveEntryList + Protects: + Synopsis: + Strategy: + + Function: AfdGetConnectData + Protects: + Synopsis: + Strategy: + + Function: AfdSetConnectData + Protects: + Synopsis: + Strategy: + + Function: AfdQueueWorkItem + Protects: + Synopsis: + Strategy: + + Function: AfdDoWork + Protects: + Synopsis: + Strategy: + + + POLL.C + + Function: AfdPoll + Protects: + Synopsis: + Strategy: + + Function: AfdCancelPoll + Protects: + Synopsis: + Strategy: + + Function: AfdIndicatePollEvent + Protects: + Synopsis: + Strategy: + + Function: AfdTimeoutPoll + Protects: + Synopsis: + Strategy: + + + RECEIVE.C + + Function: AfdReceive + Protects: + Synopsis: + Strategy: + + Function: AfdRestartReceive + Protects: + Synopsis: + Strategy: + + Function: AfdReceiveEventHandler + Protects: + Synopsis: + Strategy: + + Function: AfdReceiveExpeditedEventHandler + Protects: + Synopsis: + Strategy: + + Function: AfdQueryReceiveInformation + Protects: + Synopsis: + Strategy: + + + SEND.C + + Function: AfdRestartBufferSend + Protects: Connection->VcDisconnectIrp + Synopsis: Used to grab the disconnect IRP off a connection. + Strategy: Use InterlockedExchange. Will require changes to + blkconn!AfdFreeConnection. + + + +AfdBufferSpinLock Usage +~~~~~~~~~~~~~~~~~~~~~~~ + + BUFFER.C + + Function: AfdGetBuffer + Protects: Afd{Small|Medium|Large}BufferListHead + Synopsis: Serializes access to the various buffer lists. + Strategy: Use DaveC's SLIST instead. We'll still need the spinlock + for PPC. + + Function: AfdReturnBuffer + Protects: Afd{Small|Medium|Large}BufferListHead + Synopsis: Serializes access to the various buffer lists. + Strategy: Use DaveC's SLIST instead. We'll still need the spinlock + for PPC. + + + +Endpoint->SpinLock Usage +~~~~~~~~~~~~~~~~~~~~~~~~ + + ACCEPT.C + + Function: AfdAcceptCore + Protects: + Synopsis: + Strategy: + + + BLKCONN.C + + Function: AfdAddConnectedReference + Protects: Connection->ConnectedReferenceAdded + Synopsis: + Strategy: + + Function: AfdDeleteConnectedReference + Protects: + Synopsis: + Strategy: + + + CLOSE.C + + Function: AfdCleanup + Protects: Endpoint->{Receive|Peek}DatagramIrpListHead, + Endpoint->Vc{Receive|Send}IrpListHead, + Connection->CleanupBegun, + Endpoint->TransmitIrp + Synopsis: + Strategy: + + + CONNECT.C + + Function: AfdDoDatagramConnect + Protects: Endpoint->Common.Datagram.RemoteAddress[Length] + Synopsis: + Strategy: + + Function: AfdRestartConnect + Protects: Connection->ConnectedReferenceAdded + Synopsis: + Strategy: + + Function: AfdEnabledFailedConnectEvent + Protects: Endpoint->EventsEnabled + Synopsis: + Strategy: + + + DISCONN.C + + Function: AfdDisconnectEventHandler + Protects: Connection->Vc{Receive|Send}IrpListHead, + Connection->VcByteCounts + Synopsis: + Strategy: + + Function: AfdBeginAbort + Protects: Connection->Vc{Receive|Send}IrpListHead, + Synopsis: + Strategy: + + Function: AfdRestartAbort + Protects: Connection->Vc{Receive|Send}IrpListHead, + Synopsis: + Strategy: + + + EVENTSEL.C + + Function: AfdEventSelect + Protects: Endpoint->EventSelectStuff + Synopsis: + Strategy: + + Function: AfdEnumNetworkEvents + Protects: Endpoint->EventSelectStuff + Synopsis: + Strategy: + + + FASTIO.C + + Function: + Protects: + Synopsis: + Strategy: + + + POLL.C + + Function: + Protects: + Synopsis: + Strategy: + + + RECEIVE.C + + Function: + Protects: + Synopsis: + Strategy: + + + RECVDG.C + + Function: + Protects: + Synopsis: + Strategy: + + + RECVVC.C + + Function: + Protects: + Synopsis: + Strategy: + + + SEND.C + + Function: + Protects: + Synopsis: + Strategy: + + + TRANFILE.C + + Function: + Protects: + Synopsis: + Strategy: + + diff --git a/private/ntos/afd/tranfile.c b/private/ntos/afd/tranfile.c new file mode 100644 index 000000000..66ad60616 --- /dev/null +++ b/private/ntos/afd/tranfile.c @@ -0,0 +1,3742 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + tranfile.c + +Abstract: + + This module contains support for fast kernel-level file transmission + over a socket handle. + +Author: + + David Treadwell (davidtr) 3-Aug-1994 + +Revision History: + +--*/ + +#include "afdp.h" + +// +// Context structure for deferring a MDL read completion. +// + +typedef struct _AFD_MDL_COMPLETION_CONTEXT { + + AFD_WORK_ITEM WorkItem; + PFILE_OBJECT FileObject; + PMDL MdlChain; + LONGLONG FileOffset; + ULONG Length; + +} AFD_MDL_COMPLETION_CONTEXT, *PAFD_MDL_COMPLETION_CONTEXT; + +// +// A structure for tracking info on checked builds. +// + +#if DBG +typedef struct _AFD_TRANSMIT_TRACE_INFO { + PVOID Caller; + PVOID CallersCaller; + ULONG Reason; + PVOID OtherInfo; +} AFD_TRANSMIT_TRACE_INFO, *PAFD_TRANSMIT_TRACE_INFO; +#endif + +NTSTATUS +AfdBuildReadIrp ( + IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo + ); + +ULONG +AfdBuildTransmitSendMdls ( + IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo, + IN PMDL FileMdlChain, + IN ULONG FileDataLength, + IN PAFD_TRANSMIT_IRP_INFO IrpInfo, + OUT PIRP *DisconnectIrp + ); + +BOOLEAN +AfdCancelIrp ( + IN PIRP Irp + ); + +VOID +AfdCompleteTransmit ( + IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo, + IN NTSTATUS Status, + IN KIRQL OldIrql + ); + +VOID +AfdQueueTransmitRead ( + IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo + ); + +NTSTATUS +AfdRestartTransmitRead ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +NTSTATUS +AfdRestartTransmitSend ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +VOID +AfdStartTransmitIo ( + IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo + ); + +VOID +AfdStartNextQueuedTransmitFile( + VOID + ); + +NTSTATUS +AfdRestartMdlReadComplete ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ); + +VOID +AfdDeferredMdlReadComplete( + IN PVOID Context + ); + +#define AfdAllocateMdlCompletionContext() \ + AFD_ALLOCATE_POOL( \ + NonPagedPool, \ + sizeof(AFD_MDL_COMPLETION_CONTEXT), \ + AFD_MDL_COMPLETION_CONTEXT_POOL_TAG \ + ) + +#define AfdFreeMdlCompletionContext(context) \ + AFD_FREE_POOL( \ + (context), \ + AFD_MDL_COMPLETION_CONTEXT_POOL_TAG \ + ) + + +// +// Macros to control the status of an IRP. +// + +#define SET_IRP_BUSY(_irp) ((_irp)->UserIosb = (PVOID)0xFFFFFFFF) +#define SET_IRP_FREE(_irp) ((_irp)->UserIosb = (PVOID)0) +#define IS_IRP_BUSY(_irp) ((_irp)->UserIosb != 0) + +// +// A macro to rebuild a send IRP to contain the appropriate information. +// This is required because the IO system zeros each IRP stack location +// whenever an IRP completes. +// + + +#define AfdRebuildSend(Irp, FileObj, SendLen)\ + { \ + PTDI_REQUEST_KERNEL_SEND p; \ + PIO_STACK_LOCATION _IRPSP; \ + _IRPSP = IoGetNextIrpStackLocation (Irp); \ + ASSERT( _IRPSP->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL ); \ + _IRPSP->MinorFunction = TDI_SEND; \ + _IRPSP->FileObject = FileObj; \ + _IRPSP->Control = SL_INVOKE_ON_SUCCESS | \ + SL_INVOKE_ON_ERROR | \ + SL_INVOKE_ON_CANCEL; \ + p = (PTDI_REQUEST_KERNEL_SEND)&_IRPSP->Parameters; \ + p->SendLength = SendLen; \ + } + +// +// Debugging macros. +// + +#if DBG + +#define MAX_TRACE 255 + +#if defined(_X86_) +#define UPDATE_TRANSMIT_DEBUG_INFO( _ti, _reason, _other ) \ +{ \ + PAFD_TRANSMIT_TRACE_INFO _trace; \ + PULONG _index = (PULONG)(&((_ti)->Debug2)); \ + \ + *_index += 1; \ + \ + if ( *_index == MAX_TRACE ) { \ + *_index = 0; \ + } \ + \ + _trace = (PAFD_TRANSMIT_TRACE_INFO)(_ti)->Debug1 + *_index; \ + \ + RtlGetCallersAddress( &_trace->Caller, &_trace->CallersCaller ); \ + \ + _trace->Reason = _reason; \ + _trace->OtherInfo = _other; \ +} +#else +#define UPDATE_TRANSMIT_DEBUG_INFO( _ti, _reason, _other ) +#endif \ + +#define SET_READ_PENDING(_ti, _value) \ +{ \ + ASSERT( KeGetCurrentIrql( ) >= DISPATCH_LEVEL ); \ + if (_value) { \ + ASSERT( !(_ti)->ReadPending ); \ + (_ti)->ReadPendingLastSetTrueLine = __LINE__; \ + } else { \ + ASSERT( (_ti)->ReadPending ); \ + (_ti)->ReadPendingLastSetFalseLine = __LINE__; \ + } \ + (_ti)->ReadPending = (_value); \ +} + +#else +#define UPDATE_TRANSMIT_DEBUG_INFO( _ti, _reason, _other ) +#define SET_READ_PENDING(_ti,_value) (_ti)->ReadPending = (_value) +#endif + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGEAFD, AfdTransmitFile ) +#pragma alloc_text( PAGEAFD, AfdStartTransmitIo ) +#pragma alloc_text( PAGEAFD, AfdRestartTransmitRead ) +#pragma alloc_text( PAGEAFD, AfdRestartTransmitSend ) +#pragma alloc_text( PAGEAFD, AfdCompleteTransmit ) +#pragma alloc_text( PAGEAFD, AfdBuildReadIrp ) +#pragma alloc_text( PAGEAFD, AfdBuildTransmitSendMdls ) +#pragma alloc_text( PAGEAFD, AfdCancelIrp ) +#pragma alloc_text( PAGEAFD, AfdCancelTransmit ) +#pragma alloc_text( PAGEAFD, AfdQueueTransmitRead ) +#pragma alloc_text( PAGEAFD, AfdCompleteClosePendedTransmit ) +#pragma alloc_text( PAGEAFD, AfdStartNextQueuedTransmitFile ) +#pragma alloc_text( PAGEAFD, AfdFastTransmitFile ) +#pragma alloc_text( PAGEAFD, AfdMdlReadComplete ) +#pragma alloc_text( PAGEAFD, AfdRestartMdlReadComplete ) +#pragma alloc_text( PAGEAFD, AfdDeferredMdlReadComplete ) +#endif + + +NTSTATUS +AfdTransmitFile ( + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + Initial entrypoint for handling transmit file IRPs. This routine + verifies parameters, initializes data structures to be used for + the request, and initiates the I/O. + +Arguments: + + Irp - a pointer to a transmit file IRP. + + IrpSp - Our stack location for this IRP. + +Return Value: + + STATUS_PENDING if the request was initiated successfully, or a + failure status code if there was an error. + +--*/ + +{ + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + NTSTATUS status; + PAFD_TRANSMIT_FILE_INFO_INTERNAL transmitInfo = NULL; + KIRQL oldIrql; + BOOLEAN lockedHead; + BOOLEAN lockedTail; + BOOLEAN clearTransmitIrpOnFailure = FALSE; + + // + // Initial request validity checks: is the endpoint connected, is + // the input buffer large enough, etc. + // + + endpoint = IrpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + if ( endpoint->Type != AfdBlockTypeVcConnecting || + endpoint->State != AfdEndpointStateConnected ) { + status = STATUS_INVALID_CONNECTION; + goto complete; + } + + connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof(AFD_TRANSMIT_FILE_INFO) ) { + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + // + // Ensure there are no other TransmitFile IRPs pending on this + // endpoint. Only allowing one at a time makes completion and + // cancellation much simpler. + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + if( endpoint->TransmitIrp != NULL ) { + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + status = STATUS_INVALID_PARAMETER; + goto complete; + + } + + endpoint->TransmitIrp = Irp; + clearTransmitIrpOnFailure = TRUE; + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // If this endpoint already has an internal info buffer, use it. + // Otherwise, allocate a new one from nonpaged pool. We will use + // this structure to track the request. We'll also store a pointer + // to the structure in the IRP so we can access it whenever we + // have a pointer to the IRP. + // + + transmitInfo = endpoint->TransmitInfo; + + if( transmitInfo == NULL ) { + + transmitInfo = AFD_ALLOCATE_POOL( + NonPagedPool, + sizeof(*transmitInfo), + AFD_TRANSMIT_INFO_POOL_TAG + ); + + if ( transmitInfo == NULL ) { + + status = STATUS_INSUFFICIENT_RESOURCES; + goto complete; + + } + + endpoint->TransmitInfo = transmitInfo; + + } + + Irp->AssociatedIrp.SystemBuffer = transmitInfo; + + // + // NULL the head and tail MDL pointers so that we know whether + // we need to unlock and free them on exit. + // + + transmitInfo->HeadMdl = NULL; + lockedHead = FALSE; + transmitInfo->TailMdl = NULL; + lockedTail = FALSE; + + // + // Set up the rest of the transmit info structure. input buffer, + // then set up defaults. + // + + transmitInfo->FileObject = NULL; + transmitInfo->TdiFileObject = connection->FileObject; + transmitInfo->TdiDeviceObject = connection->DeviceObject; + transmitInfo->BytesRead = 0; + transmitInfo->BytesSent = 0; + + transmitInfo->TransmitIrp = Irp; + transmitInfo->Endpoint = endpoint; + transmitInfo->FileMdl = NULL; + transmitInfo->ReadPending = TRUE; + transmitInfo->CompletionPending = FALSE; + + transmitInfo->FirstFileMdlAfterHead = NULL; + transmitInfo->LastFileMdlBeforeTail = NULL; + transmitInfo->IrpUsedToSendTail = NULL; + + transmitInfo->Read.Irp = NULL; + transmitInfo->Read.AfdBuffer = NULL; + + transmitInfo->Send1.Irp = NULL; + transmitInfo->Send1.AfdBuffer = NULL; + + transmitInfo->Send2.Irp = NULL; + transmitInfo->Send2.AfdBuffer = NULL; + + transmitInfo->Queued = FALSE; + +#if DBG + transmitInfo->Debug1 = NULL; +#endif + + // + // Because we're using type 3 (neither) I/O for this IRP, the I/O + // system does no verification on the user buffer. Therefore, we + // must manually check it for validity inside a try-except block. + // We also leverage the try-except to validate and lock down the + // head and/or tail buffers specified by the caller. + // + + try { + + if( Irp->RequestorMode != KernelMode ) { + + // + // Validate the control buffer. + // + + ProbeForRead( + IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, + IrpSp->Parameters.DeviceIoControl.InputBufferLength, + sizeof(ULONG) + ); + + } + + // + // Copy over the user's buffer into our information structure. + // + + RtlCopyMemory( + transmitInfo, + IrpSp->Parameters.DeviceIoControl.Type3InputBuffer, + sizeof(AFD_TRANSMIT_FILE_INFO) + ); + + // + // If the caller specified head and/or tail buffers, probe and + // lock the buffers so that we have MDLs we can use to send the + // buffers. + // + + if ( transmitInfo->HeadLength > 0 ) { + + transmitInfo->HeadMdl = IoAllocateMdl( + transmitInfo->Head, + transmitInfo->HeadLength, + FALSE, // SecondaryBuffer + TRUE, // ChargeQuota + NULL // Irp + ); + if ( transmitInfo->HeadMdl == NULL ) { + ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); + } + + MmProbeAndLockPages( transmitInfo->HeadMdl, UserMode, IoReadAccess ); + lockedHead = TRUE; + + // + // Remember that we will need to send this head buffer. + // This flag will be reset as soon as the head buffer is + // given to the TDI provider. + // + + transmitInfo->NeedToSendHead = TRUE; + + } else { + + transmitInfo->NeedToSendHead = FALSE; + } + + if ( transmitInfo->TailLength > 0 ) { + + transmitInfo->TailMdl = IoAllocateMdl( + transmitInfo->Tail, + transmitInfo->TailLength, + FALSE, // SecondaryBuffer + TRUE, // ChargeQuota + NULL // Irp + ); + if ( transmitInfo->TailMdl == NULL ) { + ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); + } + + MmProbeAndLockPages( transmitInfo->TailMdl, UserMode, IoReadAccess ); + lockedTail = TRUE; + } + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + status = GetExceptionCode(); + goto complete; + } + + // + // Set up some tracking information for debugging purposes. + // + +#if DBG + transmitInfo->Completed = FALSE; + + transmitInfo->ReadPendingLastSetTrueLine = 0xFFFFFFFF; + transmitInfo->ReadPendingLastSetFalseLine = 0xFFFFFFFF; + + transmitInfo->Debug1 = AFD_ALLOCATE_POOL( + NonPagedPool, + sizeof(AFD_TRANSMIT_TRACE_INFO) * MAX_TRACE, + AFD_TRANSMIT_DEBUG_POOL_TAG + ); + + if ( transmitInfo->Debug1 == NULL ) { + + status = STATUS_INSUFFICIENT_RESOURCES; + goto complete; + + } + + transmitInfo->Debug2 = (PVOID)0; + + RtlZeroMemory( + transmitInfo->Debug1, + sizeof(AFD_TRANSMIT_TRACE_INFO) * MAX_TRACE + ); +#endif + + // + // Validate any flags specified in the request. + // + + if ( (transmitInfo->Flags & + ~(AFD_TF_WRITE_BEHIND | AFD_TF_DISCONNECT | AFD_TF_REUSE_SOCKET) ) + != 0 ) { + status = STATUS_INVALID_PARAMETER; + goto complete; + } + + // + // Setting AFD_TF_REUSE_SOCKET implies that a disconnect is desired. + // Also, setting this flag means that no more I/O is legal on the + // endpoint until the transmit request has been completed, so + // set up the endpoint's state so that I/O fails. + // + + if ( (transmitInfo->Flags & AFD_TF_REUSE_SOCKET) != 0 ) { + transmitInfo->Flags |= AFD_TF_DISCONNECT; + endpoint->State = AfdEndpointStateTransmitClosing; + } + + // + // Get a referenced pointer to the file object for the file that + // we're going to transmit. This call will fail if the file handle + // specified by the caller is invalid. + // + + status = ObReferenceObjectByHandle( + transmitInfo->FileHandle, + FILE_READ_DATA, + *IoFileObjectType, + UserMode, + (PVOID *)&transmitInfo->FileObject, + NULL + ); + if ( !NT_SUCCESS(status) ) { + goto complete; + } + + AfdRecordFileRef(); + + // + // Get the device object for the file system that supports this + // file. We can't just use the device object stored in the file + // object because that device object typically refers to the disk + // driver device and we want the file system device object. + // + + transmitInfo->DeviceObject = + IoGetRelatedDeviceObject( transmitInfo->FileObject ); + + // + // If this is a synchronous file object AND the offset specified + // by the caller is zero, then start the transmission from the + // current byte offset in the file. + // + + if ( transmitInfo->FileObject->Flags & FO_SYNCHRONOUS_IO && + transmitInfo->Offset == 0 ) { + + transmitInfo->Offset = transmitInfo->FileObject->CurrentByteOffset.QuadPart; + } + + // + // If the caller requested that we transmit the entire file, + // determine current file length so that we know when to quit. + // + + if ( transmitInfo->FileWriteLength == 0 ) { + + FILE_STANDARD_INFORMATION fileInfo; + IO_STATUS_BLOCK ioStatusBlock; + + status = ZwQueryInformationFile( + transmitInfo->FileHandle, + &ioStatusBlock, + &fileInfo, + sizeof(fileInfo), + FileStandardInformation + ); + if ( !NT_SUCCESS(status) ) { + goto complete; + } + + // + // Calculate the number of bytes to send from the file. + // This is the total file length less the specified offset. + // + + transmitInfo->FileWriteLength = + fileInfo.EndOfFile.QuadPart - transmitInfo->Offset; + } + + // + // Determine the total number of bytes we will send, including head + // and tail buffers. + // + + transmitInfo->TotalBytesToSend = + transmitInfo->FileWriteLength + + transmitInfo->HeadLength + transmitInfo->TailLength; + + // + // Allocate and initialize the IRPs we'll give to the TDI provider + // to actually send data. We allocate them with a stack size one + // larger than requested by the TDI provider so that we have a stack + // location for ourselves. + // + // !!! It would be good not to allocate the second send IRP unless + // we knew it was really necessary. + // + + transmitInfo->Send1.Irp = + IoAllocateIrp( connection->DeviceObject->StackSize, TRUE ); + + if ( transmitInfo->Send1.Irp == NULL ) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto complete; + } + + TdiBuildSend( + transmitInfo->Send1.Irp, + connection->DeviceObject, + connection->FileObject, + AfdRestartTransmitSend, + transmitInfo, + NULL, + 0, + 0, + ); + + transmitInfo->Send2.Irp = + IoAllocateIrp( connection->DeviceObject->StackSize, TRUE ); + + if ( transmitInfo->Send2.Irp == NULL ) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto complete; + } + + TdiBuildSend( + transmitInfo->Send2.Irp, + connection->DeviceObject, + connection->FileObject, + AfdRestartTransmitSend, + transmitInfo, + NULL, + 0, + 0, + ); + + // + // Determine the maximum read size that we'll use. If specified by + // the caller, respect it. Otherwise, choose an intelligent default + // based on whether the file system of interest supports the cache + // manager routines for fast file I/O. If the file system does not + // support the cache manager interfaces, we will need to allocate + // our own buffers so use a smaller, more sensible size. + // + + if ( transmitInfo->SendPacketLength == 0 ) { + if ( (transmitInfo->FileObject->Flags & FO_CACHE_SUPPORTED) != 0 ) { + transmitInfo->SendPacketLength = AfdTransmitIoLength; + } else { + transmitInfo->SendPacketLength = AfdLargeBufferSize; + } + } + + // + // Check if the transmit IRP has been cancelled. If so, quit + // now. + // + + IoAcquireCancelSpinLock( &oldIrql ); + + if ( Irp->Cancel ) { + IoReleaseCancelSpinLock( oldIrql ); + status = STATUS_CANCELLED; + goto complete; + } + + // + // Mark the transmit IRP as pending and set up the status field + // in the IRP. We leave it as STATUS_SUCCESS until some sort of + // failure occurs, at which point we modify to the failure + // code. + // + + IoMarkIrpPending( Irp ); + Irp->IoStatus.Status = STATUS_SUCCESS; + + // + // Set up the cancel routine in the IRP. The IRP pointer in the endpoint + // should already point to the current IRP. + // + + IoSetCancelRoutine( Irp, AfdCancelTransmit ); + ASSERT( endpoint->TransmitIrp == Irp ); + + // + // Determine if we can really start this file transmit now. If we've + // exceeded the configured maximum number of active TransmitFile + // requests, then append this IRP to the TransmitFile queue and set + // a flag in the transmit info structure to indicate that this IRP + // is queued. + // + + if( AfdMaxActiveTransmitFileCount > 0 ) { + + if( AfdActiveTransmitFileCount >= AfdMaxActiveTransmitFileCount ) { + + InsertTailList( + &AfdQueuedTransmitFileListHead, + &Irp->Tail.Overlay.ListEntry + ); + + transmitInfo->Queued = TRUE; + IoReleaseCancelSpinLock( oldIrql ); + return STATUS_PENDING; + + } else { + + AfdActiveTransmitFileCount++; + + } + + } + + IoReleaseCancelSpinLock( oldIrql ); + + // + // Start I/O for the file transmit. + // + + AfdStartTransmitIo( transmitInfo ); + + // + // Everything looks good so far. Indicate to the caller that we + // successfully pended their request. + // + + return STATUS_PENDING; + +complete: + + // + // There was a failure of some sort. Clean up and exit. + // + + if ( transmitInfo != NULL ) { + + if ( transmitInfo->HeadMdl != NULL ) { + if ( lockedHead ) { + MmUnlockPages( transmitInfo->HeadMdl ); + } + IoFreeMdl( transmitInfo->HeadMdl ); + } + + if ( transmitInfo->TailMdl != NULL ) { + if ( lockedTail ) { + MmUnlockPages( transmitInfo->TailMdl ); + } + IoFreeMdl( transmitInfo->TailMdl ); + } + + if ( transmitInfo->FileObject != NULL ) { + ObDereferenceObject( transmitInfo->FileObject ); + AfdRecordFileDeref(); + } + + if ( transmitInfo->Read.Irp != NULL ) { + IoFreeIrp( transmitInfo->Read.Irp ); + } + + if ( transmitInfo->Send1.Irp != NULL ) { + IoFreeIrp( transmitInfo->Send1.Irp ); + } + + if ( transmitInfo->Send2.Irp != NULL ) { + IoFreeIrp( transmitInfo->Send2.Irp ); + } + +#if DBG + if ( transmitInfo->Debug1 != NULL ) { + AFD_FREE_POOL( + transmitInfo->Debug1, + AFD_TRANSMIT_DEBUG_POOL_TAG + ); + } +#endif + + // + // Don't free the transmit info structure here, leave + // it attached to the endpoint. The structure will get + // freed when we free the endpoint. + // + + } + + // + // Zap the IRP pointer from the endpoint if necessary. + // + + if( clearTransmitIrpOnFailure ) { + endpoint->TransmitIrp = NULL; + } + + // + // Complete the request. + // + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = status; + IoCompleteRequest( Irp, 0 ); + + return status; + +} // AfdTransmitFile + + +VOID +AfdStartTransmitIo ( + IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo + ) + +/*++ + +Routine Description: + + This is the key routine for initiating transmit I/O. In the high- + performance case of reading a file from the system cache, this + routine gets file data MDLs and passes them off to the TDI provider. + If it cannot get file data inline, it pends a read IRP to get the + file data. + +Arguments: + + TransmitInfo - a pointer to the TransmitInfo structure which + contains information about the request to process. + +Return Value: + + None. + +Notes: + + Because it is illegal to call the FsRtl fast read routines at raised + IRQL or from within a completion routine, this routine must be + called from a thread whose context we own. This routine cannot be + called at distapch level or from a completion routine. + +--*/ + +{ + IO_STATUS_BLOCK ioStatus; + BOOLEAN success; + PMDL fileMdl; + KIRQL oldIrql; + KIRQL cancelIrql; + PAFD_ENDPOINT endpoint; + PAFD_TRANSMIT_IRP_INFO irpInfo; + ULONG readLength; + ULONG sendLength; + LONGLONG readOffset; + NTSTATUS status; + PIRP disconnectIrp; + PDEVICE_OBJECT tdiDeviceObject; + + // + // It is illegal to call the fast I/O routines or to submit MDL read + // IRPs at raised IRQL. + // + + ASSERT( KeGetCurrentIrql( ) < DISPATCH_LEVEL ); + + // + // Initialize local variables. + // + + endpoint = TransmitInfo->Endpoint; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + ASSERT( endpoint->TransmitInfo == TransmitInfo ); + + ASSERT( TransmitInfo->FileMdl == NULL ); + ASSERT( TransmitInfo->ReadPending ); + ASSERT( TransmitInfo->TransmitIrp != NULL ); + + // + // We have to have a special case to handle a file of length 0. + // + + if ( TransmitInfo->FileWriteLength == 0 ) { + + // + // Remember that there is not a read pending. + // + + TransmitInfo->ReadPending = FALSE; + + // + // If there was neither a head nor tail, just complete the + // transmit request now. + // + + if ( TransmitInfo->TotalBytesToSend == 0 ) { + + // + // If we need to initiate a disconnect on the endpoint, do + // so. + // + + if ( (TransmitInfo->Flags & AFD_TF_DISCONNECT) != 0 ) { + (VOID)AfdBeginDisconnect( endpoint, NULL, NULL ); + } + + // + // We must hold the cancel spin lock when completing the + // transmit IRP. + // + + IoAcquireCancelSpinLock( &oldIrql ); + AfdCompleteTransmit( TransmitInfo, STATUS_SUCCESS, oldIrql ); + + return; + } + + // + // There is a head and/or a tail buffer to send. Build an IRP + // and send the buffer(s). + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + sendLength = AfdBuildTransmitSendMdls( + TransmitInfo, + NULL, + 0, + &TransmitInfo->Send1, + &disconnectIrp + ); + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // Remember the TDI device object in a local variable. This is + // necessary because as soon as we call IoCallDriver for the + // send, the TransmitInfo structure may be freed up and have + // been reset. + // + + tdiDeviceObject = TransmitInfo->TdiDeviceObject; + + // + // We'll use the first send IRP to send the head/tail buffers. + // + + SET_IRP_BUSY( TransmitInfo->Send1.Irp ); + + AfdRebuildSend( + TransmitInfo->Send1.Irp, + TransmitInfo->TdiFileObject, + sendLength + ); + + // + // Send the head and/or tail data. + // + + IoCallDriver( tdiDeviceObject, TransmitInfo->Send1.Irp ); + + // + // If appropriate, submit a graceful disconnect on the endpoint. + // + + if ( disconnectIrp != NULL ) { + IoCallDriver( tdiDeviceObject, disconnectIrp ); + } + + return; + } + + // + // If the file system supports the fast cache manager interface, + // attempt to use the fast path to get file data MDLs. + // + + if ( (TransmitInfo->FileObject->Flags & FO_CACHE_SUPPORTED) != 0 ) { + + while ( TRUE ) { + + // + // Set fileMdl to NULL because FsRtlMdlRead attempts to + // chain the MDLs it returns off the input MDL variable. + // + + fileMdl = NULL; + + // + // Determine how many bytes we will attempt to read. This + // is either the send packet size or the remaining bytes in + // the file, whichever is less. + // + + if ( TransmitInfo->FileWriteLength - + TransmitInfo->BytesRead > + TransmitInfo->SendPacketLength ) { + readLength = TransmitInfo->SendPacketLength; + } else { + readLength = (ULONG)(TransmitInfo->FileWriteLength - + TransmitInfo->BytesRead); + } + + TransmitInfo->Read.Length = readLength; + + // + // If we have read everything we needed to read from the file, + // quit reading. + // + + if ( readLength == 0 ) { + + // + // Note that we're no longer in the process of reading + // file data. + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + SET_READ_PENDING( TransmitInfo, FALSE ); + + // + // If we are attempting to process completion of the + // transmit IRP, pass control to AfdCompleteTransmit() + // so that it can continue completion processing with + // the read pending flag turned off. + // + + if ( TransmitInfo->CompletionPending ) { + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoAcquireCancelSpinLock( &oldIrql ); + AfdCompleteTransmit( TransmitInfo, STATUS_SUCCESS, oldIrql ); + + } else { + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + } + + return; + } + + // + // Determine the offset in the file at which to start + // reading. Because all reads are funnelled through this + // function, and because this function can run in only one + // thread at a time, there isn't a synchronization issue on + // these fields of the TransmitInfo structure. + // + + readOffset = + TransmitInfo->Offset + TransmitInfo->BytesRead; + + // + // Attempt to use the fast path to get file data MDLs + // directly from the cache manager. + // + + success = FsRtlMdlRead( + TransmitInfo->FileObject, + (PLARGE_INTEGER)&readOffset, + readLength, + 0, + &fileMdl, + &ioStatus + ); + + // + // If the fast path succeeded and we have a send IRP available, + // give the send IRP to the TDI provider. + // + + if ( success ) { + + // + // If we read less information than was requested, we + // must have hit the end of the file. Fail the transmit + // request, since this can only happen if the caller + // requested that we send more data than the file + // currently contains. + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + if ( ioStatus.Information < readLength ) { + + TransmitInfo->CompletionPending = TRUE; + TransmitInfo->TransmitIrp->IoStatus.Status = + STATUS_END_OF_FILE; + + // + // If we got some MDLs from the file system, return + // them. + // + + if ( fileMdl != NULL ) { + status = AfdMdlReadComplete( + TransmitInfo->FileObject, + fileMdl, + readOffset, + readLength + ); + ASSERT( NT_SUCCESS(status) ); + + fileMdl = NULL; + } + } + + // + // Update the count of bytes read from the file. + // + + TransmitInfo->BytesRead = + TransmitInfo->BytesRead + ioStatus.Information; + + // + // If we're in the process of completing the transmit + // IRP, do not submit a send. Instead call + // AfdCompleteTransmit() so that it can continue + // completing the transmit IRP. + // + + if ( TransmitInfo->CompletionPending ) { + + // + // Release the endpoint spin lock, grab the cancel + // spin lock, then reacquire the endpoint lock. + // This is required in order to preserve lock + // acquisition ordering. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoAcquireCancelSpinLock( &cancelIrql ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + // + // Remember the file MDL and the fact that a read + // is no longer pending. + // + + TransmitInfo->FileMdl = fileMdl; + SET_READ_PENDING( TransmitInfo, FALSE ); + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // Now continue completing the transmit IRP. + // + + AfdCompleteTransmit( + TransmitInfo, + STATUS_SUCCESS, + cancelIrql + ); + + return; + } + + ASSERT( fileMdl != NULL ); + ASSERT( NT_SUCCESS(ioStatus.Status) ); + ASSERT( ioStatus.Information == readLength ); + + // + // Attempt to find a send IRP which is available to use. + // + + if ( !IS_IRP_BUSY( TransmitInfo->Send1.Irp ) ) { + + irpInfo = &TransmitInfo->Send1; + SET_IRP_BUSY( TransmitInfo->Send1.Irp ); + + } else if ( !IS_IRP_BUSY( TransmitInfo->Send2.Irp ) ) { + + irpInfo = &TransmitInfo->Send2; + SET_IRP_BUSY( TransmitInfo->Send2.Irp ); + + } else { + + // + // There are no send IRPs that we can use to send + // this file data. Just hang on to the data until + // a send IRP completes. + // + + TransmitInfo->FileMdl = fileMdl; + TransmitInfo->FileMdlLength = readLength; + SET_READ_PENDING( TransmitInfo, FALSE ); + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + return; + } + + // + // Finish building the send IRP and give it to the TDI + // provider. + // + + sendLength = AfdBuildTransmitSendMdls( + TransmitInfo, + fileMdl, + readLength, + irpInfo, + &disconnectIrp + ); + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // Remember the TDI device object in a local variable. + // This is necessary because as soon as we call + // IoCallDriver for the send, the TransmitInfo structure + // may be freed up and have been reset. + // + + tdiDeviceObject = TransmitInfo->TdiDeviceObject; + + AfdRebuildSend( + irpInfo->Irp, + TransmitInfo->TdiFileObject, + sendLength + ); + + IoCallDriver( tdiDeviceObject, irpInfo->Irp ); + + // + // If appropriate, submit a graceful disconnect on the + // endpoint. + // + + if ( disconnectIrp != NULL ) { + IoCallDriver( tdiDeviceObject, disconnectIrp ); + } + + // + // Try to get more file data to send out. + // + + } else { + + ASSERT( fileMdl == NULL ); + + // + // Drop through to build an IRP to retrieve the file + // data. + // + + break; + } + } + } + + // + // Either the file system does not support the fast cache manager + // interface or else fast I/O failed. We'll have to build a read + // IRP and submit it to the file system + // + + status = AfdBuildReadIrp( TransmitInfo ); + if ( !NT_SUCCESS(status) ) { + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + SET_READ_PENDING( TransmitInfo, FALSE ); + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + IoAcquireCancelSpinLock( &TransmitInfo->TransmitIrp->CancelIrql ); + AfdCancelTransmit( NULL, TransmitInfo->TransmitIrp ); + + return; + } + + // + // If the transmit IRP is in the process of being completed, do not + // submit the read IRP. Instead, call AfdCompleteTransmit() after + // turning off the ReadPending bit so that it can continue + // completion of the transmit IRP. + // + + IoAcquireCancelSpinLock( &cancelIrql ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + if ( TransmitInfo->CompletionPending ) { + + ASSERT( !IS_IRP_BUSY( TransmitInfo->Read.Irp ) ); + SET_READ_PENDING( TransmitInfo, FALSE ); + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + AfdCompleteTransmit( TransmitInfo, STATUS_SUCCESS, cancelIrql ); + + return; + } + + // + // We're committed to posting the read IRP. Remember that it is + // busy. Note that there is a small window between setting the IRP + // busy and the actual submission where we may need to cancel the + // IRP. This is handled by the fact that if we attempt to cancel + // the IRP before the driver gets it, the Cencel flag will be set in + // the IRP and the driver should fail it immediately. + // + + SET_IRP_BUSY( TransmitInfo->Read.Irp ); + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + IoCallDriver( TransmitInfo->DeviceObject, TransmitInfo->Read.Irp ); + + // + // Leave the ReadPending flag set in the TransmitInfo structure + // until the read IRP completes. + // + + return; + +} // AfdStartTransmitIo + + +NTSTATUS +AfdRestartTransmitRead ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This is the completion routine for transmit read IRPs. It updates the + context based on the result of the read operation and attempts to find + a send IRP to use to give the read data to the TDI provider. If no + send IRP is available, it just holds on to the read data until a send + IRP completes. + +Arguments: + + DeviceObject - ignored. + + Irp - the read IRP that is completing. + + Context - a pointer to the TransmitInfo structure that describes + the transmit file request corresponding to the IRP that is + completing. + +Return Value: + + STATUS_MORE_PROCESSING_REQUIRED which indicates to the I/O system + that it should stop completion processing of this IRP. We handle + all completion from this point forward. + +--*/ + +{ + PAFD_TRANSMIT_FILE_INFO_INTERNAL transmitInfo; + PAFD_TRANSMIT_IRP_INFO irpInfo; + KIRQL oldIrql; + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + BOOLEAN readMore; + ULONG sendLength; + PIRP disconnectIrp; + PDEVICE_OBJECT tdiDeviceObject; + + // + // Initialize local variables. + // + + transmitInfo = Context; + ASSERT( transmitInfo->Read.Irp == Irp ); + + endpoint = transmitInfo->Endpoint; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + ASSERT( endpoint->TransmitInfo == transmitInfo ); + + connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + ASSERT( IS_IRP_BUSY( Irp ) ); + ASSERT( transmitInfo->ReadPending ); + + // + // If the read failed or we're in the process of completing the + // transmit IRP, start/continue the process of completing the + // transmit IRP. + // + + if ( !NT_SUCCESS(Irp->IoStatus.Status) || transmitInfo->CompletionPending ) { + + KIRQL cancelIrql; + + IoAcquireCancelSpinLock( &cancelIrql ); + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + SET_IRP_FREE( Irp ); + SET_READ_PENDING( transmitInfo, FALSE ); + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + KdPrint(( "AfdRestartTransmitRead: failed, status == %lx\n", + Irp->IoStatus.Status )); + + AfdCompleteTransmit( transmitInfo, Irp->IoStatus.Status, cancelIrql ); + return STATUS_MORE_PROCESSING_REQUIRED; + } + + // + // Update the count of bytes we have read from the file so far. + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + transmitInfo->BytesRead = + transmitInfo->BytesRead + Irp->IoStatus.Information; + + // + // If this is a non-cached read, update the MDL byte count in the + // AFD buffer to reflect the count of bytes actually read. + // + + if ( transmitInfo->Read.AfdBuffer != NULL ) { + transmitInfo->Read.AfdBuffer->Mdl->ByteCount = Irp->IoStatus.Information; + } + + // + // Remember that the read IRP is no longer in use. + // + + SET_IRP_FREE( Irp ); + + // + // Check whether one of the send IRPs is waiting for data. + // + + irpInfo = NULL; + + if ( !IS_IRP_BUSY( transmitInfo->Send1.Irp ) ) { + irpInfo = &transmitInfo->Send1; + } else if ( !IS_IRP_BUSY( transmitInfo->Send2.Irp ) ) { + irpInfo = &transmitInfo->Send2; + } + + // + // If both send IRPs are in the TDI provider, reset the read IRP to + // "free", remember that there is read data available, and wait for + // a send to complete. + // + + if ( irpInfo == NULL ) { + + ASSERT( transmitInfo->FileMdl == NULL ); + + transmitInfo->FileMdl = Irp->MdlAddress; + Irp->MdlAddress = NULL; + transmitInfo->FileMdlLength = Irp->IoStatus.Information; + SET_IRP_FREE( Irp ); + SET_READ_PENDING( transmitInfo, FALSE ); + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + return STATUS_MORE_PROCESSING_REQUIRED; + } + + // + // There is a send IRP that we'll give to the TDI provider. Move + // the buffers from the read IRP to the send IRP. If we need to + // send the head buffer, do so. + // + + sendLength = AfdBuildTransmitSendMdls( + transmitInfo, + Irp->MdlAddress, + Irp->IoStatus.Information, + irpInfo, + &disconnectIrp + ); + + // + // If we have read all of the file data we were requested to read, + // there is no need to submit another read IRP. Check for this + // condition before releasing the lock in order to synchronize with + // send completion. + // + + if ( transmitInfo->BytesRead == transmitInfo->FileWriteLength ) { + + readMore = FALSE; + + // + // Note that there is no longer a read pending for this transmit + // IRP. + // + + SET_READ_PENDING( transmitInfo, FALSE ); + + } else { + + // + // Leave the ReadPending flag set so that completion does not + // occur between our releasing the spin lock and attempting to + // queue another read. + // + + readMore = TRUE; + } + + // + // Remember that this send IRP is in use and release the lock. + // + + SET_IRP_BUSY( irpInfo->Irp ); + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // Remember the TDI device object in a local variable. This is + // necessary because as soon as we call IoCallDriver for the send, + // the TransmitInfo structure may be freed up and have been reset. + // + + tdiDeviceObject = transmitInfo->TdiDeviceObject; + + // + // Finish setting up the send IRP and hand it off to the TDI + // provider. + // + + AfdRebuildSend( irpInfo->Irp, transmitInfo->TdiFileObject, sendLength ); + + IoCallDriver( tdiDeviceObject, irpInfo->Irp ); + + // + // If appropriate, submit a graceful disconnect on the endpoint. + // + + if ( disconnectIrp != NULL ) { + IoCallDriver( tdiDeviceObject, disconnectIrp ); + } + + // + // If there's more file data to read, queue work to a thread so that + // we can perform another read on the file. It is illegal to do a + // cache read in a completion routine because completion routines + // may be called at raised IRQL. + // + + if ( readMore ) { + AfdQueueTransmitRead( transmitInfo ); + } + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // AfdRestartTransmitRead + + +NTSTATUS +AfdRestartTransmitSend ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + This is the completion routine for TDI send IRPs used for transmit + file requests. This routine updates information based on the + results of the send and checks whether there is more file data + available to be sent. If there is more data, it is placed into the + send IRP and the IRP is resubmitted to the TDI provider. If no data + is available, the send IRP is simply held until file data becomes + available. + +Arguments: + + DeviceObject - ignored. + + Irp - the send IRP that is completing. + + Context - a pointer to the TransmitInfo structure that describes + the transmit file request corresponding to the IRP that is + completing. + +Return Value: + + STATUS_MORE_PROCESSING_REQUIRED which indicates to the I/O system + that it should stop completion processing of this IRP. We handle + all completion from this point forward. + +--*/ + +{ + PAFD_TRANSMIT_FILE_INFO_INTERNAL transmitInfo; + PAFD_TRANSMIT_IRP_INFO irpInfo; + KIRQL oldIrql; + KIRQL cancelIrql; + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + ULONG sendLength; + BOOLEAN readMore; + BOOLEAN sendAgain; + PIRP disconnectIrp; + PDEVICE_OBJECT tdiDeviceObject; + NTSTATUS status; + + // + // Initialize local variables. + // + + transmitInfo = Context; + + endpoint = transmitInfo->Endpoint; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + ASSERT( endpoint->TransmitInfo == transmitInfo ); + + connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + ASSERT( IS_IRP_BUSY( Irp ) ); + ASSERT( transmitInfo->Send1.Irp == Irp || transmitInfo->Send2.Irp == Irp ); + ASSERT( !transmitInfo->NeedToSendHead ); + + // + // Determine which of the two send IRPs completed. This completion + // routine is used for both of the TDI send IRPs. We use two send + // IRPs in order to ensure that the TDI provider always has enough + // data to send at any given time. This prevents Nagling delays and + // sub-MTU packets. + // + + if ( transmitInfo->Send1.Irp == Irp ) { + irpInfo = &transmitInfo->Send1; + } else { + irpInfo = &transmitInfo->Send2; + } + + // + // If we didn't send any file data, there is no need to return file + // MDLs or AFD buffers. The FileWriteLength will be nonzero if we + // sent file data. + // + + if ( transmitInfo->FileWriteLength != 0 ) { + + // + // Free the AFD buffer if we allocated one for this request, or + // else release the MDLs to the file system. + // + + if ( irpInfo->AfdBuffer != NULL ) { + + // + // If this send IRP was used to send the tail buffer, reset + // the Next pointer of the last file MDL to NULL. + // + + if ( transmitInfo->IrpUsedToSendTail == Irp ) { + transmitInfo->LastFileMdlBeforeTail->Next = NULL; + } + + // + // Free the AFD buffer used for the request. + // + + irpInfo->AfdBuffer->Mdl->ByteCount = irpInfo->AfdBuffer->BufferLength; + AfdReturnBuffer( irpInfo->AfdBuffer ); + irpInfo->AfdBuffer = NULL; + + } else { + + // + // We need to be careful to return only file system MDLs to + // the file system. We cannot give head or tail buffer MDLs + // to the file system. If the head buffer MDL was at the + // front of this IRP's MDL chain, use the first actual file + // MDL. + // + + if ( Irp->MdlAddress == transmitInfo->HeadMdl ) { + Irp->MdlAddress = transmitInfo->FirstFileMdlAfterHead; + } + + // + // If this send IRP was used to send the tail buffer, reset + // the Next pointer of the last file MDL to NULL. + // + + if ( transmitInfo->IrpUsedToSendTail == Irp ) { + transmitInfo->LastFileMdlBeforeTail->Next = NULL; + } + + // + // Now we can return the file data MDLs to the file system. + // + + status = AfdMdlReadComplete( + transmitInfo->FileObject, + Irp->MdlAddress, + transmitInfo->Offset + transmitInfo->BytesRead, + irpInfo->Length + ); + ASSERT( NT_SUCCESS(status) ); + } + } + + // + // If the send failed or we're in the process of completing the + // transmit IRP, start/continue the process of completing the + // transmit IRP. + // + + if ( !NT_SUCCESS(Irp->IoStatus.Status) || transmitInfo->CompletionPending ) { + + KIRQL cancelIrql; + + IoAcquireCancelSpinLock( &cancelIrql ); + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + SET_IRP_FREE( Irp ); + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + KdPrint(( "AfdRestartTransmitSend: failed, status == %lx\n", + Irp->IoStatus.Status )); + + AfdCompleteTransmit( transmitInfo, Irp->IoStatus.Status, cancelIrql ); + return STATUS_MORE_PROCESSING_REQUIRED; + } + + // + // Update the count of file data bytes we've sent so far. + // + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + transmitInfo->BytesSent = + transmitInfo->BytesSent + Irp->IoStatus.Information; + + ASSERT( transmitInfo->BytesSent <= transmitInfo->TotalBytesToSend ); + + // + // If we have sent all of the file data, then we've done all the + // necessary work for the transmit file IRP. Complete the IRP. + // + + if ( transmitInfo->BytesSent == transmitInfo->TotalBytesToSend ) { + + ASSERT( transmitInfo->BytesSent == + transmitInfo->BytesRead + transmitInfo->HeadLength + + transmitInfo->TailLength ); + + // + // Release the endpoint lock, then reacquire both the cancel + // lock and the endpoint lock in the correct lock acquisition + // order. We must hold the cancel spin lock when calling + // AfdCompleteTransmit() and we must hold the endpoint lock to + // set the send IRP as free. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + IoAcquireCancelSpinLock( &cancelIrql ); + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + SET_IRP_FREE( Irp ); + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + AfdCompleteTransmit( transmitInfo, STATUS_SUCCESS, cancelIrql ); + + return STATUS_MORE_PROCESSING_REQUIRED; + } + + // + // Check whether there is more file data waiting to be sent. If + // there is more data we can send it immediately. + // + + if ( transmitInfo->FileMdl != NULL ) { + + // + // There is data available in the read IRP. Move the buffers + // to this send IRP. + // + + sendLength = AfdBuildTransmitSendMdls( + transmitInfo, + transmitInfo->FileMdl, + transmitInfo->FileMdlLength, + irpInfo, + &disconnectIrp + ); + + transmitInfo->FileMdl = NULL; + sendAgain = TRUE; + + // + // If we have read all of the file data we were requested to + // read, there is no need to submit another read IRP. Check for + // this condition before releasing the lock in order to + // synchronize with send completion. Note that we will not + // attempt to submit another read if a read is already pending. + // + + if ( transmitInfo->BytesRead == transmitInfo->FileWriteLength || + transmitInfo->ReadPending ) { + readMore = FALSE; + } else { + readMore = TRUE; + SET_READ_PENDING( transmitInfo, TRUE ); + } + + } else { + + // + // There is no read data available now, so there must already be + // a read pending or else we've sent all the file data. + // + + readMore = FALSE; + ASSERT( transmitInfo->BytesRead == transmitInfo->FileWriteLength || + transmitInfo->ReadPending ); + + // + // This send IRP is now available for use as soon as a read + // completes. + // + + sendAgain = FALSE; + SET_IRP_FREE( Irp ); + } + + // + // Release the lock. We cannot submit IRPs while holding a lock. + // + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // If there was file data immediately available to be sent, finish + // setting up the send IRP and hand it off to the TDI provider. + // + + if ( sendAgain ) { + + // + // Remember the TDI device object in a local variable. This is + // necessary because as soon as we call IoCallDriver for the + // send, the TransmitInfo structure may be freed up and have + // been reset. + // + + tdiDeviceObject = transmitInfo->TdiDeviceObject; + + AfdRebuildSend( irpInfo->Irp, transmitInfo->TdiFileObject, sendLength ); + IoCallDriver( tdiDeviceObject, irpInfo->Irp ); + + // + // If appropriate, submit a graceful disconnect on the endpoint. + // + + if ( disconnectIrp != NULL ) { + IoCallDriver( tdiDeviceObject, disconnectIrp ); + } + } + + // + // If there is more file data to read, queue work to a thread so + // that we can perform another read on the file. It is illegal to + // do a cache read in a completion routine. + // + + if ( readMore ) { + AfdQueueTransmitRead( transmitInfo ); + } + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // AfdRestartTransmitSend + + +VOID +AfdCompleteTransmit ( + IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo, + IN NTSTATUS Status, + IN KIRQL OldIrql + ) + +/*++ + +Routine Description: + + This routine is responsible for handling all aspects of completing a + transmit file request. First it checks to make sure that all + aspects of the request (read IRPs, send IRPs, queued reads to + threads, etc.) have completed. If any are still pending, they + are cancelled and this routine exits until they complete. + + When everything has finished, this routine cleans up the resources + allocated for the request and completed the transmit file IRP. + +Arguments: + + TransmitInfo - a pointer to the TransmitInfo structure which + contains information about the request to process. + +Return Value: + + None. + +--*/ + +{ + KIRQL endpointOldIrql; + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + NTSTATUS status; + BOOLEAN reuseSocket; + PMDL fileMdl; + PIRP transmitIrp; + + endpoint = TransmitInfo->Endpoint; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + ASSERT( endpoint->TransmitInfo == TransmitInfo ); + + connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint ); + ASSERT( connection != NULL ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + // + // Make sure that we hold the cancel spin lock for the followiung + // tests. Then acquire the endpoint spin lock for proper + // synchronization. + // + + ASSERT( KeGetCurrentIrql( ) == DISPATCH_LEVEL ); + + AfdAcquireSpinLock( &endpoint->SpinLock, &endpointOldIrql ); + + transmitIrp = TransmitInfo->TransmitIrp; + + ASSERT( endpoint->TransmitIrp == NULL || endpoint->TransmitIrp == transmitIrp ); + + // + // If this transmit IRP is on the TransmitFile queue, remove it. + // + + if( TransmitInfo->Queued ) { + + RemoveEntryList( + &transmitIrp->Tail.Overlay.ListEntry + ); + + TransmitInfo->Queued = FALSE; + + } + + // + // Remember the completion status in the transmit IRP. Note that we + // use the first failure status code as the completion status code. + // Subsequent failures (for example STATUS_CANCELLED) are ignored. + // + + if ( NT_SUCCESS(transmitIrp->IoStatus.Status) ) { + transmitIrp->IoStatus.Status = Status; + } + + // + // Note that we only attempt to do the disconnect and socket reuse + // if the transmit request was successful. If it fails for any + // reason, we do not begin disconnecting the connection. + // + + if ( !NT_SUCCESS(transmitIrp->IoStatus.Status) || + (TransmitInfo->Flags & AFD_TF_REUSE_SOCKET) == 0 ) { + + endpoint->TransmitIrp = NULL; + reuseSocket = FALSE; + + // + // We're doing our best to complete the transmit IRP, so remove + // the cancel routine from the transmit IRP. Also remove the + // transmit IRP pointer from the endpoint structure in order to + // synchronize with cleanup IRPs. + // + + IoSetCancelRoutine( transmitIrp, NULL ); + + } else { + + PIO_STACK_LOCATION irpSp; + + reuseSocket = TRUE; + + // + // Reset the control code in our stack location in the IRP + // so that the cancel routine knows about the special state + // of this IRP and cancels it appropriately. + // + + irpSp = IoGetCurrentIrpStackLocation( transmitIrp ); + irpSp->Parameters.DeviceIoControl.IoControlCode = 0; + } + + // + // Remember that we are in the process of completing this IRP. + // Other steps in transmit IRP processing use this flag to know + // whether to bail out immediately instead of continuing to attempt + // transmit processing. + // + + TransmitInfo->CompletionPending = TRUE; + + // + // Determine whether any of the child IRPs are still outstanding. + // If any are, cancel them. Note that if any of these IRPs was not + // in a cancellable state (IoCancelIrp() returns FALSE) there's + // nothing we can do, so just plow on and wait for the IRPs to + // complete. + // + // As soon as we hit an active IRP quit processing. We must quit + // because the cancel spin lock gets released by IoCancelIrp() and + // the IRP of interest could get completed immediately and reenter + // AfdCompleteTransmit(), which could complete the transmit IRP + // before we continue executing. + // + + if ( TransmitInfo->Read.Irp != NULL && IS_IRP_BUSY(TransmitInfo->Read.Irp) ) { + + TransmitInfo->Read.Irp->CancelIrql = OldIrql; + UPDATE_TRANSMIT_DEBUG_INFO( TransmitInfo, 1, 0 ); + AfdReleaseSpinLock( &endpoint->SpinLock, endpointOldIrql ); + + AfdCancelIrp( TransmitInfo->Read.Irp ); + + return; + } + + if ( IS_IRP_BUSY(TransmitInfo->Send1.Irp) ) { + + TransmitInfo->Send1.Irp->CancelIrql = OldIrql; + UPDATE_TRANSMIT_DEBUG_INFO( TransmitInfo, 2, 0 ); + AfdReleaseSpinLock( &endpoint->SpinLock, endpointOldIrql ); + + AfdCancelIrp( TransmitInfo->Send1.Irp ); + + return; + } + + if ( IS_IRP_BUSY(TransmitInfo->Send2.Irp) ) { + + TransmitInfo->Send2.Irp->CancelIrql = OldIrql; + UPDATE_TRANSMIT_DEBUG_INFO( TransmitInfo, 3, 0 ); + AfdReleaseSpinLock( &endpoint->SpinLock, endpointOldIrql ); + + AfdCancelIrp( TransmitInfo->Send2.Irp ); + + return; + } + + // + // If there is a read about to happen, bail. When the read + // completes the appropriate routine will check the + // CompletionPending flag and call this routine again. + // + + if ( TransmitInfo->ReadPending ) { + + UPDATE_TRANSMIT_DEBUG_INFO( TransmitInfo, 4, 0 ); + + AfdReleaseSpinLock( &endpoint->SpinLock, endpointOldIrql ); + IoReleaseCancelSpinLock( OldIrql ); + + return; + } + +#if DBG + TransmitInfo->Completed = TRUE; +#endif + + // + // Everything is on track to complete the transmit IRP, so we're + // full steam ahead. Free the resources allocated to service this + // request. + // + + if ( TransmitInfo->Read.Irp != NULL ) { + IoFreeIrp( TransmitInfo->Read.Irp ); + } + + IoFreeIrp( TransmitInfo->Send1.Irp ); + IoFreeIrp( TransmitInfo->Send2.Irp ); + + // + // If there was an AFD buffer with read data, free it. If there was + // no AFD buffer but FileMdl is non-NULL, then we have some file + // system MDLs which we need to free. + // + + if ( TransmitInfo->Read.AfdBuffer != NULL ) { + + TransmitInfo->Read.AfdBuffer->Mdl->ByteCount = + TransmitInfo->Read.AfdBuffer->BufferLength; + AfdReturnBuffer( TransmitInfo->Read.AfdBuffer ); + + TransmitInfo->Read.AfdBuffer = NULL; + + AfdReleaseSpinLock( &endpoint->SpinLock, endpointOldIrql ); + IoReleaseCancelSpinLock( OldIrql ); + + } else if ( TransmitInfo->FileMdl != NULL ) { + + fileMdl = TransmitInfo->FileMdl; + TransmitInfo->FileMdl = NULL; + + AfdReleaseSpinLock( &endpoint->SpinLock, endpointOldIrql ); + IoReleaseCancelSpinLock( OldIrql ); + + status = AfdMdlReadComplete( + TransmitInfo->FileObject, + fileMdl, + TransmitInfo->Offset + TransmitInfo->BytesRead, + TransmitInfo->Read.Length + ); + ASSERT( NT_SUCCESS(status) ); + + } else { + + AfdReleaseSpinLock( &endpoint->SpinLock, endpointOldIrql ); + IoReleaseCancelSpinLock( OldIrql ); + } + + if ( TransmitInfo->Send1.AfdBuffer != NULL ) { + TransmitInfo->Send1.AfdBuffer->Mdl->ByteCount = + TransmitInfo->Send1.AfdBuffer->BufferLength; + AfdReturnBuffer( TransmitInfo->Send1.AfdBuffer ); + } + + if ( TransmitInfo->Send2.AfdBuffer != NULL ) { + TransmitInfo->Send2.AfdBuffer->Mdl->ByteCount = + TransmitInfo->Send2.AfdBuffer->BufferLength; + AfdReturnBuffer( TransmitInfo->Send2.AfdBuffer ); + } + + if ( TransmitInfo->HeadMdl != NULL ) { + MmUnlockPages( TransmitInfo->HeadMdl ); + IoFreeMdl( TransmitInfo->HeadMdl ); + } + + if ( TransmitInfo->TailMdl != NULL ) { + MmUnlockPages( TransmitInfo->TailMdl ); + IoFreeMdl( TransmitInfo->TailMdl ); + } + +#if DBG + if ( TransmitInfo->Debug1 != NULL ) { + AFD_FREE_POOL( + TransmitInfo->Debug1, + AFD_TRANSMIT_DEBUG_POOL_TAG + ); + TransmitInfo->Debug1 = NULL; + } +#endif + + // + // Update the file position and clean up the reference count. + // + + TransmitInfo->FileObject->CurrentByteOffset.QuadPart += TransmitInfo->BytesRead; + + ObDereferenceObject( TransmitInfo->FileObject ); + AfdRecordFileDeref(); + + // + // Remember how many bytes we sent with this request. + // + + transmitIrp->IoStatus.Information = (ULONG)TransmitInfo->BytesSent; + + // + // Begin socket reuse if so requested. This causes the transmit + // not to complete until the connection is fully disconnected + // and the endpoint is ready for reuse, i.e. it is in the same + // state as if it had just been opened. + // + + if ( reuseSocket ) { + + // + // Remember that there is a transmit IRP pended on the endpoint, + // so that when we're freeing up the connection we also complete + // the transmit IRP. + // + + connection->ClosePendedTransmit = TRUE; + + // + // Since we are going to effectively close this connection, + // remember that we have started cleanup on this connection. + // This allows AfdDeleteConnectedReference to remove the + // connected reference when appropriate. + // + + connection->CleanupBegun = TRUE; + + // + // Attempt to remove the connected reference. + // + + AfdDeleteConnectedReference( connection, FALSE ); + + // + // Delete the endpoint's reference to the connection in + // preparation for reusing this endpoint. + // + + endpoint->Common.VcConnecting.Connection = NULL; + DEREFERENCE_CONNECTION( connection ); + + // + // DO NOT access the IRP after this point, since it may have + // been completed inside AfdDereferenceConnection! + // + } + + // + // Complete the actual transmit file IRP, unless we need to pend it + // until the connection is fully disconnected as requested by the + // AFD_TF_REUSE_SOCKET flag. + // + // If we are going to wait on completion until the connection object + // is disconnected, note that we do not have a cancellation routine + // in the transmit IRP any more. This generally shouldn't cause a + // problem because the time for a cancellation is bounded. + // + + if ( !reuseSocket ) { + + IoCompleteRequest( transmitIrp, AfdPriorityBoost ); + + // + // If we're enforcing a maximum active TransmitFile count, then + // check the list of queued TransmitFile requests and start the + // next one. + // + + if( AfdMaxActiveTransmitFileCount > 0 ) { + + AfdStartNextQueuedTransmitFile(); + + } + + } + + // + // Don't free the transmit info structure here, leave + // it attached to the endpoint. The structure will get + // freed when we free the endpoint. + // + +} // AfdCompleteTransmit + + +NTSTATUS +AfdBuildReadIrp ( + IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo + ) + +/*++ + +Routine Description: + + This routine builds a read file data IRP for a transmit file + request. It determines how much data needs to be read and builds an + IRP appropriate to the functionality supported by the file system. + +Arguments: + + TransmitInfo - a pointer to the TransmitInfo structure which + contains information about the request to process. + +Return Value: + + NTSTATUS - indicates whether the IRP was built successfully. + +--*/ + +{ + ULONG readLength; + PIO_STACK_LOCATION readIrpSp; + + ASSERT( TransmitInfo->Endpoint->TransmitInfo == TransmitInfo ); + + // + // If we haven't already done so, allocate and initialize the IRP + // that we'll use to read file data. + // + + if ( TransmitInfo->Read.Irp == NULL ) { + + TransmitInfo->Read.Irp = + IoAllocateIrp( TransmitInfo->DeviceObject->StackSize, TRUE ); + if ( TransmitInfo->Read.Irp == NULL ) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + readIrpSp = IoGetNextIrpStackLocation( TransmitInfo->Read.Irp ); + readIrpSp->MajorFunction = IRP_MJ_READ; + } + + // + // We have to rebuild parts of the stack location because + // the IO system zeros them out on every I/O completion. + // + + readIrpSp = IoGetNextIrpStackLocation( TransmitInfo->Read.Irp ); + readIrpSp->FileObject = TransmitInfo->FileObject; + + IoSetCompletionRoutine( + TransmitInfo->Read.Irp, + AfdRestartTransmitRead, + TransmitInfo, + TRUE, + TRUE, + TRUE + ); + + ASSERT( readIrpSp->MajorFunction == IRP_MJ_READ ); + ASSERT( readIrpSp->Parameters.Read.Key == 0 ); + ASSERT( !IS_IRP_BUSY(TransmitInfo->Read.Irp) ); + + // + // Determine how many bytes we will attempt to read. This is + // either the send packet size or the remaining bytes in the file. + // + + if ( TransmitInfo->FileWriteLength - TransmitInfo->BytesRead > + TransmitInfo->SendPacketLength ) { + readLength = TransmitInfo->SendPacketLength; + } else { + readLength = + (ULONG)(TransmitInfo->FileWriteLength - TransmitInfo->BytesRead); + } + + TransmitInfo->Read.Length = readLength; + + readIrpSp = IoGetNextIrpStackLocation(TransmitInfo->Read.Irp ); + + // + // If supported by the file system, try to perform MDL I/O to get + // MDLs that we can pass to the TDI provider. This is by far the + // fastest way to get the file data because it avoids all file + // copies. + // + + if ( (TransmitInfo->FileObject->Flags & FO_CACHE_SUPPORTED) != 0 ) { + + readIrpSp->MinorFunction = IRP_MN_MDL; + TransmitInfo->Read.Irp->MdlAddress = NULL; + + // + // Set the synchronous flag in the IRP to tell the file system + // that we are aware of the fact that this IRP will be completed + // synchronously. This means that we must supply our own thread + // for the operation and that the disk read will occur + // synchronously in this thread if the data is not cached. + // + + TransmitInfo->Read.Irp->Flags |= IRP_SYNCHRONOUS_API; + + } else { + + // + // The file system does not support the special cache manager + // routines. Allocate an AFD buffer for the request. + // + + TransmitInfo->Read.AfdBuffer = AfdGetBuffer( readLength, 0 ); + if ( TransmitInfo->Read.AfdBuffer == NULL ) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + readIrpSp->MinorFunction = IRP_MN_NORMAL; + + TransmitInfo->Read.Irp->MdlAddress = TransmitInfo->Read.AfdBuffer->Mdl; + TransmitInfo->Read.Irp->AssociatedIrp.SystemBuffer = + TransmitInfo->Read.AfdBuffer->Buffer; + TransmitInfo->Read.Irp->UserBuffer = + TransmitInfo->Read.AfdBuffer->Buffer; + } + + // + // Finish building the read IRP. + // + + readIrpSp->Parameters.Read.Length = readLength; + readIrpSp->Parameters.Read.ByteOffset.QuadPart = + TransmitInfo->Offset + TransmitInfo->BytesRead; + + return STATUS_SUCCESS; + +} // AfdBuildReadIrp + + +ULONG +AfdBuildTransmitSendMdls ( + IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo, + IN PMDL FileMdlChain, + IN ULONG FileDataLength, + IN PAFD_TRANSMIT_IRP_INFO IrpInfo, + OUT PIRP *DisconnectIrp + ) + +/*++ + +Routine Description: + + This routine sets up the MDL chain in a send IRP. Typically this + just means moving file data MDLs to the send IRP, but it may also + involve chaining a head buffer MDL at the beginning of the chain + and/or putting a tail buffer MDL at the end of the chain. + +Arguments: + + TransmitInfo - a pointer to the TransmitInfo structure which + contains information about the request to process. + + FileMdlChain - a pointer to the first MDL in a chain of file data + MDLs. + + FileDataLength - the number of bytes in the file data MDL chain. + + IrpInfo - a pointer to the IrpInfo structure which contains the + send IRP of interest. + + DisconnectIrp - set on output to indicate that this is the final + send of the TransmitFile request, and that the caller should + submit the disconnect IRP after submitting the send IRP. + +Return Value: + + The total number of bytes represented by the MDLs in the send IRP. + +Notes: + + In order for this routine to synchronize properly its access to the + TransmitInfo structure is MUST be called with the endpoint spin lock + held. + +--*/ + +{ + ULONG sendLength; + PMDL lastMdl; + + ASSERT( TransmitInfo->Endpoint->TransmitInfo == TransmitInfo ); + + // + // This routine MUST be called with the endpoint spin lock held. + // + + ASSERT( KeGetCurrentIrql( ) >= DISPATCH_LEVEL ); + + // + // If we need to send the head buffer, chain file data MDLs after + // the head buffer MDLs. + // + + if ( TransmitInfo->NeedToSendHead ) { + + ASSERT( TransmitInfo->HeadMdl != NULL ); + + // + // Now that we're about to send the head buffer, there is no + // need to send it again. + // + + TransmitInfo->NeedToSendHead = FALSE; + + // + // Find the last MDL in the chain of head buffer MDLs. + // + + for ( lastMdl = TransmitInfo->HeadMdl; + lastMdl->Next != NULL; + lastMdl = lastMdl->Next ); + + // + // Chain the file MDLs after the last head MDL. + // + + lastMdl->Next = FileMdlChain; + + // + // Put the head buffer MDL into the send IRP. + // + + IrpInfo->Irp->MdlAddress = TransmitInfo->HeadMdl; + IrpInfo->AfdBuffer = TransmitInfo->Read.AfdBuffer; + IrpInfo->Length = TransmitInfo->Read.Length; + TransmitInfo->Read.AfdBuffer = NULL; + + // + // Remember a pointer to the first actual file MDL so that we + // can quickly jump to it when the send completes and we need + // to return the file MDLs to the file system. + // + + TransmitInfo->FirstFileMdlAfterHead = FileMdlChain; + + // + // Calculate the number of bytes in the MDLs so far. + // + + sendLength = FileDataLength + TransmitInfo->HeadLength; + + } else { + + IrpInfo->Irp->MdlAddress = FileMdlChain; + IrpInfo->AfdBuffer = TransmitInfo->Read.AfdBuffer; + IrpInfo->Length = TransmitInfo->Read.Length; + TransmitInfo->Read.AfdBuffer = NULL; + + sendLength = FileDataLength; + } + + // + // If we have read all the file data, put any tail buffer MDLs at + // the end of the MDL chain. + // + + if ( TransmitInfo->BytesRead == TransmitInfo->FileWriteLength ) { + + // + // If the caller needs to do a disconnect after the send, get a + // disconnect IRP. + // + + if ( (TransmitInfo->Flags & AFD_TF_DISCONNECT) != 0 ) { + (VOID)AfdBeginDisconnect( + TransmitInfo->Endpoint, + NULL, + DisconnectIrp + ); + } else { + *DisconnectIrp = NULL; + } + + if ( TransmitInfo->TailMdl != NULL ) { + + // + // Find the last MDL in the chain. If the FileMdlChain is + // NULL, then we're not sending any file data, so use the + // head MDL chain (if present) or just tag it on to the IRP + // if all we're sending is the tail. + // + + if ( FileMdlChain != NULL ) { + + for ( lastMdl = FileMdlChain; + lastMdl->Next != NULL; + lastMdl = lastMdl->Next ); + + // + // Chain the tail MDLs after the last file MDL. + // + + lastMdl->Next = TransmitInfo->TailMdl; + + // + // Remember a pointer to the last file MDL before the + // tail MDLs so that we can quickly jump to it when the + // send completes and we need to return the file MDLs to + // the system. Note that we must reset the Next pointer + // on this MDL to NULL before returning it to the file + // system. + // + + TransmitInfo->LastFileMdlBeforeTail = lastMdl; + + } else if ( IrpInfo->Irp->MdlAddress !=NULL ) { + + for ( lastMdl = IrpInfo->Irp->MdlAddress; + lastMdl->Next != NULL; + lastMdl = lastMdl->Next ); + + // + // Chain the tail MDLs after the last head buffer MDL. + // + + lastMdl->Next = TransmitInfo->TailMdl; + + } else { + + // + // We're only sending a tail buffer here. + // + + IrpInfo->Irp->MdlAddress = TransmitInfo->TailMdl; + } + + // + // Remember the IRP we used to send the tail so that the + // send completion routine knows whether it needs to read + // just the file MDLs. + // + + TransmitInfo->IrpUsedToSendTail = IrpInfo->Irp; + + // + // Calculate the number of bytes in the MDLs so far. + // + + sendLength += TransmitInfo->TailLength; + } + + } else { + + *DisconnectIrp = NULL; + } + + // + // Return the total number of bytes in the MDLs we chained off the + // send IRP. + // + + return sendLength; + +} // AfdBuildTransmitSendMdls + + +VOID +AfdCancelTransmit ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + The cancel routine for transmit requests. This routine simply + calls AfdCompleteTransmit which performs the actual work of + killing the transmit request. + +Arguments: + + DeviceObject - ignored. + + Irp - a pointer to the transmit file IRP to cancel. + +Return Value: + + None. + +--*/ + +{ + KIRQL oldIrql; + PIO_STACK_LOCATION irpSp; + PAFD_ENDPOINT endpoint; + + // + // Initialize some locals and grab the endpoint spin lock. + // + + irpSp = IoGetCurrentIrpStackLocation( Irp ); + + endpoint = irpSp->FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + + // + // Test whether the transmit request is normally cancellable or if + // it is in the process of closing the connection, in which case we + // will complete it directly. + // + + if ( irpSp->Parameters.DeviceIoControl.IoControlCode != 0 ) { + + ASSERT( irpSp->Parameters.DeviceIoControl.IoControlCode == + IOCTL_AFD_TRANSMIT_FILE ); + + ASSERT( endpoint->TransmitInfo == + (PAFD_TRANSMIT_FILE_INFO_INTERNAL)Irp->AssociatedIrp.SystemBuffer ); + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + + // + // The transmit request is in the normal cancellation state. + // Just call the completion routine to handle the cancellation. + // + + AfdCompleteTransmit( + (PAFD_TRANSMIT_FILE_INFO_INTERNAL)(Irp->AssociatedIrp.SystemBuffer), + STATUS_CANCELLED, + Irp->CancelIrql + ); + + } else { + + // + // The transmit request is in the process of closing the + // connection. Just complete the transmit IRP and let the + // connection closure proceed as normal. + // + + endpoint->TransmitIrp = NULL; + Irp->IoStatus.Status = STATUS_CANCELLED; + IoSetCancelRoutine( Irp, NULL ); + + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + IoCompleteRequest( Irp, AfdPriorityBoost ); + } + + return; + +} // AfdCancelTransmit + + +BOOLEAN +AfdCancelIrp ( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is invoked to cancel an individual I/O Request Packet. + It is similiar to IoCancelIrp() except that it *must* be called with + the cancel spin lock held. This routine exists because of the + synchronization requirements of the cancellation/completion of + transmit IRPs. + +Arguments: + + Irp - Supplies a pointer to the IRP to be cancelled. The CancelIrql + field of the IRP must have been correctly initialized with the + IRQL from the cancel spin lock acquisition. + + +Return Value: + + The function value is TRUE if the IRP was in a cancellable state (it + had a cancel routine), else FALSE is returned. + +Notes: + + It is assumed that the caller has taken the necessary action to ensure + that the packet cannot be fully completed before invoking this routine. + +--*/ + +{ + PDRIVER_CANCEL cancelRoutine; + + // + // Make sure that the cancel spin lock is held. + // + + ASSERT( KeGetCurrentIrql( ) == DISPATCH_LEVEL ); + + // + // Set the cancel flag in the IRP. + // + + Irp->Cancel = TRUE; + + // + // Obtain the address of the cancel routine, and if one was specified, + // invoke it. + // + + cancelRoutine = Irp->CancelRoutine; + if (cancelRoutine) { + if (Irp->CurrentLocation > (CCHAR) (Irp->StackCount + 1)) { + KeBugCheckEx( CANCEL_STATE_IN_COMPLETED_IRP, (ULONG) Irp, 0, 0, 0 ); + } + Irp->CancelRoutine = (PDRIVER_CANCEL) NULL; + cancelRoutine( Irp->Tail.Overlay.CurrentStackLocation->DeviceObject, + Irp ); + // + // The cancel spinlock should have been released by the cancel routine. + // + + return(TRUE); + + } else { + + // + // There was no cancel routine, so release the cancel spinlock and + // return indicating the Irp was not currently cancelable. + // + + IoReleaseCancelSpinLock( Irp->CancelIrql ); + + return(FALSE); + } + +} // AfdCancelIrp + + +VOID +AfdQueueTransmitRead ( + IN PAFD_TRANSMIT_FILE_INFO_INTERNAL TransmitInfo + ) + +/*++ + +Routine Description: + + Because FsRtl fast reads must be performed in thread context, + this routine is used to queue reads to a separate thread. + +Arguments: + + TransmitInfo - a pointer to the TransmitInfo structure which + contains information about the request to process. + +Return Value: + + None. + +--*/ + +{ + ASSERT( !TransmitInfo->Completed ); + ASSERT( TransmitInfo->ReadPending ); + ASSERT( TransmitInfo->Endpoint->TransmitInfo == TransmitInfo ); + + // + // Initialize an executive work quete item and hand it off to an + // executive worker thread. + // + + ExInitializeWorkItem( + &TransmitInfo->WorkQueueItem, + AfdStartTransmitIo, + TransmitInfo + ); + ExQueueWorkItem( &TransmitInfo->WorkQueueItem, DelayedWorkQueue ); + + // + // All done. + // + + return; + +} // AfdQueueTransmitRead + + +VOID +AfdCompleteClosePendedTransmit ( + IN PAFD_ENDPOINT Endpoint + ) + +/*++ + +Routine Description: + + Completes a transmit IRP that was waiting for the connection to be + completely disconnected. + +Arguments: + + Endpoint - the endpoint on which the transmit request is pending. + +Return Value: + + None. + +--*/ + +{ + PIRP irp; + KIRQL cancelIrql; + KIRQL oldIrql; + PIRP transmitIrp; + + IoAcquireCancelSpinLock( &cancelIrql ); + AfdAcquireSpinLock( &Endpoint->SpinLock, &oldIrql ); + + // + // First make sure that thre is really a transmit request pended on + // this endpoint. We do this while holding the appropriate locks + // to close the timing window that would exist otherwise, since + // the caller may not have had the locks when making the test. + // + + if ( Endpoint->TransmitIrp == NULL ) { + AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + return; + } + + // + // Grab the IRP from the endpoint and reset the endpoint's pointer + // to it while still holding the locks so that nobody else accesses + // the IRP. + // + + transmitIrp = Endpoint->TransmitIrp; + Endpoint->TransmitIrp = NULL; + + // + // Reset the cancel routine in the IRP before attempting to complete + // it. + // + + IoSetCancelRoutine( transmitIrp, NULL ); + + // + // Release the lock before completing the transmit IRP--it is + // illegal to call IoCompleteRequest while holding a spin lock. + // + + AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql ); + IoReleaseCancelSpinLock( cancelIrql ); + + // + // Make sure to refresh the endpoint BEFORE completing the transmit + // IRP. This is because the user-mode caller may reuse the endpoint + // as soon as the IRP completes, and there would be a timing window + // between reuse of the endpoint and the refresh otherwise. + // + // Also, AfdRefreshEndpoint must be called at low IRQL since it must + // do a KeAttachProcess to free some resources. + // + + AfdRefreshEndpoint( Endpoint ); + + // + // Finally, we can complete the transmit request. + // + + IoCompleteRequest( transmitIrp, AfdPriorityBoost ); + + // + // If we're enforcing a maximum active TransmitFile count, then + // check the list of queued TransmitFile requests and start the + // next one. + // + + if( AfdMaxActiveTransmitFileCount > 0 ) { + + AfdStartNextQueuedTransmitFile(); + + } + +} // AfdCompleteClosePendedTransmit + + +VOID +AfdStartNextQueuedTransmitFile( + VOID + ) +{ + + KIRQL oldIrql; + PLIST_ENTRY listEntry; + PIRP irp; + PAFD_TRANSMIT_FILE_INFO_INTERNAL transmitInfo; + + // + // This should only be called if we're actually enforcing a maximum + // TransmitFile count. + // + + ASSERT( AfdMaxActiveTransmitFileCount > 0 ); + + // + // The TransmitFile request queue is protected by the I/O cancel + // spinlock, so grab that lock before examining the queue. + // + + IoAcquireCancelSpinLock( &oldIrql ); + + // + // This routine is only called after a pended TransmitFile IRP + // completes, so account for that completion here. + // + + ASSERT( AfdActiveTransmitFileCount > 0 ); + AfdActiveTransmitFileCount--; + + if( !IsListEmpty( &AfdQueuedTransmitFileListHead ) ) { + + // + // Dequeue exactly one IRP from the list, then start the + // TransmitFile. + // + + listEntry = RemoveHeadList( + &AfdQueuedTransmitFileListHead + ); + + irp = CONTAINING_RECORD( + listEntry, + IRP, + Tail.Overlay.ListEntry + ); + + transmitInfo = irp->AssociatedIrp.SystemBuffer; + + ASSERT( transmitInfo != NULL ); + ASSERT( transmitInfo->Endpoint->TransmitInfo == transmitInfo ); + + // + // Mark this TransmitFile request as no longer queued. + // + + ASSERT( transmitInfo->Queued ); + transmitInfo->Queued = FALSE; + + // + // Adjust the count, release the I/O cancel spinlock, then queue + // the TransmitFile. + // + + AfdActiveTransmitFileCount++; + ASSERT( AfdActiveTransmitFileCount <= AfdMaxActiveTransmitFileCount ); + + IoReleaseCancelSpinLock( oldIrql ); + + AfdQueueTransmitRead( + transmitInfo + ); + + } else { + + // + // Release the I/O cancel spinlock before returning. + // + + IoReleaseCancelSpinLock( oldIrql ); + + } + +} // AfdStartNextQueuedTransmitFile + + +BOOLEAN +AfdFastTransmitFile ( + IN struct _FILE_OBJECT *FileObject, + IN PVOID InputBuffer OPTIONAL, + IN ULONG InputBufferLength, + OUT PIO_STATUS_BLOCK IoStatus + ) + +/*++ + +Routine Description: + + Attempts to perform a fast TransmitFile call. This will succeed + only if the caller requests write behind, the file data to be sent + is small, and the data is in the file system cache. + +Arguments: + + FileObject - the endpoint file object of interest. + + InputBuffer - a buffer from the caller containing the + AFD_TRANSMIT_FILE_INFO structure. + + InputBufferLength - the length of the above buffer. + + IoStatus - points to the IO status block that will be set on successful + return from this function. + +Return Value: + + TRUE if the fast path was successful; FALSE if we need to do through + the normal path. + +--*/ + +{ + PAFD_ENDPOINT endpoint; + PAFD_CONNECTION connection; + PAFD_TRANSMIT_FILE_INFO userTransmitInfo; + PAFD_BUFFER afdBuffer; + ULONG sendLength; + PFILE_OBJECT fileObject; + BOOLEAN success; + BOOLEAN sendCountersUpdated; + KIRQL oldIrql; + ULONG fileWriteLength; + NTSTATUS status; + LARGE_INTEGER fileOffset; + PMDL fileMdl; + + // + // Initialize locals so that cleanup is easier. + // + + fileObject = NULL; + afdBuffer = NULL; + sendCountersUpdated = FALSE; + fileMdl = NULL; + + // + // Any access to the user-specified input buffer must be done inside + // a try-except block. + // + + try { + + // + // Make sure that the flags are specified such that a fast-path + // TransmitFile is reasonable. The caller must have specified + // the write-behind flag, but not the disconnect or reuse + // socket flags. + // + + userTransmitInfo = InputBuffer; + + if ( userTransmitInfo->Flags != AFD_TF_WRITE_BEHIND ) { + return FALSE; + } + + // + // Calculate the length the entire send. + // + + fileWriteLength = userTransmitInfo->WriteLength.LowPart; + + sendLength = userTransmitInfo->HeadLength + + fileWriteLength + + userTransmitInfo->TailLength; + + // + // Require the following for the fast path: + // + // - The caller must specify the write length. + // - The write length must be less than the configured maximum. + // - If the entire send is larger than an AFD buffer page, + // we're going to use FsRtlMdlRead, so for purposes of + // simplicity there must be: + // - a head buffer, and + // - no tail buffer + // - The configured maximum will always be less than 4GB. + // - There be no limitation on the count of simultaneous + // TransmitFile calls. The fast path would work around + // this limit, if it exists. + // - The head buffer, if any, fits on a single page. + // + + if ( userTransmitInfo->WriteLength.LowPart == 0 + + || + + sendLength > AfdMaxFastTransmit + + || + + ( sendLength > AfdMaxFastCopyTransmit && + (userTransmitInfo->HeadLength == 0 || + userTransmitInfo->TailLength != 0 ) ) + + || + + userTransmitInfo->WriteLength.HighPart != 0 + + || + + AfdMaxActiveTransmitFileCount != 0 + + || + + userTransmitInfo->HeadLength > AfdBufferLengthForOnePage ) { + + return FALSE; + } + + // + // Initial request validity checks: is the endpoint connected, is + // the input buffer large enough, etc. + // + + endpoint = FileObject->FsContext; + ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); + + if ( endpoint->Type != AfdBlockTypeVcConnecting || + endpoint->State != AfdEndpointStateConnected ) { + return FALSE; + } + + connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint ); + ASSERT( connection->Type == AfdBlockTypeConnection ); + + if ( InputBufferLength < sizeof(AFD_TRANSMIT_FILE_INFO) ) { + return FALSE; + } + + // + // Determine whether there is already too much send data + // pending on the connection. If there is too much send + // data, don't do the fast path. + // + + if ( AfdShouldSendBlock( endpoint, connection, sendLength ) ) { + goto complete; + } + + // + // AfdShouldSendBlock() updates the send counters in the AFD + // connection object. Remember this fact so that we can clean + // them up if the fast path fails after this point. + // + + sendCountersUpdated = TRUE; + + // + // Grab an AFD buffer large enough to hold the entire send. + // + + afdBuffer = AfdGetBuffer( sendLength, 0 ); + if ( afdBuffer == NULL ) { + goto complete; + } + + // + // Get a referenced pointer to the file object for the file that + // we're going to transmit. This call will fail if the file + // handle specified by the caller is invalid. + // + + status = ObReferenceObjectByHandle( + userTransmitInfo->FileHandle, + FILE_READ_DATA, + *IoFileObjectType, + UserMode, + (PVOID *)&fileObject, + NULL + ); + if ( !NT_SUCCESS(status) ) { + goto complete; + } + + // + // If the file system doesn't support the fast cache manager + // interface, bail and go through the IRP path. + // + + if( ( fileObject->Flags & FO_CACHE_SUPPORTED ) == 0 ) { + goto complete; + } + + // + // Grab the file offset into a local so that we know that the + // offset pointer we pass to FsRtlCopyRead is valid. + // + + fileOffset = userTransmitInfo->Offset; + + // + // Get the file data. If the amount of file data is small, copy + // it directly into the AFD buffer. If it is large, get an MDL + // chain for the data and chain it on to the AFD buffer chain. + // + + if ( sendLength < AfdMaxFastCopyTransmit ) { + + success = FsRtlCopyRead( + fileObject, + &fileOffset, + fileWriteLength, + FALSE, + 0, + (PCHAR)afdBuffer->Buffer + userTransmitInfo->HeadLength, + IoStatus, + IoGetRelatedDeviceObject( fileObject ) + ); + + // + // We're done with the file object, so deference it now. + // + + ObDereferenceObject( fileObject ); + fileObject = NULL; + + } else { + + success = FsRtlMdlRead( + fileObject, + &fileOffset, + fileWriteLength, + 0, + &fileMdl, + IoStatus + ); + + // + // Save the file object in the AFD buffer. The send restart + // routine will handle dereferencing the file object and + // returning the file MDLs to the system. + // + + afdBuffer->FileObject = fileObject; + afdBuffer->FileOffset = fileOffset.QuadPart; + afdBuffer->ReadLength = fileWriteLength; + } + + if ( !success ) { + goto complete; + } + + // + // If we read less information than was requested, we must have + // hit the end of the file. Fail the transmit request, since + // this can only happen if the caller requested that we send + // more data than the file currently contains. + // + + if ( IoStatus->Information < fileWriteLength ) { + goto complete; + } + + // + // We got all the file data, so things are looking good. Copy + // in the head and tail buffers, if necessary. Note that if we + // used MDL read, then there cannot be a tail buffer because of + // the check at the beginning of this function. + // + + if ( userTransmitInfo->HeadLength > 0 ) { + RtlCopyMemory( + afdBuffer->Buffer, + userTransmitInfo->Head, + userTransmitInfo->HeadLength + ); + } + + if ( userTransmitInfo->TailLength > 0 ) { + RtlCopyMemory( + (PCHAR)afdBuffer->Buffer + userTransmitInfo->HeadLength + + fileWriteLength, + userTransmitInfo->Tail, + userTransmitInfo->TailLength + ); + } + + // + // We have to rebuild the MDL in the AFD buffer structure to + // represent exactly the number of bytes we're going to be + // sending. If the AFD buffer has all the send data, indicate + // that. If we did MDL file I/O, then chain the file data on + // to the head MDL. + // + + if ( fileMdl == NULL ) { + afdBuffer->Mdl->ByteCount = sendLength; + } else { + afdBuffer->Mdl->ByteCount = userTransmitInfo->HeadLength; + afdBuffer->Mdl->Next = fileMdl; + } + + SET_CHAIN_LENGTH( afdBuffer, sendLength ); + + // + // Remember the endpoint in the AFD buffer structure. We need + // this in order to access the endpoint in the restart routine. + // + + afdBuffer->Context = endpoint; + + // + // Use the IRP in the AFD buffer structure to give to the TDI + // provider. Build the TDI send request. + // + + TdiBuildSend( + afdBuffer->Irp, + connection->DeviceObject, + connection->FileObject, + AfdRestartBufferSend, + afdBuffer, + afdBuffer->Mdl, + 0, + sendLength + ); + + // + // Add a reference to the connection object since the send + // request will complete asynchronously. + // + + REFERENCE_CONNECTION( connection ); + + // + // Call the transport to actually perform the send. + // + + status = IoCallDriver( connection->DeviceObject, afdBuffer->Irp ); + + // + // Reset all the local variables that control cleanup. This is + // necessary because the send restart routine will handle all + // cleanup at this point, and we cannot duplicate cleanup in the + // case of a failure or exception below. + // + + fileObject = NULL; + afdBuffer = NULL; + sendCountersUpdated = FALSE; + fileMdl = NULL; + + // + // The fast path succeeded--complete the call. Note that we + // change the status code from what was returned by the TDI + // provider into STATUS_SUCCESS. This is because we don't want + // to complete the IRP with STATUS_PENDING etc. + // + + if ( NT_SUCCESS(status) ) { + IoStatus->Information = sendLength; + IoStatus->Status = STATUS_SUCCESS; + + return TRUE; + } + + // + // The call failed for some reason. Fail fast IO. + // + + goto complete; + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + goto complete; + } + +complete: + + if ( afdBuffer != NULL ) { + afdBuffer->FileObject = NULL; + afdBuffer->Mdl->Next = NULL; + afdBuffer->Mdl->ByteCount = afdBuffer->BufferLength; + AfdReturnBuffer( afdBuffer ); + } + + if ( fileMdl != NULL ) { + FsRtlMdlReadComplete( fileObject, fileMdl ); + } + if ( fileObject != NULL ) { + ObDereferenceObject( fileObject ); + } + + if ( sendCountersUpdated ) { + AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql ); + connection->VcBufferredSendBytes -= sendLength; + connection->VcBufferredSendCount -= 1; + AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql ); + } + + return FALSE; + +} // AfdFastTransmitFile + + +NTSTATUS +AfdMdlReadComplete( + IN PFILE_OBJECT FileObject, + IN PMDL MdlChain, + IN LONGLONG FileOffset, + IN ULONG Length + ) + +/*++ + +Routine Description: + + Completes a MDL read operation by calling FsRtlMdlReadComplete(). + If this returns TRUE, cool. Otherwise (it returns FALSE) and this + routine will allocate a new IRP and submit it to the filesystem. + +Arguments: + + FileObject - Pointer to the file object being read. + + MdlChain - Supplies a pointer to an MDL chain returned from CcMdlRead. + + FileOffset - Byte offset in file for desired data. + + Length - Length of desired data in bytes. + +Return Value: + + NTSTATUS - Completion status. + +--*/ + +{ + + PIRP irp; + PIO_STACK_LOCATION irpSp; + PAFD_MDL_COMPLETION_CONTEXT context; + + // + // If we're being called at low IRQL, we can handle the + // request immediately "in-line". + // + + if( KeGetCurrentIrql() == LOW_LEVEL ) { + + // + // First, try the fast path. If that succeeds, we're done. + // + + if( FsRtlMdlReadComplete( FileObject, MdlChain ) ) { + + return STATUS_SUCCESS; + + } + + // + // Fast past failed, so create a new IRP. + // + + irp = IoAllocateIrp( + FileObject->DeviceObject->StackSize, + FALSE + ); + + if( irp == NULL ) { + + return STATUS_INSUFFICIENT_RESOURCES; + + } + + // + // Setup the IRP. + // + + irpSp = IoGetNextIrpStackLocation( irp ); + + irp->MdlAddress = MdlChain; + + irpSp->MajorFunction = IRP_MJ_READ; + irpSp->MinorFunction = IRP_MN_MDL | IRP_MN_COMPLETE; + + irpSp->Parameters.Read.Length = Length; + irpSp->Parameters.Read.ByteOffset = *(PLARGE_INTEGER)&FileOffset; + irpSp->Parameters.Read.Key = 0; + + irpSp->FileObject = FileObject; + irpSp->DeviceObject = IoGetRelatedDeviceObject( FileObject ); + + // + // Submit the IRP. + // + + IoSetCompletionRoutine( + irp, + AfdRestartMdlReadComplete, + NULL, + TRUE, + TRUE, + TRUE + ); + + return IoCallDriver( irpSp->DeviceObject, irp ); + + } + + // + // We're being called at raised IRQL (probably in a TDI completion + // routine) so we cannot touch the filesystem. We'll fire off a + // worker thread to do the actual completion. + // + // Allocate a deferred completion context. + // + + context = AfdAllocateMdlCompletionContext(); + + if( context == NULL ) { + + return STATUS_INSUFFICIENT_RESOURCES; + + } + + // + // Initialize the context and queue it to the worker thread. + // + + context->FileObject = FileObject; + context->MdlChain = MdlChain; + context->FileOffset = FileOffset; + context->Length = Length; + + // + // Add a reference to the FileObject so it doesn't go away + // before our completion routine gets called + // + + ObReferenceObject( FileObject ); + + AfdQueueWorkItem( + AfdDeferredMdlReadComplete, + &context->WorkItem + ); + + return STATUS_SUCCESS; + +} // AfdMdlReadComplete + + +NTSTATUS +AfdRestartMdlReadComplete ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) + +/*++ + +Routine Description: + + Completion routine for IRPs issued by AfdMdlReadComplete. The only + purpose of this completion routine is to free the IRPs created by + AfdMdlReadComplete(). + +Arguments: + + DeviceObject - Unused. + + Irp - The completed IRP. + + Context - Unused. + +Return Value: + + NTSTATUS - Completion status. + +--*/ + +{ + // + // Free the IRP since it's no longer needed. + // + + IoFreeIrp( Irp ); + + // + // Return STATUS_MORE_PROCESSING_REQUIRED so that IoCompleteRequest + // will stop working on the IRP. + // + + return STATUS_MORE_PROCESSING_REQUIRED; + +} // AfdRestartMdlReadComplete + + +VOID +AfdDeferredMdlReadComplete( + IN PVOID Context + ) + +/*++ + +Routine Description: + + Delayed worker thread routine for completing MDL reads. This routine + is queued if we call AfdMdlReadComplete() at raised IRQL. Since it + is invalid to call file systems at raised IRQL, this routine is + scheduled to do the dirty work. + +Arguments: + + Context - Points to the AFD_WORK_ITEM structure embedded within the + AFD_MDL_COMPLETION_CONTEXT structure defining the MDL to complete. + +Return Value: + + None. + +--*/ + +{ + + PAFD_MDL_COMPLETION_CONTEXT mdlComplete; + + // + // Sanity check. + // + + ASSERT( KeGetCurrentIrql() == LOW_LEVEL ); + ASSERT( Context != NULL ); + + // + // Get the completion context pointer. + // + + mdlComplete = CONTAINING_RECORD( + Context, + AFD_MDL_COMPLETION_CONTEXT, + WorkItem + ); + + // + // Let AfdMdlReadComplete do the dirty work. + // + + AfdMdlReadComplete( + mdlComplete->FileObject, + mdlComplete->MdlChain, + mdlComplete->FileOffset, + mdlComplete->Length + ); + + // + // Remove the reference we added in AfdMdlReadComplete + // + + ObDereferenceObject( mdlComplete->FileObject ); + + // + // Free the context. + // + + AfdFreeMdlCompletionContext( mdlComplete ); + +} // AfdDeferredMdlReadComplete |