www.pudn.com > VSer.rar > TDIClient.c


/*++ 
 
Copyright (c) 2005  Changzhi Zhou All Rights Reserved 
 
Module Name: 
 
    TDIClient.c 
 
Abstract: 
 
    This module contains the routines relevant to TDI client calls. 
 
Environment: 
 
    Kernel mode 
 
Revision History: 
 
	Changzhi Zhou Feb 5, 2005 
 
--*/ 
#include "TDIClient.h" 
#include "main.h" 
 
NTSTATUS InitializeConnection( PDEVICE_EXTENSION deviceExtension ) 
/*++ 
 
Routine Description: 
 
	1. Open a transport address and get the highest deviceobject of the transport stack. 
	2. Build RecvContext in the DeviceExtension 
	3. SetTdiEventHandle, includes TDI_EVENT_RECEIVE_DATAGRAM and TDI_EVENT_ERROR 
 
Arguments: 
 
    deviceExtension - pointer to a device object extension. 
 
Return Value: 
 
    NT status code. 
 
--*/ 
{ 
	NTSTATUS	status; 
	static USHORT		Port = LOCAL_PORT; 
 
	if ( ( deviceExtension->hTransAddr ) || ( deviceExtension->lpTransAddrFileObject ) ) 
		return STATUS_INVALID_DEVICE_STATE; 
	status = TDIOpenTransportAddress( wcharNetDevName, 
		&deviceExtension->hTransAddr, 
		&deviceExtension->lpTransAddrFileObject, 
		Port 
		); 
	DebugPrint(("OpenTransAddr: handle = %x, lpFileObj = %x, status = %x\n", 
		deviceExtension->hTransAddr, deviceExtension->lpTransAddrFileObject, status )); 
	if ( status != STATUS_SUCCESS ) 
	{ 
		deviceExtension->lpTransAddrFileObject = NULL; 
		deviceExtension->hTransAddr				= NULL; 
		return status; 
	} 
	Port += 0x100; 
	deviceExtension->TDILowerDeviceObject = IoGetRelatedDeviceObject( deviceExtension->lpTransAddrFileObject ); 
	DebugPrint(("TDI Provider: 0x%x\n", deviceExtension->TDILowerDeviceObject )); 
	if ( deviceExtension->TDILowerDeviceObject == NULL ) 
	{ 
		ObDereferenceObject ( deviceExtension->lpTransAddrFileObject ); 
		ZwClose ( deviceExtension->hTransAddr ); 
		deviceExtension->lpTransAddrFileObject = NULL; 
		deviceExtension->hTransAddr				= NULL; 
		return STATUS_UNSUCCESSFUL; 
	} 
 
	TDIBuildRecvContext( &deviceExtension->recvContext ); 
 
	TDIClnSetEventHandler( deviceExtension->lpTransAddrFileObject, 
		deviceExtension->TDILowerDeviceObject, TDI_EVENT_RECEIVE_DATAGRAM, 
		TDIEventRecvDatagram, deviceExtension ); 
	TDIClnSetEventHandler( deviceExtension->lpTransAddrFileObject, 
		deviceExtension->TDILowerDeviceObject, TDI_EVENT_ERROR, 
		TDIEventError, NULL ); 
	return status; 
} 
NTSTATUS Disconnection ( PDEVICE_EXTENSION deviceExtension ) 
/*++ 
 
Routine Description: 
 
	1. SetTdiEventhandle TDI_EVENT_RECEIVE_DATAGRAM to NULL 
	2. Free RecvContext in the DeviceExtension 
	3. Close an opened transport address. 
	3. SetTdiEventHandle, includes TDI_EVENT_RECEIVE_DATAGRAM and TDI_EVENT_ERROR 
 
Arguments: 
 
    deviceExtension - pointer to a device object extension. 
 
Return Value: 
 
    NT status code. 
 
--*/ 
{ 
	NTSTATUS	status; 
	if ( ( deviceExtension->hTransAddr == NULL ) || ( deviceExtension->lpTransAddrFileObject == NULL ) ) 
		return STATUS_INVALID_DEVICE_STATE; 
	TDIClnSetEventHandler( deviceExtension->lpTransAddrFileObject, 
		deviceExtension->TDILowerDeviceObject, TDI_EVENT_RECEIVE_DATAGRAM, 
		NULL, deviceExtension ); 
	TDIFreeRecvContext( &deviceExtension->recvContext ); 
 
	ObDereferenceObject ( deviceExtension->lpTransAddrFileObject ); 
	deviceExtension->lpTransAddrFileObject = NULL; 
	status = ZwClose ( deviceExtension->hTransAddr ); 
	if ( status == STATUS_SUCCESS ) 
		deviceExtension->hTransAddr = NULL; 
	else 
	{ 
		DebugPrint(("ZwClose hTransAddr failed with status %x !\n", status )); 
	} 
	return status; 
} 
NTSTATUS	TDISendDatagram( 
    IN PDEVICE_OBJECT DeviceObject, 
    IN PIRP Irp 
	) 
