diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/afd/connect.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/afd/connect.c')
-rw-r--r-- | private/ntos/afd/connect.c | 659 |
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 + |