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.

1309 lines
37 KiB

  1. /*++
  2. Copyright (c) 1998-2000 Microsoft Corporation
  3. Module Name :
  4. rdpdrprt.c
  5. Abstract:
  6. Manage dynamic printer port allocation for the RDP device redirection
  7. kernel mode component, rdpdr.sys.
  8. Port number 0 is reserved and is never allocated.
  9. Author:
  10. tadb
  11. Revision History:
  12. --*/
  13. #include "precomp.hxx"
  14. #define TRC_FILE "rdpdrprt"
  15. #include "trc.h"
  16. #define DRIVER
  17. #include <stdio.h>
  18. #ifdef __cplusplus
  19. extern "C" {
  20. #endif
  21. #include "cfg.h"
  22. #include "pnp.h"
  23. ////////////////////////////////////////////////////////////////////////
  24. //
  25. // Function Prototypes
  26. //
  27. // Generate a port name from a port number.
  28. BOOL GeneratePortName(
  29. IN ULONG portNumber,
  30. OUT PWSTR portName
  31. );
  32. // Finds the next free port, following the last port checked, in the port bit
  33. // array.
  34. NTSTATUS FindNextFreePortInPortArray(
  35. IN ULONG lastPortChecked,
  36. OUT ULONG *nextFreePort
  37. );
  38. // Increase the size of the port bit array to hold at least one new
  39. // port.
  40. NTSTATUS IncreasePortBitArraySize();
  41. // Format a port description.
  42. void GeneratePortDescription(
  43. IN PCSTR dosPortName,
  44. IN PCWSTR clientName,
  45. IN PWSTR description
  46. );
  47. // Return TRUE if the port's device interface is usable, from dynamon's
  48. // perspective.
  49. BOOL PortIsAvailableForUse(
  50. IN HANDLE regHandle
  51. );
  52. // Set the port description string to disabled.
  53. NTSTATUS SetPortDescrToDisabled(
  54. IN PUNICODE_STRING symbolicLinkName
  55. );
  56. // Clean up ports registered in a previous boot.
  57. NTSTATUS CleanUpExistingPorts();
  58. // Allocate a port.
  59. NTSTATUS AllocatePrinterPort(
  60. IN ULONG portArrayIndex,
  61. OUT PWSTR portName,
  62. OUT ULONG *portNumber,
  63. OUT HANDLE *regHandle,
  64. OUT PUNICODE_STRING symbolicLinkName
  65. );
  66. #ifdef __cplusplus
  67. } // extern "C"
  68. #endif
  69. ////////////////////////////////////////////////////////////////////////
  70. //
  71. // Defines and Macros
  72. //
  73. #define BITSINBYTE 8
  74. #define BITSINULONG (sizeof(ULONG) * BITSINBYTE)
  75. // Printer port description length (in characters).
  76. #define RDPDRPRT_PORTDESCRLENGTH \
  77. MAX_COMPUTERNAME_LENGTH + 1 + 2 + PREFERRED_DOS_NAME_SIZE + 1
  78. // Computer Name ':' ' ' Dos Name Terminator
  79. // Initial size of the port bit array.
  80. #define RDPDRPRT_INITIALPORTCOUNT 64
  81. //restore this#define RDPDRPRT_INITIALPORTCOUNT 256
  82. // Base name for TermSrv printer ports
  83. #define RDPDRPRT_BASEPORTNAME L"TS"
  84. // Device interface port number registry value name.
  85. #define PORT_NUM_VALUE_NAME L"Port Number"
  86. // Device interface write size registry value name.
  87. #define PORT_WRITESIZE_VALUE_NAME L"MaxBufferSize"
  88. // Device interface port base name registry value name.
  89. #define PORT_BASE_VALUE_NAME L"Base Name"
  90. // Device interface port description registry value name.
  91. #define PORT_DESCR_VALUE_NAME L"Port Description"
  92. // Device interface port recyclable flag registry value name.
  93. #define RECYCLABLE_FLAG_VALUE_NAME L"recyclable"
  94. // We will use separate arrays for the printer port and printer queues to avoid
  95. // a race condition between creating a printer queue and listening on the printer port
  96. #define PRINTER_PORT_ARRAY_ID 1
  97. ////////////////////////////////////////////////////////////////////////
  98. //
  99. // External Globals
  100. //
  101. // The Physical Device Object that terminates our DO stack (defined in rdpdyn.c).
  102. extern PDEVICE_OBJECT RDPDYN_PDO;
  103. // USBMON Port Write Size. Need to keep it under 64k for 16-bit clients ...
  104. // otherwise, the go off the end of a segment. (defined in rdpdr.cpp)
  105. extern ULONG PrintPortWriteSize;
  106. ////////////////////////////////////////////////////////////////////////
  107. //
  108. // Globals for this Module
  109. //
  110. //
  111. // For Tracking Allocated Ports. A cleared bit indicates a free port.
  112. //
  113. ULONG *PortBitArray = NULL;
  114. ULONG PortBitArraySizeInBytes = 0;
  115. //
  116. // Is this module initialized?
  117. //
  118. BOOL RDPDRPRT_Initialized = FALSE;
  119. // Description for disabled port. Note: We can localize this later.
  120. WCHAR DisabledPortDescription[RDPDRPRT_PORTDESCRLENGTH] = L"Inactive TS Port";
  121. ULONG DisabledPortDescrSize = 0;
  122. // This is the GUID we use to identify a dynamic printer port to
  123. // dynamon.
  124. // {28D78FAD-5A12-11d1-AE5B-0000F803A8C2}
  125. const GUID DYNPRINT_GUID =
  126. { 0x28d78fad, 0x5a12, 0x11d1, { 0xae, 0x5b, 0x0, 0x0, 0xf8, 0x3, 0xa8, 0xc2 } };
  127. //
  128. // Port Allocation Lock
  129. //
  130. KSPIN_LOCK PortAllocLock;
  131. KIRQL OldIRQL;
  132. #define RDPDRPRT_LOCK() \
  133. KeAcquireSpinLock(&PortAllocLock, &OldIRQL)
  134. #define RDPDRPRT_UNLOCK() \
  135. KeReleaseSpinLock(&PortAllocLock, OldIRQL)
  136. NTSTATUS
  137. RDPDRPRT_Initialize(
  138. )
  139. /*++
  140. Routine Description:
  141. Initialize this module.
  142. Arguments:
  143. Return Value:
  144. STATUS_SUCCESS if successful,
  145. STATUS_UNSUCCESSFUL otherwise
  146. --*/
  147. {
  148. NTSTATUS status = STATUS_SUCCESS;
  149. BEGIN_FN("RDPDRPRT_Initialize");
  150. //
  151. // Initialize the lock for port allocations.
  152. //
  153. KeInitializeSpinLock(&PortAllocLock);
  154. //
  155. // Compute the size of the disabled port description string.
  156. //
  157. DisabledPortDescrSize = (wcslen(DisabledPortDescription)+1)*sizeof(WCHAR);
  158. //
  159. // Allocate and initialize the allocated port bits array.
  160. //
  161. // Calculate initial size.
  162. PortBitArraySizeInBytes = (RDPDRPRT_INITIALPORTCOUNT / BITSINULONG)
  163. * sizeof(ULONG);
  164. if (RDPDRPRT_INITIALPORTCOUNT % BITSINULONG) {
  165. PortBitArraySizeInBytes += sizeof(ULONG);
  166. }
  167. // Allocate.
  168. PortBitArray = (ULONG *)new(NonPagedPool) BYTE[PortBitArraySizeInBytes];
  169. if (PortBitArray == NULL) {
  170. TRC_ERR((TB, "Error allocating %ld bytes for port array",
  171. PortBitArraySizeInBytes));
  172. PortBitArraySizeInBytes = 0;
  173. status = STATUS_INSUFFICIENT_RESOURCES;
  174. }
  175. else {
  176. // Initially, all the ports are free.
  177. RtlZeroMemory(PortBitArray, PortBitArraySizeInBytes);
  178. }
  179. //
  180. // Clean up ports allocated in a previous boot.
  181. //
  182. if (status == STATUS_SUCCESS) {
  183. //
  184. // Cleanup status not critical for init
  185. //
  186. CleanUpExistingPorts();
  187. RDPDRPRT_Initialized = TRUE;
  188. }
  189. return status;
  190. }
  191. void
  192. RDPDRPRT_Shutdown()
  193. /*++
  194. Routine Description:
  195. Shut down this module.
  196. Arguments:
  197. Return Value:
  198. --*/
  199. {
  200. BEGIN_FN("RDPDRPRT_Shutdown");
  201. if (!RDPDRPRT_Initialized) {
  202. TRC_ERR((TB,
  203. "RDPDRPRT_Shutdown: RDPDRPRT is not initialized. Exiting."));
  204. return;
  205. }
  206. //
  207. // Release the allocated port bits array.
  208. //
  209. RDPDRPRT_LOCK();
  210. if (PortBitArray != NULL) {
  211. delete PortBitArray;
  212. #ifdef DBG
  213. PortBitArray = NULL;
  214. PortBitArraySizeInBytes = 0;
  215. #endif
  216. }
  217. RDPDRPRT_UNLOCK();
  218. }
  219. NTSTATUS RDPDRPRT_RegisterPrinterPortInterface(
  220. IN PWSTR clientMachineName,
  221. IN PCSTR clientPortName,
  222. IN PUNICODE_STRING clientDevicePath,
  223. OUT PWSTR portName,
  224. IN OUT PUNICODE_STRING symbolicLinkName,
  225. OUT ULONG *portNumber
  226. )
  227. /*++
  228. Routine Description:
  229. Register a new client-side port with the spooler via the dynamic port
  230. monitor.
  231. Arguments:
  232. clientMachineName - Client computer name for port description.
  233. clientPortName - Client-side port name for port description.
  234. clientDevicePath - Server-side device path for the port. Reads and
  235. writes to this device are reads and writes to the
  236. client-side device.
  237. portName - What we end up naming the port.
  238. symbolicLinkName - Symbolic link device name for port being registered.
  239. portNumber - Port number for port being registered.
  240. Return Value:
  241. STATUS_SUCCESS if successful,
  242. STATUS_UNSUCCESSFUL otherwise
  243. --*/
  244. {
  245. WCHAR portDesc[RDPDRPRT_PORTDESCRLENGTH];
  246. NTSTATUS status;
  247. UNICODE_STRING unicodeStr;
  248. HANDLE hInterfaceKey = INVALID_HANDLE_VALUE;
  249. BOOL symbolicLinkNameAllocated;
  250. BOOL isPrinterPort = FALSE;
  251. ULONG portArrayIndex = 0;
  252. ULONG len = 0;
  253. PSTR tempName = NULL;
  254. BEGIN_FN("RDPDRPRT_RegisterPrinterPortInterface");
  255. TRC_NRM((TB, "Device path %wZ", clientDevicePath));
  256. if (!RDPDRPRT_Initialized) {
  257. TRC_ERR((TB, "RDPDRPRT_RegisterPrinterPortInterface:"
  258. "RDPDRPRT is not initialized. Exiting."));
  259. return STATUS_INVALID_DEVICE_STATE;
  260. }
  261. //
  262. // Find if this is a printer port or printer name.
  263. // First make a copy of the passed in port name
  264. // and convert to upper case.
  265. //
  266. if (clientPortName != NULL) {
  267. len = strlen(clientPortName);
  268. tempName = (PSTR)new(NonPagedPool) CHAR[len + 1];
  269. if (tempName != NULL) {
  270. PSTR temp = tempName;
  271. strcpy(tempName, clientPortName);
  272. while (len--) {
  273. if (*tempName <= 'z' && *tempName >= 'a') {
  274. (*tempName) ^= 0x20;
  275. tempName++;
  276. }
  277. }
  278. //
  279. // Search for the substring ports
  280. //
  281. isPrinterPort = strstr(temp, "LPT") || strstr(temp, "COM");
  282. //
  283. // If printer port, we use a specific array index
  284. //
  285. if (isPrinterPort) {
  286. portArrayIndex = PRINTER_PORT_ARRAY_ID;
  287. }
  288. status = STATUS_SUCCESS;
  289. }
  290. else {
  291. TRC_ERR((TB, "Error allocating %ld bytes for tempName",
  292. len+1));
  293. status = STATUS_INSUFFICIENT_RESOURCES;
  294. }
  295. }
  296. else {
  297. status = STATUS_INVALID_PARAMETER;
  298. }
  299. //
  300. // Allocate a port.
  301. //
  302. if (NT_SUCCESS(status)) {
  303. status = AllocatePrinterPort(portArrayIndex, portName, portNumber, &hInterfaceKey, symbolicLinkName);
  304. symbolicLinkNameAllocated = (status == STATUS_SUCCESS);
  305. }
  306. //
  307. // Add the port number to the device interface key.
  308. //
  309. if (NT_SUCCESS(status)) {
  310. RtlInitUnicodeString(&unicodeStr,
  311. PORT_NUM_VALUE_NAME);
  312. status=ZwSetValueKey(hInterfaceKey, &unicodeStr, 0, REG_DWORD,
  313. portNumber, sizeof(ULONG));
  314. if (!NT_SUCCESS(status)) {
  315. TRC_ERR((TB, "ZwSetValueKey failed: 08X.", status));
  316. }
  317. }
  318. //
  319. // Add the port name base component to the device interface key.
  320. // This identifies us as a TS port.
  321. //
  322. if (NT_SUCCESS(status)) {
  323. RtlInitUnicodeString(&unicodeStr, PORT_BASE_VALUE_NAME);
  324. status=ZwSetValueKey(
  325. hInterfaceKey, &unicodeStr, 0, REG_SZ,
  326. RDPDRPRT_BASEPORTNAME,
  327. sizeof(RDPDRPRT_BASEPORTNAME)
  328. );
  329. if (!NT_SUCCESS(status)) {
  330. TRC_ERR((TB, "ZwSetValueKey failed with status %08X.", status));
  331. }
  332. }
  333. //
  334. // Add the port description string.
  335. //
  336. if (NT_SUCCESS(status)) {
  337. GeneratePortDescription(
  338. clientPortName,
  339. clientMachineName,
  340. portDesc
  341. );
  342. RtlInitUnicodeString(&unicodeStr, PORT_DESCR_VALUE_NAME);
  343. status=ZwSetValueKey(hInterfaceKey, &unicodeStr, 0, REG_SZ,
  344. portDesc,
  345. (wcslen(portDesc)+1)*sizeof(WCHAR));
  346. if (!NT_SUCCESS(status)) {
  347. TRC_ERR((TB, "ZwSetValueKey failed with status %08X.", status));
  348. }
  349. }
  350. //
  351. // Add Port 'Write Size' Field. This is the size of writes sent by USBMON.DLL.
  352. //
  353. if (NT_SUCCESS(status)) {
  354. RtlInitUnicodeString(&unicodeStr,
  355. PORT_WRITESIZE_VALUE_NAME);
  356. status=ZwSetValueKey(hInterfaceKey, &unicodeStr, 0, REG_DWORD,
  357. &PrintPortWriteSize, sizeof(ULONG));
  358. if (!NT_SUCCESS(status)) {
  359. TRC_ERR((TB, "ZwSetValueKey failed: 08X.", status));
  360. }
  361. }
  362. //
  363. // Associate the client device path with the device interface so we can
  364. // reparse back to the correct client device on IRP_MJ_CREATE.
  365. //
  366. if (NT_SUCCESS(status)) {
  367. RtlInitUnicodeString(&unicodeStr, CLIENT_DEVICE_VALUE_NAME);
  368. status=ZwSetValueKey(hInterfaceKey, &unicodeStr, 0, REG_SZ,
  369. clientDevicePath->Buffer,
  370. (wcslen(clientDevicePath->Buffer)+1)*sizeof(WCHAR));
  371. if (!NT_SUCCESS(status)) {
  372. TRC_ERR((TB, "ZwSetValueKey failed with status %08X.", status));
  373. }
  374. }
  375. //
  376. // Make sure the changes made it to disk in case we have a hard reboot.
  377. //
  378. if (NT_SUCCESS(status)) {
  379. status = ZwFlushKey(hInterfaceKey);
  380. if (!NT_SUCCESS(status)) {
  381. TRC_ERR((TB, "ZwFlushKey failed with status %08X.", status));
  382. }
  383. }
  384. //
  385. // Enable the interface.
  386. //
  387. if (NT_SUCCESS(status)) {
  388. status=IoSetDeviceInterfaceState(symbolicLinkName, TRUE);
  389. if (!NT_SUCCESS(status)) {
  390. TRC_ERR((TB, "IoSetDeviceInterfaceState failed with status %08X.",
  391. status));
  392. }
  393. }
  394. // If we failed, then delete the symbolic link name.
  395. if (!NT_SUCCESS(status) && symbolicLinkNameAllocated)
  396. {
  397. RtlFreeUnicodeString(symbolicLinkName);
  398. }
  399. if (hInterfaceKey != INVALID_HANDLE_VALUE) {
  400. ZwClose(hInterfaceKey);
  401. }
  402. if (tempName != NULL) {
  403. delete tempName;
  404. }
  405. TRC_NRM((TB, "returning port number %ld.", *portNumber));
  406. return status;
  407. }
  408. void RDPDRPRT_UnregisterPrinterPortInterface(
  409. IN ULONG portNumber,
  410. IN PUNICODE_STRING symbolicLinkName
  411. )
  412. /*++
  413. Routine Description:
  414. Unregister a port registered via call to RDPDRPRT_RegisterPrinterPortInterface
  415. Arguments:
  416. portNumber - Port number returned by RDPDRPRT_RegisterPrinterPortInterface.
  417. symbolicLinkName - Symbolic link device name returned by
  418. RDPDRPRT_RegisterPrinterPortInterface.
  419. Return Value:
  420. NA
  421. --*/
  422. {
  423. ULONG ofs, bit;
  424. #if DBG
  425. NTSTATUS status;
  426. #endif
  427. BEGIN_FN("RDPDRPRT_UnregisterPrinterPortInterface");
  428. if (!RDPDRPRT_Initialized) {
  429. TRC_ERR((TB, "RDPDRPRT_UnregisterPrinterPortInterface:"
  430. "RDPDRPRT is not initialized. Exiting."));
  431. return;
  432. }
  433. TRC_ASSERT(symbolicLinkName != NULL, (TB, "symbolicLinkName != NULL"));
  434. TRC_ASSERT(symbolicLinkName->Buffer != NULL,
  435. (TB, "symbolicLinkName->Buffer != NULL"));
  436. TRC_NRM((TB, "Disabling port %ld with interface %wZ",
  437. portNumber, symbolicLinkName));
  438. //
  439. // Change the port description to disabled.
  440. //
  441. SetPortDescrToDisabled(symbolicLinkName);
  442. //
  443. // Disable the device interface, which effectively hides the port from the
  444. // port monitor.
  445. //
  446. #if DBG
  447. status = IoSetDeviceInterfaceState(symbolicLinkName, FALSE);
  448. if (status != STATUS_SUCCESS) {
  449. TRC_NRM((TB, "IoSetDeviceInterfaceState returned error %08X",
  450. status));
  451. }
  452. #else
  453. IoSetDeviceInterfaceState(symbolicLinkName, FALSE);
  454. #endif
  455. //
  456. // Release the symbolic link name.
  457. //
  458. RtlFreeUnicodeString(symbolicLinkName);
  459. //
  460. // Indicate that the port is no longer in use in the port bit array.
  461. //
  462. RDPDRPRT_LOCK();
  463. ofs = portNumber / BITSINULONG;
  464. bit = portNumber % BITSINULONG;
  465. PortBitArray[ofs] &= ~(1<<bit);
  466. RDPDRPRT_UNLOCK();
  467. }
  468. NTSTATUS
  469. FindNextFreePortInPortArray(
  470. IN ULONG lastPortChecked,
  471. OUT ULONG *nextFreePort
  472. )
  473. /*++
  474. Routine Description:
  475. Finds the next free port, following the last port checked, in the port bit
  476. array. The status of the port is changed to "in use" prior to this function
  477. returning.
  478. Arguments:
  479. lastPortChecked - Last port number checked. Should be 0 if first time
  480. called.
  481. nextFreePort - Next free port number.
  482. Return Value:
  483. STATUS_SUCCESS if successful,
  484. STATUS_UNSUCCESSFUL otherwise
  485. --*/
  486. {
  487. ULONG currentPort;
  488. NTSTATUS status = STATUS_SUCCESS;
  489. ULONG ofs, bit;
  490. ULONG currentArraySize;
  491. BEGIN_FN("FindNextFreePortInPortArray");
  492. //
  493. // Find the offset of the long word for the port into the port array
  494. // and the offset for the bit into the long word.
  495. //
  496. ofs = (lastPortChecked+1) / BITSINULONG;
  497. bit = (lastPortChecked+1) % BITSINULONG;
  498. //
  499. // If we re-entered this function (because from Dynamon's perspective, the port is not available),
  500. // it is possible that the lastPortChecked was at the array boundary (ex: 31, 63, 95, 127, ...etc).
  501. // In this case, the offset will mess up the array separation.
  502. // So, we need to adjust the offset to the next higher one
  503. // to keep the printer queue array and the printer port arrays separately.
  504. //
  505. if (bit == 0) {
  506. ofs += 1;
  507. }
  508. RDPDRPRT_LOCK();
  509. //
  510. // If we need to size the port array.
  511. // Note : We have two arrays - one for the printer ports and one for printer queues
  512. // This is to avoid a race condition between creating a printer queue and listening on the printer port
  513. //
  514. currentArraySize = PortBitArraySizeInBytes/sizeof(ULONG);
  515. if (ofs >= (currentArraySize)) {
  516. status = IncreasePortBitArraySize();
  517. if (status == STATUS_SUCCESS) {
  518. currentArraySize = PortBitArraySizeInBytes/sizeof(ULONG);
  519. }
  520. else {
  521. RDPDRPRT_UNLOCK();
  522. return status;
  523. }
  524. }
  525. //
  526. // Find the next free bit.
  527. //
  528. while (1) {
  529. //
  530. // If the current port is already allocated..
  531. //
  532. if (PortBitArray[ofs] & (1<<bit)) {
  533. //
  534. // Next bit.
  535. //
  536. bit += 1;
  537. if (bit >= BITSINULONG) {
  538. bit = 0;
  539. ofs += 2;
  540. }
  541. //
  542. // See if we need to size the port bit array.
  543. //
  544. if (ofs >= (currentArraySize)) {
  545. status = IncreasePortBitArraySize();
  546. if (status == STATUS_SUCCESS) {
  547. currentArraySize = PortBitArraySizeInBytes/sizeof(ULONG);
  548. }
  549. else {
  550. break;
  551. }
  552. }
  553. }
  554. else {
  555. //
  556. // Mark the port used.
  557. //
  558. PortBitArray[ofs] |= (1<<bit);
  559. //
  560. // Return the free port.
  561. //
  562. *nextFreePort = (ofs * BITSINULONG) + bit;
  563. TRC_NRM((TB, "next free port is %ld", *nextFreePort));
  564. break;
  565. }
  566. }
  567. RDPDRPRT_UNLOCK();
  568. TRC_NRM((TB, "return status %08X", status));
  569. return status;
  570. }
  571. NTSTATUS
  572. IncreasePortBitArraySize(
  573. )
  574. /*++
  575. Routine Description:
  576. Increase the size of the port bit array to hold at least one new
  577. port.
  578. Arguments:
  579. Return Value:
  580. STATUS_SUCCESS if successful,
  581. STATUS_UNSUCCESSFUL otherwise
  582. --*/
  583. {
  584. NTSTATUS status;
  585. ULONG newPortBitArraySize;
  586. ULONG *newPortBitArray;
  587. BEGIN_FN("IncreasePortBitArraySize");
  588. TRC_ASSERT(RDPDRPRT_INITIALPORTCOUNT != 0, (TB, "invalid port count."));
  589. newPortBitArraySize = PortBitArraySizeInBytes +
  590. ((RDPDRPRT_INITIALPORTCOUNT / BITSINULONG)
  591. * sizeof(ULONG));
  592. if (RDPDRPRT_INITIALPORTCOUNT % BITSINULONG) {
  593. newPortBitArraySize += sizeof(ULONG);
  594. }
  595. //
  596. // Allocate the new port bit array.
  597. //
  598. newPortBitArray = (ULONG *)new(NonPagedPool) BYTE[newPortBitArraySize];
  599. if (newPortBitArray != NULL) {
  600. RtlZeroMemory(newPortBitArray, newPortBitArraySize);
  601. RtlCopyBytes(newPortBitArray, PortBitArray, PortBitArraySizeInBytes);
  602. delete PortBitArray;
  603. PortBitArray = newPortBitArray;
  604. PortBitArraySizeInBytes = newPortBitArraySize;
  605. status = STATUS_SUCCESS;
  606. }
  607. else {
  608. TRC_ERR((TB, "Error allocating %ld bytes for port array",
  609. newPortBitArraySize));
  610. status = STATUS_INSUFFICIENT_RESOURCES;
  611. }
  612. return status;
  613. }
  614. BOOL
  615. PortIsAvailableForUse(
  616. IN HANDLE regHandle
  617. )
  618. /*++
  619. Routine Description:
  620. Return TRUE if the port's device interface is usable, from dynamon's
  621. perspective.
  622. Arguments:
  623. regHandle - Registry handle to port's device interface.
  624. Return Value:
  625. Return TRUE if the port's device interface is usable.
  626. --*/
  627. {
  628. UNICODE_STRING unicodeStr;
  629. NTSTATUS s;
  630. ULONG bytesReturned;
  631. BOOL usable;
  632. BYTE basicValueInformation[sizeof(KEY_VALUE_BASIC_INFORMATION) + 256];
  633. BEGIN_FN("PortIsAvailableForUse");
  634. //
  635. // Make sure that the basicValueInformation buffer is large enough
  636. // to test the existence of the values we are testing for.
  637. //
  638. TRC_ASSERT((sizeof(RECYCLABLE_FLAG_VALUE_NAME) < 256) &&
  639. (sizeof(PORT_NUM_VALUE_NAME) < 256),
  640. (TB, "Increase basic value buffer."));
  641. //
  642. // If there is no client device path registry value for the port's
  643. // device interface then the port is brand new and, therefore,
  644. // usable.
  645. //
  646. RtlInitUnicodeString(&unicodeStr, CLIENT_DEVICE_VALUE_NAME);
  647. s = ZwQueryValueKey(
  648. regHandle,
  649. &unicodeStr,
  650. KeyValueBasicInformation,
  651. (PVOID)basicValueInformation,
  652. sizeof(basicValueInformation),
  653. &bytesReturned
  654. );
  655. if (s == STATUS_OBJECT_NAME_NOT_FOUND) {
  656. usable = TRUE;
  657. }
  658. //
  659. // Otherwise, see if dynamon set the recyclable flag.
  660. //
  661. else {
  662. RtlInitUnicodeString(&unicodeStr,
  663. RECYCLABLE_FLAG_VALUE_NAME);
  664. s = ZwQueryValueKey(
  665. regHandle,
  666. &unicodeStr,
  667. KeyValueBasicInformation,
  668. (PVOID)basicValueInformation,
  669. sizeof(basicValueInformation),
  670. &bytesReturned
  671. );
  672. usable = (s == STATUS_SUCCESS);
  673. }
  674. if (usable) {
  675. TRC_NRM((TB, "usable and status %08X.", s));
  676. }
  677. else {
  678. TRC_NRM((TB, "not usable and status %08X.", s));
  679. }
  680. return usable;
  681. }
  682. NTSTATUS
  683. AllocatePrinterPort(
  684. IN ULONG portArrayIndex,
  685. OUT PWSTR portName,
  686. OUT ULONG *portNumber,
  687. OUT HANDLE *regHandle,
  688. OUT PUNICODE_STRING symbolicLinkName
  689. )
  690. /*++
  691. Routine Description:
  692. Allocate a port.
  693. Arguments:
  694. portArrayIndex - Index into the port array
  695. portName - String that holds the allocated port name. There must be
  696. room in this buffer for RDPDR_MAXPORTNAMELEN wide characters.
  697. This includes the terminator.
  698. regHandle - Registry handle for the device interface associated with the
  699. port. The calling function should close this handle using
  700. ZwClose.
  701. symbolicLinkName - symbolic link name returned by IoRegisterDeviceInterface for
  702. the port's device interface. The symbolic link name is used
  703. for a number of IO API's. The caller must free this argument
  704. when finished via a call to RtlFreeUnicodeString.
  705. Return Value:
  706. STATUS_SUCCESS is returned on success.
  707. --*/
  708. {
  709. NTSTATUS status = STATUS_SUCCESS;
  710. GUID *pPrinterGuid;
  711. UNICODE_STRING unicodeStr;
  712. ULONG ofs, bit;
  713. BOOL done;
  714. BOOL symbolicLinkNameAllocated = FALSE;
  715. BEGIN_FN("AllocatePrinterPort");
  716. //
  717. // Find an available port.
  718. //
  719. *portNumber = portArrayIndex * BITSINULONG;
  720. done = FALSE;
  721. while (!done && (status == STATUS_SUCCESS)) {
  722. status = FindNextFreePortInPortArray(*portNumber, portNumber);
  723. //
  724. // Generate the port name.
  725. //
  726. if (status == STATUS_SUCCESS) {
  727. if (!GeneratePortName(*portNumber, portName)) {
  728. status = STATUS_INSUFFICIENT_RESOURCES;
  729. }
  730. }
  731. //
  732. // Register a device interface for the port. Note that this function
  733. // opens an existing interface or creates a new one, depending on whether
  734. // the port currently exists.
  735. //
  736. if (status == STATUS_SUCCESS) {
  737. pPrinterGuid = (GUID *)&DYNPRINT_GUID;
  738. RtlInitUnicodeString(&unicodeStr, portName);
  739. status=IoRegisterDeviceInterface(RDPDYN_PDO, pPrinterGuid, &unicodeStr,
  740. symbolicLinkName);
  741. }
  742. symbolicLinkNameAllocated = (status == STATUS_SUCCESS);
  743. //
  744. // Get the reg key for the device interface.
  745. //
  746. if (status == STATUS_SUCCESS) {
  747. status=IoOpenDeviceInterfaceRegistryKey(symbolicLinkName,
  748. KEY_ALL_ACCESS, regHandle);
  749. }
  750. else {
  751. TRC_NRM((TB, "IoRegisterDeviceInterface failed with %08X",
  752. status));
  753. }
  754. //
  755. // See if the port is available for use, from dynamon's perspective.
  756. //
  757. if (status == STATUS_SUCCESS) {
  758. done = PortIsAvailableForUse(*regHandle);
  759. }
  760. else {
  761. *regHandle = INVALID_HANDLE_VALUE;
  762. TRC_NRM((TB, "IoOpenDeviceInterfaceRegistryKey failed with %08X",
  763. status));
  764. }
  765. //
  766. // If this iteration unsuccessfully produced a port.
  767. //
  768. if (!done || (status != STATUS_SUCCESS)) {
  769. //
  770. // Release the symbolic link name if it was allocated.
  771. //
  772. if (symbolicLinkNameAllocated) {
  773. RtlFreeUnicodeString(symbolicLinkName);
  774. symbolicLinkNameAllocated = FALSE;
  775. }
  776. //
  777. // If it's not available, from dynamon's perspective, then we need to
  778. // set it to free in our list so it can be checked later on. This operation
  779. // needs to be locked in case the port bit array is being reallocated.
  780. //
  781. RDPDRPRT_LOCK();
  782. ofs = (*portNumber) / BITSINULONG;
  783. bit = (*portNumber) % BITSINULONG;
  784. PortBitArray[ofs] &= ~(1<<bit);
  785. RDPDRPRT_UNLOCK();
  786. //
  787. // Clean up the open registry handle.
  788. //
  789. if (*regHandle != INVALID_HANDLE_VALUE) {
  790. ZwClose(*regHandle);
  791. *regHandle = INVALID_HANDLE_VALUE;
  792. }
  793. }
  794. }
  795. //
  796. // Clean up.
  797. //
  798. if ((status != STATUS_SUCCESS) && symbolicLinkNameAllocated) {
  799. RtlFreeUnicodeString(symbolicLinkName);
  800. }
  801. return status;
  802. }
  803. BOOL
  804. GeneratePortName(
  805. IN ULONG portNumber,
  806. OUT PWSTR portName
  807. )
  808. /*++
  809. Routine Description:
  810. Generate a port name from a port number.
  811. Arguments:
  812. portNumber - port number component of port name.
  813. portName - the generated port name is formed from the RDPDRPRT_BASEPORTNAME
  814. component and the portNumber component. portName is a string
  815. that holds the generated port name. There must be room in this
  816. buffer for RDPDR_MAXPORTNAMELEN characters. This includes
  817. the terminator.
  818. Return Value:
  819. TRUE if a port name could be successfully generated from the port number.
  820. Otherwise, FALSE.
  821. --*/
  822. {
  823. ULONG baseLen;
  824. UNICODE_STRING numericUnc;
  825. OBJECT_ATTRIBUTES objectAttributes;
  826. WCHAR numericBuf[RDPDR_PORTNAMEDIGITS+1];
  827. ULONG toPad;
  828. ULONG i;
  829. ULONG digitsInPortNumber;
  830. BOOL done;
  831. ULONG tmp;
  832. BEGIN_FN("GeneratePortName");
  833. //
  834. // Compute the number of digits in the port number.
  835. //
  836. for (digitsInPortNumber=1,tmp=portNumber/10; tmp>0; digitsInPortNumber++,tmp/=10);
  837. //
  838. // Make sure we don't exceed the maximum digits allowed in a port name.
  839. //
  840. if (digitsInPortNumber > RDPDR_PORTNAMEDIGITS) {
  841. TRC_ASSERT(FALSE,(TB, "Maximum digits in port exceeded."));
  842. return FALSE;
  843. }
  844. //
  845. // Copy the port name base..
  846. //
  847. wcscpy(portName, RDPDRPRT_BASEPORTNAME);
  848. baseLen = (sizeof(RDPDRPRT_BASEPORTNAME)/sizeof(WCHAR))-1;
  849. //
  850. // Convert the port number to a unicode string.
  851. //
  852. numericUnc.Length = 0;
  853. numericUnc.MaximumLength = (RDPDR_PORTNAMEDIGITS+1) * sizeof(WCHAR);
  854. numericUnc.Buffer = numericBuf;
  855. RtlIntegerToUnicodeString(portNumber, 10, &numericUnc);
  856. //
  857. // If we need to pad the port number.
  858. //
  859. if (RDPDR_PORTNAMEDIGITSTOPAD > digitsInPortNumber) {
  860. toPad = RDPDR_PORTNAMEDIGITSTOPAD - digitsInPortNumber;
  861. //
  862. // Pad.
  863. //
  864. for (i=0; i<toPad; i++) {
  865. portName[baseLen+i] = L'0';
  866. }
  867. //
  868. // Add the rest of the name.
  869. //
  870. wcscpy(&portName[baseLen+i], numericBuf);
  871. }
  872. else {
  873. //
  874. // Add the rest of the name.
  875. //
  876. wcscpy(&portName[baseLen], numericBuf);
  877. }
  878. return TRUE;
  879. }
  880. void
  881. GeneratePortDescription(
  882. IN PCSTR dosPortName,
  883. IN PCWSTR clientName,
  884. IN PWSTR description
  885. )
  886. /*++
  887. Routine Description:
  888. Format a port description.
  889. Arguments:
  890. dosPortName - Preferred DOS name for port.
  891. clientName - Client name (computer name).
  892. description - Buffer for formatted port description. This buffer
  893. must be at least RDPDRPRT_PORTDESCRLENGTH characters
  894. wide.
  895. Return Value:
  896. NA
  897. --*/
  898. {
  899. BEGIN_FN("GeneratePortDescription");
  900. swprintf(description, L"%-s: %S", clientName, dosPortName);
  901. }
  902. NTSTATUS
  903. SetPortDescrToDisabled(
  904. IN PUNICODE_STRING symbolicLinkName
  905. )
  906. /*++
  907. Routine Description:
  908. Set the port description string to disabled.
  909. Arguments:
  910. symbolicLinkName - Symbolic link name.
  911. Return Value:
  912. STATUS_SUCCESS if successful,
  913. STATUS_UNSUCCESSFUL otherwise
  914. --*/
  915. {
  916. HANDLE hInterfaceKey = INVALID_HANDLE_VALUE;
  917. NTSTATUS status;
  918. UNICODE_STRING unicodeStr;
  919. BEGIN_FN("SetPortDescrToDisabled");
  920. TRC_NRM((TB, "Symbolic link name: %wZ",
  921. symbolicLinkName));
  922. //
  923. // Get the reg key for our device interface.
  924. //
  925. status=IoOpenDeviceInterfaceRegistryKey(
  926. symbolicLinkName,
  927. KEY_ALL_ACCESS,&hInterfaceKey
  928. );
  929. if (!NT_SUCCESS(status)) {
  930. hInterfaceKey = INVALID_HANDLE_VALUE;
  931. TRC_ERR((TB, "IoOpenDeviceInterfaceRegistryKey failed: %08X.",
  932. status));
  933. goto CleanUpAndReturn;
  934. }
  935. //
  936. // Set the string value.
  937. //
  938. RtlInitUnicodeString(&unicodeStr, PORT_DESCR_VALUE_NAME);
  939. status=ZwSetValueKey(hInterfaceKey, &unicodeStr, 0, REG_SZ,
  940. DisabledPortDescription,
  941. DisabledPortDescrSize);
  942. if (!NT_SUCCESS(status)) {
  943. TRC_ERR((TB, "ZwSetValueKey failed with status %08X.", status));
  944. goto CleanUpAndReturn;
  945. }
  946. CleanUpAndReturn:
  947. if (hInterfaceKey != INVALID_HANDLE_VALUE) {
  948. ZwClose(hInterfaceKey);
  949. }
  950. return status;
  951. }
  952. NTSTATUS
  953. CleanUpExistingPorts()
  954. /*++
  955. Routine Description:
  956. Clean up ports registered in a previous boot.
  957. Arguments:
  958. Return Value:
  959. STATUS_SUCCESS if successful.
  960. --*/
  961. {
  962. NTSTATUS returnStatus;
  963. NTSTATUS status;
  964. PWSTR symbolicLinkList=NULL;
  965. PWSTR symbolicLink;
  966. ULONG len;
  967. HANDLE deviceInterfaceKey = INVALID_HANDLE_VALUE;
  968. UNICODE_STRING unicodeStr;
  969. ULONG bytesReturned;
  970. BYTE basicValueInformation[sizeof(KEY_VALUE_BASIC_INFORMATION) + 256];
  971. BEGIN_FN("CleanUpExistingPorts");
  972. //
  973. // Make sure that the basicValueInformation buffer is large enough
  974. // to test the existence of the values we are testing for.
  975. //
  976. TRC_ASSERT((sizeof(RECYCLABLE_FLAG_VALUE_NAME) < 256),
  977. (TB, "Increase basic value buffer size."));
  978. //
  979. // Fetch all the device interfaces for dynamic printer ports created
  980. // by this driver.
  981. //
  982. TRC_ASSERT(RDPDYN_PDO != NULL, (TB, "RDPDYN_PDO == NULL"));
  983. returnStatus = IoGetDeviceInterfaces(
  984. &DYNPRINT_GUID,
  985. RDPDYN_PDO,
  986. DEVICE_INTERFACE_INCLUDE_NONACTIVE,
  987. &symbolicLinkList
  988. );
  989. if (returnStatus == STATUS_SUCCESS) {
  990. //
  991. // Remove the port number value for each interface to indicate that the
  992. // port is no longer in use to dynamon.
  993. //
  994. symbolicLink = symbolicLinkList;
  995. len = wcslen(symbolicLink);
  996. while (len > 0) {
  997. TRC_NRM((TB, "CleanUpExistingPorts disabling %ws...",
  998. symbolicLink));
  999. //
  1000. // Open the registry key for the device interface.
  1001. //
  1002. RtlInitUnicodeString(&unicodeStr, symbolicLink);
  1003. status = IoOpenDeviceInterfaceRegistryKey(
  1004. &unicodeStr,
  1005. KEY_ALL_ACCESS,
  1006. &deviceInterfaceKey
  1007. );
  1008. //
  1009. // Make sure the port description has been set to "disabled"
  1010. //
  1011. if (status == STATUS_SUCCESS) {
  1012. RtlInitUnicodeString(&unicodeStr, PORT_DESCR_VALUE_NAME);
  1013. ZwSetValueKey(deviceInterfaceKey, &unicodeStr, 0, REG_SZ,
  1014. DisabledPortDescription,
  1015. DisabledPortDescrSize);
  1016. }
  1017. else {
  1018. TRC_ERR((TB, "Unable to open device interface: %08X",
  1019. status));
  1020. // Remember that the open failed.
  1021. deviceInterfaceKey = INVALID_HANDLE_VALUE;
  1022. }
  1023. //
  1024. // See if the recyclable flag has been set by dynamon.dll. If it has
  1025. // been, then the port is deletable.
  1026. //
  1027. if (status == STATUS_SUCCESS) {
  1028. RtlInitUnicodeString(&unicodeStr,
  1029. RECYCLABLE_FLAG_VALUE_NAME);
  1030. status = ZwQueryValueKey(
  1031. deviceInterfaceKey,
  1032. &unicodeStr,
  1033. KeyValueBasicInformation,
  1034. (PVOID)basicValueInformation,
  1035. sizeof(basicValueInformation),
  1036. &bytesReturned
  1037. );
  1038. }
  1039. //
  1040. // Delete the port number value if it exists. Don't care about the
  1041. // return value because it just means that this port cannot be reused
  1042. // by us and this is not a critical error.
  1043. //
  1044. if (status == STATUS_SUCCESS) {
  1045. RtlInitUnicodeString(&unicodeStr, PORT_NUM_VALUE_NAME);
  1046. ZwDeleteValueKey(deviceInterfaceKey, &unicodeStr);
  1047. }
  1048. else {
  1049. TRC_ERR((TB, "CleanUpExistingPorts recyclable flag not set"));
  1050. }
  1051. //
  1052. // Close the registry key.
  1053. //
  1054. if (deviceInterfaceKey != INVALID_HANDLE_VALUE) {
  1055. ZwClose(deviceInterfaceKey);
  1056. }
  1057. //
  1058. // Move to the next symbolic link in the list.
  1059. //
  1060. symbolicLink += (len+1);
  1061. len = wcslen(symbolicLink);
  1062. }
  1063. //
  1064. // Release the symbolic link list.
  1065. //
  1066. if (symbolicLinkList != NULL) {
  1067. ExFreePool(symbolicLinkList);
  1068. }
  1069. }
  1070. else {
  1071. TRC_NRM((TB, "IoGetDeviceInterfaces failed with status %08X",
  1072. returnStatus));
  1073. }
  1074. return returnStatus;
  1075. }