/*++ 
 
Routine Description: 
 
  This function is called in SampleWrite. It builds a internel irp and send down  
  to the underlying transport. Until the irp completes or timeout, it will return. 
  The remote peer's IP address and port are indicated by RemoteAddress and  
  RemotePort in deviceExtension. 
 
	1. TdiBuildInternalDeviceControlIrp for TDI_SEND_DATAGRAM 
	2. Allocate and lockpage a MDL based on Irp->MdlAddress 
	3. TdiBuildSendDatagram and pass down to the underlying transport device. 
	4. Wait 3 seconds for the completion. If timeout, cancel the irp 
	5. Set the Irp according with the completed irp. 
 
Note: 
	This routine is synchronous. 
	It must be running at IRQL = PASSIVE_LEVEL 
 
Arguments: 
 
   DeviceObject - pointer to a device object. 
 
   Irp - pointer to an I/O Request Packet for IRP_MJ_WRITE. 
 
Return Value: 
 
    NT status code. 
 
--*/ 
{ 
	NTSTATUS	status; 
	PIRP		pIrp; 
	PDEVICE_EXTENSION	deviceExtension; 
	KEVENT		Event; 
	IO_STATUS_BLOCK	IoStatus; 
	PMDL		pMdl; 
	PVOID		VirtualAddress; 
	ULONG		Length; 
	EXCEPTION_POINTERS        * pExceptionInfo; 
	ULONG                       lclExceptionCode; 
	PVOID                       lclExceptionAddr; 
	PVOID		SystemAddress; 
 
	TA_IP_ADDRESS	RmtAddress = {1, {TDI_ADDRESS_LENGTH_IP, TDI_ADDRESS_TYPE_IP, { REMOTE_PORT, REMOTE_ADDRESS}}}; 
	TDI_CONNECTION_INFORMATION RmtInfo = {0, 0, 0, 0, sizeof(RmtAddress), &RmtAddress}; 
 
	deviceExtension = ( PDEVICE_EXTENSION )DeviceObject->DeviceExtension; 
	RmtAddress.Address->Address->sin_port	= deviceExtension->RemotePort; 
	RmtAddress.Address->Address->in_addr	= deviceExtension->RemoteAddress; 
	status		= STATUS_SUCCESS; 
	KeInitializeEvent( &Event, NotificationEvent, FALSE); 
	pIrp = TdiBuildInternalDeviceControlIrp( 
		TDI_SEND_DATAGRAM, 
		deviceExtension->TDILowerDeviceObject, 
		deviceExtension->lpTransAddrFileObject, 
		&Event, 
		&IoStatus ); 
	if ( NULL == pIrp ) 
	{ 
		DebugPrint(("TDISendDatagram: Allocate Irp failed\n")); 
		return STATUS_INSUFFICIENT_RESOURCES; 
	} 
	VirtualAddress	= MmGetMdlVirtualAddress ( Irp->MdlAddress ); 
	Length			= MmGetMdlByteCount( Irp->MdlAddress ); 
	ASSERT ( VirtualAddress && Length ); 
 
	pMdl = IoAllocateMdl ( VirtualAddress, Length, FALSE, FALSE, pIrp ); 
	if ( NULL == pMdl ) 
	{ 
		DebugPrint(("TDISendDatagram: Allocate Mdl failed\n")); 
		return STATUS_INSUFFICIENT_RESOURCES; 
	} 
	_try 
	{ 
		MmProbeAndLockPages(pMdl,                     // (Try to) fix buffer. 
			KernelMode, 
			IoModifyAccess 
			); 
	} 
	_except( 
		pExceptionInfo = GetExceptionInformation(),                                                                                                                                                                                                     
		lclExceptionCode = pExceptionInfo->ExceptionRecord->ExceptionCode,                                                                                                                                                                              
		lclExceptionAddr = pExceptionInfo->ExceptionRecord->ExceptionAddress,                                                                                                                                                                           
		EXCEPTION_EXECUTE_HANDLER                                                                                                                                                                                                                       
		)                                                                                                                                                                                                                                                
	{                                                                                                                                                                                                                                                     
		DebugPrint((".TDISendDatagram:  MmProbeAndLockPages() failed.  Error = 0x%08x at 0x%08x\n", 
			lclExceptionCode, lclExceptionAddr)); 
		status = STATUS_UNSUCCESSFUL; 
		IoFreeMdl ( pMdl ); 
		CompleteRequest ( pIrp, status, 0 ); 
		Irp->IoStatus.Information = 0; 
		return status; 
	} 
	TdiBuildSendDatagram( 
		pIrp, 
		deviceExtension->TDILowerDeviceObject, 
		deviceExtension->lpTransAddrFileObject, 
		NULL, 
		NULL, 
		pMdl, 
		Length, 
		&RmtInfo 
		); 
	status = IoCallDriver ( deviceExtension->TDILowerDeviceObject, pIrp ); 
	if( ( status != STATUS_SUCCESS ) || ( STATUS_PENDING != status ) ) 
	{ 
		DebugPrint(("TDISendDatagram: Problem in IoCallDriver with status %x\n", status )); 
	} 
	if ( status == STATUS_PENDING ) 
	{ 
        LARGE_INTEGER TimeOut; 
		NTSTATUS		waitStatus; 
		DebugPrint(("Send data pending... wait for 3 seconds\n")); 
        TimeOut.QuadPart = -30000 * 1000;         // Calculate timeout value (5 seconds from now). 
		waitStatus = KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, &TimeOut); 
		if ( STATUS_TIMEOUT == waitStatus ) 
		{ 
			DebugPrint(("TDISendDatagram: Send data time out...\n")); 
			IoCancelIrp ( pIrp ); 
			KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, 0); 
		} 
	} 
	Irp->IoStatus.Information = IoStatus.Information; 
	DebugPrint(("TDISendDatagram: Send %d bytes\n", Irp->IoStatus.Information )); 
	status = ((STATUS_SUCCESS==status) || (STATUS_PENDING== status)) ? IoStatus.Status : status; 
	return status; 
} 
NTSTATUS TDIEventError( 
   IN PVOID TdiEventContext,  // The endpoint's file object. 
   IN NTSTATUS Status         // Status code indicating error type. 
   ) 
