660 lines
17 KiB
C
660 lines
17 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
connect.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the code for passing on connect IRPs to
|
||
TDI providers.
|
||
|
||
Author:
|
||
|
||
David Treadwell (davidtr) 2-Mar-1992
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "afdp.h"
|
||
|
||
NTSTATUS
|
||
AfdDoDatagramConnect (
|
||
IN PAFD_ENDPOINT Endpoint,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
AfdRestartConnect (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
);
|
||
|
||
VOID
|
||
AfdSetupConnectDataBuffers (
|
||
IN PAFD_ENDPOINT Endpoint,
|
||
IN PAFD_CONNECTION Connection,
|
||
IN PTDI_CONNECTION_INFORMATION RequestConnectionInformation,
|
||
IN PTDI_CONNECTION_INFORMATION ReturnConnectionInformation
|
||
);
|
||
|
||
VOID
|
||
AfdEnableFailedConnectEvent(
|
||
IN PAFD_ENDPOINT Endpoint
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text( PAGE, AfdConnect )
|
||
#pragma alloc_text( PAGEAFD, AfdDoDatagramConnect )
|
||
#pragma alloc_text( PAGEAFD, AfdRestartConnect )
|
||
#pragma alloc_text( PAGEAFD, AfdSetupConnectDataBuffers )
|
||
#pragma alloc_text( PAGEAFD, AfdEnableFailedConnectEvent )
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
AfdConnect (
|
||
IN PIRP Irp,
|
||
IN PIO_STACK_LOCATION IrpSp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Handles the IOCTL_AFD_CONNECT IOCTL.
|
||
|
||
Arguments:
|
||
|
||
Irp - Pointer to I/O request packet.
|
||
|
||
IrpSp - pointer to the IO stack location to use for this request.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS -- Indicates whether the request was successfully queued.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
PAFD_ENDPOINT endpoint;
|
||
PAFD_CONNECTION connection;
|
||
PTDI_REQUEST_CONNECT tdiRequest;
|
||
PTDI_CONNECTION_INFORMATION requestConnectionInformation;
|
||
PTDI_CONNECTION_INFORMATION returnConnectionInformation;
|
||
ULONG offset;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Make sure that the endpoint is in the correct state.
|
||
//
|
||
|
||
endpoint = IrpSp->FileObject->FsContext;
|
||
ASSERT( endpoint->Type == AfdBlockTypeEndpoint ||
|
||
endpoint->Type == AfdBlockTypeDatagram );
|
||
|
||
IF_DEBUG(CONNECT) {
|
||
KdPrint(( "AfdConnect: starting connect on endpoint %lx\n", endpoint ));
|
||
}
|
||
|
||
//
|
||
// If the endpoint is not bound or if there is another connect
|
||
// outstanding on this endpoint, then this is an invalid request.
|
||
// If this is a datagram endpoint, it is legal to reconnect
|
||
// the endpoint.
|
||
//
|
||
|
||
if ( ( endpoint->Type != AfdBlockTypeEndpoint &&
|
||
endpoint->Type != AfdBlockTypeDatagram ) ||
|
||
endpoint->State != AfdEndpointStateBound ||
|
||
endpoint->ConnectOutstanding ) {
|
||
|
||
if ( !IS_DGRAM_ENDPOINT(endpoint) ||
|
||
endpoint->State != AfdEndpointStateConnected ) {
|
||
status = STATUS_INVALID_PARAMETER;
|
||
goto complete;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Determine where in the system buffer the request and return
|
||
// connection information structures exist. Pass pointers to
|
||
// these locations instead of the user-mode pointers in the
|
||
// tdiRequest structure so that the memory will be nonpageable.
|
||
//
|
||
|
||
tdiRequest = Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
offset = (ULONG)tdiRequest->RequestConnectionInformation -
|
||
(ULONG)Irp->UserBuffer;
|
||
|
||
if( (LONG)offset < 0 ||
|
||
( offset + sizeof(*requestConnectionInformation) ) >
|
||
IrpSp->Parameters.DeviceIoControl.InputBufferLength ) {
|
||
status = STATUS_INVALID_PARAMETER;
|
||
goto complete;
|
||
}
|
||
|
||
requestConnectionInformation = (PVOID)( (ULONG)tdiRequest + offset );
|
||
|
||
offset = (ULONG)tdiRequest->ReturnConnectionInformation -
|
||
(ULONG)Irp->UserBuffer;
|
||
|
||
if( (LONG)offset < 0 ||
|
||
( offset + sizeof(*returnConnectionInformation) ) >
|
||
IrpSp->Parameters.DeviceIoControl.InputBufferLength ) {
|
||
status = STATUS_INVALID_PARAMETER;
|
||
goto complete;
|
||
}
|
||
|
||
returnConnectionInformation = (PVOID)( (ULONG)tdiRequest + offset );
|
||
|
||
offset = (ULONG)requestConnectionInformation->RemoteAddress -
|
||
(ULONG)Irp->UserBuffer;
|
||
|
||
if( (LONG)offset < 0 ||
|
||
( offset + requestConnectionInformation->RemoteAddressLength ) >
|
||
IrpSp->Parameters.DeviceIoControl.InputBufferLength ) {
|
||
status = STATUS_INVALID_PARAMETER;
|
||
goto complete;
|
||
}
|
||
|
||
requestConnectionInformation->RemoteAddress =
|
||
(PVOID)( (ULONG)tdiRequest + offset );
|
||
|
||
//
|
||
// If this is a datagram endpoint, simply remember the specified
|
||
// address so that we can use it on sends, receives, writes, and
|
||
// reads.
|
||
//
|
||
|
||
if ( IS_DGRAM_ENDPOINT(endpoint) ) {
|
||
tdiRequest->RequestConnectionInformation = requestConnectionInformation;
|
||
tdiRequest->ReturnConnectionInformation = returnConnectionInformation;
|
||
|
||
return AfdDoDatagramConnect( endpoint, Irp );
|
||
}
|
||
|
||
//
|
||
// Create a connection object to use for the connect operation.
|
||
//
|
||
|
||
status = AfdCreateConnection(
|
||
&endpoint->TransportInfo->TransportDeviceName,
|
||
endpoint->AddressHandle,
|
||
endpoint->TdiBufferring,
|
||
endpoint->InLine,
|
||
endpoint->OwningProcess,
|
||
&connection
|
||
);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
goto complete;
|
||
}
|
||
|
||
//
|
||
// Set up a referenced pointer from the connection to the endpoint.
|
||
// Note that we set up the connection's pointer to the endpoint
|
||
// BEFORE the endpoint's pointer to the connection so that AfdPoll
|
||
// doesn't try to back reference the endpoint from the connection.
|
||
//
|
||
|
||
REFERENCE_ENDPOINT( endpoint );
|
||
connection->Endpoint = endpoint;
|
||
|
||
//
|
||
// Remember that this is now a connecting type of endpoint, and set
|
||
// up a pointer to the connection in the endpoint. This is
|
||
// implicitly a referenced pointer.
|
||
//
|
||
|
||
endpoint->Type = AfdBlockTypeVcConnecting;
|
||
endpoint->Common.VcConnecting.Connection = connection;
|
||
|
||
ASSERT( endpoint->TdiBufferring == connection->TdiBufferring );
|
||
|
||
//
|
||
// Add an additional reference to the connection. This prevents the
|
||
// connection from being closed until the disconnect event handler
|
||
// is called.
|
||
//
|
||
|
||
AfdAddConnectedReference( connection );
|
||
|
||
//
|
||
// Remember that there is a connect operation outstanding on this
|
||
// endpoint. This allows us to correctly block a send poll on the
|
||
// endpoint until the connect completes.
|
||
//
|
||
|
||
endpoint->ConnectOutstanding = TRUE;
|
||
|
||
//
|
||
// If there are connect data buffers, move them from the endpoint
|
||
// structure to the connection structure and set up the necessary
|
||
// pointers in the connection request we're going to give to the TDI
|
||
// provider. Do this in a subroutine so this routine can be pageable.
|
||
//
|
||
|
||
AfdSetupConnectDataBuffers(
|
||
endpoint,
|
||
connection,
|
||
requestConnectionInformation,
|
||
returnConnectionInformation
|
||
);
|
||
|
||
//
|
||
// Save a pointer to the return connection information structure
|
||
// so we can access it in the restart routine.
|
||
//
|
||
|
||
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = returnConnectionInformation;
|
||
|
||
//
|
||
// Since we may be reissuing a connect after a previous failed connect,
|
||
// reenable the failed connect event bit.
|
||
//
|
||
|
||
AfdEnableFailedConnectEvent( endpoint );
|
||
|
||
//
|
||
// Build a TDI kernel-mode connect request in the next stack location
|
||
// of the IRP.
|
||
//
|
||
|
||
TdiBuildConnect(
|
||
Irp,
|
||
connection->DeviceObject,
|
||
connection->FileObject,
|
||
AfdRestartConnect,
|
||
endpoint,
|
||
&tdiRequest->Timeout,
|
||
requestConnectionInformation,
|
||
returnConnectionInformation
|
||
);
|
||
|
||
//
|
||
// Reset the connect status to success so that the poll code will
|
||
// know if a connect failure occurs.
|
||
//
|
||
|
||
endpoint->Common.VcConnecting.ConnectStatus = STATUS_SUCCESS;
|
||
|
||
//
|
||
// Call the transport to actually perform the connect operation.
|
||
//
|
||
|
||
return AfdIoCallDriver( endpoint, connection->DeviceObject, Irp );
|
||
|
||
complete:
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = status;
|
||
IoCompleteRequest( Irp, AfdPriorityBoost );
|
||
|
||
return status;
|
||
|
||
} // AfdConnect
|
||
|
||
|
||
NTSTATUS
|
||
AfdDoDatagramConnect (
|
||
IN PAFD_ENDPOINT Endpoint,
|
||
IN PIRP Irp
|
||
)
|
||
{
|
||
PTRANSPORT_ADDRESS inputAddress;
|
||
KIRQL oldIrql;
|
||
NTSTATUS status;
|
||
PTDI_REQUEST_CONNECT tdiRequest;
|
||
|
||
tdiRequest = Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
//
|
||
// Save the remote address on the endpoint. We'll use this to
|
||
// send datagrams in the future and to compare received datagram's
|
||
// source addresses.
|
||
//
|
||
|
||
inputAddress = tdiRequest->RequestConnectionInformation->RemoteAddress;
|
||
|
||
AfdAcquireSpinLock( &Endpoint->SpinLock, &oldIrql );
|
||
|
||
if ( Endpoint->Common.Datagram.RemoteAddress != NULL ) {
|
||
AFD_FREE_POOL(
|
||
Endpoint->Common.Datagram.RemoteAddress,
|
||
AFD_REMOTE_ADDRESS_POOL_TAG
|
||
);
|
||
}
|
||
|
||
Endpoint->Common.Datagram.RemoteAddress =
|
||
AFD_ALLOCATE_POOL(
|
||
NonPagedPool,
|
||
tdiRequest->RequestConnectionInformation->RemoteAddressLength,
|
||
AFD_REMOTE_ADDRESS_POOL_TAG
|
||
);
|
||
|
||
if ( Endpoint->Common.Datagram.RemoteAddress == NULL ) {
|
||
AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql );
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto complete;
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
Endpoint->Common.Datagram.RemoteAddress,
|
||
inputAddress,
|
||
tdiRequest->RequestConnectionInformation->RemoteAddressLength
|
||
);
|
||
|
||
Endpoint->Common.Datagram.RemoteAddressLength =
|
||
tdiRequest->RequestConnectionInformation->RemoteAddressLength;
|
||
Endpoint->State = AfdEndpointStateConnected;
|
||
|
||
Endpoint->DisconnectMode = 0;
|
||
|
||
//
|
||
// Indicate that the connect completed. Implicitly, the
|
||
// successful completion of a connect also means that the caller
|
||
// can do a send on the socket.
|
||
//
|
||
|
||
AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql );
|
||
|
||
AfdIndicatePollEvent(
|
||
Endpoint,
|
||
AFD_POLL_CONNECT_BIT,
|
||
STATUS_SUCCESS
|
||
);
|
||
|
||
AfdIndicatePollEvent(
|
||
Endpoint,
|
||
AFD_POLL_SEND_BIT,
|
||
STATUS_SUCCESS
|
||
);
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
complete:
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = status;
|
||
IoCompleteRequest( Irp, AfdPriorityBoost );
|
||
|
||
return status;
|
||
|
||
} // AfdDoDatagramConnect
|
||
|
||
|
||
VOID
|
||
AfdSetupConnectDataBuffers (
|
||
IN PAFD_ENDPOINT Endpoint,
|
||
IN PAFD_CONNECTION Connection,
|
||
IN PTDI_CONNECTION_INFORMATION RequestConnectionInformation,
|
||
IN PTDI_CONNECTION_INFORMATION ReturnConnectionInformation
|
||
)
|
||
{
|
||
KIRQL oldIrql;
|
||
|
||
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
|
||
|
||
if ( Endpoint->ConnectDataBuffers != NULL ) {
|
||
|
||
ASSERT( Connection->ConnectDataBuffers == NULL );
|
||
|
||
Connection->ConnectDataBuffers = Endpoint->ConnectDataBuffers;
|
||
Endpoint->ConnectDataBuffers = NULL;
|
||
|
||
RequestConnectionInformation->UserData =
|
||
Connection->ConnectDataBuffers->SendConnectData.Buffer;
|
||
RequestConnectionInformation->UserDataLength =
|
||
Connection->ConnectDataBuffers->SendConnectData.BufferLength;
|
||
RequestConnectionInformation->Options =
|
||
Connection->ConnectDataBuffers->SendConnectOptions.Buffer;
|
||
RequestConnectionInformation->OptionsLength =
|
||
Connection->ConnectDataBuffers->SendConnectOptions.BufferLength;
|
||
ReturnConnectionInformation->UserData =
|
||
Connection->ConnectDataBuffers->ReceiveConnectData.Buffer;
|
||
ReturnConnectionInformation->UserDataLength =
|
||
Connection->ConnectDataBuffers->ReceiveConnectData.BufferLength;
|
||
ReturnConnectionInformation->Options =
|
||
Connection->ConnectDataBuffers->ReceiveConnectOptions.Buffer;
|
||
ReturnConnectionInformation->OptionsLength =
|
||
Connection->ConnectDataBuffers->ReceiveConnectOptions.BufferLength;
|
||
|
||
}
|
||
|
||
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
|
||
|
||
} // AfdSetupConnectDataBuffers
|
||
|
||
|
||
NTSTATUS
|
||
AfdRestartConnect (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Handles the IOCTL_AFD_CONNECT IOCTL.
|
||
|
||
Arguments:
|
||
|
||
Irp - Pointer to I/O request packet.
|
||
|
||
IrpSp - pointer to the IO stack location to use for this request.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS -- Indicates whether the request was successfully queued.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAFD_ENDPOINT endpoint;
|
||
PAFD_CONNECTION connection;
|
||
KIRQL oldIrql;
|
||
|
||
endpoint = Context;
|
||
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting );
|
||
|
||
connection = endpoint->Common.VcConnecting.Connection;
|
||
ASSERT( connection != NULL );
|
||
ASSERT( connection->Type == AfdBlockTypeConnection );
|
||
|
||
IF_DEBUG(CONNECT) {
|
||
KdPrint(( "AfdRestartConnect: connect completed, status = %X, "
|
||
"endpoint = %lx\n", Irp->IoStatus.Status, endpoint ));
|
||
}
|
||
|
||
endpoint->Common.VcConnecting.ConnectStatus = Irp->IoStatus.Status;
|
||
|
||
//
|
||
// Remember that there is no longer a connect operation outstanding
|
||
// on this endpoint. We must do this BEFORE the AfdIndicatePoll()
|
||
// in case a poll comes in while we are doing the indicate of setting
|
||
// the ConnectOutstanding bit.
|
||
//
|
||
|
||
endpoint->ConnectOutstanding = FALSE;
|
||
|
||
//
|
||
// If there are connect buffers on this endpoint, remember the
|
||
// size of the return connect data.
|
||
//
|
||
|
||
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
|
||
|
||
if ( connection->ConnectDataBuffers != NULL ) {
|
||
|
||
PIO_STACK_LOCATION irpSp;
|
||
PTDI_CONNECTION_INFORMATION returnConnectionInformation;
|
||
|
||
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
returnConnectionInformation =
|
||
irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
||
|
||
ASSERT( returnConnectionInformation != NULL );
|
||
ASSERT( connection->ConnectDataBuffers->ReceiveConnectData.BufferLength >=
|
||
(ULONG)returnConnectionInformation->UserDataLength );
|
||
ASSERT( connection->ConnectDataBuffers->ReceiveConnectOptions.BufferLength >=
|
||
(ULONG)returnConnectionInformation->OptionsLength );
|
||
|
||
connection->ConnectDataBuffers->ReceiveConnectData.BufferLength =
|
||
returnConnectionInformation->UserDataLength;
|
||
connection->ConnectDataBuffers->ReceiveConnectOptions.BufferLength =
|
||
returnConnectionInformation->OptionsLength;
|
||
}
|
||
|
||
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
|
||
|
||
//
|
||
// Indicate that the connect completed. Implicitly, the successful
|
||
// completion of a connect also means that the caller can do a send
|
||
// on the socket.
|
||
//
|
||
|
||
if ( NT_SUCCESS(Irp->IoStatus.Status) ) {
|
||
|
||
AfdIndicatePollEvent(
|
||
endpoint,
|
||
AFD_POLL_CONNECT_BIT,
|
||
Irp->IoStatus.Status
|
||
);
|
||
|
||
//
|
||
// If the request succeeded, set the endpoint to the connected
|
||
// state. The endpoint type has already been set to
|
||
// AfdBlockTypeVcConnecting.
|
||
//
|
||
|
||
endpoint->State = AfdEndpointStateConnected;
|
||
ASSERT( endpoint->Type = AfdBlockTypeVcConnecting );
|
||
|
||
//
|
||
// Remember the time that the connection started.
|
||
//
|
||
|
||
KeQuerySystemTime( (PLARGE_INTEGER)&connection->ConnectTime );
|
||
|
||
} else {
|
||
|
||
AfdIndicatePollEvent(
|
||
endpoint,
|
||
AFD_POLL_CONNECT_FAIL_BIT,
|
||
Irp->IoStatus.Status
|
||
);
|
||
|
||
//
|
||
// Manually delete the connected reference if somebody else
|
||
// hasn't already done so. We can't use
|
||
// AfdDeleteConnectedReference() because it refuses to delete
|
||
// the connected reference until the endpoint has been cleaned
|
||
// up.
|
||
//
|
||
|
||
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
|
||
|
||
//
|
||
// The connect failed, so reset the type to open.
|
||
//
|
||
|
||
endpoint->Type = AfdBlockTypeEndpoint;
|
||
|
||
if ( connection->ConnectedReferenceAdded ) {
|
||
connection->ConnectedReferenceAdded = FALSE;
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
||
DEREFERENCE_CONNECTION( connection );
|
||
} else {
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
||
}
|
||
|
||
//
|
||
// Dereference the connection block stored on the endpoint.
|
||
// This should cause the connection object reference count to go
|
||
// to zero to the connection object can be deleted.
|
||
//
|
||
|
||
DEREFERENCE_CONNECTION( connection );
|
||
endpoint->Common.VcConnecting.Connection = NULL;
|
||
}
|
||
|
||
if ( NT_SUCCESS(Irp->IoStatus.Status ) ) {
|
||
|
||
AfdIndicatePollEvent(
|
||
endpoint,
|
||
AFD_POLL_SEND_BIT,
|
||
STATUS_SUCCESS
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// If pending has be returned for this irp then mark the current
|
||
// stack as pending.
|
||
//
|
||
|
||
if ( Irp->PendingReturned ) {
|
||
IoMarkIrpPending(Irp);
|
||
}
|
||
|
||
AfdCompleteOutstandingIrp( endpoint, Irp );
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} // AfdRestartConnect
|
||
|
||
|
||
VOID
|
||
AfdEnableFailedConnectEvent(
|
||
IN PAFD_ENDPOINT Endpoint
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Reenables the failed connect poll bit on the specified endpoint.
|
||
This is off in a separate (nonpageable) routine so that the bulk
|
||
of AfdConnect() can remain pageable.
|
||
|
||
Arguments:
|
||
|
||
Endpoint - The endpoint to enable.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldIrql;
|
||
|
||
AfdAcquireSpinLock( &Endpoint->SpinLock, &oldIrql );
|
||
|
||
ASSERT( ( Endpoint->EventsActive & AFD_POLL_CONNECT ) == 0 );
|
||
Endpoint->EventsActive &= ~AFD_POLL_CONNECT_FAIL;
|
||
|
||
IF_DEBUG(EVENT_SELECT) {
|
||
KdPrint((
|
||
"AfdConnect: Endp %08lX, Active %08lX\n",
|
||
Endpoint,
|
||
Endpoint->EventsActive
|
||
));
|
||
}
|
||
|
||
AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql );
|
||
|
||
} // AfdEnableFailedConnectEvent
|
||
|