Windows2003-3790/termsrv/drivers/rdp/pdmcs/mcskapi.c
2020-09-30 16:53:55 +02:00

638 lines
22 KiB
C

/* (C) 1997-2000 Microsoft Corp.
*
* file : MCSKAPI.c
* author : Erik Mavrinac
*
* description: Implementation of the kernel mode MCS API entry points.
*/
#include "precomp.h"
#pragma hdrstop
#include <MCSImpl.h>
/*
* Main API entry point for the attach-user request primitive.
* UserCallback and SDCallback must be non-NULL for external callers,
* but there is no error checking or asserting done here to that
* HandleAttachUserRequest() in DomPDU.c can call this API internally.
*/
MCSError APIENTRY MCSAttachUserRequest(
DomainHandle hDomain,
MCSUserCallback UserCallback,
MCSSendDataCallback SDCallback,
void *UserDefined,
UserHandle *phUser,
unsigned *pMaxSendSize,
BOOLEAN *pbCompleted)
{
NTSTATUS Status;
MCSError MCSErr = MCS_NO_ERROR;
Domain *pDomain;
unsigned i;
MCSChannel *pMCSChannel;
UserAttachment *pUA;
pDomain = (Domain *)hDomain;
TraceOut(pDomain->pContext, "AttachUserRequest() entry");
// Start with incomplete state.
*pbCompleted = FALSE;
// Check against domain max users param.
if (SListGetEntries(&pDomain->UserAttachmentList) ==
pDomain->DomParams.MaxUsers) {
ErrOut(pDomain->pContext, "AttachUserReq(): Too many users");
return MCS_TOO_MANY_USERS;
}
// Allocate a UserAttachment. Try the prealloc list first.
pUA = NULL;
for (i = 0; i < NumPreallocUA; i++) {
if (!pDomain->PreallocUA[i].bInUse) {
pUA = &pDomain->PreallocUA[i];
pUA->bInUse = TRUE;
}
}
if (pUA == NULL) {
pUA = ExAllocatePoolWithTag(PagedPool, sizeof(UserAttachment),
MCS_POOL_TAG);
if (pUA != NULL)
pUA->bPreallocated = FALSE;
}
if (pUA != NULL) {
// Store info in UA.
pUA->UserDefined = UserDefined;
SListInit(&pUA->JoinedChannelList, DefaultNumChannels);
pUA->pDomain = pDomain;
pUA->Callback = UserCallback;
pUA->SDCallback = SDCallback;
}
else {
ErrOut(pDomain->pContext, "Could not alloc a UserAttachment");
return MCS_ALLOCATION_FAILURE;
}
// If all these are NULL, it must be a remote caller since there is no way
// to deliver any kind of indication or data
if (UserDefined == NULL && UserCallback == NULL && SDCallback == NULL)
pUA->bLocal = FALSE;
else
pUA->bLocal = TRUE;
// Allocate new UserID.
pUA->UserID = GetNewDynamicChannel(pDomain);
if (pUA->UserID == 0) {
ErrOut(pDomain->pContext, "AttachUser: Unable to get new dyn channel");
MCSErr = MCS_TOO_MANY_CHANNELS;
goto PostAllocUA;
}
// Allocate a Channel. Try the prealloc list first.
pMCSChannel = NULL;
for (i = 0; i < NumPreallocChannel; i++) {
if (!pDomain->PreallocChannel[i].bInUse) {
pMCSChannel = &pDomain->PreallocChannel[i];
pMCSChannel->bInUse = TRUE;
}
}
if (pMCSChannel == NULL) {
pMCSChannel = ExAllocatePoolWithTag(PagedPool, sizeof(MCSChannel),
MCS_POOL_TAG);
if (pMCSChannel != NULL)
pMCSChannel->bPreallocated = FALSE;
}
if (pMCSChannel != NULL) {
pMCSChannel->Type = Channel_UserID;
pMCSChannel->ID = pUA->UserID;
SListInit(&pMCSChannel->UserList, DefaultNumChannels);
}
else {
ErrOut(pDomain->pContext, "AttachUser: Unable to alloc channel");
MCSErr = MCS_ALLOCATION_FAILURE;
goto PostAllocUA;
}
if (SListAppend(&pDomain->ChannelList, pMCSChannel->ID, pMCSChannel)) {
// Add user attachment to attachment list.
if (SListAppend(&pDomain->UserAttachmentList, (UINT_PTR)pUA, pUA)) {
// Return the hUser and MaxSendSize.
*phUser = pUA;
*pMaxSendSize = pDomain->MaxSendSize;
}
else {
ErrOut(pDomain->pContext, "Unable to add user attachment to "
"attachment list");
MCSErr = MCS_ALLOCATION_FAILURE;
goto PostAddChannel;
}
}
else {
ErrOut(pDomain->pContext, "AttachUser: Could not add channel "
"to main list");
MCSErr = MCS_ALLOCATION_FAILURE;
goto PostAllocChannel;
}
if (pDomain->bTopProvider) {
// The action is complete, there is no need for a callback.
*pbCompleted = TRUE;
}
else {
//MCS FUTURE: We have created the local structures, now forward
// the request toward the top provider and return.
// Note that *pbCompleted is FALSE meaning there is a callback pending.
}
return MCS_NO_ERROR;
// Error handling.
PostAddChannel:
SListRemove(&pDomain->ChannelList, pMCSChannel->ID, NULL);
PostAllocChannel:
SListDestroy(&pMCSChannel->UserList);
if (pMCSChannel->bPreallocated)
pMCSChannel->bInUse = FALSE;
else
ExFreePool(pMCSChannel);
PostAllocUA:
SListDestroy(&pUA->JoinedChannelList);
if (pUA->bPreallocated)
pUA->bInUse = FALSE;
else
ExFreePool(pUA);
return MCSErr;
}
/****************************************************************************/
// MCSDetachUserRequest
//
// Detach-user kernel API.
/****************************************************************************/
MCSError APIENTRY MCSDetachUserRequest(UserHandle hUser)
{
MCSError rc;
NTSTATUS Status;
UserAttachment *pUA;
POUTBUF pOutBuf;
// hUser is pointer to user obj.
ASSERT(hUser != NULL);
pUA = (UserAttachment *)hUser;
TraceOut1(pUA->pDomain->pContext, "DetachUserRequest() entry, hUser=%X",
hUser);
// Allocate an OutBuf for sending a DUin.
Status = IcaBufferAlloc(pUA->pDomain->pContext, FALSE, TRUE,
DUinPDUSize(1), NULL, &pOutBuf);
if (Status == STATUS_SUCCESS) {
// Send DUin to all downlevel connections.
// MCS FUTURE: Since there is only one downlevel attachment just send it.
// This will need to change to support multiple downlevel nodes.
CreateDetachUserInd(REASON_USER_REQUESTED, 1, &pUA->UserID,
pOutBuf->pBuffer);
pOutBuf->ByteCount = DUinPDUSize(1);
Status = SendOutBuf(pUA->pDomain, pOutBuf);
if (NT_SUCCESS(Status)) {
// Call central code in MCSCore.c.
rc = DetachUser(pUA->pDomain, hUser, REASON_USER_REQUESTED,
FALSE);
}
else {
ErrOut(pUA->pDomain->pContext, "Problem sending DUin PDU to TD");
rc = MCS_NETWORK_ERROR;
}
}
else {
ErrOut(pUA->pDomain->pContext, "Could not allocate an OutBuf for a "
"DetachUser ind to a remote user");
rc = MCS_ALLOCATION_FAILURE;
}
return rc;
}
UserID APIENTRY MCSGetUserIDFromHandle(UserHandle hUser)
{
ASSERT(hUser != NULL);
return ((UserAttachment *)hUser)->UserID;
}
MCSError APIENTRY MCSChannelJoinRequest(
UserHandle hUser,
ChannelID ChID,
ChannelHandle *phChannel,
BOOLEAN *pbCompleted)
{
ChannelID ChannelIDToJoin;
MCSChannel *pMCSChannel, *pChannelValue;
UserAttachment *pUA;
ASSERT(hUser != NULL);
pUA = (UserAttachment *)hUser;
TraceOut1(pUA->pDomain->pContext, "ChannelJoinRequest() entry, hUser=%X\n",
hUser);
// Start with request incomplete.
*pbCompleted = FALSE;
// Look for channel.
if (SListGetByKey(&pUA->pDomain->ChannelList, ChID, &pMCSChannel)) {
// The channel exists in the main channel list. Determine actions
// by its type.
ASSERT(pMCSChannel->ID == ChID); // Consistency check.
switch (pMCSChannel->Type) {
case Channel_UserID:
if (pMCSChannel->ID == ChID) {
// We will handle joining the user to the channel below.
ChannelIDToJoin = ChID;
}
else {
ErrOut(pUA->pDomain->pContext, "ChannelJoin: User "
"attempted to join UserID channel not its own");
return MCS_CANT_JOIN_OTHER_USER_CHANNEL;
}
break;
case Channel_Static:
//PASSTHRU
case Channel_Assigned:
// Assigned channels are like static channels when they exist.
// We will handle below joining the user to the channel.
ChannelIDToJoin = ChID;
break;
case Channel_Convened:
//MCS FUTURE: Handle convened channels, incl. checking for
// whether a user is admitted to the channel.
return MCS_COMMAND_NOT_SUPPORTED;
default:
// Shouldn't happen. Includes Channel_Unused.
ErrOut(pUA->pDomain->pContext, "ChannelJoin: Channel to join "
"exists but is unknown type");
ASSERT(FALSE);
return MCS_ALLOCATION_FAILURE;
}
}
else {
// Channel did not already exist.
int ChannelType;
unsigned i;
if (ChID > 1001) {
// Not an assigned or static channel request. Error.
ErrOut(pUA->pDomain->pContext, "ChannelJoin: Requested channel "
"is not static or assigned");
return MCS_NO_SUCH_CHANNEL;
}
// Check against domain params.
if (SListGetEntries(&pUA->pDomain->ChannelList) >
pUA->pDomain->DomParams.MaxChannels) {
ErrOut(pUA->pDomain->pContext,
"ChannelJoin: Too many channels already");
return MCS_TOO_MANY_CHANNELS;
}
// Determine channel ID joined.
if (ChID == 0) {
ChannelIDToJoin = GetNewDynamicChannel(pUA->pDomain);
if (ChannelIDToJoin == 0) {
ErrOut(pUA->pDomain->pContext, "ChannelJoin: Unable to get "
"new dyn channel");
return MCS_TOO_MANY_CHANNELS;
}
ChannelType = Channel_Assigned;
}
else {
ChannelIDToJoin = ChID; // Assume static.
ChannelType = Channel_Static;
}
// Allocate, fill in, and add a new MCS channel with no UserIDs
// joined (yet). Try the prealloc list first.
pMCSChannel = NULL;
for (i = 0; i < NumPreallocChannel; i++) {
if (!pUA->pDomain->PreallocChannel[i].bInUse) {
pMCSChannel = &pUA->pDomain->PreallocChannel[i];
pMCSChannel->bInUse = TRUE;
}
}
if (pMCSChannel == NULL) {
pMCSChannel = ExAllocatePoolWithTag(PagedPool, sizeof(MCSChannel),
MCS_POOL_TAG);
if (pMCSChannel != NULL)
pMCSChannel->bPreallocated = FALSE;
}
if (pMCSChannel != NULL) {
pMCSChannel->ID = ChannelIDToJoin;
pMCSChannel->Type = ChannelType;
// pMCSChannel->UserList initialized below.
}
else {
ErrOut(pUA->pDomain->pContext, "ChannelJoin: Could not allocate "
"a new channel");
return MCS_ALLOCATION_FAILURE;
}
if (!SListAppend(&pUA->pDomain->ChannelList, (unsigned)ChannelIDToJoin,
pMCSChannel)) {
ErrOut(pUA->pDomain->pContext, "ChannelJoin: Could not add "
"channel to main list");
if (pMCSChannel->bPreallocated)
pMCSChannel->bInUse = FALSE;
else
ExFreePool(pMCSChannel);
return MCS_ALLOCATION_FAILURE;
}
// Deferred for error handling above.
SListInit(&pMCSChannel->UserList, DefaultNumUserAttachments);
}
// Check if this channel is already in the list, if already in, then we don't
// add it to the lists again
if (!SListGetByKey(&pUA->JoinedChannelList, (UINT_PTR)pMCSChannel, &pChannelValue)) {
// Put channel into user's joined channel list.
if (!SListAppend(&pUA->JoinedChannelList, (UINT_PTR)pMCSChannel,
pMCSChannel)) {
ErrOut(pUA->pDomain->pContext, "ChannelJoin: Could not add channel "
"to user channel list");
return MCS_ALLOCATION_FAILURE;
}
// Put user into channel's joined user list.
if (!SListAppend(&pMCSChannel->UserList, (UINT_PTR)pUA, pUA)) {
ErrOut(pUA->pDomain->pContext, "ChannelJoin: Could not user to "
"channel user list");
return MCS_ALLOCATION_FAILURE;
}
}
else {
ErrOut(pUA->pDomain->pContext, "ChannelJoin: Duplicate channel detected");
return MCS_DUPLICATE_CHANNEL;
}
if (pUA->pDomain->bTopProvider) {
// The join is complete.
*pbCompleted = TRUE;
}
else {
//MCS FUTURE: The user is now joined locally and all lists are updated.
// Send the channel join upward toward the top provider.
// Remember that *pbCompleted is still FALSE here indicating that
// a future callback is to be expected when top provider responds.
}
*phChannel = pMCSChannel;
return MCS_NO_ERROR;
}
MCSError APIENTRY MCSChannelLeaveRequest(
UserHandle hUser,
ChannelHandle hChannel)
{
BOOLEAN bChannelRemoved;
ASSERT(hUser != NULL);
TraceOut1(((UserAttachment *)hUser)->pDomain->pContext,
"ChannelLeaveRequest() entry, hUser=%X", hUser);
return ChannelLeave(hUser, hChannel, &bChannelRemoved);
}
ChannelID APIENTRY MCSGetChannelIDFromHandle(ChannelHandle hChannel)
{
return ((MCSChannel *)hChannel)->ID;
}
// pOutBuf->pBuffer should point to the user data inside the OutBuf allocated
// area. SendDataReqPrefixBytes must be present before this point, and
// SendDataReqSuffixBytes must be allocated after the user data.
// pOutBuf->ByteCount should be the length of the user data.
// The OutBuf is not deallocated when an error occurs.
MCSError __fastcall MCSSendDataRequest(
UserHandle hUser,
ChannelHandle hChannel,
DataRequestType RequestType,
ChannelID ChannelID,
MCSPriority Priority,
Segmentation Segmentation,
POUTBUF pOutBuf)
{
BOOLEAN bAnyNonlocalSends;
MCSError MCSErr;
NTSTATUS Status;
unsigned SavedIterationState;
MCSChannel *pMCSChannel;
SD_RAWWRITE SdWrite;
UserAttachment *pUA, *pCurUA;
ASSERT(hUser != NULL);
pUA = (UserAttachment *)hUser;
// TraceOut1(pUA->pDomain->pContext, "SendDataRequest() entry, hUser=%X",
// hUser);
MCSErr = MCS_NO_ERROR;
if (pUA->pDomain->bCanSendData) {
if (hChannel != NULL) {
pMCSChannel = (MCSChannel *)hChannel;
ChannelID = pMCSChannel->ID;
}
else {
// The user requested a send to a channel that it has not joined.
// Find the channel by its ID.
ASSERT(ChannelID >= 1 && ChannelID <= 65535);
if (!SListGetByKey(&pUA->pDomain->ChannelList, ChannelID,
&pMCSChannel)) {
if (!pUA->pDomain->bTopProvider) {
//MCS FUTURE: We did not find the channel, send to upward
// connection since it may be in another part of the
// hierarchy tree.
}
WarnOut(pUA->pDomain->pContext, "SendDataReq(): Unjoined "
"channel send requested, channel not found, "
"ignoring send");
goto FreeOutBuf;
}
}
}
else {
WarnOut1(pUA->pDomain->pContext, "%s: SendOutBuf(): Ignoring a send because "
"ICA stack not connected",
pUA->pDomain->StackClass == Stack_Primary ? "Primary" :
(pUA->pDomain->StackClass == Stack_Shadow ? "Shadow" :
"PassThru"));
goto FreeOutBuf;
}
#if DBG
// Check against maximum send size allowed.
if (pOutBuf->ByteCount > pUA->pDomain->MaxSendSize) {
ErrOut(pUA->pDomain->pContext, "SendDataReq(): Send size exceeds "
"negotiated domain maximum");
MCSErr = MCS_SEND_SIZE_TOO_LARGE;
goto FreeOutBuf;
}
#endif
#ifdef MCS_Future
if (!pUA->pDomain->bTopProvider) {
// MCS FUTURE: We are not the top provider. Send SDrq or USrq to
// upward connection. We handle nonuniform send-data sending to local
// and lower attachments below.
if (RequestType == UNIFORM_SEND_DATA)
goto FreeOutBuf;
}
#endif
// Set up for iterating the users joined to the channel.
// This includes preserving the iteration state of the domain-wide user
// list in case we are being called from within a send-data indication.
bAnyNonlocalSends = FALSE;
SavedIterationState = pMCSChannel->UserList.Hdr.CurrOffset;
// First send to local attachments, if any, skipping the sender.
SListResetIteration(&pMCSChannel->UserList);
while (SListIterate(&pMCSChannel->UserList, (UINT_PTR *)&pCurUA, &pCurUA)) {
if (pCurUA != hUser) {
if (!pCurUA->bLocal) {
bAnyNonlocalSends = TRUE;
}
else {
// Trigger callback to pCurUA user attachment.
(pCurUA->SDCallback)(
pOutBuf->pBuffer, // pData
pOutBuf->ByteCount, // DataLength
pCurUA->UserDefined, // UserDefined
pCurUA, // hUser
(BOOLEAN)(RequestType == UNIFORM_SEND_DATA), // bUniform
pMCSChannel, // hChannel
Priority,
pCurUA->UserID, // SenderID
Segmentation);
}
}
}
// Next send to lower attachments, if any.
if (bAnyNonlocalSends) {
// If we are sending more than 16383 bytes, we have to include ASN.1
// segmentation. So, the header contains encoded the number of
// 16K blocks (up to 3 in the maximum X.224 send size) encoded
// first, then the 16K blocks, then another length determinant
// for the size of the rest of the data, followed by the rest of the
// data.
// For the maximum X.224 send size, the maximum number of bytes for the
// later length determinant is 2 bytes. For this purpose we require
// the caller to add SendDataReqSuffixBytes at the end of the OutBuf,
// so that we can shift outward the tail end of the data which
// will always be the smallest chunk possible to move.
// Create send-data indication PDU header first. Note that this will
// only encode size of up to 3 16K blocks if the send size is
// greater than 16383. The remainder we have to deal with.
// MCS FUTURE: This is a top-provider-only solution, we send as
// indication PDUs instead of request PDUs.
CreateSendDataPDUHeader(RequestType == NORMAL_SEND_DATA ?
MCS_SEND_DATA_INDICATION_ENUM :
MCS_UNIFORM_SEND_DATA_INDICATION_ENUM,
pUA->UserID, ChannelID, Priority, Segmentation,
&pOutBuf->pBuffer, &pOutBuf->ByteCount);
// MCS FUTURE: Remove #if if we need to handle send sizes > 16383.
ASSERT(pOutBuf->ByteCount <= 16383);
#ifdef MCS_Future
// Check for ASN.1 segmentation requirement.
if (pOutBuf->ByteCount > 16383) {
// Now move memory around and create a new length determinant.
BYTE LengthDet[2], *pLastSegment;
unsigned Remainder, LengthEncoded, NBytesConsumed;
BOOLEAN bLarge;
Remainder = pOutBuf->ByteCount % 16384;
EncodeLengthDeterminantPER(LengthDet, Remainder, &LengthEncoded,
&bLarge, &NBytesConsumed);
ASSERT(!bLarge);
// NBytesConsumed now contains how much we have to shift the
// last segment of the data.
pLastSegment = pOutBuf->pBuffer + pOutBuf->ByteCount;
RtlMoveMemory(pLastSegment + NBytesConsumed, pLastSegment,
Remainder);
pOutBuf->ByteCount += Remainder + NBytesConsumed;
// Copy in the later length determinant (up to 2 bytes).
*pLastSegment = LengthDet[0];
if (NBytesConsumed == 2)
*(pLastSegment + 1) = LengthDet[1];
}
#endif // 0
// Send downward.
//MCS FUTURE: Needs to change for multiple connections.
SdWrite.pBuffer = NULL;
SdWrite.ByteCount = 0;
SdWrite.pOutBuf = pOutBuf;
Status = IcaCallNextDriver(pUA->pDomain->pContext, SD$RAWWRITE,
&SdWrite);
if (NT_SUCCESS(Status)) {
// Increment protocol counters.
pUA->pDomain->pStat->Output.WdFrames++;
pUA->pDomain->pStat->Output.WdBytes += pOutBuf->ByteCount;
}
else {
ErrOut1(pUA->pDomain->pContext, "Problem sending SDin or USin "
"PDU to TD, status=%X", Status);
MCSErr = MCS_NETWORK_ERROR;
// We do not free the OutBuf here, the TD is supposed to do it.
}
} else {
IcaBufferFree(pUA->pDomain->pContext, pOutBuf);
}
// Restore the iteration state from before the call.
pMCSChannel->UserList.Hdr.CurrOffset = SavedIterationState;
return MCSErr;
FreeOutBuf:
IcaBufferFree(pUA->pDomain->pContext, pOutBuf);
return MCSErr;
}