/*++ 
	Tdi error handle. 
--*/ 
{ 
   DebugPrint(("Sample ErrorEventHandler: Status: 0x%8.8X\n", Status) ); 
 
   return( STATUS_SUCCESS ); 
} 
 
NTSTATUS TDIEventRecvDatagram( 
    IN PVOID  TdiEventContext, 
    IN LONG  SourceAddressLength, 
    IN PVOID  SourceAddress, 
    IN LONG  OptionsLength, 
    IN PVOID  Options, 
    IN ULONG  ReceiveDatagramFlags, 
    IN ULONG  BytesIndicated, 
    IN ULONG  BytesAvailable, 
    OUT ULONG  *BytesTaken, 
    IN PVOID  Tsdu, 
    OUT PIRP  *IoRequestPacket 
    ) 
/*++ 
 
Routine Description: 
 
  TDI_EVENT_RECEIVE_DATAGRAM handle. 
  When a UDP packet from particular address and port arrive, this callback function will be called. 
  Usually BytesIndicated is less than or equal to BytesAvailable. If less, a Irp should be buildt 
  for the remainder data. This irp has be pre-allocated by TDIBuildRecvContext. 
 
Note: 
	Don't forget to call IoSetNextIrpStackLocation to handle the IoRequestPacket. 
	Because the stack locations of this irp between this function and underlying transport are identical. 
 
  Pls refer to Windows DDK documentation chapter relevant to this handle. 
 
--*/ 
{ 
	LONG	i; 
	PUCHAR	p; 
	PIRP	pIrp; 
 
	NTSTATUS	status; 
	ULONG	BytesToCopy; 
	ULONG	stageSize; 
	PDEVICE_EXTENSION	deviceExtension; 
 
	deviceExtension = (PDEVICE_EXTENSION)TdiEventContext; 
	DebugPrint(("SourceAddress: ")); 
	p = (PUCHAR)SourceAddress; 
	for( i = 0; i < SourceAddressLength; i++) 
	{ 
		DbgPrint("%2x ", p[ i ] ); 
	} 
	DbgPrint(("\n")); 
	DebugPrint(("ReceiveDatagramFlags: %x\n", ReceiveDatagramFlags )); 
	DebugPrint(("BytesIndicated: %d\n", BytesIndicated )); 
	DebugPrint(("BytesAvailable: %d\n", BytesAvailable )); 
	*BytesTaken = BytesIndicated; 
	CopyToRingBuffer( deviceExtension, (PUCHAR)Tsdu, BytesIndicated ); 
 
	*IoRequestPacket = NULL; 
	status = STATUS_SUCCESS; 
	if ( BytesIndicated < BytesAvailable ) 
	{ 
		if ( deviceExtension->recvContext.pIrp ) 
		{ 
			pIrp = deviceExtension->recvContext.pIrp; 
			DebugPrint(("EventRecvDatagram: pIrp = %x\n", deviceExtension->recvContext.pIrp )); 
			TdiBuildReceiveDatagram( pIrp, 
				deviceExtension->TDILowerDeviceObject, 
				deviceExtension->lpTransAddrFileObject, 
				TDIRecvRemainderCompRoutine, 
				deviceExtension, 
				deviceExtension->recvContext.pMdl, 
				0, 
				&(deviceExtension->recvContext.ReceiveDatagramInfo), 
				&(deviceExtension->recvContext.ReturnInfo), 
				TDI_RECEIVE_NORMAL); 
			IoSetNextIrpStackLocation ( pIrp ); 
			*IoRequestPacket = pIrp; 
			status = STATUS_MORE_PROCESSING_REQUIRED; 
		} 
	} 
	return status; 
} 
VOID CopyToRingBuffer( 
	PDEVICE_EXTENSION deviceExtension, 
	PUCHAR	LinearBuffer, 
	ULONG	BytesIndicated ) 
