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.

752 lines
19 KiB

  1. /*++
  2. Copyright (c) 1998-2000 Microsoft Corporation
  3. Module Name:
  4. Channel.cpp
  5. Abstract:
  6. This module implements a thin wrapper on the Read and Write routines so
  7. we can issue Read/Write Irps to termdd.
  8. Environment:
  9. Kernel mode
  10. --*/
  11. #include "precomp.hxx"
  12. #define TRC_FILE "channel"
  13. #include "trc.h"
  14. #include <winsta.h>
  15. #include <ntddvdeo.h>
  16. #include <icadd.h>
  17. #include "TSQPublic.h"
  18. //
  19. // RDPDr.cpp : The TS Worker Queue pointer
  20. //
  21. extern PVOID RDPDR_TsQueue;
  22. typedef struct tagCHANNELIOCONTEXT {
  23. SmartPtr<VirtualChannel> Channel;
  24. PIO_COMPLETION_ROUTINE CompletionRoutine;
  25. PVOID Context;
  26. PVOID Buffer;
  27. ULONG Length;
  28. ULONG IoOperation;
  29. BOOL LowPrioSend;
  30. } CHANNELIOCONTEXT, *PCHANNELIOCONTEXT;
  31. typedef struct tagCHANNELCLOSECONTEXT {
  32. SmartPtr<VirtualChannel> Channel;
  33. } CHANNELCLOSECONTEXT, *PCHANNELCLOSECONTEXT;
  34. VirtualChannel::VirtualChannel()
  35. {
  36. BEGIN_FN("VirtualChannel::VirtualChannel");
  37. SetClassName("VirtualChannel");
  38. _Channel = NULL;
  39. _ChannelFileObject = NULL;
  40. _ChannelDeviceObject = NULL;
  41. _DeletionEvent = NULL;
  42. _LowPrioChannelWriteFlags = 0;
  43. _LowPrioChannelWriteFlags |= CHANNEL_WRITE_LOWPRIO;
  44. }
  45. VirtualChannel::~VirtualChannel()
  46. {
  47. BEGIN_FN("VirtualChannel::~VirtualChannel");
  48. if (_DeletionEvent != NULL) {
  49. KeSetEvent(_DeletionEvent, IO_NO_INCREMENT, FALSE);
  50. }
  51. }
  52. BOOL VirtualChannel::Create(HANDLE hIca, ULONG SessionID, ULONG ChannelId,
  53. PKEVENT DeletionEvent)
  54. /*++
  55. Routine Description:
  56. Opens the virtual channel and make it a kernel handle
  57. Arguments:
  58. Channel - A pointer to a location to store the Channel pointer
  59. hIca - Required context for opening a channel
  60. SessionId - The session for the channel
  61. ChannelId - The Id of the RdpDr channel
  62. Return Value:
  63. a valid NTSTATUS code.
  64. Notes:
  65. --*/
  66. {
  67. NTSTATUS Status = STATUS_SUCCESS;
  68. OBJECT_HANDLE_INFORMATION HandleInformation;
  69. BEGIN_FN("VirtualChannel::Create");
  70. ASSERT(_DeletionEvent == NULL);
  71. _DeletionEvent = DeletionEvent;
  72. //
  73. // Get the channel open
  74. //
  75. Status = CreateTermDD(&_Channel, hIca, SessionID, ChannelId);
  76. if (NT_SUCCESS(Status)) {
  77. //
  78. // Get the file object from the handle
  79. //
  80. Status = ObReferenceObjectByHandle(_Channel,
  81. STANDARD_RIGHTS_REQUIRED, NULL, KernelMode, (PVOID *)(&_ChannelFileObject),
  82. &HandleInformation);
  83. }
  84. if (NT_SUCCESS(Status)) {
  85. TRC_DBG((TB, "ObReferenced channel"));
  86. _ChannelDeviceObject = IoGetRelatedDeviceObject((PFILE_OBJECT)_ChannelFileObject);
  87. }
  88. else {
  89. TRC_ERR((TB, "Failed to open channel"));
  90. if (_Channel != NULL) {
  91. ZwClose(_Channel);
  92. _Channel = NULL;
  93. }
  94. }
  95. if (NT_SUCCESS(Status)) {
  96. return TRUE;
  97. } else {
  98. if (_DeletionEvent) {
  99. KeSetEvent(_DeletionEvent, IO_NO_INCREMENT, FALSE);
  100. }
  101. return FALSE;
  102. }
  103. }
  104. NTSTATUS VirtualChannel::Read(
  105. IN PIO_COMPLETION_ROUTINE ReadRoutine OPTIONAL,
  106. IN PVOID Context,
  107. OUT PVOID Buffer,
  108. IN ULONG Length,
  109. IN BOOL bWorkerItem
  110. )
  111. /*++
  112. Routine Description:
  113. Reads data from the virtual channel for the specified Client
  114. Arguments:
  115. ReadRoutine - Completetion routine
  116. Context - Data to pass to the completion routine
  117. Buffer - Data to transfer
  118. Length - Size of data to transfer
  119. Return Value:
  120. a valid NTSTATUS code.
  121. Notes:
  122. --*/
  123. {
  124. NTSTATUS Status;
  125. BEGIN_FN("VirtualChannel::Read");
  126. #if DBG
  127. SmartPtr<DrSession> Session = (DrSession *)Context;
  128. //ASSERT(InterlockedIncrement(&(Session->_ApcCount)) == 1);
  129. InterlockedIncrement(&(Session->_ApcCount));
  130. InterlockedIncrement(&(Session->_ApcChannelRef));
  131. #endif
  132. Status = SubmitIo(ReadRoutine, Context, Buffer, Length, IRP_MJ_READ, bWorkerItem, FALSE);
  133. #if DBG
  134. if (!NT_SUCCESS(Status)) {
  135. //ASSERT(InterlockedDecrement(&(Session->_ApcCount)) == 0);
  136. //ASSERT(InterlockedDecrement(&(Session->_ApcChannelRef)) == 0);
  137. InterlockedDecrement(&(Session->_ApcCount));
  138. InterlockedDecrement(&(Session->_ApcChannelRef));
  139. }
  140. #endif
  141. return Status;
  142. }
  143. NTSTATUS VirtualChannel::Write(
  144. IN PIO_COMPLETION_ROUTINE WriteRoutine OPTIONAL,
  145. IN PVOID Context,
  146. OUT PVOID Buffer,
  147. IN ULONG Length,
  148. IN BOOL bWorkerItem,
  149. IN BOOL LowPrioSend
  150. )
  151. /*++
  152. Routine Description:
  153. Writes data to the virtual channel for the specified Client
  154. Arguments:
  155. WriteRoutine - Completetion routine
  156. Context - Data to pass to the completion routine
  157. Buffer - Data to transfer
  158. Length - Size of data to transfer
  159. LowPrioSend - Indicate that the channel write should be at
  160. lower priority than other client destined data.
  161. Return Value:
  162. a valid NTSTATUS code.
  163. Notes:
  164. --*/
  165. {
  166. BEGIN_FN("VirtualChannel::Write");
  167. return SubmitIo(WriteRoutine, Context, Buffer, Length, IRP_MJ_WRITE,
  168. bWorkerItem, LowPrioSend);
  169. }
  170. NTSTATUS VirtualChannel::SubmitIo(
  171. IN PIO_COMPLETION_ROUTINE CompletionRoutine OPTIONAL,
  172. IN PVOID Context,
  173. OUT PVOID Buffer,
  174. IN ULONG Length,
  175. ULONG IoOperation,
  176. BOOL bWorkerItem,
  177. BOOL LowPrioSend
  178. )
  179. {
  180. PCHANNELIOCONTEXT pChannelIoContext;
  181. NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
  182. BEGIN_FN("VirtualChannel::SubmitIo");
  183. TRC_ASSERT((IoOperation == IRP_MJ_READ) ||
  184. (IoOperation == IRP_MJ_WRITE), (TB, "Bad ChannelIo operation"));
  185. if (bWorkerItem) {
  186. //
  187. // Move this operation to a system thread
  188. //
  189. TRC_NRM((TB, "DrChannelIo: queueing the I/O to a system thread"));
  190. pChannelIoContext = new (NonPagedPool) CHANNELIOCONTEXT;
  191. if (pChannelIoContext != NULL) {
  192. pChannelIoContext->Channel = this;
  193. pChannelIoContext->CompletionRoutine = CompletionRoutine;
  194. pChannelIoContext->Context = Context;
  195. pChannelIoContext->Buffer = Buffer;
  196. pChannelIoContext->Length = Length;
  197. pChannelIoContext->IoOperation = IoOperation;
  198. pChannelIoContext->LowPrioSend = LowPrioSend;
  199. //
  200. // Use our own TS worker queue
  201. //
  202. Status = TSAddWorkItemToQueue(RDPDR_TsQueue,
  203. pChannelIoContext,
  204. IoWorker);
  205. if (Status == STATUS_SUCCESS) {
  206. Status = STATUS_PENDING;
  207. goto EXIT;
  208. }
  209. else {
  210. //
  211. // Ts Queue failed
  212. //
  213. TRC_ERR((TB, "RDPDR: FAILED Adding workitem to TS Queue 0x%8x", Status));
  214. delete pChannelIoContext;
  215. }
  216. }
  217. if (IoOperation == IRP_MJ_WRITE) {
  218. PIO_STATUS_BLOCK pIoStatusBlock = (PIO_STATUS_BLOCK)Context;
  219. pIoStatusBlock->Status = Status;
  220. pIoStatusBlock->Information = 0;
  221. CompletionRoutine(NULL, NULL, Context);
  222. }
  223. else {
  224. // No read should go through here for now
  225. ASSERT(FALSE);
  226. }
  227. }
  228. else {
  229. Status = Io(CompletionRoutine,
  230. Context,
  231. Buffer,
  232. Length,
  233. IoOperation,
  234. LowPrioSend);
  235. }
  236. EXIT:
  237. return Status;
  238. }
  239. VOID VirtualChannel::IoWorker(PDEVICE_OBJECT DeviceObject, PVOID Context)
  240. /*++
  241. Routine Description:
  242. Reads data from the virtual channel for the specified Client, and
  243. signals the thread wanted it done
  244. Arguments:
  245. ClientEntry - The client with which to communicate
  246. ApcRoutine - Completetion routine
  247. ApcContext - Data to pass to the completion routine
  248. IoStatusBlock - Place to store result code
  249. Buffer - Data to transfer
  250. Length - Size of data to transfer
  251. ByteOffset - Offset into Buffer
  252. IoOperation - Read or Write
  253. Event - Event to signal when done
  254. Status - Result code
  255. Return Value:
  256. None
  257. Notes:
  258. --*/
  259. {
  260. NTSTATUS Status;
  261. PCHANNELIOCONTEXT ChannelIoContext = (PCHANNELIOCONTEXT)Context;
  262. BEGIN_FN_STATIC("VirtualChannel::IoWorker");
  263. ASSERT(ChannelIoContext != NULL);
  264. UNREFERENCED_PARAMETER(DeviceObject);
  265. #if DBG
  266. SmartPtr<DrSession> Session;
  267. if (ChannelIoContext->IoOperation == IRP_MJ_READ) {
  268. Session = (DrSession *)(ChannelIoContext->Context);
  269. ASSERT(Session->GetBuffer() == ChannelIoContext->Buffer);
  270. }
  271. #endif
  272. Status = ChannelIoContext->Channel->Io(
  273. ChannelIoContext->CompletionRoutine,
  274. ChannelIoContext->Context,
  275. ChannelIoContext->Buffer,
  276. ChannelIoContext->Length,
  277. ChannelIoContext->IoOperation,
  278. ChannelIoContext->LowPrioSend);
  279. //
  280. // Now delete the context
  281. //
  282. delete ChannelIoContext;
  283. }
  284. NTSTATUS VirtualChannel::Io(
  285. IN PIO_COMPLETION_ROUTINE CompletionRoutine OPTIONAL,
  286. IN PVOID Context,
  287. OUT PVOID Buffer,
  288. IN ULONG Length,
  289. ULONG IoOperation,
  290. BOOL LowPrioSend
  291. )
  292. /*++
  293. Routine Description:
  294. Reads/Writes data from/to the virtual channel for the specified Client
  295. Arguments:
  296. CompletionRoutine - Completetion routine
  297. Context - Data to pass to the completion routine
  298. Buffer - Data to transfer
  299. Length - Size of data to transfer
  300. IoOperation - Read or Write
  301. Return Value:
  302. a valid NTSTATUS code.
  303. Notes:
  304. --*/
  305. {
  306. NTSTATUS Status;
  307. PIRP Irp;
  308. PIO_STACK_LOCATION IrpSp;
  309. LARGE_INTEGER StartOffset;
  310. IO_STATUS_BLOCK IoStatusBlock;
  311. BEGIN_FN("VirtualChannel::SubmitIo");
  312. SharedLock sl(_HandleLock);
  313. if (_Channel != NULL) {
  314. //
  315. // Build a read/write irp
  316. //
  317. StartOffset.QuadPart = 0;
  318. Irp = IoBuildAsynchronousFsdRequest(IoOperation, _ChannelDeviceObject, Buffer, Length,
  319. &StartOffset, &IoStatusBlock);
  320. if (Irp) {
  321. //
  322. // Setup the fileobject parameter
  323. //
  324. IrpSp = IoGetNextIrpStackLocation(Irp);
  325. IrpSp->FileObject = _ChannelFileObject;
  326. Irp->Tail.Overlay.Thread = NULL;
  327. //
  328. // Set for low prio write, if specified.
  329. //
  330. if (!LowPrioSend) {
  331. Irp->Tail.Overlay.DriverContext[0] = NULL;
  332. }
  333. else {
  334. Irp->Tail.Overlay.DriverContext[0] = &_LowPrioChannelWriteFlags;
  335. }
  336. //
  337. // Setup the completion routine
  338. //
  339. IoSetCompletionRoutine(Irp, CompletionRoutine, Context, TRUE, TRUE, TRUE);
  340. //
  341. // Send the Irp to Termdd
  342. //
  343. Status = IoCallDriver(_ChannelDeviceObject, Irp);
  344. goto EXIT;
  345. }
  346. else {
  347. Status = STATUS_NO_MEMORY;
  348. }
  349. }
  350. else {
  351. Status = STATUS_DEVICE_NOT_CONNECTED;
  352. }
  353. if (IoOperation == IRP_MJ_WRITE) {
  354. PIO_STATUS_BLOCK pIoStatusBlock = (PIO_STATUS_BLOCK)Context;
  355. pIoStatusBlock->Status = Status;
  356. pIoStatusBlock->Information = 0;
  357. CompletionRoutine(NULL, NULL, Context);
  358. // read completion is not called this way.
  359. }
  360. EXIT:
  361. return Status;
  362. }
  363. NTSTATUS VirtualChannel::CreateTermDD(HANDLE *Channel, HANDLE hIca,
  364. ULONG SessionID, ULONG ChannelId)
  365. /*++
  366. Routine Description:
  367. Opens a virtual channel based on the supplied context
  368. Arguments:
  369. Channel - A pointer to a location to store the Channel pointer
  370. hIca - Required context for opening a channel
  371. SessionId - The session for the channel
  372. ChannelId - The Id of the RdpDr channel
  373. Return Value:
  374. NTSTATUS code
  375. Notes:
  376. --*/
  377. {
  378. NTSTATUS Status;
  379. HANDLE hChannel = NULL;
  380. WCHAR ChannelNameBuffer[MAX_PATH];
  381. UNICODE_STRING ChannelName;
  382. UNICODE_STRING Number;
  383. OBJECT_ATTRIBUTES ChannelAttributes;
  384. IO_STATUS_BLOCK IoStatusBlock;
  385. PFILE_FULL_EA_INFORMATION pEa = NULL;
  386. ICA_OPEN_PACKET UNALIGNED * pIcaOpenPacket;
  387. ULONG cbEa = sizeof( FILE_FULL_EA_INFORMATION )
  388. + ICA_OPEN_PACKET_NAME_LENGTH
  389. + sizeof( ICA_OPEN_PACKET );
  390. BEGIN_FN("VirtualChannel::CreateTermDD");
  391. //
  392. // Kernel-mode applications open a virtual channel using ZwCreateFile on
  393. // \Device\ICA\sss\Virtualvvv , where
  394. //
  395. // � sss is the logon session ID
  396. // � vvv is the virtual channel number.
  397. //
  398. ChannelName.Buffer = ChannelNameBuffer;
  399. ChannelName.Length = 0;
  400. ChannelName.MaximumLength = sizeof(ChannelNameBuffer);
  401. Status = RtlAppendUnicodeToString(&ChannelName, L"\\Device\\Termdd\\");
  402. TRC_ASSERT(NT_SUCCESS(Status), (TB, "Creating channel path"));
  403. //
  404. // Create and append on the sessionID string
  405. //
  406. // Point another UNICODE_STRING to the next part of the buffer
  407. Number.Buffer = (PWCHAR)(((PBYTE)ChannelName.Buffer) + ChannelName.Length);
  408. Number.Length = 0;
  409. Number.MaximumLength = ChannelName.MaximumLength - ChannelName.Length;
  410. // Use that string to put the characters in the right place
  411. Status = RtlIntegerToUnicodeString(SessionID, 10, &Number);
  412. TRC_ASSERT(NT_SUCCESS(Status), (TB, "Creating channel path"));
  413. // Add the length of that string to the real string
  414. ChannelName.Length += Number.Length;
  415. //
  416. // Append the next part of the channel path
  417. //
  418. Status = RtlAppendUnicodeToString(&ChannelName, L"\\Virtual");
  419. TRC_ASSERT(NT_SUCCESS(Status), (TB, "Creating channel path"));
  420. //
  421. // Create and append the channelID string
  422. //
  423. // Point another UNICODE_STRING to the next part of the buffer
  424. Number.Buffer = (PWCHAR)(((PBYTE)ChannelName.Buffer) + ChannelName.Length);
  425. Number.Length = 0;
  426. Number.MaximumLength = ChannelName.MaximumLength - ChannelName.Length;
  427. // Use that string to put the characters in the right place
  428. Status = RtlIntegerToUnicodeString(ChannelId, 10, &Number);
  429. TRC_ASSERT(NT_SUCCESS(Status), (TB, "Creating channel path"));
  430. // Add the length of that string to the real string
  431. ChannelName.Length += Number.Length;
  432. //
  433. // Actually open the channel
  434. //
  435. InitializeObjectAttributes(&ChannelAttributes,
  436. &ChannelName,
  437. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  438. NULL,
  439. NULL);
  440. //
  441. // Pass in a cool EaBuffer thing so this will really work
  442. // I basically lifted this code from private\tsext\icaapi\stack.c
  443. // It's supposed to be a temporary measure
  444. //
  445. /*
  446. * Allocate some memory for the EA buffer
  447. */
  448. pEa = (PFILE_FULL_EA_INFORMATION)new BYTE[cbEa];
  449. if (pEa != NULL) {
  450. /*
  451. * Initialize the EA buffer
  452. */
  453. pEa->NextEntryOffset = 0;
  454. pEa->Flags = 0;
  455. pEa->EaNameLength = ICA_OPEN_PACKET_NAME_LENGTH;
  456. RtlCopyMemory(pEa->EaName, ICAOPENPACKET, ICA_OPEN_PACKET_NAME_LENGTH + 1 );
  457. pEa->EaValueLength = sizeof( ICA_OPEN_PACKET );
  458. pIcaOpenPacket = (ICA_OPEN_PACKET UNALIGNED *)(pEa->EaName +
  459. pEa->EaNameLength + 1);
  460. /*
  461. * Now put the open packet parameters into the EA buffer
  462. */
  463. pIcaOpenPacket->IcaHandle = hIca;
  464. pIcaOpenPacket->OpenType = IcaOpen_Channel;
  465. pIcaOpenPacket->TypeInfo.ChannelClass = Channel_Virtual;
  466. RtlCopyMemory(pIcaOpenPacket->TypeInfo.VirtualName, DR_CHANNEL_NAME,
  467. sizeof(DR_CHANNEL_NAME));
  468. //
  469. // We keep this next line without "pEa, cbEa"
  470. //
  471. Status = ZwCreateFile(&hChannel, GENERIC_READ | GENERIC_WRITE,
  472. &ChannelAttributes, &IoStatusBlock, 0, FILE_ATTRIBUTE_NORMAL, 0,
  473. FILE_OPEN_IF, FILE_SEQUENTIAL_ONLY, pEa, cbEa);
  474. delete pEa;
  475. } else {
  476. TRC_ERR((TB, "Unable to allocate EaBuffer for ZwCreateFile(channel)"));
  477. Status = STATUS_INSUFFICIENT_RESOURCES;
  478. }
  479. if (NT_SUCCESS(Status)) {
  480. *Channel = hChannel;
  481. }
  482. return Status;
  483. }
  484. void VirtualChannel::CloseWorker(PDEVICE_OBJECT DeviceObject, PVOID Context)
  485. /*++
  486. Routine Description:
  487. Closes the virtual channel in a work item
  488. Arguments:
  489. None
  490. Return Value:
  491. NTSTATUS code from ZwClose.
  492. Notes:
  493. --*/
  494. {
  495. PCHANNELCLOSECONTEXT ChannelCloseContext = (PCHANNELCLOSECONTEXT)Context;
  496. BEGIN_FN_STATIC("VirtualChannel::CloseWorker");
  497. ASSERT(ChannelCloseContext != NULL);
  498. UNREFERENCED_PARAMETER(DeviceObject);
  499. ChannelCloseContext->Channel->Close();
  500. //
  501. // Now delete the context
  502. //
  503. delete ChannelCloseContext;
  504. }
  505. NTSTATUS VirtualChannel::Close()
  506. /*++
  507. Routine Description:
  508. Closes the virtual channel
  509. Arguments:
  510. None
  511. Return Value:
  512. NTSTATUS code from ZwClose.
  513. Notes:
  514. --*/
  515. {
  516. NTSTATUS Status = STATUS_SUCCESS;
  517. BEGIN_FN("VirtualChannel::Close");
  518. ExclusiveLock el(_HandleLock);
  519. ASSERT(_Channel != NULL);
  520. ASSERT(_ChannelFileObject != NULL);
  521. TRC_NRM((TB, "DrChannelClose: Close the channel"));
  522. _ChannelDeviceObject = NULL;
  523. ObDereferenceObject(_ChannelFileObject);
  524. _ChannelFileObject = NULL;
  525. ZwClose(_Channel);
  526. _Channel = NULL;
  527. return Status;
  528. }
  529. NTSTATUS VirtualChannel::SubmitClose()
  530. /*++
  531. Routine Description:
  532. Post a close virtual channel request to a worker item
  533. Arguments:
  534. None
  535. Return Value:
  536. NTSTATUS code from ZwClose.
  537. Notes:
  538. --*/
  539. {
  540. PCHANNELCLOSECONTEXT pChannelCloseContext;
  541. NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
  542. BEGIN_FN("VirtualChannel::SubmitClose");
  543. //
  544. // Move this operation to a system thread
  545. //
  546. TRC_NRM((TB, "DrChannelClose: queueing the I/O to a system thread"));
  547. pChannelCloseContext = new (NonPagedPool) CHANNELCLOSECONTEXT;
  548. if (pChannelCloseContext != NULL) {
  549. pChannelCloseContext->Channel = this;
  550. //
  551. // Use our own TS worker queue
  552. //
  553. Status = TSAddWorkItemToQueue(RDPDR_TsQueue,
  554. pChannelCloseContext,
  555. CloseWorker);
  556. if( Status == STATUS_SUCCESS) {
  557. Status = STATUS_PENDING;
  558. }
  559. else {
  560. //
  561. // Ts Queue failed
  562. //
  563. TRC_ERR((TB, "RDPDR: FAILED Adding workitem to TS Queue 0x%8x", Status));
  564. delete pChannelCloseContext;
  565. }
  566. }
  567. return Status;
  568. }