/* (C) 1997-1999 Microsoft Corp. * * file : MCSCore.c * author : Erik Mavrinac * * description: MCS core manipulation code for actions that are common * between inbound PDU handling and API calls from upper components. */ #include "PreComp.h" #pragma hdrstop #include // Gets a new dynamic channel number, but does not add it to channel list. // Returns 0 if none available. ChannelID GetNewDynamicChannel(Domain *pDomain) { if (SListGetEntries(&pDomain->ChannelList) >= pDomain->DomParams.MaxChannels) return 0; pDomain->NextAvailDynChannel++; ASSERT(pDomain->NextAvailDynChannel <= 65535); return (pDomain->NextAvailDynChannel - 1); } /* * Common detach-user request code applicable to both net PDU requests and * local requests. * bDisconnect is FALSE if this is a normal detach where local attachments * are notified, nonzero if this a disconnection situation and the * attachment needs to be sent a detach-user indication with its own UserID. */ MCSError DetachUser( Domain *pDomain, UserHandle hUser, MCSReason Reason, BOOLEAN bDisconnect) { POUTBUF pOutBuf; BOOLEAN bChannelRemoved; NTSTATUS Status; MCSError MCSErr; MCSChannel *pMCSChannel; UserAttachment *pUA, *pCurUA; DetachUserIndication DUin; pUA = (UserAttachment *)hUser; TraceOut3(pDomain->pContext, "DetachUser() entry, pDomain=%X, hUser=%X, " "UserID=%X", pDomain, hUser, pUA->UserID); // Remove the user from all joined channels. SListResetIteration(&pUA->JoinedChannelList); while (SListIterate(&pUA->JoinedChannelList, (UINT_PTR *)&pMCSChannel, &pMCSChannel)) { MCSErr = ChannelLeave(hUser, pMCSChannel, &bChannelRemoved); ASSERT(MCSErr == MCS_NO_ERROR); } // Remove the requested hUser from the user attachments list. TraceOut(pDomain->pContext, "DetachUser(): Removing hUser from main list"); SListRemove(&pDomain->UserAttachmentList, (UINT_PTR)hUser, &pUA); if (pUA != NULL) { // Common callback information. DUin.UserID = pUA->UserID; DUin.Reason = Reason; } else { ErrOut(pDomain->pContext, "DetachUser: hUser is not a valid " "user attachment"); return MCS_NO_SUCH_USER; } if (bDisconnect) { // Send detach-user callback to user with its own ID. DUin.bSelf = TRUE; (pUA->Callback)(hUser, MCS_DETACH_USER_INDICATION, &DUin, pUA->UserDefined); } else if (!bDisconnect && SListGetEntries(&pDomain->UserAttachmentList)) { DUin.bSelf = FALSE; // Iterate the remaining local attachments and send the indication. // It is the caller's responsibility to inform downlevel connections // of the detachment. SListResetIteration(&pDomain->UserAttachmentList); while (SListIterate(&pDomain->UserAttachmentList, (UINT_PTR *)&hUser, &pCurUA)) { if (pCurUA->bLocal) (pCurUA->Callback)(hUser, MCS_DETACH_USER_INDICATION, &DUin, pCurUA->UserDefined); } // Reset again to keep callers from failing to iterate. SListResetIteration(&pDomain->UserAttachmentList); } // Remove UserID from channel list. It may no longer be there if the // user had joined the channel and the channel was purged above. TraceOut(pDomain->pContext, "DetachUser(): Removing UserID from main " "channel list"); SListRemove(&pDomain->ChannelList, pUA->UserID, &pMCSChannel); if (pMCSChannel) { // Consistency check -- the code above should have caught the channel // and destroyed it if the user had joined. Otherwise the channel // should be empty. ASSERT(SListGetEntries(&pMCSChannel->UserList) == 0); SListDestroy(&pMCSChannel->UserList); if (pMCSChannel->bPreallocated) pMCSChannel->bInUse = FALSE; else ExFreePool(pMCSChannel); } // Destruct and deallocate pUA. SListDestroy(&pUA->JoinedChannelList); if (pUA->bPreallocated) pUA->bInUse = FALSE; else ExFreePool(pUA); return MCS_NO_ERROR; } MCSError ChannelLeave( UserHandle hUser, ChannelHandle hChannel, BOOLEAN *pbChannelRemoved) { UserAttachment *pUA, *pUA_Channel; MCSChannel *pMCSChannel_UA, *pMCSChannel_Main; *pbChannelRemoved = FALSE; pUA = (UserAttachment *)hUser; pMCSChannel_Main = (MCSChannel *)hChannel; if (NULL == pMCSChannel_Main) { return MCS_NO_SUCH_CHANNEL; } // Remove the channel from the UA joined-channel list. TraceOut1(pUA->pDomain->pContext, "ChannelLeave(): Removing hChannel %X " "from main channel list", hChannel); SListRemove(&pUA->JoinedChannelList, (UINT_PTR)pMCSChannel_Main, &pMCSChannel_UA); ASSERT(pMCSChannel_UA == pMCSChannel_Main); // Consistency check. // Remove the hUser from the channel user list. TraceOut1(pUA->pDomain->pContext, "ChannelLeave(): Removing hUser %X " "from channel joined user list", hUser); SListRemove(&pMCSChannel_Main->UserList, (UINT_PTR)pUA, &pUA_Channel); ASSERT(pUA == pUA_Channel); // Consistency check. // Remove the channel from the main list if there are no more users joined. if (!SListGetEntries(&pMCSChannel_Main->UserList)) { TraceOut(pUA->pDomain->pContext, "ChannelLeave(): Removing channel " "from main channel list"); SListRemove(&pUA->pDomain->ChannelList, pMCSChannel_Main->ID, &pMCSChannel_Main); ASSERT(pMCSChannel_Main != NULL); if (pMCSChannel_Main == NULL) { return MCS_NO_SUCH_CHANNEL; } if (pMCSChannel_Main->bPreallocated) pMCSChannel_Main->bInUse = FALSE; else ExFreePool(pMCSChannel_Main); *pbChannelRemoved = TRUE; } if (!pUA->pDomain->bTopProvider) { // MCS FUTURE: Local lists are updated, forward the request to the // top provider. No confirm will be issued. } return MCS_NO_ERROR; } /* * Disconnect provider. Called on receipt of a disconnect-provider ultimatum * PDU, or when the connection is lost. bLocal is TRUE if this call is * for a local node controller call or lost connection, FALSE if for a * received PDU. */ NTSTATUS DisconnectProvider(Domain *pDomain, BOOLEAN bLocal, MCSReason Reason) { NTSTATUS Status; UserHandle hUser; UserAttachment *pUA; pDomain->State = State_Disconnected; TraceOut1(pDomain->pContext, "DisconnectProvider(): pDomain=%X", pDomain); // Search through the attached users list, launch detach-user indications. // For bLocal == FALSE, we do a regular-style detach for each nonlocal user. // Otherwise, we send a detach-user indication to each local user // attachment containing the attachment's own user ID, indicating that // it was forced out of the domain. // Multiple iterations: DetachUser also iterates UserAttachmentList, but // takes care to reset the iteration again after it is done. Since // removing a list entry resets the iteration, this is exactly what we // want. SListResetIteration(&pDomain->UserAttachmentList); while (SListIterate(&pDomain->UserAttachmentList, (UINT_PTR *)&hUser, &pUA)) { if (bLocal && pUA->bLocal) Status = DetachUser(pDomain, hUser, Reason, TRUE); else if (!bLocal && !pUA->bLocal) Status = DetachUser(pDomain, hUser, Reason, FALSE); } // We do not do any notification to either the local node controller or // sending across the net -- the caller is responsible for doing the // right thing. return STATUS_SUCCESS; } /* * Handles sending an OutBuf to the TD, including updating perf counters. * NOTE: This code is inlined into the critical MCSSendDataRequest() path * in MCSKAPI.c. Any changes here need to be reflected there. */ NTSTATUS SendOutBuf(Domain *pDomain, POUTBUF pOutBuf) { SD_RAWWRITE SdWrite; ASSERT(pOutBuf->ByteCount > 0); if (!pDomain->bCanSendData) { WarnOut1(pDomain->pContext, "%s: SendOutBuf(): Ignoring a send because " "ICA stack not connected", pDomain->StackClass == Stack_Primary ? "Primary" : (pDomain->StackClass == Stack_Shadow ? "Shadow" : "PassThru")); IcaBufferFree(pDomain->pContext, pOutBuf); return STATUS_SUCCESS; } // Fill out the raw write data. SdWrite.pBuffer = NULL; SdWrite.ByteCount = 0; SdWrite.pOutBuf = pOutBuf; // Increment protocol counters. pDomain->pStat->Output.WdFrames++; pDomain->pStat->Output.WdBytes += pOutBuf->ByteCount; // Send data to next driver in stack. return IcaCallNextDriver(pDomain->pContext, SD$RAWWRITE, &SdWrite); }