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.

637 lines
22 KiB

  1. /* (C) 1997-2000 Microsoft Corp.
  2. *
  3. * file : MCSKAPI.c
  4. * author : Erik Mavrinac
  5. *
  6. * description: Implementation of the kernel mode MCS API entry points.
  7. */
  8. #include "precomp.h"
  9. #pragma hdrstop
  10. #include <MCSImpl.h>
  11. /*
  12. * Main API entry point for the attach-user request primitive.
  13. * UserCallback and SDCallback must be non-NULL for external callers,
  14. * but there is no error checking or asserting done here to that
  15. * HandleAttachUserRequest() in DomPDU.c can call this API internally.
  16. */
  17. MCSError APIENTRY MCSAttachUserRequest(
  18. DomainHandle hDomain,
  19. MCSUserCallback UserCallback,
  20. MCSSendDataCallback SDCallback,
  21. void *UserDefined,
  22. UserHandle *phUser,
  23. unsigned *pMaxSendSize,
  24. BOOLEAN *pbCompleted)
  25. {
  26. NTSTATUS Status;
  27. MCSError MCSErr = MCS_NO_ERROR;
  28. Domain *pDomain;
  29. unsigned i;
  30. MCSChannel *pMCSChannel;
  31. UserAttachment *pUA;
  32. pDomain = (Domain *)hDomain;
  33. TraceOut(pDomain->pContext, "AttachUserRequest() entry");
  34. // Start with incomplete state.
  35. *pbCompleted = FALSE;
  36. // Check against domain max users param.
  37. if (SListGetEntries(&pDomain->UserAttachmentList) ==
  38. pDomain->DomParams.MaxUsers) {
  39. ErrOut(pDomain->pContext, "AttachUserReq(): Too many users");
  40. return MCS_TOO_MANY_USERS;
  41. }
  42. // Allocate a UserAttachment. Try the prealloc list first.
  43. pUA = NULL;
  44. for (i = 0; i < NumPreallocUA; i++) {
  45. if (!pDomain->PreallocUA[i].bInUse) {
  46. pUA = &pDomain->PreallocUA[i];
  47. pUA->bInUse = TRUE;
  48. }
  49. }
  50. if (pUA == NULL) {
  51. pUA = ExAllocatePoolWithTag(PagedPool, sizeof(UserAttachment),
  52. MCS_POOL_TAG);
  53. if (pUA != NULL)
  54. pUA->bPreallocated = FALSE;
  55. }
  56. if (pUA != NULL) {
  57. // Store info in UA.
  58. pUA->UserDefined = UserDefined;
  59. SListInit(&pUA->JoinedChannelList, DefaultNumChannels);
  60. pUA->pDomain = pDomain;
  61. pUA->Callback = UserCallback;
  62. pUA->SDCallback = SDCallback;
  63. }
  64. else {
  65. ErrOut(pDomain->pContext, "Could not alloc a UserAttachment");
  66. return MCS_ALLOCATION_FAILURE;
  67. }
  68. // If all these are NULL, it must be a remote caller since there is no way
  69. // to deliver any kind of indication or data
  70. if (UserDefined == NULL && UserCallback == NULL && SDCallback == NULL)
  71. pUA->bLocal = FALSE;
  72. else
  73. pUA->bLocal = TRUE;
  74. // Allocate new UserID.
  75. pUA->UserID = GetNewDynamicChannel(pDomain);
  76. if (pUA->UserID == 0) {
  77. ErrOut(pDomain->pContext, "AttachUser: Unable to get new dyn channel");
  78. MCSErr = MCS_TOO_MANY_CHANNELS;
  79. goto PostAllocUA;
  80. }
  81. // Allocate a Channel. Try the prealloc list first.
  82. pMCSChannel = NULL;
  83. for (i = 0; i < NumPreallocChannel; i++) {
  84. if (!pDomain->PreallocChannel[i].bInUse) {
  85. pMCSChannel = &pDomain->PreallocChannel[i];
  86. pMCSChannel->bInUse = TRUE;
  87. }
  88. }
  89. if (pMCSChannel == NULL) {
  90. pMCSChannel = ExAllocatePoolWithTag(PagedPool, sizeof(MCSChannel),
  91. MCS_POOL_TAG);
  92. if (pMCSChannel != NULL)
  93. pMCSChannel->bPreallocated = FALSE;
  94. }
  95. if (pMCSChannel != NULL) {
  96. pMCSChannel->Type = Channel_UserID;
  97. pMCSChannel->ID = pUA->UserID;
  98. SListInit(&pMCSChannel->UserList, DefaultNumChannels);
  99. }
  100. else {
  101. ErrOut(pDomain->pContext, "AttachUser: Unable to alloc channel");
  102. MCSErr = MCS_ALLOCATION_FAILURE;
  103. goto PostAllocUA;
  104. }
  105. if (SListAppend(&pDomain->ChannelList, pMCSChannel->ID, pMCSChannel)) {
  106. // Add user attachment to attachment list.
  107. if (SListAppend(&pDomain->UserAttachmentList, (UINT_PTR)pUA, pUA)) {
  108. // Return the hUser and MaxSendSize.
  109. *phUser = pUA;
  110. *pMaxSendSize = pDomain->MaxSendSize;
  111. }
  112. else {
  113. ErrOut(pDomain->pContext, "Unable to add user attachment to "
  114. "attachment list");
  115. MCSErr = MCS_ALLOCATION_FAILURE;
  116. goto PostAddChannel;
  117. }
  118. }
  119. else {
  120. ErrOut(pDomain->pContext, "AttachUser: Could not add channel "
  121. "to main list");
  122. MCSErr = MCS_ALLOCATION_FAILURE;
  123. goto PostAllocChannel;
  124. }
  125. if (pDomain->bTopProvider) {
  126. // The action is complete, there is no need for a callback.
  127. *pbCompleted = TRUE;
  128. }
  129. else {
  130. //MCS FUTURE: We have created the local structures, now forward
  131. // the request toward the top provider and return.
  132. // Note that *pbCompleted is FALSE meaning there is a callback pending.
  133. }
  134. return MCS_NO_ERROR;
  135. // Error handling.
  136. PostAddChannel:
  137. SListRemove(&pDomain->ChannelList, pMCSChannel->ID, NULL);
  138. PostAllocChannel:
  139. SListDestroy(&pMCSChannel->UserList);
  140. if (pMCSChannel->bPreallocated)
  141. pMCSChannel->bInUse = FALSE;
  142. else
  143. ExFreePool(pMCSChannel);
  144. PostAllocUA:
  145. SListDestroy(&pUA->JoinedChannelList);
  146. if (pUA->bPreallocated)
  147. pUA->bInUse = FALSE;
  148. else
  149. ExFreePool(pUA);
  150. return MCSErr;
  151. }
  152. /****************************************************************************/
  153. // MCSDetachUserRequest
  154. //
  155. // Detach-user kernel API.
  156. /****************************************************************************/
  157. MCSError APIENTRY MCSDetachUserRequest(UserHandle hUser)
  158. {
  159. MCSError rc;
  160. NTSTATUS Status;
  161. UserAttachment *pUA;
  162. POUTBUF pOutBuf;
  163. // hUser is pointer to user obj.
  164. ASSERT(hUser != NULL);
  165. pUA = (UserAttachment *)hUser;
  166. TraceOut1(pUA->pDomain->pContext, "DetachUserRequest() entry, hUser=%X",
  167. hUser);
  168. // Allocate an OutBuf for sending a DUin.
  169. Status = IcaBufferAlloc(pUA->pDomain->pContext, FALSE, TRUE,
  170. DUinPDUSize(1), NULL, &pOutBuf);
  171. if (Status == STATUS_SUCCESS) {
  172. // Send DUin to all downlevel connections.
  173. // MCS FUTURE: Since there is only one downlevel attachment just send it.
  174. // This will need to change to support multiple downlevel nodes.
  175. CreateDetachUserInd(REASON_USER_REQUESTED, 1, &pUA->UserID,
  176. pOutBuf->pBuffer);
  177. pOutBuf->ByteCount = DUinPDUSize(1);
  178. Status = SendOutBuf(pUA->pDomain, pOutBuf);
  179. if (NT_SUCCESS(Status)) {
  180. // Call central code in MCSCore.c.
  181. rc = DetachUser(pUA->pDomain, hUser, REASON_USER_REQUESTED,
  182. FALSE);
  183. }
  184. else {
  185. ErrOut(pUA->pDomain->pContext, "Problem sending DUin PDU to TD");
  186. rc = MCS_NETWORK_ERROR;
  187. }
  188. }
  189. else {
  190. ErrOut(pUA->pDomain->pContext, "Could not allocate an OutBuf for a "
  191. "DetachUser ind to a remote user");
  192. rc = MCS_ALLOCATION_FAILURE;
  193. }
  194. return rc;
  195. }
  196. UserID APIENTRY MCSGetUserIDFromHandle(UserHandle hUser)
  197. {
  198. ASSERT(hUser != NULL);
  199. return ((UserAttachment *)hUser)->UserID;
  200. }
  201. MCSError APIENTRY MCSChannelJoinRequest(
  202. UserHandle hUser,
  203. ChannelID ChID,
  204. ChannelHandle *phChannel,
  205. BOOLEAN *pbCompleted)
  206. {
  207. ChannelID ChannelIDToJoin;
  208. MCSChannel *pMCSChannel, *pChannelValue;
  209. UserAttachment *pUA;
  210. ASSERT(hUser != NULL);
  211. pUA = (UserAttachment *)hUser;
  212. TraceOut1(pUA->pDomain->pContext, "ChannelJoinRequest() entry, hUser=%X\n",
  213. hUser);
  214. // Start with request incomplete.
  215. *pbCompleted = FALSE;
  216. // Look for channel.
  217. if (SListGetByKey(&pUA->pDomain->ChannelList, ChID, &pMCSChannel)) {
  218. // The channel exists in the main channel list. Determine actions
  219. // by its type.
  220. ASSERT(pMCSChannel->ID == ChID); // Consistency check.
  221. switch (pMCSChannel->Type) {
  222. case Channel_UserID:
  223. if (pMCSChannel->ID == ChID) {
  224. // We will handle joining the user to the channel below.
  225. ChannelIDToJoin = ChID;
  226. }
  227. else {
  228. ErrOut(pUA->pDomain->pContext, "ChannelJoin: User "
  229. "attempted to join UserID channel not its own");
  230. return MCS_CANT_JOIN_OTHER_USER_CHANNEL;
  231. }
  232. break;
  233. case Channel_Static:
  234. //PASSTHRU
  235. case Channel_Assigned:
  236. // Assigned channels are like static channels when they exist.
  237. // We will handle below joining the user to the channel.
  238. ChannelIDToJoin = ChID;
  239. break;
  240. case Channel_Convened:
  241. //MCS FUTURE: Handle convened channels, incl. checking for
  242. // whether a user is admitted to the channel.
  243. return MCS_COMMAND_NOT_SUPPORTED;
  244. default:
  245. // Shouldn't happen. Includes Channel_Unused.
  246. ErrOut(pUA->pDomain->pContext, "ChannelJoin: Channel to join "
  247. "exists but is unknown type");
  248. ASSERT(FALSE);
  249. return MCS_ALLOCATION_FAILURE;
  250. }
  251. }
  252. else {
  253. // Channel did not already exist.
  254. int ChannelType;
  255. unsigned i;
  256. if (ChID > 1001) {
  257. // Not an assigned or static channel request. Error.
  258. ErrOut(pUA->pDomain->pContext, "ChannelJoin: Requested channel "
  259. "is not static or assigned");
  260. return MCS_NO_SUCH_CHANNEL;
  261. }
  262. // Check against domain params.
  263. if (SListGetEntries(&pUA->pDomain->ChannelList) >
  264. pUA->pDomain->DomParams.MaxChannels) {
  265. ErrOut(pUA->pDomain->pContext,
  266. "ChannelJoin: Too many channels already");
  267. return MCS_TOO_MANY_CHANNELS;
  268. }
  269. // Determine channel ID joined.
  270. if (ChID == 0) {
  271. ChannelIDToJoin = GetNewDynamicChannel(pUA->pDomain);
  272. if (ChannelIDToJoin == 0) {
  273. ErrOut(pUA->pDomain->pContext, "ChannelJoin: Unable to get "
  274. "new dyn channel");
  275. return MCS_TOO_MANY_CHANNELS;
  276. }
  277. ChannelType = Channel_Assigned;
  278. }
  279. else {
  280. ChannelIDToJoin = ChID; // Assume static.
  281. ChannelType = Channel_Static;
  282. }
  283. // Allocate, fill in, and add a new MCS channel with no UserIDs
  284. // joined (yet). Try the prealloc list first.
  285. pMCSChannel = NULL;
  286. for (i = 0; i < NumPreallocChannel; i++) {
  287. if (!pUA->pDomain->PreallocChannel[i].bInUse) {
  288. pMCSChannel = &pUA->pDomain->PreallocChannel[i];
  289. pMCSChannel->bInUse = TRUE;
  290. }
  291. }
  292. if (pMCSChannel == NULL) {
  293. pMCSChannel = ExAllocatePoolWithTag(PagedPool, sizeof(MCSChannel),
  294. MCS_POOL_TAG);
  295. if (pMCSChannel != NULL)
  296. pMCSChannel->bPreallocated = FALSE;
  297. }
  298. if (pMCSChannel != NULL) {
  299. pMCSChannel->ID = ChannelIDToJoin;
  300. pMCSChannel->Type = ChannelType;
  301. // pMCSChannel->UserList initialized below.
  302. }
  303. else {
  304. ErrOut(pUA->pDomain->pContext, "ChannelJoin: Could not allocate "
  305. "a new channel");
  306. return MCS_ALLOCATION_FAILURE;
  307. }
  308. if (!SListAppend(&pUA->pDomain->ChannelList, (unsigned)ChannelIDToJoin,
  309. pMCSChannel)) {
  310. ErrOut(pUA->pDomain->pContext, "ChannelJoin: Could not add "
  311. "channel to main list");
  312. if (pMCSChannel->bPreallocated)
  313. pMCSChannel->bInUse = FALSE;
  314. else
  315. ExFreePool(pMCSChannel);
  316. return MCS_ALLOCATION_FAILURE;
  317. }
  318. // Deferred for error handling above.
  319. SListInit(&pMCSChannel->UserList, DefaultNumUserAttachments);
  320. }
  321. // Check if this channel is already in the list, if already in, then we don't
  322. // add it to the lists again
  323. if (!SListGetByKey(&pUA->JoinedChannelList, (UINT_PTR)pMCSChannel, &pChannelValue)) {
  324. // Put channel into user's joined channel list.
  325. if (!SListAppend(&pUA->JoinedChannelList, (UINT_PTR)pMCSChannel,
  326. pMCSChannel)) {
  327. ErrOut(pUA->pDomain->pContext, "ChannelJoin: Could not add channel "
  328. "to user channel list");
  329. return MCS_ALLOCATION_FAILURE;
  330. }
  331. // Put user into channel's joined user list.
  332. if (!SListAppend(&pMCSChannel->UserList, (UINT_PTR)pUA, pUA)) {
  333. ErrOut(pUA->pDomain->pContext, "ChannelJoin: Could not user to "
  334. "channel user list");
  335. return MCS_ALLOCATION_FAILURE;
  336. }
  337. }
  338. else {
  339. ErrOut(pUA->pDomain->pContext, "ChannelJoin: Duplicate channel detected");
  340. return MCS_DUPLICATE_CHANNEL;
  341. }
  342. if (pUA->pDomain->bTopProvider) {
  343. // The join is complete.
  344. *pbCompleted = TRUE;
  345. }
  346. else {
  347. //MCS FUTURE: The user is now joined locally and all lists are updated.
  348. // Send the channel join upward toward the top provider.
  349. // Remember that *pbCompleted is still FALSE here indicating that
  350. // a future callback is to be expected when top provider responds.
  351. }
  352. *phChannel = pMCSChannel;
  353. return MCS_NO_ERROR;
  354. }
  355. MCSError APIENTRY MCSChannelLeaveRequest(
  356. UserHandle hUser,
  357. ChannelHandle hChannel)
  358. {
  359. BOOLEAN bChannelRemoved;
  360. ASSERT(hUser != NULL);
  361. TraceOut1(((UserAttachment *)hUser)->pDomain->pContext,
  362. "ChannelLeaveRequest() entry, hUser=%X", hUser);
  363. return ChannelLeave(hUser, hChannel, &bChannelRemoved);
  364. }
  365. ChannelID APIENTRY MCSGetChannelIDFromHandle(ChannelHandle hChannel)
  366. {
  367. return ((MCSChannel *)hChannel)->ID;
  368. }
  369. // pOutBuf->pBuffer should point to the user data inside the OutBuf allocated
  370. // area. SendDataReqPrefixBytes must be present before this point, and
  371. // SendDataReqSuffixBytes must be allocated after the user data.
  372. // pOutBuf->ByteCount should be the length of the user data.
  373. // The OutBuf is not deallocated when an error occurs.
  374. MCSError __fastcall MCSSendDataRequest(
  375. UserHandle hUser,
  376. ChannelHandle hChannel,
  377. DataRequestType RequestType,
  378. ChannelID ChannelID,
  379. MCSPriority Priority,
  380. Segmentation Segmentation,
  381. POUTBUF pOutBuf)
  382. {
  383. BOOLEAN bAnyNonlocalSends;
  384. MCSError MCSErr;
  385. NTSTATUS Status;
  386. unsigned SavedIterationState;
  387. MCSChannel *pMCSChannel;
  388. SD_RAWWRITE SdWrite;
  389. UserAttachment *pUA, *pCurUA;
  390. ASSERT(hUser != NULL);
  391. pUA = (UserAttachment *)hUser;
  392. // TraceOut1(pUA->pDomain->pContext, "SendDataRequest() entry, hUser=%X",
  393. // hUser);
  394. MCSErr = MCS_NO_ERROR;
  395. if (pUA->pDomain->bCanSendData) {
  396. if (hChannel != NULL) {
  397. pMCSChannel = (MCSChannel *)hChannel;
  398. ChannelID = pMCSChannel->ID;
  399. }
  400. else {
  401. // The user requested a send to a channel that it has not joined.
  402. // Find the channel by its ID.
  403. ASSERT(ChannelID >= 1 && ChannelID <= 65535);
  404. if (!SListGetByKey(&pUA->pDomain->ChannelList, ChannelID,
  405. &pMCSChannel)) {
  406. if (!pUA->pDomain->bTopProvider) {
  407. //MCS FUTURE: We did not find the channel, send to upward
  408. // connection since it may be in another part of the
  409. // hierarchy tree.
  410. }
  411. WarnOut(pUA->pDomain->pContext, "SendDataReq(): Unjoined "
  412. "channel send requested, channel not found, "
  413. "ignoring send");
  414. goto FreeOutBuf;
  415. }
  416. }
  417. }
  418. else {
  419. WarnOut1(pUA->pDomain->pContext, "%s: SendOutBuf(): Ignoring a send because "
  420. "ICA stack not connected",
  421. pUA->pDomain->StackClass == Stack_Primary ? "Primary" :
  422. (pUA->pDomain->StackClass == Stack_Shadow ? "Shadow" :
  423. "PassThru"));
  424. goto FreeOutBuf;
  425. }
  426. #if DBG
  427. // Check against maximum send size allowed.
  428. if (pOutBuf->ByteCount > pUA->pDomain->MaxSendSize) {
  429. ErrOut(pUA->pDomain->pContext, "SendDataReq(): Send size exceeds "
  430. "negotiated domain maximum");
  431. MCSErr = MCS_SEND_SIZE_TOO_LARGE;
  432. goto FreeOutBuf;
  433. }
  434. #endif
  435. #ifdef MCS_Future
  436. if (!pUA->pDomain->bTopProvider) {
  437. // MCS FUTURE: We are not the top provider. Send SDrq or USrq to
  438. // upward connection. We handle nonuniform send-data sending to local
  439. // and lower attachments below.
  440. if (RequestType == UNIFORM_SEND_DATA)
  441. goto FreeOutBuf;
  442. }
  443. #endif
  444. // Set up for iterating the users joined to the channel.
  445. // This includes preserving the iteration state of the domain-wide user
  446. // list in case we are being called from within a send-data indication.
  447. bAnyNonlocalSends = FALSE;
  448. SavedIterationState = pMCSChannel->UserList.Hdr.CurrOffset;
  449. // First send to local attachments, if any, skipping the sender.
  450. SListResetIteration(&pMCSChannel->UserList);
  451. while (SListIterate(&pMCSChannel->UserList, (UINT_PTR *)&pCurUA, &pCurUA)) {
  452. if (pCurUA != hUser) {
  453. if (!pCurUA->bLocal) {
  454. bAnyNonlocalSends = TRUE;
  455. }
  456. else {
  457. // Trigger callback to pCurUA user attachment.
  458. (pCurUA->SDCallback)(
  459. pOutBuf->pBuffer, // pData
  460. pOutBuf->ByteCount, // DataLength
  461. pCurUA->UserDefined, // UserDefined
  462. pCurUA, // hUser
  463. (BOOLEAN)(RequestType == UNIFORM_SEND_DATA), // bUniform
  464. pMCSChannel, // hChannel
  465. Priority,
  466. pCurUA->UserID, // SenderID
  467. Segmentation);
  468. }
  469. }
  470. }
  471. // Next send to lower attachments, if any.
  472. if (bAnyNonlocalSends) {
  473. // If we are sending more than 16383 bytes, we have to include ASN.1
  474. // segmentation. So, the header contains encoded the number of
  475. // 16K blocks (up to 3 in the maximum X.224 send size) encoded
  476. // first, then the 16K blocks, then another length determinant
  477. // for the size of the rest of the data, followed by the rest of the
  478. // data.
  479. // For the maximum X.224 send size, the maximum number of bytes for the
  480. // later length determinant is 2 bytes. For this purpose we require
  481. // the caller to add SendDataReqSuffixBytes at the end of the OutBuf,
  482. // so that we can shift outward the tail end of the data which
  483. // will always be the smallest chunk possible to move.
  484. // Create send-data indication PDU header first. Note that this will
  485. // only encode size of up to 3 16K blocks if the send size is
  486. // greater than 16383. The remainder we have to deal with.
  487. // MCS FUTURE: This is a top-provider-only solution, we send as
  488. // indication PDUs instead of request PDUs.
  489. CreateSendDataPDUHeader(RequestType == NORMAL_SEND_DATA ?
  490. MCS_SEND_DATA_INDICATION_ENUM :
  491. MCS_UNIFORM_SEND_DATA_INDICATION_ENUM,
  492. pUA->UserID, ChannelID, Priority, Segmentation,
  493. &pOutBuf->pBuffer, &pOutBuf->ByteCount);
  494. // MCS FUTURE: Remove #if if we need to handle send sizes > 16383.
  495. ASSERT(pOutBuf->ByteCount <= 16383);
  496. #ifdef MCS_Future
  497. // Check for ASN.1 segmentation requirement.
  498. if (pOutBuf->ByteCount > 16383) {
  499. // Now move memory around and create a new length determinant.
  500. BYTE LengthDet[2], *pLastSegment;
  501. unsigned Remainder, LengthEncoded, NBytesConsumed;
  502. BOOLEAN bLarge;
  503. Remainder = pOutBuf->ByteCount % 16384;
  504. EncodeLengthDeterminantPER(LengthDet, Remainder, &LengthEncoded,
  505. &bLarge, &NBytesConsumed);
  506. ASSERT(!bLarge);
  507. // NBytesConsumed now contains how much we have to shift the
  508. // last segment of the data.
  509. pLastSegment = pOutBuf->pBuffer + pOutBuf->ByteCount;
  510. RtlMoveMemory(pLastSegment + NBytesConsumed, pLastSegment,
  511. Remainder);
  512. pOutBuf->ByteCount += Remainder + NBytesConsumed;
  513. // Copy in the later length determinant (up to 2 bytes).
  514. *pLastSegment = LengthDet[0];
  515. if (NBytesConsumed == 2)
  516. *(pLastSegment + 1) = LengthDet[1];
  517. }
  518. #endif // 0
  519. // Send downward.
  520. //MCS FUTURE: Needs to change for multiple connections.
  521. SdWrite.pBuffer = NULL;
  522. SdWrite.ByteCount = 0;
  523. SdWrite.pOutBuf = pOutBuf;
  524. Status = IcaCallNextDriver(pUA->pDomain->pContext, SD$RAWWRITE,
  525. &SdWrite);
  526. if (NT_SUCCESS(Status)) {
  527. // Increment protocol counters.
  528. pUA->pDomain->pStat->Output.WdFrames++;
  529. pUA->pDomain->pStat->Output.WdBytes += pOutBuf->ByteCount;
  530. }
  531. else {
  532. ErrOut1(pUA->pDomain->pContext, "Problem sending SDin or USin "
  533. "PDU to TD, status=%X", Status);
  534. MCSErr = MCS_NETWORK_ERROR;
  535. // We do not free the OutBuf here, the TD is supposed to do it.
  536. }
  537. } else {
  538. IcaBufferFree(pUA->pDomain->pContext, pOutBuf);
  539. }
  540. // Restore the iteration state from before the call.
  541. pMCSChannel->UserList.Hdr.CurrOffset = SavedIterationState;
  542. return MCSErr;
  543. FreeOutBuf:
  544. IcaBufferFree(pUA->pDomain->pContext, pOutBuf);
  545. return MCSErr;
  546. }