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
637 lines
22 KiB
/* (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;
|
|
}
|
|
|