/*++ 
 
Routine Description: 
 
  This function is called by TDIEventRecvDatagram and TDIRecvRemainderCompRoutine. 
 
  It copies linear buffer containing network packet to ring buffer. If source buffer length 
  is more than the length of ReceiveBuffer, the last part of LinearBuffer will be copied 
  to destination buffer. 
 
  If there is WaitOnMaskIrp in deviceExtension and the WaitMask meets SERIAL_EV_RXCHAR || SERIAL_EV_RLSD, 
  it will complete the WaitOnMaskIrp to indicate the application that there are some data arrive. 
 
  The destination ringbuffer, deviceExtension->RxBuffer, is shared with IRP_MJ_READ. 
  So the access to the ringbuffer must be synchronous. 
	 
Arguments: 
 
  deviceExtension	- pointer to device object extension 
  LinearBuffer		- Tsdu containing network packets. 
  BytesIndicated	- the length of LinearBuffer 
 
Return Value: 
 
    NT status code. 
 
--*/ 
{ 
	ULONG	stageSize; 
	KIRQL	oldIrql; 
	ULONG	BytesToCopy; 
	PUCHAR	lpSrc; 
 
	BytesToCopy = BytesIndicated; 
	lpSrc		= LinearBuffer; 
	if ( BytesIndicated > RINGBUFFER_SIZE ) 
	{ 
		lpSrc		= (PUCHAR)LinearBuffer + BytesIndicated - RINGBUFFER_SIZE; 
		BytesToCopy = RINGBUFFER_SIZE; 
	} 
	KeAcquireSpinLock(&deviceExtension->ThreadSpinLock, &oldIrql); 
	stageSize = deviceExtension->RxBuffer + RINGBUFFER_SIZE - deviceExtension->lpRx; 
	if( BytesToCopy <= stageSize ) 
		RtlCopyMemory ( deviceExtension->lpRx, lpSrc, BytesToCopy ); 
	else 
	{ 
		RtlCopyMemory ( deviceExtension->lpRx, lpSrc, stageSize ); 
		RtlCopyMemory ( deviceExtension->RxBuffer, lpSrc + stageSize, ( BytesToCopy - stageSize ) ); 
	} 
	deviceExtension->lpRx = deviceExtension->RxBuffer + ( ( RINGBUFFER_SIZE - stageSize + BytesToCopy ) % RINGBUFFER_SIZE ); 
	deviceExtension->SerialStatus.AmountInInQueue += BytesToCopy; 
	if ( deviceExtension->SerialStatus.AmountInInQueue > RINGBUFFER_SIZE ) 
	{ 
		deviceExtension->lpRead = deviceExtension->lpRx; 
		deviceExtension->SerialStatus.AmountInInQueue = RINGBUFFER_SIZE; 
	} 
	if( ( deviceExtension->WaitOnMaskIrp ) && 
		( ( deviceExtension->WaitMask & SERIAL_EV_RXCHAR ) || ( deviceExtension->WaitMask & SERIAL_EV_RLSD ) ) ) 
	{ 
		DebugPrint(("ClientEventRecv: Complete WaitOnMaskIrp\n")); 
		deviceExtension->SerialStatus.EofReceived = TRUE; 
		*(PULONG)deviceExtension->WaitOnMaskIrp->AssociatedIrp.SystemBuffer = ( SERIAL_EV_RXCHAR | SERIAL_EV_RLSD ); 
		CompleteRequest( deviceExtension->WaitOnMaskIrp, STATUS_SUCCESS, sizeof( ULONG ) ); 
		deviceExtension->WaitOnMaskIrp = NULL; 
		SampleIoDecrement( deviceExtension ); 
	} 
	KeReleaseSpinLock( &deviceExtension->ThreadSpinLock, oldIrql );	 
} 
NTSTATUS TDIBuildRecvContext( PRECV_CONTEXT lpContext) 
/*++ 
 
Routine Description: 
 
  This function is called by InitializeConnection. 
  It builds RecvContext, including pIrp, pMdl, and locks the Mdl. 
	 
Arguments: 
 
  lpContext	- pointer to deviceExtension->recvContext 
 
Return Value: 
 
    NT status code. 
 
--*/ 
{ 
	PIRP				pIrp; 
	PMDL				pMdl; 
	PDEVICE_EXTENSION		deviceExtension; 
	EXCEPTION_POINTERS        * pExceptionInfo; 
	ULONG                       lclExceptionCode; 
	PVOID                       lclExceptionAddr; 
	PIO_STACK_LOCATION	ioStack; 
 
	if( lpContext->pIrp || lpContext->pMdl || ( lpContext->RemainderBuffer == NULL ) ) 
		return STATUS_UNSUCCESSFUL; 
	deviceExtension = CONTAINING_RECORD( lpContext, DEVICE_EXTENSION, recvContext ); 
	pIrp = IoAllocateIrp ( deviceExtension->TDILowerDeviceObject->StackSize + 2, FALSE ); 
	/* 
	Because the irp will be used for many times, TdiBuildInternalDeviceControlIrp is not best. 
	*/ 
/*	pIrp = TdiBuildInternalDeviceControlIrp( 
		TDI_RECEIVE_DATAGRAM, 
		deviceExtension->TDILowerDeviceObject, 
		deviceExtension->lpTransAddrFileObject, 
		&lpContext->Event, 
		&lpContext->IoStatus 
		); 
*/ 
	if (NULL == pIrp) 
	{ 
		DebugPrint(("TdiBuildInternalIrp failed\n")); 
		return STATUS_INSUFFICIENT_RESOURCES; 
	}else 
	{ 
		DebugPrint(("TDIBuildRecvContext: pIrp = %x\n", pIrp )); 
	} 
	pMdl = IoAllocateMdl ( lpContext->RemainderBuffer, 
		RECVREMAINDER_BUFFER_SIZE, FALSE, FALSE, NULL ); 
	if (NULL==pMdl) 
	{ 
		goto Error_Exit; 
	} 
	_try 
	{ 
		// lockpage the Mdl, even though it is allocated based on NonPagesPool 
		MmProbeAndLockPages(pMdl,                     // (Try to) fix buffer. 
			KernelMode, 
			IoModifyAccess 
			); 
		lpContext->bLocked = TRUE; 
		DebugPrint(("RemainderBuffer: 0x%x,  pMdl: 0x%x\n", lpContext->RemainderBuffer, pMdl )); 
	} 
	_except( 
		pExceptionInfo = GetExceptionInformation(),                                                                                                                                                                                                     
		lclExceptionCode = pExceptionInfo->ExceptionRecord->ExceptionCode,                                                                                                                                                                              
		lclExceptionAddr = pExceptionInfo->ExceptionRecord->ExceptionAddress,                                                                                                                                                                           
		EXCEPTION_EXECUTE_HANDLER                                                                                                                                                                                                                       
		)                                                                                                                                                                                                                                                
	{                                                                                                                                                                                                                                                     
		DebugPrint((".TDIClnRecv:  MmProbeAndLockPages() failed.  Error = 0x%08x at 0x%08x\n", 
			lclExceptionCode, lclExceptionAddr)); 
		goto Error_Exit; 
	} 
	lpContext->pIrp = pIrp; 
	lpContext->pMdl = pMdl; 
	return STATUS_SUCCESS; 
Error_Exit: 
	if ( pMdl ) 
		IoFreeMdl ( pMdl ); 
	if ( pIrp ) 
		IoFreeIrp ( pIrp ); 
	return STATUS_UNSUCCESSFUL; 
} 
NTSTATUS                                           
TDIRecvRemainderCompRoutine( 
	PDEVICE_OBJECT      DeviceObject, // TDI driver's device object. 
	PIRP                pIrp,    // Address of completed Irp. 
	PVOID               pCtx     // Pointer to context. 
	) 
