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