windows-nt/Source/XPSP1/NT/termsrv/drivers/td/tdicom/tdtdi.c

3165 lines
100 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/****************************************************************************/
// tdtdi.c
//
// Common code for all TDI based Transport Drivers
//
// Copyright (C) 1998-2000 Microsoft Corporation
/****************************************************************************/
#include <ntddk.h>
#include <tdi.h>
#include <tdikrnl.h>
#include "tdtdi.h"
#include <winstaw.h>
#define _DEFCHARINFO_
#include <icadd.h>
#include <ctxdd.h>
#include <sdapi.h>
#include <td.h>
#include <tdi.h>
#define TDTDI_LISTEN_QUEUE_DEPTH 5 // This was hardcoded in afdcom
#ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif
#if DBG
ULONG DbgPrint(PCH Format, ...);
#define DBGPRINT(x) DbgPrint x
#if DBGTRACE
#define DBGENTER(x) DbgPrint x
#define TRACE0(x) DbgPrint x
#define TRACE1(x)
#else
#define DBGENTER(x)
#define TRACE0(x)
#define TRACE1(x)
#endif
#else
#define DBGPRINT(x)
#define DBGENTER(x)
#define TRACE0(x)
#define TRACE1(x)
#endif
/*
DOCUMENT THIS INTERFACE FINALLY!
This is the best I can dig out of these existing interfaces.
Common Sequences:
Startup and Listen - DeviceOpen, DeviceCreateEndpoint, DeviceConnectionWait
DeviceConnectionWait returns an internal handle to represent the connection
it has listened for, accepted, and returned. This handle is useless for any
operations, and is only good for feeding to DeviceOpenEndpoint to get
an endpoint that can be used for communications.
Connect - DeviceOpen, DeviceOpenEndpoint
A DeviceOpen is done to create a new endpoint, then its handle
from DeviceConnectionWait is "inserted" into the empty endpoint.
We now have a real, live connection
Disconnect From Client - DeviceCancelIo
Disconnect From disconn command - DeviceCloseEndpoint?, DeviceCancelIo?
Reconnect - DeviceCancelIo, DeviceClose, DeviceOpen, DeviceOpenEndpoint
Once the user has fully logged onto a new connected WinStation, a
DeviceCancelIo and then a DeviceClose is issued to release the
new logged on WinStation from the connection. While this connection
remains up, a new DeviceOpen and DeviceOpenEndpoint is done to
connect this connection to the users previously disconnected WinStation.
NTSTATUS DeviceOpen( PTD, PSD_OPEN );
Open and initialize private data structures. Calls the TdiDeviceOpen(), but
this is a no-op.
NTSTATUS DeviceClose( PTD, PSD_CLOSE );
Close the transport driver. If its an address endpoint,
it will destroy it. If its a connection endpoint, it
DOES not destroy it.
If the connection endpoint is destroyed, disconnect/reconnect
will be broken.
Calls TdiDeviceClose(), which is another no-op.
NTSTATUS DeviceCreateEndpoint( PTD, PICA_STACK_ADDRESS, PICA_STACK_ADDRESS );
Creates and Address endpoint that can be used for listening.
This does not create any connection endpoints.
NTSTATUS DeviceOpenEndpoint( PTD, PVOID, ULONG );
Takes an existing connection endpoint handle, and makes
an "endpoint" out of it.
This is used by disconnect/reconnect.
NTSTATUS DeviceCloseEndpoint( PTD );
This closes the endpoint.
If its a connection endpoint, it is destroyed.
NTSTATUS DeviceConnectionWait( PTD, PVOID, ULONG, PULONG );
This waits for connections to come in, and returns connected
endpoints in the pIcaEndpoint structure.
NTSTATUS DeviceCancelIo( PTD );
This asks for all I/O on the given endpoint to be canceled.
With TDI, we can not actually cancel I/O, but must hold IRP's
until indication handlers tell us to submit. This is because canceling
I/O on a TDI connection causes the TDI provider to kill the connection.
NTSTATUS DeviceConnectionSend( PTD );
This names sounds like send TD specific data to the host.
This does not actually send anything, but fills in
a structure for the upper level who may actually send it
at some time.
NTSTATUS DeviceConnectionRequest( PTD, PICA_STACK_ADDRESS, PVOID, ULONG, PULONG );
This is used by shadow to act as a network client and
initiate a connection. This is obsolete and not used since
a named pipe TD will handle all shadow traffic.
NTSTATUS DeviceIoctl( PTD, PSD_IOCTL );
NTSTATUS DeviceInitializeRead( PTD, PINBUF );
NTSTATUS DeviceWaitForRead( PTD );
NTSTATUS DeviceReadComplete( PTD, PUCHAR, PULONG );
NTSTATUS DeviceInitializeWrite( PTD, POUTBUF );
NTSTATUS DeviceWaitForStatus( PTD );
NTSTATUS DeviceSetParams( PTD );
NTSTATUS DeviceGetLastError( PTD, PICA_STACK_LAST_ERROR );
NTSTATUS DeviceSubmitRead( PTD, PINBUF );
*/
/*
* Context used for connect accept
*/
typedef struct _ACCEPT_CONTEXT {
PTD_ENDPOINT pAddressEndpoint;
PTD_ENDPOINT pConnectionEndpoint;
TDI_CONNECTION_INFORMATION RequestInfo;
TDI_CONNECTION_INFORMATION ReturnInfo;
} ACCEPT_CONTEXT, *PACCEPT_CONTEXT;
/*=============================================================================
== External Functions Defined
=============================================================================*/
// These are the functions our TD supplies to ICADD
NTSTATUS DeviceOpen( PTD, PSD_OPEN );
NTSTATUS DeviceClose( PTD, PSD_CLOSE );
NTSTATUS DeviceCreateEndpoint( PTD, PICA_STACK_ADDRESS, PICA_STACK_ADDRESS );
NTSTATUS DeviceOpenEndpoint( PTD, PVOID, ULONG );
NTSTATUS DeviceCloseEndpoint( PTD );
NTSTATUS DeviceConnectionWait( PTD, PVOID, ULONG, PULONG );
NTSTATUS DeviceConnectionSend( PTD );
NTSTATUS DeviceConnectionRequest( PTD, PICA_STACK_ADDRESS, PVOID, ULONG, PULONG );
NTSTATUS DeviceIoctl( PTD, PSD_IOCTL );
NTSTATUS DeviceInitializeRead( PTD, PINBUF );
NTSTATUS DeviceWaitForRead( PTD );
NTSTATUS DeviceReadComplete( PTD, PUCHAR, PULONG );
NTSTATUS DeviceInitializeWrite( PTD, POUTBUF );
NTSTATUS DeviceWaitForStatus( PTD );
NTSTATUS DeviceCancelIo( PTD );
NTSTATUS DeviceSetParams( PTD );
NTSTATUS DeviceGetLastError( PTD, PICA_STACK_LAST_ERROR );
NTSTATUS DeviceSubmitRead( PTD, PINBUF );
NTSTATUS DeviceQueryRemoteAddress( PTD, PVOID, ULONG, PVOID, ULONG, PULONG );
/*=============================================================================
== External Functions Referenced
=============================================================================*/
// These functions are provided by the protocol specific TD module
NTSTATUS TdiDeviceOpen( PTD, PSD_OPEN );
NTSTATUS TdiDeviceClose( PTD, PSD_CLOSE );
NTSTATUS TdiDeviceOpenEndpoint( PTD, PVOID, ULONG );
NTSTATUS TdiDeviceBuildTransportNameAndAddress( PTD, PICA_STACK_ADDRESS,
PUNICODE_STRING,
PTRANSPORT_ADDRESS *, PULONG );
NTSTATUS TdiDeviceBuildWildcardAddress( PTD, PTRANSPORT_ADDRESS *, PULONG );
NTSTATUS TdiDeviceWaitForDatagramConnection( PTD, PFILE_OBJECT, PDEVICE_OBJECT,
PTRANSPORT_ADDRESS *, PULONG );
NTSTATUS TdiDeviceCompleteDatagramConnection( PTD, PFILE_OBJECT, PDEVICE_OBJECT, PTRANSPORT_ADDRESS, ULONG );
NTSTATUS TdiDeviceConnectionSend( PTD );
NTSTATUS TdiDeviceReadComplete( PTD, PUCHAR, PULONG );
// These are functions in our support libraries
NTSTATUS MemoryAllocate( ULONG, PVOID * );
VOID MemoryFree( PVOID );
// Tdilib functions
PIRP
_TdiAllocateIrp(
IN PFILE_OBJECT FileObject,
IN PDEVICE_OBJECT DeviceObject OPTIONAL
);
NTSTATUS
_TdiCreateAddress (
IN PUNICODE_STRING pTransportName,
IN PVOID TdiAddress,
IN ULONG TdiAddressLength,
OUT PHANDLE pHandle,
OUT PFILE_OBJECT *ppFileObject,
OUT PDEVICE_OBJECT *ppDeviceObject
);
NTSTATUS
_TdiOpenConnection (
IN PUNICODE_STRING pTransportName,
IN PVOID ConnectionContext,
OUT PHANDLE pHandle,
OUT PFILE_OBJECT *ppFileObject,
OUT PDEVICE_OBJECT *ppDeviceObject
);
NTSTATUS
_TdiListen(
IN PTD pTd,
IN PIRP Irp OPTIONAL,
IN PFILE_OBJECT ConnectionFileObject,
IN PDEVICE_OBJECT ConnectionDeviceObject
);
NTSTATUS
_TdiConnect(
IN PTD pTd,
IN PIRP Irp OPTIONAL,
IN PLARGE_INTEGER pTimeout OPTIONAL,
IN PFILE_OBJECT ConnectionFileObject,
IN PDEVICE_OBJECT ConnectionDeviceObject,
IN ULONG RemoteTransportAddressLength,
IN PTRANSPORT_ADDRESS pRemoteTransportAddress
);
NTSTATUS
_TdiAssociateAddress(
IN PTD pTd,
IN PIRP Irp OPTIONAL,
IN PFILE_OBJECT ConnectionFileObject,
IN HANDLE AddressHandle,
IN PDEVICE_OBJECT AddressDeviceObject
);
NTSTATUS
_TdiDisconnect(
IN PTD pTd,
IN PFILE_OBJECT ConnectionFileObject,
IN PDEVICE_OBJECT ConnectionDeviceObject
);
NTSTATUS
_TdiSetEventHandler (
IN PTD pTd,
IN PDEVICE_OBJECT DeviceObject,
IN PFILE_OBJECT FileObject,
IN ULONG EventType,
IN PVOID EventHandler,
IN PVOID EventContext
);
NTSTATUS
_TdiQueryAddressInfo(
IN PTD pTd,
IN PIRP Irp OPTIONAL,
IN PFILE_OBJECT FileObject,
IN PDEVICE_OBJECT DeviceObject,
IN PTDI_ADDRESS_INFO pAddressInfo,
IN ULONG AddressInfoLength
);
NTSTATUS
_TdCancelReceiveQueue(
PTD pTd,
PTD_ENDPOINT pEndpoint,
NTSTATUS CancelStatus
);
/*=============================================================================
== Internal Functions Defined
=============================================================================*/
NTSTATUS _TdCreateEndpointStruct( PTD, PUNICODE_STRING, PTD_ENDPOINT *, PTRANSPORT_ADDRESS, ULONG );
NTSTATUS _TdCloseEndpoint( PTD, PTD_ENDPOINT );
NTSTATUS
_TdConnectHandler(
IN PVOID TdiEventContext,
IN int RemoteAddressLength,
IN PVOID RemoteAddress,
IN int UserDataLength,
IN PVOID UserData,
IN int OptionsLength,
IN PVOID Options,
OUT CONNECTION_CONTEXT *ConnectionContext,
OUT PIRP *AcceptIrp
);
NTSTATUS
_TdDisconnectHandler (
IN PVOID TdiEventContext,
IN CONNECTION_CONTEXT ConnectionContext,
IN int DisconnectDataLength,
IN PVOID DisconnectData,
IN int DisconnectInformationLength,
IN PVOID DisconnectInformation,
IN ULONG DisconnectFlags
);
NTSTATUS
_TdReceiveHandler (
IN PVOID TdiEventContext,
IN CONNECTION_CONTEXT ConnectionContext,
IN ULONG ReceiveFlags,
IN ULONG BytesIndicated,
IN ULONG BytesAvailable,
OUT ULONG *BytesTaken,
IN PVOID Tsdu,
OUT PIRP *IoRequestPacket
);
NTSTATUS
_TdAcceptComplete (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
_TdCreateConnectionObject(
IN PTD pTd,
IN PUNICODE_STRING pTransportName,
OUT PTD_ENDPOINT *ppEndpoint,
IN PTRANSPORT_ADDRESS pTransportAddress,
IN ULONG TransportAddressLength
);
NTSTATUS
_TdWaitForDatagramConnection(
IN PTD pTd,
IN PTD_ENDPOINT pAddressEndpoint,
OUT PTD_ENDPOINT *ppConnectionEndpoint
);
/*
* Global Data
*/
extern USHORT TdiDeviceEndpointType; // Datagram or stream set by TD
extern USHORT TdiDeviceAddressType; // TDI address format by TD
extern USHORT TdiDeviceInBufHeader; // Bytes of header set by TD (0 for stream)
/*******************************************************************************
* DeviceOpen
*
* Allocate and initialize private data structures
*
* pTd (input)
* Pointer to TD data structure
* pSdOpen (input/output)
* Points to the parameter structure SD_OPEN.
******************************************************************************/
NTSTATUS DeviceOpen(PTD pTd, PSD_OPEN pSdOpen)
{
PTDTDI pTdTdi;
NTSTATUS Status;
DBGENTER(("DeviceOpen: PTD 0x%x\n",pTd));
/*
* Set protocol driver class
*/
pTd->SdClass = SdNetwork;
pTd->InBufHeader = TdiDeviceInBufHeader; // For packet oriented protocols
/*
* Return size of header and trailer
*/
pSdOpen->SdOutBufHeader = 0;
pSdOpen->SdOutBufTrailer = 0;
/*
* Allocate TDI TD data structure
*/
Status = MemoryAllocate( sizeof(*pTdTdi), &pTdTdi );
if ( !NT_SUCCESS(Status) )
goto badalloc;
ASSERT( pTd->pAfd == NULL );
pTd->pAfd = pTdTdi;
/*
* Initialize TDTDI data structure
*/
RtlZeroMemory( pTdTdi, sizeof(*pTdTdi) );
/*
* Some protocols will make decisions on lower level
* flow control depending on this value.
*/
pTdTdi->OutBufDelay = pSdOpen->PdConfig.Create.OutBufDelay;
/*
* Open device
*/
Status = TdiDeviceOpen( pTd, pSdOpen );
if ( !NT_SUCCESS(Status) )
goto badopen;
TRACE0(("DeviceOpen: Context 0x%x\n",pTd->pAfd));
return STATUS_SUCCESS;
/*=============================================================================
== Error returns
=============================================================================*/
/*
* open failed
*/
badopen:
MemoryFree( pTd->pAfd );
pTd->pAfd = NULL;
/*
* allocate failed
*/
badalloc:
return Status;
}
/*******************************************************************************
* DeviceClose
*
* Close transport driver
* NOTE: this must not close the current connection endpoint
*
* pTd (input)
* Pointer to TD data structure
* pSdClose (input/output)
* Points to the parameter structure SD_CLOSE.
******************************************************************************/
NTSTATUS DeviceClose(PTD pTd, PSD_CLOSE pSdClose)
{
PTDTDI pTdTdi;
PTD_ENDPOINT pEndpoint;
DBGENTER(("DeviceClose: PTD 0x%x Context 0x%x\n",pTd,pTd->pAfd));
/*
* Get pointer to TDI parameters
*/
pTdTdi = (PTDTDI) pTd->pAfd;
/*
* Close address endpoint if we have one
*/
if (pTdTdi != NULL) {
if ( pEndpoint = pTdTdi->pAddressEndpoint ) {
TRACE0(("DeviceClose: Closing AddressEndpoint 0x%x\n",pTdTdi->pAddressEndpoint));
pTdTdi->pAddressEndpoint = NULL;
_TdCloseEndpoint( pTd, pEndpoint );
}
#if DBG
if( pEndpoint = pTdTdi->pConnectionEndpoint ) {
ASSERT( IsListEmpty( &pEndpoint->ReceiveQueue) );
TRACE0(("DeviceClose: Connection Endpoint 0x%x idled\n",pEndpoint));
}
#endif
}
/*
* Close device
*/
(void)TdiDeviceClose(pTd, pSdClose);
// TdUnload in td\common will free pTd->pAfd
return STATUS_SUCCESS;
}
/*******************************************************************************
* DeviceCreateEndpoint
*
* Create a TDI address object. Do not wait for, or make any connections.
*
* pTd (input)
* Pointer to TD data structure
* pLocalAddress (input)
* Pointer to local address (or null)
* pReturnedAddress (input)
* Pointer to location to save returned (created) address (or null)
******************************************************************************/
NTSTATUS DeviceCreateEndpoint(
PTD pTd,
PICA_STACK_ADDRESS pLocalAddress,
PICA_STACK_ADDRESS pReturnedAddress)
{
PTDTDI pTdTdi;
NTSTATUS Status;
UNICODE_STRING TransportName;
ULONG TransportAddressLength;
PTD_ENDPOINT pEndpoint = NULL;
PTRANSPORT_ADDRESS pTransportAddress = NULL;
DBGENTER(("DeviceCreateEndpoint: PTD 0x%x\n",pTd));
/*
* Get pointer to TDI parameters
*/
pTdTdi = (PTDTDI) pTd->pAfd;
/*
* Build transport device name and address. This is in the TD.
*/
Status = TdiDeviceBuildTransportNameAndAddress( pTd, pLocalAddress,
&TransportName,
&pTransportAddress,
&TransportAddressLength );
if ( !NT_SUCCESS( Status ) ) {
DBGPRINT(("DeviceCreateEndpoint: Error building address 0x%x\n",Status));
goto badaddress;
}
/*
* Create the Endpoint structure.
*/
Status = _TdCreateEndpointStruct(
pTd,
&TransportName,
&pEndpoint,
pTransportAddress,
TransportAddressLength
);
if ( !NT_SUCCESS( Status ) ) {
DBGPRINT(("DeviceCreateEndpoint: Error creating endpointstruct 0x%x\n",Status));
goto badcreate;
}
pEndpoint->EndpointType = TdiAddressObject;
pEndpoint->TransportHandleProcess = IoGetCurrentProcess();
/*
* Create the TDI address object.
*/
Status = _TdiCreateAddress(
&pEndpoint->TransportName,
pEndpoint->pTransportAddress,
pEndpoint->TransportAddressLength,
&pEndpoint->TransportHandle,
&pEndpoint->pFileObject,
&pEndpoint->pDeviceObject
);
if( !NT_SUCCESS(Status) ) {
DBGPRINT(("DeviceCreateEndpoint: Error creating TDI address object 0x%x\n",Status));
_TdCloseEndpoint( pTd, pEndpoint );
goto badcreate;
}
if ( pReturnedAddress ) {
DBGPRINT(("DeviceCreateEndpoint: Address returned Type 0x%x\n",pTransportAddress->Address[0].AddressType));
RtlCopyMemory( pReturnedAddress,
&pTransportAddress->Address[0].AddressType,
min( sizeof( *pReturnedAddress ),
pEndpoint->TransportAddressLength ) );
}
/*
* Save a pointer to the address endpoint
*/
pTdTdi->pAddressEndpoint = pEndpoint;
/*
* Free transport name and address buffers
*/
MemoryFree( TransportName.Buffer );
MemoryFree( pTransportAddress );
TRACE0(("DeviceCreateEndPoint: AddressEndpoint 0x%x Created, FO 0x%x DO 0x%x, Handle 0x%x\n",pEndpoint,pEndpoint->pFileObject,pEndpoint->pDeviceObject,pEndpoint->TransportHandle));
return( STATUS_SUCCESS );
/*=============================================================================
== Error returns
=============================================================================*/
badcreate:
if ( TransportName.Buffer )
MemoryFree( TransportName.Buffer );
if ( pTransportAddress )
MemoryFree( pTransportAddress );
badaddress:
return( Status );
}
/*******************************************************************************
* DeviceOpenEndpoint
*
* Causes an existing end point to copy its data into a new one.
* The handle is passed in from TermSrv, and was returned to it at one time
* from DeviceConnectionWait().
* NOTE: TermSrv can call this multiple times with the same handle for
* multiple connects/disconnects.
*
* pTd (input)
* Pointer to TD data structure
* pIcaEndpoint (input)
* Pointer to ICA endpoint structure
* IcaEndpointLength (input)
* length of endpoint data
******************************************************************************/
NTSTATUS DeviceOpenEndpoint(
PTD pTd,
PVOID pIcaEndpoint,
ULONG IcaEndpointLength)
{
PTDTDI pTdTdi;
PTD_STACK_ENDPOINT pStackEndpoint;
PVOID Handle;
ULONG Length;
NTSTATUS Status;
DBGENTER(("DeviceOpenEndpoint: PTD 0x%x\n",pTd));
/*
* Get pointer to TDI parameters
*/
pTdTdi = (PTDTDI) pTd->pAfd;
TRACE(( pTd->pContext, TC_TD, TT_API2,
"TDTDI: DeviceOpenEndpoint, copying existing endpoint\n" ));
if( IcaEndpointLength < sizeof(PVOID) ) {
DBGPRINT(("DeviceOpenEndpoint: IcaEndpointLength to small %d\n",IcaEndpointLength));
Status = STATUS_INVALID_HANDLE;
goto done;
}
/*
* Capture the parameter
*/
try {
ProbeForRead( pIcaEndpoint, sizeof(PVOID), 1 );
Handle = (*((PVOID *)pIcaEndpoint));
} except ( EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
DBGPRINT(("DeviceOpenEndpoint: Exception 0x%x\n",Status));
goto done;
}
TRACE0(("DeviceOpenEndpoint: Fetching Handle 0x%x\n",Handle));
/*
* See if ICADD knows about the handle
*/
Status = IcaReturnHandle( Handle, &pStackEndpoint, &Length );
if( !NT_SUCCESS(Status) ) {
DBGPRINT(("DeviceOpenEndpoint: ICADD handle 0x%x no good 0x%x\n",Handle,Status));
Status = STATUS_INVALID_HANDLE;
goto done;
}
if( Length != sizeof(TD_STACK_ENDPOINT) ) {
#if DBG
DBGPRINT(("DeviceOpenEndpoint: Bad TD_STACK_ENDPOINT length %d, sb %d\n",Length,sizeof(TD_STACK_ENDPOINT)));
DbgBreakPoint(); // Internal corruption
#endif
Status = STATUS_INVALID_HANDLE;
goto done;
}
ASSERT( pStackEndpoint->AddressType == TdiDeviceAddressType );
ASSERT( pStackEndpoint->pEndpoint->hIcaHandle == Handle );
/*
* Save endpoint as the current connection endpoint
*/
pTdTdi->pConnectionEndpoint = pStackEndpoint->pEndpoint;
ASSERT( IsListEmpty( &pTdTdi->pConnectionEndpoint->ReceiveQueue) );
TRACE0(("DeviceOpenEndpoint: Returned Endpoint 0x%x\n",pStackEndpoint->pEndpoint));
/*
* Save the file/device objects used for I/O in the TD structure
*/
pTd->pFileObject = pTdTdi->pConnectionEndpoint->pFileObject;
pTd->pDeviceObject = pTdTdi->pConnectionEndpoint->pDeviceObject;
TRACE0(("DeviceOpenEndpoint: Connection Endpoint 0x%x opened on Context 0x%x\n",pTdTdi->pConnectionEndpoint,pTd->pAfd));
TRACE0(("FO 0x%x, DO 0x%x, Handle 0x%x\n",pTdTdi->pConnectionEndpoint->pFileObject,pTdTdi->pConnectionEndpoint->pDeviceObject,pTdTdi->pConnectionEndpoint->TransportHandle));
Status = STATUS_SUCCESS;
if ( NT_SUCCESS(Status) ) {
Status = TdiDeviceOpenEndpoint( pTd, pIcaEndpoint, IcaEndpointLength );
}
done:
return( Status );
}
/*******************************************************************************
* DeviceCloseEndpoint
******************************************************************************/
NTSTATUS DeviceCloseEndpoint(PTD pTd)
{
ULONG Length;
KIRQL OldIrql;
PTDTDI pTdTdi;
NTSTATUS Status;
PTD_ENDPOINT pEndpoint;
PTD_STACK_ENDPOINT pStackEndpoint;
DBGENTER(("DeviceCloseEndpoint: PTD 0x%x, Context 0x%x\n",pTd,pTd->pAfd));
/*
* Get pointer to TDI parameters
*/
pTdTdi = (PTDTDI) pTd->pAfd;
/*
* Close connection endpoint if we have one
* NOTE: The address endpoint, if there is one,
* gets closed in the DeviceClose routine.
*/
if ( pEndpoint = pTdTdi->pConnectionEndpoint ) {
TRACE0(("DeviceCloseEndpoint: Closing Connection Endpoint 0x%x, on Context 0x%x\n",pEndpoint,pTd->pAfd));
ASSERT( pEndpoint->EndpointType != TdiAddressObject );
ExAcquireSpinLock( &pEndpoint->Spinlock, &OldIrql );
pEndpoint->Disconnected = TRUE;
ExReleaseSpinLock( &pEndpoint->Spinlock, OldIrql );
/*
* Cancel any pended receives
*/
_TdCancelReceiveQueue(pTd, pEndpoint, STATUS_LOCAL_DISCONNECT );
pTd->pFileObject = NULL;
pTd->pDeviceObject = NULL;
pTdTdi->pConnectionEndpoint = NULL;
// If a handle registered with ICADD, close it
if( pEndpoint->hIcaHandle ) {
Status = IcaCloseHandle( pEndpoint->hIcaHandle, &pStackEndpoint, &Length );
if( NT_SUCCESS(Status) ) {
ASSERT( pStackEndpoint->pEndpoint == pEndpoint );
/*
* Release our context memory
*/
MemoryFree( pStackEndpoint );
}
else {
DBGPRINT(("DeviceCloseEndpoint: hIcaDevice 0x%x Invalid!\n",pEndpoint->hIcaHandle));
#if DBG
DbgBreakPoint();
#endif
}
}
_TdCloseEndpoint( pTd, pEndpoint );
}
return( STATUS_SUCCESS );
}
/*******************************************************************************
* DeviceConnectionWait
*
* This function is called in a loop from the upper level. We must create
* a connection object, associate it with the address object, listen on it,
* and return a single connection to our caller. We are called again for
* more connections.
* NOTE: The endpoint structure is an opaque, variable length data
* structure whose length and contents are determined by the
* transport driver.
*
* ENTRY:
* pTd (input)
* Pointer to TD data structure
* pIcaEndpoint (output)
* Points to a buffer to receive the current endpoint
* Length (input)
* Length of the buffer pointed to by pIcaEndpoint
* BytesReturned (output)
* Points to the actual number of bytes written to pIcaEndpoint
*
* EXIT:
* STATUS_SUCCESS - no error
* STATUS_BUFFER_TOO_SMALL - endpoint buffer is too small
******************************************************************************/
NTSTATUS DeviceConnectionWait(
PTD pTd,
PVOID pIcaEndpoint,
ULONG Length,
PULONG BytesReturned)
{
PTDTDI pTdTdi;
NTSTATUS Status;
KIRQL OldIrql;
PLIST_ENTRY pEntry;
PVOID Handle;
PTD_ENDPOINT pAddressEndpoint;
PTD_ENDPOINT pConnectionEndpoint = NULL;
PTD_STACK_ENDPOINT pStackEndpoint = NULL;
DBGENTER(("DeviceConnectionWait: PTD 0x%x\n",pTd));
/*
* Get pointer to TDI parameters
*/
pTdTdi = (PTDTDI) pTd->pAfd;
if (pTd->fClosing) {
return STATUS_DEVICE_NOT_READY;
}
/*
* Initialize return buffer size
*/
*BytesReturned = sizeof(TD_STACK_ENDPOINT);
/*
* Verify output endpoint buffer is large enough
*/
if ( Length < sizeof(TD_STACK_ENDPOINT) ) {
Status = STATUS_BUFFER_TOO_SMALL;
DBGPRINT(("DeviceConnectionWait: Output buffer to small\n"));
goto done;
}
/*
* Ensure we have a TDI address endpoint already
*/
if ( (pAddressEndpoint = pTdTdi->pAddressEndpoint) == NULL ) {
Status = STATUS_DEVICE_NOT_READY;
DBGPRINT(("DeviceConnectionWait: No TDI address object\n"));
goto done;
}
/*
* Different handling for datagram connections
*/
if (TdiDeviceEndpointType == TdiConnectionDatagram) {
Status = _TdWaitForDatagramConnection(
pTd,
pAddressEndpoint,
&pConnectionEndpoint);
if (!NT_SUCCESS(Status)) {
DBGPRINT(("DeviceConnectionWait: Error Status 0x%x from "
"_TdWaitForDatagramConnection\n", Status));
return Status;
}
goto ConnectAccepted;
}
ExAcquireSpinLock(&pAddressEndpoint->Spinlock, &OldIrql);
/*
* Data receive indication must be registered on the
* address endpoint before any data endpoints are created.
*
* This is because we can not set receive indication on a
* dataendpoint, it can only be inherited from its
* address endpoint.
*/
if (!pAddressEndpoint->RecvIndicationRegistered) {
pAddressEndpoint->RecvIndicationRegistered = TRUE;
ExReleaseSpinLock(&pAddressEndpoint->Spinlock, OldIrql);
/*
* Register the receive event handler
*/
Status = _TdiSetEventHandler(
pTd,
pAddressEndpoint->pDeviceObject,
pAddressEndpoint->pFileObject,
TDI_EVENT_RECEIVE,
(PVOID)_TdReceiveHandler,
(PVOID)pAddressEndpoint // Context
);
ASSERT( NT_SUCCESS(Status) );
pAddressEndpoint->DisconnectIndicationRegistered = TRUE;
/*
* Register the disconnect event handler
*/
Status = _TdiSetEventHandler(
pTd,
pAddressEndpoint->pDeviceObject,
pAddressEndpoint->pFileObject,
TDI_EVENT_DISCONNECT,
(PVOID)_TdDisconnectHandler,
(PVOID)pAddressEndpoint // Context
);
ASSERT( NT_SUCCESS(Status) );
ExAcquireSpinLock( &pAddressEndpoint->Spinlock, &OldIrql );
}
/*
* Everytime into this function, we attempt to create more
* Connection object's util we have reached TDTDI_LISTEN_QUEUE_DEPTH.
*
* These connection objects are linked off of our address endpoint.
*
* This is because we can only create connection objects
* at call level, not at indication level. The indication level
* will grab connection objects off of the address endpoint,
* and accept them. It will then set the accept event on the
* address object. This is optimized for only (1) thread
* accepting connections, which is the current TD design.
* Otherwise, thundering herd could occur on the accept event.
*
* This function then returns with the accepted connection
* object. The upper level listen thread will then call
* this function again to retrieve another connection.
*
* This prevents the refusing of connections due to not
* having any outstanding listen's available when a WinFrame
* client connect request comes in.
*/
while (pAddressEndpoint->ConnectionQueueSize <= TDTDI_LISTEN_QUEUE_DEPTH) {
ExReleaseSpinLock( &pAddressEndpoint->Spinlock, OldIrql );
Status = _TdCreateConnectionObject(
pTd,
&pAddressEndpoint->TransportName,
&pConnectionEndpoint,
pAddressEndpoint->pTransportAddress,
pAddressEndpoint->TransportAddressLength
);
ExAcquireSpinLock( &pAddressEndpoint->Spinlock, &OldIrql );
if( !NT_SUCCESS(Status) ) {
DBGPRINT(("DeviceConnectionWait: Error 0x%x Creating ConnectionObject\n",Status));
break;
}
ASSERT( pConnectionEndpoint->Connected == FALSE );
InsertTailList( &pAddressEndpoint->ConnectionQueue, &pConnectionEndpoint->ConnectionLink );
pAddressEndpoint->ConnectionQueueSize++;
}
/*
* If we have not registered our Connect Indication handler
* yet, do it now. We had to delay it until connection objects
* were created and ready.
*/
if (!pAddressEndpoint->ConnectIndicationRegistered) {
pTdTdi->pAddressEndpoint->ConnectIndicationRegistered = TRUE;
ASSERT( !IsListEmpty( &pAddressEndpoint->ConnectionQueue ) );
ExReleaseSpinLock( &pAddressEndpoint->Spinlock, OldIrql );
/*
* Register to receive connect indications
*
* *** Note that Connect events can be delivered IMMEDIATELY upon
* completion of this request!
*/
Status = _TdiSetEventHandler(
pTd,
pAddressEndpoint->pDeviceObject,
pAddressEndpoint->pFileObject,
TDI_EVENT_CONNECT,
(PVOID)_TdConnectHandler,
(PVOID)pAddressEndpoint // Context
);
if (!NT_SUCCESS(Status)) {
DBGPRINT(("DeviceConnectionWait: Error 0x%x Setting TdiConnectHandler\n",Status));
ExAcquireSpinLock( &pAddressEndpoint->Spinlock, &OldIrql );
pTdTdi->pAddressEndpoint->ConnectIndicationRegistered = FALSE;
ExReleaseSpinLock( &pAddressEndpoint->Spinlock, OldIrql );
goto done;
}
ExAcquireSpinLock( &pAddressEndpoint->Spinlock, &OldIrql );
}
/*
* While holding the spinlock, see if any connected objects
* are on the connected queue.
*/
while (IsListEmpty( &pAddressEndpoint->ConnectedQueue)) {
KeResetEvent( &pAddressEndpoint->AcceptEvent );
ASSERT( pAddressEndpoint->Waiter == FALSE );
pAddressEndpoint->Waiter = TRUE;
ExReleaseSpinLock( &pAddressEndpoint->Spinlock, OldIrql );
Status = IcaWaitForSingleObject(
pTd->pContext,
&pAddressEndpoint->AcceptEvent,
(-1) // No timeout
);
ExAcquireSpinLock( &pAddressEndpoint->Spinlock, &OldIrql );
ASSERT( pAddressEndpoint->Waiter == TRUE );
pAddressEndpoint->Waiter = FALSE;
/*
* Wait failure, could be due to thread receiving APC.
*/
if( Status != STATUS_SUCCESS ) {
DBGPRINT(("DeviceConnectionWait: Thread wait interrupted! Status 0x%x\n",Status));
ExReleaseSpinLock( &pAddressEndpoint->Spinlock, OldIrql );
return( Status );
}
if( pTd->fClosing ) {
DBGPRINT(("DeviceConnectionWait: TD is Closing!\n"));
ExReleaseSpinLock( &pAddressEndpoint->Spinlock, OldIrql );
return( STATUS_CTX_CLOSE_PENDING );
}
// Should only be (1) accept thread in the TD.
ASSERT( !IsListEmpty(&pAddressEndpoint->ConnectedQueue) );
}
/*
* Dequeue the connected connection object
*/
pEntry = RemoveHeadList( &pAddressEndpoint->ConnectedQueue );
pAddressEndpoint->ConnectionQueueSize--;
pConnectionEndpoint = CONTAINING_RECORD( pEntry, TD_ENDPOINT, ConnectionLink );
ASSERT( pConnectionEndpoint->Connected == TRUE );
/*
* There could have been a final phase accept error, or
* the remote side dropped the connection right away.
*
* In this case, we must tear down the errored connection.
*/
if (!NT_SUCCESS(pConnectionEndpoint->Status)) {
Status = pConnectionEndpoint->Status;
DBGPRINT(("DeviceConnectionWait: Accept indication failed, Status 0x%x\n",Status));
ExReleaseSpinLock( &pAddressEndpoint->Spinlock, OldIrql );
_TdCloseEndpoint( pTd, pConnectionEndpoint );
return( Status );
}
ExReleaseSpinLock( &pAddressEndpoint->Spinlock, OldIrql );
ConnectAccepted:
/*
* Allocate a context structure and register our endpoint as
* a handle with ICADD. The handle returned by ICADD will then
* be placed into the user mode callers buffer as the endpoint
* handle.
*
* A later call to DeviceOpenEndpoint() will validate this handle,
* retreive the context, and allow use of the endpoint.
*/
Status = MemoryAllocate( sizeof(TD_STACK_ENDPOINT), &pStackEndpoint );
if( !NT_SUCCESS(Status) ) {
DBGPRINT(("DeviceConnectionWait: Could not allocate memory 0x%x\n",Status));
_TdCloseEndpoint( pTd, pConnectionEndpoint );
return( Status );
}
pStackEndpoint->AddressType = TdiDeviceAddressType;
pStackEndpoint->pEndpoint = pConnectionEndpoint;
Status = IcaCreateHandle( (PVOID)pStackEndpoint, sizeof(TD_STACK_ENDPOINT), &Handle );
if( !NT_SUCCESS(Status) ) {
DBGPRINT(("DeviceConnectionWait: Error creating ICADD handle 0x%x\n",Status));
MemoryFree( pStackEndpoint );
_TdCloseEndpoint( pTd, pConnectionEndpoint );
return( Status );
}
Status = STATUS_SUCCESS;
/*
* Fill in the stack endpoint structure to be returned
*/
try {
ProbeForWrite( pIcaEndpoint, sizeof(PVOID), 1 );
*((PVOID *)pIcaEndpoint) = Handle;
*BytesReturned = sizeof(PVOID);
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
DBGPRINT(("DeviceConnectionWait: Exception returning result 0x%x\n",Status));
}
// Exception
if( !NT_SUCCESS(Status) ) {
ULONG Length;
NTSTATUS Status2;
DBGPRINT(("DeviceConnectionWait: Exception returning result 0x%x\n",Status));
Status2 = IcaCloseHandle( Handle, &pStackEndpoint, &Length );
if( NT_SUCCESS(Status2) ) {
MemoryFree( pStackEndpoint );
}
_TdCloseEndpoint( pTd, pConnectionEndpoint );
return( Status );
}
pConnectionEndpoint->hIcaHandle = Handle;
TRACE0(("DeviceConnectionWait: New Connection Endpoint 0x%x Returned on Context 0x%x, AddressEndpoint 0x%x\n",pConnectionEndpoint,pTd->pAfd,pAddressEndpoint));
TRACE0(("FO 0x%x, DO 0x%x, Handle 0x%x\n",pConnectionEndpoint->pFileObject,pConnectionEndpoint->pDeviceObject,pConnectionEndpoint->TransportHandle));
done:
return Status;
}
/*******************************************************************************
* DeviceConnectionSend
*
* Initialize host module data structure, which gets sent to the client.
******************************************************************************/
NTSTATUS DeviceConnectionSend(PTD pTd)
{
return TdiDeviceConnectionSend(pTd);
}
/*******************************************************************************
* DeviceConnectionRequest
*
* Initiate a connection to the specified remote address
*
* ENTRY:
* pTd (input)
* Pointer to TD data structure
* pRemoteAddress (input)
* Pointer to remote address to connect to
* pIcaEndpoint (output)
* Points to a buffer to receive the current endpoint
* Length (input)
* Length of the buffer pointed to by pIcaEndpoint
* BytesReturned (output)
* Pointer to location to return length of pIcaEndpoint
*
* EXIT:
* STATUS_SUCCESS - no error
* STATUS_BUFFER_TOO_SMALL - endpoint buffer is too small
******************************************************************************/
NTSTATUS DeviceConnectionRequest(
PTD pTd,
PICA_STACK_ADDRESS pRemoteAddress,
PVOID pIcaEndpoint,
ULONG Length,
PULONG BytesReturned)
{
PTDTDI pTdTdi;
NTSTATUS Status;
KIRQL OldIrql;
PLIST_ENTRY pEntry;
PVOID Handle;
PTD_ENDPOINT pAddressEndpoint;
PTD_ENDPOINT pConnectionEndpoint = NULL;
PTD_STACK_ENDPOINT pStackEndpoint = NULL;
ICA_STACK_ADDRESS LocalAddress;
PICA_STACK_ADDRESS pLocalAddress = &LocalAddress;
UNICODE_STRING TransportName;
ULONG TransportAddressLength;
PTD_ENDPOINT pEndpoint = NULL;
PTRANSPORT_ADDRESS pTransportAddress = NULL;
UNICODE_STRING RemoteTransportName;
ULONG RemoteTransportAddressLength;
PTRANSPORT_ADDRESS pRemoteTransportAddress = NULL;
ULONG timeout;
LARGE_INTEGER WaitTimeout;
PLARGE_INTEGER pWaitTimeout = NULL;
PTDI_ADDRESS_IP pTdiAddress;
#if DBG
PTDI_ADDRESS_INFO pTdiLocalAddressInfo;
ULONG LocalAddressInfoLength;
#endif
DBGENTER(("DeviceConnectionRequest: PTD 0x%x\n",pTd));
//
// This part is from the above DeviceConnectionwait:
//
if (pRemoteAddress == NULL) {
Status = STATUS_INVALID_PARAMETER;
goto done;
}
/*
* Get pointer to TDI parameters
*/
pTdTdi = (PTDTDI) pTd->pAfd;
if (pTd->fClosing) {
return STATUS_DEVICE_NOT_READY;
}
/*
* Initialize return buffer size
*/
*BytesReturned = sizeof(TD_STACK_ENDPOINT);
/*
* Verify output endpoint buffer is large enough
*/
if ( Length < sizeof(TD_STACK_ENDPOINT) ) {
Status = STATUS_BUFFER_TOO_SMALL;
DBGPRINT(("DeviceConnectionRequest: Output buffer to small\n"));
goto done;
}
/*
* Different handling for datagram connections
*/
if (TdiDeviceEndpointType == TdiConnectionDatagram) {
Status = STATUS_NOT_SUPPORTED;
goto done;
}
//
// Extract timeout value and reset to NULL, will require adding timeout
// into ICA_STACK_ADDRESS, too riskly for Whistler.
//
pTdiAddress = (PTDI_ADDRESS_IP) ((PCHAR)pRemoteAddress + 2);
RtlCopyMemory( &timeout, &pTdiAddress->sin_zero[0], sizeof(timeout) );
RtlZeroMemory( &pTdiAddress->sin_zero[0], sizeof(timeout) );
//
// Build the remote address
//
DBGPRINT(("TDTCP:DeviceConnectionRequest: building REMOTE address ...\n"));
DBGPRINT(("TDTCP:DeviceConnectionRequest: Timeout %d\n", timeout));
Status = TdiDeviceBuildTransportNameAndAddress( pTd, pRemoteAddress,
&RemoteTransportName,
&pRemoteTransportAddress,
&RemoteTransportAddressLength );
if ( !NT_SUCCESS( Status ) ) {
DBGPRINT(("DeviceConnectionRequest: Error building address 0x%x\n",Status));
goto badaddress;
}
MemoryFree( RemoteTransportName.Buffer ); // not used, should make it optional in the call above
RemoteTransportName.Buffer = NULL;
/*
* Build transport device name and address. This is in the TD.
*/
DBGPRINT(("TDTCP:DeviceConnectionRequest: building LOCAL address ...\n"));
//
// We build a wild card local address let tcpip driver pick up port and NIC card.
//
RtlZeroMemory(pLocalAddress, sizeof(LocalAddress));
*(PUSHORT)pLocalAddress = TDI_ADDRESS_TYPE_IP;
Status = TdiDeviceBuildTransportNameAndAddress( pTd, pLocalAddress,
&TransportName,
&pTransportAddress,
&TransportAddressLength );
if ( !NT_SUCCESS( Status ) ) {
DBGPRINT(("DeviceConnectionRequest: Error building address 0x%x\n",Status));
goto badaddress;
}
/*
* Create the Endpoint structure.
*/
Status = _TdCreateEndpointStruct(
pTd,
&TransportName,
&pEndpoint,
pTransportAddress,
TransportAddressLength
);
if ( !NT_SUCCESS( Status ) ) {
DBGPRINT(("DeviceConnectionRequest: Error creating endpointstruct 0x%x\n",Status));
goto badcreate;
}
pEndpoint->EndpointType = TdiAddressObject;
pEndpoint->TransportHandleProcess = IoGetCurrentProcess();
/*
* Create the TDI address object.
*/
Status = _TdiCreateAddress(
&pEndpoint->TransportName,
pEndpoint->pTransportAddress,
pEndpoint->TransportAddressLength,
&pEndpoint->TransportHandle,
&pEndpoint->pFileObject,
&pEndpoint->pDeviceObject
);
if( !NT_SUCCESS(Status) ) {
DBGPRINT(("DeviceConnectionRequest: Error creating TDI address object 0x%x\n",Status));
_TdCloseEndpoint( pTd, pEndpoint );
goto badcreate;
}
/*
* Save a pointer to the address endpoint
*/
pTdTdi->pAddressEndpoint = pEndpoint;
pAddressEndpoint = pTdTdi->pAddressEndpoint;
/*
* Free transport name and address buffers
*/
MemoryFree( TransportName.Buffer );
TransportName.Buffer = NULL;
MemoryFree( pTransportAddress );
pTransportAddress = NULL;
//*************************************************************
ExAcquireSpinLock(&pAddressEndpoint->Spinlock, &OldIrql);
/*
* Data receive indication must be registered on the
* address endpoint before any data endpoints are created.
*
* This is because we can not set receive indication on a
* dataendpoint, it can only be inherited from its
* address endpoint.
*/
if (!pAddressEndpoint->RecvIndicationRegistered) {
pAddressEndpoint->RecvIndicationRegistered = TRUE;
ExReleaseSpinLock(&pAddressEndpoint->Spinlock, OldIrql);
/*
* Register the receive event handler
*/
Status = _TdiSetEventHandler(
pTd,
pAddressEndpoint->pDeviceObject,
pAddressEndpoint->pFileObject,
TDI_EVENT_RECEIVE,
(PVOID)_TdReceiveHandler,
(PVOID)pAddressEndpoint // Context
);
ASSERT( NT_SUCCESS(Status) );
if( !NT_SUCCESS(Status) )
{
// Already released the spin lock
DBGPRINT(("DeviceConnectionRequest: failed to _TdiSetEventHandler on TDI_EVENT_RECEIVE 0x%x\n",Status));
goto badconnect1;
}
pAddressEndpoint->DisconnectIndicationRegistered = TRUE;
/*
* Register the disconnect event handler
*/
Status = _TdiSetEventHandler(
pTd,
pAddressEndpoint->pDeviceObject,
pAddressEndpoint->pFileObject,
TDI_EVENT_DISCONNECT,
(PVOID)_TdDisconnectHandler,
(PVOID)pAddressEndpoint // Context
);
ASSERT( NT_SUCCESS(Status) );
if( !NT_SUCCESS(Status) )
{
// Already released the spin lock
DBGPRINT(("DeviceConnectionRequest: failed to _TdiSetEventHandler on TDI_EVENT_DISCONNECT 0x%x\n",Status));
goto badconnect1;
}
}
else {
ExReleaseSpinLock( &pAddressEndpoint->Spinlock, OldIrql );
}
// now create a TDI connection object
Status = _TdCreateConnectionObject(
pTd,
&pAddressEndpoint->TransportName,
&pConnectionEndpoint,
pAddressEndpoint->pTransportAddress,
pAddressEndpoint->TransportAddressLength
);
if ( !NT_SUCCESS(Status) ) {
DBGPRINT(("DeviceConnectionRequest: failed to create ConnectionObject 0x%x\n",Status));
goto badconnect1;
}
if( 0 != timeout ) {
WaitTimeout = RtlEnlargedIntegerMultiply( timeout * 1000, -10000 );
pWaitTimeout = &WaitTimeout;
}
pTdTdi->pConnectionEndpoint = pConnectionEndpoint;
Status = _TdiConnect( pTd,
NULL, // will allocate the IRP internally
pWaitTimeout,
pConnectionEndpoint->pFileObject,
pConnectionEndpoint->pDeviceObject,
RemoteTransportAddressLength,
pRemoteTransportAddress
);
if ( !NT_SUCCESS(Status) ) {
DBGPRINT(("DeviceConnectionRequest: failed to connect 0x%x\n",Status));
goto badconnect;
}
//
// signal accept event, connect logic don't depend on it.
//
KeSetEvent( &pAddressEndpoint->AcceptEvent, IO_NETWORK_INCREMENT, FALSE );
MemoryFree( pRemoteTransportAddress );
pRemoteTransportAddress = NULL;
#if DBG
//
// Query local address use to connect.
//
LocalAddressInfoLength = pAddressEndpoint->TransportAddressLength+4;
Status = MemoryAllocate( LocalAddressInfoLength, &pTdiLocalAddressInfo );
if ( NT_SUCCESS( Status ) ) {
Status = _TdiQueryAddressInfo(
pTd,
NULL,
pConnectionEndpoint->pFileObject,
pConnectionEndpoint->pDeviceObject,
pTdiLocalAddressInfo,
LocalAddressInfoLength
);
if( NT_SUCCESS(Status) )
{
int i;
TA_ADDRESS* pTdAddress;
TDI_ADDRESS_IP* pTdiAddressIp;
pTdAddress = &pTdiLocalAddressInfo->Address.Address[0];
DBGPRINT( ("number of local address %d\n", pTdiLocalAddressInfo->Address.TAAddressCount) );
for( i=0; i < pTdiLocalAddressInfo->Address.TAAddressCount; i++ )
{
DBGPRINT( (" Address Type %d\n", pTdAddress->AddressType) );
if( TDI_ADDRESS_TYPE_IP == pTdAddress->AddressType )
{
pTdiAddressIp = (TDI_ADDRESS_IP *)&pTdAddress->Address[0];
DBGPRINT((" Port %x\n", pTdiAddressIp->sin_port) );
DBGPRINT((" IP %u.%u.%u.%u\n",
(pTdiAddressIp->in_addr & 0xff000000) >> 24,
(pTdiAddressIp->in_addr & 0x00ff0000) >> 16,
(pTdiAddressIp->in_addr & 0x0000ff00) >> 8,
(pTdiAddressIp->in_addr & 0x000000ff) ));
}
pTdAddress++;
}
}
MemoryFree( pTdiLocalAddressInfo );
}
#endif
//**********************************************************************************
/*
* Allocate a context structure and register our endpoint as
* a handle with ICADD. The handle returned by ICADD will then
* be placed into the user mode callers buffer as the endpoint
* handle.
*/
Status = MemoryAllocate( sizeof(TD_STACK_ENDPOINT), &pStackEndpoint );
if( !NT_SUCCESS(Status) ) {
DBGPRINT(("DeviceConnectionRequest: Could not allocate memory 0x%x\n",Status));
goto badconnect;
}
pStackEndpoint->AddressType = TdiDeviceAddressType;
pStackEndpoint->pEndpoint = pConnectionEndpoint;
Status = IcaCreateHandle( (PVOID)pStackEndpoint, sizeof(TD_STACK_ENDPOINT), &Handle );
if( !NT_SUCCESS(Status) ) {
DBGPRINT(("DeviceConnectionRequest: Error creating ICADD handle 0x%x\n",Status));
MemoryFree( pStackEndpoint );
goto badconnect;
}
Status = STATUS_SUCCESS;
/*
* Fill in the stack endpoint structure to be returned
*/
try {
ProbeForWrite( pIcaEndpoint, sizeof(PVOID), 1 );
*((PVOID *)pIcaEndpoint) = Handle;
*BytesReturned = sizeof(PVOID);
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
DBGPRINT(("DeviceConnectionRequest: Exception returning result 0x%x\n",Status));
}
// Exception
if( !NT_SUCCESS(Status) ) {
goto badsetup;
}
pConnectionEndpoint->hIcaHandle = Handle;
/*
* Save the file/device objects used for I/O in the TD structure
*/
pTd->pFileObject = pTdTdi->pConnectionEndpoint->pFileObject;
pTd->pDeviceObject = pTdTdi->pConnectionEndpoint->pDeviceObject;
TRACE0(("DeviceConnectionRequest: New Connection Endpoint 0x%x Returned on Context 0x%x, AddressEndpoint 0x%x\n",pConnectionEndpoint,pTd->pAfd,pAddressEndpoint));
TRACE0(("FO 0x%x, DO 0x%x, Handle 0x%x\n",pConnectionEndpoint->pFileObject,pConnectionEndpoint->pDeviceObject,pConnectionEndpoint->TransportHandle));
//**********************************************************************************
// should be a success
return Status;
/*=============================================================================
== Error returns
=============================================================================*/
badsetup:
{
ULONG Length;
NTSTATUS Status2;
DBGPRINT(("DeviceConnectionRequest: Exception returning result 0x%x\n",Status));
Status2 = IcaCloseHandle( Handle, &pStackEndpoint, &Length );
if( NT_SUCCESS(Status2) ) {
MemoryFree( pStackEndpoint );
}
}
badconnect:
_TdCloseEndpoint(pTd, pConnectionEndpoint);
pTdTdi->pConnectionEndpoint = NULL;
badconnect1:
//
// It is imperative that we do not close address end point,
// We will close address end point on next IOCTL call which triggle
// Closing of address end point, if we do it here, we will end up
// double free and bug check.
// _TdCloseEndpoint(pTd, pAddressEndpoint);
badcreate:
if ( TransportName.Buffer )
MemoryFree( TransportName.Buffer );
if ( pTransportAddress )
MemoryFree( pTransportAddress );
if ( RemoteTransportName.Buffer )
MemoryFree( RemoteTransportName.Buffer );
if ( pRemoteTransportAddress )
MemoryFree( pRemoteTransportAddress );
badaddress:
done:
return Status;
}
/*******************************************************************************
* DeviceIoctl
*
* Query/Set configuration information for the td.
*
* pTd (input)
* Pointer to TD data structure
* pSdIoctl (input/output)
* Points to the parameter structure SD_IOCTL
******************************************************************************/
NTSTATUS DeviceIoctl(PTD pTd, PSD_IOCTL pSdIoctl)
{
DBGENTER(("DeviceIoctl: PTD 0x%x\n",pTd));
return STATUS_NOT_SUPPORTED;
}
/*******************************************************************************
* DeviceInitializeRead
*
* Setup the IRP for a TDI read.
*
* pTd (input)
* Pointer to TD data structure
******************************************************************************/
NTSTATUS DeviceInitializeRead(PTD pTd, PINBUF pInBuf)
{
PIRP Irp;
PTDTDI pTdTdi;
PIO_STACK_LOCATION _IRPSP;
pTdTdi = (PTDTDI) pTd->pAfd;
ASSERT( pTdTdi != NULL );
ASSERT( pTdTdi->pConnectionEndpoint != NULL );
ASSERT( pTd );
ASSERT( pTd->pDeviceObject );
ASSERT( !(pTd->pDeviceObject->Flags & DO_BUFFERED_IO) );
Irp = pInBuf->pIrp;
_IRPSP = IoGetNextIrpStackLocation( Irp );
ASSERT( Irp->MdlAddress == NULL );
/*
* TDI interfaces always use an MDL regardless of the driver I/O type.
*/
MmInitializeMdl( pInBuf->pMdl, pInBuf->pBuffer, pTd->InBufHeader + pTd->OutBufLength );
MmBuildMdlForNonPagedPool( pInBuf->pMdl );
Irp->MdlAddress = pInBuf->pMdl;
if( pTdTdi->pConnectionEndpoint->EndpointType == TdiConnectionStream ) {
PTDI_REQUEST_KERNEL_RECEIVE p;
KIRQL OldIrql;
NTSTATUS Status;
PTD_ENDPOINT pEndpoint = pTdTdi->pConnectionEndpoint;
ASSERT( TdiDeviceEndpointType == TdiConnectionStream );
/*
* Most TDI users use the macro TdiBuildReceive(), but since
* our caller has already fiddled with the IrpStackLocation,
* we must do it inline.
*/
_IRPSP->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
_IRPSP->MinorFunction = TDI_RECEIVE;
ASSERT( _IRPSP->DeviceObject == pTd->pDeviceObject );
ASSERT( _IRPSP->FileObject == pTd->pFileObject );
ASSERT( Irp->MdlAddress );
// Cast the generic parameters field to the TDI structure needed
p = (PTDI_REQUEST_KERNEL_RECEIVE)&_IRPSP->Parameters;
p->ReceiveFlags = 0;
p->ReceiveLength = pTd->InBufHeader + pTd->OutBufLength;
return( STATUS_SUCCESS );
}
else if( pTdTdi->pConnectionEndpoint->EndpointType == TdiConnectionDatagram ) {
PTDI_REQUEST_KERNEL_RECEIVEDG p;
ASSERT( TdiDeviceEndpointType == TdiConnectionDatagram );
/*
* Most TDI users use the macro TdiBuildReceiveDatagram(), but since
* our caller has already fiddled with the IrpStackLocation,
* we must do it inline.
*/
_IRPSP->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
_IRPSP->MinorFunction = TDI_RECEIVE_DATAGRAM;
ASSERT( _IRPSP->DeviceObject );
ASSERT( _IRPSP->FileObject );
ASSERT( Irp->MdlAddress );
// Cast the generic parameters field to the TDI structure needed
p = (PTDI_REQUEST_KERNEL_RECEIVEDG)&_IRPSP->Parameters;
p->ReceiveFlags = 0;
p->ReceiveLength = pTd->InBufHeader + pTd->OutBufLength;
p->ReceiveDatagramInformation = NULL;
p->ReturnDatagramInformation = NULL;
return( STATUS_SUCCESS );
}
else {
DBGPRINT(("DeviceInitializeRead: Bad EndpointType 0x%x\n",pTdTdi->pConnectionEndpoint->EndpointType));
return( STATUS_INVALID_HANDLE );
}
// NOTREACHED
}
/*******************************************************************************
* DeviceSubmitRead
*
* Submit the read IRP to the driver.
******************************************************************************/
NTSTATUS DeviceSubmitRead(PTD pTd, PINBUF pInBuf)
{
NTSTATUS Status;
PIRP Irp;
PTDTDI pTdTdi;
KIRQL OldIrql;
PLIST_ENTRY pEntry;
PTD_ENDPOINT pEndpoint;
PIO_STACK_LOCATION _IRPSP;
PTDI_REQUEST_KERNEL_RECEIVE p;
Irp = pInBuf->pIrp;
/*
* Datagram endpoints do not use a receive indication handler.
*/
if( TdiDeviceEndpointType == TdiConnectionDatagram ) {
Status = IoCallDriver( pTd->pDeviceObject, Irp );
return( Status );
}
pTdTdi = (PTDTDI) pTd->pAfd;
ASSERT( pTdTdi != NULL );
ASSERT( pTdTdi->pConnectionEndpoint != NULL );
pEndpoint = pTdTdi->pConnectionEndpoint;
ExAcquireSpinLock( &pEndpoint->Spinlock, &OldIrql );
// The other end may have disconnected
if( pEndpoint->Disconnected ) {
TRACE0(("DeviceSubmitRead: Connection disconnecting! pEndpoint 0x%x\n",pEndpoint));
ExReleaseSpinLock( &pEndpoint->Spinlock, OldIrql );
Irp->IoStatus.Status = STATUS_REMOTE_DISCONNECT;
Irp->IoStatus.Information = 0;
// Since the IRP has not been submitted with IoCallDriver() yet,
// we must simulate.
IoSetNextIrpStackLocation( Irp );
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return STATUS_REMOTE_DISCONNECT;
}
/*
* We queue the receive IRP onto the connection
* endpoint so that the indication handler can
* submit it. Because we could have received an
* indication while processing the previous receive,
* the indication will set the indicated byte count
* in RecvBytesReady that the call side can submit the IRP.
*
* The ReceiveQueue is designed to allow our caller to submit
* multiple read IRP's in case we need to handle a TDI
* provider that drops data when no receives are ready.
*/
InsertTailList( &pEndpoint->ReceiveQueue, &Irp->Tail.Overlay.ListEntry );
/*
* Connection oriented endpoints disconnect the connection when
* a submitted I/O is canceled. This breaks the Citrix disconnect
* reconnect sequence that occurs on a new connection since the
* reader thread must be killed on one winstation, and the
* connection passed to another reader thread for a different
* winstation.
*
* This problem is solved by using a receive indication handler
* and only submitting IRP's when we know we will not block.
* This allows us to "cancel" I/O within our driver, and not
* have to do a IoCancelIrp() onto the TDI provider.
*/
if( pEndpoint->RecvBytesReady ) {
//
// Indication came in without IRP ready, or more bytes indicated than
// ICA outbuf IRP can handle.
//
// We subtract the number of bytes we can receive from the indication
// bytes. We do not need to handle the IRP cancel case, since the TDI
// will nuke the connection anyway.
//
ASSERT( !IsListEmpty( &pEndpoint->ReceiveQueue ) );
pEntry = RemoveHeadList( &pEndpoint->ReceiveQueue );
Irp = CONTAINING_RECORD( pEntry, IRP, Tail.Overlay.ListEntry );
_IRPSP = IoGetNextIrpStackLocation( Irp );
p = (PTDI_REQUEST_KERNEL_RECEIVE)&_IRPSP->Parameters;
if( p->ReceiveLength > pEndpoint->RecvBytesReady ) {
pEndpoint->RecvBytesReady = 0;
}
else {
pEndpoint->RecvBytesReady -= p->ReceiveLength;
}
TRACE1(("DeviceSubmitRead: RecvBytesReady, Calling Driver with IRP 0x%x\n",Irp));
ExReleaseSpinLock( &pEndpoint->Spinlock, OldIrql );
Status = IoCallDriver( pTd->pDeviceObject, Irp );
return( Status );
}
else {
// In this case we let the indication handler submit it.
TRACE1(("DeviceSubmitRead: Letting indication handler submit. Irp 0x%x\n",Irp));
ExReleaseSpinLock( &pEndpoint->Spinlock, OldIrql );
return( STATUS_SUCCESS );
}
}
/*******************************************************************************
* DeviceWaitForRead
******************************************************************************/
NTSTATUS DeviceWaitForRead(PTD pTd)
{
/*
* Just wait on the input event and return the wait status
*/
return IcaWaitForSingleObject(pTd->pContext, &pTd->InputEvent, -1);
}
/*******************************************************************************
* DeviceReadComplete
*
* pTd (input)
* Pointer to TD data structure
* pBuffer (input)
* Pointer to input buffer
* pByteCount (input/output)
* Pointer to location to return byte count read
******************************************************************************/
NTSTATUS DeviceReadComplete(PTD pTd, PUCHAR pBuffer, PULONG pByteCount)
{
/*
* Do any protocol specific read complete processing
*/
return TdiDeviceReadComplete(pTd, pBuffer, pByteCount);
}
/*******************************************************************************
* DeviceInitializeWrite
******************************************************************************/
NTSTATUS DeviceInitializeWrite(PTD pTd, POUTBUF pOutBuf)
{
PIRP Irp;
PTDTDI pTdTdi;
ULONG WriteLength;
PIO_STACK_LOCATION _IRPSP;
pTdTdi = (PTDTDI) pTd->pAfd;
ASSERT( pTdTdi != NULL );
ASSERT( pTdTdi->pConnectionEndpoint != NULL );
ASSERT( pTd );
ASSERT( pTd->pDeviceObject );
Irp = pOutBuf->pIrp;
_IRPSP = IoGetNextIrpStackLocation(Irp);
ASSERT(Irp->MdlAddress == NULL);
/*
* TDI interfaces always use an MDL regardless of the driver I/O type.
*/
MmInitializeMdl(pOutBuf->pMdl, pOutBuf->pBuffer, pOutBuf->ByteCount);
MmBuildMdlForNonPagedPool(pOutBuf->pMdl);
Irp->MdlAddress = pOutBuf->pMdl;
if (pTdTdi->pConnectionEndpoint->EndpointType == TdiConnectionStream) {
PTDI_REQUEST_KERNEL_SEND p;
/*
* Most TDI users use the macro TdiBuildSend(), but since
* our caller has already fiddled with the IrpStackLocation,
* we must do it inline.
*/
ASSERT( TdiDeviceEndpointType == TdiConnectionStream );
/*
* Now write in the reformatted parameters for a TDI SEND
*/
_IRPSP->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
_IRPSP->MinorFunction = TDI_SEND;
ASSERT( _IRPSP->DeviceObject == pTd->pDeviceObject );
ASSERT( _IRPSP->FileObject == pTd->pFileObject );
ASSERT( Irp->MdlAddress );
p = (PTDI_REQUEST_KERNEL_SEND)&_IRPSP->Parameters;
p->SendFlags = 0;
p->SendLength = pOutBuf->ByteCount;
return STATUS_SUCCESS;
}
else if (pTdTdi->pConnectionEndpoint->EndpointType ==
TdiConnectionDatagram) {
PTDI_REQUEST_KERNEL_SENDDG p;
/*
* Most TDI users use the macro TdiBuildSendDatagram(), but since
* our caller has already fiddled with the IrpStackLocation,
* we must do it inline.
*/
ASSERT( TdiDeviceEndpointType == TdiConnectionDatagram );
/*
* Now write in the reformatted parameters for a TDI SEND
*/
_IRPSP->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
_IRPSP->MinorFunction = TDI_SEND_DATAGRAM;
ASSERT( _IRPSP->DeviceObject );
ASSERT( _IRPSP->FileObject );
ASSERT( Irp->MdlAddress );
p = (PTDI_REQUEST_KERNEL_SENDDG)&_IRPSP->Parameters;
p->SendLength = pOutBuf->ByteCount;
// Include the remote address with every datagram send
p->SendDatagramInformation = &pTdTdi->pConnectionEndpoint->SendInfo;
return STATUS_SUCCESS;
}
else {
DBGPRINT(("DeviceInitializeWrite: Bad EndpointType 0x%x\n",
pTdTdi->pConnectionEndpoint->EndpointType));
ASSERT(FALSE); // Catch this
return STATUS_INVALID_HANDLE;
}
}
/*******************************************************************************
* DeviceWaitForStatus
*
* Wait for device status to change (unused for network TDs)
******************************************************************************/
NTSTATUS DeviceWaitForStatus(PTD pTd)
{
DBGENTER(("DeviceWaitForStatus: PTD 0x%x\n",pTd));
return STATUS_INVALID_DEVICE_REQUEST;
}
/*******************************************************************************
* DeviceCancelIo
*
* cancel all current and future i/o
******************************************************************************/
NTSTATUS DeviceCancelIo(PTD pTd)
{
KIRQL OldIrql;
PTDTDI pTdTdi;
PIRP Irp;
PLIST_ENTRY pEntry;
PTD_ENDPOINT pEndpoint;
POUTBUF pOutBuf;
DBGENTER(("DeviceCancelIo: PTD 0x%x\n", pTd));
pTdTdi = (PTDTDI)pTd->pAfd;
ASSERT(pTdTdi != NULL);
if ((pEndpoint = pTdTdi->pConnectionEndpoint) != NULL ) {
DBGPRINT(("DeviceCancelIo [%p]: Endpoint 0x%p\n", pTd, pEndpoint));
// DbgPrint("DeviceCancelIo [0x%p]: Endpoint 0x%p, connected = %ld, disconnected = %ld\n",
// pTd, pEndpoint, pEndpoint->Connected, pEndpoint->Disconnected);
/*
* Disconnect the endpoint first to make all the I/O activity stop!
*/
if (pEndpoint->Connected) {
NTSTATUS Status;
Status = _TdiDisconnect(pTd,
pEndpoint->pFileObject,
pEndpoint->pDeviceObject);
pEndpoint->Connected = FALSE;
}
/*
* Cancel any pended receives
*/
_TdCancelReceiveQueue(pTd, pEndpoint, STATUS_LOCAL_DISCONNECT);
/*
* We now check to see if we have send IRP's on the
* outgoing queue that have been submitted to the TDI.
* When we register a disconnect indication handler, the TDI
* provider will not cancel IRP's when the connection drops.
* They will hang waiting to send on a connection that no longer
* is taking data.
*
* NOTE: We should be protected by the stack driver lock
* while we walk this chain.
*/
pEntry = pTd->IoBusyOutBuf.Flink;
while (pEntry != &pTd->IoBusyOutBuf) {
pOutBuf = CONTAINING_RECORD(pEntry, OUTBUF, Links);
ASSERT(pOutBuf->pIrp != NULL);
// We must not cancel IRPs that have already completed.
if (!pOutBuf->fIrpCompleted) {
TRACE0(("DeviceCancelIo: Canceling Write IRP 0x%p Endpoint 0x%p\n",
pOutBuf->pIrp, pEndpoint));
IoCancelIrp(pOutBuf->pIrp);
}
pEntry = pEntry->Flink;
}
}
else
DBGPRINT(("DeviceCancelIo [0x%p]: Endpoint is NULL\n", pTd));
return STATUS_SUCCESS;
}
/*******************************************************************************
* DeviceSetParams
*
* Set device pararameters (unused for network TDs)
******************************************************************************/
NTSTATUS DeviceSetParams(PTD pTd)
{
DBGENTER(("DeviceSetParams: PTD 0x%x\n", pTd));
return STATUS_SUCCESS;
}
/*******************************************************************************
* DeviceGetLastError
*
* This routine returns the last transport error code and message
*
* pTd (input)
* Pointer to TD data structure
* pLastError (output)
* address to return information on last transport error
******************************************************************************/
NTSTATUS DeviceGetLastError(PTD pTd, PICA_STACK_LAST_ERROR pLastError)
{
DBGENTER(("DeviceGetLastError: PTD 0x%x\n",pTd));
return STATUS_SUCCESS;
}
/*******************************************************************************
* _TdCreateEndpointStruct
*
* Create and initialize a new Endpoint structure. Does not create any
* TDI objects.
*
* pTd (input)
* Pointer to TD data structure
* pTransportName (input)
* Pointer to UNICODE_STRING containing transport device name
* ppEndpoint (output)
* Pointer to location to return TD_ENDPOINT pointer
******************************************************************************/
NTSTATUS _TdCreateEndpointStruct(
IN PTD pTd,
IN PUNICODE_STRING pTransportName,
OUT PTD_ENDPOINT *ppEndpoint,
IN PTRANSPORT_ADDRESS pTransportAddress,
IN ULONG TransportAddressLength)
{
NTSTATUS Status;
ULONG Length;
PTD_ENDPOINT pEndpoint;
NTSTATUS Status2;
PVOID pContext;
ULONG ContextLength;
/*
* Allocate an endpoint object and room for the transport name
*/
Length = sizeof(*pEndpoint) + pTransportName->MaximumLength;
Status = MemoryAllocate(Length, &pEndpoint);
if (NT_SUCCESS(Status)) {
RtlZeroMemory(pEndpoint, Length);
Status = IcaCreateHandle( (PVOID)pEndpoint, sizeof(TD_ENDPOINT), &pEndpoint->hConnectionEndPointIcaHandle );
if (!NT_SUCCESS(Status)) {
MemoryFree(pEndpoint);
return Status;
}
} else {
return Status;
}
KeInitializeSpinLock( &pEndpoint->Spinlock );
KeInitializeEvent( &pEndpoint->AcceptEvent, NotificationEvent, FALSE );
InitializeListHead( &pEndpoint->ConnectionQueue );
InitializeListHead( &pEndpoint->ConnectedQueue );
InitializeListHead( &pEndpoint->AcceptQueue );
InitializeListHead( &pEndpoint->ReceiveQueue );
/*
* Build the transport name UNICODE_STRING and copy it
*/
pEndpoint->TransportName.Length = pTransportName->Length;
pEndpoint->TransportName.MaximumLength = pTransportName->MaximumLength;
pEndpoint->TransportName.Buffer = (PWCHAR)(pEndpoint + 1);
RtlCopyMemory( pEndpoint->TransportName.Buffer, pTransportName->Buffer,
pTransportName->MaximumLength );
/*
* If a transport address is supplied, copy it in.
*/
if (pTransportAddress && TransportAddressLength) {
/*
* Allocate and copy the transport address
*/
Status = MemoryAllocate(TransportAddressLength,
&pEndpoint->pTransportAddress);
if (NT_SUCCESS(Status)) {
Status = IcaCreateHandle( (PVOID)pEndpoint->pTransportAddress, sizeof(TRANSPORT_ADDRESS), &pEndpoint->hTransportAddressIcaHandle );
if (!NT_SUCCESS(Status)) {
Status2 = IcaCloseHandle( pEndpoint->hConnectionEndPointIcaHandle , &pContext, &ContextLength );
MemoryFree(pEndpoint->pTransportAddress);
MemoryFree(pEndpoint);
return Status;
}
RtlCopyMemory(pEndpoint->pTransportAddress, pTransportAddress,
TransportAddressLength);
pEndpoint->TransportAddressLength = TransportAddressLength;
}
else {
Status2 = IcaCloseHandle( pEndpoint->hConnectionEndPointIcaHandle , &pContext, &ContextLength );
MemoryFree(pEndpoint);
return Status;
}
}
*ppEndpoint = pEndpoint;
return STATUS_SUCCESS;
}
/*******************************************************************************
* _TdCloseEndpoint
*
* Close a TDI endpoint object
*
* pTd (input)
* Pointer to TD data structure
* pEndpoint (input)
* Pointer TD_ENDPOINT object
******************************************************************************/
NTSTATUS _TdCloseEndpoint(IN PTD pTd, IN PTD_ENDPOINT pEndpoint)
{
KIRQL OldIrql;
NTSTATUS Status;
PTDTDI pAfd;
PVOID pContext;
ULONG ContextLength ;
NTSTATUS Status2;
TRACE0(("_TdCloseEndpoint: pEndpoint 0x%x Context 0x%x Type 0x%x FO 0x%x, "
"DO 0x%x, Handle 0x%x\n", pEndpoint,pTd->pAfd,
pEndpoint->EndpointType, pEndpoint->pFileObject,
pEndpoint->pDeviceObject, pEndpoint->TransportHandle));
/*
* If this is an address endpoint, we could have
* a thread waiting for a connection.
*
* NOTE: Closing an address endpoint causes TDI to nuke all of the
* open connections that were created from it. Our upper
* level caller code understands this.
*/
if (pEndpoint->EndpointType == TdiAddressObject) {
PTD_ENDPOINT p;
PLIST_ENTRY pEntry;
ExAcquireSpinLock(&pEndpoint->Spinlock, &OldIrql);
while (pEndpoint->Waiter) {
TRACE0(("_TdCloseEndpoint: Closing AddressEndpoint, Cleaning up listen thread...\n"));
KeSetEvent( &pEndpoint->AcceptEvent, IO_NETWORK_INCREMENT, FALSE );
ExReleaseSpinLock( &pEndpoint->Spinlock, OldIrql );
IcaSleep( pTd->pContext, 100 );
ExAcquireSpinLock( &pEndpoint->Spinlock, &OldIrql );
}
ExReleaseSpinLock( &pEndpoint->Spinlock, OldIrql );
/*
* Cancel the accept indication handler if necessary
* (FileObject may not be present if DeviceCreateEndpoint fails).
*/
if (( TdiDeviceEndpointType != TdiConnectionDatagram ) &&
(pEndpoint->pFileObject)) {
Status = _TdiSetEventHandler(
pTd,
pEndpoint->pDeviceObject,
pEndpoint->pFileObject,
TDI_EVENT_CONNECT,
(PVOID)NULL, // Handler
(PVOID)NULL // Context
);
ASSERT( NT_SUCCESS(Status) );
}
/*
* Free the connection object queues.
*
* NOTE: We no longer need the spinlock since we have
* cancelled the connect indication handler from TDI,
* and the upper level protects calls to teardown an
* address endpoint.
*/
/*
* Cleanup connected, but not returned objects
*/
while( !IsListEmpty( &pEndpoint->ConnectedQueue ) ) {
pEntry = pEndpoint->ConnectedQueue.Flink;
RemoveEntryList( pEntry );
p = CONTAINING_RECORD( pEntry, TD_ENDPOINT, ConnectionLink );
ASSERT( p->EndpointType != TdiAddressObject );
_TdCloseEndpoint( pTd, p );
}
/*
* Cleanup queue of wait for Accept complete connections
*/
while( !IsListEmpty( &pEndpoint->AcceptQueue ) ) {
pEntry = pEndpoint->AcceptQueue.Flink;
RemoveEntryList( pEntry );
p = CONTAINING_RECORD( pEntry, TD_ENDPOINT, ConnectionLink );
ASSERT( p->EndpointType != TdiAddressObject );
_TdCloseEndpoint( pTd, p );
}
/*
* Cleanup queue of empty connections
*/
while( !IsListEmpty( &pEndpoint->ConnectionQueue ) ) {
pEntry = pEndpoint->ConnectionQueue.Flink;
RemoveEntryList( pEntry );
p = CONTAINING_RECORD( pEntry, TD_ENDPOINT, ConnectionLink );
ASSERT( p->EndpointType != TdiAddressObject );
_TdCloseEndpoint( pTd, p );
}
}
/*
* If this endpoint has ever been connected,
* then tell the transport driver we are closing down.
*/
if (pEndpoint->Connected) {
(VOID) _TdiDisconnect(pTd,
pEndpoint->pFileObject,
pEndpoint->pDeviceObject);
}
pEndpoint->pDeviceObject = NULL;
/*
* If a file object, dereference it
*/
if (pEndpoint->pFileObject) {
ObDereferenceObject( pEndpoint->pFileObject );
pEndpoint->pFileObject = NULL;
}
/*
* If a file handle, close it
*/
if (pEndpoint->TransportHandle) {
ASSERT( pEndpoint->TransportHandleProcess == IoGetCurrentProcess() );
ZwClose( pEndpoint->TransportHandle );
pEndpoint->TransportHandleProcess = NULL;
pEndpoint->TransportHandle = NULL;
}
/*
* If an IRP, free it
*
* NOTE: This must be *AFTER* the close since the
* IRP is in the bowels of the TCP driver!
*/
if( pEndpoint->AcceptIrp ) {
IoFreeIrp( pEndpoint->AcceptIrp );
pEndpoint->AcceptIrp = NULL;
}
/*
* If a transport address, free it, and Also close it handle if there is one.
*/
if (pEndpoint->hTransportAddressIcaHandle != NULL) {
Status2 = IcaCloseHandle( pEndpoint->hTransportAddressIcaHandle , &pContext, &ContextLength );
}
if ( pEndpoint->pTransportAddress ) {
MemoryFree( pEndpoint->pTransportAddress );
pEndpoint->pTransportAddress = NULL;
}
/*
* If a remote address, free it
*/
if ( pEndpoint->pRemoteAddress ) {
MemoryFree( pEndpoint->pRemoteAddress );
pEndpoint->pRemoteAddress = NULL;
}
if (pEndpoint->hConnectionEndPointIcaHandle != NULL) {
Status2 = IcaCloseHandle( pEndpoint->hConnectionEndPointIcaHandle , &pContext, &ContextLength );
}
DBGPRINT(("_TdCloseEndpoint [%p]: 0x%p\n", pTd, pEndpoint));
MemoryFree(pEndpoint);
return STATUS_SUCCESS;
}
/****************************************************************************/
// _TdConnectHandler
//
// This is the transport connect event handler for the server. It is
// specified as the connect handler for all endpoints opened by the
// server. It attempts to dequeue a free connection from a list
// anchored in the address endpoint. If successful, it returns the
// connection to the transport. Otherwise, the connection is rejected.
/****************************************************************************/
NTSTATUS _TdConnectHandler(
IN PVOID TdiEventContext,
IN int RemoteAddressLength,
IN PVOID RemoteAddress,
IN int UserDataLength,
IN PVOID UserData,
IN int OptionsLength,
IN PVOID Options,
OUT CONNECTION_CONTEXT *ConnectionContext,
OUT PIRP *AcceptIrp)
{
KIRQL OldIrql;
NTSTATUS Status;
PLIST_ENTRY pEntry;
PTD_ENDPOINT pConnection;
PTD_ENDPOINT pAddressEndpoint;
PACCEPT_CONTEXT Context;
UserDataLength, UserData; // avoid compiler warnings
OptionsLength, Options;
pAddressEndpoint = (PTD_ENDPOINT)TdiEventContext;
TRACE0(("_TdConnectHandler: Connect event! Context 0x%x\n",pAddressEndpoint));
/*
* First try and get memory. If error, the TDI transport provider
* will drop the connect request.
*/
Status = MemoryAllocate(sizeof(ACCEPT_CONTEXT), &Context);
if (NT_SUCCESS(Status)) {
memset(Context, 0, sizeof(ACCEPT_CONTEXT));
}
else {
DBGPRINT(("_TdConnectHandler: No memory for context\n"));
return Status;
}
/*
* Get the spinlock to synchronize with the call side
*/
ExAcquireSpinLock(&pAddressEndpoint->Spinlock, &OldIrql);
/*
* Get the connection object on the front of the list
*/
if (!IsListEmpty(&pAddressEndpoint->ConnectionQueue)) {
pEntry = RemoveHeadList(&pAddressEndpoint->ConnectionQueue);
pConnection = CONTAINING_RECORD(pEntry, TD_ENDPOINT, ConnectionLink);
// Put it on the end of the accept list
InsertTailList(&pAddressEndpoint->AcceptQueue,
&pConnection->ConnectionLink);
}
else {
DBGPRINT(("_TdConnectHandler: Empty ConnectionQueue! 0x%x\n",
pAddressEndpoint));
ExReleaseSpinLock(&pAddressEndpoint->Spinlock, OldIrql);
MemoryFree(Context);
return STATUS_INSUFFICIENT_RESOURCES;
}
Context->pAddressEndpoint = pAddressEndpoint;
Context->pConnectionEndpoint = pConnection;
ASSERT(pConnection->AcceptIrp != NULL);
//
// remember the remote address in the connection endpoint
//
if ( NULL != RemoteAddress )
{
ASSERT( NULL == pConnection->pRemoteAddress );
ASSERT( 0 != RemoteAddressLength );
if ( NT_SUCCESS( MemoryAllocate( RemoteAddressLength, &pConnection->pRemoteAddress )))
{
RtlCopyMemory( pConnection->pRemoteAddress, RemoteAddress, RemoteAddressLength );
pConnection->RemoteAddressLength = RemoteAddressLength;
}
}
TdiBuildAccept(
pConnection->AcceptIrp,
pConnection->pDeviceObject,
pConnection->pFileObject,
_TdAcceptComplete, // Completion routine
Context, // Context
&Context->RequestInfo,
&Context->ReturnInfo);
/*
* Make the next stack location current. Normally IoCallDriver would
* do this, but since we're bypassing that, we do it directly.
*/
IoSetNextIrpStackLocation(pConnection->AcceptIrp);
/*
* Return the connection context (the connection address) to the
* transport. Return a pointer to the Accept IRP. Indicate that
* the Connect event has been handled. This must be the same
* context specified when the connection object was created.
*/
*ConnectionContext = pConnection;
*AcceptIrp = pConnection->AcceptIrp;
ExReleaseSpinLock(&pAddressEndpoint->Spinlock, OldIrql);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS _TdAcceptComplete(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Ctx)
{
KIRQL OldIrql;
PLIST_ENTRY pEntry;
PACCEPT_CONTEXT Context;
PTD_ENDPOINT pConnection;
PTD_ENDPOINT pAddressEndpoint;
Context = (PACCEPT_CONTEXT)Ctx;
pConnection = Context->pConnectionEndpoint;
pAddressEndpoint = Context->pAddressEndpoint;
ASSERT( pConnection != NULL );
ASSERT( pAddressEndpoint != NULL );
ASSERT( pConnection->EndpointType == TdiConnectionStream );
ASSERT( pAddressEndpoint->EndpointType == TdiAddressObject );
TRACE0(("_TdAcceptComplete: Status 0x%x, Endpoint 0x%x\n",Irp->IoStatus.Status,pConnection));
/*
* Get the spinlock to synchronize with the call side
*/
ExAcquireSpinLock( &pAddressEndpoint->Spinlock, &OldIrql );
if (IsListEmpty( &pAddressEndpoint->AcceptQueue)) {
DBGPRINT(("_TdAcceptComplete: Empty Accept Queue! 0x%x\n",
pAddressEndpoint));
ExReleaseSpinLock(&pAddressEndpoint->Spinlock, OldIrql);
/*
* Release the context memory
*/
MemoryFree(Context);
// Let it drop
return STATUS_MORE_PROCESSING_REQUIRED;
}
else {
pEntry = RemoveHeadList(&pAddressEndpoint->AcceptQueue);
pConnection = CONTAINING_RECORD(pEntry, TD_ENDPOINT, ConnectionLink);
/*
* Put it on the end of the connect list
*/
InsertTailList(&pAddressEndpoint->ConnectedQueue,
&pConnection->ConnectionLink);
}
/*
* If the accept failed, the caller will check this status
* and tear down the connection, causing a RST to be sent
* to the other side.
*/
pConnection->Status = Irp->IoStatus.Status;
/*
* Signal that it is connected (Could be in error)
*/
pConnection->Connected = TRUE;
/*
* Set the event on the address object
*/
KeSetEvent(&Context->pAddressEndpoint->AcceptEvent, IO_NETWORK_INCREMENT, FALSE);
ExReleaseSpinLock(&pAddressEndpoint->Spinlock, OldIrql);
/*
* Release the context memory
*/
MemoryFree(Context);
// Return STATUS_MORE_PROCESSING_REQUIRED so that IoCompleteRequest
// will stop working on the IRP.
return STATUS_MORE_PROCESSING_REQUIRED;
}
/*****************************************************************************
* _TdReceiveHandler
*
* This function is called by the TDI when receive data is available
* on the connection. This is done so we do not submit the IRP until
* data is available. The disconnect-reconnect logic in ICA causes an
* IoCancelIrp() to be sent on the input thread, and TDI providers drop the
* connection on a read or write I/O cancel.
*
* WARNING: This critical behavior is only needed for a reconnect
* sequence. For normal ICA I/O, it is OK to block the reader
* thread in the TDI driver.
*
* TdiEventContext (input)
* Context registered with event handler on address object.
* (address endpoint)
*
* ConnectionContext (input)
* Connection context registered with the connection
* create.
****************************************************************************/
NTSTATUS _TdReceiveHandler(
IN PVOID TdiEventContext,
IN CONNECTION_CONTEXT ConnectionContext,
IN ULONG ReceiveFlags,
IN ULONG BytesIndicated,
IN ULONG BytesAvailable,
OUT ULONG *BytesTaken,
IN PVOID Tsdu,
OUT PIRP *IoRequestPacket)
{
KIRQL OldIrql;
PIRP Irp;
PLIST_ENTRY pEntry;
PIO_STACK_LOCATION _IRPSP;
PTDI_REQUEST_KERNEL_RECEIVE p;
PTD_ENDPOINT pEndpoint = (PTD_ENDPOINT)ConnectionContext;
/*
* Only stream transports use a receive indication handler.
*/
ASSERT( TdiDeviceEndpointType != TdiConnectionDatagram );
ASSERT( pEndpoint != NULL );
ASSERT( pEndpoint->EndpointType == TdiConnectionStream );
TRACE1(("_TdReceiveHandler: ReceiveDataIndication! pEndpoint 0x%x\n",pEndpoint));
ExAcquireSpinLock(&pEndpoint->Spinlock, &OldIrql);
*BytesTaken = 0;
/*
* Submit an IRP at indication time if we have one on
* the queue.
*/
if (!IsListEmpty( &pEndpoint->ReceiveQueue)) {
pEntry = RemoveHeadList(&pEndpoint->ReceiveQueue);
Irp = CONTAINING_RECORD(pEntry, IRP, Tail.Overlay.ListEntry);
TRACE1(("_TdReceiveHandler: Passing IRP for Receive Indication %d bytes\n",
BytesAvailable));
_IRPSP = IoGetNextIrpStackLocation(Irp);
p = (PTDI_REQUEST_KERNEL_RECEIVE)&_IRPSP->Parameters;
if (p->ReceiveLength < BytesAvailable) {
pEndpoint->RecvBytesReady += (BytesAvailable - p->ReceiveLength);
TRACE1(("_TdReceiveHandler: Excess Bytes %d Added to RecvBytesReady, now %d\n",
(BytesAvailable - p->ReceiveLength),
pEndpoint->RecvBytesReady));
}
ExReleaseSpinLock(&pEndpoint->Spinlock, OldIrql);
*IoRequestPacket = Irp;
IoSetNextIrpStackLocation(Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
// No RecvIrp, So we can not take any data. Let the callside get it.
TRACE1(("_TdReceiveHandler: No RecvIrp, Adding To RecvBytesReady. %d Bytes\n",BytesAvailable));
pEndpoint->RecvBytesReady += BytesAvailable;
ExReleaseSpinLock(&pEndpoint->Spinlock, OldIrql);
return STATUS_DATA_NOT_ACCEPTED;
}
/*****************************************************************************
* _TdDisconnectHandler
*
* This function is called by the TDI when a disconnect occurs
* on the connection.
*
* TdiEventContext (input)
* Context registered with event handler on address object.
* (address endpoint)
*
* ConnectionContext (input)
* Connection context registered with the connection
* create.
****************************************************************************/
NTSTATUS _TdDisconnectHandler(
IN PVOID TdiEventContext,
IN CONNECTION_CONTEXT ConnectionContext,
IN int DisconnectDataLength,
IN PVOID DisconnectData,
IN int DisconnectInformationLength,
IN PVOID DisconnectInformation,
IN ULONG DisconnectFlags)
{
KIRQL OldIrql;
PIRP Irp;
PIO_STACK_LOCATION irpSp;
PLIST_ENTRY pEntry;
PTD_ENDPOINT pEndpoint = (PTD_ENDPOINT)ConnectionContext;
/*
* Only stream transports use a disconnect indication handler.
*/
ASSERT( TdiDeviceEndpointType != TdiConnectionDatagram );
ASSERT( pEndpoint != NULL );
ASSERT( pEndpoint->EndpointType == TdiConnectionStream );
// DbgPrint("\n");
// DbgPrint("_TdDisconnectHandler : pEndpoint = 0x%p\n", pEndpoint);
ExAcquireSpinLock( &pEndpoint->Spinlock, &OldIrql );
pEndpoint->Disconnected = TRUE;
ExReleaseSpinLock( &pEndpoint->Spinlock, OldIrql );
_TdCancelReceiveQueue(NULL, pEndpoint, STATUS_REMOTE_DISCONNECT );
return STATUS_SUCCESS;
}
/****************************************************************************/
// Create an idle connection object associated with an address object.
// This must be called from call (thread) level, and not from indication
// time (DPC level).
/****************************************************************************/
NTSTATUS _TdCreateConnectionObject(
IN PTD pTd,
IN PUNICODE_STRING pTransportName,
OUT PTD_ENDPOINT *ppEndpoint,
IN PTRANSPORT_ADDRESS pTransportAddress,
IN ULONG TransportAddressLength)
{
PTDTDI pTdTdi;
NTSTATUS Status;
PTD_ENDPOINT pEndpoint;
pTdTdi = (PTDTDI)pTd->pAfd;
// Create and init structure and spinlock
Status = _TdCreateEndpointStruct(
pTd,
pTransportName,
ppEndpoint,
pTransportAddress,
TransportAddressLength
);
if (NT_SUCCESS(Status)) {
pEndpoint = *ppEndpoint;
}
else {
return Status;
}
// The TD sets whether data gram, or stream
pEndpoint->EndpointType = TdiDeviceEndpointType;
pEndpoint->TransportHandleProcess = IoGetCurrentProcess();
/*
* Create a TDI connection object
*/
Status = _TdiOpenConnection(
&pEndpoint->TransportName,
(PVOID)pEndpoint, // Context
&pEndpoint->TransportHandle,
&pEndpoint->pFileObject,
&pEndpoint->pDeviceObject
);
if (!NT_SUCCESS(Status)) {
DBGPRINT(("_TdCreateConnectionObject: _TdiOpenConnection failed, Status 0x%x\n",Status));
_TdCloseEndpoint( pTd, pEndpoint );
return Status;
}
// Allocate an IRP for connect/disconnect handling
// This is needed since we use the connect indication hander.
pEndpoint->AcceptIrp = _TdiAllocateIrp(pEndpoint->pFileObject,
pEndpoint->pDeviceObject);
if (pEndpoint->AcceptIrp == NULL) {
DBGPRINT(("_TdCreateConnectionObject: Could not allocate IRP\n"));
_TdCloseEndpoint(pTd, pEndpoint);
return STATUS_INSUFFICIENT_RESOURCES;
}
// Associate the connection object with its address object
Status = _TdiAssociateAddress(
pTd,
pEndpoint->AcceptIrp,
pEndpoint->pFileObject,
pTdTdi->pAddressEndpoint->TransportHandle,
pTdTdi->pAddressEndpoint->pDeviceObject
);
if (!NT_SUCCESS(Status)) {
DBGPRINT(("_TdCreateConnectionObject: _TdiAssociateAddress failed, Status 0x%x\n",Status));
_TdCloseEndpoint(pTd, pEndpoint);
return Status;
}
return Status;
}
/*******************************************************************************
* _TdWaitForDatagramConnection
*
* For for an incoming datagram connection request and accept it.
*
* Datagram endpoints listen on a TDI address object bound to the local
* (netcard) and well known ICA socket number. Packets then received on
* the ICA socket number are checked for ICA request connection, and then
* a new TDI address object is bound with the wild-card local address
* (0). This causes a new, unused socket number to be assigned to this
* address object. This new TDI address object is used for further
* communication to the now "connected" IPX ICA client.
*
* pTd (input)
* Pointer to TD data structure
* pAddressEndpoint (input)
* Pointer Address endpoint object
* ppConnectionEndpoint (output)
* Pointer to location to return Connection endpoint pointer
******************************************************************************/
NTSTATUS _TdWaitForDatagramConnection(
IN PTD pTd,
IN PTD_ENDPOINT pAddressEndpoint,
OUT PTD_ENDPOINT *ppConnectionEndpoint)
{
NTSTATUS Status;
PTRANSPORT_ADDRESS pLocalAddress;
ULONG LocalAddressLength;
ULONG AddressInfoLength;
ULONG RemoteAddressLength = 0;
PTD_ENDPOINT pEndpoint = NULL;
PTRANSPORT_ADDRESS pRemoteAddress = NULL;
PTDI_ADDRESS_INFO pAddressInfo = NULL;
/*
* Get a copy of the local transport address.
*
* Clear the TDI address part of the structure so that we can
* use it to bind the connection endpoint to a wild-card address.
*
* This wildcard address (0), will cause the packet level TDI
* provider to assign us a unique socket when the TDI address
* object is created.
*/
Status = MemoryAllocate(pAddressEndpoint->TransportAddressLength,
&pLocalAddress);
if (NT_SUCCESS(Status)) {
RtlCopyMemory(pLocalAddress, pAddressEndpoint->pTransportAddress,
pAddressEndpoint->TransportAddressLength);
RtlZeroMemory(pLocalAddress->Address[0].Address,
pLocalAddress->Address[0].AddressLength);
}
else {
goto badmalloc;
}
LocalAddressLength = pAddressEndpoint->TransportAddressLength;
/*
* Call protocol specific routine to wait for
* a datagram connection request to arrive.
*
* This returns when a valid ICA connect datagram comes in
* from a remote address. No reply has been sent.
*/
Status = TdiDeviceWaitForDatagramConnection(pTd,
pAddressEndpoint->pFileObject,
pAddressEndpoint->pDeviceObject,
&pRemoteAddress,
&RemoteAddressLength);
if (!NT_SUCCESS(Status)) {
DBGPRINT(("_TdWaitForDatagramConnection: Error 0x%x in TdiDeviceWaitForDatagramConnction\n",Status));
goto badwait;
}
ASSERT( pRemoteAddress != NULL );
/*
* Create a new address endpoint bound to the wildcard local address.
* A unique "socket" will be created for us. This will become
* our datagram "connection".
*/
Status = _TdCreateEndpointStruct(
pTd,
&pAddressEndpoint->TransportName,
&pEndpoint,
pLocalAddress,
LocalAddressLength
);
if ( !NT_SUCCESS( Status ) ) {
DBGPRINT(("_TdWaitForDatagramConnection: Error 0x%x in _TdCreateEndpointStruct\n",Status));
goto badopen;
}
pEndpoint->EndpointType = TdiConnectionDatagram;
pEndpoint->TransportHandleProcess = IoGetCurrentProcess();
/*
* Create the TDI address object.
*/
Status = _TdiCreateAddress(
&pEndpoint->TransportName,
pEndpoint->pTransportAddress,
pEndpoint->TransportAddressLength,
&pEndpoint->TransportHandle,
&pEndpoint->pFileObject,
&pEndpoint->pDeviceObject
);
if ( !NT_SUCCESS( Status ) ) {
DBGPRINT(("_TdWaitForDatagramConnection: Error 0x%x in _TdiCreateAddress\n",Status));
goto badbind;
}
/*
* Allocate a work buffer for querying the transport address
*/
AddressInfoLength = pEndpoint->TransportAddressLength+4;
Status = MemoryAllocate( AddressInfoLength, &pAddressInfo );
if ( !NT_SUCCESS( Status ) ) {
DBGPRINT(("_TdWaitForDatagramConnection: Error 0x%x Allocating Memory %d bytes\n",Status,AddressInfoLength));
goto badbind;
}
/*
* Now query the unique socket address that the TDI assigned for us.
*/
Status = _TdiQueryAddressInfo(
pTd,
NULL, // Irp
pEndpoint->pFileObject,
pEndpoint->pDeviceObject,
pAddressInfo,
AddressInfoLength
);
if( !NT_SUCCESS(Status) ) {
DBGPRINT(("_TdWaitForDatagramConnection: Error 0x%x in _TdiQueryAddressInfo\n",Status));
goto badbind;
}
/*
* Update the callers transport address buffer
*/
RtlCopyMemory( pEndpoint->pTransportAddress,
&pAddressInfo->Address,
pEndpoint->TransportAddressLength );
/*
* Save the remote address in the connection endpoint
* structure so that we can send to it with our datagram sends.
*/
ASSERT( pEndpoint->pRemoteAddress == NULL );
pEndpoint->pRemoteAddress = pRemoteAddress;
pEndpoint->RemoteAddressLength = RemoteAddressLength;
pEndpoint->SendInfo.RemoteAddress = pRemoteAddress;
pEndpoint->SendInfo.RemoteAddressLength = RemoteAddressLength;
/*
* Call protocol specific routine to complete the datagram connection.
*
* This sends the ICA connect reply datagram.
*/
Status = TdiDeviceCompleteDatagramConnection(
pTd,
pEndpoint->pFileObject,
pEndpoint->pDeviceObject,
pEndpoint->SendInfo.RemoteAddress,
pEndpoint->SendInfo.RemoteAddressLength
);
if (!NT_SUCCESS(Status)) {
DBGPRINT(("_TdWaitForDatagramConnection: Error 0x%x in TdiDeviceCompleteDatagramConnection\n",Status));
goto badcomplete;
}
*ppConnectionEndpoint = pEndpoint;
MemoryFree(pLocalAddress);
MemoryFree(pAddressInfo);
return STATUS_SUCCESS;
/*=============================================================================
== Error returns
=============================================================================*/
badcomplete:
badbind:
if (pEndpoint)
_TdCloseEndpoint(pTd, pEndpoint);
badopen:
if (pAddressInfo)
MemoryFree(pAddressInfo);
if (pRemoteAddress)
MemoryFree(pRemoteAddress);
badwait:
MemoryFree(pLocalAddress);
badmalloc:
return Status;
}
/*****************************************************************************
*
* returns the remote address
*
****************************************************************************/
NTSTATUS DeviceQueryRemoteAddress(
PTD pTd,
PVOID pIcaEndpoint,
ULONG EndpointSize,
PVOID pOutputAddress,
ULONG OutputAddressSize,
PULONG BytesReturned)
{
NTSTATUS status = STATUS_NOT_SUPPORTED;
PTD_STACK_ENDPOINT pStackEndpoint;
PTRANSPORT_ADDRESS pRemoteAddress;
PTA_ADDRESS pRemoteIP;
PVOID Handle;
ULONG Length;
struct {
USHORT sa_family;
CHAR sa_data[1];
} *pOutput;
*BytesReturned = 0;
if ( sizeof(PVOID) != EndpointSize )
{
status = STATUS_INVALID_PARAMETER_4;
goto exitpt;
}
pOutput = pOutputAddress;
if ( NULL == pOutput )
{
status = STATUS_INVALID_PARAMETER_5;
goto exitpt;
}
try {
RtlZeroMemory( pOutput, OutputAddressSize );
} except ( EXCEPTION_EXECUTE_HANDLER )
{
status = GetExceptionCode();
DBGPRINT(("DeviceQueryRemoteAddress: Exception 0x%x\n", status));
goto exitpt;
}
/*
* Capture the parameter
*/
try {
ProbeForRead( pIcaEndpoint, sizeof(PVOID), 1 );
Handle = (*((PVOID *)pIcaEndpoint));
} except ( EXCEPTION_EXECUTE_HANDLER ) {
status = GetExceptionCode();
DBGPRINT(("DeviceQueryRemoteAddress: Exception 0x%x\n", status));
goto exitpt;
}
TRACE0(("DeviceOpenEndpoint: Fetching Handle 0x%x\n", Handle));
/*
* See if TERMDD knows about the handle
*/
status = IcaReturnHandle( Handle, &pStackEndpoint, &Length );
if( !NT_SUCCESS(status) ) {
DBGPRINT(("DeviceQueryRemoteAddress: TERMDD handle 0x%x no good 0x%x\n", Handle, status));
status = STATUS_INVALID_PARAMETER_3;
goto exitpt;
}
if ( TDI_ADDRESS_TYPE_IP != pStackEndpoint->AddressType &&
TDI_ADDRESS_TYPE_IP6 != pStackEndpoint->AddressType )
{
status = STATUS_NOT_SUPPORTED;
goto exitpt;
}
if ( NULL == pStackEndpoint->pEndpoint )
{
status = STATUS_INVALID_PARAMETER_3; // remote address wasn't recorded
goto exitpt;
}
pRemoteAddress = pStackEndpoint->pEndpoint->pRemoteAddress;
ASSERT( 1 == pRemoteAddress->TAAddressCount );
pRemoteIP = pRemoteAddress->Address;
//
// check the size of the output including the protocol family
//
if ( pRemoteIP->AddressLength + sizeof( USHORT ) > OutputAddressSize )
{
status = STATUS_BUFFER_TOO_SMALL;
goto exitpt;
}
ASSERT( sizeof( TDI_ADDRESS_IP ) == pRemoteIP->AddressLength ||
sizeof( TDI_ADDRESS_IP6 ) == pRemoteIP->AddressLength );
ASSERT( TDI_ADDRESS_TYPE_IP == pRemoteIP->AddressType ||
TDI_ADDRESS_TYPE_IP6 == pRemoteIP->AddressType );
pOutput->sa_family = pRemoteIP->AddressType;
RtlCopyMemory( &pOutput->sa_data, &(pRemoteIP->Address), pRemoteIP->AddressLength );
*BytesReturned = sizeof( *pOutput );
status = STATUS_SUCCESS;
exitpt:
return status;
}
/*****************************************************************************
* _TdCancelReceiveQueue
*
* Cancel all of the I/O in the current Receive Queue
****************************************************************************/
NTSTATUS _TdCancelReceiveQueue(PTD pTd, PTD_ENDPOINT pEndpoint, NTSTATUS CancelStatus)
{
PIRP Irp;
KIRQL OldIrql;
PLIST_ENTRY pEntry;
DBGPRINT(("_TdCancelReceiveQueue [%p]: Endpoint 0x%p\n", pTd, pEndpoint));
ExAcquireSpinLock( &pEndpoint->Spinlock, &OldIrql );
/*
* If we have any Receive Irp's, we are waiting for the
* indication handler to submit the I/O. Since the IRP
* is not submitted yet, we must cancel the IRP's.
*/
while (!IsListEmpty(&pEndpoint->ReceiveQueue)) {
pEntry = RemoveHeadList( &pEndpoint->ReceiveQueue );
Irp = CONTAINING_RECORD( pEntry, IRP, Tail.Overlay.ListEntry );
TRACE0(("_TdCancelReceiveQueue: Cancel Receive Irp 0x%x on pEndpoint 0x%x\n",Irp,pEndpoint));
ExReleaseSpinLock( &pEndpoint->Spinlock, OldIrql );
Irp->IoStatus.Status = CancelStatus;
Irp->IoStatus.Information = 0;
// Since the IRP has not been submitted with IoCallDriver() yet,
// we must simulate.
IoSetNextIrpStackLocation(Irp);
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
ExAcquireSpinLock(&pEndpoint->Spinlock, &OldIrql);
}
ExReleaseSpinLock(&pEndpoint->Spinlock, OldIrql);
return STATUS_SUCCESS;
}