/*++ 
 
Routine Description: 
 
  Completion routine of IoRequestPacket in TDIEventRecvDatagram. 
  It copies receiving data in recvContext.RemainderBuffer to RxBuffer. 
  The length of receining data is indicated by pIrp->IoStatus.Information. 
 
Arguments: 
 
    DeviceObject - pointer to the device object 
	pIrp		- pointer to completed Irp, which is recvContext->pIrp 
	pCtx		- pointer to context, which is deviceExtension 
 
Return Value: 
 
    NT status code. 
 
--*/ 
{ 
	PDEVICE_EXTENSION	deviceExtension; 
	PRECV_CONTEXT lpContext; 
	ULONG			RecvDataLength; 
	PUCHAR			lpSrc; 
 
	DebugPrint(("TDIRecvRemainderCompRoutine...\n")); 
	RecvDataLength = pIrp->IoStatus.Information; 
	deviceExtension = (PDEVICE_EXTENSION)pCtx; 
	DebugPrint(("Remainder length: %d\n", RecvDataLength )); 
	CopyToRingBuffer ( deviceExtension, 
		(PUCHAR)deviceExtension->recvContext.RemainderBuffer, 
		RecvDataLength ); 
	return STATUS_MORE_PROCESSING_REQUIRED; 
} 
NTSTATUS TDIFreeRecvContext( PRECV_CONTEXT lpContext ) 
/*++ 
 
Routine Description: 
 
	Free recvContext. 
 
Arguments: 
 
	lpContext	- pointer to recvContext. 
 
Return Value: 
 
    NT status code. 
 
--*/ 
{ 
	if ( lpContext->pIrp ) 
	{ 
		IoFreeIrp ( lpContext->pIrp ); 
		lpContext->pIrp = NULL; 
	} 
	if ( lpContext->bLocked ) 
	{ 
		MmUnlockPages ( lpContext->pMdl ); 
		lpContext->bLocked = FALSE; 
	} 
	if ( lpContext->pMdl ) 
	{ 
		IoFreeMdl ( lpContext->pMdl ); 
		lpContext->pMdl = NULL; 
	} 
	return STATUS_SUCCESS; 
} 
/**************************************************************************************************/ 
/*                                                                                                */ 
/* Build and send down an Irp for Receive.                                                        */ 
/*                                                                                                */ 
/* Notes:                                                                                         */ 
/*        1) This routine is NOT synchronous (no particular reason it's not, other than to show   */ 
/*           off asynchronous operation of an Irp).                                               */ 
/*                                                                                                */ 
/*        2) There are indications that an MDL and probe-and-lock are needed even if the buffer   */ 
/*           is from the non-paged pool.  Eg, microsoft.public.win32.programmer.kernel,           */ 
/*           "MmProbeAndLockPages bug checks," dave porter, 2001-03-02.  But see the further      */ 
/*           discussion in comp.os.ms-windows.programmer.nt.kernel-mode, "Free MDL in completion  */ 
/*           routine," 1999/05/09.                                                                */ 
/*                                                                                                */ 
/*        3) Although not documented, it appears that the Irp, the Mdl and the locking are undone */ 
/*           by the transport.  There are claims to that effect in several places in newsgroups,  */ 
/*           and freeing the MDL always caused errors in a test (eg, bad pool caller or touching  */ 
/*           paged storage at wrong IRQL).  Further, testing showed that IoFreeMdl() was being    */ 
/*           called by somebody (presumably, the transport) for a given MDL.                      */ 
/*                                                                                                */ 
/*        4) The supplied context (pCtx) is an event, which the I/O completion routine for the    */ 
/*           Receive Irp will signal.                                                             */ 
/*                                                                                                */ 
/**************************************************************************************************/ 
NTSTATUS TDIQueryNetworkInformation( PDEVICE_OBJECT DeviceObject, PIRP Irp ) 
{ 
	PDEVICE_EXTENSION deviceExtension; 
	PIO_STACK_LOCATION IrpStack; 
	IO_STATUS_BLOCK	IoStatus; 
	NTSTATUS	status; 
	ULONG		InputLength, OutputLength; 
	ULONG		QType; 
	PIRP		pIrp; 
	KEVENT		Event; 
	PMDL		pMdl; 
	ULONG_PTR	virtualAddress; 
	 
	EXCEPTION_POINTERS        * pExceptionInfo; 
	ULONG                       lclExceptionCode; 
	PVOID                       lclExceptionAddr; 
	BOOLEAN	pageLocked; 
 
	deviceExtension = DeviceObject->DeviceExtension; 
	IrpStack	= IoGetCurrentIrpStackLocation(Irp); 
	QType		= *((ULONG *)Irp->AssociatedIrp.SystemBuffer); 
	DebugPrint(("QueryNetworkInformation: QType = %x\n", QType )); 
	KeInitializeEvent(&Event, NotificationEvent, FALSE); 
	pIrp = TdiBuildInternalDeviceControlIrp(TDI_QUERY_INFORMATION , 
		deviceExtension->TDILowerDeviceObject,   // TDI driver's device object. 
		deviceExtension->lpTransAddrFileObject, // Address file object. 
		&Event,       // Event to be signalled when Irp completes. 
		&IoStatus     // I/O status block. 
		); 
	if (NULL==pIrp) 
        return STATUS_INSUFFICIENT_RESOURCES; 
	virtualAddress = (ULONG_PTR) MmGetMdlVirtualAddress(Irp->MdlAddress); 
	OutputLength	= MmGetMdlByteCount(Irp->MdlAddress); 
	pMdl = IoAllocateMdl( (PVOID)virtualAddress, OutputLength, FALSE, FALSE, NULL); 
	pageLocked = FALSE; 
	_try{ 
		MmProbeAndLockPages(pMdl, KernelMode, IoModifyAccess ); 
		pageLocked = TRUE; 
	} 
	_except( 
		pExceptionInfo = GetExceptionInformation(),                                                                                                                                                                                                     
		lclExceptionCode = pExceptionInfo->ExceptionRecord->ExceptionCode,                                                                                                                                                                              
		lclExceptionAddr = pExceptionInfo->ExceptionRecord->ExceptionAddress,                                                                                                                                                                           
		EXCEPTION_EXECUTE_HANDLER                                                                                                                                                                                                                       
		)                                                                                                                                                                                                                                                
	{                                                                                                                                                                                                                                                     
		DebugPrint((".TDIClnRecv:  MmProbeAndLockPages() failed.  Error = 0x%08x at 0x%08x\n", 
			lclExceptionCode, lclExceptionAddr)); 
		status = lclExceptionCode;                                                                                                                                                                                                                           
		goto done; 
	}          
	TdiBuildQueryInformation ( pIrp, deviceExtension->TDILowerDeviceObject, 
		deviceExtension->lpTransAddrFileObject, 
		NULL, NULL, QType, pMdl ); 
	status = IoCallDriver ( deviceExtension->TDILowerDeviceObject, pIrp ); 
	if (STATUS_PENDING==status) 
		KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, 0); 
