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.
991 lines
29 KiB
991 lines
29 KiB
/*++
|
|
|
|
Copyright (c) 1998-2000 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
prnport.cpp
|
|
|
|
Abstract:
|
|
|
|
Printer port Device object handles one redirected printer port
|
|
|
|
Revision History:
|
|
--*/
|
|
#include "precomp.hxx"
|
|
#define TRC_FILE "prnport"
|
|
#include "trc.h"
|
|
#include "TSQPublic.h"
|
|
|
|
extern "C" void RDPDYN_TracePrintAnnounceMsg(
|
|
IN OUT PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg,
|
|
IN ULONG sessionID,
|
|
IN PCWSTR portName,
|
|
IN PCWSTR clientName
|
|
);
|
|
|
|
//
|
|
// RDPDR.CPP: Configure Devices to send IO packets to client at
|
|
// low priority.
|
|
//
|
|
extern ULONG DeviceLowPrioSendFlags;
|
|
|
|
//
|
|
// RDPDR.CPP: RDPDR.SYS Device Object
|
|
//
|
|
extern PRDBSS_DEVICE_OBJECT DrDeviceObject;
|
|
|
|
//
|
|
// RDPDr.cpp : The TS Worker Queue pointer
|
|
//
|
|
extern PVOID RDPDR_TsQueue;
|
|
|
|
#define LPTNAME "LPT"
|
|
#define COMNAME "COM"
|
|
#define PRNNAME "PRN"
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// DrPrinterPort Methods
|
|
//
|
|
|
|
DrPrinterPort::DrPrinterPort(SmartPtr<DrSession> &Session, ULONG DeviceType, ULONG DeviceId,
|
|
PUCHAR PreferredDosName) : DrDevice(Session, DeviceType, DeviceId, PreferredDosName)
|
|
{
|
|
BEGIN_FN("DrPrinterPort::DrPrinterPort");
|
|
SetClassName("DrPrinterPort");
|
|
_PortNumber = 0;
|
|
_SymbolicLinkName.Length = 0;
|
|
_SymbolicLinkName.MaximumLength = 0;
|
|
_SymbolicLinkName.Buffer = NULL;
|
|
_IsOpen = FALSE;
|
|
_PortType = FILE_DEVICE_PRINTER;
|
|
}
|
|
DrPrinterPort::~DrPrinterPort()
|
|
{
|
|
//
|
|
// If the device has a port registered, then unregister the port.
|
|
//
|
|
if ((_PortNumber != 0) && (_SymbolicLinkName.Buffer != NULL)) {
|
|
RDPDRPRT_UnregisterPrinterPortInterface(_PortNumber,
|
|
&_SymbolicLinkName);
|
|
}
|
|
}
|
|
|
|
NTSTATUS DrPrinterPort::Initialize(PRDPDR_DEVICE_ANNOUNCE DeviceAnnounce, ULONG Length)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
DrPrinterPortWorkItem *pItem;
|
|
|
|
BEGIN_FN("DrPrinterPort::Initialize");
|
|
ASSERT(DeviceAnnounce != NULL);
|
|
|
|
//
|
|
// Create a new context for the work item.
|
|
//
|
|
pItem = new DrPrinterPortWorkItem;
|
|
if (pItem == NULL) {
|
|
status = STATUS_NO_MEMORY;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
pItem->pObj = this;
|
|
|
|
//
|
|
// Copy the device announce message.
|
|
//
|
|
pItem->deviceAnnounce = (PRDPDR_DEVICE_ANNOUNCE)new(NonPagedPool)
|
|
BYTE[sizeof(RDPDR_DEVICE_ANNOUNCE) + Length];
|
|
if (pItem->deviceAnnounce == NULL) {
|
|
TRC_ERR((TB, "Failed to allocate device announce message."));
|
|
status = STATUS_NO_MEMORY;
|
|
goto CLEANUPANDEXIT;
|
|
}
|
|
RtlCopyMemory(pItem->deviceAnnounce, DeviceAnnounce,
|
|
sizeof(RDPDR_DEVICE_ANNOUNCE) + Length);
|
|
|
|
//
|
|
// AddRef ourselves so we don't go away while the work item is trying to complete.
|
|
//
|
|
AddRef();
|
|
|
|
//
|
|
// Use our TS queue worker to queue the workitem
|
|
//
|
|
status = TSAddWorkItemToQueue( RDPDR_TsQueue, pItem, ProcessWorkItem );
|
|
|
|
if ( status != STATUS_SUCCESS ) {
|
|
TRC_ERR((TB, "RDPDR: FAILED Adding workitem to TS Queue 0x%8x", status));
|
|
}
|
|
|
|
CLEANUPANDEXIT:
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
if (pItem != NULL) {
|
|
if (pItem->deviceAnnounce != NULL) {
|
|
delete pItem->deviceAnnounce;
|
|
}
|
|
delete pItem;
|
|
}
|
|
}
|
|
|
|
TRC_NRM((TB, "exit PrnPort::Initialize"));
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
DrPrinterPort::FinishDeferredInitialization(
|
|
DrPrinterPortWorkItem *pItem
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
FinishDeferredInitialization
|
|
|
|
Handles deferred initialization of this object in a work item.
|
|
|
|
Arguments:
|
|
|
|
pItem - Printer port work item.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
BEGIN_FN("DrPrinterPort::FinishDeferredInitialization");
|
|
|
|
TRC_ASSERT(pItem->deviceAnnounce != NULL,
|
|
(TB, "pItem->deviceAnnounce != NULL"));
|
|
|
|
//
|
|
// If printer redirection is enabled at all and the subclass okays it
|
|
// then create the announce message.
|
|
//
|
|
if (ShouldCreatePrinter()) {
|
|
TRC_NRM((TB, "Creating printer."));
|
|
#if DBG
|
|
// Trace information about the printer.
|
|
RDPDYN_TracePrintAnnounceMsg(pItem->deviceAnnounce,
|
|
_Session->GetSessionId(), L"",
|
|
_Session->GetClientName());
|
|
#endif
|
|
Status = AnnouncePrinter(pItem->deviceAnnounce);
|
|
}
|
|
//
|
|
// Otherwise, check to see if we should only announce a port device.
|
|
//
|
|
else if (ShouldAnnouncePrintPort()) {
|
|
TRC_NRM((TB, "Announcing printer port."));
|
|
Status = AnnouncePrintPort(pItem->deviceAnnounce);
|
|
} else {
|
|
TRC_NRM((TB, "Skipping printing device."));
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Release the work item.
|
|
//
|
|
if (pItem != NULL) {
|
|
delete pItem->deviceAnnounce;
|
|
delete pItem;
|
|
}
|
|
|
|
//
|
|
// Release the ref count on ourselves that was added in the main initialization
|
|
// routine.
|
|
//
|
|
Release();
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS DrPrinterPort::CreateDevicePath(PUNICODE_STRING DevicePath)
|
|
/*++
|
|
Create NT DeviceName compatible with RDBSS convention
|
|
|
|
Format is:
|
|
\device\rdpdrport\;<DriveLetter>:<sessionid>\ClientName\DosDeviceName
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING DevicePathTail;
|
|
|
|
BEGIN_FN("DrPrinterPort::CreateDevicePath");
|
|
ASSERT(DevicePath != NULL);
|
|
|
|
DevicePath->Length = 0;
|
|
Status = RtlAppendUnicodeToString(DevicePath, RDPDR_PORT_DEVICE_NAME_U);
|
|
|
|
if (!NT_ERROR(Status)) {
|
|
// Add the reference string to the end:
|
|
// Format is: \;<DriveLetter>:<sessionid>\clientName\share
|
|
DevicePathTail.Length = 0;
|
|
DevicePathTail.MaximumLength = DevicePath->MaximumLength - DevicePath->Length;
|
|
DevicePathTail.Buffer = DevicePath->Buffer + (DevicePath->Length / sizeof(WCHAR));
|
|
|
|
CreateReferenceString(&DevicePathTail);
|
|
|
|
DevicePath->Length += DevicePathTail.Length;
|
|
}
|
|
|
|
TRC_NRM((TB, "DevicePath=%wZ", DevicePath));
|
|
|
|
return Status;
|
|
}
|
|
|
|
BOOL DrPrinterPort::ShouldAnnouncePrintPort()
|
|
{
|
|
BEGIN_FN("DrPrinterPort::ShouldAnnouncePrintPort");
|
|
return IsDeviceNameValid();
|
|
}
|
|
|
|
BOOL DrPrinterPort::ShouldCreatePrinter()
|
|
{
|
|
BEGIN_FN("DrPrinterPort::ShouldCreatePrinter");
|
|
if(!_Session->DisablePrinterMapping()) {
|
|
return IsDeviceNameValid();
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL DrPrinterPort::ShouldCreatePort()
|
|
{
|
|
BEGIN_FN("DrPrinterPort::ShouldCreatePort");
|
|
if (!_Session->DisablePrinterMapping()) {
|
|
return IsDeviceNameValid();
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL DrPrinterPort::IsDeviceNameValid()
|
|
{
|
|
BEGIN_FN("DrPrinterPort::IsDeviceNameValid");
|
|
BOOL fRet = FALSE;
|
|
PUCHAR PreferredDosName = _PreferredDosName;
|
|
char* portName = NULL;
|
|
//
|
|
// Our device name is valid only if
|
|
// the first 3 chars contain "LPT or "COM" or PRN"
|
|
// and the rest are digits.
|
|
// We will do case-sensitive compare.
|
|
//
|
|
switch(_DeviceType) {
|
|
case RDPDR_DTYP_SERIAL:
|
|
portName = COMNAME;
|
|
break;
|
|
|
|
case RDPDR_DTYP_PARALLEL:
|
|
portName = LPTNAME;
|
|
break;
|
|
|
|
case RDPDR_DTYP_PRINT:
|
|
portName = PRNNAME;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (portName != NULL) {
|
|
DWORD numChars = strlen(portName);
|
|
//
|
|
// ASSERT that we got atleast 3 chars for devicename
|
|
//
|
|
ASSERT(strlen((char*)PreferredDosName) >= numChars);
|
|
|
|
if(!strncmp((char*)PreferredDosName, portName, numChars)) {
|
|
fRet = TRUE;
|
|
//
|
|
// portname matches, check for digits.
|
|
//
|
|
PreferredDosName += numChars;
|
|
while(PreferredDosName && *PreferredDosName) {
|
|
if(!isdigit(*PreferredDosName)) {
|
|
fRet = FALSE;
|
|
break;
|
|
}
|
|
PreferredDosName++;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// This assert should never fire for port redirection
|
|
//
|
|
ASSERT(fRet);
|
|
return fRet;
|
|
}
|
|
|
|
NTSTATUS
|
|
DrPrinterPort::Write(
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN BOOL LowPrioSend
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Override the 'Write' method. This needs to go to the client at low priority
|
|
to prevent us from filling the entire pipe on a slow link with print data.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
return DrDevice::Write(
|
|
RxContext,
|
|
DeviceLowPrioSendFlags & DEVICE_LOWPRIOSEND_PRINTERS
|
|
);
|
|
}
|
|
|
|
VOID
|
|
DrPrinterPort::ProcessWorkItem(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ProcessWorkItem
|
|
|
|
Arguments:
|
|
|
|
deviceObject - Associated device object.
|
|
context - Work item context.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS on success. Otherwise, an error code is returned.
|
|
|
|
--*/
|
|
{
|
|
DrPrinterPortWorkItem* pItem = (DrPrinterPortWorkItem*)context;
|
|
pItem->pObj->FinishDeferredInitialization(pItem);
|
|
}
|
|
|
|
NTSTATUS DrPrinterPort::CreatePrinterPort(PWCHAR portName)
|
|
{
|
|
NTSTATUS status;
|
|
WCHAR ntDevicePathBuffer[RDPDRMAXREFSTRINGLEN];
|
|
UNICODE_STRING ntDevicePath = {0, sizeof(ntDevicePathBuffer),
|
|
ntDevicePathBuffer};
|
|
|
|
BEGIN_FN("DrPrinterPort::CreatePrinterPort");
|
|
CreateReferenceString(&ntDevicePath);
|
|
|
|
status = RDPDRPRT_RegisterPrinterPortInterface(_Session->GetClientName(),
|
|
(LPSTR)_PreferredDosName, &ntDevicePath, portName, &_SymbolicLinkName,
|
|
&_PortNumber);
|
|
if (status != STATUS_SUCCESS) {
|
|
_PortNumber = 0;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS DrPrinterPort::AnnouncePrintPort(PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG portAnnounceEventReqSize;
|
|
PRDPDR_PORTDEVICE_SUB portAnnounceEvent;
|
|
|
|
BEGIN_FN("DrPrinterPort::AnnouncePrintPort");
|
|
|
|
WCHAR portName[RDPDR_MAXPORTNAMELEN];
|
|
Status = CreatePrinterPort(portName);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
goto CleanUpAndReturn;
|
|
}
|
|
|
|
TRC_ASSERT(wcslen(portName)+1 <= RDPDR_MAXPORTNAMELEN,
|
|
(TB, "Port name too long"));
|
|
|
|
//
|
|
// Allocate the port device announce buffer.
|
|
//
|
|
Status = CreatePortAnnounceEvent(
|
|
devAnnounceMsg,
|
|
NULL,
|
|
0,
|
|
//L"",
|
|
portName,
|
|
&portAnnounceEventReqSize
|
|
);
|
|
|
|
ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
|
|
|
|
if( Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_SUCCESS ) {
|
|
|
|
portAnnounceEvent = (PRDPDR_PORTDEVICE_SUB)new(NonPagedPool)
|
|
BYTE[portAnnounceEventReqSize];
|
|
|
|
if (portAnnounceEvent == NULL) {
|
|
TRC_ERR((TB, "Unable to allocate portAnnounceEvent"));
|
|
Status = STATUS_NO_MEMORY;
|
|
goto CleanUpAndReturn;
|
|
}
|
|
|
|
//
|
|
// Create the port anounce message.
|
|
//
|
|
Status = CreatePortAnnounceEvent(
|
|
devAnnounceMsg,
|
|
portAnnounceEvent,
|
|
portAnnounceEventReqSize,
|
|
//L"",
|
|
portName,
|
|
&portAnnounceEventReqSize
|
|
);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
|
|
delete portAnnounceEvent;
|
|
#if DBG
|
|
portAnnounceEvent = NULL;
|
|
#endif
|
|
goto CleanUpAndReturn;
|
|
}
|
|
|
|
// device is a printer port.
|
|
portAnnounceEvent->deviceFields.DeviceType = RDPDR_DRYP_PRINTPORT;
|
|
|
|
//
|
|
// This happens in a work item so we need to avoid a race in terms of having us
|
|
// get disconnected previous to announcing the device to the user-mode component.
|
|
//
|
|
_Session->LockRDPDYNConnectStateChange();
|
|
if (_Session->IsConnected()) {
|
|
|
|
//
|
|
// Dispatch the event to the associated session.
|
|
//
|
|
Status = RDPDYN_DispatchNewDevMgmtEvent(
|
|
portAnnounceEvent,
|
|
_Session->GetSessionId(),
|
|
RDPDREVT_PORTANNOUNCE,
|
|
this
|
|
);
|
|
}
|
|
else {
|
|
delete portAnnounceEvent;
|
|
portAnnounceEvent = NULL;
|
|
}
|
|
_Session->UnlockRDPDYNConnectStateChange();
|
|
}
|
|
|
|
CleanUpAndReturn:
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS DrPrinterPort::AnnouncePrinter(PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG prnAnnounceEventReqSize;
|
|
PRDPDR_PRINTERDEVICE_SUB prnAnnounceEvent;
|
|
|
|
BEGIN_FN("DrPrinterPort::AnnouncePrinter");
|
|
|
|
WCHAR portName[RDPDR_MAXPORTNAMELEN];
|
|
Status = CreatePrinterPort(portName);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
goto CleanUpAndReturn;
|
|
}
|
|
|
|
TRC_ASSERT(wcslen(portName)+1 <= RDPDR_MAXPORTNAMELEN,
|
|
(TB, "Port name too long"));
|
|
|
|
//
|
|
// Allocate the printer device announce buffer.
|
|
//
|
|
Status = CreatePrinterAnnounceEvent(devAnnounceMsg, NULL, 0,
|
|
//L"",
|
|
portName,
|
|
&prnAnnounceEventReqSize);
|
|
ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
|
|
|
|
if (Status != STATUS_BUFFER_TOO_SMALL) {
|
|
goto CleanUpAndReturn;
|
|
}
|
|
|
|
prnAnnounceEvent = (PRDPDR_PRINTERDEVICE_SUB)new(NonPagedPool)
|
|
BYTE[prnAnnounceEventReqSize];
|
|
|
|
if (prnAnnounceEvent == NULL) {
|
|
TRC_ERR((TB, "Unable to allocate prnAnnounceEvent"));
|
|
Status = STATUS_NO_MEMORY;
|
|
goto CleanUpAndReturn;
|
|
}
|
|
|
|
//
|
|
// Create the printer anounce message, but defer assigning a
|
|
// port name until just before we return the announce event
|
|
// back to user mode.
|
|
//
|
|
Status = CreatePrinterAnnounceEvent(devAnnounceMsg, prnAnnounceEvent,
|
|
prnAnnounceEventReqSize,
|
|
//L"",
|
|
portName,
|
|
NULL);
|
|
if (Status != STATUS_SUCCESS) {
|
|
delete prnAnnounceEvent;
|
|
#if DBG
|
|
prnAnnounceEvent = NULL;
|
|
#endif
|
|
goto CleanUpAndReturn;
|
|
}
|
|
|
|
//
|
|
// This happens in a work item so we need to avoid a race in terms of having us
|
|
// get disconnected previous to announcing the device to the user-mode component.
|
|
//
|
|
_Session->LockRDPDYNConnectStateChange();
|
|
if (_Session->IsConnected()) {
|
|
|
|
//
|
|
// Dispatch the event to the associated session.
|
|
//
|
|
Status = RDPDYN_DispatchNewDevMgmtEvent(
|
|
prnAnnounceEvent,
|
|
_Session->GetSessionId(),
|
|
RDPDREVT_PRINTERANNOUNCE,
|
|
this
|
|
);
|
|
}
|
|
else {
|
|
delete prnAnnounceEvent;
|
|
prnAnnounceEvent = NULL;
|
|
}
|
|
|
|
_Session->UnlockRDPDYNConnectStateChange();
|
|
|
|
CleanUpAndReturn:
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS DrPrinterPort::CreatePrinterAnnounceEvent(
|
|
IN PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg,
|
|
IN OUT PRDPDR_PRINTERDEVICE_SUB prnAnnounceEvent,
|
|
IN ULONG prnAnnounceEventSize,
|
|
IN PCWSTR portName,
|
|
OPTIONAL OUT ULONG *prnAnnounceEventReqSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generate a RDPDR_PRINTERDEVICE_SUB event from a client-sent
|
|
RDPDR_DEVICE_ANNOUNCE message.
|
|
|
|
Arguments:
|
|
|
|
devAnnounceMsg - Device announce message received from client.
|
|
prnAnnounceEvent - Buffer for receiving finished printer announce event.
|
|
prnAnnounceEventSize - Size of prnAnnounceEvent buffer.
|
|
portName - Name of local printer port to be associated with
|
|
client-side printing device.
|
|
prnAnnounceEventReqSize - Returned required size of prnAnnounceMsg buffer.
|
|
|
|
Return Value:
|
|
|
|
STATUS_INVALID_BUFFER_SIZE is returned if the prnAnnounceEventSize size is
|
|
too small. STATUS_SUCCESS is returned on success.
|
|
|
|
--*/
|
|
{
|
|
ULONG requiredSize;
|
|
PRDPDR_PRINTERDEVICE_ANNOUNCE pClientPrinterFields;
|
|
ULONG sz;
|
|
|
|
BEGIN_FN("DrPrinterPort::CreatePrinterAnnounceEvent");
|
|
|
|
// Make sure the client-sent device announce message is a printer announce
|
|
// message.
|
|
TRC_ASSERT(devAnnounceMsg->DeviceType == RDPDR_DTYP_PRINT,
|
|
(TB, "Printing device expected"));
|
|
|
|
//
|
|
// Validate device datalengths for some minimum lengths.
|
|
// Maximum lengths are verified by the device manager.
|
|
//
|
|
if (devAnnounceMsg->DeviceDataLength < sizeof(RDPDR_PRINTERDEVICE_ANNOUNCE)) {
|
|
|
|
TRC_ASSERT(FALSE,
|
|
(TB, "Innvalid device announce buf."));
|
|
TRC_ERR((TB, "Invalid device datalength %ld", devAnnounceMsg->DeviceDataLength));
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Get access to the printer-specific fields for the device announce message.
|
|
pClientPrinterFields = (PRDPDR_PRINTERDEVICE_ANNOUNCE)(((PBYTE)devAnnounceMsg) +
|
|
sizeof(RDPDR_DEVICE_ANNOUNCE));
|
|
|
|
//
|
|
// Calculate the number of bytes needed in the output buffer.
|
|
//
|
|
requiredSize = sizeof(RDPDR_PRINTERDEVICE_SUB) +
|
|
pClientPrinterFields->PnPNameLen +
|
|
pClientPrinterFields->DriverLen +
|
|
pClientPrinterFields->PrinterNameLen +
|
|
pClientPrinterFields->CachedFieldsLen;
|
|
|
|
if (prnAnnounceEventSize < requiredSize) {
|
|
if (prnAnnounceEventReqSize != NULL) {
|
|
*prnAnnounceEventReqSize = requiredSize;
|
|
}
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
// Check the integrity of the input buffer using known sizes.
|
|
sz = pClientPrinterFields->PnPNameLen +
|
|
pClientPrinterFields->DriverLen +
|
|
pClientPrinterFields->PrinterNameLen +
|
|
pClientPrinterFields->CachedFieldsLen +
|
|
sizeof(RDPDR_PRINTERDEVICE_ANNOUNCE);
|
|
|
|
//
|
|
// Sanity Check
|
|
//
|
|
|
|
if (devAnnounceMsg->DeviceDataLength != sz) {
|
|
TRC_ASSERT(devAnnounceMsg->DeviceDataLength == sz,
|
|
(TB, "Size integrity questionable in dev announce buf."));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
//
|
|
// The above check alone is not enough.
|
|
// Someone can do an overflow attack
|
|
// Overflow means for example:
|
|
// PnpNameLen : 1,
|
|
// DriverLen: 2,
|
|
// PrinterNameLen:0xfffffffd,
|
|
// CachedFieldsLen:2
|
|
// Combined these will be good, but individually, one of them will cause havoc
|
|
//
|
|
if (pClientPrinterFields->PnPNameLen > devAnnounceMsg->DeviceDataLength ||
|
|
pClientPrinterFields->DriverLen > devAnnounceMsg->DeviceDataLength ||
|
|
pClientPrinterFields->PrinterNameLen > devAnnounceMsg->DeviceDataLength ||
|
|
pClientPrinterFields->CachedFieldsLen > devAnnounceMsg->DeviceDataLength) {
|
|
|
|
TRC_ASSERT(FALSE,
|
|
(TB, "Field lengths and device datalengths mismatched in dev announce buf."));
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
//
|
|
// Add the data to the output buffer.
|
|
//
|
|
|
|
// Port Name.
|
|
TRC_ASSERT(wcslen(portName)+1 <= RDPDR_MAXPORTNAMELEN,
|
|
(TB, "Port name too long"));
|
|
wcscpy(prnAnnounceEvent->portName, portName);
|
|
|
|
// Client Name (computer name).
|
|
TRC_ASSERT(wcslen(_Session->GetClientName())+1 <= RDPDR_MAX_COMPUTER_NAME_LENGTH,
|
|
(TB, "Client name too long"));
|
|
wcscpy(prnAnnounceEvent->clientName, _Session->GetClientName());
|
|
|
|
// Client-received device announce message.
|
|
RtlCopyMemory(&prnAnnounceEvent->deviceFields, devAnnounceMsg,
|
|
sizeof(RDPDR_DEVICE_ANNOUNCE) +
|
|
devAnnounceMsg->DeviceDataLength);
|
|
|
|
// Return the size.
|
|
if (prnAnnounceEventReqSize != NULL) {
|
|
*prnAnnounceEventReqSize = requiredSize;
|
|
}
|
|
|
|
TRC_NRM((TB, "exit CreatePrinterAnnounceEvent."));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS DrPrinterPort::CreatePortAnnounceEvent(
|
|
IN PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg,
|
|
IN OUT PRDPDR_PORTDEVICE_SUB portAnnounceEvent,
|
|
IN ULONG portAnnounceEventSize,
|
|
IN PCWSTR portName,
|
|
OPTIONAL OUT ULONG *portAnnounceEventReqSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generate a PRDPDR_PORTDEVICE_SUB event from a client-sent
|
|
RDPDR_DEVICE_ANNOUNCE message.
|
|
|
|
Arguments:
|
|
|
|
devAnnounceMsg - Device announce message received from
|
|
client.
|
|
portAnnounceEvent - Buffer for receiving finished printer
|
|
announce event.
|
|
portAnnounceEventSize - Size of prnAnnounceEvent buffer.
|
|
portName - Name of local printer port to be associated
|
|
with client-side printing device.
|
|
portAnnounceEventReqSize - Returned required size of prnAnnounceMsg
|
|
buffer.
|
|
|
|
Return Value:
|
|
|
|
STATUS_INVALID_BUFFER_SIZE is returned if the prnAnnounceEventSize size is
|
|
too small. STATUS_SUCCESS is returned on success.
|
|
|
|
--*/
|
|
{
|
|
ULONG requiredSize;
|
|
PRDPDR_PRINTERDEVICE_ANNOUNCE pClientPrinterFields;
|
|
#if DBG
|
|
ULONG sz;
|
|
#endif
|
|
|
|
WCHAR NtDevicePathBuffer[RDPDRMAXNTDEVICENAMEGLEN + 1];
|
|
UNICODE_STRING NtDevicePath;
|
|
NTSTATUS Status;
|
|
|
|
NtDevicePath.MaximumLength = sizeof(NtDevicePathBuffer);
|
|
NtDevicePath.Length = 0;
|
|
NtDevicePath.Buffer = &NtDevicePathBuffer[0];
|
|
|
|
BEGIN_FN("CreatePortAnnounceEvent");
|
|
|
|
//
|
|
// Get the NT device path to this dr device
|
|
//
|
|
|
|
Status = CreateDevicePath(&NtDevicePath);
|
|
TRC_NRM((TB, "Nt Device path: %wZ", &NtDevicePath));
|
|
|
|
if (!NT_ERROR(Status)) {
|
|
|
|
// Make sure the client-sent device announce message is a printer announce
|
|
// message.
|
|
TRC_ASSERT((devAnnounceMsg->DeviceType == RDPDR_DTYP_SERIAL) ||
|
|
(devAnnounceMsg->DeviceType == RDPDR_DTYP_PARALLEL),
|
|
(TB, "Port device expected"));
|
|
|
|
//
|
|
// Make sure device data length is what we expect from the client
|
|
//
|
|
if(!DR_CHECK_DEVICEDATALEN(devAnnounceMsg, RDPDR_PORTDEVICE_SUB)) {
|
|
|
|
TRC_ASSERT(FALSE,
|
|
(TB, "Invalid Device DataLength"));
|
|
|
|
TRC_ERR((TB,"Invalid Device DataLength %d", devAnnounceMsg->DeviceDataLength));
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Calculate the number of bytes needed in the output buffer.
|
|
//
|
|
requiredSize = sizeof(RDPDR_PORTDEVICE_SUB) + devAnnounceMsg->DeviceDataLength;
|
|
if (portAnnounceEventSize < requiredSize) {
|
|
if (portAnnounceEventReqSize != NULL) {
|
|
*portAnnounceEventReqSize = requiredSize;
|
|
}
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
// We shouldn't have any "additional" device-specific data from the client.
|
|
TRC_ASSERT(devAnnounceMsg->DeviceDataLength == 0,
|
|
(TB, "Size integrity questionable in dev announce buf."));
|
|
|
|
//
|
|
// Add the data to the output buffer.
|
|
//
|
|
|
|
// Port Name.
|
|
TRC_ASSERT(wcslen(portName)+1 <= RDPDR_MAXPORTNAMELEN,
|
|
(TB, "Port name too long"));
|
|
wcscpy(portAnnounceEvent->portName, portName);
|
|
|
|
// Device Path.
|
|
NtDevicePath.Buffer[NtDevicePath.Length/sizeof(WCHAR)] = L'\0';
|
|
TRC_ASSERT(wcslen(NtDevicePath.Buffer)+1 <= RDPDRMAXNTDEVICENAMEGLEN,
|
|
(TB, "Device path too long"));
|
|
wcscpy(portAnnounceEvent->devicePath, NtDevicePath.Buffer);
|
|
|
|
// Client-received device announce message.
|
|
RtlCopyMemory(&portAnnounceEvent->deviceFields, devAnnounceMsg,
|
|
sizeof(RDPDR_DEVICE_ANNOUNCE) +
|
|
devAnnounceMsg->DeviceDataLength);
|
|
|
|
// Return the size.
|
|
if (portAnnounceEventReqSize != NULL) {
|
|
*portAnnounceEventReqSize = requiredSize;
|
|
}
|
|
|
|
TRC_NRM((TB, "exit CreatePortAnnounceEvent."));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
VOID DrPrinterPort::Remove()
|
|
{
|
|
PUNICODE_STRING symbolicLinkName;
|
|
PRDPDR_REMOVEDEVICE deviceRemoveEventPtr = NULL;
|
|
|
|
BEGIN_FN("DrPrinterPort::Remove");
|
|
|
|
//
|
|
// Create and dispatch the remove device event.
|
|
//
|
|
deviceRemoveEventPtr = new(NonPagedPool) RDPDR_REMOVEDEVICE;
|
|
|
|
if (deviceRemoveEventPtr != NULL) {
|
|
|
|
//
|
|
// Dispatch it.
|
|
//
|
|
deviceRemoveEventPtr->deviceID = _DeviceId;
|
|
RDPDYN_DispatchNewDevMgmtEvent(
|
|
deviceRemoveEventPtr,
|
|
_Session->GetSessionId(),
|
|
RDPDREVT_REMOVEDEVICE,
|
|
NULL
|
|
);
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Unable to allocate %ld bytes for remove event",
|
|
sizeof(RDPDR_REMOVEDEVICE)));
|
|
}
|
|
}
|
|
|
|
NTSTATUS DrPrinterPort::Create(IN OUT PRX_CONTEXT RxContext)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
BEGIN_FN("DrPrinterPort::Create");
|
|
//
|
|
// Fail Creates when we're already open once
|
|
//
|
|
|
|
DrAcquireSpinLock();
|
|
if (_IsOpen) {
|
|
DrReleaseSpinLock();
|
|
TRC_ALT((TB, "Failing create while already open"));
|
|
return STATUS_SHARING_VIOLATION;
|
|
} else {
|
|
_IsOpen = TRUE;
|
|
DrReleaseSpinLock();
|
|
}
|
|
|
|
Status = DrDevice::Create(RxContext);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DrAcquireSpinLock();
|
|
ASSERT(_IsOpen);
|
|
TRC_NRM((TB, "Marking creatable for failed open"));
|
|
_IsOpen = FALSE;
|
|
DrReleaseSpinLock();
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS DrPrinterPort::QueryVolumeInfo(IN OUT PRX_CONTEXT RxContext)
|
|
{
|
|
NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
|
|
RxCaptureFcb;
|
|
RxCaptureFobx;
|
|
PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
|
|
PMRX_SRV_CALL SrvCall = NetRoot->pSrvCall;
|
|
SmartPtr<DrSession> Session = _Session;
|
|
FS_INFORMATION_CLASS FsInformationClass = RxContext->Info.FsInformationClass;
|
|
|
|
BEGIN_FN("DrPrinterPort:QueryVolumeInfo");
|
|
|
|
//
|
|
// Make sure it's okay to access the Client at this time
|
|
// This is an optimization, we don't need to acquire the spin lock,
|
|
// because it is okay if we're not, we'll just catch it later
|
|
//
|
|
ASSERT(RxContext != NULL);
|
|
ASSERT(RxContext->MajorFunction == IRP_MJ_QUERY_VOLUME_INFORMATION);
|
|
ASSERT(Session != NULL);
|
|
|
|
if (!Session->IsConnected()) {
|
|
TRC_ALT((TB, "Tried to query client device volume information while not "
|
|
"connected. State: %ld", _Session->GetState()));
|
|
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
//
|
|
// Make sure the device is still enabled
|
|
//
|
|
|
|
if (_DeviceStatus != dsAvailable) {
|
|
TRC_ALT((TB, "Tried to query client device volume information while not "
|
|
"available. State: %ld", _DeviceStatus));
|
|
return STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
TRC_DBG((TB, "QueryVolume information class = %x", FsInformationClass));
|
|
|
|
switch (FsInformationClass) {
|
|
case FileFsDeviceInformation:
|
|
{
|
|
PLONG pLengthRemaining = &RxContext->Info.LengthRemaining;
|
|
PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
|
|
|
|
if (sizeof(FILE_FS_DEVICE_INFORMATION) <= *pLengthRemaining) {
|
|
PFILE_FS_DEVICE_INFORMATION UsersBuffer =
|
|
(PFILE_FS_DEVICE_INFORMATION) RxContext->Info.Buffer;
|
|
|
|
UsersBuffer->Characteristics = FILE_REMOTE_DEVICE;
|
|
UsersBuffer->DeviceType = _PortType;
|
|
*pLengthRemaining -= (sizeof(FILE_FS_DEVICE_INFORMATION));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
FILE_FS_DEVICE_INFORMATION UsersBuffer;
|
|
|
|
UsersBuffer.Characteristics = FILE_REMOTE_DEVICE;
|
|
UsersBuffer.DeviceType = _PortType;
|
|
RtlCopyMemory(RxContext->Info.Buffer, &UsersBuffer, *pLengthRemaining);
|
|
*pLengthRemaining = 0;
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
}
|
|
|
|
default:
|
|
TRC_DBG((TB, "Unhandled FsInformationClass=%x", FsInformationClass));
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID DrPrinterPort::NotifyClose()
|
|
{
|
|
BEGIN_FN("DrPrinterPort::NotifyClose");
|
|
|
|
DrDevice::NotifyClose();
|
|
|
|
DrAcquireSpinLock();
|
|
ASSERT(_IsOpen);
|
|
TRC_NRM((TB, "Marking creatable once closed"));
|
|
_IsOpen = FALSE;
|
|
DrReleaseSpinLock();
|
|
}
|