Leaked source code of windows server 2003
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

  1. /*++
  2. Copyright (c) 1998-2000 Microsoft Corporation
  3. Module Name :
  4. prnport.cpp
  5. Abstract:
  6. Printer port Device object handles one redirected printer port
  7. Revision History:
  8. --*/
  9. #include "precomp.hxx"
  10. #define TRC_FILE "prnport"
  11. #include "trc.h"
  12. #include "TSQPublic.h"
  13. extern "C" void RDPDYN_TracePrintAnnounceMsg(
  14. IN OUT PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg,
  15. IN ULONG sessionID,
  16. IN PCWSTR portName,
  17. IN PCWSTR clientName
  18. );
  19. //
  20. // RDPDR.CPP: Configure Devices to send IO packets to client at
  21. // low priority.
  22. //
  23. extern ULONG DeviceLowPrioSendFlags;
  24. //
  25. // RDPDR.CPP: RDPDR.SYS Device Object
  26. //
  27. extern PRDBSS_DEVICE_OBJECT DrDeviceObject;
  28. //
  29. // RDPDr.cpp : The TS Worker Queue pointer
  30. //
  31. extern PVOID RDPDR_TsQueue;
  32. #define LPTNAME "LPT"
  33. #define COMNAME "COM"
  34. #define PRNNAME "PRN"
  35. /////////////////////////////////////////////////////////////////
  36. //
  37. // DrPrinterPort Methods
  38. //
  39. DrPrinterPort::DrPrinterPort(SmartPtr<DrSession> &Session, ULONG DeviceType, ULONG DeviceId,
  40. PUCHAR PreferredDosName) : DrDevice(Session, DeviceType, DeviceId, PreferredDosName)
  41. {
  42. BEGIN_FN("DrPrinterPort::DrPrinterPort");
  43. SetClassName("DrPrinterPort");
  44. _PortNumber = 0;
  45. _SymbolicLinkName.Length = 0;
  46. _SymbolicLinkName.MaximumLength = 0;
  47. _SymbolicLinkName.Buffer = NULL;
  48. _IsOpen = FALSE;
  49. _PortType = FILE_DEVICE_PRINTER;
  50. }
  51. DrPrinterPort::~DrPrinterPort()
  52. {
  53. //
  54. // If the device has a port registered, then unregister the port.
  55. //
  56. if ((_PortNumber != 0) && (_SymbolicLinkName.Buffer != NULL)) {
  57. RDPDRPRT_UnregisterPrinterPortInterface(_PortNumber,
  58. &_SymbolicLinkName);
  59. }
  60. }
  61. NTSTATUS DrPrinterPort::Initialize(PRDPDR_DEVICE_ANNOUNCE DeviceAnnounce, ULONG Length)
  62. {
  63. NTSTATUS status = STATUS_SUCCESS;
  64. DrPrinterPortWorkItem *pItem;
  65. BEGIN_FN("DrPrinterPort::Initialize");
  66. ASSERT(DeviceAnnounce != NULL);
  67. //
  68. // Create a new context for the work item.
  69. //
  70. pItem = new DrPrinterPortWorkItem;
  71. if (pItem == NULL) {
  72. status = STATUS_NO_MEMORY;
  73. goto CLEANUPANDEXIT;
  74. }
  75. pItem->pObj = this;
  76. //
  77. // Copy the device announce message.
  78. //
  79. pItem->deviceAnnounce = (PRDPDR_DEVICE_ANNOUNCE)new(NonPagedPool)
  80. BYTE[sizeof(RDPDR_DEVICE_ANNOUNCE) + Length];
  81. if (pItem->deviceAnnounce == NULL) {
  82. TRC_ERR((TB, "Failed to allocate device announce message."));
  83. status = STATUS_NO_MEMORY;
  84. goto CLEANUPANDEXIT;
  85. }
  86. RtlCopyMemory(pItem->deviceAnnounce, DeviceAnnounce,
  87. sizeof(RDPDR_DEVICE_ANNOUNCE) + Length);
  88. //
  89. // AddRef ourselves so we don't go away while the work item is trying to complete.
  90. //
  91. AddRef();
  92. //
  93. // Use our TS queue worker to queue the workitem
  94. //
  95. status = TSAddWorkItemToQueue( RDPDR_TsQueue, pItem, ProcessWorkItem );
  96. if ( status != STATUS_SUCCESS ) {
  97. TRC_ERR((TB, "RDPDR: FAILED Adding workitem to TS Queue 0x%8x", status));
  98. }
  99. CLEANUPANDEXIT:
  100. if (status != STATUS_SUCCESS) {
  101. if (pItem != NULL) {
  102. if (pItem->deviceAnnounce != NULL) {
  103. delete pItem->deviceAnnounce;
  104. }
  105. delete pItem;
  106. }
  107. }
  108. TRC_NRM((TB, "exit PrnPort::Initialize"));
  109. return status;
  110. }
  111. NTSTATUS
  112. DrPrinterPort::FinishDeferredInitialization(
  113. DrPrinterPortWorkItem *pItem
  114. )
  115. /*++
  116. Routine Description:
  117. FinishDeferredInitialization
  118. Handles deferred initialization of this object in a work item.
  119. Arguments:
  120. pItem - Printer port work item.
  121. Return Value:
  122. STATUS_SUCCESS on success. Otherwise, an error code is returned.
  123. --*/
  124. {
  125. NTSTATUS Status = STATUS_SUCCESS;
  126. BEGIN_FN("DrPrinterPort::FinishDeferredInitialization");
  127. TRC_ASSERT(pItem->deviceAnnounce != NULL,
  128. (TB, "pItem->deviceAnnounce != NULL"));
  129. //
  130. // If printer redirection is enabled at all and the subclass okays it
  131. // then create the announce message.
  132. //
  133. if (ShouldCreatePrinter()) {
  134. TRC_NRM((TB, "Creating printer."));
  135. #if DBG
  136. // Trace information about the printer.
  137. RDPDYN_TracePrintAnnounceMsg(pItem->deviceAnnounce,
  138. _Session->GetSessionId(), L"",
  139. _Session->GetClientName());
  140. #endif
  141. Status = AnnouncePrinter(pItem->deviceAnnounce);
  142. }
  143. //
  144. // Otherwise, check to see if we should only announce a port device.
  145. //
  146. else if (ShouldAnnouncePrintPort()) {
  147. TRC_NRM((TB, "Announcing printer port."));
  148. Status = AnnouncePrintPort(pItem->deviceAnnounce);
  149. } else {
  150. TRC_NRM((TB, "Skipping printing device."));
  151. Status = STATUS_SUCCESS;
  152. }
  153. //
  154. // Release the work item.
  155. //
  156. if (pItem != NULL) {
  157. delete pItem->deviceAnnounce;
  158. delete pItem;
  159. }
  160. //
  161. // Release the ref count on ourselves that was added in the main initialization
  162. // routine.
  163. //
  164. Release();
  165. return Status;
  166. }
  167. NTSTATUS DrPrinterPort::CreateDevicePath(PUNICODE_STRING DevicePath)
  168. /*++
  169. Create NT DeviceName compatible with RDBSS convention
  170. Format is:
  171. \device\rdpdrport\;<DriveLetter>:<sessionid>\ClientName\DosDeviceName
  172. --*/
  173. {
  174. NTSTATUS Status;
  175. UNICODE_STRING DevicePathTail;
  176. BEGIN_FN("DrPrinterPort::CreateDevicePath");
  177. ASSERT(DevicePath != NULL);
  178. DevicePath->Length = 0;
  179. Status = RtlAppendUnicodeToString(DevicePath, RDPDR_PORT_DEVICE_NAME_U);
  180. if (!NT_ERROR(Status)) {
  181. // Add the reference string to the end:
  182. // Format is: \;<DriveLetter>:<sessionid>\clientName\share
  183. DevicePathTail.Length = 0;
  184. DevicePathTail.MaximumLength = DevicePath->MaximumLength - DevicePath->Length;
  185. DevicePathTail.Buffer = DevicePath->Buffer + (DevicePath->Length / sizeof(WCHAR));
  186. CreateReferenceString(&DevicePathTail);
  187. DevicePath->Length += DevicePathTail.Length;
  188. }
  189. TRC_NRM((TB, "DevicePath=%wZ", DevicePath));
  190. return Status;
  191. }
  192. BOOL DrPrinterPort::ShouldAnnouncePrintPort()
  193. {
  194. BEGIN_FN("DrPrinterPort::ShouldAnnouncePrintPort");
  195. return IsDeviceNameValid();
  196. }
  197. BOOL DrPrinterPort::ShouldCreatePrinter()
  198. {
  199. BEGIN_FN("DrPrinterPort::ShouldCreatePrinter");
  200. if(!_Session->DisablePrinterMapping()) {
  201. return IsDeviceNameValid();
  202. }
  203. return FALSE;
  204. }
  205. BOOL DrPrinterPort::ShouldCreatePort()
  206. {
  207. BEGIN_FN("DrPrinterPort::ShouldCreatePort");
  208. if (!_Session->DisablePrinterMapping()) {
  209. return IsDeviceNameValid();
  210. }
  211. return FALSE;
  212. }
  213. BOOL DrPrinterPort::IsDeviceNameValid()
  214. {
  215. BEGIN_FN("DrPrinterPort::IsDeviceNameValid");
  216. BOOL fRet = FALSE;
  217. PUCHAR PreferredDosName = _PreferredDosName;
  218. char* portName = NULL;
  219. //
  220. // Our device name is valid only if
  221. // the first 3 chars contain "LPT or "COM" or PRN"
  222. // and the rest are digits.
  223. // We will do case-sensitive compare.
  224. //
  225. switch(_DeviceType) {
  226. case RDPDR_DTYP_SERIAL:
  227. portName = COMNAME;
  228. break;
  229. case RDPDR_DTYP_PARALLEL:
  230. portName = LPTNAME;
  231. break;
  232. case RDPDR_DTYP_PRINT:
  233. portName = PRNNAME;
  234. break;
  235. default:
  236. break;
  237. }
  238. if (portName != NULL) {
  239. DWORD numChars = strlen(portName);
  240. //
  241. // ASSERT that we got atleast 3 chars for devicename
  242. //
  243. ASSERT(strlen((char*)PreferredDosName) >= numChars);
  244. if(!strncmp((char*)PreferredDosName, portName, numChars)) {
  245. fRet = TRUE;
  246. //
  247. // portname matches, check for digits.
  248. //
  249. PreferredDosName += numChars;
  250. while(PreferredDosName && *PreferredDosName) {
  251. if(!isdigit(*PreferredDosName)) {
  252. fRet = FALSE;
  253. break;
  254. }
  255. PreferredDosName++;
  256. }
  257. }
  258. }
  259. //
  260. // This assert should never fire for port redirection
  261. //
  262. ASSERT(fRet);
  263. return fRet;
  264. }
  265. NTSTATUS
  266. DrPrinterPort::Write(
  267. IN OUT PRX_CONTEXT RxContext,
  268. IN BOOL LowPrioSend
  269. )
  270. /*++
  271. Routine Description:
  272. Override the 'Write' method. This needs to go to the client at low priority
  273. to prevent us from filling the entire pipe on a slow link with print data.
  274. Arguments:
  275. Return Value:
  276. STATUS_SUCCESS on success. Otherwise, an error code is returned.
  277. --*/
  278. {
  279. return DrDevice::Write(
  280. RxContext,
  281. DeviceLowPrioSendFlags & DEVICE_LOWPRIOSEND_PRINTERS
  282. );
  283. }
  284. VOID
  285. DrPrinterPort::ProcessWorkItem(
  286. IN PDEVICE_OBJECT DeviceObject,
  287. IN PVOID context
  288. )
  289. /*++
  290. Routine Description:
  291. ProcessWorkItem
  292. Arguments:
  293. deviceObject - Associated device object.
  294. context - Work item context.
  295. Return Value:
  296. STATUS_SUCCESS on success. Otherwise, an error code is returned.
  297. --*/
  298. {
  299. DrPrinterPortWorkItem* pItem = (DrPrinterPortWorkItem*)context;
  300. pItem->pObj->FinishDeferredInitialization(pItem);
  301. }
  302. NTSTATUS DrPrinterPort::CreatePrinterPort(PWCHAR portName)
  303. {
  304. NTSTATUS status;
  305. WCHAR ntDevicePathBuffer[RDPDRMAXREFSTRINGLEN];
  306. UNICODE_STRING ntDevicePath = {0, sizeof(ntDevicePathBuffer),
  307. ntDevicePathBuffer};
  308. BEGIN_FN("DrPrinterPort::CreatePrinterPort");
  309. CreateReferenceString(&ntDevicePath);
  310. status = RDPDRPRT_RegisterPrinterPortInterface(_Session->GetClientName(),
  311. (LPSTR)_PreferredDosName, &ntDevicePath, portName, &_SymbolicLinkName,
  312. &_PortNumber);
  313. if (status != STATUS_SUCCESS) {
  314. _PortNumber = 0;
  315. }
  316. return status;
  317. }
  318. NTSTATUS DrPrinterPort::AnnouncePrintPort(PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg)
  319. {
  320. NTSTATUS Status;
  321. ULONG portAnnounceEventReqSize;
  322. PRDPDR_PORTDEVICE_SUB portAnnounceEvent;
  323. BEGIN_FN("DrPrinterPort::AnnouncePrintPort");
  324. WCHAR portName[RDPDR_MAXPORTNAMELEN];
  325. Status = CreatePrinterPort(portName);
  326. if (Status != STATUS_SUCCESS) {
  327. goto CleanUpAndReturn;
  328. }
  329. TRC_ASSERT(wcslen(portName)+1 <= RDPDR_MAXPORTNAMELEN,
  330. (TB, "Port name too long"));
  331. //
  332. // Allocate the port device announce buffer.
  333. //
  334. Status = CreatePortAnnounceEvent(
  335. devAnnounceMsg,
  336. NULL,
  337. 0,
  338. //L"",
  339. portName,
  340. &portAnnounceEventReqSize
  341. );
  342. ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
  343. if( Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_SUCCESS ) {
  344. portAnnounceEvent = (PRDPDR_PORTDEVICE_SUB)new(NonPagedPool)
  345. BYTE[portAnnounceEventReqSize];
  346. if (portAnnounceEvent == NULL) {
  347. TRC_ERR((TB, "Unable to allocate portAnnounceEvent"));
  348. Status = STATUS_NO_MEMORY;
  349. goto CleanUpAndReturn;
  350. }
  351. //
  352. // Create the port anounce message.
  353. //
  354. Status = CreatePortAnnounceEvent(
  355. devAnnounceMsg,
  356. portAnnounceEvent,
  357. portAnnounceEventReqSize,
  358. //L"",
  359. portName,
  360. &portAnnounceEventReqSize
  361. );
  362. if (Status != STATUS_SUCCESS) {
  363. delete portAnnounceEvent;
  364. #if DBG
  365. portAnnounceEvent = NULL;
  366. #endif
  367. goto CleanUpAndReturn;
  368. }
  369. // device is a printer port.
  370. portAnnounceEvent->deviceFields.DeviceType = RDPDR_DRYP_PRINTPORT;
  371. //
  372. // This happens in a work item so we need to avoid a race in terms of having us
  373. // get disconnected previous to announcing the device to the user-mode component.
  374. //
  375. _Session->LockRDPDYNConnectStateChange();
  376. if (_Session->IsConnected()) {
  377. //
  378. // Dispatch the event to the associated session.
  379. //
  380. Status = RDPDYN_DispatchNewDevMgmtEvent(
  381. portAnnounceEvent,
  382. _Session->GetSessionId(),
  383. RDPDREVT_PORTANNOUNCE,
  384. this
  385. );
  386. }
  387. else {
  388. delete portAnnounceEvent;
  389. portAnnounceEvent = NULL;
  390. }
  391. _Session->UnlockRDPDYNConnectStateChange();
  392. }
  393. CleanUpAndReturn:
  394. return Status;
  395. }
  396. NTSTATUS DrPrinterPort::AnnouncePrinter(PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg)
  397. {
  398. NTSTATUS Status;
  399. ULONG prnAnnounceEventReqSize;
  400. PRDPDR_PRINTERDEVICE_SUB prnAnnounceEvent;
  401. BEGIN_FN("DrPrinterPort::AnnouncePrinter");
  402. WCHAR portName[RDPDR_MAXPORTNAMELEN];
  403. Status = CreatePrinterPort(portName);
  404. if (Status != STATUS_SUCCESS) {
  405. goto CleanUpAndReturn;
  406. }
  407. TRC_ASSERT(wcslen(portName)+1 <= RDPDR_MAXPORTNAMELEN,
  408. (TB, "Port name too long"));
  409. //
  410. // Allocate the printer device announce buffer.
  411. //
  412. Status = CreatePrinterAnnounceEvent(devAnnounceMsg, NULL, 0,
  413. //L"",
  414. portName,
  415. &prnAnnounceEventReqSize);
  416. ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
  417. if (Status != STATUS_BUFFER_TOO_SMALL) {
  418. goto CleanUpAndReturn;
  419. }
  420. prnAnnounceEvent = (PRDPDR_PRINTERDEVICE_SUB)new(NonPagedPool)
  421. BYTE[prnAnnounceEventReqSize];
  422. if (prnAnnounceEvent == NULL) {
  423. TRC_ERR((TB, "Unable to allocate prnAnnounceEvent"));
  424. Status = STATUS_NO_MEMORY;
  425. goto CleanUpAndReturn;
  426. }
  427. //
  428. // Create the printer anounce message, but defer assigning a
  429. // port name until just before we return the announce event
  430. // back to user mode.
  431. //
  432. Status = CreatePrinterAnnounceEvent(devAnnounceMsg, prnAnnounceEvent,
  433. prnAnnounceEventReqSize,
  434. //L"",
  435. portName,
  436. NULL);
  437. if (Status != STATUS_SUCCESS) {
  438. delete prnAnnounceEvent;
  439. #if DBG
  440. prnAnnounceEvent = NULL;
  441. #endif
  442. goto CleanUpAndReturn;
  443. }
  444. //
  445. // This happens in a work item so we need to avoid a race in terms of having us
  446. // get disconnected previous to announcing the device to the user-mode component.
  447. //
  448. _Session->LockRDPDYNConnectStateChange();
  449. if (_Session->IsConnected()) {
  450. //
  451. // Dispatch the event to the associated session.
  452. //
  453. Status = RDPDYN_DispatchNewDevMgmtEvent(
  454. prnAnnounceEvent,
  455. _Session->GetSessionId(),
  456. RDPDREVT_PRINTERANNOUNCE,
  457. this
  458. );
  459. }
  460. else {
  461. delete prnAnnounceEvent;
  462. prnAnnounceEvent = NULL;
  463. }
  464. _Session->UnlockRDPDYNConnectStateChange();
  465. CleanUpAndReturn:
  466. return Status;
  467. }
  468. NTSTATUS DrPrinterPort::CreatePrinterAnnounceEvent(
  469. IN PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg,
  470. IN OUT PRDPDR_PRINTERDEVICE_SUB prnAnnounceEvent,
  471. IN ULONG prnAnnounceEventSize,
  472. IN PCWSTR portName,
  473. OPTIONAL OUT ULONG *prnAnnounceEventReqSize
  474. )
  475. /*++
  476. Routine Description:
  477. Generate a RDPDR_PRINTERDEVICE_SUB event from a client-sent
  478. RDPDR_DEVICE_ANNOUNCE message.
  479. Arguments:
  480. devAnnounceMsg - Device announce message received from client.
  481. prnAnnounceEvent - Buffer for receiving finished printer announce event.
  482. prnAnnounceEventSize - Size of prnAnnounceEvent buffer.
  483. portName - Name of local printer port to be associated with
  484. client-side printing device.
  485. prnAnnounceEventReqSize - Returned required size of prnAnnounceMsg buffer.
  486. Return Value:
  487. STATUS_INVALID_BUFFER_SIZE is returned if the prnAnnounceEventSize size is
  488. too small. STATUS_SUCCESS is returned on success.
  489. --*/
  490. {
  491. ULONG requiredSize;
  492. PRDPDR_PRINTERDEVICE_ANNOUNCE pClientPrinterFields;
  493. ULONG sz;
  494. BEGIN_FN("DrPrinterPort::CreatePrinterAnnounceEvent");
  495. // Make sure the client-sent device announce message is a printer announce
  496. // message.
  497. TRC_ASSERT(devAnnounceMsg->DeviceType == RDPDR_DTYP_PRINT,
  498. (TB, "Printing device expected"));
  499. //
  500. // Validate device datalengths for some minimum lengths.
  501. // Maximum lengths are verified by the device manager.
  502. //
  503. if (devAnnounceMsg->DeviceDataLength < sizeof(RDPDR_PRINTERDEVICE_ANNOUNCE)) {
  504. TRC_ASSERT(FALSE,
  505. (TB, "Innvalid device announce buf."));
  506. TRC_ERR((TB, "Invalid device datalength %ld", devAnnounceMsg->DeviceDataLength));
  507. return STATUS_INVALID_PARAMETER;
  508. }
  509. // Get access to the printer-specific fields for the device announce message.
  510. pClientPrinterFields = (PRDPDR_PRINTERDEVICE_ANNOUNCE)(((PBYTE)devAnnounceMsg) +
  511. sizeof(RDPDR_DEVICE_ANNOUNCE));
  512. //
  513. // Calculate the number of bytes needed in the output buffer.
  514. //
  515. requiredSize = sizeof(RDPDR_PRINTERDEVICE_SUB) +
  516. pClientPrinterFields->PnPNameLen +
  517. pClientPrinterFields->DriverLen +
  518. pClientPrinterFields->PrinterNameLen +
  519. pClientPrinterFields->CachedFieldsLen;
  520. if (prnAnnounceEventSize < requiredSize) {
  521. if (prnAnnounceEventReqSize != NULL) {
  522. *prnAnnounceEventReqSize = requiredSize;
  523. }
  524. return STATUS_BUFFER_TOO_SMALL;
  525. }
  526. // Check the integrity of the input buffer using known sizes.
  527. sz = pClientPrinterFields->PnPNameLen +
  528. pClientPrinterFields->DriverLen +
  529. pClientPrinterFields->PrinterNameLen +
  530. pClientPrinterFields->CachedFieldsLen +
  531. sizeof(RDPDR_PRINTERDEVICE_ANNOUNCE);
  532. //
  533. // Sanity Check
  534. //
  535. if (devAnnounceMsg->DeviceDataLength != sz) {
  536. TRC_ASSERT(devAnnounceMsg->DeviceDataLength == sz,
  537. (TB, "Size integrity questionable in dev announce buf."));
  538. return STATUS_INVALID_PARAMETER;
  539. }
  540. //
  541. // The above check alone is not enough.
  542. // Someone can do an overflow attack
  543. // Overflow means for example:
  544. // PnpNameLen : 1,
  545. // DriverLen: 2,
  546. // PrinterNameLen:0xfffffffd,
  547. // CachedFieldsLen:2
  548. // Combined these will be good, but individually, one of them will cause havoc
  549. //
  550. if (pClientPrinterFields->PnPNameLen > devAnnounceMsg->DeviceDataLength ||
  551. pClientPrinterFields->DriverLen > devAnnounceMsg->DeviceDataLength ||
  552. pClientPrinterFields->PrinterNameLen > devAnnounceMsg->DeviceDataLength ||
  553. pClientPrinterFields->CachedFieldsLen > devAnnounceMsg->DeviceDataLength) {
  554. TRC_ASSERT(FALSE,
  555. (TB, "Field lengths and device datalengths mismatched in dev announce buf."));
  556. return STATUS_INVALID_PARAMETER;
  557. }
  558. //
  559. // Add the data to the output buffer.
  560. //
  561. // Port Name.
  562. TRC_ASSERT(wcslen(portName)+1 <= RDPDR_MAXPORTNAMELEN,
  563. (TB, "Port name too long"));
  564. wcscpy(prnAnnounceEvent->portName, portName);
  565. // Client Name (computer name).
  566. TRC_ASSERT(wcslen(_Session->GetClientName())+1 <= RDPDR_MAX_COMPUTER_NAME_LENGTH,
  567. (TB, "Client name too long"));
  568. wcscpy(prnAnnounceEvent->clientName, _Session->GetClientName());
  569. // Client-received device announce message.
  570. RtlCopyMemory(&prnAnnounceEvent->deviceFields, devAnnounceMsg,
  571. sizeof(RDPDR_DEVICE_ANNOUNCE) +
  572. devAnnounceMsg->DeviceDataLength);
  573. // Return the size.
  574. if (prnAnnounceEventReqSize != NULL) {
  575. *prnAnnounceEventReqSize = requiredSize;
  576. }
  577. TRC_NRM((TB, "exit CreatePrinterAnnounceEvent."));
  578. return STATUS_SUCCESS;
  579. }
  580. NTSTATUS DrPrinterPort::CreatePortAnnounceEvent(
  581. IN PRDPDR_DEVICE_ANNOUNCE devAnnounceMsg,
  582. IN OUT PRDPDR_PORTDEVICE_SUB portAnnounceEvent,
  583. IN ULONG portAnnounceEventSize,
  584. IN PCWSTR portName,
  585. OPTIONAL OUT ULONG *portAnnounceEventReqSize
  586. )
  587. /*++
  588. Routine Description:
  589. Generate a PRDPDR_PORTDEVICE_SUB event from a client-sent
  590. RDPDR_DEVICE_ANNOUNCE message.
  591. Arguments:
  592. devAnnounceMsg - Device announce message received from
  593. client.
  594. portAnnounceEvent - Buffer for receiving finished printer
  595. announce event.
  596. portAnnounceEventSize - Size of prnAnnounceEvent buffer.
  597. portName - Name of local printer port to be associated
  598. with client-side printing device.
  599. portAnnounceEventReqSize - Returned required size of prnAnnounceMsg
  600. buffer.
  601. Return Value:
  602. STATUS_INVALID_BUFFER_SIZE is returned if the prnAnnounceEventSize size is
  603. too small. STATUS_SUCCESS is returned on success.
  604. --*/
  605. {
  606. ULONG requiredSize;
  607. PRDPDR_PRINTERDEVICE_ANNOUNCE pClientPrinterFields;
  608. #if DBG
  609. ULONG sz;
  610. #endif
  611. WCHAR NtDevicePathBuffer[RDPDRMAXNTDEVICENAMEGLEN + 1];
  612. UNICODE_STRING NtDevicePath;
  613. NTSTATUS Status;
  614. NtDevicePath.MaximumLength = sizeof(NtDevicePathBuffer);
  615. NtDevicePath.Length = 0;
  616. NtDevicePath.Buffer = &NtDevicePathBuffer[0];
  617. BEGIN_FN("CreatePortAnnounceEvent");
  618. //
  619. // Get the NT device path to this dr device
  620. //
  621. Status = CreateDevicePath(&NtDevicePath);
  622. TRC_NRM((TB, "Nt Device path: %wZ", &NtDevicePath));
  623. if (!NT_ERROR(Status)) {
  624. // Make sure the client-sent device announce message is a printer announce
  625. // message.
  626. TRC_ASSERT((devAnnounceMsg->DeviceType == RDPDR_DTYP_SERIAL) ||
  627. (devAnnounceMsg->DeviceType == RDPDR_DTYP_PARALLEL),
  628. (TB, "Port device expected"));
  629. //
  630. // Make sure device data length is what we expect from the client
  631. //
  632. if(!DR_CHECK_DEVICEDATALEN(devAnnounceMsg, RDPDR_PORTDEVICE_SUB)) {
  633. TRC_ASSERT(FALSE,
  634. (TB, "Invalid Device DataLength"));
  635. TRC_ERR((TB,"Invalid Device DataLength %d", devAnnounceMsg->DeviceDataLength));
  636. return STATUS_INVALID_PARAMETER;
  637. }
  638. //
  639. // Calculate the number of bytes needed in the output buffer.
  640. //
  641. requiredSize = sizeof(RDPDR_PORTDEVICE_SUB) + devAnnounceMsg->DeviceDataLength;
  642. if (portAnnounceEventSize < requiredSize) {
  643. if (portAnnounceEventReqSize != NULL) {
  644. *portAnnounceEventReqSize = requiredSize;
  645. }
  646. return STATUS_BUFFER_TOO_SMALL;
  647. }
  648. // We shouldn't have any "additional" device-specific data from the client.
  649. TRC_ASSERT(devAnnounceMsg->DeviceDataLength == 0,
  650. (TB, "Size integrity questionable in dev announce buf."));
  651. //
  652. // Add the data to the output buffer.
  653. //
  654. // Port Name.
  655. TRC_ASSERT(wcslen(portName)+1 <= RDPDR_MAXPORTNAMELEN,
  656. (TB, "Port name too long"));
  657. wcscpy(portAnnounceEvent->portName, portName);
  658. // Device Path.
  659. NtDevicePath.Buffer[NtDevicePath.Length/sizeof(WCHAR)] = L'\0';
  660. TRC_ASSERT(wcslen(NtDevicePath.Buffer)+1 <= RDPDRMAXNTDEVICENAMEGLEN,
  661. (TB, "Device path too long"));
  662. wcscpy(portAnnounceEvent->devicePath, NtDevicePath.Buffer);
  663. // Client-received device announce message.
  664. RtlCopyMemory(&portAnnounceEvent->deviceFields, devAnnounceMsg,
  665. sizeof(RDPDR_DEVICE_ANNOUNCE) +
  666. devAnnounceMsg->DeviceDataLength);
  667. // Return the size.
  668. if (portAnnounceEventReqSize != NULL) {
  669. *portAnnounceEventReqSize = requiredSize;
  670. }
  671. TRC_NRM((TB, "exit CreatePortAnnounceEvent."));
  672. return STATUS_SUCCESS;
  673. }
  674. else {
  675. return Status;
  676. }
  677. }
  678. VOID DrPrinterPort::Remove()
  679. {
  680. PUNICODE_STRING symbolicLinkName;
  681. PRDPDR_REMOVEDEVICE deviceRemoveEventPtr = NULL;
  682. BEGIN_FN("DrPrinterPort::Remove");
  683. //
  684. // Create and dispatch the remove device event.
  685. //
  686. deviceRemoveEventPtr = new(NonPagedPool) RDPDR_REMOVEDEVICE;
  687. if (deviceRemoveEventPtr != NULL) {
  688. //
  689. // Dispatch it.
  690. //
  691. deviceRemoveEventPtr->deviceID = _DeviceId;
  692. RDPDYN_DispatchNewDevMgmtEvent(
  693. deviceRemoveEventPtr,
  694. _Session->GetSessionId(),
  695. RDPDREVT_REMOVEDEVICE,
  696. NULL
  697. );
  698. }
  699. else {
  700. TRC_ERR((TB, "Unable to allocate %ld bytes for remove event",
  701. sizeof(RDPDR_REMOVEDEVICE)));
  702. }
  703. }
  704. NTSTATUS DrPrinterPort::Create(IN OUT PRX_CONTEXT RxContext)
  705. {
  706. NTSTATUS Status;
  707. BEGIN_FN("DrPrinterPort::Create");
  708. //
  709. // Fail Creates when we're already open once
  710. //
  711. DrAcquireSpinLock();
  712. if (_IsOpen) {
  713. DrReleaseSpinLock();
  714. TRC_ALT((TB, "Failing create while already open"));
  715. return STATUS_SHARING_VIOLATION;
  716. } else {
  717. _IsOpen = TRUE;
  718. DrReleaseSpinLock();
  719. }
  720. Status = DrDevice::Create(RxContext);
  721. if (!NT_SUCCESS(Status)) {
  722. DrAcquireSpinLock();
  723. ASSERT(_IsOpen);
  724. TRC_NRM((TB, "Marking creatable for failed open"));
  725. _IsOpen = FALSE;
  726. DrReleaseSpinLock();
  727. }
  728. return Status;
  729. }
  730. NTSTATUS DrPrinterPort::QueryVolumeInfo(IN OUT PRX_CONTEXT RxContext)
  731. {
  732. NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
  733. RxCaptureFcb;
  734. RxCaptureFobx;
  735. PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
  736. PMRX_SRV_CALL SrvCall = NetRoot->pSrvCall;
  737. SmartPtr<DrSession> Session = _Session;
  738. FS_INFORMATION_CLASS FsInformationClass = RxContext->Info.FsInformationClass;
  739. BEGIN_FN("DrPrinterPort:QueryVolumeInfo");
  740. //
  741. // Make sure it's okay to access the Client at this time
  742. // This is an optimization, we don't need to acquire the spin lock,
  743. // because it is okay if we're not, we'll just catch it later
  744. //
  745. ASSERT(RxContext != NULL);
  746. ASSERT(RxContext->MajorFunction == IRP_MJ_QUERY_VOLUME_INFORMATION);
  747. ASSERT(Session != NULL);
  748. if (!Session->IsConnected()) {
  749. TRC_ALT((TB, "Tried to query client device volume information while not "
  750. "connected. State: %ld", _Session->GetState()));
  751. return STATUS_DEVICE_NOT_CONNECTED;
  752. }
  753. //
  754. // Make sure the device is still enabled
  755. //
  756. if (_DeviceStatus != dsAvailable) {
  757. TRC_ALT((TB, "Tried to query client device volume information while not "
  758. "available. State: %ld", _DeviceStatus));
  759. return STATUS_DEVICE_NOT_CONNECTED;
  760. }
  761. TRC_DBG((TB, "QueryVolume information class = %x", FsInformationClass));
  762. switch (FsInformationClass) {
  763. case FileFsDeviceInformation:
  764. {
  765. PLONG pLengthRemaining = &RxContext->Info.LengthRemaining;
  766. PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
  767. if (sizeof(FILE_FS_DEVICE_INFORMATION) <= *pLengthRemaining) {
  768. PFILE_FS_DEVICE_INFORMATION UsersBuffer =
  769. (PFILE_FS_DEVICE_INFORMATION) RxContext->Info.Buffer;
  770. UsersBuffer->Characteristics = FILE_REMOTE_DEVICE;
  771. UsersBuffer->DeviceType = _PortType;
  772. *pLengthRemaining -= (sizeof(FILE_FS_DEVICE_INFORMATION));
  773. return STATUS_SUCCESS;
  774. }
  775. else {
  776. FILE_FS_DEVICE_INFORMATION UsersBuffer;
  777. UsersBuffer.Characteristics = FILE_REMOTE_DEVICE;
  778. UsersBuffer.DeviceType = _PortType;
  779. RtlCopyMemory(RxContext->Info.Buffer, &UsersBuffer, *pLengthRemaining);
  780. *pLengthRemaining = 0;
  781. return STATUS_BUFFER_OVERFLOW;
  782. }
  783. }
  784. default:
  785. TRC_DBG((TB, "Unhandled FsInformationClass=%x", FsInformationClass));
  786. return STATUS_NOT_IMPLEMENTED;
  787. }
  788. return Status;
  789. }
  790. VOID DrPrinterPort::NotifyClose()
  791. {
  792. BEGIN_FN("DrPrinterPort::NotifyClose");
  793. DrDevice::NotifyClose();
  794. DrAcquireSpinLock();
  795. ASSERT(_IsOpen);
  796. TRC_NRM((TB, "Marking creatable once closed"));
  797. _IsOpen = FALSE;
  798. DrReleaseSpinLock();
  799. }