|
|
/*++
Copyright (c) 1998-2000 Microsoft Corporation
Module Name :
rdpdyn.c
Abstract:
This module is the dynamic device management component for RDP device redirection. It exposes an interface that can be opened by device management user-mode components running in session context.
Need a check in IRP_MJ_CREATE to make sure that we are not being opened 2x by the same session. This shouldn't be allowed.
Where can I safely use PAGEDPOOL instead of NONPAGEDPOOL.
Author:
tadb
Revision History: --*/
#include "precomp.hxx"
#define TRC_FILE "rdpdyn"
#include "trc.h"
#define DRIVER
#include "cfg.h"
#include "pnp.h"
#include "stdarg.h"
#include "stdio.h"
// Just shove the typedefs in for the Power Management functions now because I can't
// get the header conflicts resolved.
#ifdef __cplusplus
extern "C" { #endif // __cplusplus
NTKERNELAPI VOID PoStartNextPowerIrp(IN PIRP Irp); NTKERNELAPI NTSTATUS PoCallDriver(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
////////////////////////////////////////////////////////////////////////
//
// Defines
//
// Calculate the size of a completed RDPDR_PRINTERDEVICE_SUB event.
#define CALCPRINTERDEVICE_SUB_SZ(rec) \
sizeof(RDPDR_PRINTERDEVICE_SUB) + \ (rec)->clientPrinterFields.PnPNameLen + \ (rec)->clientPrinterFields.DriverLen + \ (rec)->clientPrinterFields.PrinterNameLen + \ (rec)->clientPrinterFields.CachedFieldsLen
// Calculate the size of a completed RDPDR_REMOVEDEVICE event.
#define CALCREMOVEDEVICE_SUB_SZ(rec) \
sizeof(RDPDR_REMOVEDEVICE)
// Calculate the size of a completed RDPDR_PORTDEVICE_SUB event.
#define CALCPORTDEVICE_SUB_SZ(rec) \
sizeof(RDPDR_PORTDEVICE_SUB) // Calculate the size of a completed RDPDR_DRIVEDEVICE_SUB event.
#define CALCDRIVEDEVICE_SUB_SZ(rec) \
sizeof(RDPDR_DRIVEDEVICE_SUB)
#if DBG
#define DEVMGRCONTEXTMAGICNO 0x55445544
// Test defines.
#define TESTDRIVERNAME L"HP LaserJet 4P"
//#define TESTDRIVERNAME L"This driver has no match"
#define TESTPNPNAME L""
#define TESTPRINTERNAME TESTDRIVERNAME
#define TESTDEVICEID 0xfafafafa
// Test port name.
#define TESTPORTNAME L"LPT1"
#endif
////////////////////////////////////////////////////////////////////////
//
// Internal Typedefs
//
//
// Context for each open by a user-mode device manager component. This
// structure is stored in the FsContext field of the file object.
//
typedef struct tagDEVMGRCONTEXT { #if DBG
ULONG magicNo; #endif
ULONG sessionID; } DEVMGRCONTEXT, *PDEVMGRCONTEXT;
//
// Non-Opaque Version of Associated Data for a Device Managed by this Module
//
typedef struct tagRDPDYN_DEVICEDATAREC { ULONG PortNumber; UNICODE_STRING SymbolicLinkName; } RDPDYN_DEVICEDATAREC, *PRDPDYN_DEVICEDATAREC;
typedef struct tagCLIENTMESSAGECONTEXT { RDPDR_ClientMessageCB *CB; PVOID ClientData; } CLIENTMESSAGECONTEXT, *PCLIENTMESSAGECONTEXT;
////////////////////////////////////////////////////////////////////////
//
// Internal Prototypes
//
#ifdef __cplusplus
extern "C" { #endif // __cplusplus
// Return the next available printer port number.
NTSTATUS GetNextPrinterPortNumber( OUT ULONG *portNumber );
// Handle file object creation by a client of this driver.
NTSTATUS RDPDYN_Create( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); // Handle file object closure by a client of this driver.
NTSTATUS RDPDYN_Close( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
// This routine modifies the file object in preparation for returning STATUS_REPARSE.
NTSTATUS RDPDYN_PrepareForReparse( PFILE_OBJECT fileObject );
// Handle IOCTL IRP's.
NTSTATUS RDPDYN_DeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
// This routine modifies the file object in preparation for returning STATUS_REPARSE.
NTSTATUS RDPDYN_PrepareForDevMgmt( PFILE_OBJECT fileObject, PCWSTR sessionIDStr, PIRP irp, PIO_STACK_LOCATION irpStackLocation );
// Generates a printer announce message for testing.
NTSTATUS RDPDYN_GenerateTestPrintAnnounceMsg( IN OUT PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg, IN ULONG devAnnounceMsgSize, OPTIONAL OUT ULONG *prnAnnounceMsgReqSize );
// Completely handles IOCTL_RDPDR_GETNEXTDEVMGMTEVENT IRP's.
NTSTATUS RDPDYN_HandleGetNextDevMgmtEventIOCTL( IN PDEVICE_OBJECT deviceObject, IN PIRP irp );
// Handle the cleanup IRP for a file object.
NTSTATUS RDPDYN_Cleanup( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
// Calculate the size of a device management event.
ULONG RDPDYN_DevMgmtEventSize( IN PVOID devMgmtEvent, IN ULONG type );
// Completely handles IOCTL_RDPDR_CLIENTMSG IRP's.
NTSTATUS RDPDYN_HandleClientMsgIOCTL( IN PDEVICE_OBJECT deviceObject, IN PIRP pIrp );
// Complete a pending IRP with a device management event.
NTSTATUS CompleteIRPWithDevMgmtEvent( IN PDEVICE_OBJECT deviceObject, IN PIRP pIrp, IN ULONG eventSize, IN ULONG eventType, IN PVOID event, IN DrDevice *drDevice );
// Complete a pending IRP with a resize buffer event to the user-mode
// component.
NTSTATUS CompleteIRPWithResizeMsg( IN PIRP pIrp, IN ULONG requiredUserBufSize );
// Format a port description.
void GeneratePortDescription( IN PCSTR dosPortName, IN PCWSTR clientName, IN PWSTR description );
NTSTATUS NTAPI DrSendMessageToClientCompletion(PVOID Context, PIO_STATUS_BLOCK IoStatusBlock);
#if DBG
// This is for testing so we can create a new test printer on
// demand from user-mode.
NTSTATUS RDPDYN_HandleDbgAddNewPrnIOCTL( IN PDEVICE_OBJECT deviceObject, IN PIRP pIrp );
// Generates a printer announce message for testing.
void RDPDYN_TracePrintAnnounceMsg( IN OUT PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg, IN ULONG sessionID, IN PCWSTR portName, IN PCWSTR clientName ); #endif
// Returns the next pending device management event request for the specified
// session, in the form of an IRP. Note that this function can not be called
// if a spinlock has been acquired.
PIRP GetNextEventRequest( IN RDPEVNTLIST list, IN ULONG sessionID );
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
////////////////////////////////////////////////////////////////////////
//
// Globals
//
// Pointer to the device Object for the minirdr. This global is defined in rdpdr.c.
extern PRDBSS_DEVICE_OBJECT DrDeviceObject;
//
// Global Registry Path for RDPDR.SYS. This global is defined in rdpdr.c.
//
extern UNICODE_STRING DrRegistryPath;
// The Physical Device Object that terminates our DO stack.
PDEVICE_OBJECT RDPDYN_PDO = NULL;
// Manages user-mode component device management events and event requests.
RDPEVNTLIST UserModeEventListMgr = RDPEVNTLIST_INVALID_LIST;
// Remove this check, eventually.
#if DBG
BOOL RDPDYN_StopReceived = FALSE; BOOL RDPDYN_QueryStopReceived = FALSE; DWORD RDPDYN_StartCount = 0; #endif
////////////////////////////////////////////////////////////////////////
//
// Function Definitions
//
NTSTATUS RDPDYN_Initialize( ) /*++
Routine Description:
Init function for this module.
Arguments:
Return Value:
Status
--*/ { NTSTATUS status;
BEGIN_FN("RDPDYN_Initialize");
//
// Create the user-mode device event manager.
//
TRC_ASSERT(UserModeEventListMgr == RDPEVNTLIST_INVALID_LIST, (TB, "Initialize called more than 1 time")); UserModeEventListMgr = RDPEVNTLIST_CreateNewList(); if (UserModeEventListMgr != RDPEVNTLIST_INVALID_LIST) { status = STATUS_SUCCESS; } else { status = STATUS_UNSUCCESSFUL; }
//
// Initialize the dynamic port management module.
//
if (status == STATUS_SUCCESS) { status = RDPDRPRT_Initialize(); }
TRC_NRM((TB, "return status %08X.", status)); return status; }
NTSTATUS RDPDYN_Shutdown( ) /*++
Routine Description:
Shutdown function for this module.
Arguments:
Return Value:
Status
--*/ { ULONG sessionID; void *devMgmtEvent; PIRP pIrp; ULONG type; #if DBG
ULONG sz; #endif
DrDevice *device; KIRQL oldIrql; PDRIVER_CANCEL setCancelResult;
BEGIN_FN("RDPDYN_Shutdown");
//
// Clean up any pending device management events and any pending IRP's.
//
TRC_ASSERT(UserModeEventListMgr != RDPEVNTLIST_INVALID_LIST, (TB, "Invalid list mgr"));
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql); while (RDPEVNTLLIST_GetFirstSessionID(UserModeEventListMgr, &sessionID)) { //
// Remove pending IRP's
//
pIrp = (PIRP)RDPEVNTLIST_DequeueRequest( UserModeEventListMgr, sessionID ); while (pIrp != NULL) { //
// Set the cancel routine to NULL and record the current state.
//
setCancelResult = IoSetCancelRoutine(pIrp, NULL);
//
// Fail the IRP request.
//
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
//
// If the IRP is not being canceled.
//
if (setCancelResult != NULL) { //
// Fail the request.
//
pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL; IoCompleteRequest(pIrp, IO_NO_INCREMENT); }
//
// Remove the next IRP from the event/request queue.
//
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql); pIrp = (PIRP)RDPEVNTLIST_DequeueRequest( UserModeEventListMgr, sessionID ); } RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
//
// Remove pending device management events.
//
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql); while (RDPEVNTLIST_DequeueEvent( UserModeEventListMgr, sessionID, &type, &devMgmtEvent, &device )) {
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql); #if DBG
// Zero the free'd event in checked builds.
sz = RDPDYN_DevMgmtEventSize(devMgmtEvent, type); if (sz > 0) { RtlZeroMemory(devMgmtEvent, sz); } #endif
if (devMgmtEvent != NULL) { delete devMgmtEvent; }
// Release the device, if appropriate.
if (device != NULL) { device->Release(); } RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql); } RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql); }
//
// Shut down the dynamic port management module.
//
RDPDRPRT_Shutdown();
return STATUS_SUCCESS; }
NTSTATUS RDPDYN_Dispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++
Routine Description:
Handle IRP's for DO's sitting on top of our physical device object.
Arguments:
DeviceObject - Supplies the device object for the packet being processed.
Irp - Supplies the Irp being processed
Return Value:
NTSTATUS - The status for the Irp
--*/ { PIO_STACK_LOCATION ioStackLocation; NTSTATUS status; PRDPDYNDEVICE_EXTENSION deviceExtension; PDEVICE_OBJECT stackDeviceObject; BOOLEAN isPowerIRP;
BEGIN_FN("RDPDYN_Dispatch");
//
// Get our location in the IRP stack.
//
ioStackLocation = IoGetCurrentIrpStackLocation(Irp); TRC_NRM((TB, "Major is %08X", ioStackLocation->MajorFunction));
//
// Get our device extension and stack device object.
//
deviceExtension = (PRDPDYNDEVICE_EXTENSION)DeviceObject->DeviceExtension; TRC_ASSERT(deviceExtension != NULL, (TB, "Invalid device extension.")); if (deviceExtension == NULL) { Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; return STATUS_UNSUCCESSFUL; }
stackDeviceObject = deviceExtension->TopOfStackDeviceObject; TRC_ASSERT(stackDeviceObject != NULL, (TB, "Invalid device object."));
//
// Function Dispatch Switch
//
isPowerIRP = FALSE; switch (ioStackLocation->MajorFunction) { case IRP_MJ_CREATE:
TRC_NRM((TB, "IRP_MJ_CREATE"));
// RDPDYN_Create handles this completely.
return RDPDYN_Create(DeviceObject, Irp);
case IRP_MJ_CLOSE:
TRC_NRM((TB, "IRP_MJ_CLOSE"));
// RDPDYN_Close handles this completely.
return RDPDYN_Close(DeviceObject, Irp);
case IRP_MJ_CLEANUP:
TRC_NRM((TB, "IRP_MJ_CLEANUP"));
// RDPDYN_Cleanup handles this completely.
return RDPDYN_Cleanup(DeviceObject, Irp);
case IRP_MJ_READ:
// We shouldn't be receiving any read requests.
TRC_ASSERT(FALSE, (TB, "Read requests not supported.")); Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; IoCompleteRequest (Irp, IO_NO_INCREMENT); return STATUS_NOT_SUPPORTED;
case IRP_MJ_WRITE:
// We shouldn't be receiving any write requests.
TRC_ASSERT(FALSE, (TB, "Write requests not supported.")); Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; IoCompleteRequest (Irp, IO_NO_INCREMENT); return STATUS_NOT_SUPPORTED;
case IRP_MJ_DEVICE_CONTROL:
// RDPDYN_DeviceControl handles this completely.
return RDPDYN_DeviceControl(DeviceObject, Irp);
case IRP_MJ_POWER:
TRC_NRM((TB, "IRP_MJ_POWER")); isPowerIRP = TRUE;
switch (ioStackLocation->MinorFunction) { case IRP_MN_SET_POWER: Irp->IoStatus.Status = STATUS_SUCCESS; break; default: TRC_NRM((TB, "Unknown Power IRP")); } break;
case IRP_MJ_PNP: TRC_NRM((TB, "IRP_MJ_PNP"));
switch (ioStackLocation->MinorFunction) { case IRP_MN_START_DEVICE: #if DBG
// Remove this debug code, eventually.
RDPDYN_StartCount++; #endif
return(RDPDRPNP_HandleStartDeviceIRP(stackDeviceObject, ioStackLocation, Irp));
case IRP_MN_STOP_DEVICE:
#if DBG
// Remove this debug code, eventually.
RDPDYN_StopReceived = TRUE; #endif
TRC_NRM((TB, "IRP_MN_STOP_DEVICE")); Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; IoCompleteRequest (Irp, IO_NO_INCREMENT); return STATUS_UNSUCCESSFUL;
case IRP_MN_REMOVE_DEVICE:
return(RDPDRPNP_HandleRemoveDeviceIRP(DeviceObject, stackDeviceObject, Irp));
case IRP_MN_QUERY_CAPABILITIES:
TRC_NRM((TB, "IRP_MN_QUERY_CAPABILITIES")); break;
case IRP_MN_QUERY_ID: TRC_NRM((TB, "IRP_MN_QUERY_ID")); break;
case IRP_MN_QUERY_DEVICE_RELATIONS:
TRC_NRM((TB, "IRP_MN_QUERY_DEVICE_RELATIONS")); switch(ioStackLocation->Parameters.QueryDeviceRelations.Type) { case EjectionRelations: TRC_NRM((TB, "Type==EjectionRelations")); break;
case BusRelations: // Note that we need to handle this if we end up kicking out any PDO's.
TRC_NRM((TB, "Type==BusRelations")); break;
case PowerRelations: TRC_NRM((TB, "Type==PowerRelations")); Irp->IoStatus.Status = STATUS_SUCCESS; break;
case RemovalRelations: TRC_NRM((TB, "Type==RemovalRelations")); Irp->IoStatus.Status = STATUS_SUCCESS; break;
case TargetDeviceRelation: TRC_NRM((TB, "Type==TargetDeviceRelation")); break;
default: TRC_NRM((TB, "Unknown IRP_MN_QUERY_DEVICE_RELATIONS minor type")); } break;
case IRP_MN_QUERY_STOP_DEVICE:
#if DBG
// Remove this debug code, eventually.
RDPDYN_QueryStopReceived = TRUE; #endif
// We will not allow a device to be stopped for load balancing.
TRC_NRM((TB, "IRP_MN_QUERY_STOP_DEVICE")); Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; IoCompleteRequest (Irp, IO_NO_INCREMENT); return STATUS_UNSUCCESSFUL;
case IRP_MN_QUERY_REMOVE_DEVICE:
// We will not allow our device to be removed.
TRC_NRM((TB, "IRP_MN_QUERY_REMOVE_DEVICE")); Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; Irp->IoStatus.Information = 0; IoCompleteRequest (Irp, IO_NO_INCREMENT); return STATUS_UNSUCCESSFUL;
case IRP_MN_CANCEL_STOP_DEVICE: TRC_NRM((TB, "IRP_MN_CANCEL_STOP_DEVICE")); Irp->IoStatus.Status = STATUS_SUCCESS; break;
case IRP_MN_CANCEL_REMOVE_DEVICE: TRC_NRM((TB, "IRP_MN_CANCEL_REMOVE_DEVICE")); Irp->IoStatus.Status = STATUS_SUCCESS; break;
case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: TRC_NRM((TB, "IRP_MN_FILTER_RESOURCE_REQUIREMENTS")); break;
case IRP_MN_QUERY_PNP_DEVICE_STATE: TRC_NRM((TB, "IRP_MN_QUERY_PNP_DEVICE_STATE")); break;
case IRP_MN_QUERY_BUS_INFORMATION: TRC_NRM((TB, "IRP_MN_QUERY_BUS_INFORMATION")); break;
default: TRC_ALT((TB, "Unhandled PnP IRP with minor %08X", ioStackLocation->MinorFunction)); } }
//
// By default, pass the IRP down the stack.
//
if (isPowerIRP) { PoStartNextPowerIrp(Irp); IoSkipCurrentIrpStackLocation(Irp); return PoCallDriver(stackDeviceObject, Irp); } else { IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(stackDeviceObject,Irp); } }
NTSTATUS RDPDYN_Create( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++
Routine Description:
Entry point for CreateFile calls.
Arguments:
DeviceObject - pointer to our device object.
Return Value:
NT status code
--*/ { NTSTATUS ntStatus; PFILE_OBJECT fileObject; PIO_STACK_LOCATION nextStackLocation; PIO_STACK_LOCATION currentStackLocation; ULONG i; BOOL matches; WCHAR sessionIDString[]=RDPDYN_SESSIONIDSTRING; ULONG idStrLen; WCHAR *sessionIDPtr; ULONG fnameLength;
BEGIN_FN("RDPDYN_Create");
// Get the current stack location.
currentStackLocation = IoGetCurrentIrpStackLocation(Irp); TRC_ASSERT(currentStackLocation != NULL, (TB, "Invalid stack location.")); fileObject = currentStackLocation->FileObject;
// Return STATUS_REPARSE with the minirdr DO so it gets opened instead, if
// we have a file name.
if (fileObject->FileName.Length != 0) { //
// Find out if the client is trying to open us as the device manager from
// user-mode.
//
// Check for the session identifer string as the first few characters in
// the reference string.
idStrLen = wcslen(sessionIDString); fnameLength = fileObject->FileName.Length/sizeof(WCHAR); for (i=0; i<fnameLength && i<idStrLen; i++) { if (fileObject->FileName.Buffer[i] != sessionIDString[i]) { break; } } matches = (i == idStrLen);
//
// If the client is trying to open us as the device manager from user-
// mode.
//
if (matches) {
// Prepare the file object for managing device management comms to
// the user-mode component that opened it.
ntStatus = RDPDYN_PrepareForDevMgmt( fileObject, &fileObject->FileName.Buffer[idStrLen], Irp, currentStackLocation ); } // Otherwise, we can assume that this create is for a device that is being
// managed by RDPDR and the IFS kit.
else { // Prepare the file object for reparse.
ntStatus = RDPDYN_PrepareForReparse(fileObject); } } // Otherwise, fail. This should never happen.
else { ntStatus = STATUS_UNSUCCESSFUL; }
// Complete the IO request and return.
Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = 0; IoCompleteRequest (Irp, IO_NO_INCREMENT );
return ntStatus; }
NTSTATUS RDPDYN_PrepareForReparse( PFILE_OBJECT fileObject ) /*++
Routine Description:
This routine modifies the file object in preparation for returning STATUS_REPARSE
Arguments:
fileObject - the file object
Return Value:
STATUS_REPARSE if everything is successful
Notes:
--*/ { NTSTATUS ntStatus; USHORT rootDeviceNameLength, reparsePathLength, clientDevicePathLength; PWSTR pFileNameBuffer = NULL; ULONG i; ULONG len; BOOL clientDevPathMissingSlash; HANDLE deviceInterfaceKey = INVALID_HANDLE_VALUE; UNICODE_STRING unicodeStr; ULONG requiredBytes; PKEY_VALUE_PARTIAL_INFORMATION keyValueInfo = NULL; WCHAR *clientDevicePath=L""; GUID *pPrinterGuid; UNICODE_STRING symbolicLinkName; WCHAR *refString;
BEGIN_FN("RDPDYN_PrepareForReparse");
// We are not going to use these fields for storing any contextual
// information.
fileObject->FsContext = NULL; fileObject->FsContext2 = NULL;
// Compute the number of bytes required to store the root of the device
// path, without the terminator.
rootDeviceNameLength = wcslen(RDPDR_DEVICE_NAME_U) * sizeof(WCHAR);
//
// Get a pointer to the reference string for the reparse.
//
if (fileObject->FileName.Buffer[0] == L'\\') { refString = &fileObject->FileName.Buffer[1]; } else { refString = &fileObject->FileName.Buffer[0]; }
//
// Resolve the reference name for the device into the symbolic link
// name for the device interface. We can optimize out this
// step and the next one by maintaining an internal table to convert
// from port names to symbolic link names.
//
pPrinterGuid = (GUID *)&DYNPRINT_GUID; RtlInitUnicodeString(&unicodeStr, refString); ntStatus=IoRegisterDeviceInterface( RDPDYN_PDO, pPrinterGuid, &unicodeStr, &symbolicLinkName ); if (ntStatus == STATUS_SUCCESS) {
TRC_ERR((TB, "IoRegisterDeviceInterface succeeded."));
//
// Open the registry key for the device being opened.
//
ntStatus = IoOpenDeviceInterfaceRegistryKey( &symbolicLinkName, KEY_ALL_ACCESS, &deviceInterfaceKey );
RtlFreeUnicodeString(&symbolicLinkName); }
//
// Get the size of the value info buffer required for the client device
// path for the device being opened.
//
if (ntStatus == STATUS_SUCCESS) { TRC_NRM((TB, "IoOpenDeviceInterfaceRegistryKey succeeded.")); RtlInitUnicodeString(&unicodeStr, CLIENT_DEVICE_VALUE_NAME); ntStatus = ZwQueryValueKey( deviceInterfaceKey, &unicodeStr, KeyValuePartialInformation, NULL, 0, &requiredBytes ); } else { TRC_NRM((TB, "IoOpenDeviceInterfaceRegistryKey failed: %08X.", ntStatus));
deviceInterfaceKey = INVALID_HANDLE_VALUE; }
//
// Size the data buffer.
//
if (ntStatus == STATUS_BUFFER_TOO_SMALL) { keyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION) new(NonPagedPool) BYTE[requiredBytes]; if (keyValueInfo != NULL) { ntStatus = STATUS_SUCCESS; } else { TRC_NRM((TB, "failed to allocate client device path.")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } }
//
// Read the client device path.
//
if (ntStatus == STATUS_SUCCESS) { ntStatus = ZwQueryValueKey( deviceInterfaceKey, &unicodeStr, KeyValuePartialInformation, keyValueInfo, requiredBytes, &requiredBytes ); }
//
// Allocate the reparsed filename.
//
if (ntStatus == STATUS_SUCCESS) { TRC_NRM((TB, "ZwQueryValueKey succeeded.")); clientDevicePath = (WCHAR *)keyValueInfo->Data;
// Compute the number of bytes required to store the client device path,
// without the terminator.
clientDevicePathLength = wcslen(clientDevicePath) * sizeof(WCHAR);
// See if the client device path is prefixed by a '\'
clientDevPathMissingSlash = clientDevicePath[0] != L'\\';
// Get the length (in bytes) of the entire reparsed device path, without the
// terminator.
reparsePathLength = rootDeviceNameLength + clientDevicePathLength; if (clientDevPathMissingSlash) { reparsePathLength += sizeof(WCHAR); }
pFileNameBuffer = (PWSTR)ExAllocatePoolWithTag( NonPagedPool, reparsePathLength + (1 * sizeof(WCHAR)), RDPDYN_POOLTAG); if (pFileNameBuffer == NULL) { TRC_NRM((TB, "failed to allocate reparse buffer.")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } }
//
// Assign the reparse string to the IRP's file name for reparse.
//
if (ntStatus == STATUS_SUCCESS) { // Copy the device name
RtlCopyMemory( pFileNameBuffer, RDPDR_DEVICE_NAME_U, rootDeviceNameLength);
// Make sure we get a '\' between the root device name and
// the device path.
if (clientDevPathMissingSlash) { pFileNameBuffer[rootDeviceNameLength/sizeof(WCHAR)] = L'\\'; rootDeviceNameLength += sizeof(WCHAR); }
// Append the client device path to the end of the device name and
// include the client device path's terminator.
RtlCopyMemory( ((PBYTE)pFileNameBuffer + rootDeviceNameLength), clientDevicePath, clientDevicePathLength + (1 * sizeof(WCHAR)) );
// Release the IRP's previous file name.
ExFreePool(fileObject->FileName.Buffer);
// Assign the reparse string to the IRP's file name.
fileObject->FileName.Buffer = pFileNameBuffer; fileObject->FileName.Length = reparsePathLength; fileObject->FileName.MaximumLength = fileObject->FileName.Length;
ntStatus = STATUS_REPARSE; } else {
TRC_ERR((TB, "failed with status %08X.", ntStatus));
if (pFileNameBuffer != NULL) { ExFreePool(pFileNameBuffer); pFileNameBuffer = NULL; } }
TRC_NRM((TB, "device file name after processing %wZ.", &fileObject->FileName));
//
// Clean up and exit.
//
if (deviceInterfaceKey != INVALID_HANDLE_VALUE) { ZwClose(deviceInterfaceKey); } if (keyValueInfo != NULL) { delete keyValueInfo; }
return ntStatus; }
NTSTATUS RDPDYN_PrepareForDevMgmt( PFILE_OBJECT fileObject, PCWSTR sessionIDStr, PIRP irp, PIO_STACK_LOCATION irpStackLocation ) /*++
Routine Description:
This routine modifies the file object for managing device management comms with the user-mode component that opened us.
Arguments:
fileObject - the file object. sessionID - session identifier string. irp - irp corresponding to the create for this file object. irpStackLocation - current location in the IRP stack for the create.
Return Value:
STATUS_SUCCESS if everything is successful
Notes:
--*/ { PDEVMGRCONTEXT context; ULONG sessionID; ULONG i; UNICODE_STRING uncSessionID; NTSTATUS ntStatus; ULONG irpSessionId;
BEGIN_FN("RDPDYN_PrepareForDevMgmt"); //
// Security check the IRP to make sure it comes from a thread
// with admin privilege
//
if (!DrIsAdminIoRequest(irp, irpStackLocation)) { TRC_ALT((TB, "Access denied for non-Admin IRP.")); return STATUS_ACCESS_DENIED; } else { TRC_DBG((TB, "Admin IRP accepted.")); }
//
// Convert the session identifier string into a number.
//
RtlInitUnicodeString(&uncSessionID, sessionIDStr); ntStatus = RtlUnicodeStringToInteger(&uncSessionID, 10, &sessionID); if (!NT_SUCCESS(ntStatus)) { return ntStatus; }
//
// Allocate a context struct so we can remember information about
// which session we were opened from.
//
context = new(NonPagedPool) DEVMGRCONTEXT; if (context == NULL) { return STATUS_NO_MEMORY; }
// Initialize this struct.
#if DBG
context->magicNo = DEVMGRCONTEXTMAGICNO; #endif
context->sessionID = sessionID; fileObject->FsContext = context;
// Success.
return STATUS_SUCCESS; }
NTSTATUS RDPDYN_Close( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++
Routine Description:
Handle the closure of a file object.
Arguments:
Return Value:
NT status code
--*/ {
NTSTATUS ntStatus; PFILE_OBJECT fileObject; PIO_STACK_LOCATION irpStack; PDEVMGRCONTEXT context; PIRP pIrp; KIRQL oldIrql; PDRIVER_CANCEL setCancelResult;
BEGIN_FN("RDPDYN_Close");
irpStack = IoGetCurrentIrpStackLocation (Irp); fileObject = irpStack->FileObject;
// Grab our "open" context for this instance of us from the current stack
// location's file object.
context = (PDEVMGRCONTEXT)irpStack->FileObject->FsContext; TRC_ASSERT(context->magicNo == DEVMGRCONTEXTMAGICNO, (TB, "invalid context"));
//
// Make sure we got all the pending IRP's.
//
TRC_ASSERT(UserModeEventListMgr != NULL, (TB, "RdpDyn EventList is NULL")); RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql); pIrp = (PIRP)RDPEVNTLIST_DequeueRequest( UserModeEventListMgr, context->sessionID ); while (pIrp != NULL) {
//
// Set the cancel routine to NULL and record the current state.
//
setCancelResult = IoSetCancelRoutine(pIrp, NULL);
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
TRC_NRM((TB, "canceling an IRP."));
//
// If the IRP is not being canceled.
//
if (setCancelResult != NULL) { //
// Fail the request.
//
pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL; IoCompleteRequest(pIrp, IO_NO_INCREMENT); }
//
// Get the next one.
//
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql); pIrp = (PIRP)RDPEVNTLIST_DequeueRequest( UserModeEventListMgr, context->sessionID ); } RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
//
// Release our context.
//
delete context; irpStack->FileObject->FsContext = NULL;
Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0;
ntStatus = Irp->IoStatus.Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return ntStatus; }
NTSTATUS RDPDYN_Cleanup( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++
Routine Description:
Handle the cleanup IRP for a file object.
Arguments:
Return Value:
NT status code
--*/ { NTSTATUS ntStatus; PFILE_OBJECT fileObject; PIO_STACK_LOCATION irpStack; PDEVMGRCONTEXT context; KIRQL oldIrql; PIRP pIrp; PDRIVER_CANCEL setCancelResult;
BEGIN_FN("RDPDYN_Cleanup");
irpStack = IoGetCurrentIrpStackLocation (Irp); fileObject = irpStack->FileObject;
// Grab our "open" context for this instance of us from the current stack
// location's file object.
context = (PDEVMGRCONTEXT)irpStack->FileObject->FsContext; TRC_ASSERT(context->magicNo == DEVMGRCONTEXTMAGICNO, (TB, "invalid context"));
TRC_NRM((TB, "cancelling IRP's for session %ld.", context->sessionID));
//
// Remove pending requests (IRP's)
// Nothing to do if event list is NULL
//
TRC_ASSERT(UserModeEventListMgr != NULL, (TB, "RdpDyn EventList is NULL")); if (UserModeEventListMgr == NULL) { goto CleanupAndExit; } RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql); pIrp = (PIRP)RDPEVNTLIST_DequeueRequest( UserModeEventListMgr, context->sessionID ); while (pIrp != NULL) {
//
// Set the cancel routine to NULL and record the current state.
//
setCancelResult = IoSetCancelRoutine(pIrp, NULL);
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
TRC_NRM((TB, "canceling an IRP."));
//
// If the IRP is not being canceled.
//
if (setCancelResult != NULL) { //
// Fail the request.
//
pIrp->IoStatus.Status = STATUS_CANCELLED; pIrp->IoStatus.Information = 0; IoCompleteRequest(pIrp, IO_NO_INCREMENT); }
//
// Get the next one.
//
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql); pIrp = (PIRP)RDPEVNTLIST_DequeueRequest( UserModeEventListMgr, context->sessionID ); } RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
CleanupAndExit: Irp->IoStatus.Status = STATUS_SUCCESS; ntStatus = Irp->IoStatus.Status; IoCompleteRequest (Irp, IO_NO_INCREMENT );
return ntStatus; }
NTSTATUS RDPDYN_DeviceControl( IN PDEVICE_OBJECT deviceObject, IN PIRP irp ) /*++
Routine Description:
Handle IOCTL IRP's.
Arguments:
DeviceObject - pointer to the device object for this printer. Irp - the IRP.
Return Value:
NT status code
--*/ { PIO_STACK_LOCATION currentStackLocation; NTSTATUS ntStatus; ULONG controlCode;
BEGIN_FN("RDPDYN_DeviceControl");
// Get the current stack location.
currentStackLocation = IoGetCurrentIrpStackLocation(irp); TRC_ASSERT(currentStackLocation != NULL, (TB, "Invalid stack location."));
//
// Grab some info. out of the stack location.
//
controlCode = currentStackLocation->Parameters.DeviceIoControl.IoControlCode;
//
// Dispatch the IOCTL.
//
switch(controlCode) { case IOCTL_RDPDR_GETNEXTDEVMGMTEVENT :
ntStatus = RDPDYN_HandleGetNextDevMgmtEventIOCTL(deviceObject, irp); break;
case IOCTL_RDPDR_CLIENTMSG :
ntStatus = RDPDYN_HandleClientMsgIOCTL(deviceObject, irp); break;
#if DBG
case IOCTL_RDPDR_DBGADDNEWPRINTER :
// This is for testing so we can create a new test printer on
// demand from user-mode.
ntStatus = RDPDYN_HandleDbgAddNewPrnIOCTL(deviceObject, irp); break;
#endif
default : TRC_ASSERT(FALSE, (TB, "RPDR.SYS:Invalid IOCTL %08X.", controlCode)); ntStatus = STATUS_INVALID_DEVICE_REQUEST; irp->IoStatus.Status = ntStatus; irp->IoStatus.Information = 0; IoCompleteRequest(irp, IO_NO_INCREMENT); }
return ntStatus; }
NTSTATUS RDPDYN_HandleClientMsgIOCTL( IN PDEVICE_OBJECT deviceObject, IN PIRP pIrp ) /*++
Routine Description:
Completely handles IOCTL_RDPDR_CLIENTMSG IRP's.
Arguments:
DeviceObject - pointer to our device object. currentStackLocation - current location on the IRP stack.
Return Value:
NT status code
--*/ { PIO_STACK_LOCATION currentStackLocation; PDEVMGRCONTEXT context; NTSTATUS ntStatus; ULONG inputLength;
BEGIN_FN("RDPDYN_HandleClientMsgIOCTL");
//
// Get the current stack location.
//
currentStackLocation = IoGetCurrentIrpStackLocation(pIrp); TRC_ASSERT(currentStackLocation != NULL, (TB, "Invalid stack location."));
//
// Grab our "open" context for this instance of use from the current stack
// location's file object.
//
context = (PDEVMGRCONTEXT)currentStackLocation->FileObject->FsContext;
TRC_NRM((TB, "Requestor session ID %d.", context->sessionID ));
TRC_ASSERT(context->magicNo == DEVMGRCONTEXTMAGICNO, (TB, "invalid context"));
//
// Grab some information about the user-mode's buffer off the IRP stack.
//
inputLength = currentStackLocation->Parameters.DeviceIoControl.InputBufferLength;
//
// Send the message to the client.
//
ntStatus = DrSendMessageToSession( context->sessionID, pIrp->AssociatedIrp.SystemBuffer, inputLength, NULL, NULL ); if (ntStatus != STATUS_SUCCESS) { TRC_ERR((TB, "msg failed."));
// Fail the IRP request.
pIrp->IoStatus.Status = ntStatus; } else { TRC_ERR((TB, "msg succeeded."));
pIrp->IoStatus.Status = STATUS_SUCCESS; pIrp->IoStatus.Information = 0; } IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return ntStatus; }
VOID DevMgmtEventRequestIRPCancel( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++
Routine Description:
IRP cancel routine that is attached to device mgmt event request IRP's. This routine is called with the cancel spinlock held.
Arguments:
DeviceObject - pointer to our device object. pIrp - The IRP.
Return Value:
NA
--*/ { PIO_STACK_LOCATION currentStackLocation; KIRQL oldIrql; ULONG sessionID; PDEVMGRCONTEXT context;
BEGIN_FN("DevMgmtEventRequestIRPCancel");
//
// Get the current stack location.
//
currentStackLocation = IoGetCurrentIrpStackLocation(Irp); TRC_ASSERT(currentStackLocation != NULL, (TB, "Invalid stack location."));
//
// Grab our "open" context for this instance of use from the current stack
// location's file object.
//
context = (PDEVMGRCONTEXT)currentStackLocation->FileObject->FsContext;
//
// Grab the session ID.
//
sessionID = context->sessionID; TRC_NRM((TB, "session ID %d.", sessionID)); TRC_ASSERT(context->magicNo == DEVMGRCONTEXTMAGICNO, (TB, "invalid context"));
//
// Wax the current cancel routine pointer.
//
IoSetCancelRoutine(Irp, NULL);
//
// Release the IRP cancellation spinlock.
//
IoReleaseCancelSpinLock(Irp->CancelIrql);
//
// Remove the request from the device management list.
//
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql); RDPEVNTLIST_DequeueSpecificRequest(UserModeEventListMgr, sessionID, Irp); RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
//
// Complete the IRP.
//
Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_CANCELLED; IoCompleteRequest(Irp, IO_NO_INCREMENT);
TRC_NRM((TB, "DevMgmtEventRequestIRPCancel exiting.")); }
NTSTATUS RDPDYN_HandleGetNextDevMgmtEventIOCTL( IN PDEVICE_OBJECT deviceObject, IN PIRP pIrp ) /*++
Routine Description:
Completely handles IOCTL_RDPDR_GETNEXTDEVMGMTEVENT IRP's.
Arguments:
DeviceObject - pointer to our device object. pIrp - The IRP.
Return Value:
NT status code
--*/ { PIO_STACK_LOCATION currentStackLocation; NTSTATUS status; ULONG outputLength; PDEVMGRCONTEXT context; ULONG evType; PVOID evt; DrDevice *drDevice; KIRQL oldIrql; ULONG sessionID; ULONG eventSize; ULONG requiredUserBufSize;
BEGIN_FN("RDPDYN_HandleGetNextDevMgmtEventIOCTL");
//
// Get the current stack location.
//
currentStackLocation = IoGetCurrentIrpStackLocation(pIrp); TRC_ASSERT(currentStackLocation != NULL, (TB, "Invalid stack location."));
//
// Grab our "open" context for this instance of use from the current stack
// location's file object.
//
context = (PDEVMGRCONTEXT)currentStackLocation->FileObject->FsContext;
//
// Grab the session ID.
//
sessionID = context->sessionID;
TRC_NRM((TB, "Requestor session ID %d.", context->sessionID ));
TRC_ASSERT(context->magicNo == DEVMGRCONTEXTMAGICNO, (TB, "invalid context"));
// Grab some information about the user-mode's buffer off the IRP stack.
outputLength = currentStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
TRC_ASSERT(UserModeEventListMgr != NULL, (TB, "RdpDyn EventList is NULL")); //
// Lock the device management event list.
//
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql);
//
// See if we have a "device mgmt event pending."
//
if (RDPEVNTLIST_PeekNextEvent( UserModeEventListMgr, sessionID, &evt, &evType, &drDevice )) { //
// If the pending IRP's pending buffer is large enough for the
// next event.
//
eventSize = RDPDYN_DevMgmtEventSize(evt, evType); requiredUserBufSize = eventSize + sizeof(RDPDRDVMGR_EVENTHEADER); if (outputLength >= requiredUserBufSize) { //
// Dequeue the next pending event. This better be the one
// we just peeked at.
//
RDPEVNTLIST_DequeueEvent( UserModeEventListMgr, sessionID, &evType, &evt, NULL );
//
// It's safe to unlock the device management event list now.
//
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
//
// Complete the pending IRP.
//
status = CompleteIRPWithDevMgmtEvent( deviceObject, pIrp, eventSize, evType, evt, drDevice );
//
// Release the event.
//
if (evt != NULL) { delete evt; evt = NULL; }
//
// Release our reference to the device, if we own one.
//
if (drDevice != NULL) { drDevice->Release(); } } //
// Otherwise, need to send a resize buffer message to the
// user-mode copmonent.
//
else { //
// It's safe to unlock the device management event list now.
//
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
//
// Complete the IRP.
//
status = CompleteIRPWithResizeMsg(pIrp, requiredUserBufSize); } } //
// Otherwise, queue the IRP, mark the IRP pending and return.
//
else { //
// Queue the request.
//
status = RDPEVNTLIST_EnqueueRequest(UserModeEventListMgr, context->sessionID, pIrp); //
// Set the cancel routine for the pending IRP.
//
if (status == STATUS_SUCCESS) { IoMarkIrpPending(pIrp); IoSetCancelRoutine(pIrp, DevMgmtEventRequestIRPCancel); status = STATUS_PENDING; } else { // Fail the IRP request.
pIrp->IoStatus.Status = status; IoCompleteRequest(pIrp, IO_NO_INCREMENT); }
//
// It's safe to unlock the device management event list now.
//
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql); }
return status; }
void RDPDYN_SessionConnected( IN ULONG sessionID ) /*++
Routine Description:
This function is called when a new session is connected.
Arguments:
sessionID - Identifier for removed session.
Return Value:
None.
--*/ { #if DBG
BOOL result; PVOID evt; DrDevice *drDevice; KIRQL oldIrql; ULONG evType; #endif
BEGIN_FN("RDPDYN_SessionConnected"); TRC_NRM((TB, "Session %ld.", sessionID)); //
// Nothing to do if the event list is NULL
//
TRC_ASSERT(UserModeEventListMgr != NULL, (TB, "RdpDyn EventList is NULL")); if (UserModeEventListMgr == NULL) { goto CleanupAndExit; } #if DBG
//
// See if there is still an event in the queue. Really, we should be checking
// to see if there is more than one event in the queue. This will catch most
// problems with events not gettin cleaned up on session disconnect.
//
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql); result = RDPEVNTLIST_PeekNextEvent( UserModeEventListMgr, sessionID, &evt, &evType, &drDevice); RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
//
// The only pending event allowed in the queue, at this point, is
// a remove client device event. RDPDYN_SessionDisconnected discards
// all other events.
//
if (result) { TRC_ASSERT(evType == RDPDREVT_SESSIONDISCONNECT, (TB, "Pending non-remove events %x on session connect.", evType)); } #endif
CleanupAndExit: return; }
void RDPDYN_SessionDisconnected( IN ULONG sessionID ) /*++
Routine Description:
This function is called when a session is disconnected from the system.
Arguments:
sessionID - Identifier for removed session.
Return Value:
None.
--*/ { void *devMgmtEvent; ULONG type; BOOL queued; KIRQL oldIrql; DrDevice *device;
BEGIN_FN("RDPDYN_SessionDisconnected"); TRC_NRM((TB, "Session %ld.", sessionID));
//
// Remove all pending device management events for this session.
// Nothing to do if the event list is NULL
//
TRC_ASSERT(UserModeEventListMgr != NULL, (TB, "RdpDyn EventList is NULL")); if (UserModeEventListMgr == NULL) { goto CleanupAndExit; } RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql); while (RDPEVNTLIST_DequeueEvent( UserModeEventListMgr, sessionID, &type, &devMgmtEvent, &device )) {
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
if (devMgmtEvent != NULL) { delete devMgmtEvent; } if (device != NULL) { device->Release(); } RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql); } RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
//
// Dispatch a "session disconnect" event for the session to let user
// mode know about the event.
//
RDPDYN_DispatchNewDevMgmtEvent( NULL, sessionID, RDPDREVT_SESSIONDISCONNECT, NULL ); CleanupAndExit: return; }
PIRP GetNextEventRequest( IN RDPEVNTLIST list, IN ULONG sessionID ) /*++
Routine Description:
Returns the next pending device management event request for the specified session, in the form of an IRP. Note that this function can not be called if a spinlock has been acquired.
Arguments:
list - Device Management Event and Requeust List sessionID - Destination session ID for event.
Return Value:
The next pending request (IRP) for the specified session or NULL if there are not any IRP's pending.
--*/ { PIRP pIrp; KIRQL oldIrql; BOOL done; PDRIVER_CANCEL setCancelResult;
BEGIN_FN("GetNextEventRequest"); //
// Loop until we get an IRP that is not currently being cancelled.
//
done = FALSE; setCancelResult = NULL; while (!done) {
//
// Dequeue an IRP and take it out of a cancellable state.
//
RDPEVNTLIST_Lock(list, &oldIrql); pIrp = (PIRP)RDPEVNTLIST_DequeueRequest(list, sessionID); if (pIrp != NULL) { setCancelResult = IoSetCancelRoutine(pIrp, NULL); } RDPEVNTLIST_Unlock(list, oldIrql);
done = (pIrp == NULL) || (setCancelResult != NULL); }
return pIrp; }
NTSTATUS RDPDYN_DispatchNewDevMgmtEvent( IN PVOID devMgmtEvent, IN ULONG sessionID, IN ULONG eventType, OPTIONAL IN DrDevice *devDevice ) /*++
Routine Description:
Dispatch a device management event to the appropriate (session-wise) user-mode device manager component. If there are not any event request IRP's pending for the specified session, then the event is queued for future dispatch.
Arguments:
devMgmtEvent - The event. sessionID - Destination session ID for event. eventType - Type of event. queued - TRUE if the event was queued for future dispatch. devDevice - Device object associated with the event. NULL, if not specified.
Return Value:
STATUS_SUCCESS if successful, error status otherwise.
--*/ { PIRP pIrp; NTSTATUS status; KIRQL oldIrql; PIO_STACK_LOCATION currentStackLocation; ULONG outputLength; ULONG eventSize; ULONG requiredUserBufSize; DrDevice *drDevice = NULL; PVOID evt; ULONG evType;
BEGIN_FN("RDPDYN_DispatchNewDevMgmtEvent");
//
// Nothing to do if the event list is NULL
//
TRC_ASSERT(UserModeEventListMgr != NULL, (TB, "RdpDyn EventList is NULL")); if (UserModeEventListMgr == NULL) { return STATUS_INVALID_DEVICE_STATE; }
//
// Ref count the device, if provided.
//
if (devDevice != NULL) { devDevice->AddRef(); } //
// Enqueue the new event.
//
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql); status = RDPEVNTLIST_EnqueueEvent( UserModeEventListMgr, sessionID, devMgmtEvent, eventType, devDevice );
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
//
// If we have an IRP pending for the specified session.
//
if (status == STATUS_SUCCESS) { pIrp = GetNextEventRequest(UserModeEventListMgr, sessionID); } else { if (devDevice != NULL) { devDevice->Release(); } } if ((status == STATUS_SUCCESS) && (pIrp != NULL)) { TRC_NRM((TB, "found an IRP pending for " "session %ld", sessionID));
//
// Find out about the pending IRP.
//
currentStackLocation = IoGetCurrentIrpStackLocation(pIrp); TRC_ASSERT(currentStackLocation != NULL, (TB, "Invalid stack location.")); outputLength = currentStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
//
// If we have a pending event.
//
RDPEVNTLIST_Lock(UserModeEventListMgr, &oldIrql); if (RDPEVNTLIST_PeekNextEvent( UserModeEventListMgr, sessionID, &evt, &evType, &drDevice )) { //
// If the pending IRP's pending buffer is large enough for the
// next event.
//
eventSize = RDPDYN_DevMgmtEventSize(evt, evType); requiredUserBufSize = eventSize + sizeof(RDPDRDVMGR_EVENTHEADER); if (outputLength >= requiredUserBufSize) { //
// Dequeue the next pending event. This better be the one
// we just peeked at.
//
RDPEVNTLIST_DequeueEvent( UserModeEventListMgr, sessionID, &evType, &evt, NULL );
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
//
// Complete the pending IRP.
//
status = CompleteIRPWithDevMgmtEvent( RDPDYN_PDO, pIrp, eventSize, evType, evt, drDevice );
//
// Release the event.
//
if (evt != NULL) { delete evt; evt = NULL; }
//
// Release our reference to the device, if we own one.
//
if (drDevice != NULL) { drDevice->Release(); }
} //
// Otherwise, need to send a resize buffer message to the
// user-mode component.
//
else { RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
//
// Complete the IRP.
//
status = CompleteIRPWithResizeMsg(pIrp, requiredUserBufSize); } } //
// Otherwise, we need to requeue the IRP request.
//
else { status = RDPEVNTLIST_EnqueueRequest(UserModeEventListMgr, sessionID, pIrp);
RDPEVNTLIST_Unlock(UserModeEventListMgr, oldIrql);
//
// If we fail here, we need to fail the IRP.
//
if (status != STATUS_SUCCESS) { pIrp->IoStatus.Status = status; pIrp->IoStatus.Information = 0; IoCompleteRequest(pIrp, IO_NO_INCREMENT); } } }
TRC_NRM((TB, "exit RDPDYN_DispatchNewDevMgmtEvent")); return status; }
ULONG RDPDYN_DevMgmtEventSize( IN PVOID devMgmtEvent, IN ULONG type ) /*++
Routine Description:
Calculate the size of a device management event. This is more efficient than storing the size with each event.
Arguments:
devMgmtEvent - Supplies the device object for the packet being processed.
type - Supplies the Irp being processed
Return Value:
The size, in bytes, of the event.
--*/ { ULONG sz = 0;
BEGIN_FN("RDPDYN_DevMgmtEventSize"); switch(type) { case RDPDREVT_PRINTERANNOUNCE : sz = CALCPRINTERDEVICE_SUB_SZ((PRDPDR_PRINTERDEVICE_SUB)devMgmtEvent); break; case RDPDREVT_REMOVEDEVICE : sz = CALCREMOVEDEVICE_SUB_SZ((PRDPDR_REMOVEDEVICE)devMgmtEvent); break; case RDPDREVT_PORTANNOUNCE : sz = CALCPORTDEVICE_SUB_SZ((PRDPDR_PORTDEVICE_SUB)devMgmtEvent); break; case RDPDREVT_DRIVEANNOUNCE : sz = CALCDRIVEDEVICE_SUB_SZ((PRDPDR_DRIVEDEVICE_SUB)devMgmtEvent); break;
case RDPDREVT_SESSIONDISCONNECT : // There is no associated event data.
sz = 0; break; default: TRC_ASSERT(FALSE, (TB, "Invalid event type")); } return sz; }
NTSTATUS CompleteIRPWithDevMgmtEvent( IN PDEVICE_OBJECT deviceObject, IN PIRP pIrp, IN ULONG eventSize, IN ULONG eventType, IN PVOID event, IN DrDevice *drDevice ) /*++
Routine Description:
Complete a pending IRP with a device management event.
Arguments:
deviceObject- Associated Device Object. Must be non-NULL if drDevice is non-NULL. pIrp - Pending IRP. eventSize - Size of event being returned. eventType - Event type being returned. event - The event being returned. drDevice - Device object associated with the IRP.
Return Value:
STATUS_SUCCESS on success.
--*/ { PRDPDRDVMGR_EVENTHEADER msgHeader; ULONG bytesReturned; void *usrDevMgmtEvent; NTSTATUS status;
BEGIN_FN("CompleteIRPWithDevMgmtEvent");
//
// Optional last-minute event completion.
//
if (drDevice != NULL) { status = drDevice->OnDevMgmtEventCompletion(deviceObject, event); } else { status = STATUS_SUCCESS; }
//
// Compute the size of the return buffer.
//
bytesReturned = eventSize + sizeof(RDPDRDVMGR_EVENTHEADER);
//
// Create the message header.
//
msgHeader = (PRDPDRDVMGR_EVENTHEADER)pIrp->AssociatedIrp.SystemBuffer; msgHeader->EventType = eventType; msgHeader->EventLength = eventSize;
//
// Copy the device mgmt event over to the user-mode buffer.
//
usrDevMgmtEvent = ((PBYTE)pIrp->AssociatedIrp.SystemBuffer + sizeof(RDPDRDVMGR_EVENTHEADER)); if (event != NULL && eventSize > 0) { RtlCopyMemory(usrDevMgmtEvent, event, eventSize); } status = STATUS_SUCCESS;
pIrp->IoStatus.Status = status; pIrp->IoStatus.Information = bytesReturned; IoCompleteRequest(pIrp, IO_NO_INCREMENT);
TRC_NRM((TB, "exit CompleteIRPWithDevMgmtEvent"));
return status; }
NTSTATUS CompleteIRPWithResizeMsg( IN PIRP pIrp, IN ULONG requiredUserBufSize ) /*++
Routine Description:
Complete a pending IRP with a resize buffer event to the user-mode component.
Return Value:
STATUS_SUCCESS is returned on success.
--*/ { PIO_STACK_LOCATION currentStackLocation; ULONG outputLength; PRDPDR_BUFFERTOOSMALL bufTooSmallMsg; PRDPDRDVMGR_EVENTHEADER msgHeader; ULONG bytesReturned; NTSTATUS status;
BEGIN_FN("CompleteIRPWithResizeMsg");
// Get the current stack location.
currentStackLocation = IoGetCurrentIrpStackLocation(pIrp); TRC_ASSERT(currentStackLocation != NULL, (TB, "Invalid stack location."));
// Grab some stuff off the IRP stack.
outputLength = currentStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
//
// Fail the request if there isn't room for a buffer too small
// message.
//
if (outputLength < (sizeof(RDPDRDVMGR_EVENTHEADER) + sizeof(RDPDR_BUFFERTOOSMALL))) {
TRC_NRM((TB, "CompleteIRPWithResizeMsg no room for header."));
bytesReturned = 0; status = STATUS_INVALID_BUFFER_SIZE; } else { // Create the header.
msgHeader = (PRDPDRDVMGR_EVENTHEADER)pIrp->AssociatedIrp.SystemBuffer; msgHeader->EventType = RDPDREVT_BUFFERTOOSMALL; msgHeader->EventLength = sizeof(RDPDR_BUFFERTOOSMALL);
// Create the buffer too small message.
bufTooSmallMsg = (PRDPDR_BUFFERTOOSMALL) ((PBYTE)pIrp->AssociatedIrp.SystemBuffer + sizeof(RDPDRDVMGR_EVENTHEADER)); bufTooSmallMsg->RequiredSize = requiredUserBufSize;
// Calculate the number of bytes that we are returning.
bytesReturned = sizeof(RDPDRDVMGR_EVENTHEADER) + sizeof(RDPDR_BUFFERTOOSMALL);
status = STATUS_SUCCESS; }
//
// Complete the IRP.
//
pIrp->IoStatus.Status = status; pIrp->IoStatus.Information = bytesReturned; IoCompleteRequest(pIrp, IO_NO_INCREMENT);
TRC_NRM((TB, "exit CompleteIRPWithResizeMsg"));
return status; }
NTSTATUS DrSendMessageToSession( IN ULONG SessionId, IN PVOID Msg, IN DWORD MsgSize, OPTIONAL IN RDPDR_ClientMessageCB CB, OPTIONAL IN PVOID ClientData ) /*++
Routine Description:
Send a message to the client with the specified session ID.
Arguments:
SessionId - The session id. Msg - The Message MsgSize - Size (in bytes) of message. CB - Optional callback to be called when the message is completely sent. ClientData - Optional client-data passed to callback when message is completely sent.
Return Value:
NTSTATUS - Success/failure indication of the operation
Notes:
--*/ { NTSTATUS Status; SmartPtr<DrSession> Session; PCLIENTMESSAGECONTEXT Context;
BEGIN_FN("DrSendMessageToSession");
//
// Find the client entry.
//
if (Sessions->FindSessionById(SessionId, Session)) { //
// Allocate the context for the function call.
//
Context = new CLIENTMESSAGECONTEXT;
if (Context != NULL) {
TRC_NRM((TB, "sending %ld bytes to server", MsgSize));
//
// Set up the context.
//
Context->CB = CB; Context->ClientData = ClientData; Status = Session->SendToClient(Msg, MsgSize, DrSendMessageToClientCompletion, FALSE, FALSE, Context); } else { TRC_ERR((TB, "unable to allocate memory.")); Status = STATUS_INSUFFICIENT_RESOURCES; } } else { Status = STATUS_NOT_FOUND; }
return Status; }
NTSTATUS NTAPI DrSendMessageToClientCompletion(PVOID Context, PIO_STATUS_BLOCK IoStatusBlock) /*++
Routine Description:
IoCompletion APC routine for DrSendMessageToClient.
Arguments:
ApcContext - Contains a pointer to the client message context. IoStatusBlock - Status information about the operation. The Information indicates the actual number of bytes written Reserved - Reserved
Return Value:
None
--*/ { PCLIENTMESSAGECONTEXT MsgContext = (PCLIENTMESSAGECONTEXT)Context;
BEGIN_FN("DrSendMessageToClientCompletion");
TRC_ASSERT(MsgContext != NULL, (TB, "Message context NULL.")); TRC_ASSERT(IoStatusBlock != NULL, (TB, "IoStatusBlock NULL."));
TRC_NRM((TB, "status %lx", IoStatusBlock->Status));
//
// Call the client callback if it is defined.
//
if (MsgContext->CB != NULL) { MsgContext->CB(MsgContext->ClientData, IoStatusBlock->Status); }
//
// Clean up.
//
// delete IoStatusBlock; // I don't think so, not really
delete Context; return STATUS_SUCCESS; }
/*++
Routine Description:
Generates a printer announce message for testing.
Return Value:
STATUS_INVALID_BUFFER_SIZE is returned if the prnAnnounceEventSize size is too small. STATUS_SUCCESS is returned on success.
--*/
#if DBG
void RDPDYN_TracePrintAnnounceMsg( IN OUT PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg, IN ULONG sessionID, IN PCWSTR portName, IN PCWSTR clientName ) /*++
Routine Description:
Trace a printer device announce message.
Return Value:
--*/ { PWSTR driverName, printerName; PWSTR pnpName; PRDPDR_PRINTERDEVICE_ANNOUNCE clientPrinterFields; PBYTE pClientPrinterData; ULONG sz;
BEGIN_FN("RDPDYN_TracePrintAnnounceMsg");
// Check the type.
TRC_ASSERT(devAnnounceMsg->DeviceType == RDPDR_DTYP_PRINT, (TB, "Invalid device type"));
// Get the address of all data following the base message.
pClientPrinterData = ((PBYTE)devAnnounceMsg) + sizeof(RDPDR_DEVICE_ANNOUNCE) + sizeof(RDPDR_PRINTERDEVICE_ANNOUNCE);
// Get the address of the client printer fields.
clientPrinterFields = (PRDPDR_PRINTERDEVICE_ANNOUNCE)(((PBYTE)devAnnounceMsg) + sizeof(RDPDR_DEVICE_ANNOUNCE));
sz = clientPrinterFields->PnPNameLen + clientPrinterFields->DriverLen + clientPrinterFields->PrinterNameLen + clientPrinterFields->CachedFieldsLen + sizeof(RDPDR_PRINTERDEVICE_ANNOUNCE);
if (devAnnounceMsg->DeviceDataLength != sz) { TRC_ASSERT(FALSE,(TB, "Size integrity questionable in dev announce buf.")); } else {
// Get the specific fields.
pnpName = (PWSTR)((clientPrinterFields->PnPNameLen) ? pClientPrinterData : NULL); driverName = (PWSTR)((clientPrinterFields->DriverLen) ? (pClientPrinterData + clientPrinterFields->PnPNameLen) : NULL); printerName = (PWSTR)((clientPrinterFields->PrinterNameLen) ? (pClientPrinterData + clientPrinterFields->PnPNameLen + clientPrinterFields->DriverLen) : NULL); TRC_NRM((TB, "New printer received for session %ld.", sessionID)); TRC_NRM((TB, "-----------------------------------------")); TRC_NRM((TB, "port:\t%ws", portName));
if (clientPrinterFields->Flags & RDPDR_PRINTER_ANNOUNCE_FLAG_ANSI) { TRC_NRM((TB, "driver:\t%s", (PSTR)driverName)); TRC_NRM((TB, "pnp name:\t%s", (PSTR)pnpName)); TRC_NRM((TB, "printer name:\t%s", (PSTR)printerName)); } else { TRC_NRM((TB, "driver:\t%ws", driverName)); TRC_NRM((TB, "pnp name:\t%ws", pnpName)); TRC_NRM((TB, "printer name:\t%ws", printerName)); }
TRC_NRM((TB, "client name:\t%ws", clientName)); TRC_NRM((TB, "-----------------------------------------")); TRC_NRM((TB, "exit RDPDYN_TracePrintAnnounceMsg")); } }
NTSTATUS RDPDYN_GenerateTestPrintAnnounceMsg( IN OUT PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg, IN ULONG devAnnounceMsgSize, OPTIONAL OUT ULONG *prnAnnounceMsgReqSize ) /*++
Routine Description:
Generates a printer announce message for testing.
Return Value:
STATUS_INVALID_BUFFER_SIZE is returned if the prnAnnounceMsgSize size is too small. STATUS_SUCCESS is returned on success.
--*/ { ULONG requiredSize; PBYTE pClientPrinterData; PWSTR driverName, printerName; PWSTR pnpName; PRDPDR_PRINTERDEVICE_ANNOUNCE prnMsg; PRDPDR_PRINTERDEVICE_ANNOUNCE clientPrinterFields; PBYTE pCachedFields;
BEGIN_FN("RDPDYN_GenerateTestPrintAnnounceMsg"); requiredSize = (ULONG)(sizeof(RDPDR_DEVICE_ANNOUNCE) + sizeof(RDPDR_PRINTERDEVICE_ANNOUNCE) + ((wcslen(TESTDRIVERNAME) + 1) * sizeof(WCHAR)) + ((wcslen(TESTPNPNAME) + 1) * sizeof(WCHAR)) + ((wcslen(TESTPRINTERNAME) + 1) * sizeof(WCHAR)));
//
// Find out if there isn't room in the return buffer for our response.
//
if (devAnnounceMsgSize < requiredSize) { if (prnAnnounceMsgReqSize != NULL) { *prnAnnounceMsgReqSize = requiredSize; } return STATUS_BUFFER_TOO_SMALL; }
// Type
devAnnounceMsg->DeviceType = RDPDR_DTYP_PRINT;
// ID
devAnnounceMsg->DeviceId = TESTDEVICEID;
// Get the address of the client printer fields in the device announce
// message.
clientPrinterFields = (PRDPDR_PRINTERDEVICE_ANNOUNCE)(((PBYTE)devAnnounceMsg) + sizeof(RDPDR_DEVICE_ANNOUNCE));
// Get the address of all data following the base message.
pClientPrinterData = ((PBYTE)devAnnounceMsg) + sizeof(RDPDR_DEVICE_ANNOUNCE) + sizeof(RDPDR_PRINTERDEVICE_ANNOUNCE);
//
// Add the PnP Name.
//
// The PnP name is the first field.
pnpName = (PWSTR)pClientPrinterData; wcscpy(pnpName, TESTPNPNAME); clientPrinterFields->PnPNameLen = ((wcslen(TESTPNPNAME) + 1) * sizeof(WCHAR));
//
// Add the Driver Name.
//
// The driver name is the second field.
driverName = (PWSTR)(pClientPrinterData + clientPrinterFields->PnPNameLen); wcscpy(driverName, TESTDRIVERNAME); clientPrinterFields->DriverLen = ((wcslen(TESTDRIVERNAME) + 1) * sizeof(WCHAR));
//
// Add the Printer Name.
//
// The driver name is the second field.
printerName = (PWSTR)(pClientPrinterData + clientPrinterFields->PnPNameLen + clientPrinterFields->DriverLen); wcscpy(printerName, TESTPRINTERNAME); clientPrinterFields->PrinterNameLen = ((wcslen(TESTPRINTERNAME) + 1) * sizeof(WCHAR));
//
// Add the Cached Fields Len.
//
// The cached fields follow everything else.
/* Don't need this for testing, yet.
pCachedFields = (PBYTE)(pClientPrinterData + clientPrinterFields->PnPNameLen + clientPrinterFields->DriverLen + clientPrinterFields->PrinterNameLen); */ clientPrinterFields->CachedFieldsLen = 0;
//
// Set to non-ansi for now.
//
clientPrinterFields->Flags = 0;
// Length of all data following deviceFields.
devAnnounceMsg->DeviceDataLength = sizeof(RDPDR_PRINTERDEVICE_ANNOUNCE) + clientPrinterFields->PnPNameLen + clientPrinterFields->DriverLen + clientPrinterFields->PrinterNameLen + clientPrinterFields->CachedFieldsLen;
if (prnAnnounceMsgReqSize != NULL) { *prnAnnounceMsgReqSize = requiredSize; }
return STATUS_SUCCESS; }
#endif
#if DBG
NTSTATUS RDPDYN_HandleDbgAddNewPrnIOCTL( IN PDEVICE_OBJECT deviceObject, IN PIRP pIrp ) /*++
Routine Description:
This is for testing so we can create a new test printer on demand from user-mode.
Arguments:
DeviceObject - pointer to our device object. currentStackLocation - current location on the IRP stack.
Return Value:
NT status code
--*/ { PRDPDR_DEVICE_ANNOUNCE pDevAnnounceMsg; ULONG bytesToAlloc; PIO_STACK_LOCATION currentStackLocation; ULONG requiredSize; ULONG bytesReturned = 0; PDEVMGRCONTEXT context; NTSTATUS ntStatus; WCHAR buffer[64]=L"Test Printer"; UNICODE_STRING referenceString; PBYTE tmp;
BEGIN_FN("RDPDYN_HandleDbgAddNewPrnIOCTL");
// Get the current stack location.
currentStackLocation = IoGetCurrentIrpStackLocation(pIrp); TRC_ASSERT(currentStackLocation != NULL, (TB, "Invalid stack location."));
// Grab our "open" context for this instance of us from the current stack
// location's file object.
context = (PDEVMGRCONTEXT)currentStackLocation->FileObject->FsContext; TRC_ASSERT(context->magicNo == DEVMGRCONTEXTMAGICNO, (TB, "invalid context"));
// Find out how much room we need for the test message.
RDPDYN_GenerateTestPrintAnnounceMsg(NULL, 0, &requiredSize);
// Generate the message.
pDevAnnounceMsg = (PRDPDR_DEVICE_ANNOUNCE)new(NonPagedPool) BYTE[requiredSize]; if (pDevAnnounceMsg != NULL) { RDPDYN_GenerateTestPrintAnnounceMsg(pDevAnnounceMsg, requiredSize, &requiredSize); //
// Announce the new port (just send to session 0 for now).
//
RtlInitUnicodeString(&referenceString, buffer); //#pragma message(__LOC__"Unit test to add device disabled")
/*
//
// Initialize the client entry struct.
//
RtlZeroMemory(&clientEntry, sizeof(clientEntry)); wcscpy(clientEntry.ClientName, L"DBGTEST"); clientEntry.SessionId = 0; // Note that I am ignoring the returned device data for this test.
// This is okay, since I never call RDPDYN_RemoveClientDevice(
ntStatus = RDPDYN_AddClientDevice( &clientEntry, pDevAnnounceMsg, &referenceString, &tmp ); */ // For a test, delete the device next.
// RDPDYN_RemoveClientDevice(TESTDEVICEID, 0, tmp);
ntStatus = STATUS_SUCCESS; } else { ntStatus = STATUS_INSUFFICIENT_RESOURCES; }
pIrp->IoStatus.Status = ntStatus; pIrp->IoStatus.Information = 0; IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return ntStatus; }
#endif
|