Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2685 lines
83 KiB

/*++
Copyright (c) 1989-1999 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:
Vadim Eydelman (vadime) 1999 JoinLeaf implementation
Datagram connect via transport
Connect optimizations and syncronization with
user mode code.
--*/
#include "afdp.h"
NTSTATUS
AfdDoDatagramConnect (
IN PFILE_OBJECT FileObject,
IN PIRP Irp,
IN BOOLEAN HalfConnect
);
NTSTATUS
AfdRestartConnect (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
AfdRestartDgConnect (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
VOID
AfdSetupConnectDataBuffers (
IN PAFD_ENDPOINT Endpoint,
IN PAFD_CONNECTION Connection,
IN OUT PTDI_CONNECTION_INFORMATION *RequestConnectionInformation,
IN OUT PTDI_CONNECTION_INFORMATION *ReturnConnectionInformation
);
BOOLEAN
AfdConnectionStart (
IN PAFD_ENDPOINT Endpoint
);
VOID
AfdEnableFailedConnectEvent(
IN PAFD_ENDPOINT Endpoint
);
NTSTATUS
AfdRestartJoin (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
VOID
AfdJoinInviteSetup (
PAFD_ENDPOINT RootEndpoint,
PAFD_ENDPOINT LeafEndpoint
);
VOID
AfdConnectApcKernelRoutine (
IN struct _KAPC *Apc,
IN OUT PKNORMAL_ROUTINE *NormalRoutine,
IN OUT PVOID *NormalContext,
IN OUT PVOID *SystemArgument1,
IN OUT PVOID *SystemArgument2
);
VOID
AfdConnectApcRundownRoutine (
IN struct _KAPC *Apc
);
VOID
AfdFinishConnect (
PAFD_ENDPOINT Endpoint,
PIRP Irp,
PAFD_ENDPOINT RootEndpoint
);
NTSTATUS
AfdRestartSuperConnect (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, AfdConnect )
#pragma alloc_text( PAGEAFD, AfdDoDatagramConnect )
#pragma alloc_text( PAGEAFD, AfdRestartConnect )
#pragma alloc_text( PAGEAFD, AfdRestartDgConnect )
#pragma alloc_text( PAGEAFD, AfdSetupConnectDataBuffers )
#pragma alloc_text( PAGEAFD, AfdEnableFailedConnectEvent )
#pragma alloc_text( PAGE, AfdJoinLeaf )
#pragma alloc_text( PAGEAFD, AfdRestartJoin )
#pragma alloc_text( PAGEAFD, AfdJoinInviteSetup )
#pragma alloc_text( PAGE, AfdConnectApcKernelRoutine )
#pragma alloc_text( PAGE, AfdConnectApcRundownRoutine )
#pragma alloc_text( PAGEAFD, AfdFinishConnect )
#pragma alloc_text( PAGE, AfdSuperConnect )
#pragma alloc_text( PAGEAFD, AfdRestartSuperConnect )
#endif
typedef struct _AFD_CONNECT_CONTEXT {
TDI_CONNECTION_INFORMATION RequestConnectionInfo;
TDI_CONNECTION_INFORMATION ReturnConnectionInfo;
TRANSPORT_ADDRESS RemoteAddress;
} AFD_CONNECT_CONTEXT, *PAFD_CONNECT_CONTEXT;
C_ASSERT ( (FIELD_OFFSET (AFD_CONNECTION, SListEntry) % MEMORY_ALLOCATION_ALIGNMENT) == 0 );
NTSTATUS
FASTCALL
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;
PAFD_CONNECT_CONTEXT context;
HANDLE connectEndpointHandle;
PFILE_OBJECT fileObject;
PTRANSPORT_ADDRESS remoteAddress;
ULONG remoteAddressLength;
BOOLEAN sanActive;
PTDI_CONNECTION_INFORMATION requestConnectionInfo, returnConnectionInfo;
PAGED_CODE( );
//
// Initialize for proper cleanup
//
fileObject = NULL;
context = NULL;
#ifdef _WIN64
if (IoIs32bitProcess (Irp)) {
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength<
(ULONG)FIELD_OFFSET(AFD_CONNECT_JOIN_INFO32, RemoteAddress.Address[0].Address) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength!=0
&& IrpSp->Parameters.DeviceIoControl.OutputBufferLength<
sizeof (IO_STATUS_BLOCK32))){
status = STATUS_INVALID_PARAMETER;
goto complete;
}
AFD_W4_INIT status = STATUS_SUCCESS;
try {
if( Irp->RequestorMode != KernelMode ) {
ProbeForRead(
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
PROBE_ALIGNMENT32 (AFD_CONNECT_JOIN_INFO32)
);
}
sanActive = ((PAFD_CONNECT_JOIN_INFO32)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer)->SanActive;
connectEndpointHandle =
((PAFD_CONNECT_JOIN_INFO32)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer)->ConnectEndpoint;
remoteAddress = (PTRANSPORT_ADDRESS)
&((PAFD_CONNECT_JOIN_INFO32)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer)->RemoteAddress;
ASSERT (((ULONG_PTR)remoteAddress & (PROBE_ALIGNMENT(TRANSPORT_ADDRESS)-1))==0);
remoteAddressLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength
- FIELD_OFFSET (AFD_CONNECT_JOIN_INFO32, RemoteAddress);
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
goto complete;
}
}
else
#endif //_WIN64
{
//
// 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.
//
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength<
(ULONG)FIELD_OFFSET(AFD_CONNECT_JOIN_INFO, RemoteAddress.Address[0].Address) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength!=0 &&
IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof (IO_STATUS_BLOCK))) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
AFD_W4_INIT status = STATUS_SUCCESS;
try {
PAFD_CONNECT_JOIN_INFO connectInfo;
if( Irp->RequestorMode != KernelMode ) {
ProbeForRead(
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
PROBE_ALIGNMENT (AFD_CONNECT_JOIN_INFO)
);
}
connectInfo = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
sanActive = connectInfo->SanActive;
connectEndpointHandle = connectInfo->ConnectEndpoint;
remoteAddress = &connectInfo->RemoteAddress;
remoteAddressLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength
- FIELD_OFFSET (AFD_CONNECT_JOIN_INFO, RemoteAddress);
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
goto complete;
}
}
//
// Do sanity check on remoteAddressLength to prevent addition overflow below
//
if ((LONG)remoteAddressLength < 0) {
//
// address length is unreasonably large
//
status = STATUS_INVALID_PARAMETER;
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 connect\n",
PsGetCurrentProcessId ()));
status = STATUS_INVALID_PARAMETER_12;
goto complete;
}
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
try {
context = AFD_ALLOCATE_POOL_WITH_QUOTA (NonPagedPool,
FIELD_OFFSET (AFD_CONNECT_CONTEXT, RemoteAddress)
+ remoteAddressLength,
AFD_TDI_POOL_TAG
);
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets POOL_RAISE_IF_ALLOCATION_FAILURE flag
ASSERT (context!=NULL);
Irp->AssociatedIrp.SystemBuffer = context;
RtlZeroMemory (context,
FIELD_OFFSET (AFD_CONNECT_CONTEXT, RemoteAddress));
RtlCopyMemory (&context->RemoteAddress,
remoteAddress,
remoteAddressLength);
//
// Validate internal consistency of the transport address structure.
// Note that we HAVE to do this after copying since the malicious
// application can change the content of the buffer on us any time
// and our check will be bypassed.
//
if ((context->RemoteAddress.TAAddressCount!=1) ||
(LONG)remoteAddressLength<
FIELD_OFFSET (TRANSPORT_ADDRESS,
Address[0].Address[context->RemoteAddress.Address[0].AddressLength])) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
context->RequestConnectionInfo.RemoteAddress = &context->RemoteAddress;
context->RequestConnectionInfo.RemoteAddressLength = remoteAddressLength;
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength>0 &&
Irp->RequestorMode==UserMode) {
ProbeForWriteIoStatusEx (
((PIO_STATUS_BLOCK)Irp->UserBuffer),
IoIs32bitProcess (Irp));
}
}
except (AFD_EXCEPTION_FILTER(status)) {
ASSERT (NT_ERROR (status));
goto complete;
}
fileObject = IrpSp->FileObject;
endpoint = fileObject->FsContext;
if (endpoint->Type==AfdBlockTypeHelper) {
//
// This is async connect which uses helper endpoint to
// communicate to AFD. Get the real endpoint.
//
status = ObReferenceObjectByHandle(
connectEndpointHandle,
(IrpSp->Parameters.DeviceIoControl.IoControlCode>>14) & 3,
// DesiredAccess
*IoFileObjectType, // ObjectType
Irp->RequestorMode,
(PVOID *)&fileObject,
NULL
);
if (!NT_SUCCESS (status)) {
goto complete;
}
if (fileObject->DeviceObject!=AfdDeviceObject) {
status = STATUS_INVALID_HANDLE;
goto complete_deref;
}
endpoint = fileObject->FsContext;
IrpSp->FileObject = fileObject;
}
else {
ObReferenceObject (fileObject);
}
IF_DEBUG(CONNECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdConnect: starting connect on endpoint %p\n",
endpoint ));
}
//
// 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) ) {
return AfdDoDatagramConnect( fileObject, Irp, FALSE );
}
if (!AFD_START_STATE_CHANGE (endpoint, AfdEndpointStateConnected)) {
status = STATUS_INVALID_PARAMETER;
goto complete_deref;
}
if ( endpoint->Type != AfdBlockTypeEndpoint &&
endpoint->Type != AfdBlockTypeVcConnecting ) {
status = STATUS_INVALID_PARAMETER;
goto complete_state_change;
}
//
// If the endpoint is not bound, then this is an invalid request.
// Listening endpoints are not allowed as well.
//
if ( endpoint->Listening ||
endpoint->State != AfdEndpointStateBound ) {
status = STATUS_INVALID_PARAMETER;
goto complete_state_change;
}
//
// Create a connection object to use for the connect operation.
//
status = AfdCreateConnection(
endpoint->TransportInfo,
endpoint->AddressHandle,
IS_TDI_BUFFERRING(endpoint),
endpoint->InLine,
endpoint->OwningProcess,
&connection
);
if ( !NT_SUCCESS(status) ) {
goto complete_state_change;
}
//
// 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->Common.VcConnecting.Connection = connection;
endpoint->Type = AfdBlockTypeVcConnecting;
ASSERT( IS_TDI_BUFFERRING(endpoint) == 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 );
//
// 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.
//
requestConnectionInfo = &context->RequestConnectionInfo;
returnConnectionInfo = &context->ReturnConnectionInfo;
if ( endpoint->Common.VirtualCircuit.ConnectDataBuffers != NULL ) {
AfdSetupConnectDataBuffers(
endpoint,
connection,
&requestConnectionInfo,
&returnConnectionInfo
);
}
//
// Since we may be reissuing a connect after a previous failed connect,
// reenable the failed connect event bit.
//
AfdEnableFailedConnectEvent( endpoint );
//
// Reference the connection block so it does not go away even if
// endpoint's reference to it is removed (in cleanup)
//
REFERENCE_CONNECTION (connection);
//
// Build a TDI kernel-mode connect request in the next stack location
// of the IRP.
//
TdiBuildConnect(
Irp,
connection->DeviceObject,
connection->FileObject,
AfdRestartConnect,
connection,
&AfdInfiniteTimeout,
requestConnectionInfo,
returnConnectionInfo
);
AFD_VERIFY_ADDRESS (connection, &requestConnectionInfo->RemoteAddress);
//
// Call the transport to actually perform the connect operation.
//
return AfdIoCallDriver( endpoint, connection->DeviceObject, Irp );
complete_state_change:
AFD_END_STATE_CHANGE (endpoint);
complete_deref:
ASSERT (fileObject!=NULL);
ObDereferenceObject (fileObject);
complete:
if (context!=NULL) {
AFD_FREE_POOL (context, AFD_TDI_POOL_TAG);
ASSERT (Irp->AssociatedIrp.SystemBuffer==context);
Irp->AssociatedIrp.SystemBuffer = NULL;
}
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, AfdPriorityBoost );
return status;
} // AfdConnect
NTSTATUS
AfdDoDatagramConnect (
IN PFILE_OBJECT FileObject,
IN PIRP Irp,
IN BOOLEAN HalfConnect
)
{
PAFD_ENDPOINT endpoint;
AFD_LOCK_QUEUE_HANDLE lockHandle;
NTSTATUS status;
PAFD_CONNECT_CONTEXT context;
endpoint = FileObject->FsContext;
context = Irp->AssociatedIrp.SystemBuffer;
if (!AFD_START_STATE_CHANGE (endpoint, AfdEndpointStateConnected)) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
if (endpoint->State!=AfdEndpointStateBound &&
endpoint->State!=AfdEndpointStateConnected) {
status = STATUS_INVALID_PARAMETER;
goto complete_state_change;
}
//
// 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.
//
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
if ((endpoint->Common.Datagram.RemoteAddress==NULL) ||
(endpoint->Common.Datagram.RemoteAddressLength<
(ULONG)context->RequestConnectionInfo.RemoteAddressLength)) {
PTRANSPORT_ADDRESS remoteAddress;
remoteAddress =
AFD_ALLOCATE_REMOTE_ADDRESS (
context->RequestConnectionInfo.RemoteAddressLength);
if (remoteAddress == NULL) {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
status = STATUS_INSUFFICIENT_RESOURCES;
goto complete_state_change;
}
if ( endpoint->Common.Datagram.RemoteAddress != NULL ) {
AFD_RETURN_REMOTE_ADDRESS (
endpoint->Common.Datagram.RemoteAddress,
endpoint->Common.Datagram.RemoteAddressLength
);
}
endpoint->Common.Datagram.RemoteAddress = remoteAddress;
}
RtlCopyMemory(
endpoint->Common.Datagram.RemoteAddress,
context->RequestConnectionInfo.RemoteAddress,
context->RequestConnectionInfo.RemoteAddressLength
);
endpoint->Common.Datagram.RemoteAddressLength =
context->RequestConnectionInfo.RemoteAddressLength;
endpoint->DisconnectMode = 0;
endpoint->Common.Datagram.HalfConnect = HalfConnect;
if (!IS_TDI_DGRAM_CONNECTION(endpoint)) {
endpoint->State = AfdEndpointStateConnected;
//
// Indicate that the connect completed. Implicitly, the
// successful completion of a connect also means that the caller
// can do a send on the socket.
//
endpoint->EnableSendEvent = TRUE;
AfdIndicateEventSelectEvent(
endpoint,
AFD_POLL_CONNECT | AFD_POLL_SEND,
STATUS_SUCCESS
);
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
AfdIndicatePollEvent(
endpoint,
AFD_POLL_CONNECT | AFD_POLL_SEND,
STATUS_SUCCESS
);
status = STATUS_SUCCESS;
}
else {
//
// Reset the connect status to success so that the poll code will
// know if a connect failure occurs.
// Do this inline as we already hold spinlock
//
endpoint->EventsActive &= ~AFD_POLL_CONNECT_FAIL;
endpoint->EventStatus[AFD_POLL_CONNECT_FAIL_BIT] = STATUS_SUCCESS;
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
//
// Build a TDI kernel-mode connect request in the next stack location
// of the IRP.
//
TdiBuildConnect(
Irp,
endpoint->AddressDeviceObject,
endpoint->AddressFileObject,
AfdRestartDgConnect,
endpoint,
&AfdInfiniteTimeout,
&context->RequestConnectionInfo,
&context->ReturnConnectionInfo
);
//
// Call the transport to actually perform the connect operation.
//
return AfdIoCallDriver( endpoint, endpoint->AddressDeviceObject, Irp );
}
complete_state_change:
AFD_END_STATE_CHANGE (endpoint);
complete:
ObDereferenceObject (FileObject);
AFD_FREE_POOL (context, AFD_TDI_POOL_TAG);
ASSERT (Irp->AssociatedIrp.SystemBuffer==context);
Irp->AssociatedIrp.SystemBuffer = NULL;
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 OUT PTDI_CONNECTION_INFORMATION *RequestConnectionInformation,
IN OUT PTDI_CONNECTION_INFORMATION *ReturnConnectionInformation
)
{
AFD_LOCK_QUEUE_HANDLE lockHandle;
ASSERT (Endpoint->Type!=AfdBlockTypeDatagram);
AfdAcquireSpinLock( &Endpoint->SpinLock, &lockHandle );
if ( Endpoint->Common.VirtualCircuit.ConnectDataBuffers != NULL ) {
PTDI_CONNECTION_INFORMATION requestConnectionInformation,
returnConnectionInformation;
ASSERT( Connection->ConnectDataBuffers == NULL );
Connection->ConnectDataBuffers = Endpoint->Common.VirtualCircuit.ConnectDataBuffers;
Endpoint->Common.VirtualCircuit.ConnectDataBuffers = NULL;
requestConnectionInformation = &Connection->ConnectDataBuffers->RequestConnectionInfo,
requestConnectionInformation->UserData =
Connection->ConnectDataBuffers->SendConnectData.Buffer;
requestConnectionInformation->UserDataLength =
Connection->ConnectDataBuffers->SendConnectData.BufferLength;
requestConnectionInformation->Options =
Connection->ConnectDataBuffers->SendConnectOptions.Buffer;
requestConnectionInformation->OptionsLength =
Connection->ConnectDataBuffers->SendConnectOptions.BufferLength;
requestConnectionInformation->RemoteAddress =
(*RequestConnectionInformation)->RemoteAddress;
requestConnectionInformation->RemoteAddressLength =
(*RequestConnectionInformation)->RemoteAddressLength;
*RequestConnectionInformation = requestConnectionInformation;
returnConnectionInformation = &Connection->ConnectDataBuffers->ReturnConnectionInfo;
returnConnectionInformation->UserData =
Connection->ConnectDataBuffers->ReceiveConnectData.Buffer;
returnConnectionInformation->UserDataLength =
Connection->ConnectDataBuffers->ReceiveConnectData.BufferLength;
returnConnectionInformation->Options =
Connection->ConnectDataBuffers->ReceiveConnectOptions.Buffer;
returnConnectionInformation->OptionsLength =
Connection->ConnectDataBuffers->ReceiveConnectOptions.BufferLength;
returnConnectionInformation->RemoteAddress =
(*ReturnConnectionInformation)->RemoteAddress;
returnConnectionInformation->RemoteAddressLength =
(*ReturnConnectionInformation)->RemoteAddressLength;
*ReturnConnectionInformation = returnConnectionInformation;
}
AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle );
} // 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;
AFD_LOCK_QUEUE_HANDLE lockHandle;
PIO_STACK_LOCATION irpSp;
PFILE_OBJECT fileObject;
PAFD_CONNECT_CONTEXT context;
UNREFERENCED_PARAMETER (DeviceObject);
connection = Context;
ASSERT( connection->Type == AfdBlockTypeConnection );
irpSp = IoGetCurrentIrpStackLocation( Irp );
fileObject = irpSp->FileObject;
ASSERT( fileObject->DeviceObject==AfdDeviceObject );
endpoint = fileObject->FsContext;
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting );
ASSERT( endpoint==connection->Endpoint );
context = Irp->AssociatedIrp.SystemBuffer;
ASSERT( context != NULL );
IF_DEBUG(CONNECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdRestartConnect: connect completed, status = %X, endpoint = %p\n",
Irp->IoStatus.Status, endpoint ));
}
if ( connection->ConnectDataBuffers != NULL ) {
//
// If there are connect buffers on this endpoint, remember the
// size of the return connect data.
//
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
//
// Double-check under the lock
//
if ( connection->ConnectDataBuffers != NULL ) {
NTSTATUS status;
status = AfdSaveReceivedConnectData(
&connection->ConnectDataBuffers,
IOCTL_AFD_SET_CONNECT_DATA,
connection->ConnectDataBuffers->ReturnConnectionInfo.UserData,
connection->ConnectDataBuffers->ReturnConnectionInfo.UserDataLength
);
ASSERT (NT_SUCCESS (status));
status = AfdSaveReceivedConnectData(
&connection->ConnectDataBuffers,
IOCTL_AFD_SET_CONNECT_OPTIONS,
connection->ConnectDataBuffers->ReturnConnectionInfo.Options,
connection->ConnectDataBuffers->ReturnConnectionInfo.OptionsLength
);
ASSERT (NT_SUCCESS (status));
}
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
}
//
// 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)) {
//
// 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.
//
connection->ConnectTime = KeQueryInterruptTime();
} else {
//
// The connect failed, so reset the type to open.
// Otherwise, we won't be able to start another connect
//
endpoint->Type = AfdBlockTypeEndpoint;
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
if (endpoint->Common.VcConnecting.Connection!=NULL) {
ASSERT (connection==endpoint->Common.VcConnecting.Connection);
endpoint->Common.VcConnecting.Connection = NULL;
//
// 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.
//
if ( connection->ConnectedReferenceAdded ) {
connection->ConnectedReferenceAdded = FALSE;
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
DEREFERENCE_CONNECTION( connection );
} else {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
}
//
// 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 );
}
else {
//
// The endpoint's reference to connection was removed
// (perhaps in cleanup);
//
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
}
}
AFD_FREE_POOL (context, AFD_TDI_POOL_TAG);
Irp->AssociatedIrp.SystemBuffer = NULL;
//
// If pending has be returned for this irp then mark the current
// stack as pending.
//
if ( Irp->PendingReturned ) {
IoMarkIrpPending(Irp);
}
AfdCompleteOutstandingIrp( endpoint, Irp );
//
// Dereference connection to account for reference we added in AfdConnect
//
DEREFERENCE_CONNECTION( connection );
//
// Try to queue kernel APC to the user thread that
// started the connection operation, so we can
// communicate the status of the connect operation to
// msafd.dll before we inform the application through
// the select or EventSelect. Otherwise, we run into the
// race condition when application learns about connect first,
// calls msafd.dll that is not aware of the completion and
// returns WSAENOTCONN.
//
if ((Irp->RequestorMode==UserMode) && // Must be user mode calls
(Irp->UserBuffer!=NULL) && // Must be interested in status
// Thread should be able to
// run APCs.
(KeInitializeApc (&endpoint->Common.VcConnecting.Apc,
PsGetThreadTcb (Irp->Tail.Overlay.Thread),
Irp->ApcEnvironment,
AfdConnectApcKernelRoutine,
AfdConnectApcRundownRoutine,
(PKNORMAL_ROUTINE)NULL,
KernelMode,
NULL
),
KeInsertQueueApc (&endpoint->Common.VcConnecting.Apc,
Irp,
NULL,
AfdPriorityBoost))) {
//
// We will complete the IRP in the APC.
//
return STATUS_MORE_PROCESSING_REQUIRED;
}
else {
//
// APC was not necessary or did not work.
// Complete it here.
//
AfdFinishConnect (endpoint, Irp, NULL);
return STATUS_SUCCESS;
}
} // AfdRestartConnect
VOID
AfdConnectApcKernelRoutine (
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, rootEndpoint;
UNREFERENCED_PARAMETER (NormalContext);
#if DBG
try {
ASSERT (*NormalRoutine==NULL);
#else
UNREFERENCED_PARAMETER (NormalRoutine);
#endif
//
// Validate parameters.
//
endpoint = CONTAINING_RECORD (Apc, AFD_ENDPOINT, Common.VcConnecting.Apc);
ASSERT (IS_AFD_ENDPOINT_TYPE (endpoint));
irp = *SystemArgument1;
ASSERT (irp->UserBuffer!=NULL && irp->RequestorMode==UserMode);
irpSp = IoGetCurrentIrpStackLocation( irp );
rootEndpoint = *SystemArgument2;
ASSERT (rootEndpoint==NULL || IS_AFD_ENDPOINT_TYPE (endpoint));
//
// Update the status for the user mode caller before
// signalling events.
//
try {
#ifdef _WIN64
if (IoIs32bitProcess (irp)) {
((PIO_STATUS_BLOCK32)irp->UserBuffer)->Status = (LONG)irp->IoStatus.Status;
}
else
#endif //_WIN64
{
((PIO_STATUS_BLOCK)irp->UserBuffer)->Status = irp->IoStatus.Status;
}
}
except (AFD_EXCEPTION_FILTER_NO_STATUS()) {
NOTHING;
}
AfdFinishConnect (endpoint, irp, rootEndpoint);
IoCompleteRequest (irp, AfdPriorityBoost);
#if DBG
}
except (AfdApcExceptionFilter (GetExceptionInformation (),
__FILE__,
__LINE__)) {
DbgBreakPoint ();
}
#endif
}
VOID
AfdConnectApcRundownRoutine (
IN struct _KAPC *Apc
)
{
PIRP irp;
PAFD_ENDPOINT endpoint, rootEndpoint;
#if DBG
try {
#endif
endpoint = CONTAINING_RECORD (Apc, AFD_ENDPOINT, Common.VcConnecting.Apc);
ASSERT (IS_AFD_ENDPOINT_TYPE (endpoint));
irp = Apc->SystemArgument1;
rootEndpoint = Apc->SystemArgument2;
ASSERT (rootEndpoint==NULL || IS_AFD_ENDPOINT_TYPE (endpoint));
ASSERT (irp->UserBuffer!=NULL && irp->RequestorMode==UserMode);
//
// Thread is exiting, don't bother updating user mode status.
// Just signal the events and complet the IRP.
//
AfdFinishConnect (endpoint, irp, rootEndpoint);
IoCompleteRequest (irp, AfdPriorityBoost);
#if DBG
}
except (AfdApcExceptionFilter (GetExceptionInformation (),
__FILE__,
__LINE__)) {
DbgBreakPoint ();
}
#endif
}
VOID
AfdFinishConnect (
PAFD_ENDPOINT Endpoint,
PIRP Irp,
PAFD_ENDPOINT RootEndpoint
)
{
PIO_STACK_LOCATION irpSp;
PFILE_OBJECT fileObject;
AFD_LOCK_QUEUE_HANDLE lockHandle;
ULONG eventMask;
if (NT_SUCCESS(Irp->IoStatus.Status)) {
eventMask = AFD_POLL_CONNECT;
}
else {
eventMask = AFD_POLL_CONNECT_FAIL;
}
irpSp = IoGetCurrentIrpStackLocation( Irp );
fileObject = irpSp->FileObject;
if (RootEndpoint != NULL) {
AfdAcquireSpinLock(&RootEndpoint->SpinLock, &lockHandle);
//
// mswsock.dll will reissue join leaf call for non-blocking sockets
// in its async helper if the original requesting thread exits
// causing the connect IRP to cancel. We only want to signal events
// based upon the final resolution of the join leaf call (once only)!
// However if the endpoint is now cleaning up, a followup call to
// join leaf will fail without signalling the events.
//
if (RootEndpoint->EndpointCleanedUp ||
(Irp->IoStatus.Status != STATUS_CANCELLED) ||
!RootEndpoint->NonBlocking) {
AfdIndicateEventSelectEvent(RootEndpoint, eventMask, Irp->IoStatus.Status);
AfdReleaseSpinLock(&RootEndpoint->SpinLock, &lockHandle);
AfdIndicatePollEvent(RootEndpoint, eventMask, Irp->IoStatus.Status);
//
// Only indicate connection once to the root control plane.
//
eventMask = 0;
} else {
AfdReleaseSpinLock(&RootEndpoint->SpinLock, &lockHandle);
}
AFD_END_STATE_CHANGE(RootEndpoint);
if (!NT_SUCCESS(Irp->IoStatus.Status))
DEREFERENCE_ENDPOINT(RootEndpoint);
} // if (RootEndpoint != NULL)
AfdAcquireSpinLock(&Endpoint->SpinLock, &lockHandle);
if (NT_SUCCESS(Irp->IoStatus.Status)) {
eventMask |= AFD_POLL_SEND;
Endpoint->EnableSendEvent = TRUE;
if (Endpoint->Common.VcConnecting.Connection != NULL) {
Endpoint->Common.VcConnecting.Connection->State = AfdConnectionStateConnected;
if (IS_DATA_ON_CONNECTION(Endpoint->Common.VcConnecting.Connection)) {
eventMask |= AFD_POLL_RECEIVE;
}
}
} else {
//
// mswsock.dll will reissue connect call for non-blocking sockets
// in its async helper if the original requesting thread exits
// causing the connect IRP to cancel. We only want to signal events
// based upon the final resolution of the connect call (once only)!
// However if the endpoint is now cleaning up, a followup call to
// connect will fail without signalling the events.
//
if ((Irp->IoStatus.Status == STATUS_CANCELLED) &&
!Endpoint->EndpointCleanedUp &&
Endpoint->NonBlocking) {
eventMask = 0;
}
} // if (NT_SUCCESS(Irp->IoStatus.Status))
if (eventMask) {
AfdIndicateEventSelectEvent(Endpoint, eventMask, Irp->IoStatus.Status);
AfdReleaseSpinLock(&Endpoint->SpinLock, &lockHandle);
AfdIndicatePollEvent(Endpoint, eventMask, Irp->IoStatus.Status);
}
else {
AfdReleaseSpinLock(&Endpoint->SpinLock, &lockHandle);
}
AFD_END_STATE_CHANGE(Endpoint);
ObDereferenceObject(fileObject);
}
NTSTATUS
AfdRestartDgConnect (
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;
PIO_STACK_LOCATION irpSp;
PFILE_OBJECT fileObject;
AFD_LOCK_QUEUE_HANDLE lockHandle;
ULONG eventMask;
UNREFERENCED_PARAMETER (DeviceObject);
endpoint = Context;
ASSERT( IS_DGRAM_ENDPOINT(endpoint) );
irpSp = IoGetCurrentIrpStackLocation( Irp );
fileObject = irpSp->FileObject;
ASSERT (endpoint == fileObject->FsContext);
IF_DEBUG(CONNECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdRestartDgConnect: connect completed, status = %X, endpoint = %p\n",
Irp->IoStatus.Status, endpoint ));
}
//
// Indicate that the connect completed. Implicitly, the successful
// completion of a connect also means that the caller can do a send
// on the socket.
//
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
if ( NT_SUCCESS(Irp->IoStatus.Status) ) {
endpoint->State = AfdEndpointStateConnected;
endpoint->EnableSendEvent = TRUE;
eventMask = AFD_POLL_CONNECT | AFD_POLL_SEND;
} else {
eventMask = AFD_POLL_CONNECT_FAIL;
}
AfdIndicateEventSelectEvent (endpoint, eventMask, Irp->IoStatus.Status);
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
AfdIndicatePollEvent (endpoint, eventMask, Irp->IoStatus.Status);
AFD_END_STATE_CHANGE (endpoint);
ASSERT (Irp->AssociatedIrp.SystemBuffer!=NULL);
AFD_FREE_POOL (Irp->AssociatedIrp.SystemBuffer, AFD_TDI_POOL_TAG);
Irp->AssociatedIrp.SystemBuffer = NULL;
//
// If pending has be returned for this irp then mark the current
// stack as pending.
//
if ( Irp->PendingReturned ) {
IoMarkIrpPending(Irp);
}
AfdCompleteOutstandingIrp( endpoint, Irp );
//
// Remove reference added in AfdConnect
//
ObDereferenceObject (fileObject);
return STATUS_SUCCESS;
} // AfdRestartDgConnect
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.
--*/
{
AFD_LOCK_QUEUE_HANDLE lockHandle;
AfdAcquireSpinLock( &Endpoint->SpinLock, &lockHandle );
ASSERT( ( Endpoint->EventsActive & AFD_POLL_CONNECT ) == 0 );
Endpoint->EventsActive &= ~AFD_POLL_CONNECT_FAIL;
Endpoint->EventStatus[AFD_POLL_CONNECT_FAIL_BIT] = STATUS_SUCCESS;
IF_DEBUG(EVENT_SELECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdConnect: Endp %08lX, Active %08lX\n",
Endpoint,
Endpoint->EventsActive
));
}
AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle );
} // AfdEnableFailedConnectEvent
NTSTATUS
FASTCALL
AfdJoinLeaf (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Handles the IOCTL_AFD_JOIN_LEAF 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 leafEndpoint;
PAFD_CONNECTION connection;
PAFD_CONNECT_CONTEXT context;
HANDLE connectEndpointHandle;
HANDLE rootEndpointHandle;
PFILE_OBJECT fileObject;
PTRANSPORT_ADDRESS remoteAddress;
ULONG remoteAddressLength;
PTDI_CONNECTION_INFORMATION requestConnectionInfo, returnConnectionInfo;
PAGED_CODE( );
//
// Initialize for proper cleanup
//
fileObject = NULL;
connection = NULL;
context = NULL;
#ifdef _WIN64
if (IoIs32bitProcess (Irp)) {
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength<
(ULONG)FIELD_OFFSET(AFD_CONNECT_JOIN_INFO32, RemoteAddress.Address[0].Address) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength!=0 &&
IrpSp->Parameters.DeviceIoControl.OutputBufferLength<
sizeof (IO_STATUS_BLOCK32))){
status = STATUS_INVALID_PARAMETER;
goto complete;
}
AFD_W4_INIT status = STATUS_SUCCESS;
try {
if( Irp->RequestorMode != KernelMode ) {
ProbeForRead(
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
PROBE_ALIGNMENT32 (AFD_CONNECT_JOIN_INFO32)
);
}
connectEndpointHandle =
((PAFD_CONNECT_JOIN_INFO32)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer)->ConnectEndpoint;
rootEndpointHandle =
((PAFD_CONNECT_JOIN_INFO32)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer)->RootEndpoint;
remoteAddress = (PTRANSPORT_ADDRESS)&((PAFD_CONNECT_JOIN_INFO32)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer)->RemoteAddress;
ASSERT (((ULONG_PTR)remoteAddress & (PROBE_ALIGNMENT(TRANSPORT_ADDRESS)-1))==0);
remoteAddressLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength
- FIELD_OFFSET (AFD_CONNECT_JOIN_INFO32, RemoteAddress);
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
goto complete;
}
}
else
#endif //_WIN64
{
//
// 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.
//
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength<
(ULONG)FIELD_OFFSET (AFD_CONNECT_JOIN_INFO, RemoteAddress.Address[0].Address) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength!=0 &&
IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof (IO_STATUS_BLOCK))) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
AFD_W4_INIT status = STATUS_SUCCESS;
try {
if( Irp->RequestorMode != KernelMode ) {
ProbeForRead(
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
PROBE_ALIGNMENT (AFD_CONNECT_JOIN_INFO)
);
}
connectEndpointHandle =
((PAFD_CONNECT_JOIN_INFO)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer)->ConnectEndpoint;
rootEndpointHandle =
((PAFD_CONNECT_JOIN_INFO)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer)->RootEndpoint;
remoteAddress = &((PAFD_CONNECT_JOIN_INFO)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer)->RemoteAddress;
remoteAddressLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength
- FIELD_OFFSET (AFD_CONNECT_JOIN_INFO, RemoteAddress);
}
except (AFD_EXCEPTION_FILTER (status)) {
ASSERT (NT_ERROR (status));
goto complete;
}
}
//
// Do sanity check on remoteAddressLength to prevent addition overflow below
//
if ((LONG)remoteAddressLength < 0) {
//
// address length is unreasonably large
//
status = STATUS_INVALID_PARAMETER;
goto complete;
}
AFD_W4_INIT ASSERT (status == STATUS_SUCCESS);
try {
context = AFD_ALLOCATE_POOL_WITH_QUOTA (NonPagedPool,
FIELD_OFFSET (AFD_CONNECT_CONTEXT, RemoteAddress)
+ remoteAddressLength,
AFD_TDI_POOL_TAG
);
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets POOL_RAISE_IF_ALLOCATION_FAILURE flag
ASSERT (context!=NULL);
Irp->AssociatedIrp.SystemBuffer = context;
RtlZeroMemory (context,
FIELD_OFFSET (AFD_CONNECT_CONTEXT, RemoteAddress));
RtlCopyMemory (&context->RemoteAddress,
remoteAddress,
remoteAddressLength);
//
// Validate internal consistency of the transport address structure.
// Note that we HAVE to do this after copying since the malicious
// application can change the content of the buffer on us any time
// and our check will be bypassed.
//
if ((context->RemoteAddress.TAAddressCount!=1) ||
(LONG)remoteAddressLength<
FIELD_OFFSET (TRANSPORT_ADDRESS,
Address[0].Address[context->RemoteAddress.Address[0].AddressLength])) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
context->RequestConnectionInfo.RemoteAddress = &context->RemoteAddress;
context->RequestConnectionInfo.RemoteAddressLength = remoteAddressLength;
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength>0 &&
Irp->RequestorMode==UserMode) {
ProbeForWriteIoStatusEx (
((PIO_STATUS_BLOCK)Irp->UserBuffer),
IoIs32bitProcess (Irp));
}
}
except (AFD_EXCEPTION_FILTER(status)) {
ASSERT (NT_ERROR (status));
goto complete;
}
fileObject = IrpSp->FileObject;
leafEndpoint = fileObject->FsContext;
if (leafEndpoint->Type==AfdBlockTypeHelper) {
//
// This is async join leaf which uses helper endpoint to
// communicate to AFD. Get the real endpoint.
//
status = ObReferenceObjectByHandle(
connectEndpointHandle,
(IrpSp->Parameters.DeviceIoControl.IoControlCode>>14) & 3,
// DesiredAccess
*IoFileObjectType, // ObjectType
Irp->RequestorMode,
(PVOID *)&fileObject,
NULL
);
if (!NT_SUCCESS (status)) {
goto complete;
}
if (fileObject->DeviceObject!=AfdDeviceObject) {
status = STATUS_INVALID_HANDLE;
goto complete_deref;
}
leafEndpoint = fileObject->FsContext;
IrpSp->FileObject = fileObject;
}
else
ObReferenceObject (fileObject);
if (rootEndpointHandle!=NULL) {
//
// Root inviting leaf
//
PFILE_OBJECT rootObject;
PAFD_ENDPOINT rootEndpoint;
status = ObReferenceObjectByHandle(
rootEndpointHandle,
(IrpSp->Parameters.DeviceIoControl.IoControlCode>>14) & 3,
// DesiredAccess
*IoFileObjectType, // ObjectType
Irp->RequestorMode,
(PVOID *)&rootObject,
NULL
);
if (!NT_SUCCESS (status)) {
goto complete_deref;
}
if (rootObject->DeviceObject!=AfdDeviceObject) {
ObDereferenceObject (rootObject);
status = STATUS_INVALID_HANDLE;
goto complete_deref;
}
//
// Get the endpoint structure of the file object
//
rootEndpoint = rootObject->FsContext;
if (!AFD_START_STATE_CHANGE (leafEndpoint, AfdEndpointStateConnected)) {
ObDereferenceObject (rootObject);
status = STATUS_INVALID_PARAMETER;
goto complete_deref;
}
//
// Verify root and leaf endpoint's type and states
//
if (IS_VC_ENDPOINT(rootEndpoint) &&
rootEndpoint->afdC_Root &&
rootEndpoint->State==AfdEndpointStateConnected &&
(leafEndpoint->Type == AfdBlockTypeEndpoint ||
leafEndpoint->Type == AfdBlockTypeVcConnecting) &&
leafEndpoint->TransportInfo==rootEndpoint->TransportInfo &&
leafEndpoint->State==AfdEndpointStateOpen) {
//
// Create a connection object to use for the connect operation.
//
status = AfdCreateConnection(
rootEndpoint->TransportInfo,
rootEndpoint->AddressHandle,
IS_TDI_BUFFERRING(rootEndpoint),
leafEndpoint->InLine,
leafEndpoint->OwningProcess,
&connection
);
//
// No more joins are allowed while this one is active
//
if (AFD_START_STATE_CHANGE (rootEndpoint, rootEndpoint->State)) {
AfdJoinInviteSetup (rootEndpoint, leafEndpoint);
}
else {
status = STATUS_INVALID_PARAMETER;
}
}
else {
status = STATUS_INVALID_PARAMETER;
}
//
// We referenced root endpoint in invite routine, so
// we no longer need reference to root file object
//
ObDereferenceObject (rootObject);
if (!NT_SUCCESS (status)) {
goto complete_state_change;
}
}
else {
//
// If this is a datagram endpoint, simply remember the specified
// address so that we can use it on sends, and writes.
//
if ( IS_DGRAM_ENDPOINT(leafEndpoint) ) {
if (leafEndpoint->State!=AfdEndpointStateConnected) {
return AfdDoDatagramConnect( fileObject, Irp, TRUE);
}
else {
//
// If endpoint is already connected, that connection takes
// precedence
//
status = STATUS_SUCCESS;
goto complete_deref;
}
}
else {
if (!AFD_START_STATE_CHANGE (leafEndpoint, AfdEndpointStateConnected)) {
status = STATUS_INVALID_PARAMETER;
goto complete_deref;
}
if ((leafEndpoint->Type != AfdBlockTypeEndpoint &&
leafEndpoint->Type != AfdBlockTypeVcConnecting) ||
leafEndpoint->State != AfdEndpointStateBound) {
status = STATUS_INVALID_PARAMETER;
goto complete_state_change;
}
//
// Create a connection object to use for the connect operation.
//
status = AfdCreateConnection(
leafEndpoint->TransportInfo,
leafEndpoint->AddressHandle,
IS_TDI_BUFFERRING(leafEndpoint),
leafEndpoint->InLine,
leafEndpoint->OwningProcess,
&connection
);
if ( !NT_SUCCESS(status) ) {
goto complete_state_change;
}
}
}
IF_DEBUG(CONNECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdJoinLeaf: starting join for endpoint %p\n",
leafEndpoint ));
}
//
// 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( leafEndpoint );
connection->Endpoint = leafEndpoint;
//
// 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.
//
leafEndpoint->Common.VcConnecting.Connection = connection;
leafEndpoint->Type = AfdBlockTypeVcConnecting;
ASSERT( IS_TDI_BUFFERRING(leafEndpoint) == 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 );
//
// 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.
//
requestConnectionInfo = &context->RequestConnectionInfo;
returnConnectionInfo = &context->ReturnConnectionInfo;
if ( leafEndpoint->Common.VirtualCircuit.ConnectDataBuffers != NULL ) {
AfdSetupConnectDataBuffers(
leafEndpoint,
connection,
&requestConnectionInfo,
&returnConnectionInfo
);
}
//
// Since we may be reissuing a connect after a previous failed connect,
// reenable the failed connect event bit.
//
AfdEnableFailedConnectEvent( leafEndpoint );
REFERENCE_CONNECTION (connection);
//
// Build a TDI kernel-mode connect request in the next stack location
// of the IRP.
//
TdiBuildConnect(
Irp,
connection->DeviceObject,
connection->FileObject,
AfdRestartJoin,
connection,
&AfdInfiniteTimeout,
requestConnectionInfo,
returnConnectionInfo
);
AFD_VERIFY_ADDRESS (connection, &context->ReturnConnectionInfo->RemoteAddress);
//
// Call the transport to actually perform the connect operation.
//
return AfdIoCallDriver( leafEndpoint, connection->DeviceObject, Irp );
complete_state_change:
AFD_END_STATE_CHANGE (leafEndpoint);
complete_deref:
ObDereferenceObject (fileObject);
complete:
if (context!=NULL) {
AFD_FREE_POOL (context, AFD_TDI_POOL_TAG);
ASSERT (Irp->AssociatedIrp.SystemBuffer==context);
Irp->AssociatedIrp.SystemBuffer = NULL;
}
if (connection!=NULL) {
DEREFERENCE_CONNECTION (connection);
}
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, AfdPriorityBoost );
return status;
} // AfdJoinLeaf
VOID
AfdJoinInviteSetup (
PAFD_ENDPOINT RootEndpoint,
PAFD_ENDPOINT LeafEndpoint
)
{
NTSTATUS status;
AFD_LOCK_QUEUE_HANDLE lockHandle;
RootEndpoint->EventsActive &= ~AFD_POLL_CONNECT;
AfdAcquireSpinLock (&LeafEndpoint->SpinLock, &lockHandle);
LeafEndpoint->TdiServiceFlags = RootEndpoint->TdiServiceFlags;
//
// Set up a referenced pointer to the root endpoint. This is
// necessary so that the endpoint does not go away until all
// leaf endpoints have gone away. Without this, we can free
// several shared strucutures that are associated with root
// endpoint and then attempt to use them in leaf endpoints.
//
REFERENCE_ENDPOINT (RootEndpoint);
LeafEndpoint->Common.VcConnecting.ListenEndpoint = RootEndpoint;
//
// Set up a referenced pointer in the accepted endpoint to the
// TDI address object.
//
ObReferenceObject( RootEndpoint->AddressFileObject );
AfdRecordAddrRef();
LeafEndpoint->AddressFileObject = RootEndpoint->AddressFileObject;
LeafEndpoint->AddressDeviceObject = RootEndpoint->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.
//
LeafEndpoint->LocalAddress = RootEndpoint->LocalAddress;
LeafEndpoint->LocalAddressLength = RootEndpoint->LocalAddressLength;
status = STATUS_SUCCESS;
AfdReleaseSpinLock (&LeafEndpoint->SpinLock, &lockHandle);
} // AfdJoinInviteSetup
NTSTATUS
AfdRestartJoin (
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, rootEndpoint;
PAFD_CONNECTION connection;
AFD_LOCK_QUEUE_HANDLE lockHandle;
PIO_STACK_LOCATION irpSp;
PFILE_OBJECT fileObject;
PAFD_CONNECT_CONTEXT context;
UNREFERENCED_PARAMETER (DeviceObject);
connection = Context;
ASSERT( connection->Type == AfdBlockTypeConnection );
irpSp = IoGetCurrentIrpStackLocation( Irp );
fileObject = irpSp->FileObject;
ASSERT( fileObject->DeviceObject == AfdDeviceObject );
endpoint = fileObject->FsContext;
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting );
context = Irp->AssociatedIrp.SystemBuffer;
ASSERT( context != NULL );
IF_DEBUG(CONNECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdRestartJoin: join completed, status = %X, "
"LeafEndpoint = %p, RootEndpoint = %p\n",
Irp->IoStatus.Status, endpoint,
endpoint->Common.VcConnecting.ListenEndpoint ));
}
//
// If this endpoint has root associated with it,
// we need to update it as well.
//
rootEndpoint = endpoint->Common.VcConnecting.ListenEndpoint;
ASSERT ( rootEndpoint==NULL ||
(rootEndpoint->afdC_Root &&
(rootEndpoint->Type == AfdBlockTypeVcConnecting ||
rootEndpoint->Type == AfdBlockTypeVcBoth) ) );
//
// If there are connect buffers on this endpoint, remember the
// size of the return connect data.
//
if ( connection->ConnectDataBuffers != NULL ) {
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
//
// Double-check under the lock
//
if ( connection->ConnectDataBuffers != NULL ) {
NTSTATUS status;
status = AfdSaveReceivedConnectData(
&connection->ConnectDataBuffers,
IOCTL_AFD_SET_CONNECT_DATA,
connection->ConnectDataBuffers->ReturnConnectionInfo.UserData,
connection->ConnectDataBuffers->ReturnConnectionInfo.UserDataLength
);
ASSERT (NT_SUCCESS (status));
status = AfdSaveReceivedConnectData(
&connection->ConnectDataBuffers,
IOCTL_AFD_SET_CONNECT_OPTIONS,
connection->ConnectDataBuffers->ReturnConnectionInfo.Options,
connection->ConnectDataBuffers->ReturnConnectionInfo.OptionsLength
);
ASSERT (NT_SUCCESS (status));
}
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
}
//
// 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) ) {
//
// 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.
//
connection->ConnectTime = KeQueryInterruptTime();
} else {
//
// The connect failed, so reset the type to open.
// If we don't reset, we won't be able to start
// another join
//
endpoint->Type = AfdBlockTypeEndpoint;
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
//
// Remove references to listening endpoint and connection
// Actual dereferncing is below after we release the spinlock
if (rootEndpoint!=NULL) {
endpoint->Common.VcConnecting.ListenEndpoint = NULL;
//
// We used the local address from the listening endpoint,
// simply reset it, it will be freed when listening endpoint
// is freed.
//
ASSERT (endpoint->LocalAddress==rootEndpoint->LocalAddress);
endpoint->LocalAddress = NULL;
endpoint->LocalAddressLength = 0;
}
if (endpoint->Common.VcConnecting.Connection != NULL) {
endpoint->Common.VcConnecting.Connection = NULL;
//
// 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.
//
if ( connection->ConnectedReferenceAdded ) {
connection->ConnectedReferenceAdded = FALSE;
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
DEREFERENCE_CONNECTION( connection );
} else {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
}
//
// 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 );
}
else {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
}
}
AFD_FREE_POOL (context, AFD_TDI_POOL_TAG);
Irp->AssociatedIrp.SystemBuffer = NULL;
//
// If pending has be returned for this irp then mark the current
// stack as pending.
//
if ( Irp->PendingReturned ) {
IoMarkIrpPending(Irp);
}
AfdCompleteOutstandingIrp( endpoint, Irp );
//
// Dereference connection to account for reference
// we added in AfdConnect
//
DEREFERENCE_CONNECTION( connection );
//
// Try to queue kernel APC to the user thread that
// started the connection operation, so we can
// communicate the status of the connect operation to
// msafd.dll before we inform the application through
// the select or EventSelect. Otherwise, we run into the
// race condition when application learns about connect first,
// calls msafd.dll that is not aware of the completion and
// returns WSAENOTCONN.
//
if ((Irp->RequestorMode==UserMode) && // Must be user mode calls
(Irp->UserBuffer!=NULL) && // Must be interested in status
// Thread should be able to
// run APCs.
(KeInitializeApc (&endpoint->Common.VcConnecting.Apc,
PsGetThreadTcb (Irp->Tail.Overlay.Thread),
Irp->ApcEnvironment,
AfdConnectApcKernelRoutine,
AfdConnectApcRundownRoutine,
(PKNORMAL_ROUTINE)NULL,
KernelMode,
NULL
),
KeInsertQueueApc (&endpoint->Common.VcConnecting.Apc,
Irp,
rootEndpoint,
AfdPriorityBoost))) {
//
// We will complete the IRP in the APC.
//
return STATUS_MORE_PROCESSING_REQUIRED;
}
else {
//
// APC was not necessary or did not work.
// Complete it here.
//
AfdFinishConnect (endpoint, Irp, rootEndpoint);
return STATUS_SUCCESS;
}
} // AfdRestartJoin
NTSTATUS
FASTCALL
AfdSuperConnect (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
/*++
Routine Description:
Handles the IOCTL_AFD_SUPER_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;
PAFD_BUFFER afdBuffer;
PAFD_SUPER_CONNECT_INFO connectInfo;
PTRANSPORT_ADDRESS remoteAddress;
PVOID context;
PTDI_CONNECTION_INFORMATION requestConnectionInfo, returnConnectionInfo;
PAGED_CODE( );
//
// Initialize for proper cleanup
//
afdBuffer = NULL;
endpoint = IrpSp->FileObject->FsContext;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength<
(ULONG)FIELD_OFFSET(AFD_SUPER_CONNECT_INFO, RemoteAddress.Address[0].Address)) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
AFD_W4_INIT status = STATUS_SUCCESS;
try {
if( Irp->RequestorMode != KernelMode ) {
ProbeForRead(
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
PROBE_ALIGNMENT (AFD_SUPER_CONNECT_INFO)
);
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength!=0) {
ProbeForRead (Irp->UserBuffer,
IrpSp->Parameters.DeviceIoControl.OutputBufferLength,
sizeof (UCHAR));
}
}
connectInfo = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
//
// Check for if the caller is unaware of the SAN
// provider activation and report the error.
//
if (!connectInfo->SanActive && AfdSanServiceHelper!=NULL) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
"AFD: Process %p is being told to enable SAN on connect\n",
PsGetCurrentProcessId ()));
status = STATUS_INVALID_PARAMETER_12;
goto complete;
}
afdBuffer = AfdGetBufferRaiseOnFailure (
endpoint,
IrpSp->Parameters.DeviceIoControl.OutputBufferLength,
IrpSp->Parameters.DeviceIoControl.InputBufferLength-
FIELD_OFFSET(AFD_SUPER_CONNECT_INFO, RemoteAddress),
endpoint->OwningProcess
);
remoteAddress = afdBuffer->TdiInfo.RemoteAddress;
afdBuffer->TdiInfo.RemoteAddressLength =
IrpSp->Parameters.DeviceIoControl.InputBufferLength-
FIELD_OFFSET(AFD_SUPER_CONNECT_INFO, RemoteAddress);
RtlCopyMemory (afdBuffer->TdiInfo.RemoteAddress,
&connectInfo->RemoteAddress,
afdBuffer->TdiInfo.RemoteAddressLength);
//
// Validate internal consistency of the transport address structure.
// Note that we HAVE to do this after copying since the malicious
// application can change the content of the buffer on us any time
// and our check will be bypassed.
//
if ((remoteAddress->TAAddressCount!=1) ||
(LONG)afdBuffer->TdiInfo.RemoteAddressLength<
FIELD_OFFSET (TRANSPORT_ADDRESS,
Address[0].Address[remoteAddress->Address[0].AddressLength])) {
ExRaiseStatus (STATUS_INVALID_PARAMETER);
}
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength>0) {
RtlCopyMemory (afdBuffer->Buffer,
Irp->UserBuffer,
IrpSp->Parameters.DeviceIoControl.OutputBufferLength
);
afdBuffer->DataLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
}
else {
afdBuffer->DataLength = 0;
}
}
except (AFD_EXCEPTION_FILTER(status)) {
ASSERT (NT_ERROR (status));
goto complete;
}
if (!AFD_START_STATE_CHANGE (endpoint, AfdEndpointStateConnected)) {
status = STATUS_INVALID_PARAMETER;
goto complete;
}
//
// If the endpoint is not bound, then this is an invalid request.
// Listening endpoints are not allowed as well.
// We do not support sending data with TDI buffering transports too.
//
if ( endpoint->Type != AfdBlockTypeEndpoint ||
endpoint->State != AfdEndpointStateBound ||
endpoint->Listening ||
(IS_TDI_BUFFERRING (endpoint) &&
IrpSp->Parameters.DeviceIoControl.OutputBufferLength!=0)) {
if (endpoint->State==AfdEndpointStateConnected) {
status = STATUS_CONNECTION_ACTIVE;
}
else {
status = STATUS_INVALID_PARAMETER;
}
goto complete_state_change;
}
IF_DEBUG(CONNECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdSuperConnect: starting connect on endpoint %p\n",
endpoint ));
}
//
// Create a connection object to use for the connect operation.
//
status = AfdCreateConnection(
endpoint->TransportInfo,
endpoint->AddressHandle,
IS_TDI_BUFFERRING(endpoint),
endpoint->InLine,
endpoint->OwningProcess,
&connection
);
if ( !NT_SUCCESS(status) ) {
goto complete_state_change;
}
//
// 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->Common.VcConnecting.Connection = connection;
endpoint->Type = AfdBlockTypeVcConnecting;
ASSERT( IS_TDI_BUFFERRING(endpoint) == 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 );
//
// Since we may be reissuing a connect after a previous failed connect,
// reenable the failed connect event bit.
//
AfdEnableFailedConnectEvent( 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 >=
remoteAddress->Address[0].AddressLength +
sizeof(u_short))) {
RtlMoveMemory ((PUCHAR)context +
endpoint->Common.VcConnecting.RemoteSocketAddressOffset,
&remoteAddress->Address[0].AddressType,
remoteAddress->Address[0].AddressLength +
sizeof(u_short));
}
else {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_WARNING_LEVEL,
"AfdSuperConnect: Could not copy remote address for AcceptEx on endpoint: %p, process: %p\n",
endpoint, endpoint->OwningProcess));
}
AfdUnlockEndpointContext (endpoint, context);
//
// Reference the connection block so it does not go away even if
// endpoint's reference to it is removed (in cleanup)
//
REFERENCE_CONNECTION (connection);
//
// 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.
//
requestConnectionInfo = &afdBuffer->TdiInfo;
afdBuffer->TdiInfo.UserDataLength = 0;
afdBuffer->TdiInfo.UserData = NULL;
afdBuffer->TdiInfo.OptionsLength = 0;
afdBuffer->TdiInfo.Options = NULL;
//
// Temporarily use IRP embedded in afd buffer
// for return connection information.
//
{
C_ASSERT (sizeof (TDI_CONNECTION_INFORMATION)<=
sizeof (IO_STACK_LOCATION));
}
returnConnectionInfo =
(PTDI_CONNECTION_INFORMATION)IoGetNextIrpStackLocation (afdBuffer->Irp);
RtlZeroMemory (returnConnectionInfo, sizeof (*returnConnectionInfo));
if ( endpoint->Common.VirtualCircuit.ConnectDataBuffers != NULL ) {
AfdSetupConnectDataBuffers(
endpoint,
connection,
&requestConnectionInfo,
&returnConnectionInfo
);
}
afdBuffer->Context = connection;
//
// Build a TDI kernel-mode connect request in the next stack location
// of the IRP.
//
TdiBuildConnect(
Irp,
connection->DeviceObject,
connection->FileObject,
AfdRestartSuperConnect,
afdBuffer,
&AfdInfiniteTimeout,
requestConnectionInfo,
returnConnectionInfo
);
AFD_VERIFY_ADDRESS (connection, afdBuffer->TdiInfo.RemoteAddress);
ObReferenceObject (IrpSp->FileObject);
//
// Call the transport to actually perform the connect operation.
//
return AfdIoCallDriver( endpoint, connection->DeviceObject, Irp );
complete_state_change:
AFD_END_STATE_CHANGE (endpoint);
complete:
if (afdBuffer!=NULL) {
AfdReturnBuffer (&afdBuffer->Header, endpoint->OwningProcess);
}
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, AfdPriorityBoost );
return status;
} // AfdSuperConnect
NTSTATUS
AfdRestartSuperConnect (
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;
AFD_LOCK_QUEUE_HANDLE lockHandle;
PIO_STACK_LOCATION irpSp;
PAFD_BUFFER afdBuffer;
UNREFERENCED_PARAMETER (DeviceObject);
afdBuffer = Context;
connection = afdBuffer->Context;
ASSERT( connection->Type == AfdBlockTypeConnection );
irpSp = IoGetCurrentIrpStackLocation( Irp );
endpoint = irpSp->FileObject->FsContext;
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting );
ASSERT( endpoint==connection->Endpoint );
IF_DEBUG(CONNECT) {
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
"AfdRestartConnect: connect completed, status = %X, endpoint = %p\n",
Irp->IoStatus.Status, endpoint ));
}
if ( connection->ConnectDataBuffers != NULL ) {
//
// If there are connect buffers on this endpoint, remember the
// size of the return connect data.
//
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
//
// Double-check under the lock
//
if ( connection->ConnectDataBuffers != NULL ) {
AfdSaveReceivedConnectData(
&connection->ConnectDataBuffers,
IOCTL_AFD_SET_CONNECT_DATA,
connection->ConnectDataBuffers->ReturnConnectionInfo.UserData,
connection->ConnectDataBuffers->ReturnConnectionInfo.UserDataLength
);
AfdSaveReceivedConnectData(
&connection->ConnectDataBuffers,
IOCTL_AFD_SET_CONNECT_OPTIONS,
connection->ConnectDataBuffers->ReturnConnectionInfo.Options,
connection->ConnectDataBuffers->ReturnConnectionInfo.OptionsLength
);
}
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
}
//
// 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)) {
//
// 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.
//
connection->ConnectTime = KeQueryInterruptTime();
} else {
//
// The connect failed, so reset the type to open.
// Otherwise, we won't be able to start another connect
//
endpoint->Type = AfdBlockTypeEndpoint;
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
if (endpoint->Common.VcConnecting.Connection!=NULL) {
ASSERT (connection==endpoint->Common.VcConnecting.Connection);
endpoint->Common.VcConnecting.Connection = NULL;
//
// 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.
//
if ( connection->ConnectedReferenceAdded ) {
connection->ConnectedReferenceAdded = FALSE;
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
DEREFERENCE_CONNECTION( connection );
} else {
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
}
//
// 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 );
}
else {
//
// The endpoint's reference to connection was removed
// (perhaps in cleanup);
//
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
}
}
//
// If pending has be returned for this irp then mark the current
// stack as pending.
//
if ( Irp->PendingReturned ) {
IoMarkIrpPending(Irp);
}
AfdCompleteOutstandingIrp( endpoint, Irp );
AfdFinishConnect (endpoint, Irp, NULL);
if (NT_SUCCESS (Irp->IoStatus.Status) && afdBuffer->DataLength>0) {
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
if ( !connection->CleanupBegun && !connection->Aborted ) {
NTSTATUS status;
//
// Update count of send bytes pending on the connection.
//
connection->VcBufferredSendBytes += afdBuffer->DataLength;
connection->VcBufferredSendCount += 1;
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
afdBuffer->Mdl->ByteCount = afdBuffer->DataLength;
ASSERT (afdBuffer->Context == connection );
TdiBuildSend(
afdBuffer->Irp,
connection->DeviceObject,
connection->FileObject,
AfdRestartBufferSend,
afdBuffer,
afdBuffer->Mdl,
0,
afdBuffer->DataLength
);
Irp->IoStatus.Information = afdBuffer->DataLength;
//
// Call the transport to actually perform the send.
//
status = IoCallDriver (
connection->DeviceObject,
afdBuffer->Irp
);
if (!NT_SUCCESS (status)) {
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
}
goto exit;
}
if (connection->CleanupBegun) {
Irp->IoStatus.Status = STATUS_LOCAL_DISCONNECT;
}
else {
ASSERT (connection->Aborted);
Irp->IoStatus.Status = STATUS_REMOTE_DISCONNECT;
}
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
}
afdBuffer->DataOffset = 0;
AfdReturnBuffer (&afdBuffer->Header, endpoint->OwningProcess);
//
// Dereference connection to account for reference we added in AfdConnect
//
DEREFERENCE_CONNECTION (connection);
exit:
return STATUS_SUCCESS;
} // AfdRestartSuperConnect