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
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
|