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.

1473 lines
45 KiB

  1. /****************************************************************************/
  2. // mcsapi.c
  3. //
  4. // TS RDPWSX MCS user-mode multiplexing layer code.
  5. //
  6. // Copyright (C) 1997-2000 Microsoft Corporation
  7. /****************************************************************************/
  8. #include "precomp.h"
  9. #pragma hdrstop
  10. #include "mcsmux.h"
  11. /*
  12. * Defines
  13. */
  14. #define DefaultNumDomains 10
  15. #define DefaultNumChannels 3
  16. #define MinDomainControlMarker 0xFFFFFFF0
  17. #define ReadChannelRequestMarker 0xFFFFFFFD
  18. #define CloseChannelRequestMarker 0xFFFFFFFE
  19. #define ExitIoThreadMarker 0xFFFFFFFF
  20. // Defines the maximum number of threads that can be waiting on the global
  21. // I/O completion port. 0 means the same as the number of processors in
  22. // the system.
  23. #define MaxIoPortThreads 0
  24. /*
  25. * DLL globals
  26. */
  27. static PVOID g_NCUserDefined = NULL;
  28. static DWORD g_IoThreadID = 0;
  29. static HANDLE g_hIoPort = NULL,
  30. g_hIoThread = NULL,
  31. g_hIoThreadEndEvent = NULL;
  32. static OVERLAPPED g_NCOverlapped;
  33. static MCSNodeControllerCallback g_NCCallback = NULL;
  34. // Externs global to all sources.
  35. BOOL g_bInitialized = FALSE;
  36. #ifdef MCS_FUTURE
  37. CRITICAL_SECTION g_csGlobalListLock;
  38. SList g_UserList;
  39. SList g_ConnList;
  40. #endif
  41. /*
  42. * Local function prototypes.
  43. */
  44. MCSError SendConnectProviderResponse(Domain *, ConnectionHandle, MCSResult,
  45. BYTE *, unsigned);
  46. /*
  47. * Initialization done at RDPWSX.dll load.
  48. */
  49. BOOL MCSDLLInit(void)
  50. {
  51. return TRUE;
  52. }
  53. /*
  54. * Cleanup done at RDPWSX.dll unload.
  55. */
  56. void MCSDllCleanup(void)
  57. {
  58. // Cleanup all resources. MCSCleanup() must handle abnormal
  59. // terminations where MCSCleanup() was not properly called
  60. // by the node controller.
  61. MCSCleanup();
  62. }
  63. /*
  64. * Utility functions.
  65. */
  66. void DestroyUserInfo(UserInformation *pUserInfo)
  67. {
  68. MCSChannel *pMCSChannel;
  69. SListResetIteration(&pUserInfo->JoinedChannelList);
  70. while (SListIterate(&pUserInfo->JoinedChannelList,
  71. (unsigned *)&pMCSChannel, &pMCSChannel))
  72. Free(pMCSChannel);
  73. SListDestroy(&pUserInfo->JoinedChannelList);
  74. }
  75. // Perform common domain destruction. Used in multiple places in code.
  76. void DestroyDomain(Domain *pDomain)
  77. {
  78. // Fill the to-be-freed Domain with recognizable trash
  79. // and then release it back to the heap
  80. DeleteCriticalSection(&pDomain->csLock);
  81. Free(pDomain);
  82. }
  83. /*
  84. * Handles a connect-provider indication ChanelInput coming from kernel mode.
  85. */
  86. void HandleConnectProviderIndication(
  87. Domain *pDomain,
  88. unsigned BytesTransferred,
  89. ConnectProviderIndicationIoctl *pCPin)
  90. {
  91. MCSError MCSErr;
  92. Connection *pConn;
  93. ConnectProviderIndication CP;
  94. if (BytesTransferred != sizeof(ConnectProviderIndicationIoctl)) {
  95. ErrOutIca1(pDomain->hIca, "HandleConnectProvInd(): Wrong size data "
  96. "received (%d), ignoring", BytesTransferred);
  97. return;
  98. }
  99. ASSERT(pCPin->UserDataLength <= MaxGCCConnectDataLength);
  100. if (pDomain->State != Dom_Unconnected) {
  101. ErrOutIca(pDomain->hIca, "HandleConnectProvInd(): Connect received "
  102. "unexpectedly, ignoring");
  103. return;
  104. }
  105. // Generate a new Connection for user mode. Associate it with the domain.
  106. // This allows the ConnectProviderResponse() to find the domain again.
  107. // TODO FUTURE: We use a static Connection embedded in the Domain since
  108. // we are currently a single-connection system. Change this in the
  109. // future for multiple-connection system.
  110. pConn = &pDomain->MainConn;
  111. pConn->pDomain = pDomain;
  112. pConn->hConnKernel = pCPin->hConn;
  113. #ifdef MCS_Future
  114. pConn = Malloc(sizeof(Connection));
  115. if (pConn == NULL) {
  116. ErrOutIca(pDomain->hIca, "HandleConnectProvInd(): Could not "
  117. "create Connection");
  118. // Send error PDU back.
  119. MCSErr = SendConnectProviderResponse(pDomain, pCPin->hConn,
  120. RESULT_UNSPECIFIED_FAILURE, NULL, 0);
  121. // We cannot do much more error handling if this does not work.
  122. ASSERT(MCSErr == MCS_NO_ERROR);
  123. return;
  124. }
  125. EnterCriticalSection(&g_csGlobalListLock);
  126. if (!SListAppend(&g_ConnList, (unsigned)pConn, pDomain)) {
  127. LeaveCriticalSection(&g_csGlobalListLock);
  128. ErrOutIca(pDomain->hIca, "ConnectProvInd: Could not "
  129. "add hConn to global list");
  130. // Send error PDU back.
  131. MCSErr = SendConnectProviderResponse(pDomain, pCPin->hConn,
  132. RESULT_UNSPECIFIED_FAILURE, NULL, 0);
  133. // We cannot do much more error handling if this does not work.
  134. ASSERT(MCSErr == MCS_NO_ERROR);
  135. return;
  136. }
  137. LeaveCriticalSection(&g_csGlobalListLock);
  138. #endif
  139. // Store information away for future use.
  140. pDomain->DomParams = pCPin->DomainParams;
  141. // Prepare a ConnectProviderIndication for sending up to
  142. // the node controller.
  143. CP.hConnection = pConn;
  144. CP.bUpwardConnection = pCPin->bUpwardConnection;
  145. CP.DomainParams = pCPin->DomainParams;
  146. CP.UserDataLength = pCPin->UserDataLength;
  147. if (CP.UserDataLength == 0)
  148. CP.pUserData = NULL;
  149. else
  150. CP.pUserData = pCPin->UserData;
  151. //TODO FUTURE: This is a hack, assumes only one connection
  152. // per domain. This is used in DisconnectProviderInd
  153. // to get the user mode hConn for the domain.
  154. pDomain->hConn = pConn;
  155. // Set state to pending response.
  156. pDomain->State = Dom_PendingCPResponse;
  157. // Call the node controller callback.
  158. TraceOutIca(pDomain->hIca, "MCS_CONNECT_PROV_IND received, calling node "
  159. "ctrl callback");
  160. ASSERT(g_NCCallback != NULL);
  161. g_NCCallback(pDomain, MCS_CONNECT_PROVIDER_INDICATION,
  162. &CP, pDomain->NCUserDefined);
  163. }
  164. /*
  165. * Handles a disconnect-provider indication ChanelInput coming from kernel
  166. * mode.
  167. */
  168. void HandleDisconnectProviderIndication(
  169. Domain *pDomain,
  170. unsigned BytesTransferred,
  171. DisconnectProviderIndicationIoctl *pDPin)
  172. {
  173. Domain *pDomainConn;
  174. Connection *pConn;
  175. DisconnectProviderIndication DP;
  176. if (BytesTransferred != sizeof(DisconnectProviderIndicationIoctl)) {
  177. ErrOutIca1(pDomain->hIca, "HandleDiscProvInd(): Wrong size data "
  178. "received (%d), ignoring", BytesTransferred);
  179. return;
  180. }
  181. #ifdef MCS_Future
  182. // Remove the connection from the connection list, destroy.
  183. EnterCriticalSection(&g_csGlobalListLock);
  184. SListResetIteration(&g_ConnList);
  185. while (SListIterate(&g_ConnList, (unsigned *)&pConn, &pDomainConn)) {
  186. if (pConn->hConnKernel == pDPin->hConn) {
  187. ASSERT(pDomainConn == pDomain);
  188. SListRemove(&g_ConnList, (unsigned)pConn, NULL);
  189. // TODO FUTURE: This was removed to work with the statically
  190. // allocated Connection object contained in the Domain.
  191. // Restore if moving to a multiple-connection system.
  192. Free(pConn);
  193. break;
  194. }
  195. }
  196. LeaveCriticalSection(&g_csGlobalListLock);
  197. #endif
  198. // We are now no longer connected.
  199. pDomain->hConn = NULL;
  200. pDomain->State = Dom_Unconnected;
  201. // Prepare a DisconnectProviderIndication for sending up
  202. // to node controller.
  203. DP.hDomain = pDomain;
  204. //TODO FUTURE: This hack assumes only one connection per
  205. // domain.
  206. DP.hConnection = pDomain->hConn;
  207. DP.Reason = pDPin->Reason;
  208. // Call the node controller callback.
  209. TraceOutIca(pDomain->hIca, "MCS_DISCONNECT_PROV_IND received, calling "
  210. "node ctrl callback");
  211. ASSERT(g_NCCallback != NULL);
  212. g_NCCallback(pDomain, MCS_DISCONNECT_PROVIDER_INDICATION, &DP,
  213. pDomain->NCUserDefined);
  214. }
  215. /*
  216. * Takes a reference count on a domain
  217. */
  218. VOID MCSReferenceDomain(Domain *pDomain)
  219. {
  220. if (InterlockedIncrement(&pDomain->RefCount) <= 0)
  221. ASSERT(0);
  222. }
  223. /*
  224. * Releases domain resources if the reference count goes to zero
  225. */
  226. VOID MCSDereferenceDomain(Domain *pDomain)
  227. {
  228. ASSERT(pDomain->RefCount > 0);
  229. // Don't delete the domain unless everyone is done with it. This means
  230. // GCC has let it go, and there are no pending I/O's for it.
  231. if (InterlockedDecrement(&pDomain->RefCount) == 0) {
  232. DestroyDomain(pDomain);
  233. }
  234. }
  235. /*
  236. * Closes the domain channel. Does nothing if the channel is already closed
  237. */
  238. VOID MCSChannelClose(Domain *pDomain)
  239. {
  240. // Note that the channel is disconnected, and then
  241. // close the ica channel if it is still open
  242. pDomain->bPortDisconnected = TRUE;
  243. if (pDomain->hIcaT120Channel != NULL)
  244. {
  245. //TraceOutIca(pDomain->hIca, "MCSChannelClose(): Closing "
  246. // "T120 ICA channel");
  247. CancelIo(pDomain->hIcaT120Channel);
  248. IcaChannelClose(pDomain->hIcaT120Channel);
  249. pDomain->hIcaT120Channel = NULL;
  250. }
  251. }
  252. /*
  253. * Initiates port disconnection
  254. */
  255. NTSTATUS MCSDisconnectPort(Domain *pDomain,
  256. MCSReason Reason)
  257. {
  258. NTSTATUS ntStatus = STATUS_SUCCESS;
  259. DisconnectProviderRequestIoctl DPrq;
  260. // Send special disconnect-provider request to kernel to trigger
  261. // sending detach-user requests to local attachments with their own
  262. // UserIDs, signaling that the domain is going away.
  263. if (!pDomain->bPortDisconnected) {
  264. DPrq.Header.Type = MCS_DISCONNECT_PROVIDER_REQUEST;
  265. DPrq.Header.hUser = NULL; // Special meaning node controller.
  266. DPrq.hConn = NULL; // Special meaning last local connection.
  267. DPrq.Reason = Reason;
  268. // Call kernel mode
  269. ntStatus = IcaStackIoControl(pDomain->hIcaStack, IOCTL_T120_REQUEST, &DPrq,
  270. sizeof(DPrq), NULL, 0, NULL);
  271. }
  272. // Queue a channel close request to the IoThreadFunc() to cancel the I/O
  273. // since GCC is done with it. This is necessary as the I/O must
  274. // be canceled from the same thread that initially issued it.
  275. MCSReferenceDomain(pDomain);
  276. PostQueuedCompletionStatus(g_hIoPort, CloseChannelRequestMarker,
  277. (ULONG_PTR)pDomain, NULL);
  278. return ntStatus;
  279. }
  280. /*
  281. * Handles port data and reissues the read
  282. */
  283. VOID MCSPortData(Domain *pDomain,
  284. DWORD BytesTransferred)
  285. {
  286. IoctlHeader *pHeader;
  287. EnterCriticalSection(&pDomain->csLock);
  288. // If real data has been received on the channel instead of a queued domain
  289. // control message, then process the data.
  290. if (BytesTransferred < MinDomainControlMarker) {
  291. // We only do callbacks if MCSDeleteDomain() was not called.
  292. if (pDomain->bDeleteDomainCalled == FALSE) {
  293. // Decode the ChannelInput and make the callback.
  294. pHeader = (IoctlHeader *)pDomain->InputBuf;
  295. switch (pHeader->Type)
  296. {
  297. case MCS_CONNECT_PROVIDER_INDICATION:
  298. ASSERT(pHeader->hUser == NULL);
  299. HandleConnectProviderIndication(pDomain,
  300. BytesTransferred,
  301. (ConnectProviderIndicationIoctl *)pHeader);
  302. break;
  303. case MCS_DISCONNECT_PROVIDER_INDICATION:
  304. ASSERT(pHeader->hUser == NULL);
  305. HandleDisconnectProviderIndication(pDomain,
  306. BytesTransferred,
  307. (DisconnectProviderIndicationIoctl *)pHeader);
  308. MCSChannelClose(pDomain);
  309. break;
  310. default:
  311. //TODO FUTURE: Handle other MCS indications/confirms.
  312. ErrOutIca2(pDomain->hIca, "IoThreadFunc(): Unknown "
  313. "node controller ioctl %d received for "
  314. "domain %X", pHeader->Type, pDomain);
  315. break;
  316. }
  317. // Set the message number to an invalid value.
  318. // This makes sure that any improperly-received dequeued
  319. // messages that do not bring data with them will, when
  320. // reusing pDomain->InputBuf, fall to the default part of
  321. // the switch statement and throw an error.
  322. pHeader->Type = 0xFFFFFFFF;
  323. }
  324. }
  325. // Else, a special domain control request has been queued to the I/O port
  326. else {
  327. switch (BytesTransferred) {
  328. case ReadChannelRequestMarker :
  329. break;
  330. case CloseChannelRequestMarker :
  331. MCSChannelClose(pDomain);
  332. break;
  333. default:
  334. ErrOutIca2(pDomain->hIca, "MCSPortData: Unknown domain control "
  335. "for Domain(%lx), code(%lx)",
  336. pDomain, BytesTransferred);
  337. }
  338. }
  339. // Issue a new read to catch the next indication/confirm. Overlapped
  340. // I/O reads will return as "unsuccessful" if there is no data already
  341. // waiting to be read so check for that status specifically.
  342. if (pDomain->hIcaT120Channel) {
  343. if (ReadFile(pDomain->hIcaT120Channel, pDomain->InputBuf,
  344. DefaultInputBufSize, NULL, &pDomain->Overlapped) ||
  345. (GetLastError() == ERROR_IO_PENDING))
  346. MCSReferenceDomain(pDomain);
  347. else
  348. {
  349. // Warning only. This should occur only when the ICA stack
  350. // is being torn down without our direct knowledge. In that
  351. // case, we simply keep running until we are torn down at the
  352. // user mode level. Take off a ref count since there is no
  353. // longer a pending I/O.
  354. WarnOutIca2(pDomain->hIca, "IoThreadFunc(): Could not perform "
  355. "ReadFile, pDomain=%X, rc=%X", pDomain, GetLastError());
  356. }
  357. }
  358. // Release the lock on the domain
  359. LeaveCriticalSection(&pDomain->csLock);
  360. // drop the reference count since we just completed processing
  361. MCSDereferenceDomain(pDomain);
  362. }
  363. /*
  364. * Param is the handle for the I/O completion port to wait on.
  365. * Completion key of ExitIoThreadMarker tells the thread to exit.
  366. * On exit we set an event to indicate we are done. This must be done instead
  367. * of relying solely on the thread's handle signaling in the case where
  368. * the unload is occurring because of abnormal termination, and Cleanup()
  369. * does not get called normally but instead from within DLL_PROCESS_DETACH.
  370. * The call to DllEntryPoint prevents the thread handle from signaling,
  371. * so we would end up in a race condition. A parallel event handle can be
  372. * signaled correctly in all cases.
  373. */
  374. DWORD WINAPI IoThreadFunc(void *Param)
  375. {
  376. BOOL bSuccess;
  377. DWORD BytesTransferred;
  378. Domain *pDomain;
  379. OVERLAPPED *pOverlapped;
  380. ASSERT(Param != NULL);
  381. for (;;)
  382. {
  383. // Wait for a port completion status
  384. pDomain = NULL;
  385. pOverlapped = NULL;
  386. bSuccess = GetQueuedCompletionStatus((HANDLE)Param, &BytesTransferred,
  387. (ULONG_PTR *)&pDomain, &pOverlapped, INFINITE);
  388. // Check for failed dequeue, at which point pDomain is not valid.
  389. if (!bSuccess && (pOverlapped == NULL)) {
  390. continue;
  391. }
  392. // If the pDomain is ExitIoThreadMarker then we are
  393. // shutting down and being unloaded
  394. if (pDomain == (Domain *)(DWORD_PTR)ExitIoThreadMarker)
  395. break;
  396. // We have successfully received a completion status. Either it is a
  397. // domain control request or user data
  398. if (bSuccess)
  399. MCSPortData(pDomain, BytesTransferred);
  400. // Else a cancel or abort I/O has occurred. Release a ref count since
  401. // an I/O is no longer pending.
  402. else
  403. MCSDereferenceDomain(pDomain);
  404. }
  405. ASSERT(g_hIoThreadEndEvent);
  406. SetEvent(g_hIoThreadEndEvent);
  407. return 0;
  408. }
  409. /*
  410. * MUX-only non-MCS primitive function to allow ICA code to inject a new entry
  411. * into the MUX-internal domain/stack database.
  412. */
  413. MCSError APIENTRY MCSCreateDomain(
  414. HANDLE hIca,
  415. HANDLE hIcaStack,
  416. void *pContext,
  417. DomainHandle *phDomain)
  418. {
  419. NTSTATUS status;
  420. Domain *pDomain;
  421. NTSTATUS Status;
  422. IoctlHeader StartIoctl;
  423. CheckInitialized("CreateDomain()");
  424. // Init the receiver's data to NULL to ensure a fault if they skip the
  425. // error code.
  426. *phDomain = NULL;
  427. pDomain = Malloc(sizeof(Domain));
  428. if (pDomain != NULL) {
  429. // Create and enter the locking critical section.
  430. status = RtlInitializeCriticalSection(&pDomain->csLock);
  431. if (status == STATUS_SUCCESS) {
  432. EnterCriticalSection(&pDomain->csLock);
  433. #if MCS_Future
  434. pDomain->SelLen = 0;
  435. #endif
  436. pDomain->hIca = hIca;
  437. pDomain->hIcaStack = hIcaStack;
  438. pDomain->NCUserDefined = pContext;
  439. pDomain->State = Dom_Unconnected;
  440. pDomain->Overlapped.hEvent = NULL;
  441. pDomain->Overlapped.Offset = pDomain->Overlapped.OffsetHigh = 0;
  442. pDomain->RefCount = 0;
  443. // Take a reference count for the caller of this function (GCC).
  444. MCSReferenceDomain(pDomain);
  445. // Open T.120 ICA virtual channel.
  446. Status = IcaChannelOpen(hIca, Channel_Virtual, Virtual_T120,
  447. &pDomain->hIcaT120Channel);
  448. if (!NT_SUCCESS(Status)) {
  449. ErrOutIca(hIca, "CreateDomain: Error opening virtual channel");
  450. goto PostInitCS;
  451. }
  452. // Add the hIcaT120 Channel to the handles associated with the
  453. // main I/O completion port. We use pDomain as the completion key
  454. // so we can find it during callback processing.
  455. if (CreateIoCompletionPort(pDomain->hIcaT120Channel, g_hIoPort,
  456. (ULONG_PTR)pDomain, MaxIoPortThreads) == NULL) {
  457. ErrOutIca(hIca, "CreateDomain(): Could not add ICA channel to "
  458. "I/O completion port");
  459. goto PostChannel;
  460. }
  461. // Tell kernel mode to start the MCS I/O.
  462. StartIoctl.hUser = NULL;
  463. StartIoctl.Type = MCS_T120_START;
  464. Status = IcaStackIoControl(hIcaStack, IOCTL_T120_REQUEST,
  465. &StartIoctl, sizeof(StartIoctl), NULL, 0, NULL);
  466. if (!NT_SUCCESS(Status)) {
  467. ErrOutIca(hIca, "Could not start kernel T120 I/O");
  468. goto PostChannel;
  469. }
  470. // Set the domain handle for use by GCC
  471. *phDomain = pDomain;
  472. // Send a message to the IoThreadFunc to initiate the read on
  473. // the channel. We do this so that the same thread is always
  474. // responsible for all I/O operations. This is important for
  475. // CancelIo. Take another ref count since an I/O result is
  476. // pending for the completion port now.
  477. MCSReferenceDomain(pDomain);
  478. PostQueuedCompletionStatus(g_hIoPort, ReadChannelRequestMarker,
  479. (ULONG_PTR)pDomain, NULL);
  480. // Leave the Domain locking critical section.
  481. LeaveCriticalSection(&pDomain->csLock);
  482. }
  483. else {
  484. ErrOutIca(hIca, "CreateDomain: Error initialize pDomain->csLock");
  485. goto PostAlloc;
  486. }
  487. }
  488. else {
  489. ErrOutIca(hIca, "CreateDomain(): Error allocating a new domain");
  490. return MCS_ALLOCATION_FAILURE;
  491. }
  492. return MCS_NO_ERROR;
  493. // Error handling
  494. PostChannel:
  495. IcaChannelClose(pDomain->hIcaT120Channel);
  496. PostInitCS:
  497. LeaveCriticalSection(&pDomain->csLock);
  498. DeleteCriticalSection(&pDomain->csLock);
  499. PostAlloc:
  500. Free(pDomain);
  501. return MCS_ALLOCATION_FAILURE;
  502. }
  503. /*
  504. * Signals that an hIca is no longer valid.
  505. * Tears down the associated domain, including sending detach-user
  506. * indications to remaining local attachments.
  507. */
  508. MCSError APIENTRY MCSDeleteDomain(
  509. HANDLE hIca,
  510. DomainHandle hDomain,
  511. MCSReason Reason)
  512. {
  513. Domain *pDomain, pDomainConn;
  514. NTSTATUS Status;
  515. MCSError MCSErr;
  516. Connection *pConn;
  517. pDomain = (Domain *)hDomain;
  518. TraceOutIca(hIca, "DeleteDomain() entry");
  519. // Gain access to the domain. Prevents collision with other API functions
  520. // and concurrent callbacks.
  521. EnterCriticalSection(&pDomain->csLock);
  522. // This API should not be called more than once per domain.
  523. if (pDomain->bDeleteDomainCalled) {
  524. ASSERT(!pDomain->bDeleteDomainCalled);
  525. MCSErr = MCS_INVALID_PARAMETER;
  526. LeaveCriticalSection(&pDomain->csLock);
  527. goto PostLockDomain;
  528. }
  529. #ifdef MCS_Future
  530. // If we are still connected find and remove remaining connections
  531. // which refer to this domain.
  532. if (pDomain->State != Dom_Unconnected) {
  533. //TODO FUTURE: This assumes only one connection per domain.
  534. EnterCriticalSection(&g_csGlobalListLock);
  535. SListRemove(&g_ConnList, (unsigned)pDomain->hConn,
  536. (void **)&pDomainConn);
  537. LeaveCriticalSection(&g_csGlobalListLock);
  538. // TODO FUTURE: This was removed to work with the statically
  539. // allocated Connection object contained in the Domain.
  540. // Restore if moving to a multiple-connection system.
  541. if (pDomainConn != NULL)
  542. Free(pConn);
  543. }
  544. #endif
  545. pDomain->hConn = NULL;
  546. pDomain->State = Dom_Unconnected;
  547. //TODO: Implement detach-user indications for local attachments.
  548. // Queue the "Destroy this domain!" request. This allows the domain
  549. // free code to be in only one place and the IoPort queue will
  550. // serialize this request along with closed virtual channel indications.
  551. pDomain->bDeleteDomainCalled = TRUE;
  552. MCSDisconnectPort(pDomain, Reason);
  553. // Drop a ref count because GCC is done with the domain. This count was
  554. // originally incremented for GCC in MCSCreateDomain().
  555. LeaveCriticalSection(&pDomain->csLock);
  556. MCSDereferenceDomain(pDomain);
  557. MCSErr = MCS_NO_ERROR;
  558. PostLockDomain:
  559. return MCSErr;
  560. }
  561. /*
  562. * Called by node controller to initialize DLL.
  563. */
  564. MCSError APIENTRY MCSInitialize(MCSNodeControllerCallback Callback)
  565. {
  566. NTSTATUS status;
  567. #if DBG
  568. if (g_bInitialized) {
  569. ErrOut("Initialize() called when already initialized");
  570. return MCS_ALREADY_INITIALIZED;
  571. }
  572. #endif
  573. // Initialize node controller specific information.
  574. g_NCCallback = Callback;
  575. #ifdef MCS_FUTURE
  576. // Initialize global MCS lists.
  577. status = RtlInitializeCriticalSection(&g_csGlobalListLock);
  578. if (status != STATUS_SUCCESS) {
  579. ErrOut("MCSInitialize: Error initialize g_csGlobalListLock");
  580. return MCS_ALLOCATION_FAILURE;
  581. }
  582. EnterCriticalSection(&g_csGlobalListLock);
  583. SListInit(&g_UserList, DefaultNumDomains);
  584. SListInit(&g_ConnList, DefaultNumDomains);
  585. LeaveCriticalSection(&g_csGlobalListLock);
  586. #endif
  587. // Create I/O completion port, not associated with any requests.
  588. g_hIoPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0,
  589. MaxIoPortThreads);
  590. if (g_hIoPort == NULL) {
  591. ErrOut1("IO completion port not created (rc = %ld)", GetLastError());
  592. return MCS_ALLOCATION_FAILURE;
  593. }
  594. // Create I/O port listening thread, starts waiting immediately.
  595. g_hIoThread = CreateThread(NULL, 0, IoThreadFunc, g_hIoPort, 0,
  596. &g_IoThreadID);
  597. if (g_hIoThread == NULL) {
  598. ErrOut1("IO thread not created (rc = %ld)", GetLastError());
  599. return MCS_ALLOCATION_FAILURE;
  600. }
  601. // Increase the priority of the IoThread to increase connect performance.
  602. SetThreadPriority(g_hIoThread, THREAD_PRIORITY_HIGHEST);
  603. g_bInitialized = TRUE;
  604. return MCS_NO_ERROR;
  605. }
  606. /*
  607. * Called by the node controller or DllEntryPoint() to shut down the DLL.
  608. */
  609. MCSError APIENTRY MCSCleanup(void)
  610. {
  611. DWORD WaitResult;
  612. Domain *pDomain;
  613. Connection *pConn;
  614. UserInformation *pUserInfo;
  615. CheckInitialized("Cleanup()");
  616. if (!g_bInitialized)
  617. return MCS_NO_ERROR;
  618. // Kill I/O completion port, thread(s) waiting on it.
  619. g_hIoThreadEndEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  620. PostQueuedCompletionStatus(g_hIoPort, 0, ExitIoThreadMarker, NULL);
  621. WaitResult = WaitForSingleObject(g_hIoThreadEndEvent, INFINITE);
  622. CloseHandle(g_hIoThreadEndEvent);
  623. CloseHandle(g_hIoThread);
  624. CloseHandle(g_hIoPort);
  625. // Cleanup node controller-specific information.
  626. g_NCCallback = NULL;
  627. g_NCUserDefined = NULL;
  628. /*
  629. * Clean up global MCS lists.
  630. */
  631. #ifdef MCS_Future
  632. EnterCriticalSection(&g_csGlobalListLock);
  633. // Kill remaining users.
  634. SListResetIteration(&g_UserList);
  635. while (SListIterate(&g_UserList, (unsigned *)&pUserInfo, NULL)) {
  636. DestroyUserInfo(pUserInfo);
  637. Free(pUserInfo);
  638. }
  639. SListDestroy(&g_UserList);
  640. // Kill remaining connections.
  641. SListResetIteration(&g_ConnList);
  642. // TODO FUTURE: Removed to work with statically allocated Connection
  643. // contained in Domain. Restore fo multiple-connection system.
  644. while (SListIterate(&g_ConnList, (unsigned *)&pConn, NULL))
  645. Free(pConn);
  646. SListDestroy(&g_ConnList);
  647. LeaveCriticalSection(&g_csGlobalListLock);
  648. DeleteCriticalSection(&g_csGlobalListLock);
  649. #endif
  650. g_bInitialized = FALSE;
  651. return MCS_NO_ERROR;
  652. }
  653. /*
  654. * Response function for an MCS_CONNECT_PROVIDER_INDICATION callback. Result
  655. * values are as defined by T.125.
  656. */
  657. MCSError APIENTRY MCSConnectProviderResponse(
  658. DomainHandle hDomain,
  659. MCSResult Result,
  660. BYTE *pUserData,
  661. unsigned UserDataLen)
  662. {
  663. Domain *pDomain;
  664. MCSError MCSErr;
  665. ConnectProviderResponseIoctl CPrs;
  666. CheckInitialized("ConnectProvResponse()");
  667. pDomain = (Domain *) hDomain;
  668. // Lock the domain.
  669. EnterCriticalSection(&pDomain->csLock);
  670. // It is possible for us to call this function in the wrong state, e.g.
  671. // if we have gotten a disconnect very soon after processing a
  672. // connection, and have transitioned to State_Unconnected.
  673. if (pDomain->State != Dom_PendingCPResponse) {
  674. TraceOutIca(pDomain->hIca, "ConnectProvderResp(): in wrong state, "
  675. "ignoring call");
  676. MCSErr = MCS_NO_ERROR;
  677. goto PostLockDomain;
  678. }
  679. // TODO FUTURE: We are assuming an hConn in the Domain which is a hack
  680. // only for a single-connection system.
  681. MCSErr = SendConnectProviderResponse(pDomain,
  682. ((Connection *)pDomain->hConn)->hConnKernel,
  683. Result, pUserData, UserDataLen);
  684. if (MCSErr == MCS_NO_ERROR) {
  685. if (Result == RESULT_SUCCESSFUL)
  686. pDomain->State = Dom_Connected;
  687. else
  688. pDomain->State = Dom_Rejected;
  689. }
  690. PostLockDomain:
  691. // Release the domain.
  692. LeaveCriticalSection(&pDomain->csLock);
  693. return MCSErr;
  694. }
  695. // Utility function called internally as well as part of
  696. // MCSConnectProviderResponse(). Domain must be locked before calling.
  697. MCSError SendConnectProviderResponse(
  698. Domain *pDomain,
  699. ConnectionHandle hConnKernel,
  700. MCSResult Result,
  701. BYTE *pUserData,
  702. unsigned UserDataLen)
  703. {
  704. NTSTATUS Status;
  705. ConnectProviderResponseIoctl CPrs;
  706. // Transfer params.
  707. CPrs.Header.hUser = NULL; // Special value meaning node controller.
  708. CPrs.Header.Type = MCS_CONNECT_PROVIDER_RESPONSE;
  709. CPrs.hConn = hConnKernel;
  710. CPrs.Result = Result;
  711. CPrs.UserDataLength = UserDataLen;
  712. // Points to user data.
  713. if (UserDataLen)
  714. CPrs.pUserData = pUserData;
  715. // Call kernel mode. No callback is expected.
  716. Status = IcaStackIoControl(pDomain->hIcaStack, IOCTL_T120_REQUEST, &CPrs,
  717. sizeof(ConnectProviderResponseIoctl), NULL, 0, NULL);
  718. if (!NT_SUCCESS(Status)) {
  719. ErrOutIca(pDomain->hIca, "ConnectProvResponse(): Stack ioctl failed");
  720. return MCS_ALLOCATION_FAILURE;
  721. }
  722. TraceOutIca(pDomain->hIca, "Sent MCS_CONNECT_PROVIDER_RESPONSE");
  723. return MCS_NO_ERROR;
  724. }
  725. /*
  726. * Terminates an existing MCS connection or aborts a creation in-progress.
  727. */
  728. MCSError APIENTRY MCSDisconnectProviderRequest(
  729. HANDLE hIca,
  730. ConnectionHandle hConn,
  731. MCSReason Reason)
  732. {
  733. Domain *pDomain;
  734. NTSTATUS Status;
  735. MCSError MCSErr;
  736. Connection *pConn;
  737. DisconnectProviderRequestIoctl DPrq;
  738. CheckInitialized("DisconnectProviderReq()");
  739. pConn = (Connection *)hConn;
  740. pDomain = pConn->pDomain;
  741. // Lock the domain.
  742. EnterCriticalSection(&pDomain->csLock);
  743. // Transfer params.
  744. DPrq.Header.hUser = NULL; // Special meaning node controller.
  745. DPrq.Header.Type = MCS_DISCONNECT_PROVIDER_REQUEST;
  746. DPrq.hConn = pConn->hConnKernel;
  747. DPrq.Reason = Reason;
  748. // TODO FUTURE: Assumes only one connection.
  749. pDomain->hConn = NULL;
  750. pDomain->State = Dom_Unconnected;
  751. // TODO FUTURE: We do not deallocate the Connection. Unnecessary for now
  752. // since we are using a static Connection in the Domain, but will be an
  753. // issue when mallocing the Connection objects.
  754. #ifdef MCS_Future
  755. EnterCriticalSection(&g_csGlobalListLock);
  756. SListRemove(&g_ConnList, (unsigned)pConn, NULL);
  757. LeaveCriticalSection(&g_csGlobalListLock);
  758. Free(pConn);
  759. #endif
  760. // Call kernel mode. No callback is expected.
  761. Status = IcaStackIoControl(pDomain->hIcaStack, IOCTL_T120_REQUEST, &DPrq,
  762. sizeof(DPrq), NULL, 0, NULL);
  763. if (!NT_SUCCESS(Status)) {
  764. ErrOutIca(hIca, "DisconnectProvRequest(): Stack ioctl failed");
  765. MCSErr = MCS_ALLOCATION_FAILURE;
  766. goto PostLockDomain;
  767. }
  768. TraceOutIca(hIca, "Sent MCS_DISCONNECT_PROVIDER_REQUEST");
  769. MCSErr = MCS_NO_ERROR;
  770. PostLockDomain:
  771. // Release the domain.
  772. LeaveCriticalSection(&pDomain->csLock);
  773. return MCSErr;
  774. }
  775. /*
  776. *
  777. * CODE PAST THIS POINT IS FOR REFERENCE PURPOSES ONLY. IT IS NOT PART OF
  778. * THE PRODUCT.
  779. *
  780. */
  781. #if MCS_Future
  782. /*
  783. * Connects a local domain to a remote domain, merging the two domains.
  784. */
  785. MCSError APIENTRY MCSConnectProviderRequest(
  786. DomainSelector CallingDom,
  787. unsigned CallingDomLen,
  788. DomainSelector CalledDom,
  789. unsigned CalledDomLen,
  790. BOOL bUpwardConnection,
  791. PDomainParameters pDomParams,
  792. BYTE *UserData,
  793. unsigned UserDataLen,
  794. DomainHandle *phDomain,
  795. ConnectionHandle *phConn)
  796. {
  797. CheckInitialized("ConnectProvReq()");
  798. // Create new domain or look up already-created one.
  799. // call to kernel mode to set up the connection.
  800. ErrOut("ConnectProviderRequest: Not implemented");
  801. return MCS_COMMAND_NOT_SUPPORTED;
  802. }
  803. #endif // MCS_Future
  804. #if MCS_Future
  805. /*
  806. * Attaches a user SAP to an existing domain.
  807. */
  808. MCSError APIENTRY MCSAttachUserRequest(
  809. DomainHandle hDomain,
  810. MCSUserCallback UserCallback,
  811. MCSSendDataCallback SDCallback,
  812. void *UserDefined,
  813. UserHandle *phUser,
  814. unsigned *pMaxSendSize,
  815. BOOLEAN *pbCompleted)
  816. {
  817. Domain *pDomain;
  818. unsigned i, Err, OutBufSize, BytesReturned;
  819. NTSTATUS ntStatus;
  820. UserInformation *pUserInfo;
  821. AttachUserReturnIoctl AUrt;
  822. AttachUserRequestIoctl AUrq;
  823. CheckInitialized("AttachUserReq()");
  824. pDomain = (Domain *)hDomain;
  825. *pbCompleted = FALSE;
  826. // We must by this time have an hICA assigned to the domain.
  827. ASSERT(pDomain->hIca != NULL);
  828. // Make a new user-mode user information struct.
  829. *phUser = pUserInfo = Malloc(sizeof(UserInformation));
  830. if (pUserInfo == NULL) {
  831. ErrOutIca(pDomain->hIca, "AttachUserReq(): Alloc failure for "
  832. "user info");
  833. return MCS_ALLOCATION_FAILURE;
  834. }
  835. // Fill in the struct
  836. pUserInfo->Callback = UserCallback;
  837. pUserInfo->SDCallback = SDCallback;
  838. pUserInfo->UserDefined = UserDefined;
  839. pUserInfo->State = User_AttachConfirmPending;
  840. pUserInfo->hUserKernel = NULL; // Don't yet have kernel hUser assignment.
  841. pUserInfo->UserID = 0; // Don't yet have kernel UserID assignment.
  842. pUserInfo->pDomain = pDomain;
  843. SListInit(&pUserInfo->JoinedChannelList, DefaultNumChannels);
  844. // Transfer params for kernel-mode call.
  845. AUrq.Header.hUser = NULL;
  846. AUrq.Header.Type = MCS_ATTACH_USER_REQUEST;
  847. AUrq.UserDefined = UserDefined;
  848. // Add the user to the global UserInfo list.
  849. EnterCriticalSection(&g_csGlobalListLock);
  850. if (!SListAppend(&g_UserList, (unsigned)pUserInfo, pDomain)) {
  851. ErrOutIca(pDomain->hIca, "AttachUserReq(): Could not add user to "
  852. "user list");
  853. AUrt.MCSErr = MCS_ALLOCATION_FAILURE;
  854. LeaveCriticalSection(&g_csGlobalListLock);
  855. goto PostAlloc;
  856. }
  857. LeaveCriticalSection(&g_csGlobalListLock);
  858. // Issue the T120 request to kernel mode.
  859. ntStatus = IcaStackIoControl(pDomain->hIcaStack, IOCTL_T120_REQUEST,
  860. &AUrq, sizeof(AUrq), &AUrt, sizeof(AUrt), &BytesReturned);
  861. if (!NT_SUCCESS(ntStatus)) {
  862. ErrOutIca(pDomain->hIca, "AttachUserRequest(): T120 request failed");
  863. AUrt.MCSErr = MCS_ALLOCATION_FAILURE;
  864. goto PostAddList;
  865. }
  866. if (AUrt.MCSErr != MCS_NO_ERROR)
  867. goto PostAddList;
  868. pUserInfo->hUserKernel = AUrt.hUser;
  869. *phUser = pUserInfo;
  870. *pMaxSendSize = AUrt.MaxSendSize;
  871. pUserInfo->MaxSendSize = AUrt.MaxSendSize;
  872. if (AUrt.bCompleted) {
  873. pUserInfo->UserID = AUrt.UserID;
  874. *pbCompleted = TRUE;
  875. }
  876. return MCS_NO_ERROR;
  877. // Error handling.
  878. PostAddList:
  879. EnterCriticalSection(&g_csGlobalListLock);
  880. SListRemove(&g_UserList, (unsigned)pUserInfo, NULL);
  881. LeaveCriticalSection(&g_csGlobalListLock);
  882. PostAlloc:
  883. SListDestroy(&pUserInfo->JoinedChannelList);
  884. Free(pUserInfo);
  885. return AUrt.MCSErr;
  886. }
  887. #endif // MCS_Future
  888. #if MCS_Future
  889. UserID MCSGetUserIDFromHandle(UserHandle hUser)
  890. {
  891. return ((UserInformation *)hUser)->UserID;
  892. }
  893. #endif // MCS_Future
  894. #if MCS_Future
  895. /*
  896. * Detach a previously created user attachment from a domain.
  897. */
  898. MCSError APIENTRY MCSDetachUserRequest(UserHandle hUser)
  899. {
  900. Domain *pDomain;
  901. NTSTATUS Status;
  902. unsigned BytesReturned;
  903. MCSError MCSErr;
  904. MCSChannel *pMCSChannel;
  905. UserInformation *pUserInfo;
  906. DetachUserRequestIoctl DUrq;
  907. CheckInitialized("DetachUserReq()");
  908. pUserInfo = (UserInformation *)hUser;
  909. // Fill in detach-user request for sending to kernel mode.
  910. DUrq.Header.Type = MCS_DETACH_USER_REQUEST;
  911. DUrq.Header.hUser = pUserInfo->hUserKernel;
  912. // Call down to kernel mode, using the user attachment channel.
  913. // Issue the T120 request to kernel mode.
  914. Status = IcaStackIoControl(pUserInfo->pDomain->hIcaStack,
  915. IOCTL_T120_REQUEST, &DUrq, sizeof(DUrq), &MCSErr, sizeof(MCSErr),
  916. &BytesReturned);
  917. if (!NT_SUCCESS(Status)) {
  918. ErrOutIca(pDomain->hIca, "DetachUserRequest(): T120 request failed");
  919. return MCS_USER_NOT_ATTACHED;
  920. }
  921. if (MCSErr != MCS_NO_ERROR)
  922. return MCSErr;
  923. // Remove the hUser from the User list.
  924. EnterCriticalSection(&g_csGlobalListLock);
  925. SListRemove(&g_UserList, (unsigned)hUser, &pDomain);
  926. LeaveCriticalSection(&g_csGlobalListLock);
  927. if (pDomain == NULL)
  928. return MCS_NO_SUCH_USER;
  929. // Clean up contents of pUserInfo then free.
  930. DestroyUserInfo(pUserInfo);
  931. Free(pUserInfo);
  932. return MCS_NO_ERROR;
  933. }
  934. #endif // MCS_Future
  935. #if MCS_Future
  936. /*
  937. * Join a data channel. Once joined, an attachment can receive data sent on
  938. * that channel. Static channels are in range 1..1000 and can be joined by
  939. * any user. Dynamic channels are in range 1001..65535 and cannot be joined
  940. * unless they are convened by a user and the convenor allows this user
  941. * to be admitted, or the dynamic channel is a previously assigned channel
  942. * requested by a user attachment calling JoinRequest() with a channel ID
  943. * of 0.
  944. */
  945. MCSError APIENTRY MCSChannelJoinRequest(
  946. UserHandle hUser,
  947. ChannelID ChannelID,
  948. ChannelHandle *phChannel,
  949. BOOLEAN *pbCompleted)
  950. {
  951. unsigned Err, BytesReturned;
  952. NTSTATUS Status;
  953. MCSChannel *pMCSChannel;
  954. UserInformation *pUserInfo;
  955. ChannelJoinReturnIoctl CJrt;
  956. ChannelJoinRequestIoctl CJrq;
  957. CheckInitialized("ChannelJoinReq()");
  958. pUserInfo = (UserInformation *)hUser;
  959. *pbCompleted = FALSE;
  960. // Alloc a new user-mode channel struct.
  961. pMCSChannel = Malloc(sizeof(MCSChannel));
  962. if (pMCSChannel == NULL) {
  963. ErrOutIca(pUserInfo->pDomain->hIca, "ChannelJoinReq(): "
  964. "Could not alloc a user-mode channel");
  965. return MCS_ALLOCATION_FAILURE;
  966. }
  967. pMCSChannel->hChannelKernel = NULL;
  968. pMCSChannel->ChannelID = 0;
  969. // Add channel to user list of joined channels.
  970. if (!SListAppend(&pUserInfo->JoinedChannelList, (unsigned)pMCSChannel,
  971. pMCSChannel)) {
  972. ErrOutIca(pUserInfo->pDomain->hIca, "ChannelJoinReq(): "
  973. "Could not add channel to user mode user list");
  974. CJrt.MCSErr = MCS_ALLOCATION_FAILURE;
  975. goto PostAlloc;
  976. }
  977. // Transfer params for kernel mode call.
  978. CJrq.Header.hUser = pUserInfo->hUserKernel;
  979. CJrq.Header.Type = MCS_CHANNEL_JOIN_REQUEST;
  980. CJrq.ChannelID = ChannelID;
  981. // Issue the T120 request to kernel mode
  982. Status = IcaStackIoControl(pUserInfo->pDomain->hIcaStack,
  983. IOCTL_T120_REQUEST, &CJrq, sizeof(CJrq), &CJrt, sizeof(CJrt),
  984. &BytesReturned);
  985. if (!NT_SUCCESS(Status)) {
  986. ErrOutIca1(pUserInfo->pDomain->hIca, "ChannelJoinReq(): "
  987. "T120 request failed (%ld)", Status);
  988. CJrt.MCSErr = MCS_ALLOCATION_FAILURE;
  989. goto PostAddList;
  990. }
  991. if (CJrt.MCSErr != MCS_NO_ERROR)
  992. goto PostAddList;
  993. if (CJrt.bCompleted) {
  994. // Fill in the user-mode channel info.
  995. pMCSChannel->hChannelKernel = CJrt.hChannel;
  996. pMCSChannel->ChannelID = CJrt.ChannelID;
  997. *phChannel = pMCSChannel;
  998. *pbCompleted = TRUE;
  999. }
  1000. return MCS_NO_ERROR;
  1001. // Error handling.
  1002. PostAddList:
  1003. SListRemove(&pUserInfo->JoinedChannelList, (unsigned)pMCSChannel, NULL);
  1004. PostAlloc:
  1005. Free(pMCSChannel);
  1006. return CJrt.MCSErr;
  1007. }
  1008. #endif // MCS_Future
  1009. #if MCS_Future
  1010. /*
  1011. * Leave a data channel previously joined. Once unjoined, the user attachment
  1012. * does not receive data from that channel.
  1013. */
  1014. MCSError APIENTRY MCSChannelLeaveRequest(
  1015. UserHandle hUser,
  1016. ChannelHandle hChannel)
  1017. {
  1018. unsigned BytesReturned;
  1019. MCSError MCSErr;
  1020. NTSTATUS Status;
  1021. MCSChannel *pMCSChannel;
  1022. UserInformation *pUserInfo;
  1023. ChannelLeaveRequestIoctl CLrq;
  1024. CheckInitialized("ChannelLeaveReq()");
  1025. pUserInfo = (UserInformation *)hUser;
  1026. #if DBG
  1027. // Check that the indicated channel is actually present.
  1028. if (!SListGetByKey(&pUserInfo->JoinedChannelList, (unsigned)hChannel,
  1029. &pMCSChannel)) {
  1030. ErrOutIca(pUserInfo->pDomain->hIca, "ChannelLeaveReq(): "
  1031. "Given hChannel not present!");
  1032. return MCS_NO_SUCH_CHANNEL;
  1033. }
  1034. #endif
  1035. pMCSChannel = (MCSChannel *)hChannel;
  1036. // Transfer params.
  1037. CLrq.Header.Type = MCS_CHANNEL_LEAVE_REQUEST;
  1038. CLrq.Header.hUser = pUserInfo->hUserKernel;
  1039. CLrq.hChannel = pMCSChannel->hChannelKernel;
  1040. // Issue the T120 request to kernel mode
  1041. Status = IcaStackIoControl(pUserInfo->pDomain->hIcaStack,
  1042. IOCTL_T120_REQUEST, &CLrq, sizeof(CLrq), &MCSErr,
  1043. sizeof(MCSErr), &BytesReturned);
  1044. if (!NT_SUCCESS(Status)) {
  1045. ErrOutIca1(pUserInfo->pDomain->hIca, "ChannelLeaveReq(): "
  1046. "T120 request failed (%ld)", Status);
  1047. return MCS_ALLOCATION_FAILURE;
  1048. }
  1049. if (MCSErr != MCS_NO_ERROR)
  1050. return MCSErr;
  1051. // Remove the user-mode channel from the user list and destroy.
  1052. SListRemove(&pUserInfo->JoinedChannelList, (unsigned)pMCSChannel, NULL);
  1053. Free(pMCSChannel);
  1054. return MCS_NO_ERROR;
  1055. }
  1056. #endif // MCS_Future
  1057. /*
  1058. * Allocates a SendData buffer. This must be done by MCS to ensure high
  1059. * performance operation without memcpy()'s.
  1060. */
  1061. #if MCS_Future
  1062. MCSError APIENTRY MCSGetBufferRequest(
  1063. UserHandle hUser,
  1064. unsigned Size,
  1065. void **ppBuffer)
  1066. {
  1067. BYTE *pBuf;
  1068. CheckInitialized("GetBufferReq()");
  1069. // Leave sizeof(MCSSendDataRequestIoctl) in front of the block to bypass
  1070. // memcpy()'s during SendData.
  1071. pBuf = Malloc(sizeof(SendDataRequestIoctl) + Size);
  1072. if (pBuf == NULL) {
  1073. ErrOut("GetBufferReq(): Malloc failed");
  1074. return MCS_ALLOCATION_FAILURE;
  1075. }
  1076. *ppBuffer = pBuf + sizeof(SendDataRequestIoctl);
  1077. return MCS_NO_ERROR;
  1078. }
  1079. #endif // MCS_Future
  1080. /*
  1081. * Free a buffer allocated using GetBufferRequest(). This should only need to
  1082. * be done in the case where a buffer was allocated but never used.
  1083. * SendDataReq() automatically frees the buffer used before it returns.
  1084. */
  1085. #if MCS_Future
  1086. MCSError APIENTRY MCSFreeBufferRequest(UserHandle hUser, void *pBuffer)
  1087. {
  1088. CheckInitialized("FreeBufReq()");
  1089. ASSERT(pBuffer != NULL);
  1090. // Reverse the process done in GetBufferReq() above.
  1091. Free((BYTE *)pBuffer - sizeof(SendDataRequestIoctl));
  1092. return MCS_NO_ERROR;
  1093. }
  1094. #endif // MCS_Future
  1095. #if MCS_Future
  1096. /*
  1097. * Sends data on a channel. The channel need not have been joined before
  1098. * sending. The pData[] buffers must have been generated from successful
  1099. * calls to MCSGetBufferRequest(). After this call the pData[] are invalid;
  1100. * MCS will free them, and assign NULL to the contents in pData[].
  1101. */
  1102. MCSError APIENTRY MCSSendDataRequest(
  1103. UserHandle hUser,
  1104. DataRequestType RequestType,
  1105. ChannelHandle hChannel,
  1106. ChannelID ChannelID,
  1107. MCSPriority Priority,
  1108. Segmentation Segmentation,
  1109. BYTE *pData,
  1110. unsigned DataLength)
  1111. {
  1112. unsigned BytesReturned;
  1113. NTSTATUS Status;
  1114. MCSError MCSErr;
  1115. MCSChannel *pMCSChannel;
  1116. UserInformation *pUserInfo;
  1117. SendDataRequestIoctl *pSDrq;
  1118. CheckInitialized("SendDataReq()");
  1119. ASSERT(pData != NULL);
  1120. pUserInfo = (UserInformation *)hUser;
  1121. #if DBG
  1122. // Check against maximum send size allowed.
  1123. if (DataLength > pUserInfo->MaxSendSize) {
  1124. ErrOutIca(pUserInfo->pDomain->hIca, "SendDataReq(): Send size "
  1125. "exceeds negotiated domain maximum");
  1126. return MCS_SEND_SIZE_TOO_LARGE;
  1127. }
  1128. #endif
  1129. // Inbound buffer was allocated by GetBufferReq() with
  1130. // sizeof(MCSSendDataRequestIoctl) bytes at the beginning.
  1131. pSDrq = (SendDataRequestIoctl *)(pData - sizeof(SendDataRequestIoctl));
  1132. if (hChannel == NULL) {
  1133. // The user requested a send to a channel that it has not joined.
  1134. ASSERT(ChannelID >= 1 && ChannelID <= 65535);
  1135. // Forward the channel number to kernel mode for handling.
  1136. pSDrq->hChannel = NULL;
  1137. pSDrq->ChannelID = ChannelID;
  1138. }
  1139. else {
  1140. #if DBG
  1141. // Check that the indicated channel is actually present.
  1142. if (!SListGetByKey(&pUserInfo->JoinedChannelList, (unsigned)hChannel,
  1143. &pMCSChannel)) {
  1144. ErrOutIca(pUserInfo->pDomain->hIca, "SendDataReq(): "
  1145. "Given hChannel not joined by user!");
  1146. return MCS_NO_SUCH_CHANNEL;
  1147. }
  1148. #endif
  1149. pMCSChannel = (MCSChannel *)hChannel;
  1150. pSDrq->hChannel = pMCSChannel->hChannelKernel;
  1151. pSDrq->ChannelID = 0;
  1152. }
  1153. // Fill out data for sending to kernel mode.
  1154. pSDrq->Header.Type = (RequestType == NORMAL_SEND_DATA ?
  1155. MCS_SEND_DATA_REQUEST : MCS_UNIFORM_SEND_DATA_REQUEST);
  1156. pSDrq->Header.hUser = pUserInfo->hUserKernel;
  1157. pSDrq->RequestType = RequestType;
  1158. pSDrq->Priority = Priority;
  1159. pSDrq->Segmentation = Segmentation;
  1160. pSDrq->DataLength = DataLength;
  1161. // Issue the T120 request to kernel mode.
  1162. Status = IcaStackIoControl(pUserInfo->pDomain->hIcaStack,
  1163. IOCTL_T120_REQUEST, (BYTE *)pSDrq,
  1164. sizeof(SendDataRequestIoctl) + DataLength,
  1165. &MCSErr, sizeof(MCSErr), &BytesReturned);
  1166. if (!NT_SUCCESS(Status)) {
  1167. ErrOutIca1(pUserInfo->pDomain->hIca, "MCSSendDataReq(): "
  1168. "T120 request failed (%ld)", Status);
  1169. return MCS_ALLOCATION_FAILURE;
  1170. }
  1171. if (MCSErr != MCS_NO_ERROR)
  1172. return MCSErr;
  1173. // Buffer sent to kernel mode is copied as necessary. We free
  1174. // the memory after we are done.
  1175. MCSFreeBufferRequest(hUser, pData);
  1176. return MCS_NO_ERROR;
  1177. }
  1178. #endif // MCS_Future