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.

558 lines
19 KiB

  1. /* (C) 1997-1999 Microsoft Corp.
  2. *
  3. * file : IcaIFace.c
  4. * author : Erik Mavrinac
  5. *
  6. * description: MCS setup/shutdown and direct entry points for use with the
  7. * ICA programming model. See also Decode.c for IcaRawInput() handling.
  8. *
  9. * History:
  10. * 10-Aug-1997 jparsons Revised for new calling model
  11. * 05-Aug-1998 jparsons Added shadowing support
  12. *
  13. */
  14. #include "precomp.h"
  15. #pragma hdrstop
  16. #include <MCSImpl.h>
  17. // Prototype for WD function used below, so we don't need to include
  18. // lots of extra headers.
  19. void WDW_OnClientDisconnected(void *);
  20. /*
  21. * Main initialization entry point for kernel-mode MCS.
  22. * Called by the WD during its processing of WdOpen().
  23. */
  24. MCSError APIENTRY MCSInitialize(
  25. PSDCONTEXT pContext,
  26. PSD_OPEN pSdOpen,
  27. DomainHandle *phDomain,
  28. void *pSMData)
  29. {
  30. Domain *pDomain;
  31. unsigned i;
  32. ULONG ulBufferLen;
  33. TraceOut(pContext, "MCSInitialize(): entry");
  34. //
  35. // Alloc the Domain struct. We allocate the basic size plus the typical
  36. // input buffer size. The default in the registry today is 2048 which
  37. // nicely fits the max virtual channel PDU of about 1640. If we get a
  38. // message that exceeds this length we will dynamically allocate a buffer
  39. // just for use in a one time reassembly then delete it.
  40. //
  41. if (pSdOpen->StackClass != Stack_Passthru) {
  42. ulBufferLen = pSdOpen->WdConfig.WdInputBufferLength;
  43. }
  44. else {
  45. ulBufferLen = 1024 * 10;
  46. }
  47. pDomain = ExAllocatePoolWithTag(PagedPool, sizeof(Domain) +
  48. ulBufferLen + INPUT_BUFFER_BIAS, MCS_POOL_TAG);
  49. if (pDomain != NULL) {
  50. memset(pDomain, 0, sizeof(Domain));
  51. }
  52. else {
  53. ErrOut(pContext, "MCSInitialize(kernel): Alloc failure "
  54. "allocating Domain");
  55. return MCS_ALLOCATION_FAILURE;
  56. }
  57. // Save pContext -- it is needed for future tracing and ICA interaction.
  58. pDomain->pContext = pContext;
  59. // Save pSMData - needed for calling fast-path input decoding function.
  60. pDomain->pSMData = pSMData;
  61. // Store what we need from SD_OPEN.
  62. pDomain->pStat = pSdOpen->pStatus;
  63. pDomain->ReceiveBufSize = ulBufferLen;
  64. // We have one reference to this pDomain
  65. pDomain->PseudoRefCount = 1;
  66. // Indicate that we do not want TermDD managing outbuf headers/trailers
  67. pSdOpen->SdOutBufHeader = 0;
  68. pSdOpen->SdOutBufTrailer = 0;
  69. // Initialize MCS-specific Domain members. We already zeroed mem
  70. // so set only nonzero variables.
  71. SListInit(&pDomain->ChannelList, DefaultNumChannels);
  72. SListInit(&pDomain->UserAttachmentList, DefaultNumUserAttachments);
  73. pDomain->bTopProvider = TRUE;
  74. pDomain->NextAvailDynChannel = MinDynamicChannel;
  75. pDomain->State = State_Unconnected;
  76. pDomain->StackClass = pSdOpen->StackClass;
  77. for (i = 0; i < NumPreallocUA; i++) {
  78. pDomain->PreallocUA[i].bInUse = FALSE;
  79. pDomain->PreallocUA[i].bPreallocated = TRUE;
  80. }
  81. for (i = 0; i < NumPreallocChannel; i++) {
  82. pDomain->PreallocChannel[i].bInUse = FALSE;
  83. pDomain->PreallocChannel[i].bPreallocated = TRUE;
  84. }
  85. // Give the Domain to the caller.
  86. *phDomain = pDomain;
  87. // If this is a shadow or passthru stack, the default all the info
  88. // otherwise, all this info gets built when the client connects
  89. if ((pDomain->StackClass == Stack_Passthru) ||
  90. (pDomain->StackClass == Stack_Shadow))
  91. MCSCreateDefaultDomain(pContext, *phDomain);
  92. return MCS_NO_ERROR;
  93. }
  94. /*
  95. * Called by WD during shadow connect processing to retrieve the client MCS
  96. * domain parameters for use by the shadow target stack.
  97. */
  98. MCSError APIENTRY MCSGetDomainInfo(
  99. DomainHandle hDomain,
  100. PDomainParameters pDomParams,
  101. unsigned *MaxSendSize,
  102. unsigned *MaxX224DataSize,
  103. unsigned *X224SourcePort)
  104. {
  105. Domain *pDomain = hDomain;
  106. TraceOut(pDomain->pContext, "MCSGetDomainInfo(): entry");
  107. *pDomParams = pDomain->DomParams;
  108. *MaxSendSize = pDomain->MaxSendSize;
  109. *MaxX224DataSize = pDomain->MaxX224DataSize;
  110. *X224SourcePort = pDomain->X224SourcePort;
  111. return MCS_NO_ERROR;
  112. }
  113. /*
  114. * Called by WD during shadow connect processing to initialize the MCS
  115. * domain for the shadow & passthru stacks.
  116. */
  117. MCSError APIENTRY MCSCreateDefaultDomain(PSDCONTEXT pContext,
  118. DomainHandle hDomain)
  119. {
  120. Domain *pDomain = hDomain;
  121. TraceOut(pContext, "MCSCreateDefaultDomain(): entry");
  122. pDomain->DomParams.MaxChannels = 34;
  123. pDomain->DomParams.MaxUsers = RequiredMinUsers;
  124. pDomain->DomParams.MaxTokens = 0;
  125. pDomain->DomParams.NumPriorities = RequiredPriorities;
  126. pDomain->DomParams.MinThroughput = 0;
  127. pDomain->DomParams.MaxDomainHeight = RequiredDomainHeight;
  128. pDomain->DomParams.MaxPDUSize = X224_DefaultDataSize;
  129. pDomain->DomParams.ProtocolVersion = RequiredProtocolVer;
  130. pDomain->MaxSendSize = X224_DefaultDataSize - 6 -
  131. GetTotalLengthDeterminantEncodingSize(X224_DefaultDataSize);
  132. pDomain->MaxX224DataSize = X224_DefaultDataSize;
  133. pDomain->X224SourcePort = 0x1234;
  134. pDomain->State = State_MCS_Connected;
  135. pDomain->bCanSendData = 1;
  136. return MCS_NO_ERROR;
  137. }
  138. /*
  139. /*
  140. * Called by WD during shadow connect processing to get the default domain
  141. * params for the shadow target stack.
  142. */
  143. MCSError APIENTRY MCSGetDefaultDomain(PSDCONTEXT pContext,
  144. PDomainParameters pDomParams,
  145. unsigned *MaxSendSize,
  146. unsigned *MaxX224DataSize,
  147. unsigned *X224SourcePort)
  148. {
  149. TraceOut(pContext, "MCSGetDefaultDomain(): entry");
  150. pDomParams->MaxChannels = 34;
  151. pDomParams->MaxUsers = RequiredMinUsers;
  152. pDomParams->MaxTokens = 0;
  153. pDomParams->NumPriorities = RequiredPriorities;
  154. pDomParams->MinThroughput = 0;
  155. pDomParams->MaxDomainHeight = RequiredDomainHeight;
  156. pDomParams->MaxPDUSize = X224_DefaultDataSize;
  157. pDomParams->ProtocolVersion = RequiredProtocolVer;
  158. *MaxSendSize = pDomParams->MaxPDUSize - 6 -
  159. GetTotalLengthDeterminantEncodingSize(pDomParams->MaxPDUSize);
  160. *MaxX224DataSize = X224_DefaultDataSize;
  161. *X224SourcePort = 0x1234;
  162. return MCS_NO_ERROR;
  163. }
  164. /*
  165. * Called by WD during shadow connect processing to register which channel
  166. * should receive all shadow data.
  167. */
  168. MCSError APIENTRY MCSSetShadowChannel(
  169. DomainHandle hDomain,
  170. ChannelID shadowChannel)
  171. {
  172. Domain *pDomain = hDomain;
  173. TraceOut(pDomain->pContext, "MCSSetShadowChannel: entry");
  174. pDomain->shadowChannel = shadowChannel;
  175. return MCS_NO_ERROR;
  176. }
  177. /*
  178. * Main destruction entry point for kernel-mode MCS.
  179. * Called by the WD during its processing of WdClose().
  180. */
  181. MCSError APIENTRY MCSCleanup(DomainHandle *phDomain)
  182. {
  183. Domain *pDomain;
  184. UINT_PTR ChannelID;
  185. MCSChannel *pMCSChannel;
  186. UserHandle hUser;
  187. UserAttachment *pUA;
  188. pDomain = (Domain *)(*phDomain);
  189. TraceOut1(pDomain->pContext, "MCSCleanup(): pDomain=%X", pDomain);
  190. /*
  191. * Free any remaining data in the Domain.
  192. */
  193. // Deallocate all remaining channels, if present. Note we should take care
  194. // of channels first since they're usually attached to other objects and
  195. // need to have their bPreallocated status determined first.
  196. for (;;) {
  197. SListRemoveFirst(&pDomain->ChannelList, &ChannelID, &pMCSChannel);
  198. if (pMCSChannel == NULL)
  199. break;
  200. SListDestroy(&pMCSChannel->UserList);
  201. if (!pMCSChannel->bPreallocated)
  202. ExFreePool(pMCSChannel);
  203. }
  204. // Deallocate all remaining user attachments, if present.
  205. for (;;) {
  206. SListRemoveFirst(&pDomain->UserAttachmentList, (UINT_PTR *)&hUser,
  207. &pUA);
  208. if (pUA == NULL)
  209. break;
  210. SListDestroy(&pUA->JoinedChannelList);
  211. if (!pUA->bPreallocated)
  212. ExFreePool(pUA);
  213. }
  214. // Kill lists.
  215. SListDestroy(&pDomain->ChannelList);
  216. SListDestroy(&pDomain->UserAttachmentList);
  217. // Free outstanding dynamic input reassembly buffer if present.
  218. if (pDomain->pReassembleData != NULL &&
  219. pDomain->pReassembleData != pDomain->PacketBuf)
  220. ExFreePool(pDomain->pReassembleData);
  221. // Free the Domain.
  222. PDomainRelease(pDomain);
  223. *phDomain = NULL;
  224. return MCS_NO_ERROR;
  225. }
  226. /*
  227. * Callout from WD when an IOCTL_ICA_VIRTUAL_QUERY_BINDINGS is received.
  228. * pVBind is a pointer to an empty SD_VCBIND struct.
  229. */
  230. NTSTATUS MCSIcaVirtualQueryBindings(
  231. DomainHandle hDomain,
  232. PSD_VCBIND *ppVBind,
  233. unsigned *pBytesReturned)
  234. {
  235. Domain *pDomain;
  236. NTSTATUS Status;
  237. PSD_VCBIND pVBind;
  238. pDomain = (Domain *)hDomain;
  239. pVBind = *ppVBind;
  240. // Define the user mode T120 channel.
  241. if (!pDomain->bChannelBound) {
  242. RtlCopyMemory(pVBind->VirtualName, Virtual_T120,
  243. sizeof(Virtual_T120));
  244. pVBind->VirtualClass = Virtual_T120ChannelNum;
  245. *pBytesReturned = sizeof(SD_VCBIND);
  246. pDomain->bChannelBound = TRUE;
  247. // Skip our entry and advance the caller's pointer.
  248. pVBind++;
  249. *ppVBind = pVBind;
  250. }
  251. else {
  252. *pBytesReturned = 0;
  253. }
  254. Status = STATUS_SUCCESS;
  255. // This is one of the events which must occur before the data flow can be
  256. // sent across the net. If we have gotten an MCS_T120_START indication
  257. // already, and an X.224 connect-request, then it is now time to send
  258. // the X.224 response and kick off the data flow.
  259. if (pDomain->bCanSendData && pDomain->State == State_X224_Requesting) {
  260. TraceOut(pDomain->pContext,
  261. "IcaQueryVirtBind(): Sending X.224 response");
  262. Status = SendX224Confirm(pDomain);
  263. }
  264. return Status;
  265. }
  266. /*
  267. * Callout from WD upon reception of a IOCTL_T120_REQUEST, i.e. a user-mode
  268. * ioctl.
  269. */
  270. NTSTATUS MCSIcaT120Request(DomainHandle hDomain, PSD_IOCTL pSdIoctl)
  271. {
  272. Domain *pDomain;
  273. IoctlHeader *pHeader;
  274. pDomain = (Domain *)hDomain;
  275. // Get the request type.
  276. ASSERT(pSdIoctl->InputBufferLength >= sizeof(IoctlHeader));
  277. pHeader = (IoctlHeader *)pSdIoctl->InputBuffer;
  278. // Make sure request within bounds.
  279. if (pHeader->Type < MCS_ATTACH_USER_REQUEST ||
  280. pHeader->Type > MCS_T120_START) {
  281. ErrOut(pDomain->pContext, "Invalid IOCTL_T120_REQUEST type");
  282. return STATUS_INVALID_DEVICE_REQUEST;
  283. }
  284. // Check that request is supported.
  285. if (g_T120RequestDispatch[pHeader->Type] == NULL) {
  286. ErrOut(pDomain->pContext, "IOCTL_T120_REQUEST type unsupported");
  287. return STATUS_INVALID_DEVICE_REQUEST;
  288. }
  289. // Make the call. The entry points are defined in MCSIoctl.c.
  290. return (g_T120RequestDispatch[pHeader->Type])(pDomain, pSdIoctl);
  291. }
  292. /*
  293. * Processes channel inputs from TD. For MCS we only need to check for
  294. * upward-bound command channel inputs for broken-connection indications;
  295. * everything else can be passed up the stack.
  296. */
  297. // Utility function. Used here and in Decode.c for X.224 disconnection.
  298. void SignalBrokenConnection(Domain *pDomain)
  299. {
  300. NTSTATUS Status;
  301. DisconnectProviderIndicationIoctl DPin;
  302. // Check if disconnection already happened.
  303. if (pDomain->State != State_MCS_Connected)
  304. return;
  305. if (!pDomain->bChannelBound) {
  306. TraceOut(pDomain->pContext, "SignalBrokenConnection(): Cannot "
  307. "send disconnect-provider indication: user mode link broken");
  308. return;
  309. }
  310. TraceOut(pDomain->pContext, "SignalBrokenConnection(): Sending "
  311. "disconnect-provider indication to user mode");
  312. // Begin filling out disconnect-provider indication for the node controller.
  313. DPin.Header.Type = MCS_DISCONNECT_PROVIDER_INDICATION;
  314. DPin.Header.hUser = NULL; // Node controller.
  315. DPin.hConn = NULL;
  316. DPin.Reason = REASON_DOMAIN_DISCONNECTED;
  317. TraceOut1(pDomain->pContext, "%s: SignalBrokenConnection!!!",
  318. pDomain->StackClass == Stack_Primary ? "Primary" :
  319. (pDomain->StackClass == Stack_Shadow ? "Shadow" :
  320. "PassThru"));
  321. // Send the DPin to the node controller channel.
  322. Status = IcaChannelInput(pDomain->pContext, Channel_Virtual,
  323. Virtual_T120ChannelNum, NULL, (BYTE *)&DPin, sizeof(DPin));
  324. if (!NT_SUCCESS(Status)) {
  325. ErrOut(pDomain->pContext, "SignalBrokenConn(): Could not send "
  326. "disconnect-provider indication: error on ChannelInput()");
  327. // Ignore errors sending disconnect-provider upward. If the stack is
  328. // going down we will no longer have connectivity.
  329. }
  330. // Transition to state unconnected, detach nonlocal users.
  331. DisconnectProvider(pDomain, FALSE, REASON_DOMAIN_DISCONNECTED);
  332. }
  333. /*
  334. * This function is called directly by TermDD with a pointer to the WD data
  335. * structure. By convention, we assume that the DomainHandle is first in
  336. * that struct so we can simply do a double-indirection to get to our data.
  337. */
  338. NTSTATUS MCSIcaChannelInput(
  339. void *pTSWd,
  340. CHANNELCLASS ChannelClass,
  341. VIRTUALCHANNELCLASS VirtualClass,
  342. PINBUF pInBuf,
  343. BYTE *pBuffer,
  344. ULONG ByteCount)
  345. {
  346. Domain *pDomain;
  347. NTSTATUS Status;
  348. PICA_CHANNEL_COMMAND pCommand;
  349. pDomain = (Domain *)(*((HANDLE *)pTSWd));
  350. if (ChannelClass != Channel_Command)
  351. goto SendUpStack;
  352. if (ByteCount < sizeof(ICA_CHANNEL_COMMAND)) {
  353. ErrOut(pDomain->pContext, "ChannelInput(): Channel_Command bad "
  354. "byte count");
  355. goto SendUpStack;
  356. }
  357. pCommand = (PICA_CHANNEL_COMMAND)pBuffer;
  358. if (pCommand->Header.Command != ICA_COMMAND_BROKEN_CONNECTION)
  359. goto SendUpStack;
  360. TraceOut1(pDomain->pContext, "%s: ChannelInput(): broken connection received",
  361. pDomain->StackClass == Stack_Primary ? "Primary" :
  362. (pDomain->StackClass == Stack_Shadow ? "Shadow" :
  363. "PassThru"));
  364. // Block further send attempts from MCS. We will eventually receive an
  365. // IOCTL_ICA_STACK_CANCEL_IO which means the same thing, but that is
  366. // only done after we issue the ICA_COMMAND_BROKEN_CONNECTION
  367. // upward.
  368. pDomain->bCanSendData = FALSE;
  369. // Signal that the client closed the connection, both for MCS and
  370. // directly to the WD to release any session locks waiting on
  371. // the client to complete a connection protocol sequence.
  372. if (pDomain->pBrokenEvent)
  373. KeSetEvent (pDomain->pBrokenEvent, EVENT_INCREMENT, FALSE);
  374. WDW_OnClientDisconnected(pTSWd);
  375. // If we have not already received a disconnect-provider request from
  376. // user mode, send an indication.
  377. if (pDomain->State == State_MCS_Connected && pDomain->bChannelBound)
  378. SignalBrokenConnection(pDomain);
  379. SendUpStack:
  380. Status = IcaChannelInput(pDomain->pContext, ChannelClass, VirtualClass,
  381. pInBuf, pBuffer, ByteCount);
  382. if (!NT_SUCCESS(Status))
  383. ErrOut(pDomain->pContext, "MCSIcaChannelInput(): Failed to forward "
  384. "input upward");
  385. return Status;
  386. }
  387. /*
  388. * Receives signal from WD that an IOCTL_ICA_STACK_CANCEL_IO was received
  389. * which signals that I/O on the stack is no longer allowed. After this
  390. * point no further ICA buffer allocation, freeing, or data sends should be
  391. * performed.
  392. */
  393. void MCSIcaStackCancelIo(DomainHandle hDomain)
  394. {
  395. TraceOut(((Domain *)hDomain)->pContext, "Received STACK_CANCEL_IO");
  396. ((Domain *)hDomain)->bCanSendData = FALSE;
  397. }
  398. /*
  399. * Returns number of bytes (octets) consumed finding the size in NBytesConsumed.
  400. * Returns length in Result. Sets *pbLarge to nonzero if there are more
  401. * encoded blocks following this one.
  402. * Note that the maximum size encoded is 64K -- 0xC4 indicates
  403. * that 4 16K blocks are encoded here. If the block is larger, for instance
  404. * in a large MCS Send Data PDU, multiple blocks will be encoded one
  405. * after another. If the block is an exact multiple of 16K, a trailing byte
  406. * code 0x00 is appended as a placemarker to indicate that the encoding is
  407. * complete.
  408. * Examples of large encodings:
  409. *
  410. * 16K: 0xC1, then 16K of data, then 0x00 as the final placeholder.
  411. * 16K + 1: 0xC1, then 16K of data, then 0x01, and finally the extra byte of data.
  412. * 64K: 0xC4, then 64K of data, then 0x00 as the final placeholder.
  413. * 128K + 1: 0xC4 then 64K of data, 0xC4 + 64K of data, 0x01 + 1 byte of data.
  414. *
  415. * pStart is assumed to be an octet(BYTE)-aligned address -- this function is
  416. * designed for ALIGNED-PER encoding type, which is the type used in MCS.
  417. * Note that bit references here are in the range 7..0 where 7 is the high bit.
  418. * The ASN.1 spec uses 8..1.
  419. * Returns FALSE if length could not be retrieved.
  420. */
  421. BOOLEAN __fastcall DecodeLengthDeterminantPER(
  422. BYTE *pStart, // [IN], points to start of encoded bytes.
  423. unsigned BytesLeft, // [IN], number of bytes remaining in frame.
  424. BOOLEAN *pbLarge, // [OUT] TRUE if there are more encoded blocks following.
  425. unsigned *Length, // [OUT] Number of bytes encoded here.
  426. unsigned *pNBytesConsumed) // [OUT] Count of bytes consumed in decoding.
  427. {
  428. if (BytesLeft >= 1) {
  429. if (*pStart <= 0x7F) {
  430. *pNBytesConsumed = 1;
  431. *Length = *pStart;
  432. *pbLarge = FALSE;
  433. }
  434. else {
  435. // High bit 7 set, check to see if bit 6 is set.
  436. if (*pStart & 0x40) {
  437. // Bit 6 is set, the lowest 3 bits encode the number (1..4) of
  438. // full 16K chunks following.
  439. *pNBytesConsumed = 1;
  440. *Length = 16384 * (*pStart & 0x07);
  441. *pbLarge = TRUE;
  442. }
  443. else {
  444. // Bit 6 is clear, length is encoded in 14 bits of last 6 bits
  445. // of *pStart as most significant bits and all of the next
  446. // byte as least significant.
  447. if (BytesLeft >= 2) {
  448. *pNBytesConsumed = 2;
  449. *Length = ((unsigned)((*pStart & 0x3F) << 8) +
  450. (unsigned)(*(pStart + 1)));
  451. *pbLarge = FALSE;
  452. }
  453. else {
  454. return FALSE;
  455. }
  456. }
  457. }
  458. return TRUE;
  459. }
  460. else {
  461. return FALSE;
  462. }
  463. }