Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1239 lines
39 KiB

/****************************************************************************/
// tdapi.c
//
// Common code for all Transport Drivers
//
// Typical connection sequence:
//
// TdLoad load driver
// TdOpen open driver (parameters)
// StackCreateEndpoint create new endpoint
// StackConnectionWait establish client connection (endpoint)
// TdClose close driver (does not close endpoint)
// TdUnload unload driver
//
// TdLoad load driver
// TdOpen open driver
// StackOpenEndpoint bind to an existing endpoint
// StackConnectionSend initialize host module data sent to client
//
// (connected session)
//
// StackCloseEndpoint disconnect client connection
// TdClose close driver
// TdUnload unload driver
//
// Copyright (C) 1997-2000 Microsoft Corporation
/****************************************************************************/
#include <ntddk.h>
#include <ntddvdeo.h>
#include <ntddkbd.h>
#include <ntddmou.h>
#include <ntddbeep.h>
#include <winstaw.h>
#include <icadd.h>
#include <sdapi.h>
#include <td.h>
#define REG_GUID_TABLE L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Terminal Server\\lanatable"
#define LANA_ID L"LanaId"
/*=============================================================================
== External Functions Defined
=============================================================================*/
NTSTATUS ModuleEntry( PSDCONTEXT, BOOLEAN );
NTSTATUS TdLoad( PSDCONTEXT );
NTSTATUS TdUnload( PSDCONTEXT );
NTSTATUS TdOpen( PTD, PSD_OPEN );
NTSTATUS TdClose( PTD, PSD_CLOSE );
NTSTATUS TdRawWrite( PTD, PSD_RAWWRITE );
NTSTATUS TdChannelWrite( PTD, PSD_CHANNELWRITE );
NTSTATUS TdSyncWrite( PTD, PSD_SYNCWRITE );
NTSTATUS TdIoctl( PTD, PSD_IOCTL );
/*=============================================================================
== Internal Functions Defined
=============================================================================*/
NTSTATUS _TdInitializeWrite( PTD, POUTBUF );
NTSTATUS _TdWriteCompleteRoutine( PDEVICE_OBJECT, PIRP, PVOID );
VOID _TdWriteCompleteWorker( PTD, PVOID );
/*=============================================================================
== Functions used
=============================================================================*/
NTSTATUS DeviceOpen( PTD, PSD_OPEN );
NTSTATUS DeviceClose( PTD, PSD_CLOSE );
NTSTATUS DeviceInitializeWrite( PTD, POUTBUF );
NTSTATUS DeviceIoctl( PTD, PSD_IOCTL );
NTSTATUS StackCreateEndpoint( PTD, PSD_IOCTL );
NTSTATUS StackCdCreateEndpoint( PTD, PSD_IOCTL );
NTSTATUS StackCallbackInitiate( PTD, PSD_IOCTL );
NTSTATUS StackCallbackComplete( PTD, PSD_IOCTL );
NTSTATUS StackOpenEndpoint( PTD, PSD_IOCTL );
NTSTATUS StackCloseEndpoint( PTD, PSD_IOCTL );
NTSTATUS StackConnectionWait( PTD, PSD_IOCTL );
NTSTATUS StackConnectionSend( PTD, PSD_IOCTL );
NTSTATUS StackConnectionRequest( PTD, PSD_IOCTL );
NTSTATUS StackQueryParams( PTD, PSD_IOCTL );
NTSTATUS StackSetParams( PTD, PSD_IOCTL );
NTSTATUS StackQueryLastError( PTD, PSD_IOCTL );
NTSTATUS StackWaitForStatus( PTD, PSD_IOCTL );
NTSTATUS StackCancelIo( PTD, PSD_IOCTL );
NTSTATUS StackSetBrokenReason( PTD, PSD_IOCTL );
NTSTATUS StackQueryRemoteAddress( PTD, PSD_IOCTL );
VOID OutBufFree( PTD, POUTBUF );
VOID OutBufError( PTD, POUTBUF );
NTSTATUS MemoryAllocate( ULONG, PVOID * );
VOID MemoryFree( PVOID );
/*=============================================================================
== Static global data
=============================================================================*/
/*
* Transport driver procedures
*/
PSDPROCEDURE G_pTdProcedures[] =
{
TdOpen,
TdClose,
TdRawWrite,
TdChannelWrite,
TdSyncWrite,
TdIoctl,
};
/*******************************************************************************
* ModuleEntry
*
* ICA driver entry point.
*
* pContext (input/output)
* pointer to the SD context structure
* fLoad (input)
* TRUE - load driver
* FALSE - unload driver
******************************************************************************/
NTSTATUS ModuleEntry(PSDCONTEXT pContext, BOOLEAN fLoad)
{
if (fLoad)
return TdLoad(pContext);
else
return TdUnload(pContext);
}
/*******************************************************************************
* TdLoad
*
* The ICA driver directly calls this routine immediately after loading
* this transport driver.
*
* 1) initialize procedure dispatch table
* 2) allocate transport driver data structure
******************************************************************************/
NTSTATUS TdLoad(PSDCONTEXT pContext)
{
NTSTATUS Status;
PTD pTd;
/*
* Initialize td procedures
*/
pContext->pProcedures = G_pTdProcedures;
/*
* Since this is the last stack driver there are no callup procedures
*/
pContext->pCallup = NULL;
/*
* Allocate TD data structure
*/
Status = MemoryAllocate( sizeof(TD), &pTd );
if (Status == STATUS_SUCCESS) {
RtlZeroMemory(pTd, sizeof(TD));
pTd->pContext = pContext;
pContext->pContext = pTd;
}
else {
TRACE((pContext, TC_TD, TT_ERROR, "TdLoad: Failed alloc TD\n"));
}
return Status;
}
/*******************************************************************************
* TdUnload
*
* The ICA driver directly calls this routine immediately after closing
* this transport driver.
*
* 1) free all transport driver data structures
******************************************************************************/
NTSTATUS TdUnload(PSDCONTEXT pContext)
{
PTD pTd;
/*
* Get pointers to TD data structures
*/
pTd = pContext->pContext;
/*
* Free TD private data structures
*/
if (pTd->pPrivate)
MemoryFree(pTd->pPrivate);
if (pTd->pAfd)
MemoryFree(pTd->pAfd);
/*
* Free TD data structure
*/
MemoryFree(pTd);
/*
* Clear context structure
*/
pContext->pContext = NULL;
pContext->pProcedures = NULL;
pContext->pCallup = NULL;
return STATUS_SUCCESS;
}
/*******************************************************************************
* TdOpen
*
* The ICA driver directly calls this routine immediately after loading
* this transport driver.
*
* 1) initialize transport driver parameters
* 2) call device specfic open
* 3) allocate data buffers
*
* ENTRY:
* pTd (input)
* Pointer to TD data structure
* pSdOpen (input/output)
* Points to the parameter structure SD_OPEN.
******************************************************************************/
NTSTATUS TdOpen(PTD pTd, PSD_OPEN pSdOpen)
{
SD_CLOSE SdClose;
NTSTATUS Status;
/*
* Initialize TD data structure
*/
InitializeListHead( &pTd->IoBusyOutBuf );
pTd->InBufCount = 1;
KeInitializeSpinLock( &pTd->InBufListLock );
InitializeListHead( &pTd->InBufBusyHead );
InitializeListHead( &pTd->InBufDoneHead );
InitializeListHead( &pTd->WorkItemHead );
pTd->pClient = pSdOpen->pClient;
pTd->pStatus = pSdOpen->pStatus;
pTd->PdFlag = pSdOpen->PdConfig.Create.PdFlag;
pTd->OutBufLength = pSdOpen->PdConfig.Create.OutBufLength;
pTd->PortNumber = pSdOpen->PdConfig.Create.PortNumber;
pTd->Params = pSdOpen->PdConfig.Params;
pTd->UserBrokenReason = TD_USER_BROKENREASON_UNEXPECTED;
/*
* Open device
*/
Status = DeviceOpen(pTd, pSdOpen);
if (NT_SUCCESS(Status)) {
/*
* Save size of header and trailer for td
*/
pTd->OutBufHeader = pSdOpen->SdOutBufHeader;
pTd->OutBufTrailer = pSdOpen->SdOutBufTrailer;
KeInitializeEvent(&pTd->SyncWriteEvent, NotificationEvent, FALSE);
TRACE((pTd->pContext, TC_TD, TT_API1, "TdOpen: success\n"));
}
else {
DeviceClose(pTd, &SdClose);
TRACE((pTd->pContext, TC_TD, TT_ERROR, "TdOpen, Status=0x%x\n", Status));
}
return Status;
}
/*******************************************************************************
* TdClose
*
* The ICA driver directly calls this routine immediately before unloading
* this transport driver.
*
* NOTE: This does NOT terminate the client connection
*
* 1) cancel all i/o (returns all OUTBUFs)
* 2) terminate read thread
* 3) free data buffers
* 4) call device specific close
*
* pTd (input)
* Pointer to TD data structure
* pSdClose (input/output)
* Points to the parameter structure SD_CLOSE.
******************************************************************************/
NTSTATUS TdClose(PTD pTd, PSD_CLOSE pSdClose)
{
NTSTATUS Status;
TRACE((pTd->pContext, TC_TD, TT_API1, "TdClose: (enter)\n"));
/*
* Cancel all pending i/o (read thread)
*/
(VOID)StackCancelIo(pTd, NULL);
/*
* Return size of header and trailer for pd
*/
pSdClose->SdOutBufHeader = pTd->OutBufHeader;
pSdClose->SdOutBufTrailer = pTd->OutBufTrailer;
/*
* All reads and writes should have previously been canceled
*/
ASSERT( pTd->fClosing );
ASSERT( IsListEmpty( &pTd->IoBusyOutBuf ) );
/*
* Wait for input thread to exit
*/
if (pTd->pInputThread) {
Status = IcaWaitForSingleObject(pTd->pContext, pTd->pInputThread, 60000);
if ( !NT_SUCCESS(Status) && (Status!=STATUS_CTX_CLOSE_PENDING) ) {
DbgPrint("TdClose: wait for the input thread to exit failed: status=%x pTd=%p\n", Status, pTd);
ASSERT( NT_SUCCESS(Status) || (Status==STATUS_CTX_CLOSE_PENDING) );
}
/*
* Dereference input thread if it hasn't been already
* (it may have been done in StackCallbackComplete while we waited).
*/
if (pTd->pInputThread) {
ObDereferenceObject(pTd->pInputThread);
pTd->pInputThread = NULL;
}
}
/*
* Close device
*/
Status = DeviceClose(pTd, pSdClose);
TRACE((pTd->pContext, TC_TD, TT_API1, "TdClose: Status=0x%x\n", Status));
return Status;
}
/*******************************************************************************
* _TdInitializeWrite
*
* Initialize the supplied OutBuf and corresponding IRP for writing.
*
* pTd (input)
* Pointer to td data structure
* pOutBuf (input/output)
* Points to the OutBuf to be initialized for writing
******************************************************************************/
__inline NTSTATUS _TdInitializeWrite(PTD pTd, POUTBUF pOutBuf)
{
PIRP irp = pOutBuf->pIrp;
PIO_STACK_LOCATION irpSp;
NTSTATUS Status;
/*
* Make sure endpoint is open
*/
if (pTd->pDeviceObject != NULL) {
// Set current thread for IoSetHardErrorOrVerifyDevice.
irp->Tail.Overlay.Thread = PsGetCurrentThread();
// Get a pointer to the stack location of the first driver which will be
// invoked. This is where the function codes and the parameters are set.
irpSp = IoGetNextIrpStackLocation(irp);
// Set the major function code, file/device objects, and write
// parameters.
irpSp->FileObject = pTd->pFileObject;
irpSp->DeviceObject = pTd->pDeviceObject;
irp->Flags = 0;
return STATUS_SUCCESS;
}
else {
return STATUS_CTX_CLOSE_PENDING;
}
}
/*******************************************************************************
* TdRawWrite
*
* The up stream stack driver calls this routine when it has data
* to write to the transport. This data has all the necessary
* headers and trailers already appended.
*
* The OUTBUF pointed to by this write request must always be
* returned to the up stream stack driver after the write completes
* successfully or unsuccessfully.
*
* 1) call device specific write
* 2) return OUTBUF after write completes (OutBufFree)
* return OUTBUF after an error (OutBufError)
*
* pTd (input)
* Pointer to td data structure
* pSdRawWrite (input)
* Points to the parameter structure SD_RAWWRITE
******************************************************************************/
NTSTATUS TdRawWrite(PTD pTd, PSD_RAWWRITE pSdRawWrite)
{
POUTBUF pOutBuf;
NTSTATUS Status;
PLIST_ENTRY pWorkItem = NULL;
KIRQL oldIrql;
pOutBuf = pSdRawWrite->pOutBuf;
ASSERT(pOutBuf);
// Check if driver is being closed
if (!pTd->fClosing) {
// See if we have had too many consecutive write errors
if (pTd->WriteErrorCount <= pTd->WriteErrorThreshold) {
// Initialize the IRP contained in the outbuf.
Status = _TdInitializeWrite(pTd, pOutBuf);
if (NT_SUCCESS(Status)) {
// Let the device level code complete the IRP initialization.
Status = DeviceInitializeWrite(pTd, pOutBuf);
if (NT_SUCCESS(Status)) {
// Update the MDL byte count to reflect the exact number
// of bytes to send.
pOutBuf->pMdl->ByteCount = pOutBuf->ByteCount;
// Save our TD structure pointer in the OUTBUF
// so the I/O completion routine can get it.
pOutBuf->pPrivate = pTd;
// Insert outbuf on busy list
InsertTailList(&pTd->IoBusyOutBuf, &pOutBuf->Links);
// Preallocate a completion workitem now and chain it to list of workitems.
Status = IcaAllocateWorkItem(&pWorkItem);
if (!NT_SUCCESS(Status)) {
//
//we inserted the outbuf into the list. In badwrite below,
//we reinitialize this entry and we free it (or return to the pool)
//so, we need to remove this outbuf entry from the list
//
TRACE((pTd->pContext, TC_TD, TT_OUT1,
"TdRawWrite : No memory to allocate WorkItem. Removing Outbuf from the list %04u, %p\n",
pOutBuf->ByteCount, pOutBuf));
RemoveEntryList( &pOutBuf->Links );
goto badwrite;
}
ExAcquireSpinLock( &pTd->InBufListLock, &oldIrql );
InsertTailList( &pTd->WorkItemHead, pWorkItem );
ExReleaseSpinLock( &pTd->InBufListLock, oldIrql );
// Register I/O completion routine
if ( pTd->pSelfDeviceObject == NULL ) {
IoSetCompletionRoutine(pOutBuf->pIrp,
_TdWriteCompleteRoutine, pOutBuf, TRUE, TRUE,
TRUE);
} else {
IoSetCompletionRoutineEx(pTd->pSelfDeviceObject,
pOutBuf->pIrp,
_TdWriteCompleteRoutine, pOutBuf, TRUE, TRUE,
TRUE);
}
// Call the device driver
// From this point on we must NOT free the outbuf.
// It will be free'd by the write complete routine.
Status = IoCallDriver(pTd->pDeviceObject, pOutBuf->pIrp);
if (NT_SUCCESS(Status)) {
// Update output counters
pTd->pStatus->Output.Bytes += pOutBuf->ByteCount;
pTd->pStatus->Output.Frames++;
TRACE((pTd->pContext, TC_TD, TT_OUT1,
"TdRawWrite %04u, %08x\n",
pOutBuf->ByteCount, pOutBuf));
TRACEBUF((pTd->pContext, TC_TD, TT_ORAW,
pOutBuf->pBuffer, pOutBuf->ByteCount));
Status = STATUS_SUCCESS;
}
else {
//
//for some reason, IoCallDriver failed (probably a out of memory?)
//in this case, we are leaking the WorkItem and Outbuf because
//we may never a get a call into our completion routine?
//do we need to remove the workitem and outbuf from the list here and free it?
//
goto badcalldriver;
}
}
else {
goto badwrite;
}
}
else {
goto badwrite;
}
}
else {
OutBufError(pTd, pOutBuf);
TRACE((pTd->pContext, TC_TD, TT_API2,
"TdRawWrite: WriteErrorThreshold exceeded\n"));
Status = pTd->LastError;
}
}
else {
OutBufError(pTd, pOutBuf);
TRACE((pTd->pContext, TC_TD, TT_API2, "TdRawWrite: closing\n"));
Status = STATUS_CTX_CLOSE_PENDING;
}
return Status;
/*=============================================================================
== Error returns
=============================================================================*/
/*
* write completed with an error
*/
badwrite:
InitializeListHead( &pOutBuf->Links );
OutBufError(pTd, pOutBuf);
/*
* IoCallDriver returned an error
* NOTE: We must NOT free the outbuf here.
* It will be free'd by the write complete routine.
*/
badcalldriver:
TRACE(( pTd->pContext, TC_TD, TT_OUT1, "TdRawWrite, Status=0x%x\n", Status ));
pTd->LastError = Status;
pTd->WriteErrorCount++;
pTd->pStatus->Output.TdErrors++;
if (pTd->WriteErrorCount < pTd->WriteErrorThreshold)
Status = STATUS_SUCCESS;
return Status;
}
/*******************************************************************************
* TdChannelWrite - channel write
*
* This routine should never be called
*
* pTd (input)
* Pointer to td data structure
* pSdChannelWrite (input)
* Points to the parameter structure SD_CHANNELWRITE
******************************************************************************/
NTSTATUS TdChannelWrite(PTD pTd, PSD_CHANNELWRITE pSdChannelWrite)
{
return STATUS_INVALID_DEVICE_REQUEST;
}
/*******************************************************************************
* TdSyncWrite
*
* This routine is called by the up stream stack driver to wait
* for all pending writes to complete.
*
* 1) wait for all writes to complete
* 2) return all OUTBUFs
*
* pTd (input)
* Pointer to td data structure
* pSdFlush (input)
* Points to the parameter structure SD_FLUSH
******************************************************************************/
NTSTATUS TdSyncWrite(PTD pTd, PSD_SYNCWRITE pSdSyncWrite)
{
NTSTATUS Status;
TRACE(( pTd->pContext, TC_TD, TT_OUT1, "TdSyncWrite (enter)\n" ));
/*
* Return if there are no writes pending
*/
if (IsListEmpty(&pTd->IoBusyOutBuf))
return STATUS_SUCCESS;
/*
* Reset sync event and indicate we are waiting
*/
if (!pTd->fSyncWriteWaiter) {
pTd->fSyncWriteWaiter = TRUE;
KeResetEvent(&pTd->SyncWriteEvent);
}
/*
* Wait for event to be triggered
*/
Status = IcaWaitForSingleObject(pTd->pContext, &pTd->SyncWriteEvent, -1);
if (Status == STATUS_CTX_CLOSE_PENDING)
Status = STATUS_SUCCESS;
TRACE((pTd->pContext, TC_TD, TT_OUT1, "TdSyncWrite (exit)\n"));
return Status;
}
/*******************************************************************************
* TdIoctl
*
* This routine is called by the up stream stack driver. These
* ioctls are used to connect, disconnect, query parameters, and
* set parameters.
*
* pTd (input)
* Pointer to td data structure
* pSdIoctl (input/output)
* Points to the parameter structure SD_IOCTL
******************************************************************************/
NTSTATUS TdIoctl(PTD pTd, PSD_IOCTL pSdIoctl)
{
NTSTATUS Status;
switch (pSdIoctl->IoControlCode) {
case IOCTL_ICA_STACK_CREATE_ENDPOINT:
Status = StackCreateEndpoint(pTd, pSdIoctl);
break;
case IOCTL_ICA_STACK_OPEN_ENDPOINT:
Status = StackOpenEndpoint(pTd, pSdIoctl);
break;
case IOCTL_ICA_STACK_CLOSE_ENDPOINT:
StackCancelIo(pTd, pSdIoctl);
Status = StackCloseEndpoint(pTd, pSdIoctl);
break;
case IOCTL_ICA_STACK_CONNECTION_WAIT :
Status = StackConnectionWait(pTd, pSdIoctl);
break;
case IOCTL_ICA_STACK_CONNECTION_SEND :
Status = StackConnectionSend(pTd, pSdIoctl);
break;
case IOCTL_ICA_STACK_CONNECTION_REQUEST :
Status = StackConnectionRequest(pTd, pSdIoctl);
break;
case IOCTL_ICA_STACK_QUERY_PARAMS :
Status = StackQueryParams(pTd, pSdIoctl);
break;
case IOCTL_ICA_STACK_SET_PARAMS :
Status = StackSetParams(pTd, pSdIoctl);
break;
case IOCTL_ICA_STACK_QUERY_LAST_ERROR :
Status = StackQueryLastError(pTd, pSdIoctl);
break;
case IOCTL_ICA_STACK_WAIT_FOR_STATUS :
Status = StackWaitForStatus(pTd, pSdIoctl);
break;
case IOCTL_ICA_STACK_CANCEL_IO :
Status = StackCancelIo(pTd, pSdIoctl);
break;
case IOCTL_ICA_STACK_CD_CREATE_ENDPOINT :
Status = StackCdCreateEndpoint(pTd, pSdIoctl);
break;
case IOCTL_ICA_STACK_CALLBACK_INITIATE :
Status = StackCallbackInitiate(pTd, pSdIoctl);
break;
case IOCTL_ICA_STACK_CALLBACK_COMPLETE :
Status = StackCallbackComplete(pTd, pSdIoctl);
break;
case IOCTL_TS_STACK_QUERY_REMOTEADDRESS:
Status = StackQueryRemoteAddress( pTd, pSdIoctl);
break;
case IOCTL_ICA_STACK_QUERY_STATE :
case IOCTL_ICA_STACK_SET_STATE :
case IOCTL_ICA_STACK_ENABLE_DRIVER :
case IOCTL_ICA_STACK_CONNECTION_QUERY :
Status = STATUS_SUCCESS;
break;
case IOCTL_ICA_STACK_SET_BROKENREASON:
Status = StackSetBrokenReason(pTd, pSdIoctl);
break;
default:
Status = DeviceIoctl(pTd, pSdIoctl);
break;
}
TRACE((pTd->pContext, TC_TD, TT_API1, "TdIoctl(0x%08x): Status=0x%08x\n",
pSdIoctl->IoControlCode, Status));
return Status;
}
/*******************************************************************************
* _TdWriteCompleteRoutine
*
* This routine is called at DPC level by the lower level device
* driver when an IRP corresponding to an outbuf is completed.
*
* DeviceObject (input)
* not used
* pIrp (input)
* pointer to IRP that is complete
* Context (input)
* Context pointer setup when IRP was initialized.
* This is a pointer to the corresponding outbuf.
******************************************************************************/
NTSTATUS _TdWriteCompleteRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
{
POUTBUF pOutBuf = (POUTBUF)Context;
PTD pTd = (PTD)pOutBuf->pPrivate;
PLIST_ENTRY pWorkItem;
KIRQL oldIrql;
// To prevent the OutBuf associated IRP from being canceled by
// DeviceCancelIo between queuing the PASSIVE_LEVEL work item below
// and the actual processing, set the completed flag.
pOutBuf->fIrpCompleted = TRUE;
/*
* Unqueue one of the pre-allocated workitems and use it
* to queue the completion worker.
*/
ExAcquireSpinLock( &pTd->InBufListLock, &oldIrql );
ASSERT(!IsListEmpty(&pTd->WorkItemHead));
pWorkItem = pTd->WorkItemHead.Flink;
RemoveEntryList(pWorkItem);
ExReleaseSpinLock( &pTd->InBufListLock, oldIrql );
/*
* Queue the outbuf completion processing to a worker thread
* since we are not in the correct context to do it here.
*/
IcaQueueWorkItemEx( pTd->pContext, _TdWriteCompleteWorker, Context,
ICALOCK_DRIVER, pWorkItem );
/*
* We return STATUS_MORE_PROCESS_REQUIRED so that no further
* processing for this IRP is done by the I/O completion routine.
*/
return STATUS_MORE_PROCESSING_REQUIRED;
}
/*******************************************************************************
* _TdWriteCompleteWorker
*
* This routine is called by an ExWorker thread to complete processing
* on an outbuf. We will release the outbuf and trigger the syncwrite
* event if anyone is waiting.
*
* pTd (input)
* Pointer to td data structure
* Context (input)
* Context pointer setup when IRP was initialized.
* This is a pointer to the corresponding outbuf.
******************************************************************************/
void _TdWriteCompleteWorker(IN PTD pTd, IN PVOID Context)
{
POUTBUF pOutBuf = (POUTBUF)Context;
PIRP pIrp = pOutBuf->pIrp;
NTSTATUS Status;
TRACE(( pTd->pContext, TC_TD, TT_API3, "_TdWriteCompleteWorker: %08x\n", pOutBuf ));
/*
* Unlink outbuf from busy list
*/
RemoveEntryList( &pOutBuf->Links );
InitializeListHead( &pOutBuf->Links );
//
// Check to see whether any pages need to be unlocked.
//
if (pIrp->MdlAddress != NULL) {
PMDL mdl, thisMdl;
// Unlock any pages that may be described by MDLs.
mdl = pIrp->MdlAddress;
while (mdl != NULL) {
thisMdl = mdl;
mdl = mdl->Next;
if (thisMdl == pOutBuf->pMdl)
continue;
MmUnlockPages( thisMdl );
IoFreeMdl( thisMdl );
}
}
/*
* Any MDL we set in DeviceInitializeWrite() is part of the OUTBUF.
*/
pIrp->MdlAddress = NULL;
// Check for IRP cancellation and success.
if (!pIrp->Cancel && NT_SUCCESS(pIrp->IoStatus.Status)) {
// Clear the consecutive error count and complete the outbuf by
// calling OutBufFree.
pTd->WriteErrorCount = 0;
OutBufFree(pTd, pOutBuf);
}
else {
// If IRP was cancelled or completed with a failure status,
// then increment the error counts and call OutBufError.
if (pIrp->Cancel)
pTd->LastError = (ULONG)STATUS_CANCELLED;
else
pTd->LastError = pIrp->IoStatus.Status;
pTd->WriteErrorCount++;
pTd->pStatus->Output.TdErrors++;
OutBufError(pTd, pOutBuf);
}
/*
* If there is a waiter in TdSyncWrite and the outbuf busy list
* is now empty, then satisfy the wait now.
*/
if (pTd->fSyncWriteWaiter && IsListEmpty(&pTd->IoBusyOutBuf)) {
pTd->fSyncWriteWaiter = FALSE;
KeSetEvent(&pTd->SyncWriteEvent, 1, FALSE);
}
}
NTSTATUS _OpenRegKey(PHANDLE HandlePtr, PWCHAR KeyName)
/*++
Opens a Registry key and returns a handle to it.
Arguments:
HandlePtr - The varible into which to write the opened handle.
KeyName - The name of the Registry key to open.
--*/
{
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING UKeyName;
PAGED_CODE();
RtlInitUnicodeString(&UKeyName, KeyName);
memset(&ObjectAttributes, 0, sizeof(OBJECT_ATTRIBUTES));
InitializeObjectAttributes(&ObjectAttributes, &UKeyName,
OBJ_CASE_INSENSITIVE, NULL, NULL);
return ZwOpenKey(HandlePtr, KEY_READ, &ObjectAttributes);
}
NTSTATUS _GetRegDWORDValue(HANDLE KeyHandle, PWCHAR ValueName, PULONG ValueData)
/*++
Reads a REG_DWORD value from the registry into the supplied variable.
Arguments:
KeyHandle - Open handle to the parent key of the value to read.
ValueName - The name of the value to read.
ValueData - The variable into which to read the data.
--*/
{
NTSTATUS status;
ULONG resultLength;
PKEY_VALUE_FULL_INFORMATION keyValueFullInformation;
#define WORK_BUFFER_SIZE 512
UCHAR keybuf[WORK_BUFFER_SIZE];
UNICODE_STRING UValueName;
RtlInitUnicodeString(&UValueName, ValueName);
keyValueFullInformation = (PKEY_VALUE_FULL_INFORMATION)keybuf;
RtlZeroMemory(keyValueFullInformation, sizeof(keyValueFullInformation));
status = ZwQueryValueKey(KeyHandle,
&UValueName,
KeyValueFullInformation,
keyValueFullInformation,
WORK_BUFFER_SIZE,
&resultLength);
if (NT_SUCCESS(status)) {
if (keyValueFullInformation->Type != REG_DWORD) {
status = STATUS_INVALID_PARAMETER_MIX;
} else {
*ValueData = *((ULONG UNALIGNED *)((PCHAR)keyValueFullInformation +
keyValueFullInformation->DataOffset));
}
}
return status;
}
NTSTATUS _GetRegStringValue(
HANDLE KeyHandle,
PWCHAR ValueName,
PKEY_VALUE_PARTIAL_INFORMATION *ValueData,
PUSHORT ValueSize)
/*++
Reads a REG_*_SZ string value from the Registry into the supplied
key value buffer. If the buffer string buffer is not large enough,
it is reallocated.
Arguments:
KeyHandle - Open handle to the parent key of the value to read.
ValueName - The name of the value to read.
ValueData - Destination for the read data.
ValueSize - Size of the ValueData buffer. Updated on output.
--*/
{
NTSTATUS status;
ULONG resultLength;
UNICODE_STRING UValueName;
PAGED_CODE();
RtlInitUnicodeString(&UValueName, ValueName);
status = ZwQueryValueKey(
KeyHandle,
&UValueName,
KeyValuePartialInformation,
*ValueData,
(ULONG) *ValueSize,
&resultLength);
if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL) {
PVOID temp;
// Free the old buffer and allocate a new one of the
// appropriate size.
ASSERT(resultLength > (ULONG) *ValueSize);
if (resultLength <= 0xFFFF) {
status = MemoryAllocate(resultLength, &temp);
if (status != STATUS_SUCCESS)
return status;
if (*ValueData != NULL)
MemoryFree(*ValueData);
*ValueData = temp;
*ValueSize = (USHORT) resultLength;
status = ZwQueryValueKey(KeyHandle,
&UValueName,
KeyValuePartialInformation,
*ValueData,
*ValueSize,
&resultLength);
ASSERT((status != STATUS_BUFFER_OVERFLOW) &&
(status != STATUS_BUFFER_TOO_SMALL));
}
else {
status = STATUS_BUFFER_TOO_SMALL;
}
}
return status;
}
NTSTATUS _GetRegMultiSZValue(
HANDLE KeyHandle,
PWCHAR ValueName,
PUNICODE_STRING ValueData)
/*++
Reads a REG_MULTI_SZ string value from the Registry into the supplied
Unicode string. If the Unicode string buffer is not large enough,
it is reallocated.
Arguments:
KeyHandle - Open handle to the parent key of the value to read.
ValueName - The name of the value to read.
ValueData - Destination Unicode string for the value data.
--*/
{
NTSTATUS status;
ULONG resultLength;
PKEY_VALUE_PARTIAL_INFORMATION keyValuePartialInformation;
UNICODE_STRING UValueName;
PAGED_CODE();
ValueData->Length = 0;
status = _GetRegStringValue(
KeyHandle,
ValueName,
(PKEY_VALUE_PARTIAL_INFORMATION *) &(ValueData->Buffer),
&(ValueData->MaximumLength));
if (NT_SUCCESS(status)) {
keyValuePartialInformation =
(PKEY_VALUE_PARTIAL_INFORMATION)ValueData->Buffer;
if (keyValuePartialInformation->Type == REG_MULTI_SZ) {
ValueData->Length = (USHORT)
keyValuePartialInformation->DataLength;
RtlCopyMemory(
ValueData->Buffer,
&(keyValuePartialInformation->Data),
ValueData->Length);
}
else {
status = STATUS_INVALID_PARAMETER_MIX;
}
}
return status;
}
NTSTATUS _GetRegSZValue(
HANDLE KeyHandle,
PWCHAR ValueName,
PUNICODE_STRING ValueData,
PULONG ValueType)
/*++
Reads a REG_SZ string value from the Registry into the supplied
Unicode string. If the Unicode string buffer is not large enough,
it is reallocated.
Arguments:
KeyHandle - Open handle to the parent key of the value to read.
ValueName - The name of the value to read.
ValueData - Destination Unicode string for the value data.
ValueType - On return, contains the Registry type of the value read.
--*/
{
NTSTATUS status;
ULONG resultLength;
PKEY_VALUE_PARTIAL_INFORMATION keyValuePartialInformation;
UNICODE_STRING UValueName;
PAGED_CODE();
ValueData->Length = 0;
status = _GetRegStringValue(
KeyHandle,
ValueName,
(PKEY_VALUE_PARTIAL_INFORMATION *) &(ValueData->Buffer),
&(ValueData->MaximumLength));
if (NT_SUCCESS(status)) {
keyValuePartialInformation =
(PKEY_VALUE_PARTIAL_INFORMATION)ValueData->Buffer;
if ((keyValuePartialInformation->Type == REG_SZ) ||
(keyValuePartialInformation->Type == REG_EXPAND_SZ)) {
WCHAR *src;
WCHAR *dst;
ULONG dataLength;
*ValueType = keyValuePartialInformation->Type;
dataLength = keyValuePartialInformation->DataLength;
ASSERT(dataLength <= ValueData->MaximumLength);
dst = ValueData->Buffer;
src = (PWCHAR) &(keyValuePartialInformation->Data);
while (ValueData->Length <= dataLength) {
if ((*dst++ = *src++) == UNICODE_NULL)
break;
ValueData->Length += sizeof(WCHAR);
}
if (ValueData->Length < (ValueData->MaximumLength - 1)) {
ValueData->Buffer[ValueData->Length / sizeof(WCHAR)] =
UNICODE_NULL;
}
}
else {
status = STATUS_INVALID_PARAMETER_MIX;
}
}
return status;
}
PWCHAR _EnumRegMultiSz(
IN PWCHAR MszString,
IN ULONG MszStringLength,
IN ULONG StringIndex)
/*++
Parses a REG_MULTI_SZ string and returns the specified substring.
Arguments:
MszString - A pointer to the REG_MULTI_SZ string.
MszStringLength - The length of the REG_MULTI_SZ string, including the
terminating null character.
StringIndex - Index number of the substring to return. Specifiying
index 0 retrieves the first substring.
Return Value:
A pointer to the specified substring.
Notes:
This code is called at raised IRQL. It is not pageable.
--*/
{
PWCHAR string = MszString;
if (MszStringLength < (2 * sizeof(WCHAR)))
return NULL;
// Find the start of the desired string.
while (StringIndex) {
while (MszStringLength >= sizeof(WCHAR)) {
MszStringLength -= sizeof(WCHAR);
if (*string++ == UNICODE_NULL)
break;
}
// Check for index out of range.
if (MszStringLength < (2 * sizeof(UNICODE_NULL)))
return NULL;
StringIndex--;
}
if (MszStringLength < (2 * sizeof(UNICODE_NULL)))
return NULL;
return string;
}
VOID GetGUID(
OUT PUNICODE_STRING szGuid,
IN int Lana)
/*++
Enumerates through the guid table setup from TSConfig tool
Arguments:
szGuid - This is an out param containing the guid in this format '{ ... }'
Lana - The id to confirm the one to one association
Return Value:
VOID -- _TcpGetTransportAddress will fail if szGuid is invalid
--*/
{
// open guidtable key
HANDLE hKey;
UNICODE_STRING TempString;
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS status;
status = _OpenRegKey(&hKey, REG_GUID_TABLE);
if (NT_SUCCESS(status)) {
// enumerate this key
ULONG ulByteRead = 0;
ULONG Index = 0;
ULONG ulLana = 0;
HANDLE hSubKey;
PKEY_BASIC_INFORMATION pKeyBasicInformation = NULL;
BYTE buffer[ 512 ]; // work space
pKeyBasicInformation = (PKEY_BASIC_INFORMATION)buffer;
RtlZeroMemory(pKeyBasicInformation, sizeof(buffer));
do {
status = ZwEnumerateKey(
hKey,
Index,
KeyBasicInformation,
(PVOID)pKeyBasicInformation,
sizeof(buffer),
&ulByteRead);
KdPrint(("TDTCP: GetGUID ZwEnumerateKey returned 0x%x\n", status));
if (status != STATUS_SUCCESS)
break;
// extract unicode name
TempString.Length = (USHORT) pKeyBasicInformation->NameLength;
TempString.MaximumLength = (USHORT) pKeyBasicInformation->NameLength;
TempString.Buffer = pKeyBasicInformation->Name;
RtlZeroMemory( &ObjectAttributes , sizeof( OBJECT_ATTRIBUTES ) );
InitializeObjectAttributes(
&ObjectAttributes,
&TempString,
OBJ_CASE_INSENSITIVE,
hKey,
NULL);
status = ZwOpenKey(&hSubKey, KEY_READ, &ObjectAttributes);
if (NT_SUCCESS(status)) {
status = _GetRegDWORDValue(hSubKey, LANA_ID, &ulLana);
ZwClose(hSubKey);
if (NT_SUCCESS(status)) {
if (Lana == (int)ulLana) {
KdPrint(("TDTCP:GetGUID We've found a Lana %d\n", ulLana));
status = MemoryAllocate(TempString.Length +
sizeof(WCHAR), &szGuid->Buffer);
if (NT_SUCCESS(status)) {
szGuid->MaximumLength = TempString.Length +
sizeof(WCHAR);
RtlZeroMemory(szGuid->Buffer, szGuid->MaximumLength);
RtlCopyUnicodeString(szGuid, &TempString);
break;
}
}
}
}
Index++;
} while (TRUE);
ZwClose(hKey);
}
}