done: 
	status = ((STATUS_SUCCESS==status) || (STATUS_PENDING==status)) ? IoStatus.Status : status; 
	Irp->IoStatus.Information = IoStatus.Information; 
	DebugPrint(("QueryInfo: status %x, Info %d\n", status, Irp->IoStatus.Information )); 
 
	return status; 
} 
 
NTSTATUS 
TDIClnSetEventHandler( 
                      PFILE_OBJECT   pAddrFileObj,    // Address file object. 
                      PDEVICE_OBJECT pTcpDevObj,      // TDI driver's device object. 
                      LONG           EventType,       // Type of event. 
                      PVOID          pEventHandler,   // Event handler routine. 
                      PVOID          pEventContext    // Context for event handler. 
					  ) 
/**************************************************************************************************/ 
/*                                                                                                */ 
/* Set up an event handler.                                                                       */ 
/*                                                                                                */ 
/* Note:  This routine is synchronous.                                                            */ 
/*                                                                                                */ 
/**************************************************************************************************/ 
{ 
	NTSTATUS            status; 
	KEVENT              Event; 
	IO_STATUS_BLOCK     IoStatus; 
	PIRP				pIrp; 
	do {                                                 // Single-iteration loop, to make possible escape via break. 
		KeInitializeEvent(&Event, NotificationEvent, FALSE); 
		// Get an Irp for internal device ioctl. 
		pIrp = TdiBuildInternalDeviceControlIrp(TDI_SET_EVENT_HANDLER, 
			pTcpDevObj,   // TDI driver's device object. 
			pAddrFileObj, // Address file object. 
			&Event,       // Event to be signalled when Irp completes. 
			&IoStatus     // I/O status block. 
			); 
		if (NULL==pIrp) 
		{ 
			status = STATUS_INSUFFICIENT_RESOURCES; 
			break; 
		} 
		TdiBuildSetEventHandler(pIrp, 
			pTcpDevObj, 
			pAddrFileObj, 
			NULL,                    // I/O completion routine. 
			NULL,                    // Context for I/O completion routine. 
			EventType, 
			pEventHandler,           // Event handler routine. 
			pEventContext            // Context for event handler routine. 
			); 
		 
		status = IoCallDriver(pTcpDevObj, pIrp); 
		 
		if (STATUS_PENDING==status)                      // Have to wait on this Irp? 
			KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, 0); 
// Problem from IoCallDriver() or Problem discovered in completion? 
		if ( ( STATUS_SUCCESS!=status && STATUS_PENDING!=status	) 
			|| ( STATUS_PENDING==status && 0!=IoStatus.Status ) ) 
		{ 
			// Note:  If problem was in IoCallDriver(), IoStatus.Status probably won't be meaningful. 
			DebugPrint(("TDIClnSetEventHandler:  Problem in IoCallDriver().  status = 0x%08x, IoStatus.Status = 0x%08x\n", 
				STATUS_PENDING==status ? 0 : status , IoStatus.Status)); 
		} 
    } while(0);                                       // End 'do-while' single-iteration loop. 
	 
	status = ((STATUS_SUCCESS==status) || (STATUS_PENDING==status)) ? IoStatus.Status : status; 
	 
	return status; 
}                                                     // End TDIClnSetEventHandler(). 
 
 
NTSTATUS 
TDIOpenTransportAddress( 
                    PWSTR          pTransDevName,     // Transport device name. 
                    PHANDLE        pHandle,           // Output handle address. 
                    PFILE_OBJECT * ppAddrFileObj,     // Output address file object. 
                    USHORT         Port               // Port to open. 
					) 
