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.
 
 
 
 
 
 

1566 lines
54 KiB

/* (C) 1997-1999 Microsoft Corp.
*
* file : DomPDU.c
* author : Erik Mavrinac
*
* description: Encode/decode functions for MCS domain PDUs. Domain PDUs are
* encoded with ASN.1 packed encoding rules (PER). Included in this file
* are local functions to PER-decode and -encode various types used in MCS
* PDUs. Note that this implementation follows closely the T.122/T.125 LITE
* specification published by the IMTC, reducing the number of fully-
* implemented code paths and providing default behavior for unimplemented
* functions.
*
* NOTE: Bit numbers used in comments are decoded as follows:
*
* Byte: 0101 1001 ( = 0x59)
* Bit: 7654 3210
*
* History:
* 11-Aug-97 jparsons Set byte counts for outbufs.
*/
#include "precomp.h"
#pragma hdrstop
#include <MCSImpl.h>
#include "domain.h"
/*
* Prototypes for unpacking functions, defined across the indicated files.
*/
// Defined below.
BOOLEAN __fastcall HandlePlumbDomainInd(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleErectDomainReq(PDomain, BYTE *, unsigned, unsigned *);
// Defined in MergePDU.c.
BOOLEAN __fastcall HandleMergeChannelsReq(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleMergeChannelsCon(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandlePurgeChannelsInd(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleMergeTokensReq(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleMergeTokensCon(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandlePurgeTokensInd(PDomain, BYTE *, unsigned, unsigned *);
// Defined below.
BOOLEAN __fastcall HandleDisconnectProviderUlt(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleRejectMCSPDUUlt(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleAttachUserReq(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleAttachUserCon(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleDetachUserReq(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleDetachUserInd(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleChannelJoinReq(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleChannelJoinCon(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleChannelLeaveReq(PDomain, BYTE *, unsigned, unsigned *);
// Defined in CnvChPDU.c.
BOOLEAN __fastcall HandleChannelConveneReq(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleChannelConveneCon(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleChannelDisbandReq(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleChannelDisbandInd(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleChannelAdmitReq(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleChannelAdmitInd(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleChannelExpelReq(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleChannelExpelInd(PDomain, BYTE *, unsigned, unsigned *);
// Defined below (prototype in MCSImpl.h for visibility in Decode.c).
//BOOLEAN __fastcall HandleAllSendDataPDUs(PDomain, BYTE *, unsigned, unsigned *);
// Defined in TokenPDU.c.
BOOLEAN __fastcall HandleTokenGrabReq(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleTokenGrabCon(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleTokenInhibitReq(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleTokenInhibitCon(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleTokenGiveReq(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleTokenGiveInd(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleTokenGiveRes(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleTokenGiveCon(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleTokenPleaseReq(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleTokenPleaseInd(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleTokenReleaseReq(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleTokenReleaseCon(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleTokenTestReq(PDomain, BYTE *, unsigned, unsigned *);
BOOLEAN __fastcall HandleTokenTestCon(PDomain, BYTE *, unsigned, unsigned *);
/*
* These are listed in the 0-based enumeration order specified in the T.125
* spec. Decode the 6-bit PER encoded PDU type enumeration bits and cast to
* an index into this table to get the info.
*/
const MCSPDUInfo DomainPDUTable[] =
{
// 0
StrOnDbg("Plumb Domain Indication", NULL /* HandlePlumbDomainInd */),
StrOnDbg("Erect Domain Request", HandleErectDomainReq),
StrOnDbg("Merge Channels Request", NULL /* HandleMergeChannelsReq */),
StrOnDbg("Merge Channels Confirm", NULL /* HandleMergeChannelsCon */),
StrOnDbg("Purge Channels Indication", NULL /* HandlePurgeChannelsInd */),
// 5
StrOnDbg("Merge Tokens Request", NULL /* HandleMergeTokensReq */),
StrOnDbg("Merge Tokens Confirm", NULL /* HandleMergeTokensCon */),
StrOnDbg("Purge Tokens Indication", NULL /* HandlePurgeTokensInd */),
StrOnDbg("Disconnect Provider Ultimatum", HandleDisconnectProviderUlt),
StrOnDbg("Reject MCS PDU Ultimatum", NULL /*HandleRejectMCSPDUUlt */),
// 10
StrOnDbg("Attach User Request", HandleAttachUserReq),
StrOnDbg("Attach User Confirm", NULL /* HandleAttachUserCon */),
StrOnDbg("Detach User Request", NULL /* HandleDetachUserReq */),
StrOnDbg("Detach User Indication", NULL /* HandleDetachUserInd */),
StrOnDbg("Channel Join Request", HandleChannelJoinReq),
// 15
StrOnDbg("Channel Join Confirm", NULL /* HandleChannelJoinCon */),
StrOnDbg("Channel Leave Request", NULL /* HandleChannelLeaveReq */),
StrOnDbg("Channel Convene Request", NULL /* HandleChannelConveneReq */),
StrOnDbg("Channel Convene Confirm", NULL /* HandleChannelConveneCon */),
StrOnDbg("Channel Disband Request", NULL /* HandleChannelDisbandReq */),
// 20
StrOnDbg("Channel Disband Indication", NULL /* HandleChannelDisbandInd */),
StrOnDbg("Channel Admit Request", NULL /* HandleChannelAdmitReq */),
StrOnDbg("Channel Admit Indication", NULL /* HandleChannelAdmitInd */),
StrOnDbg("Channel Expel Request", NULL /* HandleChannelExpelReq */),
StrOnDbg("Channel Expel Indication", NULL /* HandleChannelExpelInd */),
// 25
StrOnDbg("Send Data Request", HandleAllSendDataPDUs),
StrOnDbg("Send Data Indication", HandleAllSendDataPDUs),
StrOnDbg("Uniform Send Data Request", HandleAllSendDataPDUs),
StrOnDbg("Uniform Send Data Indication", HandleAllSendDataPDUs),
StrOnDbg("Token Grab Request", NULL /* HandleTokenGrabReq */),
// 30
StrOnDbg("Token Grab Confirm", NULL /* HandleTokenGrabCon */),
StrOnDbg("Token Inhibit Request", NULL /* HandleTokenInhibitReq */),
StrOnDbg("Token Inhibit Confirm", NULL /* HandleTokenInhibitCon */),
StrOnDbg("Token Give Request", NULL /* HandleTokenGiveReq */),
StrOnDbg("Token Give Indication", NULL /* HandleTokenGiveInd */),
// 35
StrOnDbg("Token Give Response", NULL /* HandleTokenGiveRes */),
StrOnDbg("Token Give Confirm", NULL /* HandleTokenGiveCon */),
StrOnDbg("Token Please Request", NULL /* HandleTokenPleaseReq */),
StrOnDbg("Token Please Indication", NULL /* HandleTokenPleaseInd */),
StrOnDbg("Token Release Request", NULL /* HandleTokenReleaseReq */),
// 40
StrOnDbg("Token Release Confirm", NULL /* HandleTokenReleaseCon */),
StrOnDbg("Token Test Request", NULL /* HandleTokenTestReq */),
StrOnDbg("Token Test Confirm", NULL /* HandleTokenTestCon */)
};
/*
* Returns the number of bytes total that will be used to encode this length.
*/
int GetTotalLengthDeterminantEncodingSize(int Length) {
int N16KBlocks;
if (Length <= 127) return 1;
if (Length <= 16383) return 2;
N16KBlocks = Length / 16384;
if (N16KBlocks > 4) N16KBlocks = 4;
// 1 byte for # 16K blocks up to 4, then remainder encoded separately.
return 1 + GetTotalLengthDeterminantEncodingSize(Length - N16KBlocks *
16384);
}
/*
* Encodes a length determinant.
* Note that bit references here are in the range 7..0 where 7 is the high bit.
* The ASN.1 spec uses 8..1.
*/
void __fastcall EncodeLengthDeterminantPER(
BYTE *pBuffer, // [IN], where to encode.
unsigned Length, // [IN], length number to encode.
unsigned *pLengthEncoded, // [OUT], number of bytes that were encoded.
BOOLEAN *pbLarge, // [OUT] TRUE if more encoded blocks are needed to encode this length.
unsigned *pNBytesConsumed) // [OUT] Count of bytes consumed in encoding.
{
*pbLarge = FALSE;
*pLengthEncoded = Length;
*pNBytesConsumed = 1;
if (Length <= 0x7F) {
// <= 127 means encode in lower 7 bits of first byte, so that bit 7
// is zero.
*pBuffer = (BYTE)Length;
}
else if (Length <= 16383) {
// Set bit 7 but not bit 6, encode length in last 6 bits
// of 1st byte and entire 2nd byte (14 bits total).
PutByteswappedShort(pBuffer, (Length | 0x8000));
*pNBytesConsumed = 2;
}
else {
// Set bits 7 and 6, encode up to four blocks of 16K (16384) into
// one byte, pass back that block is large for future coding.
int N16KBlocks = Length / 16384;
// We never expect more than 64K of data owing to X.224 limits.
ASSERT(N16KBlocks <= 4);
if (N16KBlocks > 4) N16KBlocks = 4;
*pBuffer = N16KBlocks | 0xC0;
*pLengthEncoded = N16KBlocks * 16384;
*pbLarge = TRUE;
}
}
/*
* Handler functions
*/
#ifdef MCS_Future
/*
* PDU 0
*
* PDin ::= [APPLICATION 0] IMPLICIT SEQUENCE {
* heightLimit INTEGER (0..MAX)
* }
*
* This PDU is sent from the top provider downward when a new node is added
* to the domain. It is intended to ferret out loops in the domain, as well
* as enforce the negotiated maximum domain height.
*/
// pBuffer should point to the place where the X.224 header will start. Total
// size specified by PDinPDUSize.
void CreatePlumbDomainInd(
unsigned short HeightLimit,
BYTE *pBuffer)
{
// Set up first byte with the type.
pBuffer[X224_DataHeaderSize] = MCS_PLUMB_DOMAIN_INDICATION_ENUM << 2;
// Add HeightLimit.
PutByteswappedShort(pBuffer + X224_DataHeaderSize + 1, HeightLimit);
// Set up X224 header based on the final size of the packet.
CreateX224DataHeader(pBuffer, PDinBaseSize, TRUE);
}
BOOLEAN __fastcall HandlePlumbDomainInd(
PDomain pDomain,
BYTE *Frame,
unsigned BytesLeft,
unsigned *pNBytesConsumed)
{
if (BytesLeft < PDinBaseSize)
return FALSE;
#if 0
// Data unpacking code, not used right now since we should not receive this PDU.
int HeightLimit;
// Get HeightLimit.
HeightLimit = (int)GetByteswappedShort(Frame + 1);
#endif
if (pDomain->bTopProvider) {
ErrOut(pDomain->pContext, "Plumb-domain indication PDU received; "
"we are top provider, this should never happen, rejecting");
ReturnRejectPDU(pDomain, Diag_ForbiddenPDUUpward, Frame, PDinBaseSize);
}
else {
ErrOut(pDomain->pContext, "Plumb-domain indication PDU received, "
"not supported");
ASSERT(FALSE);
//MCS FUTURE: Unpack, check if height limit is zero. If so, we need
// to disconnect all providers lower than us, since this means
// we have reached the maximum allowable depth in the domain.
}
// Skip the bytes received no matter what.
*pNBytesConsumed = PDinBaseSize;
return TRUE;
}
#endif // MCS_Future
/*
* PDU 1
*
* EDrq ::= [APPLICATION 1] IMPLICIT SEQUENCE {
* subHeight INTEGER (0..MAX)
* subInterval INTEGER (0..MAX)
* }
*
* This PDU is sent upwards by a lower node to its upward connection when
* either its height in the domain changes (which occurs only when
* domains are merged) or its requirements for throughput enforcement
* change.
* Though this PDU may be sent by a lower node upon completion of a
* domain connection, its information is unneeded for this implementation.
*/
BOOLEAN __fastcall HandleErectDomainReq(
PDomain pDomain,
BYTE *Frame,
unsigned BytesLeft,
unsigned *pNBytesConsumed)
{
if (BytesLeft < EDrqBaseSize)
return FALSE;
if (pDomain->bTopProvider) {
// We don't unpack the PDU, just ignore for this implementation.
TraceOut(pDomain->pContext, "Erect-domain request PDU received, ignored");
#if 0
// PDU unpacking code, unneeded for the current implementation.
int SubHeight, SubInterval;
// Get parameters.
SubHeight = (int)GetByteswappedShort(Frame + 1);
SubInterval = (int)GetByteswappedShort(Frame + 3);
// Actions at this point would be to update the internal database
// of subordinate nodes with the new information and, possibly,
// pass the PDU upward again (though the latter is not well
// specified in the T.125 spec).
#endif
}
else {
ErrOut(pDomain->pContext, "Erect-domain request PDU received, "
"not supported");
ASSERT(FALSE);
// MCS FUTURE: Forward PDU to upward connection.
}
// Skip the bytes for this PDU no matter what.
*pNBytesConsumed = EDrqBaseSize;
return TRUE;
}
/*
* PDU 8
*
* DPum ::= [APPLICATION 8] IMPLICIT SEQUENCE {
* reason Reason
* }
*
* This PDU is sent upward or downward on a connection when it is about
* to be destroyed. There is no reply to this PDU; it simply means a node
* is going away, irrevocably.
*/
// pBuffer points to where the X.224 header will start. Total size is given
// in DPumPDUSize.
void CreateDisconnectProviderUlt(int Reason, BYTE *pBuffer)
{
// Set up first byte with the type.
pBuffer[X224_DataHeaderSize] = MCS_DISCONNECT_PROVIDER_ULTIMATUM_ENUM << 2;
// Add Reason to first and second bytes.
pBuffer[X224_DataHeaderSize + 1] = 0;
Put3BitFieldAtBit1(pBuffer + X224_DataHeaderSize, Reason);
// Set up X224 header based on the final size of the packet.
CreateX224DataHeader(pBuffer, DPumBaseSize, TRUE);
}
BOOLEAN __fastcall HandleDisconnectProviderUlt(
PDomain pDomain,
BYTE *Frame,
unsigned BytesLeft,
unsigned *pNBytesConsumed)
{
NTSTATUS Status;
DisconnectProviderIndicationIoctl DPin;
if (BytesLeft < DPumBaseSize)
return FALSE;
*pNBytesConsumed = DPumBaseSize;
TraceOut(pDomain->pContext, "Received DPum PDU");
// If we already received a DPum or X.224 disconnect, do not send the
// indications upward.
if (pDomain->State == State_Disconnected) {
ErrOut(pDomain->pContext, "Received an extra DPum PDU, ignoring");
return TRUE;
}
// We do not check the connection state other than disconnected -- it
// is a small client bug that it will send a DPum no matter if
// we've connected yet.
pDomain->State = State_Disconnected;
// Begin filling out disconnect-provider indication for the node
// controller.
DPin.Header.hUser = NULL; // Node controller.
DPin.Header.Type = MCS_DISCONNECT_PROVIDER_INDICATION;
DPin.hConn = NULL;
// Reason is a 3-bit field starting at bit 1 of the 1st byte.
DPin.Reason = (int)Get3BitFieldAtBit1(Frame);
// Disconnect remote users only.
DisconnectProvider(pDomain, FALSE, DPin.Reason);
pDomain->bEndConnectionPacketReceived = TRUE;
if (!pDomain->bChannelBound || !pDomain->bT120StartReceived) {
if (!pDomain->bChannelBound)
TraceOut(pDomain->pContext, "HandleDisconnProvUlt(): Cannot "
"send DISCONNECT_PROV_IND to user mode, "
"QueryVirtBindings not received or server-side "
"disconnect occurred");
else
TraceOut(pDomain->pContext, "HandleDisconnProvUlt(): Cannot "
"send DISCONN_PROV_IND to user mode, T120_START not "
"received");
pDomain->bDPumReceivedNotInput = TRUE;
pDomain->DelayedDPumReason = DPin.Reason;
}
else {
//
// Let TD know that the comming disconnection is expected
//
ICA_STACK_BROKENREASON brkReason;
SD_IOCTL SdIoctl;
brkReason.BrokenReason = TD_USER_BROKENREASON_TERMINATING;
//
// Send an IOCTL down to notify that this is an expected
// disconnection otherwise the broken reason would
// eventually make it's way back to termsrv as
// 'BrokenReason_Unexpected' and this causes problems
// e.g see whistler bug 17714
//
SdIoctl.IoControlCode = IOCTL_ICA_STACK_SET_BROKENREASON;
SdIoctl.InputBuffer = &brkReason;
SdIoctl.InputBufferLength = sizeof(brkReason);
SdIoctl.OutputBuffer = NULL;
SdIoctl.OutputBufferLength = 0;
Status = IcaCallNextDriver(pDomain->pContext,
SD$IOCTL,
&SdIoctl);
if (!NT_SUCCESS(Status)) {
WarnOut1(pDomain->pContext, "HandleDisconnProvUlt(): "
"Could not send broken reason notifcation to next driver"
"status=%X, ignoring error", Status);
}
// Send the DPin to the node controller channel.
TraceOut(pDomain->pContext, "HandleDisconnProvUlt(): Sending "
"DISCONNECT_PROV_IND upward");
Status = IcaChannelInput(pDomain->pContext, Channel_Virtual,
Virtual_T120ChannelNum, NULL, (BYTE *)&DPin, sizeof(DPin));
if (!NT_SUCCESS(Status)) {
// We ignore the error -- if the stack is coming down, the link
// may have been broken, so this is not a major concern.
WarnOut1(pDomain->pContext, "HandleDisconnProvUlt(): "
"Could not send notification to node controller, "
"status=%X, ignoring error", Status);
}
}
return TRUE;
}
/*
* PDU 9
*
* RJum ::= [APPLICATION 9] IMPLICIT SEQUENCE {
* diagnostic Diagnostic,
* initialOctets OCTET STRING
* }
*/
NTSTATUS ReturnRejectPDU(
PDomain pDomain,
int Diagnostic,
BYTE *BadPDUData,
unsigned BadPDUSize)
{
POUTBUF pOutBuf;
NTSTATUS Status;
unsigned Size;
// Determine the largest chunk that will fit in a maximum-sized PDU.
Size = RJumPDUSize(BadPDUSize);
if (Size > pDomain->DomParams.MaxPDUSize)
BadPDUSize = pDomain->DomParams.MaxPDUSize;
// Alloc buffer for return. Must be constrained by the largest size of
// return PDU.
Status = IcaBufferAlloc(pDomain->pContext, FALSE, TRUE,
RJumPDUSize(BadPDUSize), NULL, &pOutBuf);
if (Status != STATUS_SUCCESS) {
ErrOut(pDomain->pContext, "Could not allocate an OutBuf for an RJum PDU "
"send, send ignored");
// No asserts, if we cannot send this PDU it's like the incoming PDU
// was ignored.
return Status;
}
CreateRejectMCSPDUUlt(Diagnostic, BadPDUData, BadPDUSize, pOutBuf->pBuffer);
pOutBuf->ByteCount = RJumPDUSize(BadPDUSize);
// MCS FUTURE: This would have to change to make sure we send to the
// right connection instead of the implicit downward connection.
Status = SendOutBuf(pDomain, pOutBuf);
if (!NT_SUCCESS(Status))
ErrOut(pDomain->pContext, "Could not send a RJum PDU, send ignored");
return Status;
}
// pBuffer points to the beginning of the space where the X.224 data header
// will start. Total size is given in macro RJumPDUSize().
void CreateRejectMCSPDUUlt(
int Diagnostic,
BYTE *BadPDUData,
unsigned BadPDUSize,
BYTE *pBuffer)
{
BOOLEAN bLarge;
unsigned NBytesConsumed, EncodedLength;
// Set up first byte with the type.
pBuffer[X224_DataHeaderSize] = MCS_REJECT_ULTIMATUM_ENUM << 2;
// Add Diagnostic to first and second bytes.
pBuffer[X224_DataHeaderSize + 1] = 0;
Put4BitFieldAtBit1(pBuffer + X224_DataHeaderSize, Diagnostic);
// Encode the PDU size.
EncodeLengthDeterminantPER(
pBuffer + X224_DataHeaderSize + 2,
BadPDUSize,
&EncodedLength,
&bLarge,
&NBytesConsumed);
// We won't handle greater than 16383 bytes of encoded length right now.
ASSERT(!bLarge);
// Copy offending data into the output PDU.
RtlCopyMemory(pBuffer + X224_DataHeaderSize + 2 + NBytesConsumed,
BadPDUData, BadPDUSize);
// Set up X224 header based on the final size of the packet.
CreateX224DataHeader(pBuffer, RJumBaseSize(BadPDUSize), TRUE);
}
BOOLEAN __fastcall HandleRejectMCSPDUUlt(
PDomain pDomain,
BYTE *Frame,
unsigned BytesLeft,
unsigned *pNBytesConsumed)
{
BOOLEAN bLarge;
unsigned Diagnostic, DataLength, NBytesConsumed;
// There must at least be Diagnostic field.
if (BytesLeft < 2)
return FALSE;
// Get Diagnostic code -- 4-bit enumeration bitfield, starting at bit 1 of
// byte 1, shifted to make a code starting at 0.
Diagnostic = Get4BitFieldAtBit1(Frame);
// Get DataLength. This is a special case requiring handling a length
// determinant.
if (!DecodeLengthDeterminantPER(Frame + 2, BytesLeft - 2, &bLarge,
&DataLength, &NBytesConsumed))
return FALSE;
// We are not handling more than 16383 bytes encoded length right now.
ASSERT(!bLarge);
// Raw data is at Frame + 2 + NBytesConsumed.
if (BytesLeft < (2 + NBytesConsumed + DataLength))
return FALSE;
ErrOut1(pDomain->pContext, "Received reject PDU ultimatum, type byte is 0x%X",
Frame[2 + NBytesConsumed + X224_DataHeaderSize]);
*pNBytesConsumed = 2 + NBytesConsumed + DataLength;
return TRUE;
}
/*
* PDU 10
*
* AUrq ::= [APPLICATION 10] IMPLICIT SEQUENCE {
* }
*/
BOOLEAN __fastcall HandleAttachUserReq(
PDomain pDomain,
BYTE *Frame,
unsigned BytesLeft,
unsigned *pNBytesConsumed)
{
TraceOut(pDomain->pContext, "Received an AttachUserReq PDU");
// We don't need to do any decoding here -- this is a null packet beyond
// the initial PDU type byte.
*pNBytesConsumed = AUrqBaseSize;
if (pDomain->bTopProvider) {
POUTBUF pOutBuf;
BOOLEAN bCompleted;
unsigned MaxSendSize;
NTSTATUS Status;
MCSError MCSErr;
UserAttachment *pUA;
// Alloc buffer for largest size of return PDU.
// This allocation is vital to stack communication and must succeed.
do {
Status = IcaBufferAlloc(pDomain->pContext, FALSE, TRUE,
AUcfPDUSize(TRUE), NULL, &pOutBuf);
if (Status != STATUS_SUCCESS)
ErrOut(pDomain->pContext, "Could not allocate an OutBuf for an "
"AUcf PDU send, retrying");
} while (Status != STATUS_SUCCESS);
// Call the kernel mode API. We will munge extra info below as needed.
MCSErr = MCSAttachUserRequest(pDomain, NULL, NULL, NULL, &pUA,
&MaxSendSize, &bCompleted);
if (MCSErr == MCS_NO_ERROR) {
ASSERT(bCompleted);
CreateAttachUserCon(RESULT_SUCCESSFUL, TRUE, pUA->UserID,
pOutBuf->pBuffer);
pOutBuf->ByteCount = AUcfPDUSize(TRUE);
// Change the UA to show that user is nonlocal.
pUA->bLocal = FALSE;
}
else {
CreateAttachUserCon(RESULT_UNSPECIFIED_FAILURE, FALSE,
NULL_ChannelID, pOutBuf->pBuffer);
pOutBuf->ByteCount = AUcfPDUSize(FALSE);
}
Status = SendOutBuf(pDomain, pOutBuf);
if (!NT_SUCCESS(Status)) {
// This should only occur if the stack is going down; the
// requester will not ever receive a reply.
ErrOut(pDomain->pContext, "Problem sending AUcf PDU to TD");
// Remove the user from the user list, quietly.
SListRemove(&pDomain->UserAttachmentList, (UINT_PTR)pUA, &pUA);
ASSERT(FALSE);
return TRUE;
}
}
else {
ErrOut(pDomain->pContext, "Attach-user request PDU received, "
"not supported");
ASSERT(FALSE);
// MCS FUTURE: Forward PDU to upward connection.
}
return TRUE;
}
/*
* PDU 11
*
* AUcf ::= [APPLICATION 11] IMPLICIT SEQUENCE {
* result Result,
* initiator UserId OPTIONAL
* }
*/
// pBuffer points to beginning of space where X.224 data header will start.
// Total size needed given by macro AUcfPDUSize().
void CreateAttachUserCon(
int Result,
BOOLEAN bInitiatorPresent,
UserID Initiator,
BYTE *pBuffer)
{
// Set up first byte with the type and whether Initiator is present.
pBuffer[X224_DataHeaderSize] = MCS_ATTACH_USER_CONFIRM_ENUM << 2;
if (bInitiatorPresent) pBuffer[X224_DataHeaderSize] |= 0x02;
// Add Result to first and second bytes.
pBuffer[X224_DataHeaderSize + 1] = 0;
Put4BitFieldAtBit0(pBuffer + X224_DataHeaderSize, Result);
// Add Initiator, if present.
if (bInitiatorPresent)
PutUserID(pBuffer + X224_DataHeaderSize + 2, Initiator);
// Set up X224 header based on the final size of the packet.
CreateX224DataHeader(pBuffer, AUcfBaseSize(bInitiatorPresent), TRUE);
}
#ifdef MCS_Future
BOOLEAN __fastcall HandleAttachUserCon(
PDomain pDomain,
BYTE *Frame,
unsigned BytesLeft,
unsigned *pNBytesConsumed)
{
BOOLEAN bInitiatorPresent;
unsigned Size;
// At least enough bytes to get to bInitiatorPresent bit and Result.
if (BytesLeft < 2)
return FALSE;
// Bit 1 in byte 1 is a flag for whether the initiator UserID is present
// (it is OPTIONAL in the ASN.1 source for this PDU).
bInitiatorPresent = (*Frame & 0x02);
// We did not receive the whole frame.
Size = AUcfBaseSize(bInitiatorPresent);
if (BytesLeft < Size)
return FALSE;
if (pDomain->bTopProvider) {
ErrOut(pDomain->pContext, "Attach-user confirm PDU received; "
"we are top provider, this should never happen, rejecting");
ReturnRejectPDU(pDomain, Diag_ForbiddenPDUUpward, Frame, Size);
}
else {
ErrOut(pDomain->pContext, "Attach-user confirm PDU received, "
"not supported");
ASSERT(FALSE);
#if 0
// Decode code not used right now.
int Result;
UserID InitiatorID;
// Result, starting at bit 0 of the 1st byte.
Result = Get4BitFieldAtBit0(Frame);
// Get Initiator.
if (bUserIDPresent) UserID = GetUserID(Frame + 2);
//MCS FUTURE: Actions here are to state-transition past a waiting-
// for-AU confirm state, check the Result code, store the Initiator
// userID as our new UserID.
#endif
}
// At least skip the bytes in the PDU.
*pNBytesConsumed = Size;
return TRUE;
}
#endif // MCS_Future
/*
* PDU 12
*
* DUrq ::= [APPLICATION 12] IMPLICIT SEQUENCE {
* reason Reason,
* userIds SET OF UserId
* }
*/
BOOLEAN __fastcall HandleDetachUserReq(
PDomain pDomain,
BYTE *Frame,
unsigned BytesLeft,
unsigned *pNBytesConsumed)
{
UserID CurUserID;
BOOLEAN bLarge, bFound;
unsigned i, Reason, NUsers, NBytesConsumed, Size;
NTSTATUS Status;
UserHandle hUser;
UserAttachment *pUA;
// Must at least have Reason bytes.
if (BytesLeft < 2)
return FALSE;
// Get NUsers. This is a special case requiring handling a length
// determinant.
if (!DecodeLengthDeterminantPER(Frame + 2, BytesLeft - 2, &bLarge,
&NUsers, &NBytesConsumed))
return FALSE;
// We are not handling more than 16383 detach users in a PDU.
ASSERT(!bLarge);
Size = DUrqBaseSize(NUsers);
if (BytesLeft < Size)
return FALSE;
if (pDomain->bTopProvider) {
MCSError MCSErr;
// Get Reason.
Reason = Get3BitFieldAtBit1(Frame);
// User IDs are a byteswapped word array starting at Frame + 2 +
// NBytesConsumed.
// Iterate all listed users, find in attachment list, remove.
for (i = 0; i < NUsers; i++) {
CurUserID = GetUserID(Frame + 2 + NBytesConsumed +
sizeof(short) * i);
// We do not have a list indexed by UserID, so we have to do the
// search here.
bFound = FALSE;
SListResetIteration(&pDomain->UserAttachmentList);
while (SListIterate(&pDomain->UserAttachmentList,
(UINT_PTR *)&hUser, &pUA)) {
if (pUA->UserID == CurUserID) {
bFound = TRUE;
break;
}
}
if (bFound)
MCSErr = DetachUser(pDomain, hUser, REASON_USER_REQUESTED,
FALSE);
else
ErrOut(pDomain->pContext, "A UserID received in a detach-user "
"request PDU is not present");
}
}
else {
ErrOut(pDomain->pContext, "Detach-user request PDU received, "
"not supported");
ASSERT(FALSE);
// MCS FUTURE: Forward PDU to upward connection.
}
*pNBytesConsumed = Size;
return TRUE;
}
/*
* PDU 13
*
* DUin ::= [APPLICATION 13] IMPLICIT SEQUENCE {
* reason Reason,
* userIds SET OF UserId
* }
*/
// pBuffer is a pointer to the beginning of where the X.224 data header
// will be. Required encoding size for the whole PDU is given in
// macro DUinPDUSize().
void CreateDetachUserInd(
MCSReason Reason, // [IN] Reason code.
int NUserIDs, // [IN] # of user IDs to encode.
UserID *UserIDs, // [IN] Array of UserIDs.
BYTE *pBuffer) // [IN] Pointer to buffer in which to encode.
{
int i, EncodedLength, NBytesConsumed;
BOOLEAN bLarge;
// Set up first byte with the type.
pBuffer[X224_DataHeaderSize] = MCS_DETACH_USER_INDICATION_ENUM << 2;
// Add Reason to first and second bytes.
pBuffer[X224_DataHeaderSize + 1] = 0;
Put3BitFieldAtBit1(pBuffer + X224_DataHeaderSize, Reason);
// Encode the number of User IDs.
EncodeLengthDeterminantPER(
pBuffer + X224_DataHeaderSize + 2,
NUserIDs,
&EncodedLength,
&bLarge,
&NBytesConsumed);
// We are not handling more than 16383 UserIDs.
ASSERT(!bLarge);
// Encode user ID array.
for (i = 0; i < NUserIDs; i++)
PutUserID(pBuffer + X224_DataHeaderSize + 2 + NBytesConsumed
+ sizeof(short) * i, UserIDs[i]);
// Set up X224 header based on the final size of the packet.
CreateX224DataHeader(pBuffer, DUinBaseSize(NUserIDs), TRUE);
}
#ifdef MCS_Future
BOOLEAN __fastcall HandleDetachUserInd(
PDomain pDomain,
BYTE *Frame,
unsigned BytesLeft,
unsigned *pNBytesConsumed)
{
BOOLEAN bLarge;
unsigned NUsers, NBytesConsumed, Size;
// Must at least have Reason bytes.
if (BytesLeft < 2) return FALSE; // Only cover Reason bytes.
// Get NUsers. This is a special case requiring handling a length
// determinant.
if (!DecodeLengthDeterminantPER(Frame + 2, BytesLeft - 2, &bLarge,
&NUsers, &NBytesConsumed))
return FALSE;
// We are not handling more than 16383 detached users in a PDU.
ASSERT(!bLarge);
Size = DUinBaseSize(NUsers);
if (BytesLeft < Size)
return FALSE;
if (pDomain->bTopProvider) {
ErrOut(pDomain->pContext, "Detach-user indication PDU received; "
"we are top provider, this should never happen, rejecting");
ReturnRejectPDU(pDomain, Diag_ForbiddenPDUUpward, Frame, Size);
}
else {
ErrOut(pDomain->pContext, "Detach-user indication PDU received, "
"not supported");
ASSERT(FALSE);
#if 0
// This is decode code, which is not needed because we should not
// receive this PDU.
int Reason;
// Get Reason.
Reason = Get3BitFieldAtBit1(Frame);
// User IDs are a byteswapped word array starting at Frame + 2 +
// NBytesConsumed.
#endif
}
*pNBytesConsumed = Size;
return TRUE;
}
#endif // MCS_Future
/*
* PDU 14
*
* CJrq ::= [APPLICATION 14] IMPLICIT SEQUENCE {
* initiator UserId,
* channelId ChannelId
* }
*/
BOOLEAN __fastcall HandleChannelJoinReq(
PDomain pDomain,
BYTE *Frame,
unsigned BytesLeft,
unsigned *pNBytesConsumed)
{
TraceOut(pDomain->pContext, "Received a ChannelJoinReq PDU");
if (BytesLeft < CJrqBaseSize)
return FALSE;
*pNBytesConsumed = CJrqBaseSize;
if (pDomain->bTopProvider) {
UserID Initiator;
POUTBUF pOutBuf;
BOOLEAN bFound, bCompleted;
MCSError MCSErr;
NTSTATUS Status;
MCSResult Result;
ChannelID ChannelID;
ChannelHandle hChannel;
UserAttachment *pUA;
Initiator = GetUserID(Frame + 1);
ChannelID = GetChannelID(Frame + 3);
// We do not have a list indexed by UserID, so we have to do the
// search here.
bFound = FALSE;
SListResetIteration(&pDomain->UserAttachmentList);
while (SListIterate(&pDomain->UserAttachmentList,
(UINT_PTR *)&pUA, &pUA)) {
if (pUA->UserID == Initiator) {
bFound = TRUE;
break;
}
}
if (bFound) {
// Call kernel-mode API.
MCSErr = MCSChannelJoinRequest(pUA, ChannelID, &hChannel,
&bCompleted);
if (MCSErr == MCS_NO_ERROR) {
ASSERT(bCompleted);
Result = RESULT_SUCCESSFUL;
}
else if (MCSErr == MCS_NO_SUCH_USER) {
Result = RESULT_NO_SUCH_USER;
}
else if (MCSErr == MCS_DUPLICATE_CHANNEL) {
goto EXIT_POINT;
}
else {
Result = RESULT_UNSPECIFIED_FAILURE;
}
}
else {
ErrOut(pDomain->pContext, "Initiator UserID received in a "
"channel-join request PDU is not in user list");
Result = RESULT_NO_SUCH_USER;
}
// Alloc buffer for largest size of return PDU.
// This allocation is vital to stack communication and must succeed.
do {
Status = IcaBufferAlloc(pDomain->pContext, FALSE, TRUE,
CJcfPDUSize(TRUE), NULL, &pOutBuf);
if (Status != STATUS_SUCCESS)
ErrOut(pDomain->pContext, "Could not allocate an OutBuf for a "
"CJcf PDU send, retrying");
} while (Status != STATUS_SUCCESS);
if (Result == RESULT_SUCCESSFUL) {
CreateChannelJoinCon(Result, Initiator, ChannelID,
TRUE, ChannelID, pOutBuf->pBuffer);
pOutBuf->ByteCount = CJcfPDUSize(TRUE) ;
}
else {
CreateChannelJoinCon(Result, Initiator, ChannelID,
FALSE, NULL_ChannelID, pOutBuf->pBuffer);
pOutBuf->ByteCount = CJcfPDUSize(FALSE) ;
}
Status = SendOutBuf(pDomain, pOutBuf);
if (!NT_SUCCESS(Status)) {
// This should only occur if the stack is going down; the
// requester will not ever receive a reply.
ErrOut(pDomain->pContext, "Problem sending CJcf PDU to TD");
return TRUE;
}
}
else {
ErrOut(pDomain->pContext, "Channel-join request received, "
"not supported");
ASSERT(FALSE);
// MCS FUTURE: Forward the PDU to the upward connection.
}
EXIT_POINT:
return TRUE; // We don't have user data to which to skip.
}
/*
* PDU 15
*
* CJcf ::= [APPLICATION 15] IMPLICIT SEQUENCE {
* result Result,
* initiator UserId,
* requested ChannelId,
* channelId ChannelId OPTIONAL
* }
*/
// pBuffer points to the beginning of the space where the X.224 data header
// will start. Total bytes required given by CJcfPDUSize() macro.
void CreateChannelJoinCon(
int Result,
UserID Initiator,
ChannelID RequestedChannelID,
BOOLEAN bJoinedChannelIDPresent,
ChannelID JoinedChannelID,
BYTE *pBuffer)
{
// Set up first byte with the type and whether JoinedChannelID is present.
pBuffer[X224_DataHeaderSize] = MCS_CHANNEL_JOIN_CONFIRM_ENUM << 2;
if (bJoinedChannelIDPresent) pBuffer[X224_DataHeaderSize] |= 0x02;
// Add Result to first and second bytes.
pBuffer[X224_DataHeaderSize + 1] = 0;
Put4BitFieldAtBit0(pBuffer + X224_DataHeaderSize, Result);
// Add Initiator, RequestedChannelID.
PutUserID(pBuffer + X224_DataHeaderSize + 2, Initiator);
PutChannelID(pBuffer + X224_DataHeaderSize + 4, RequestedChannelID);
// Add JoinedChannelID, if present.
if (bJoinedChannelIDPresent)
PutChannelID(pBuffer + X224_DataHeaderSize + 6, JoinedChannelID);
// Set up X224 header based on the final size of the packet.
CreateX224DataHeader(pBuffer, CJcfBaseSize(bJoinedChannelIDPresent), TRUE);
}
#ifdef MCS_Future
BOOLEAN __fastcall HandleChannelJoinCon(
PDomain pDomain,
BYTE *Frame,
unsigned BytesLeft,
unsigned *pNBytesConsumed)
{
BOOLEAN bJoinedChannelIDPresent;
unsigned Size;
// First byte containing the bJoinedChannelIDPresent bit is guaranteed
// to be present since it had to be present to decode the PDU type.
// Bit 1 in 1st byte is a flag for whether the joined ChannelID is present
// (it is OPTIONAL in the ASN.1 source for this PDU).
bJoinedChannelIDPresent = (*Frame & 0x02);
Size = CJcfBaseSize(bJoinedChannelIDPresent);
if (BytesLeft < Size)
return FALSE;
if (pDomain->bTopProvider) {
ErrOut(pDomain->pContext, "Channel-join confirm PDU received;"
"we are top provider, this should never happen, rejecting");
ReturnRejectPDU(pDomain, Diag_ForbiddenPDUUpward, Frame, Size);
}
else {
ErrOut(pDomain->pContext, "Channel-join confirm PDU received, "
"not supported");
ASSERT(FALSE);
//MCS FUTURE: Decode and handle
#if 0
/*
* This is decode code which is not used for now because we always expect
* to be top provider.
*/
int Result;
UserID Initiator;
ChannelID RequestedChannelID, JoinedChannelID;
if (BytesLeft < (6 + (bJoinedChannelPresent ? 2 : 0))) return FALSE;
Result = Get4BitFieldAtBit0(Frame);
Initiator = GetUserID(Frame + 2);
RequestedChannelID = GetChannelID(Frame + 4);
if (bJoinedChannelIDPresent) JoinedChannelID = GetChannelID(Frame + 6);
#endif
}
*pNBytesConsumed = Size;
return TRUE;
}
#endif // MCS_Future
/*
* PDU 16
*
* CLrq ::= [APPLICATION 16] IMPLICIT SEQUENCE {
* channelIds SET OF ChannelId
* }
*/
BOOLEAN __fastcall HandleChannelLeaveReq(
PDomain pDomain,
BYTE *Frame,
unsigned BytesLeft,
unsigned *pNBytesConsumed)
{
BOOLEAN bLarge, bChannelRemoved;
unsigned i, UserID, NChannels, NBytesConsumed, Size;
NTSTATUS Status;
ChannelID ChannelID;
MCSChannel *pMCSChannel;
UserAttachment *pUA;
// Get NChannels. This is a special case requiring handling a length
// determinant.
if (!DecodeLengthDeterminantPER(Frame + 1, BytesLeft - 1, &bLarge,
&NChannels, &NBytesConsumed))
return FALSE;
// We are not handling more than 16383 channels to join.
ASSERT(!bLarge);
Size = CLrqBaseSize(NChannels);
if (BytesLeft < Size)
return FALSE;
*pNBytesConsumed = Size;
if (pDomain->bTopProvider) {
// MCS FUTURE: Be aware that this PDU does not include user
// attachments, so if we move to multiple connections per PD this
// will have to be differentiated to make sure only users joined
// from the connection this arrived on will be removed from the
// channel list.
MCSError MCSErr;
// For each channel listed, remove all nonlocal users.
for (i = 0; i < NChannels; i++) {
ChannelID = GetChannelID(Frame + 1 + NBytesConsumed +
sizeof(short) * i);
if (!SListGetByKey(&pDomain->ChannelList, ChannelID,
&pMCSChannel)) {
ErrOut(pDomain->pContext, "A channel specified in a "
"channel-leave request PDU does not exist");
continue;
}
// Iterate user list in the channel, check for nonlocal users.
SListResetIteration(&pMCSChannel->UserList);
while (SListIterate(&pMCSChannel->UserList, (UINT_PTR *)&pUA,
&pUA)) {
if (!pUA->bLocal) {
// Call central code in MCSCore.c.
MCSErr = ChannelLeave(pUA, pMCSChannel, &bChannelRemoved);
if (bChannelRemoved)
// The MCSChannel beneath us went away, do not continue
// trying to access list data.
break;
}
}
}
}
else {
ErrOut(pDomain->pContext, "Channel-leave request received, "
"not supported");
ASSERT(FALSE);
// MCS FUTURE: Forward the PDU to the upward connection.
}
return TRUE; // We don't have user data to which to skip.
}
/*
* PDUs 25-28
*
* SDrq/SDin/USrq/USin ::= [APPLICATION 25-28] IMPLICIT SEQUENCE {
* initiator UserId,
* channelId ChannelId,
* dataPriority DataPriority,
* segmentation Segmentation,
* userData OCTET STRING
* }
*/
/*
* Create function for all SendData PDUs. Prepends header to beginning of
* data specified in MCSSendDataRequestIoctl.pData, returns in
* pNewDataStart the new beginning of the data, i.e. the beginning of the
* PDU.
* Assumes that enough space has already been allocated before the
* start of the user data.
*/
// Does not return indications that ASN.1 segmentation has occurred --
// requires caller to understand and encode separately any segment
// data.
void CreateSendDataPDUHeader(
int PDUType, // MCS_SEND_DATA_INDICATION_ENUM, etc.
UserID Initiator,
ChannelID ChannelID,
MCSPriority Priority,
Segmentation Segmentation,
BYTE **ppData,
unsigned *pDataLength)
{
int NBytesConsumed, EncodedLength, PDULength;
BYTE *pCurData, *pData;
BOOLEAN bLarge;
unsigned DataLength;
// Save original pData and DataLength.
pData = *ppData;
DataLength = *pDataLength;
// First get the length determinant. It is a maximum of 2 bytes, so
// encode it at (pData-2) and shift it forward if it is smaller.
EncodeLengthDeterminantPER(
pData - 2,
DataLength,
&EncodedLength,
&bLarge,
&NBytesConsumed);
// We ignore bLarge, EncodedLength, see above comment.
if (NBytesConsumed == 1)
*(pData - 1) = *(pData - 2);
// Set the beginning of the rest of the data (which is 6 bytes).
pCurData = pData - NBytesConsumed - 6;
// Set up first byte with the type.
*pCurData = PDUType << 2;
pCurData++;
// Add Initiator, ChannelID.
PutUserID(pCurData, Initiator);
PutChannelID(pCurData + 2, ChannelID);
pCurData += 4;
// Add DataPriority (2 bits in bits 7 and 6) and Segmentation (2 bits
// into bits 5 and 4) into 6th byte. Note that the Segmentation flags
// (SEGMENTATION_BEGIN, SEGMENTATION_END) are assumed to be defined
// in their exact bit positions corresponding to the positons required
// here for encoding.
*pCurData = (BYTE)(Priority << 6) + (BYTE)Segmentation;
// Set up X.224 header based on the final size of the packet.
PDULength = NBytesConsumed + 6 + DataLength;
pCurData = pData - NBytesConsumed - 6 - X224_DataHeaderSize;
CreateX224DataHeader(pCurData, PDULength, TRUE);
*ppData = pCurData;
*pDataLength = PDULength + X224_DataHeaderSize;
}
/*
* Handler function for all SendData PDUs.
*/
BOOLEAN __fastcall HandleAllSendDataPDUs(
PDomain pDomain,
BYTE *Frame,
unsigned BytesLeft,
unsigned *pNBytesConsumed)
{
BYTE *pBuffer;
UserID SenderID;
BOOLEAN bLarge, bFound;
unsigned NBytesConsumed, Size, PDUNum, DataLength;
UINT_PTR CurUserID;
NTSTATUS Status;
ChannelID ChannelID;
MCSChannel *pMCSChannel;
UserHandle hUser;
MCSPriority Priority;
Segmentation Segmentation;
UserAttachment *pUA;
// TraceOut(pDomain->pContext, "Received a SendData PDU");
// MCS FUTURE: We should forward the data upward here, since below we
// will modify the encoded buffer for ASN.1 segmentation.
// Get the PDU number again since this function handles multiple PDUs.
PDUNum = (*Frame) >> 2;
ASSERT(PDUNum >= MCS_SEND_DATA_REQUEST_ENUM &&
PDUNum <= MCS_UNIFORM_SEND_DATA_INDICATION_ENUM);
#if DBG
// Enforce hierarchical requirements on PDUs -- indications move only
// away from the top provider, requests move only toward it.
// MCS FUTURE: For indications moving toward TP from any downlevel
// connection we should send a reject-MCS-PDU back down the link.
// MCS FUTURE: If we are not TP and a request is coming from the upward
// direction send back up rejected.
// TODO FUTURE: With cross server shadowing we have two top providers
// talking to each other. Need to decide how this is best represented.
if (pDomain->StackClass == Stack_Primary) {
if (pDomain->bTopProvider && (PDUNum == MCS_SEND_DATA_INDICATION_ENUM ||
PDUNum == MCS_UNIFORM_SEND_DATA_INDICATION_ENUM)) {
ErrOut(pDomain->pContext, "HandleAllSendDataPDUs(): Received a "
"(uniform)send-data indication when we are top provider!");
// Ignore the error.
}
}
#endif
// At least to DataLength should be present.
if (BytesLeft < 6)
return FALSE;
// Get initiator, ChannelID.
SenderID = GetUserID(Frame + 1);
ChannelID = GetChannelID(Frame + 3);
// Get DataPriority, a 2-bit bitfield stored in bits 7 and 6 of byte 6,
// shifted right to get a number in range 0..3.
Priority = ((*(Frame + 5)) & 0xC0) >> 6;
// Get Segmentation, a 2-bit bitfield stored in bits 5 and 4 of byte 6.
// Note that the flag values bits defined for SEGMENTATION_BEGIN and
// SEGMENTATION_END correspond to these exact bit positions, no further
// shifting is required.
Segmentation = ((*(Frame + 5)) & 0x30);
// Get DataLength. This is a special case requiring handling a length
// determinant. bLarge (meaning ASN.1 segmentation occurred) will be
// handled below.
if (!DecodeLengthDeterminantPER(Frame + 6, BytesLeft - 6, &bLarge,
&DataLength, &NBytesConsumed))
return FALSE;
Size = 6 + NBytesConsumed + DataLength;
if (BytesLeft < Size)
return FALSE;
if (bLarge) {
// The packet is ASN.1 segmented. This means that there is a second
// length determinant embedded in the data buffer. The header
// encoded size contained only a multiple of 16K blocks; we
// have to get the remainder length determinant and then shift
// the remaining data onto the determinant to make a contiguous
// block.
unsigned Remainder, ExtraBytesConsumed;
if (!DecodeLengthDeterminantPER(Frame + 6 + NBytesConsumed +
DataLength, BytesLeft - 6 - NBytesConsumed - DataLength,
&bLarge, &Remainder, &ExtraBytesConsumed))
return FALSE;
ASSERT(!bLarge);
Size += ExtraBytesConsumed + Remainder;
if (BytesLeft < Size)
return FALSE;
RtlMoveMemory(Frame + 6 + NBytesConsumed + DataLength,
Frame + 6 + NBytesConsumed + DataLength +
ExtraBytesConsumed, Remainder);
DataLength += Remainder;
// Leave NBytesConsumed alone, it's still the size of the first length
// determinant.
}
// We have received an entire SendData frame.
*pNBytesConsumed = Size;
// MCS FUTURE: We do not handle MCS segmentation at all -- do we want to
//reconstruct here since we're already doing a memcpy()? If so, need to
//allocate a buffer for this priority and copy.
if (pDomain->bTopProvider ||
(!pDomain->bTopProvider && PDUNum != MCS_UNIFORM_SEND_DATA_REQUEST)) {
#if DBG
// Verify that Initiator exists. We do not have a list indexed by
// UserID, so we have to do the search here.
bFound = FALSE;
SListResetIteration(&pDomain->UserAttachmentList);
while (SListIterate(&pDomain->UserAttachmentList, (UINT_PTR *)&hUser,
&pUA)) {
if (pUA->UserID == SenderID) {
bFound = TRUE;
break;
}
}
if (!bFound && pDomain->bTopProvider) {
// Not knowing initiator is bad when coming from downlevel,
// but okay coming from upward connection since this node may
// not have seen the attach-user request pass by.
// In this case we will signal an error and ignore the send.
ErrOut2(pDomain->pContext, "%s: Initiator UserID[%lx] received in a "
"send-data PDU is not present, ignoring send",
pDomain->StackClass == Stack_Shadow ? "Shadow stack" :
(pDomain->StackClass == Stack_Passthru ? "Passthru stack" :
"Primary stack"), SenderID);
return TRUE;
}
#endif
// Find channel in channel list.
if (!SListGetByKey(&pDomain->ChannelList, ChannelID, &pMCSChannel)) {
// Ignore sends on missing channels. This means that no one
// has joined the channel. Give a warning only.
WarnOut1(pDomain->pContext, "ChannelID %d received in a send-data "
"PDU does not exist", ChannelID);
//MCS FUTURE: If we are not top provider, send to upward
// connection.
return TRUE;
}
// Send indication to all local attachments.
SListResetIteration(&pMCSChannel->UserList);
while (SListIterate(&pMCSChannel->UserList, &CurUserID, &pUA)) {
//If SDCallback fails, we need to return FALSE
if (pUA->bLocal)
if (!(pUA->SDCallback)(
(Frame + 6 + NBytesConsumed), // pData
DataLength,
pUA->UserDefined, // UserDefined
pUA, // hUser
(BOOLEAN)(PDUNum == MCS_UNIFORM_SEND_DATA_REQUEST), // bUniform
pMCSChannel, // hChannel
Priority,
SenderID,
Segmentation)) {
return FALSE;
}
// WD_Close can jump in to clean the Channel list and user list
// during the pUA->SDCallback when disconnecting
// at this time pDomain->bCanSendData will be set to FALSE
if (!pDomain->bCanSendData) {
return FALSE;
}
}
// MCS FUTURE: We do not handle indications and requests differently
// and enforce standard usage.
// MCS FUTURE: We need to check if there are any other downlevel attachments
// and forward the data down to them.
}
else {
//MCS FUTURE: Forward (Uniform)SendData PDU to upward connection.
}
return TRUE;
}