summaryrefslogtreecommitdiffstats
path: root/private/ntos/afd
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/afd
downloadNT4.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')
-rw-r--r--private/ntos/afd/accept.c1473
-rw-r--r--private/ntos/afd/afd.rc12
-rw-r--r--private/ntos/afd/afddata.c349
-rw-r--r--private/ntos/afd/afddata.h201
-rw-r--r--private/ntos/afd/afdkd/addr.c139
-rw-r--r--private/ntos/afd/afdkd/afdkd.def25
-rw-r--r--private/ntos/afd/afdkd/afdkd.rc11
-rw-r--r--private/ntos/afd/afdkd/afdkdp.h104
-rw-r--r--private/ntos/afd/afdkd/afdutil.c2221
-rw-r--r--private/ntos/afd/afdkd/buffer.c89
-rw-r--r--private/ntos/afd/afdkd/conn.c93
-rw-r--r--private/ntos/afd/afdkd/cons.h36
-rw-r--r--private/ntos/afd/afdkd/data.h40
-rw-r--r--private/ntos/afd/afdkd/dbgutil.c140
-rw-r--r--private/ntos/afd/afdkd/endp.c486
-rw-r--r--private/ntos/afd/afdkd/enumendp.c136
-rw-r--r--private/ntos/afd/afdkd/help.c81
-rw-r--r--private/ntos/afd/afdkd/kdexts.c136
-rw-r--r--private/ntos/afd/afdkd/makefile6
-rw-r--r--private/ntos/afd/afdkd/proc.h117
-rw-r--r--private/ntos/afd/afdkd/ref.c336
-rw-r--r--private/ntos/afd/afdkd/sources45
-rw-r--r--private/ntos/afd/afdkd/stats.c335
-rw-r--r--private/ntos/afd/afdkd/tdiutil.c367
-rw-r--r--private/ntos/afd/afdkd/tranfile.c94
-rw-r--r--private/ntos/afd/afdkd/type.h39
-rw-r--r--private/ntos/afd/afdp.h361
-rw-r--r--private/ntos/afd/afdprocs.h1073
-rw-r--r--private/ntos/afd/afdstr.h591
-rw-r--r--private/ntos/afd/bind.c828
-rw-r--r--private/ntos/afd/blkconn.c1453
-rw-r--r--private/ntos/afd/blkendp.c1010
-rw-r--r--private/ntos/afd/buffer.c651
-rw-r--r--private/ntos/afd/close.c488
-rw-r--r--private/ntos/afd/connect.c659
-rw-r--r--private/ntos/afd/create.c298
-rw-r--r--private/ntos/afd/daytona/afd.prf0
-rw-r--r--private/ntos/afd/daytona/makefile6
-rw-r--r--private/ntos/afd/daytona/sources32
-rw-r--r--private/ntos/afd/dirs29
-rw-r--r--private/ntos/afd/disconn.c1183
-rw-r--r--private/ntos/afd/dispatch.c604
-rw-r--r--private/ntos/afd/eventsel.c478
-rw-r--r--private/ntos/afd/fastio.c2095
-rw-r--r--private/ntos/afd/group.c537
-rw-r--r--private/ntos/afd/init.c1056
-rw-r--r--private/ntos/afd/listen.c1435
-rw-r--r--private/ntos/afd/misc.c4804
-rw-r--r--private/ntos/afd/nt351/makefile6
-rw-r--r--private/ntos/afd/nt351/place351.inc19
-rw-r--r--private/ntos/afd/nt351/sources33
-rw-r--r--private/ntos/afd/poll.c1265
-rw-r--r--private/ntos/afd/receive.c1245
-rw-r--r--private/ntos/afd/recvdg.c1653
-rw-r--r--private/ntos/afd/recvvc.c1884
-rw-r--r--private/ntos/afd/registry.txt158
-rw-r--r--private/ntos/afd/send.c1734
-rw-r--r--private/ntos/afd/sources.inc77
-rw-r--r--private/ntos/afd/spinlock.txt440
-rw-r--r--private/ntos/afd/tranfile.c3742
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,
+ &currentSlot,
+ 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,
+ &quotaStats,
+ 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( &registryPath, REGISTRY_AFD_INFORMATION );
+
+ status = AfdOpenRegistry( &registryPath, &parametersHandle );
+
+ 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 (&parametersKeyName, parametersString);
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &parametersKeyName, // 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)&currentTime );
+
+ 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,
+ &parameters,
+ 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 = &currentMdl->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