/**************************************************************************************************/ 
/*                                                                                                */ 
/* Open transport address.                                                                        */ 
/*                                                                                                */ 
/**************************************************************************************************/ 
{ 
	NTSTATUS                    status; 
	UNICODE_STRING              TransDeviceName; 
	OBJECT_ATTRIBUTES           Attr; 
	IO_STATUS_BLOCK             IoStatus; 
	PTA_IP_ADDRESS				pSin; 
	PFILE_FULL_EA_INFORMATION	pEa; 
	char Buffer[sizeof(FILE_FULL_EA_INFORMATION) + TDI_TRANSPORT_ADDRESS_LENGTH + sizeof(TA_IP_ADDRESS)]; 
	 
	ASSERT ( *pHandle == NULL ); 
	ASSERT ( *ppAddrFileObj == NULL ); 
	RtlInitUnicodeString(&TransDeviceName,               // Build Unicode transport device name. 
		pTransDevName 
		); 
	 
	InitializeObjectAttributes(&Attr,                    // Attributes (to be initialized); 
		&TransDeviceName, 
		OBJ_CASE_INSENSITIVE 
		| 
		OBJ_KERNEL_HANDLE,        // Only in kernel mode but in any process. 
		0, 
		0 
		); 
	pEa					= (PFILE_FULL_EA_INFORMATION)Buffer; 
	pEa->NextEntryOffset= 0; 
	pEa->Flags			= 0; 
	pEa->EaNameLength	= TDI_TRANSPORT_ADDRESS_LENGTH;    // Length of TdiTransportAddress, namely of "TransportAddress" string less 1 (ie, without terminator). 
	RtlCopyMemory ( pEa->EaName, TdiTransportAddress, pEa->EaNameLength + 1 ); 
	pEa->EaValueLength	= sizeof(TA_IP_ADDRESS);          // Length of structure. 
 
    // Point to Buffer just after what's been used (ie, after terminator). 
	pSin				=(PTA_IP_ADDRESS) ( pEa->EaName + pEa->EaNameLength + 1); 
	pSin->TAAddressCount= 1;                                                                                                                                                                                                                                      
	pSin->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP; 
	pSin->Address[0].AddressType = TDI_ADDRESS_TYPE_IP; 
	pSin->Address[0].Address[0].sin_port = Port; 
	pSin->Address[0].Address[0].in_addr = 0;//0x601a8c0;		// 192.168.1.6 
	RtlZeroMemory ( pSin->Address[ 0 ].Address[ 0 ].sin_zero, sizeof ( pSin->Address[ 0 ].Address[ 0 ].sin_zero ) ); 
	 
	status = ZwCreateFile(pHandle,                       // Actual open of transport address. 
		GENERIC_READ | GENERIC_WRITE, 
		&Attr, 
		&IoStatus, 
		0, 
		FILE_ATTRIBUTE_NORMAL, 
		FILE_SHARE_READ, 
		FILE_OPEN_IF, 
		0, 
		pEa,	//NULL, 
		sizeof ( Buffer ) 
		); 
	 
	if (!NT_SUCCESS(status)) 
	{ 
		DebugPrint(("ZwCreateFile failed with status %x\n", status )); 
		return status; 
	} 
	 
	status = ObReferenceObjectByHandle(*pHandle,         // Get the object pointer for the address file object's handle. 
		GENERIC_READ | GENERIC_WRITE, 
		0, 
		KernelMode, 
		(PVOID *)ppAddrFileObj, 
		NULL 
		); 
	 
	if (!NT_SUCCESS(status)) 
	{ 
		DebugPrint(("ObReferenceObject failed with status %x\n", status )); 
		ZwClose ( *pHandle ); 
		*pHandle = NULL; 
	} 
	return status; 
}