NT4/private/ntos/afd/connect.c
2020-09-30 17:12:29 +02:00

660 lines
17 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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