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

3241 lines
95 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1989-1999 Microsoft Corporation
Module Name:
accept.c
Abstract:
This module contains the handling code for IOCTL_AFD_ACCEPT.
as well as IOCTL_AFD_SUPER_ACCEPT
Author:
David Treadwell (davidtr) 21-Feb-1992
Revision History:
Vadim Eydelman (vadime) 1999 - No spinlock performance path in super accept,
general code restructuring.
--*/
#include "afdp.h"
VOID
AfdDoListenBacklogReplenish (
IN PVOID Context
);
NTSTATUS
AfdReplenishListenBacklog (
IN PAFD_ENDPOINT Endpoint
);
VOID
AfdReportConnectionAllocationFailure (
PAFD_ENDPOINT Endpoint,
NTSTATUS Status
);
NTSTATUS
AfdContinueSuperAccept (
IN PIRP Irp,
PAFD_CONNECTION Connection
);
NTSTATUS
AfdRestartSuperAcceptGetAddress (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
AfdRestartSuperAcceptReceive (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
AfdRestartDelayedAccept (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
VOID
AfdSuperAcceptApcKernelRoutine (
IN struct _KAPC *Apc,
IN OUT PKNORMAL_ROUTINE *NormalRoutine,
IN OUT PVOID *NormalContext,
IN OUT PVOID *SystemArgument1,
IN OUT PVOID *SystemArgument2
);
VOID
AfdSuperAcceptApcRundownRoutine (
IN struct _KAPC *Apc
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGEAFD, AfdAccept )
#pragma alloc_text( PAGEAFD, AfdSuperAccept )
#pragma alloc_text( PAGEAFD, AfdDeferAccept )
#pragma alloc_text( PAGE, AfdDoListenBacklogReplenish )
#pragma alloc_text( PAGEAFD, AfdSetupAcceptEndpoint )
#pragma alloc_text( PAGE, AfdReplenishListenBacklog )
#pragma alloc_text( PAGEAFD, AfdReportConnectionAllocationFailure )
#pragma alloc_text( PAGEAFD, AfdInitiateListenBacklogReplenish )
#pragma alloc_text( PAGEAFD, AfdRestartSuperAccept )
#pragma alloc_text( PAGEAFD, AfdRestartSuperAcceptListen )
#pragma alloc_text( PAGEAFD, AfdContinueSuperAccept )
#pragma alloc_text( PAGEAFD, AfdServiceSuperAccept )
#pragma alloc_text( PAGEAFD, AfdRestartSuperAcceptGetAddress )
#pragma alloc_text( PAGEAFD, AfdRestartSuperAcceptReceive )
#pragma alloc_text( PAGE, AfdSuperAcceptApcKernelRoutine )
#pragma alloc_text( PAGE, AfdSuperAcceptApcRundownRoutine )
#pragma alloc_text( PAGEAFD, AfdCancelSuperAccept )
#pragma alloc_text( PAGEAFD, AfdCleanupSuperAccept )
#pragma alloc_text( PAGEAFD, AfdRestartDelayedAccept)
#pragma alloc_text( PAGEAFD, AfdRestartDelayedSuperAccept)
#endif
//
// Macros to make the super accept restart code more maintainable.
//
#define AfdRestartSuperAcceptInfo DeviceIoControl
// Used while IRP is in AFD queue (otherwise AfdAcceptFileObject
// is stored as completion routine context).
#define AfdAcceptFileObject Type3InputBuffer
// Used when IRP is passed to the transport (otherwise MdlAddress
// is stored in the IRP itself).
#define AfdMdlAddress Type3InputBuffer
#define AfdReceiveDataLength OutputBufferLength
#define AfdRemoteAddressLength InputBufferLength
#define AfdLocalAddressLength IoControlCode
//
// Similar macros for delayed accept restart code.
//
#define AfdRestartDelayedAcceptInfo DeviceIoControl
#define AfdSystemBuffer Type3InputBuffer
NTSTATUS
FASTCALL
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 listenEndpoint;
PFILE_OBJECT acceptFileObject;
PAFD_ENDPOINT acceptEndpoint;
PAFD_CONNECTION connection;
//
// Set up local variables.
//
listenEndpoint = IrpSp->FileObject->FsContext;
Irp->IoStatus.Information = 0;
#ifdef _WIN64
if (IoIs32bitProcess (Irp)) {
PAFD_ACCEPT_INFO newSystemBuffer;
PAFD_ACCEPT_INFO32 oldSystemBuffer = Irp->AssociatedIrp.SystemBuffer;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(AFD_ACCEPT_INFO32) ) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
try {
newSystemBuffer = ExAllocatePoolWithQuota (NonPagedPool, sizeof (AFD_ACCEPT_INFO));
}
except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode ();
goto complete;
}
newSystemBuffer->AcceptHandle = oldSystemBuffer->AcceptHandle;
newSystemBuffer->Sequence = oldSystemBuffer->Sequence;
ExFreePool (Irp->AssociatedIrp.SystemBuffer);
Irp->AssociatedIrp.SystemBuffer = newSystemBuffer;
IrpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof (AFD_ACCEPT_INFO);
}
#endif // _WIN64
acceptInfo = Irp->AssociatedIrp.SystemBuffer;
//
// Make sure that this request is valid.
//
if ( !listenEndpoint->Listening ||
IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(AFD_ACCEPT_INFO) ||
Irp->MdlAddress!=NULL) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
ASSERT ((listenEndpoint->Type & AfdBlockTypeVcListening)==AfdBlockTypeVcListening);
//
// Check for if the caller is unaware of the SAN
// provider activation and report the error.
//
if (!acceptInfo->SanActive && AfdSanServiceHelper!=NULL) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
"AFD: Process %p is being told to enable SAN on accept\n",
PsGetCurrentProcessId ()));
status = STATUS_INVALID_PARAMETER_12;
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(
&listenEndpoint->Common.VcListening.FailedConnectionAdds
);
AfdReplenishListenBacklog( listenEndpoint );
//
// Obtain a pointer to the endpoint on which we're going to
// accept the connection.
//
status = ObReferenceObjectByHandle(
acceptInfo->AcceptHandle,
(IrpSp->Parameters.DeviceIoControl.IoControlCode>>14) & 3,
// DesiredAccess
*IoFileObjectType,
Irp->RequestorMode,
(PVOID *)&acceptFileObject,
NULL
);
if ( !NT_SUCCESS(status) ) {
goto complete;
}
//
// We may have a file object that is not an AFD endpoint. Make sure
// that this is an actual AFD endpoint.
//
if ( acceptFileObject->DeviceObject!=AfdDeviceObject) {
status = STATUS_INVALID_HANDLE;
goto complete_deref;
}
acceptEndpoint = acceptFileObject->FsContext;
if (acceptEndpoint->TransportInfo!=listenEndpoint->TransportInfo) {
status = STATUS_INVALID_PARAMETER;
goto complete_deref;
}
ASSERT( InterlockedIncrement( &acceptEndpoint->ObReferenceBias ) > 0 );
IF_DEBUG(ACCEPT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdAccept: file object %p, accept endpoint %p, listen endpoint %p\n",
acceptFileObject, acceptEndpoint, listenEndpoint ));
}
if (AFD_START_STATE_CHANGE (acceptEndpoint, AfdEndpointStateConnected)) {
if (acceptEndpoint->State!=AfdEndpointStateOpen) {
status = STATUS_INVALID_PARAMETER;
}
else {
AFD_LOCK_QUEUE_HANDLE lockHandle;
AfdAcquireSpinLock (&listenEndpoint->SpinLock, &lockHandle);
connection = AfdGetReturnedConnection (listenEndpoint,
acceptInfo->Sequence);
if (connection==NULL) {
AfdReleaseSpinLock (&listenEndpoint->SpinLock, &lockHandle);
status = STATUS_INVALID_PARAMETER;
}
else if (connection->SanConnection) {
Irp->Tail.Overlay.DriverContext[3] = acceptInfo->AcceptHandle;
IrpSp->Parameters.AfdRestartSuperAcceptInfo.AfdAcceptFileObject = acceptFileObject;
IrpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength = 0;
IrpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength = 0;
IrpSp->Parameters.AfdRestartSuperAcceptInfo.AfdRemoteAddressLength = 0;
status = AfdSanAcceptCore (Irp, acceptFileObject, connection, &lockHandle);
if (status==STATUS_PENDING) {
return STATUS_PENDING;
}
}
else {
status = AfdAcceptCore (Irp, acceptEndpoint, connection);
AfdReleaseSpinLock (&listenEndpoint->SpinLock, &lockHandle);
AFD_RETURN_REMOTE_ADDRESS (
connection->RemoteAddress,
connection->RemoteAddressLength
);
connection->RemoteAddress = NULL;
if (status==STATUS_SUCCESS) {
NOTHING;
}
else if (status==STATUS_PENDING) {
//
// Remember that a TDI accept has started on this endpoint.
//
InterlockedIncrement(
&listenEndpoint->Common.VcListening.TdiAcceptPendingCount
);
IrpSp->Parameters.AfdRestartDelayedAcceptInfo.AfdSystemBuffer =
Irp->AssociatedIrp.SystemBuffer;
Irp->AssociatedIrp.SystemBuffer = NULL;
ASSERT (Irp->MdlAddress==NULL);
IoSetCompletionRoutine(
Irp,
AfdRestartDelayedAccept,
acceptFileObject,
TRUE,
TRUE,
TRUE
);
AfdIoCallDriver (
acceptEndpoint,
acceptEndpoint->Common.VcConnecting.Connection->DeviceObject,
Irp
);
return STATUS_PENDING;
}
else {
AfdAbortConnection (connection);
ASSERT (status==STATUS_CANCELLED);
}
}
}
AFD_END_STATE_CHANGE (acceptEndpoint);
}
else {
status = STATUS_INVALID_PARAMETER;
}
ASSERT( InterlockedDecrement( &acceptEndpoint->ObReferenceBias ) >= 0 );
complete_deref:
ObDereferenceObject( acceptFileObject );
complete:
Irp->IoStatus.Status = status;
ASSERT( Irp->CancelRoutine == NULL );
IoCompleteRequest( Irp, AfdPriorityBoost );
return status;
} // AfdAccept
NTSTATUS
AfdAcceptCore (
IN PIRP AcceptIrp,
IN PAFD_ENDPOINT AcceptEndpoint,
IN PAFD_CONNECTION Connection
)
/*++
Routine Description:
Performs the key functions of associating a connection accepted
on a listening endpoint with a new endpoint.
Arguments:
AcceptIrp - Irp used for accept operation
AcceptEndpoint - the new endpoint with which to associate the
connectuion.
Connection - the connection being accepted.
Return Value:
STATUS_SUCCESS if the operation was completed successfully,
STATUS_PENDING if IRP was passed further to the transport
failure status code if there was an error.
--*/
{
PAFD_ENDPOINT listenEndpoint;
NTSTATUS status;
PIO_STACK_LOCATION irpSp;
AFD_LOCK_QUEUE_HANDLE lockHandle;
irpSp = IoGetCurrentIrpStackLocation (AcceptIrp);
listenEndpoint = irpSp->FileObject->FsContext;
//
// Reenable the accept event bit, and if there are additional
// unaccepted connections on the endpoint, post another event.
//
listenEndpoint->EventsActive &= ~AFD_POLL_ACCEPT;
IF_DEBUG(EVENT_SELECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdAcceptCore: Endp %08lX, Active %08lX\n",
listenEndpoint,
listenEndpoint->EventsActive
));
}
if( !IsListEmpty( &listenEndpoint->Common.VcListening.UnacceptedConnectionListHead ) ) {
AfdIndicateEventSelectEvent(
listenEndpoint,
AFD_POLL_ACCEPT,
STATUS_SUCCESS
);
}
//
// Do not release the listening endpoint spinlock here.
// We are going to be chaning connection object which assumes
// protection from listening endpoint spinlock until it is associated
// with accept endpoint (this plugs nasty racing conditions when
// receive is indicated rigth before connection object is updated, so
// it takes listening endpoint spinlock, and rigth before it queues
// the buffer to the connection object, it gets associated with accept
// endpoint and AcceptEx'es receive does not notice the buffer because
// it takes accept endpoint spinlock)
//
// AfdReleaseSpinLock( &ListenEndpoint->SpinLock, &lockHandle );
//
// Check the state of the accepting endpoint under the guard
// of the endpoint's spinlock.
//
AfdAcquireSpinLockAtDpcLevel( &AcceptEndpoint->SpinLock, &lockHandle);
status = AfdSetupAcceptEndpoint (listenEndpoint, AcceptEndpoint, Connection);
if (status==STATUS_SUCCESS) {
if (IS_DELAYED_ACCEPTANCE_ENDPOINT (listenEndpoint)) {
PTDI_CONNECTION_INFORMATION requestConnectionInformation;
if( Connection->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 =
&Connection->ConnectDataBuffers->RequestConnectionInfo;
RtlZeroMemory(
requestConnectionInformation,
sizeof(*requestConnectionInformation)
);
requestConnectionInformation->UserData =
Connection->ConnectDataBuffers->SendConnectData.Buffer;
requestConnectionInformation->UserDataLength =
Connection->ConnectDataBuffers->SendConnectData.BufferLength;
requestConnectionInformation->Options =
Connection->ConnectDataBuffers->SendConnectOptions.Buffer;
requestConnectionInformation->OptionsLength =
Connection->ConnectDataBuffers->SendConnectOptions.BufferLength;
} else {
requestConnectionInformation = NULL;
}
TdiBuildAccept(
AcceptIrp,
Connection->DeviceObject,
Connection->FileObject,
NULL,
NULL,
requestConnectionInformation,
NULL
);
status = STATUS_PENDING;
}
else {
//
// Set the endpoint to the connected state.
//
AcceptEndpoint->State = AfdEndpointStateConnected;
Connection->State = AfdConnectionStateConnected;
//
// Set events active field base on data accumulated on the connection.
//
if( IS_DATA_ON_CONNECTION( Connection ) ||
( AcceptEndpoint->InLine &&
IS_EXPEDITED_DATA_ON_CONNECTION( Connection ) ) ) {
AcceptEndpoint->EventsActive |= AFD_POLL_RECEIVE;
}
if( !AcceptEndpoint->InLine &&
IS_EXPEDITED_DATA_ON_CONNECTION( Connection ) ) {
AcceptEndpoint->EventsActive |= AFD_POLL_RECEIVE_EXPEDITED;
}
AcceptEndpoint->EventsActive |= AFD_POLL_SEND;
if( Connection->DisconnectIndicated ) {
AcceptEndpoint->EventsActive |= AFD_POLL_DISCONNECT;
}
if( Connection->AbortIndicated ) {
AcceptEndpoint->EventsActive |= AFD_POLL_ABORT;
}
IF_DEBUG(EVENT_SELECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdAcceptCore: Endp %08lX, Active %08lX\n",
AcceptEndpoint,
AcceptEndpoint->EventsActive
));
}
status = STATUS_SUCCESS;
}
}
AfdReleaseSpinLockFromDpcLevel( &AcceptEndpoint->SpinLock, &lockHandle);
return status;
} // 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.
--*/
{
AFD_LOCK_QUEUE_HANDLE lockHandle;
//
// Check if backlog replenish is active already.
//
AfdAcquireSpinLock (&Endpoint->SpinLock, &lockHandle);
if (!Endpoint->Common.VcListening.BacklogReplenishActive) {
Endpoint->Common.VcListening.BacklogReplenishActive = TRUE;
//
// Reference the endpoint so that it won't go away until we're
// done with it.
//
REFERENCE_ENDPOINT( Endpoint );
AfdQueueWorkItem(
AfdDoListenBacklogReplenish,
&Endpoint->WorkItem
);
}
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
return;
} // 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;
PAGED_CODE( );
endpoint = CONTAINING_RECORD(
Context,
AFD_ENDPOINT,
WorkItem
);
ASSERT( endpoint->Type == AfdBlockTypeVcListening ||
endpoint->Type == AfdBlockTypeVcBoth );
ASSERT (endpoint->Common.VcListening.BacklogReplenishActive == TRUE);
//
// It is ok to do this without spinlock protection
// because only one thread can do this at a time
// inside of the worker routine.
//
endpoint->Common.VcListening.BacklogReplenishActive = FALSE;
//
// If the endpoint's state changed, don't replenish the backlog.
//
if ( endpoint->Listening ) {
NTSTATUS status;
//
// Fill up the free connection backlog.
//
status = AfdReplenishListenBacklog( endpoint );
if (!NT_SUCCESS (status)) {
//
// If we failed, try to notify application
//
AfdReportConnectionAllocationFailure (endpoint, status);
}
}
//
// Clean up and return.
//
DEREFERENCE_ENDPOINT( endpoint );
return;
} // AfdDoListenBacklogReplenish
NTSTATUS
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:
STATUS_SUCCESS - if new connection was allocated or we already
had enough
status of conneciton allocation failure otherwise
--*/
{
NTSTATUS status;
LONG result;
PAGED_CODE( );
ASSERT( Endpoint->Type == AfdBlockTypeVcListening ||
Endpoint->Type == AfdBlockTypeVcBoth );
//
// 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(LISTEN) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdReplenishListenBacklog: AfdAddFreeConnection failed: %X, fail count = %ld\n", status,
Endpoint->Common.VcListening.FailedConnectionAdds ));
}
//
// Return connection allocation failure to the application
// if it cares to know (posted accept request).
//
return status;
}
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 STATUS_SUCCESS;
} // AfdReplenishListenBacklog
VOID
AfdReportConnectionAllocationFailure (
PAFD_ENDPOINT Endpoint,
NTSTATUS Status
)
/*++
Routine Description:
Reports connection allocation failure to the application by
failing then first wait for listen irp in the queue (if application
has AcceptEx or blockin accept outstanding, it will get this notification).
Arguments:
Endpoint - the listening endpoint on which to report an error
Status - status code to report
Return Value:
None
--*/
{
AFD_LOCK_QUEUE_HANDLE lockHandle;
AfdAcquireSpinLock (&Endpoint->SpinLock, &lockHandle);
if ((Endpoint->Common.VcListening.FailedConnectionAdds>0) &&
IsListEmpty (&Endpoint->Common.VcListening.UnacceptedConnectionListHead) &&
!IsListEmpty (&Endpoint->Common.VcListening.ListeningIrpListHead)) {
PIRP irp;
PIO_STACK_LOCATION irpSp;
irp = CONTAINING_RECORD (Endpoint->Common.VcListening.ListeningIrpListHead.Flink,
IRP,
Tail.Overlay.ListEntry);
irpSp = IoGetCurrentIrpStackLocation (irp);
RemoveEntryList (&irp->Tail.Overlay.ListEntry);
irp->Tail.Overlay.ListEntry.Flink = NULL;
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
if (IoSetCancelRoutine (irp, NULL)==NULL) {
KIRQL cancelIrql;
//
// If the cancel routine was NULL then cancel routine
// may be running. Wait on the cancel spinlock until
// the cancel routine is done.
//
// Note: The cancel routine will not find the IRP
// since it is not in the list.
//
IoAcquireCancelSpinLock( &cancelIrql );
ASSERT( irp->Cancel );
IoReleaseCancelSpinLock( cancelIrql );
}
if (irpSp->MajorFunction==IRP_MJ_INTERNAL_DEVICE_CONTROL) {
AfdCleanupSuperAccept (irp, Status);
}
else {
irp->IoStatus.Status = Status;
irp->IoStatus.Information = 0;
}
IoCompleteRequest (irp, AfdPriorityBoost);
}
else {
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
}
}
NTSTATUS
FASTCALL
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;
HANDLE acceptHandle;
ULONG receiveDataLength, localAddressLength, remoteAddressLength;
BOOLEAN sanActive;
#ifndef i386
BOOLEAN fixAddressAlignment;
#endif
NTSTATUS status;
ULONG totalLength;
PSINGLE_LIST_ENTRY listEntry;
PAFD_CONNECTION connection;
AFD_LOCK_QUEUE_HANDLE lockHandle;
//
// Set up local variables.
//
listenEndpoint = IrpSp->FileObject->FsContext;
acceptFileObject = NULL;
acceptEndpoint = NULL;
#ifdef _WIN64
if (IoIs32bitProcess (Irp)) {
PAFD_SUPER_ACCEPT_INFO32 superAcceptInfo32;
superAcceptInfo32 = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(AFD_SUPER_ACCEPT_INFO32) ) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
try {
if (Irp->RequestorMode!=KernelMode) {
ProbeForRead (superAcceptInfo32,
sizeof (*superAcceptInfo32),
PROBE_ALIGNMENT32 (AFD_SUPER_ACCEPT_INFO32));
}
acceptHandle = superAcceptInfo32->AcceptHandle;
receiveDataLength = superAcceptInfo32->ReceiveDataLength;
localAddressLength = superAcceptInfo32->LocalAddressLength;
remoteAddressLength = superAcceptInfo32->RemoteAddressLength;
sanActive = superAcceptInfo32->SanActive;
fixAddressAlignment = superAcceptInfo32->FixAddressAlignment;
}
except (AFD_EXCEPTION_FILTER (&status)) {
goto complete;
}
}
else
#endif // _WIN64
{
PAFD_SUPER_ACCEPT_INFO superAcceptInfo;
superAcceptInfo = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
try {
if (Irp->RequestorMode!=KernelMode) {
ProbeForRead (superAcceptInfo,
sizeof (*superAcceptInfo),
PROBE_ALIGNMENT (AFD_SUPER_ACCEPT_INFO));
}
acceptHandle = superAcceptInfo->AcceptHandle;
receiveDataLength = superAcceptInfo->ReceiveDataLength;
localAddressLength = superAcceptInfo->LocalAddressLength;
remoteAddressLength = superAcceptInfo->RemoteAddressLength;
sanActive = superAcceptInfo->SanActive;
#ifndef i386
fixAddressAlignment = superAcceptInfo->FixAddressAlignment;
#endif
}
except (AFD_EXCEPTION_FILTER (&status)) {
goto complete;
}
}
//
// Check for if the caller is unaware of the SAN
// provider activation and report the error.
//
if (!sanActive && AfdSanServiceHelper!=NULL) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
"AFD: Process %p is being told to enable SAN on AcceptEx\n",
PsGetCurrentProcessId ()));
status = STATUS_INVALID_PARAMETER_12;
goto complete;
}
//
// 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->Listening
||
remoteAddressLength < (ULONG)FIELD_OFFSET (TRANSPORT_ADDRESS,
Address[0].Address)
||
//
// Do the check in such a manner that integer overflow
// (which is not enabled by the compiler) does not
// affect the validity of the result.
//
(totalLength=IrpSp->Parameters.DeviceIoControl.OutputBufferLength)<
receiveDataLength
||
(totalLength-=receiveDataLength) < localAddressLength
||
(totalLength-=localAddressLength) < remoteAddressLength
) {
if( !listenEndpoint->Listening ) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
"AfdSuperAccept: non-listening endpoint @ %08lX\n",
listenEndpoint
));
status = STATUS_INVALID_PARAMETER;
}
else {
status = STATUS_BUFFER_TOO_SMALL;
}
goto complete;
}
try {
if (IoAllocateMdl(Irp->UserBuffer,
IrpSp->Parameters.DeviceIoControl.OutputBufferLength,
FALSE, // Secondary buffer
TRUE, // Charge quota
Irp
)==NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto complete;
}
MmProbeAndLockPages (Irp->MdlAddress, Irp->RequestorMode, IoWriteAccess);
}
except (AFD_EXCEPTION_FILTER (&status)) {
goto complete;
}
ASSERT ((listenEndpoint->Type & AfdBlockTypeVcListening) == AfdBlockTypeVcListening);
//
// Obtain a pointer to the endpoint on which we're going to
// accept the connection.
//
status = ObReferenceObjectByHandle(
acceptHandle,
(IrpSp->Parameters.DeviceIoControl.IoControlCode>>14) & 3,
// DesiredAccess
*IoFileObjectType,
Irp->RequestorMode,
&acceptFileObject,
NULL
);
if ( !NT_SUCCESS(status) ) {
goto complete;
}
//
// We may have a file object that is not an AFD endpoint. Make sure
// that this is an actual AFD endpoint.
//
if (acceptFileObject->DeviceObject!= AfdDeviceObject) {
status = STATUS_INVALID_HANDLE;
goto complete;
}
acceptEndpoint = acceptFileObject->FsContext;
ASSERT( InterlockedIncrement( &acceptEndpoint->ObReferenceBias ) > 0 );
if (!AFD_START_STATE_CHANGE (acceptEndpoint, AfdEndpointStateConnected)) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
if (acceptEndpoint->TransportInfo!=listenEndpoint->TransportInfo ||
acceptEndpoint->State != AfdEndpointStateOpen ) {
status = STATUS_INVALID_PARAMETER;
goto complete_state_change;
}
#ifndef i386
acceptEndpoint->Common.VcConnecting.FixAddressAlignment = fixAddressAlignment;
#endif
Irp->Tail.Overlay.DriverContext[3] = acceptHandle;
//
// Save common IRP parameters in our stack location so
// we can retreive them when necessary
//
IrpSp->Parameters.AfdRestartSuperAcceptInfo.AfdAcceptFileObject = acceptFileObject;
IrpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength = receiveDataLength;
IrpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength = localAddressLength;
IrpSp->Parameters.AfdRestartSuperAcceptInfo.AfdRemoteAddressLength = remoteAddressLength;
//
// 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
);
status = AfdReplenishListenBacklog( listenEndpoint );
//
// Save the IRP, so that accept enpoint cleanup can find it.
// Note, that even if found, the cleanup won't touch the IRP
// until cancel routine is set in it.
//
ASSERT (acceptEndpoint->Irp==NULL);
acceptEndpoint->Irp = Irp;
//
// Get free connection from the list, if none is available,
// or direct super accept is disabled, go through the regular
// listen-accept path.
//
if (AfdDisableDirectSuperAccept ||
IS_DELAYED_ACCEPTANCE_ENDPOINT (listenEndpoint) ||
ExQueryDepthSList (&listenEndpoint->Common.VcListening.PreacceptedConnectionsListHead)
> AFD_MAXIMUM_FREE_CONNECTIONS ||
((listEntry = InterlockedPopEntrySList (
&listenEndpoint->Common.VcListening.FreeConnectionListHead
))==NULL)) {
//
// Setup super accept IRP to be put into the wait for
// listen queue. Internal device control distinguishes this
// from the regular wait for listen IRPs that come directly
// from the application.
//
IrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
//
// Mark this IRP as pending since we are going to return
// STATUS_PENDING no matter what (sometimes the actual
// status is hidden very deep inside the call stack and
// it is impossible to propagate it all the way up).
//
IoMarkIrpPending (Irp);
AfdWaitForListen (Irp, IrpSp);
//
// If connection allocation failed above, we need to report
// this to application. We delay this call in case there is
// already a preaccepted connection, so the allocation failure
// is not important.
//
if (!NT_SUCCESS (status)) {
AfdReportConnectionAllocationFailure (listenEndpoint, status);
}
return STATUS_PENDING;
}
//
// Get connection object of the list entry
//
connection = CONTAINING_RECORD(
listEntry,
AFD_CONNECTION,
SListEntry
);
//
// Stuff special constant into the connection object accept IRP
// pointer, so that cancel routine does not complete the IRP
// while we are still looking at it, but in the same time we
// can detect that the IRP was cancelled (cancel routine will
// replace the -1 with NULL to indicate that it has been ran).
// This technique avoids extra spinlock acquire/release and
// associated IRQL raise/lower on an extremely performance-sensitive
// code path.
//
connection->AcceptIrp = (PIRP)-1;
Irp->Tail.Overlay.DriverContext[0] = connection;
//
// We are going to pend this Irp, so mark it as pending
// and set up the cancel routine.
//
IoMarkIrpPending (Irp);
IoSetCancelRoutine( Irp, AfdCancelSuperAccept );
//
// Check if the IRP has already been canceled.
// If the cancel routine ran, it just reset the connection
// object accept pointer to NULL (instead of -1 that we stuffed
// in it above), but it did not complete the IRP.
//
if ( !Irp->Cancel &&
(InterlockedCompareExchangePointer (
(PVOID *)&connection->AcceptIrp,
Irp,
(PVOID)-1)==(PVOID)-1)) {
//
// Can't touch the IRP after this point since it may have already
// been canceled.
//
DEBUG Irp = NULL;
//
// Push the connection and associated Irp/endpoint
// onto preaccepted connection list.
//
if (InterlockedPushEntrySList(
&listenEndpoint->Common.VcListening.PreacceptedConnectionsListHead,
&connection->SListEntry
)==NULL) {
//
// This is the first Irp in the list, we need to check
// if there are any unaccepted connections that we
// can use to satisfy super accept.
//
AfdAcquireSpinLock (&listenEndpoint->SpinLock, &lockHandle);
if (!listenEndpoint->EndpointCleanedUp) {
LIST_ENTRY irpList;
InitializeListHead (&irpList);
//
// First see if there is an unaccepted connection
//
while (!IsListEmpty (&listenEndpoint->Common.VcListening.UnacceptedConnectionListHead)) {
connection = CONTAINING_RECORD(
listenEndpoint->Common.VcListening.UnacceptedConnectionListHead.Flink,
AFD_CONNECTION,
ListEntry
);
RemoveEntryList (&connection->ListEntry);
//
// Now make sure we still have super accept irp
//
if (AfdServiceSuperAccept (listenEndpoint, connection, &lockHandle, &irpList)) {
//
// The routine has found and completed super accept IRP
// Reaquire a spinlock and continue searching for more.
//
AfdAcquireSpinLock (&listenEndpoint->SpinLock, &lockHandle);
}
else {
//
// No super accept Irps, put connection back onto the list
// while we are still holding the lock and bail out.
//
InsertHeadList (&listenEndpoint->Common.VcListening.UnacceptedConnectionListHead,
&connection->ListEntry);
break;
}
}
AfdReleaseSpinLock (&listenEndpoint->SpinLock, &lockHandle);
//
// Complete failed super accept IRPs (if any)
//
if (!IsListEmpty (&irpList)) {
KIRQL cancelIrql;
//
// Make sure cancel routines will
// not access the completed IRPs
//
IoAcquireCancelSpinLock (&cancelIrql);
IoReleaseCancelSpinLock (cancelIrql);
while (!IsListEmpty (&irpList)) {
PIRP irp;
irp = CONTAINING_RECORD (irpList.Flink, IRP, Tail.Overlay.ListEntry);
RemoveEntryList (&irp->Tail.Overlay.ListEntry);
IoCompleteRequest (irp, AfdPriorityBoost);
}
}
}
else {
AfdReleaseSpinLock (&listenEndpoint->SpinLock, &lockHandle);
AfdFreeQueuedConnections (listenEndpoint);
}
}
else {
USHORT depth =
ExQueryDepthSList (&listenEndpoint->Common.VcListening.PreacceptedConnectionsListHead);
if (depth > listenEndpoint->Common.VcListening.MaxExtraConnections) {
//
// Update under the lock, so we do not corrupt
// other fields in the same memory access granularity unit.
// This should be infrequent operation anyway.
//
AfdAcquireSpinLock (&listenEndpoint->SpinLock, &lockHandle);
listenEndpoint->Common.VcListening.MaxExtraConnections = depth;
AfdReleaseSpinLock (&listenEndpoint->SpinLock, &lockHandle);
}
}
}
else {
//
// Reset and call the cancel routine, since
// even if cancel routine ran, it could not complete
// the irp because it was not set in the connection
// object. Note that cancel routine is done with the IRP
// once it releases cancel spinlock which we acquire here.
//
AfdCleanupSuperAccept (Irp, STATUS_CANCELLED);
if (IoSetCancelRoutine (Irp, NULL)==NULL) {
KIRQL cancelIrql;
IoAcquireCancelSpinLock (&cancelIrql);
IoReleaseCancelSpinLock (cancelIrql);
}
IoCompleteRequest (Irp, AfdPriorityBoost);
//
// We have to return pending because we have already
// marked the Irp as pending.
//
}
return STATUS_PENDING;
complete_state_change:
AFD_END_STATE_CHANGE (acceptEndpoint);
complete:
if ( acceptFileObject != NULL ) {
if (acceptEndpoint!=NULL) {
ASSERT( InterlockedDecrement( &acceptEndpoint->ObReferenceBias ) >= 0 );
}
ObDereferenceObject( acceptFileObject );
}
//
// Free MDL here as IO system can't do it if it is
// not locked.
//
if (Irp->MdlAddress!=NULL) {
if (Irp->MdlAddress->MdlFlags & MDL_PAGES_LOCKED) {
MmUnlockPages (Irp->MdlAddress);
}
IoFreeMdl (Irp->MdlAddress);
Irp->MdlAddress = NULL;
}
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, 0 );
return status;
} // AfdSuperAccept
NTSTATUS
AfdRestartSuperAccept (
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 accept file object.
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;
PFILE_OBJECT acceptFileObject;
PAFD_ENDPOINT acceptEndpoint;
PAFD_CONNECTION connection;
PIO_STACK_LOCATION irpSp;
AFD_LOCK_QUEUE_HANDLE lockHandle;
//
// Initialize some locals.
//
irpSp = IoGetCurrentIrpStackLocation( Irp );
listenEndpoint = irpSp->FileObject->FsContext;
acceptFileObject = Context;
acceptEndpoint = acceptFileObject->FsContext;
ASSERT (IS_AFD_ENDPOINT_TYPE (acceptEndpoint));
connection = acceptEndpoint->Common.VcConnecting.Connection;
ASSERT (connection->Type==AfdBlockTypeConnection);
ASSERT (connection->Endpoint==acceptEndpoint);
//
// Overwrite listen file object with accept file object
// since we won't be using listen file object anymore,
// while we still need to deference accept file object
// upon IRP completion.
//
irpSp->FileObject = acceptFileObject;
//
// Fix up the MDL pointer in the IRP.
//
ASSERT (Irp->MdlAddress==NULL);
Irp->MdlAddress = irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdMdlAddress;
//
// If pending has been returned for this irp then mark the current
// stack as pending.
//
if ( Irp->PendingReturned ) {
IoMarkIrpPending( Irp );
}
//
// Remember that a TDI accept has completed on this endpoint.
//
InterlockedDecrement(
&listenEndpoint->Common.VcListening.TdiAcceptPendingCount
);
connection->ConnectTime = KeQueryInterruptTime();
if ( NT_SUCCESS(Irp->IoStatus.Status)) {
//
// Set the endpoint to the connected state.
//
AfdAcquireSpinLock (&acceptEndpoint->SpinLock, &lockHandle);
acceptEndpoint->State = AfdEndpointStateConnected;
connection->State = AfdConnectionStateConnected;
acceptEndpoint->EventsActive |= AFD_POLL_SEND;
//
// Reference connection to prevent it from going away during
// accept process as the result of transmit file completion.
// (transmit file can now occur at any time since we
// marked the endpoint as connected and about to end state change)
//
REFERENCE_CONNECTION (connection);
AfdReleaseSpinLock (&acceptEndpoint->SpinLock, &lockHandle);
AFD_END_STATE_CHANGE(acceptEndpoint);
return AfdContinueSuperAccept (Irp, connection);
}
else {
//
// 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.
//
AFD_END_STATE_CHANGE(acceptEndpoint);
AfdDisconnectEventHandler(
NULL,
connection,
0,
NULL,
0,
NULL,
TDI_DISCONNECT_ABORT
);
ASSERT( InterlockedDecrement( &acceptEndpoint->ObReferenceBias ) >= 0 );
ObDereferenceObject( acceptFileObject );
//
// Check if we have secondary MDL for local address query and
// free it.
//
if (Irp->MdlAddress->Next!=NULL) {
//
// We never lock pages for this one (they are locked
// as part of main MDL).
//
ASSERT (irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength>0);
ASSERT ((Irp->MdlAddress->Next->MdlFlags & MDL_PAGES_LOCKED)==0);
IoFreeMdl (Irp->MdlAddress->Next);
Irp->MdlAddress->Next = NULL;
}
return STATUS_SUCCESS;
}
} // AfdRestartSuperAccept
VOID
AfdRestartSuperAcceptListen (
IN PIRP Irp,
IN PAFD_CONNECTION Connection
)
/*++
Routine Description:
The completion routine for the AFD wait for listen IRP portion
of a super accept.
Arguments:
Irp - The super accept IRP.
Connection - points to the connection object
Return Value:
None
--*/
{
PAFD_ENDPOINT acceptEndpoint;
PIO_STACK_LOCATION irpSp;
NTSTATUS status;
//
// Initialize some locals.
//
irpSp = IoGetCurrentIrpStackLocation( Irp );
acceptEndpoint = irpSp->FileObject->FsContext;
ASSERT (IS_AFD_ENDPOINT_TYPE (acceptEndpoint));
AFD_END_STATE_CHANGE(acceptEndpoint);
//
// Fix up the system buffer and MDL pointers in the IRP.
//
ASSERT (Irp->MdlAddress==NULL);
ASSERT (Irp->AssociatedIrp.SystemBuffer == NULL);
Irp->MdlAddress = irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdMdlAddress;
//
// This routine shouldn't have been called if accept failed
//
ASSERT ( NT_SUCCESS(Irp->IoStatus.Status) );
//
// Reference connection to prevent it from going away during
// accept process as the result of transmit file completion.
// (transmit file can now occur at any time since we
// marked the endpoint as connected and about to end state change)
//
REFERENCE_CONNECTION (Connection);
status = AfdContinueSuperAccept (Irp, Connection);
//
// If completion routine return anything other
// than STATUS_MORE_PROCESSING_REQUIRED, the IRP
// is ready to be completed. Otherwise, is was
// reused to call transport driver and will be completed
// by the driver. Note that in the latter case
// the IRP cannot be touched because
// it could have been completed inside of the
// completion routine or by the driver before the
// completion routine returned.
//
if (status!=STATUS_MORE_PROCESSING_REQUIRED) {
IoCompleteRequest (Irp, AfdPriorityBoost);
}
} // AfdRestartSuperAcceptListen
NTSTATUS
AfdRestartDelayedSuperAccept (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
The completion routine for the AFD wait for delayed accept 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 accept file object
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;
PFILE_OBJECT acceptFileObject;
PAFD_ENDPOINT acceptEndpoint;
PAFD_CONNECTION connection;
PIO_STACK_LOCATION irpSp;
AFD_LOCK_QUEUE_HANDLE lockHandle;
//
// Initialize some locals.
//
irpSp = IoGetCurrentIrpStackLocation( Irp );
listenEndpoint = irpSp->FileObject->FsContext;
acceptFileObject = Context;
acceptEndpoint = acceptFileObject->FsContext;
ASSERT (IS_AFD_ENDPOINT_TYPE (acceptEndpoint));
//
// Overwrite listen file object with accept file object
// since we won't be using listen file object anymore,
// while we still need to deference accept file object
// upon IRP completion.
//
irpSp->FileObject = acceptFileObject;
//
// Fix up the MDL pointer in the IRP.
//
ASSERT (Irp->MdlAddress==NULL);
Irp->MdlAddress = irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdMdlAddress;
AfdCompleteOutstandingIrp (acceptEndpoint, Irp);
//
// If pending has been returned for this irp then mark the current
// stack as pending.
//
if ( Irp->PendingReturned ) {
IoMarkIrpPending( Irp );
}
//
// Remember that a TDI accept has completed on this endpoint.
//
InterlockedDecrement(
&listenEndpoint->Common.VcListening.TdiAcceptPendingCount
);
AfdAcquireSpinLock (&acceptEndpoint->SpinLock, &lockHandle);
//
// The AFD connection object should now be in the endpoiont.
//
connection = AFD_CONNECTION_FROM_ENDPOINT( acceptEndpoint );
if (connection!=NULL) {
//
// If the IRP failed, quit processing.
//
if ( NT_SUCCESS(Irp->IoStatus.Status) ) {
acceptEndpoint->State = AfdEndpointStateConnected;
connection->State = AfdConnectionStateConnected;
acceptEndpoint->EventsActive |= AFD_POLL_SEND;
//
// Reference connection to prevent it from going away during
// accept process as the result of transmit file completion.
// (transmit file can now occur at any time since we
// marked the endpoint as connected and about to end state change)
//
REFERENCE_CONNECTION (connection);
AfdReleaseSpinLock (&acceptEndpoint->SpinLock, &lockHandle);
AFD_END_STATE_CHANGE(acceptEndpoint);
return AfdContinueSuperAccept (Irp, connection);
}
else {
//
// 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.
//
AfdReleaseSpinLock (&acceptEndpoint->SpinLock, &lockHandle);
AFD_END_STATE_CHANGE(acceptEndpoint);
AfdDisconnectEventHandler(
NULL,
connection,
0,
NULL,
0,
NULL,
TDI_DISCONNECT_ABORT
);
ASSERT( InterlockedDecrement( &acceptEndpoint->ObReferenceBias ) >= 0 );
ObDereferenceObject( acceptFileObject );
//
// After dereferencing file object we shouldn't be accessing it
// or associated endpoint structure
//
}
}
else {
// this could happed if transmit file cleaned up the object
// really quickly somehow,
AfdReleaseSpinLock (&acceptEndpoint->SpinLock, &lockHandle);
ASSERT( InterlockedDecrement( &acceptEndpoint->ObReferenceBias ) >= 0 );
ObDereferenceObject( acceptFileObject );
//
// After dereferencing file object we shouldn't be accessing it
// or associated endpoint structure
//
}
//
// Check if we have secondary MDL for local address query and
// free it.
//
if (Irp->MdlAddress->Next!=NULL) {
//
// We never lock pages for this one (they are locked
// as part of main MDL).
//
ASSERT (irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength>0);
ASSERT ((Irp->MdlAddress->Next->MdlFlags & MDL_PAGES_LOCKED)==0);
IoFreeMdl (Irp->MdlAddress->Next);
Irp->MdlAddress->Next = NULL;
}
return STATUS_SUCCESS;
} // AfdRestartDelayedSuperAccept
NTSTATUS
AfdContinueSuperAccept (
IN PIRP Irp,
PAFD_CONNECTION Connection
)
/*++
Routine Description:
Continues super accept IRP processing after the initial accept
phase by requesting local address and/or first portion of the
received data.
Arguments:
Irp - a pointer to the super accept IRP
Connection - pointer to the accepted connection
Return Value:
STATUS_SUCCESS if Irp processing is completed
STATUS_MORE_PROCESSING_REQUIRED if it submits another request
and processing will ocurr in the completion routine.
--*/
{
PAFD_ENDPOINT acceptEndpoint;
PIO_STACK_LOCATION irpSp;
ULONG length;
//
// Initialize locals
//
irpSp = IoGetCurrentIrpStackLocation (Irp);
acceptEndpoint = irpSp->FileObject->FsContext;
//
// See if we need to get local address.
//
if (irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength>0) {
ASSERT (Irp->MdlAddress->Next!=NULL);
//
// Get the MDL that describes local address part of the user buffer
// The oritinal mdl chain address is safe in our stack location
//
Irp->MdlAddress = Irp->MdlAddress->Next;
//
// Unchain the address MDL from the receive MDL - we will
// free it upon completion of the address query operation
//
((PMDL)irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdMdlAddress)->Next = NULL;
ASSERT (irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength
== MmGetMdlByteCount (Irp->MdlAddress));
IoBuildPartialMdl (
irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdMdlAddress,
Irp->MdlAddress,
MmGetMdlVirtualAddress (Irp->MdlAddress),
MmGetMdlByteCount (Irp->MdlAddress)
);
TdiBuildQueryInformation(
Irp,
Connection->DeviceObject,
Connection->FileObject,
AfdRestartSuperAcceptGetAddress,
Connection,
TDI_QUERY_ADDRESS_INFO,
Irp->MdlAddress
);
//
// Perform the local address query. We'll continue processing from
// the completion routine.
//
AfdIoCallDriver( acceptEndpoint, Connection->DeviceObject, Irp );
return STATUS_MORE_PROCESSING_REQUIRED;
}
//
// See if want to get first portion of the data
//
else if (irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength>0) {
PIO_STACK_LOCATION nextIrpSp;
ASSERT (Irp->MdlAddress->Next==NULL);
//
// Get the length of the receive portion of the buffer
// and save the length of the MDL to restore in the completion routine
// Set the length of the MDL to match that of the receive request
//
length = irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength;
irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength =
MmGetMdlByteCount (Irp->MdlAddress);
Irp->MdlAddress->ByteCount = length;
//
// 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 = irpSp->FileObject;
nextIrpSp->DeviceObject = IoGetRelatedDeviceObject( nextIrpSp->FileObject );
nextIrpSp->MajorFunction = IRP_MJ_READ;
nextIrpSp->Parameters.Read.Length = length;
nextIrpSp->Parameters.Read.Key = 0;
nextIrpSp->Parameters.Read.ByteOffset.QuadPart = 0;
IoSetCompletionRoutine(
Irp,
AfdRestartSuperAcceptReceive,
Connection,
TRUE,
TRUE,
TRUE
);
//
// Perform the receive. We'll continue processing from
// the completion routine.
//
AfdIoCallDriver( acceptEndpoint, nextIrpSp->DeviceObject, Irp );
return STATUS_MORE_PROCESSING_REQUIRED;
}
Irp->IoStatus.Information = 0;
if (Connection->AbortIndicated && NT_SUCCESS (Irp->IoStatus.Status)) {
Irp->IoStatus.Status = STATUS_CONNECTION_RESET;
}
if (NT_SUCCESS (Irp->IoStatus.Status) &&
(Connection->RemoteAddress!=NULL) &&
(KeInitializeApc (&acceptEndpoint->Common.VcConnecting.Apc,
PsGetThreadTcb (Irp->Tail.Overlay.Thread),
Irp->ApcEnvironment,
AfdSuperAcceptApcKernelRoutine,
AfdSuperAcceptApcRundownRoutine,
(PKNORMAL_ROUTINE)NULL,
KernelMode,
NULL
),
KeInsertQueueApc (&acceptEndpoint->Common.VcConnecting.Apc,
Irp,
Connection,
AfdPriorityBoost))) {
return STATUS_MORE_PROCESSING_REQUIRED;
}
else {
//
// Dereference the accept file object and tell IO to complete this IRP.
//
ASSERT( InterlockedDecrement( &acceptEndpoint->ObReferenceBias ) >= 0 );
ObDereferenceObject( irpSp->FileObject );
//
// After dereferencing file object we shouldn't be accessing it
// or associated endpoint structure
//
DEREFERENCE_CONNECTION (Connection);
return STATUS_SUCCESS;
}
}
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 accepted connection
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 acceptEndpoint;
PAFD_CONNECTION connection;
PIO_STACK_LOCATION irpSp;
//
// Initialize some locals.
//
irpSp = IoGetCurrentIrpStackLocation (Irp);
acceptEndpoint = irpSp->FileObject->FsContext;
ASSERT (IS_AFD_ENDPOINT_TYPE (acceptEndpoint));
connection = Context;
ASSERT (connection->Type==AfdBlockTypeConnection);
ASSERT (connection->Endpoint==acceptEndpoint);
AfdCompleteOutstandingIrp (acceptEndpoint, Irp);
//
// If pending has been returned for this irp then mark the current
// stack as pending.
//
if ( Irp->PendingReturned ) {
IoMarkIrpPending( Irp );
}
ASSERT (Irp->MdlAddress->MdlFlags & MDL_PARTIAL);
IoFreeMdl( Irp->MdlAddress );
//
// Fix up the MDL pointer in the IRP and set local address length
// to 0 to use the common routine for receive part of the super
// accept Irp
//
Irp->MdlAddress = irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdMdlAddress;
if (NT_SUCCESS (Irp->IoStatus.Status)) {
#ifndef i386
if (acceptEndpoint->Common.VcConnecting.FixAddressAlignment) {
PTDI_ADDRESS_INFO addressInfo = (PVOID)
((PUCHAR)MmGetSystemAddressForMdl(Irp->MdlAddress)
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength);
USHORT addressLength = addressInfo->Address.Address[0].AddressLength+sizeof(USHORT);
USHORT UNALIGNED *pAddrLength = (PVOID)
((PUCHAR)addressInfo
+irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength
-sizeof(USHORT));
RtlMoveMemory (addressInfo,
&addressInfo->Address.Address[0].AddressType,
addressLength);
*pAddrLength = addressLength;
}
#endif // ifndef i386
irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength = 0;
return AfdContinueSuperAccept (Irp, connection);
}
else {
//
// Dereference the accept file object and tell IO to complete this IRP.
//
ASSERT( InterlockedDecrement( &acceptEndpoint->ObReferenceBias ) >= 0 );
ObDereferenceObject( irpSp->FileObject );
//
// After dereferencing file object we shouldn't be accessing it
// or associated endpoint structure
//
DEREFERENCE_CONNECTION (connection);
return STATUS_SUCCESS;
}
} // 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 accepted connection
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 acceptEndpoint;
PAFD_CONNECTION connection;
PIO_STACK_LOCATION irpSp;
//
// Initialize some locals.
//
irpSp = IoGetCurrentIrpStackLocation (Irp);
acceptEndpoint = irpSp->FileObject->FsContext;
ASSERT (IS_AFD_ENDPOINT_TYPE (acceptEndpoint));
connection = Context;
ASSERT (connection->Type == AfdBlockTypeConnection);
ASSERT (connection->Endpoint==acceptEndpoint);
AfdCompleteOutstandingIrp (acceptEndpoint, Irp);
//
// Restore MDL length so that IO system can properly unmap
// and unlock it when it completes the IRP
//
ASSERT (Irp->MdlAddress!=NULL);
ASSERT (Irp->MdlAddress->Next==NULL);
Irp->MdlAddress->ByteCount =
irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength;
//
// If pending has been returned for this irp then mark the current
// stack as pending.
//
if ( Irp->PendingReturned ) {
IoMarkIrpPending( Irp );
}
if (NT_SUCCESS (Irp->IoStatus.Status) &&
(connection->RemoteAddress!=NULL) &&
(KeInitializeApc (&acceptEndpoint->Common.VcConnecting.Apc,
PsGetThreadTcb (Irp->Tail.Overlay.Thread),
Irp->ApcEnvironment,
AfdSuperAcceptApcKernelRoutine,
AfdSuperAcceptApcRundownRoutine,
(PKNORMAL_ROUTINE)NULL,
KernelMode,
NULL
),
KeInsertQueueApc (&acceptEndpoint->Common.VcConnecting.Apc,
Irp,
connection,
AfdPriorityBoost))) {
return STATUS_MORE_PROCESSING_REQUIRED;
}
else {
//
// Dereference the accept file object and tell IO to complete this IRP.
//
ASSERT( InterlockedDecrement( &acceptEndpoint->ObReferenceBias ) >= 0 );
ObDereferenceObject( irpSp->FileObject );
//
// After dereferencing file object we shouldn't be accessing it
// or associated endpoint structure
//
DEREFERENCE_CONNECTION (connection);
return STATUS_SUCCESS;
}
} // AfdRestartSuperAcceptReceive
VOID
AfdSuperAcceptApcKernelRoutine (
IN struct _KAPC *Apc,
IN OUT PKNORMAL_ROUTINE *NormalRoutine,
IN OUT PVOID *NormalContext,
IN OUT PVOID *SystemArgument1,
IN OUT PVOID *SystemArgument2
)
{
PIRP irp;
PIO_STACK_LOCATION irpSp;
PAFD_ENDPOINT endpoint;
PAFD_CONNECTION connection;
PVOID context;
PAGED_CODE ();
#if DBG
try {
#endif
ASSERT (*NormalRoutine == NULL);
endpoint = CONTAINING_RECORD (Apc, AFD_ENDPOINT, Common.VcConnecting.Apc);
ASSERT (IS_AFD_ENDPOINT_TYPE (endpoint));
irp = *SystemArgument1;
irpSp = IoGetCurrentIrpStackLocation( irp );
ASSERT (irpSp->FileObject->FsContext==endpoint);
connection = *SystemArgument2;
ASSERT( connection->Type == AfdBlockTypeConnection );
ASSERT (connection->Endpoint==endpoint);
//
// Copy remote address to the user mode context
//
context = AfdLockEndpointContext (endpoint);
if ( (((CLONG)(endpoint->Common.VcConnecting.RemoteSocketAddressOffset+
endpoint->Common.VcConnecting.RemoteSocketAddressLength)) <
endpoint->ContextLength) &&
(endpoint->Common.VcConnecting.RemoteSocketAddressLength >=
connection->RemoteAddress->Address[0].AddressLength +
sizeof(u_short))) {
RtlMoveMemory ((PUCHAR)context +
endpoint->Common.VcConnecting.RemoteSocketAddressOffset,
&connection->RemoteAddress->Address[0].AddressType,
connection->RemoteAddress->Address[0].AddressLength +
sizeof(u_short));
}
else {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_WARNING_LEVEL,
"AfdSuperAcceptApcKernelRoutine: Could not copy remote address for AcceptEx on endpoint: %p, process: %p\n",
endpoint, endpoint->OwningProcess));
}
AfdUnlockEndpointContext (endpoint, context);
AFD_RETURN_REMOTE_ADDRESS (
connection->RemoteAddress,
connection->RemoteAddressLength
);
connection->RemoteAddress = NULL;
//
// Dereference the accept file object and tell IO to complete this IRP.
//
ASSERT( InterlockedDecrement( &endpoint->ObReferenceBias ) >= 0 );
ObDereferenceObject( irpSp->FileObject );
//
// After dereferencing file object we shouldn't be accessing it
// or associated endpoint structure
//
DEREFERENCE_CONNECTION (connection);
IoCompleteRequest (irp, AfdPriorityBoost);
#if DBG
}
except (AfdApcExceptionFilter (
GetExceptionInformation(),
(LPSTR)__FILE__,
(LONG)__LINE__)) {
DbgBreakPoint ();
}
#endif
}
VOID
AfdSuperAcceptApcRundownRoutine (
IN struct _KAPC *Apc
)
{
PIRP irp;
PIO_STACK_LOCATION irpSp;
PAFD_ENDPOINT endpoint;
PAFD_CONNECTION connection;
PAGED_CODE ();
endpoint = CONTAINING_RECORD (Apc, AFD_ENDPOINT, Common.VcConnecting.Apc);
ASSERT (IS_AFD_ENDPOINT_TYPE (endpoint));
irp = Apc->SystemArgument1;
irpSp = IoGetCurrentIrpStackLocation( irp );
ASSERT (irpSp->FileObject->FsContext==endpoint);
connection = Apc->SystemArgument2;
ASSERT( connection->Type == AfdBlockTypeConnection );
ASSERT (connection->Endpoint==endpoint);
//
// Dereference the accept file object and tell IO to complete this IRP.
//
ASSERT( InterlockedDecrement( &endpoint->ObReferenceBias ) >= 0 );
ObDereferenceObject( irpSp->FileObject );
//
// After dereferencing file object we shouldn't be accessing it
// or associated endpoint structure
//
DEREFERENCE_CONNECTION (connection);
IoCompleteRequest (irp, AfdPriorityBoost);
}
NTSTATUS
FASTCALL
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;
AFD_LOCK_QUEUE_HANDLE lockHandle;
//
// Set up local variables.
//
endpoint = IrpSp->FileObject->FsContext;
deferAcceptInfo = Irp->AssociatedIrp.SystemBuffer;
Irp->IoStatus.Information = 0;
//
// Make sure that this request is valid.
//
if( !endpoint->Listening ||
IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(AFD_DEFER_ACCEPT_INFO) ) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
ASSERT ((endpoint->Type & AfdBlockTypeVcListening) == AfdBlockTypeVcListening);
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
//
// Find the specified connection. If it cannot be found, then this
// is a bogus request.
//
connection = AfdGetReturnedConnection(
endpoint,
deferAcceptInfo->Sequence
);
if( connection == NULL ) {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
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 ) {
//
// Reenable the accept event bit, and if there are additional
// unaccepted connections on the endpoint, post another event.
//
endpoint->EventsActive &= ~AFD_POLL_ACCEPT;
IF_DEBUG(EVENT_SELECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdDeferAccept: Endp %08lX, Active %08lX\n",
endpoint,
endpoint->EventsActive
));
}
if( !IsListEmpty( &endpoint->Common.VcListening.UnacceptedConnectionListHead ) ) {
AfdIndicateEventSelectEvent(
endpoint,
AFD_POLL_ACCEPT,
STATUS_SUCCESS
);
}
//
// Special handling for SAN connections
//
if (connection->SanConnection) {
PIRP connectIrp;
//
// Snag the connect indication IRP
//
connectIrp = connection->ConnectIrp;
ASSERT (connectIrp!=NULL);
connection->ConnectIrp = NULL;
//
// We can now release listen endpoint spinlock
// The cancel routine will not find IRP in the connection
//
if (IoSetCancelRoutine (connectIrp, NULL)==NULL) {
KIRQL cancelIrql;
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
//
// Cancel routine is running, make sure
// it finishes before proceeding further
//
IoAcquireCancelSpinLock (&cancelIrql);
IoReleaseCancelSpinLock (cancelIrql);
connectIrp->IoStatus.Status = STATUS_CANCELLED;
}
else {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
connectIrp->IoStatus.Status = STATUS_CONNECTION_REFUSED;
}
//
// Return the connection and complete SAN provider IRP
//
connection->Endpoint = NULL;
connection->SanConnection = FALSE;
AfdSanReleaseConnection (endpoint, connection, FALSE);
DEREFERENCE_ENDPOINT (endpoint);
connectIrp->IoStatus.Information = 0;
IoCompleteRequest (connectIrp, AfdPriorityBoost);
}
else {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
//
// Abort the connection.
//
AfdAbortConnection( connection );
//
// 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;
InsertHeadList(
&endpoint->Common.VcListening.UnacceptedConnectionListHead,
&connection->ListEntry
);
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
}
status = STATUS_SUCCESS;
complete:
Irp->IoStatus.Status = status;
ASSERT( Irp->CancelRoutine == NULL );
IoCompleteRequest( Irp, AfdPriorityBoost );
return status;
} // AfdDeferAccept
NTSTATUS
AfdRestartDelayedAccept (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
The completion routine for the AFD wait for delayed accept IRP portion
of an accept.
Arguments:
DeviceObject - the devoce object on which the request is completing.
Irp - The accept IRP.
Context - points to accept file object
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.
--*/
{
PIO_STACK_LOCATION irpSp;
PFILE_OBJECT acceptFileObject;
PAFD_ENDPOINT acceptEndpoint;
PAFD_CONNECTION connection;
PAFD_ENDPOINT listenEndpoint;
AFD_LOCK_QUEUE_HANDLE lockHandle;
acceptFileObject = Context;
acceptEndpoint = acceptFileObject->FsContext;
irpSp = IoGetCurrentIrpStackLocation (Irp);
listenEndpoint = irpSp->FileObject->FsContext;
//
// Remember that a TDI accept has completed on this endpoint.
//
InterlockedDecrement(
&listenEndpoint->Common.VcListening.TdiAcceptPendingCount
);
AfdCompleteOutstandingIrp (acceptEndpoint, Irp);
if ( Irp->PendingReturned ) {
IoMarkIrpPending( Irp );
}
AfdAcquireSpinLock (&acceptEndpoint->SpinLock, &lockHandle);
//
// The AFD connection object should now be in the endpoiont.
//
connection = AFD_CONNECTION_FROM_ENDPOINT( acceptEndpoint );
if (connection!=NULL) {
if (NT_SUCCESS (Irp->IoStatus.Status)) {
acceptEndpoint->State = AfdEndpointStateConnected;
connection->State = AfdConnectionStateConnected;
acceptEndpoint->EventsActive |= AFD_POLL_SEND;
acceptEndpoint->EnableSendEvent = TRUE;
AfdReleaseSpinLock (&acceptEndpoint->SpinLock, &lockHandle);
AFD_END_STATE_CHANGE (acceptEndpoint);
}
else {
//
// 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.
//
REFERENCE_CONNECTION (connection);
AfdReleaseSpinLock (&acceptEndpoint->SpinLock, &lockHandle);
AFD_END_STATE_CHANGE (acceptEndpoint);
AfdDisconnectEventHandler(
NULL,
connection,
0,
NULL,
0,
NULL,
TDI_DISCONNECT_ABORT
);
DEREFERENCE_CONNECTION (connection);
}
}
ASSERT( InterlockedDecrement( &acceptEndpoint->ObReferenceBias ) >= 0 );
ObDereferenceObject( acceptFileObject );
Irp->AssociatedIrp.SystemBuffer = irpSp->Parameters.AfdRestartDelayedAcceptInfo.AfdSystemBuffer;
return STATUS_SUCCESS;
}
VOID
AfdCleanupSuperAccept (
IN PIRP Irp,
IN NTSTATUS Status
)
/*++
Routine Description:
Cleans up a super accept IRP and prepeares it for completion
Arguments:
Irp - the IRP to cleanup.
Status - failure status
Return Value:
None.
--*/
{
PAFD_ENDPOINT listenEndpoint;
PFILE_OBJECT acceptFileObject;
PAFD_ENDPOINT acceptEndpoint;
PIO_STACK_LOCATION irpSp;
AFD_LOCK_QUEUE_HANDLE lockHandle;
ASSERT (!NT_SUCCESS (Status));
//
// Initialize some locals.
//
irpSp = IoGetCurrentIrpStackLocation (Irp);
listenEndpoint = irpSp->FileObject->FsContext;
//
// 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 once it is pulled from pre-accepted connection
// list.
//
InterlockedDecrement (&listenEndpoint->Common.VcListening.FailedConnectionAdds);
acceptFileObject = irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdAcceptFileObject;
acceptEndpoint = acceptFileObject->FsContext;
ASSERT (IS_AFD_ENDPOINT_TYPE (acceptEndpoint));
//
// Cleanup super accept IRP out of endpoint.
//
AfdAcquireSpinLock (&acceptEndpoint->SpinLock, &lockHandle);
ASSERT (acceptEndpoint->Irp==Irp); // May need to remove this assert
// in the future.
acceptEndpoint->Irp = NULL;
AfdReleaseSpinLock (&acceptEndpoint->SpinLock, &lockHandle);
//
// Mark the end of state change letting the endpoint
// to be used again in state change operation (e.g. accept).
//
AFD_END_STATE_CHANGE (acceptEndpoint);
//
// Dereference accept file object
//
ASSERT( InterlockedDecrement( &acceptEndpoint->ObReferenceBias ) >= 0 );
ObDereferenceObject( acceptFileObject );
//
// Check if we have secondary MDL for local address query and
// free it.
//
if (Irp->MdlAddress->Next!=NULL) {
//
// We never lock pages for this one (they are locked
// as part of main MDL).
//
ASSERT (irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength>0);
ASSERT ((Irp->MdlAddress->Next->MdlFlags & MDL_PAGES_LOCKED)==0);
IoFreeMdl (Irp->MdlAddress->Next);
Irp->MdlAddress->Next = NULL;
}
//
// Set the status specified in the IRP and return
// The caller will eventually complete it.
//
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
return;
}
VOID
AfdCancelSuperAccept (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Cancels a super accept IRP that is pended in AFD.
Arguments:
DeviceObject - not used.
Irp - the IRP to cancel.
Return Value:
None.
--*/
{
PAFD_CONNECTION connection;
connection = Irp->Tail.Overlay.DriverContext[0];
ASSERT (connection->Type==AfdBlockTypeConnection);
ASSERT (connection->Endpoint==NULL);
//
// If IRP is in the connection object, cleanup and complete it
//
if (InterlockedExchangePointer (
(PVOID *)&connection->AcceptIrp,
NULL)==Irp) {
IoReleaseCancelSpinLock( Irp->CancelIrql );
AfdCleanupSuperAccept (Irp, STATUS_CANCELLED);
IoCompleteRequest( Irp, AfdPriorityBoost );
}
else {
IoReleaseCancelSpinLock( Irp->CancelIrql );
}
return;
}
BOOLEAN
AfdServiceSuperAccept (
IN PAFD_ENDPOINT Endpoint,
IN PAFD_CONNECTION Connection,
IN PAFD_LOCK_QUEUE_HANDLE LockHandle,
OUT PLIST_ENTRY AcceptIrpList
)
/*++
Routine Description:
Attemts to satisfy super accept irp using the incoming
connection. This routine must be called with listening endpoint
spinlock held.
Arguments:
Endpoint - listening endpoint on which connection is
being accepted
Connection - connection being accepted.
AcceptIrpList - returns a list of accept Irps which were failed and
need to be completed after listening endpoint spinlock
is released.
Return Value:
TRUE - the super accept IRP was found and is in the head of the list
FALSE - no usable super accept IRP exists.
--*/
{
PSINGLE_LIST_ENTRY listEntry;
PIRP acceptIrp;
PAFD_CONNECTION oldConnection;
//
// Keep removing super accept IRPs while there are any there
//
while ((listEntry = InterlockedPopEntrySList (
&Endpoint->Common.VcListening.PreacceptedConnectionsListHead
))!=NULL) {
NTSTATUS status;
//
// Find the connection pointer from the list entry and return a
// pointer to the connection object.
//
oldConnection = CONTAINING_RECORD(
listEntry,
AFD_CONNECTION,
SListEntry
);
acceptIrp = InterlockedExchangePointer ((PVOID *)&oldConnection->AcceptIrp, NULL);
//
// Check if there is accept irp associated with
// this connection, if not just put it back on the free list
// (the IRP must have been cancelled)
//
if (acceptIrp!=NULL) {
if (IoSetCancelRoutine (acceptIrp, NULL)!=NULL) {
PFILE_OBJECT acceptFileObject;
PAFD_ENDPOINT acceptEndpoint;
PIO_STACK_LOCATION irpSp;
//
// Initialize some locals.
//
irpSp = IoGetCurrentIrpStackLocation (acceptIrp);
acceptFileObject = irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdAcceptFileObject;
acceptEndpoint = acceptFileObject->FsContext;
ASSERT (IS_AFD_ENDPOINT_TYPE (acceptEndpoint));
//
// Check if super accept Irp has enough space for
// the remote address
//
if (Connection->RemoteAddressLength>
irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdRemoteAddressLength) {
status = STATUS_BUFFER_TOO_SMALL;
}
//
// Check if we have enough system PTE's to map
// the buffer.
//
else if ((status = AfdMapMdlChain (acceptIrp->MdlAddress)),
!NT_SUCCESS (status)) {
NOTHING;
}
else if (Connection->SanConnection) {
status = AfdSanAcceptCore (acceptIrp, acceptFileObject, Connection, LockHandle);
if (status==STATUS_PENDING) {
//
// Accept IRP is pending waiting for Switch
// completion notification
// Release old connection object
//
ASSERT (oldConnection->Endpoint==NULL);
InterlockedPushEntrySList (
&Endpoint->Common.VcListening.FreeConnectionListHead,
&oldConnection->SListEntry);
}
else {
//
// Something failed, we need to complete accept IRP
//
ASSERT (NT_ERROR (status));
AfdCleanupSuperAccept (acceptIrp, status);
IoCompleteRequest (acceptIrp, AfdPriorityBoost);
//
// This connection has already been diassociated from endpoint.
// If backlog is below the level we need, put it on the free
// list, otherwise, get rid of it.
//
ASSERT (oldConnection->Endpoint==NULL);
if (InterlockedIncrement (&Endpoint->Common.VcListening.FailedConnectionAdds)>0) {
InterlockedDecrement (&Endpoint->Common.VcListening.FailedConnectionAdds);
InterlockedPushEntrySList (
&Endpoint->Common.VcListening.FreeConnectionListHead,
&oldConnection->SListEntry);
}
else {
DEREFERENCE_CONNECTION (oldConnection);
}
}
//
// Complete previously failed accept irps if any.
//
while (!IsListEmpty (AcceptIrpList)) {
PIRP irp;
irp = CONTAINING_RECORD (AcceptIrpList->Flink, IRP, Tail.Overlay.ListEntry);
RemoveEntryList (&irp->Tail.Overlay.ListEntry);
IoCompleteRequest (irp, AfdPriorityBoost);
}
return TRUE;
}
//
// Allocate MDL for local address query if requested
//
else if ((irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength>0) &&
(IoAllocateMdl ((PUCHAR)acceptIrp->UserBuffer+irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength,
irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength,
TRUE,
FALSE,
acceptIrp)==NULL)){
status = STATUS_INSUFFICIENT_RESOURCES;
}
else {
//
// Copy over the address information to the user's buffer.
//
#ifndef i386
if (acceptEndpoint->Common.VcConnecting.FixAddressAlignment) {
USHORT addressLength =
Connection->RemoteAddress->Address[0].AddressLength
+ sizeof (USHORT);
USHORT UNALIGNED *pAddrLength = (PVOID)
((PUCHAR)MmGetSystemAddressForMdl (acceptIrp->MdlAddress)
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdRemoteAddressLength
- sizeof (USHORT));
RtlMoveMemory (
(PUCHAR)MmGetSystemAddressForMdl (acceptIrp->MdlAddress)
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength,
&Connection->RemoteAddress->Address[0].AddressType,
addressLength);
*pAddrLength = addressLength;
}
else
#endif
{
RtlMoveMemory (
(PUCHAR)MmGetSystemAddressForMdl (acceptIrp->MdlAddress)
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdReceiveDataLength
+ irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdLocalAddressLength,
Connection->RemoteAddress,
Connection->RemoteAddressLength);
}
status = AfdAcceptCore (acceptIrp, acceptEndpoint, Connection);
if (status==STATUS_SUCCESS) {
AfdReleaseSpinLock (&Endpoint->SpinLock, LockHandle);
//
// Decrement counter to account for connection being
// returned to the free pool. No need to do this because
// we are picking up a connection from the free pool
// InterlockedDecrement (&Endpoint->Common.VcListening.FailedConnectionAdds);
//
ASSERT (oldConnection->Endpoint==NULL);
InterlockedPushEntrySList (
&Endpoint->Common.VcListening.FreeConnectionListHead,
&oldConnection->SListEntry);
//
// Complete previously failed accept irps if any.
//
while (!IsListEmpty (AcceptIrpList)) {
PIRP irp;
irp = CONTAINING_RECORD (AcceptIrpList->Flink, IRP, Tail.Overlay.ListEntry);
RemoveEntryList (&irp->Tail.Overlay.ListEntry);
IoCompleteRequest (irp, AfdPriorityBoost);
}
//
// Make irp look like it is completed by the
// transport.
//
acceptIrp->IoStatus.Status = STATUS_SUCCESS;
irpSp->Parameters.AfdRestartSuperAcceptInfo.AfdMdlAddress = acceptIrp->MdlAddress;
acceptIrp->MdlAddress = NULL;
irpSp->FileObject = acceptFileObject;
//
// Call completion routine directly to simulate
// completion by the transport stack
//
AfdRestartSuperAcceptListen (acceptIrp, Connection);
return TRUE;
}
else {
ASSERT (status!=STATUS_PENDING);
}
}
}
else { // if (IoSetCancelRoutine (accpetIrp, NULL)!=NULL)
status = STATUS_CANCELLED;
}
//
// Cleanup the IRP and insert it into the completion list
//
AfdCleanupSuperAccept (acceptIrp, status);
InsertTailList (AcceptIrpList,
&acceptIrp->Tail.Overlay.ListEntry);
} // if (acceptIrp!=NULL)
else {
status = STATUS_CANCELLED;
}
//
// This connection has already been diassociated from endpoint.
// If backlog is below the level we need, put it on the free
// list, otherwise, get rid of it.
//
ASSERT (oldConnection->Endpoint==NULL);
if (Endpoint->Common.VcListening.FailedConnectionAdds>=0 &&
status!=STATUS_INSUFFICIENT_RESOURCES &&
ExQueryDepthSList (&Endpoint->Common.VcListening.FreeConnectionListHead)<AFD_MAXIMUM_FREE_CONNECTIONS) {
InterlockedPushEntrySList (
&Endpoint->Common.VcListening.FreeConnectionListHead,
&oldConnection->SListEntry);
}
else {
InterlockedIncrement (&Endpoint->Common.VcListening.FailedConnectionAdds);
DEREFERENCE_CONNECTION (oldConnection);
}
}
return FALSE;
}
NTSTATUS
AfdSetupAcceptEndpoint (
PAFD_ENDPOINT ListenEndpoint,
PAFD_ENDPOINT AcceptEndpoint,
PAFD_CONNECTION Connection
)
/*++
Routine Description:
Sets up the accept endpoint to get ready to accept connection
(copies parameters of the listening endpoint on which connection
was indicated)
Arguments:
ListenEndpoint - endpoint on which connection was indicated
AcceptEndpoint - endpoint on which to accept the connection
Connection - connection to accept
Return Value:
STATUS_SUCCESS - endpoint state/parameters adjusted OK
STATUS_CANCELLED - endpoint has already been cleaned up.
Note:
Both accepting and listening endpoint spinlocks must be held when
calling this routine.
--*/
{
//
// Check the state of the accepting endpoint.
//
if ( AcceptEndpoint->EndpointCleanedUp ) {
return STATUS_CANCELLED;
}
//
// Remove super accept IRP from the endpoint
//
AcceptEndpoint->Irp = NULL;
//
// 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->TdiServiceFlags = ListenEndpoint->TdiServiceFlags;
ASSERT (AcceptEndpoint->TransportInfo == ListenEndpoint->TransportInfo);
ASSERT (AcceptEndpoint->TransportInfo->ReferenceCount>0);
//
// 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. Note that we actually already have a refernce to
// the endpoint by the virtue of its file object
//
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 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;
//
// Copy the pointer to the local address. Because we keep listen
// endpoint alive for as long as any of its connection is
// active, we can rely on the fact that address structure won't go
// away as well.
//
AcceptEndpoint->LocalAddress = ListenEndpoint->LocalAddress;
AcceptEndpoint->LocalAddressLength = ListenEndpoint->LocalAddressLength;
return STATUS_SUCCESS;
}