/* (C) 1997-2000 Microsoft Corp. * * file : ConPDU.c * author : Erik Mavrinac * * description: Handles decoding of MCS connect PDUs. Connect PDUs are always * encoded with ASN.1 basic encoding rules (BER). Included in this file are * local functions to BER-decode and -encode various types used in MCS PDUs. * * History: * 11-Aug-1997 jparsons Fixed BER decode routines. */ #include "precomp.h" #pragma hdrstop #include /* * Defines */ // Return codes for encode/decode functions. #define H_OK 0 #define H_TooShort 1 #define H_BadContents 2 #define H_Error 3 /* * Prototypes for handler functions. */ BOOLEAN __fastcall HandleConnectInitial(PDomain, BYTE *, unsigned, unsigned *); BOOLEAN __fastcall HandleConnectResponse(PDomain, BYTE *, unsigned, unsigned *); BOOLEAN __fastcall HandleConnectAdditional(PDomain, BYTE *, unsigned, unsigned *); BOOLEAN __fastcall HandleConnectResult(PDomain, BYTE *, unsigned, unsigned *); /* * These are listed in the 101-based enumeration order specified in the T.125 * spec. Decode the initial BER connect PDU 0x7F, then subtract 101 decimal * from the next byte value to get an index into this table. E.g. the bytes * 0x7F65 at the beginning refer to a connect-initial PDU. */ const MCSPDUInfo ConnectPDUTable[] = { StrOnDbg("Connect Initial", HandleConnectInitial), StrOnDbg("Connect Response", NULL /* HandleConnectResponse */), StrOnDbg("Connect Additional", NULL /* HandleConnectAdditional */), StrOnDbg("Connect Result", NULL /* HandleConnectResult */), }; /* * Decodes BER strings used by MCS. A BER stream is a set of tags * containing ID-length-contents triplets, using byte values as type and * length indicators unless length escapes are used. For example, a * typical tag: * * 0x02 0x02 0x04 0x00 * * Decomposition: * 0x02: Id = INTEGER_TAG * 0x02: Length = 2 octets * 0x04 0x00: Contents = 1024 (0x0400) * * Escaped tag: * * 0x04 0x82 0x04 0x00 0x8a 0x96... * * Decomposition: * 0x04: Id = OCTET_STRING_TAG * 0x82: Length stored in TWO bytes * 0x04 0x00: Length = 1024 octets * 0x8a 0x96...: Contents = 0x8 0x96... (1022 more octets) * * Returns FALSE if the frame is too small. * * History: * 11-Aug-97 jparsons Fixed pointer dereferencing error in calculating length * */ #define LengthModifier_Indefinite 0x80 #define LengthModifier_1 0x81 #define LengthModifier_2 0x82 #define LengthModifier_3 0x83 #define LengthModifier_4 0x84 #define TagType_Boolean 0x01 #define TagType_Integer 0x02 #define TagType_BitString 0x03 #define TagType_OctetString 0x04 #define TagType_Enumeration 0x0A #define TagType_Sequence 0x30 #define TagType_SetOf 0x31 #define TagType_ConnectInitial 0x65 #define TagType_ConnectResponse 0x66 #define TagType_ConnectAdditional 0x67 #define TagType_ConnectResult 0x68 int DecodeTagBER( PSDCONTEXT pContext, // For tracing. BYTE *Frame, unsigned *OutBytesLeft, int TagTypeExpected, unsigned *OutDataLength, UINT_PTR *Data, BYTE **newFrame) { int rc = H_OK; int TagType; unsigned i, BytesLeft, DataLength; BytesLeft = *OutBytesLeft; DataLength = *OutDataLength; if (BytesLeft >= 2) { // Get tag type, check it. TagType = *Frame; Frame++; BytesLeft--; if (TagType != TagTypeExpected) { ErrOut2(pContext, "Unexpected tag type found decoding BER tag, " "recv %d != expect %d", TagType, TagTypeExpected); rc = H_BadContents; goto ExitFunc; } } else { ErrOut(pContext, "BER PDU too short"); rc = H_TooShort; goto ExitFunc; } // Find tag length indicator, including escapes. if (*Frame >= LengthModifier_Indefinite && *Frame <= LengthModifier_4) { unsigned NLengthBytes; // Check zero size for LengthModifier_Indefinite. NLengthBytes = 4 + *Frame - LengthModifier_4; if (NLengthBytes == 0) NLengthBytes = 1; if (BytesLeft >= NLengthBytes) { Frame++; // Now at beginning of length bytes BytesLeft--; DataLength = 0; for (i = 0; i < NLengthBytes; i++) { DataLength = (DataLength << 8) + (unsigned)(*Frame); Frame++; } BytesLeft -= NLengthBytes; } else { ErrOut(pContext, "BER PDU too short"); rc = H_TooShort; goto ExitFunc; } } else { DataLength = *Frame; Frame++; BytesLeft--; } if (BytesLeft >= DataLength) { // Frame now points to beginning of contents. Fill out *Data with info // based on the tag type. switch (TagType) { case TagType_Boolean: case TagType_Integer: case TagType_Enumeration: // Fill in *Data with the actual data. Fill out *BytesLeft // so that we consume the contents. Discard if requested. if (Data != NULL) { unsigned Sum; Sum = 0; for (i = 0; i < DataLength; i++) { Sum = (Sum << 8) + *Frame; Frame++; } *Data = Sum; } else Frame += DataLength; BytesLeft -= DataLength; break; case TagType_OctetString: // Fill in *Data with a pointer into the frame of the // beginning of the data. if (Data != NULL) *Data = (UINT_PTR)Frame; Frame += DataLength; BytesLeft -= DataLength; break; // For these, we really just want to consume the tag and length case TagType_ConnectInitial: case TagType_Sequence: break; // MCS FUTURE: Add TagType_BitString default: ErrOut1(pContext, "Unknown TagType in DecodeTagBER (%u)", TagType); rc = H_BadContents; goto ExitFunc; } } else { ErrOut(pContext, "BER PDU too short"); rc = H_TooShort; goto ExitFunc; } ExitFunc: *newFrame = Frame; *OutBytesLeft = BytesLeft; *OutDataLength = DataLength; return rc; } /* * BER-encodes by parameter type. Advances pointer at *Frame past the encoded * bytes to allow for a current-pointer mechanism to be used. Parameter * usage as follows: * * Tag type Params * --------------------------------------------------------------- * bool, int, enum Data: The value to encode, maximum 0x7FFFFFFF. * DataLength: Unused. * * octet str, seq DataLength: Length of the sequence/string. * Data: Pointer to beginning of data to copy. * (Data can be NULL to prevent copying user data.) * * bitstring Not yet supported */ void EncodeTagBER ( PSDCONTEXT pContext, // For tracing. BYTE *Frame, int TagType, unsigned DataLength, UINT_PTR Data, unsigned *pNBytesConsumed, BYTE **newFrame) { int i, Length, NBytesConsumed; // Encode tag type. *Frame = (BYTE)TagType; Frame++; NBytesConsumed = 1; // Encode tag length indicator, including escapes, then encode the actual // tag data, if applicable. switch (TagType) { case TagType_Boolean: case TagType_Integer: case TagType_Enumeration: // Encode the bool or int size in bytes. if (Data < 0x80) Length = 1; else if (Data < 0x8000) Length = 2; else if (Data < 0x800000) Length = 3; else if (Data < 0x80000000) Length = 4; else { ErrOut(pContext, "Cannot BER-encode the size for an int/bool tag"); ASSERT(FALSE); break; } *Frame = (BYTE)Length; Frame++; NBytesConsumed++; // Encode the bool/int/enum data. for (i = 0; i < Length; i++) { *Frame = (BYTE)(Data >> (8 * (Length - 1 - i))); Frame++; } NBytesConsumed += Length; break; case TagType_OctetString: case TagType_Sequence: // Determine the length of DataLength. Escape if greater than 1. if (DataLength < 0x80) Length = 1; else if (DataLength < 0x8000) { Length = 2; *Frame = LengthModifier_2; Frame++; NBytesConsumed++; } else if (DataLength < 0x800000) { Length = 3; *Frame = LengthModifier_3; Frame++; NBytesConsumed++; } else if (DataLength < 0x80000000) { Length = 4; *Frame = LengthModifier_4; Frame++; NBytesConsumed++; } else { ErrOut(pContext, "Cannot BER-encode the length for an octet string tag"); ASSERT(FALSE); break; } for (i = 0; i < Length; i++) { *Frame = (BYTE)(DataLength >> (8 * (Length - 1 - i))); Frame++; } NBytesConsumed += Length; // Encode the string data. if (((BYTE *)Data) != NULL) { // This case is never used since we create headers only. // If this were to be used we would need to copy memory. memcpy(Frame, (BYTE *)Data, DataLength); Frame += DataLength; NBytesConsumed += DataLength; } break; // MCS FUTURE: Add TagType_BitString. } *newFrame = Frame; *pNBytesConsumed = NBytesConsumed; } /* * BER-encodes the given domain parameters. */ void EncodeDomainParameters( PSDCONTEXT pContext, // For tracing. BYTE *Frame, int *pNBytesConsumed, const DomainParameters *pDomParams, BYTE **newFrame) { BYTE *pSeqLength; unsigned NBytesConsumed, TotalBytes; // Encode the sequence tag type beginning manually. We'll fill in the // length after we're done with the rest of the domain parameters. *Frame = TagType_Sequence; pSeqLength = Frame + 1; Frame += 2; TotalBytes = 2; // Encode the 8 domain parameters. EncodeTagBER(pContext, Frame, TagType_Integer, 0, pDomParams->MaxChannels, &NBytesConsumed, newFrame); TotalBytes += NBytesConsumed; Frame = *newFrame; EncodeTagBER(pContext, Frame, TagType_Integer, 0, pDomParams->MaxUsers, &NBytesConsumed, newFrame); TotalBytes += NBytesConsumed; Frame = *newFrame; EncodeTagBER(pContext, Frame, TagType_Integer, 0, pDomParams->MaxTokens, &NBytesConsumed, newFrame); TotalBytes += NBytesConsumed; Frame = *newFrame; EncodeTagBER(pContext, Frame, TagType_Integer, 0, pDomParams->NumPriorities, &NBytesConsumed, newFrame); TotalBytes += NBytesConsumed; Frame = *newFrame; EncodeTagBER(pContext, Frame, TagType_Integer, 0, pDomParams->MinThroughput, &NBytesConsumed, newFrame); TotalBytes += NBytesConsumed; Frame = *newFrame; EncodeTagBER(pContext, Frame, TagType_Integer, 0, pDomParams->MaxDomainHeight, &NBytesConsumed, newFrame); TotalBytes += NBytesConsumed; Frame = *newFrame; EncodeTagBER(pContext, Frame, TagType_Integer, 0, pDomParams->MaxPDUSize, &NBytesConsumed, newFrame); TotalBytes += NBytesConsumed; Frame = *newFrame; EncodeTagBER(pContext, Frame, TagType_Integer, 0, pDomParams->ProtocolVersion, &NBytesConsumed, newFrame); TotalBytes += NBytesConsumed; Frame = *newFrame; *pNBytesConsumed = TotalBytes; *pSeqLength = TotalBytes - 2; } /* * BER-decodes domain parameters. Returns one of the H_... codes defined above. */ int DecodeDomainParameters( PSDCONTEXT pContext, // For tracing. BYTE *Frame, unsigned *BytesLeft, DomainParameters *pDomParams, BYTE **newFrame) { int Result; unsigned DataLength = 0; UINT_PTR Data = 0; // Get sequence indicator and block length. Result = DecodeTagBER(pContext, Frame, BytesLeft, TagType_Sequence, &DataLength, &Data, newFrame); if (Result == H_OK) { if (*BytesLeft >= DataLength) Frame = *newFrame; else return H_TooShort; } else { return Result; } // Get all 8 integer-tag values. Result = DecodeTagBER(pContext, Frame, BytesLeft, TagType_Integer, &DataLength, &Data, newFrame); if (Result == H_OK) { Frame = *newFrame; pDomParams->MaxChannels = (unsigned)Data; } else { return Result; } Result = DecodeTagBER(pContext, Frame, BytesLeft, TagType_Integer, &DataLength, &Data, newFrame); if (Result == H_OK) { Frame = *newFrame; pDomParams->MaxUsers = (unsigned)Data; } else { return Result; } Result = DecodeTagBER(pContext, Frame, BytesLeft, TagType_Integer, &DataLength, &Data, newFrame); if (Result == H_OK) { Frame = *newFrame; pDomParams->MaxTokens = (unsigned)Data; } else { return Result; } Result = DecodeTagBER(pContext, Frame, BytesLeft, TagType_Integer, &DataLength, &Data, newFrame); if (Result == H_OK) { Frame = *newFrame; pDomParams->NumPriorities = (unsigned)Data; } else { return Result; } Result = DecodeTagBER(pContext, Frame, BytesLeft, TagType_Integer, &DataLength, &Data, newFrame); if (Result == H_OK) { Frame = *newFrame; pDomParams->MinThroughput = (unsigned)Data; } else { return Result; } Result = DecodeTagBER(pContext, Frame, BytesLeft, TagType_Integer, &DataLength, &Data, newFrame); if (Result == H_OK) { Frame = *newFrame; pDomParams->MaxDomainHeight = (unsigned)Data; } else { return Result; } Result = DecodeTagBER(pContext, Frame, BytesLeft, TagType_Integer, &DataLength, &Data, newFrame); if (Result == H_OK) { Frame = *newFrame; pDomParams->MaxPDUSize = (unsigned)Data; } else { return Result; } Result = DecodeTagBER(pContext, Frame, BytesLeft, TagType_Integer, &DataLength, &Data, newFrame); if (Result == H_OK) { Frame = *newFrame; pDomParams->ProtocolVersion = (unsigned)Data; } else { return Result; } return H_OK; } /* * PDU 101 * * Connect-Initial ::= [APPLICATION 101] IMPLICIT SEQUENCE { * callingDomainSelector OCTET STRING, * calledDomainSelector OCTET STRING, * upwardFlag BOOLEAN, * targetParameters DomainParameters, * minimumParameters DomainParameters, * maximumParameters DomainParameters, * userData OCTET STRING * } * * Returns FALSE if the in parameters are not acceptable. */ BOOLEAN NegotiateDomParams( PDomain pDomain, DomainParameters *pTarget, DomainParameters *pMin, DomainParameters *pMax, DomainParameters *pOut) { // Maximum channels. if (pTarget->MaxChannels >= RequiredMinChannels) { pOut->MaxChannels = pTarget->MaxChannels; } else if (pMax->MaxChannels >= RequiredMinChannels) { pOut->MaxChannels = RequiredMinChannels; } else { ErrOut(pDomain->pContext, "Could not negotiate max channels"); return FALSE; } // Maximum users. if (pTarget->MaxUsers >= RequiredMinUsers) { pOut->MaxUsers = pTarget->MaxUsers; } else if (pMax->MaxUsers >= RequiredMinUsers) { pOut->MaxUsers = RequiredMinUsers; } else { ErrOut(pDomain->pContext, "Could not negotiate max users"); return FALSE; } // Maximum tokens. We don't implement tokens right now, so just take // the target number and we'll return an error if they try to use them. //MCS FUTURE: This needs to be negotiated if tokens are implemented. pOut->MaxTokens = pTarget->MaxTokens; // Number of data priorities. We accept only one priority. if (pMin->NumPriorities <= RequiredPriorities) { pOut->NumPriorities = RequiredPriorities; } else { ErrOut(pDomain->pContext, "Could not negotiate # priorities"); return FALSE; } // Minimum throughput. We don't care about this, take whatever. pOut->MinThroughput = pTarget->MinThroughput; // Maximum domain height. We only allow a height of 1 in this product. //MCS FUTURE: This needs to change if we support deeper domains. if (pTarget->MaxDomainHeight == RequiredDomainHeight || pMin->MaxDomainHeight <= RequiredDomainHeight) { pOut->MaxDomainHeight = RequiredDomainHeight; } else { ErrOut(pDomain->pContext, "Could not negotiate max domain height"); return FALSE; } // Max MCS PDU size. Minimum required for headers and lowest X.224 // allowable size. Max was negotiated by X.224. if (pTarget->MaxPDUSize >= RequiredMinPDUSize) { if (pTarget->MaxPDUSize <= pDomain->MaxX224DataSize) { pOut->MaxPDUSize = pTarget->MaxPDUSize; } else if (pMin->MaxPDUSize >= RequiredMinPDUSize && pMin->MaxPDUSize <= pDomain->MaxX224DataSize) { // Take maximum possible size as long as we're within range. pOut->MaxPDUSize = pDomain->MaxX224DataSize; } else { ErrOut(pDomain->pContext, "Could not negotiate max PDU size, " "sender outside X.224 negotiated limits"); return FALSE; } } else { if (pMax->MaxPDUSize >= RequiredMinPDUSize) { pOut->MaxPDUSize = pMax->MaxPDUSize; } else { ErrOut(pDomain->pContext, "Could not negotiate max PDU size, " "sender max too small"); return FALSE; } } // MCS protocol version. We support only version 2. if (pTarget->ProtocolVersion == RequiredProtocolVer || (pMin->ProtocolVersion <= RequiredProtocolVer && pMax->ProtocolVersion >= RequiredProtocolVer)) { pOut->ProtocolVersion = RequiredProtocolVer; } else { ErrOut(pDomain->pContext, "Could not negotiate protocol version"); return FALSE; } return TRUE; } BOOLEAN __fastcall HandleConnectInitial( PDomain pDomain, BYTE *Frame, unsigned BytesLeft, unsigned *pNBytesConsumed) { int Result; BYTE *SaveFrame, *pUserData, *pCPinBuf, *newFrame; UINT_PTR Data = 0; NTSTATUS Status; unsigned DataLength = 0; unsigned SaveBytesLeft; unsigned PDULength = 0; DomainParameters TargetParams, MinParams, MaxParams; ConnectProviderIndicationIoctl CPin; if (pDomain->State == State_X224_Connected) { // Save for error handling below. SaveBytesLeft = BytesLeft; newFrame = SaveFrame = Frame; // Get the PDU length, verify it against BytesLeft. Result = DecodeTagBER(pDomain->pContext, Frame, &BytesLeft, TagType_ConnectInitial, &PDULength, NULL, &newFrame); if (Result == H_OK) { if (BytesLeft >= PDULength) Frame = newFrame; else return FALSE; } else { goto BadResult; } } else { ErrOut(pDomain->pContext, "Connect-Initial PDU received when not in " "state X224_Connected"); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_MCS_UnexpectedConnectInitialPDU, Frame, BytesLeft); // Consume all the data given to us. *pNBytesConsumed = BytesLeft; return TRUE; } // Decode and skip calling domain selector. Result = DecodeTagBER(pDomain->pContext, Frame, &BytesLeft, TagType_OctetString, &DataLength, NULL, &newFrame); if (Result == H_OK) Frame = newFrame; else goto BadResult; // Decode and skip called domain selector. Result = DecodeTagBER(pDomain->pContext, Frame, &BytesLeft, TagType_OctetString, &DataLength, NULL, &newFrame); if (Result == H_OK) Frame = newFrame; else goto BadResult; // Decode Upward boolean. Result = DecodeTagBER(pDomain->pContext, Frame, &BytesLeft, TagType_Boolean, &DataLength, &Data, &newFrame); if (Result == H_OK) { Frame = newFrame; CPin.bUpwardConnection = (Data ? TRUE : FALSE); } else { goto BadResult; } // Decode target, max, min domain parameters. We will handle internal // negotiation for these parameters and pass up to the MUX only // the results, if the negotiation can succeed. Result = DecodeDomainParameters(pDomain->pContext, Frame, &BytesLeft, &TargetParams, &newFrame); if (Result == H_OK) Frame = newFrame; else goto BadResult; Result = DecodeDomainParameters(pDomain->pContext, Frame, &BytesLeft, &MinParams, &newFrame); if (Result == H_OK) Frame = newFrame; else goto BadResult; Result = DecodeDomainParameters(pDomain->pContext, Frame, &BytesLeft, &MaxParams, &newFrame); if (Result == H_OK) Frame = newFrame; else goto BadResult; // Get the user data (an octet string). After this Frame should point to // the end of the user data. Result = DecodeTagBER(pDomain->pContext, Frame, &BytesLeft, TagType_OctetString, &CPin.UserDataLength, &Data, &newFrame); if (Result == H_OK) { Frame = newFrame; pUserData = (BYTE *)Data; *pNBytesConsumed = SaveBytesLeft - BytesLeft; } else { goto BadResult; } // Check maximum user data size. if (CPin.UserDataLength > MaxGCCConnectDataLength) { POUTBUF pOutBuf; ICA_CHANNEL_COMMAND Command; ErrOut(pDomain->pContext, "HandleConnectInitial(): Attached user data " "is too large, returning error and failing connection"); // Alloc OutBuf for sending PDU. // This allocation is vital to the session and must succeed. do { Status = IcaBufferAlloc(pDomain->pContext, FALSE, TRUE, ConnectResponseHeaderSize, NULL, &pOutBuf); if (Status != STATUS_SUCCESS) ErrOut(pDomain->pContext, "Could not allocate an OutBuf for a " "connect-response PDU, retrying"); } while (Status != STATUS_SUCCESS); // Fill in PDU. // Encode PDU header. Param 2, the called connect ID, does not need to // be anything special because we do not allow extra sockets to be // opened for other data priorities. CreateConnectResponseHeader(pDomain->pContext, RESULT_UNSPECIFIED_FAILURE, 0, &pDomain->DomParams, 0, pOutBuf->pBuffer, &pOutBuf->ByteCount); // Send the PDU. Status = SendOutBuf(pDomain, pOutBuf); if (!NT_SUCCESS(Status)) { ErrOut(pDomain->pContext, "Could not send connect-response PDU " "to TD"); // Ignore error -- this should only occur if stack is going down. return TRUE; } // Signal that we need to drop the link. Command.Header.Command = ICA_COMMAND_BROKEN_CONNECTION; Command.BrokenConnection.Reason = Broken_Unexpected; Command.BrokenConnection.Source = BrokenSource_Server; Status = IcaChannelInput(pDomain->pContext, Channel_Command, 0, NULL, (BYTE *)&Command, sizeof(Command)); if (!NT_SUCCESS(Status)) ErrOut(pDomain->pContext, "HandleConnectInitial(): Could not " "send BROKEN_CONN upward"); return TRUE; } // Domain parameters negotiation. if (NegotiateDomParams(pDomain, &TargetParams, &MinParams, &MaxParams, &CPin.DomainParams)) { pDomain->DomParams = CPin.DomainParams; } else { MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_MCS_UnnegotiableDomainParams, Frame, BytesLeft); return TRUE; } // Calculate the MaxSendSize. This is the maximum PDU size minus the // maximum possible number of bytes for MCS headers and ASN.1 // segmentation. pDomain->MaxSendSize = CPin.DomainParams.MaxPDUSize - 6 - GetTotalLengthDeterminantEncodingSize( CPin.DomainParams.MaxPDUSize); // Fill in remaining CPin fields and send to MCSMUX // MCS FUTURE: hConn should point to a real Connection object. CPin.Header.hUser = NULL; // Signals node controller traffic. CPin.Header.Type = MCS_CONNECT_PROVIDER_INDICATION; CPin.hConn = (PVOID) 1; // Non-NULL so we know this is remote connection. RtlCopyMemory(CPin.UserData, pUserData, CPin.UserDataLength); // Set state for this connection, we are waiting for a reply from NC. pDomain->State = State_ConnectProvIndPending; ASSERT(pDomain->bChannelBound); TraceOut(pDomain->pContext, "HandleConnectInitial(): Sending " "CONNECT_PROVIDER_IND upward"); Status = IcaChannelInput(pDomain->pContext, Channel_Virtual, Virtual_T120ChannelNum, NULL, (BYTE *)&CPin, sizeof(CPin)); if (!NT_SUCCESS(Status)) { ErrOut(pDomain->pContext, "ChannelInput failed on " "connect-provider indication"); // Ignore errors here. This should only happen if stack is going down. return TRUE; } return TRUE; BadResult: if (Result == H_TooShort) return FALSE; // Must be H_BadContents. ErrOut(pDomain->pContext, "HandleConnectInitial(): Could not parse PDU, " "returning PDU reject"); ReturnRejectPDU(pDomain, Diag_InvalidBEREncoding, SaveFrame, SaveBytesLeft - BytesLeft); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_MCS_ConnectPDUBadPEREncoding, Frame, BytesLeft); // Attempt to skip the entire PDU. *pNBytesConsumed = SaveBytesLeft; // Return FALSE to force the caller to fail. return FALSE; } /* * PDU 102 * * Connect-Response ::= [APPLICATION 102] IMPLICIT SEQUENCE { * result Result, * calledConnectId INTEGER (0..MAX), * domainParameters DomainParameters, * userData OCTET STRING * } */ // pBuffer is assumed to point to a buffer of at least size given // by macro ConnectResponseHeaderSize; X.224 header will start here. // Actual number of bytes used for the encoding is returned in // *pNBytesConsumed. // We do not encode the user data, but instead just the header, which // allows some optimization by allowing the header to be encoded // and copied to the beginning of user data. void CreateConnectResponseHeader( PSDCONTEXT pContext, // For tracing. MCSResult Result, int CalledConnectID, DomainParameters *pDomParams, unsigned UserDataLength, BYTE *pBuffer, unsigned *pNBytesConsumed) { BYTE *OutFrame, *newFrame; unsigned NBytesConsumed, TotalSize, EncodeLength; // Set up for creating the PDU. OutFrame = pBuffer + X224_DataHeaderSize; NBytesConsumed = 0; // Encode the BER prefix, PDU type, and leave space for the PDU length. // Note that the length is the number of bytes following this length // indicator. // The most-oft-encountered case is where the PDU length is less than 128 // bytes. So, special-case larger sizes at the end of the function // when we know the total size. OutFrame[0] = MCS_CONNECT_PDU; OutFrame[1] = MCS_CONNECT_RESPONSE_ENUM; // Skip OutFrame[2] for the default 1-byte (<= 128) size. OutFrame += 3; TotalSize = 3; // Encode Result, CalledConnectID, DomParams. We use OutFrame // as a current pointer. EncodeTagBER(pContext, OutFrame, TagType_Enumeration, 0, Result, &NBytesConsumed, &newFrame); TotalSize += NBytesConsumed; OutFrame = newFrame; EncodeTagBER(pContext, OutFrame, TagType_Integer, 0, CalledConnectID, &NBytesConsumed, &newFrame); TotalSize += NBytesConsumed; OutFrame = newFrame; EncodeDomainParameters(pContext, OutFrame, &NBytesConsumed, pDomParams, &newFrame); TotalSize += NBytesConsumed; OutFrame = newFrame; // Encode only the length bytes, not the user data body. EncodeTagBER(pContext, OutFrame, TagType_OctetString, UserDataLength, (UINT_PTR)NULL, &NBytesConsumed, &newFrame); TotalSize += NBytesConsumed; OutFrame = newFrame; // Encode the final size. Here we special-case a too-large size by // shifting data around. The large size is the exceptional case. EncodeLength = TotalSize - 3 + UserDataLength; if (EncodeLength < 128) { pBuffer[2 + X224_DataHeaderSize] = (BYTE)EncodeLength; } else { unsigned i, Len = 0; WarnOut(pContext, "CreateConnRespHeader(): Perf hit from too-large " "PDU size"); // Since we can only send up to 64K bytes, the length determinant // cannot be any more than 3 bytes long. ASSERT(EncodeLength < 65535); if (EncodeLength < 0x8000) Len = 2; else if (EncodeLength < 0x800000) Len = 3; else ASSERT(FALSE); // Size escape comes first. pBuffer[2 + X224_DataHeaderSize] = LengthModifier_2 + Len - 2; RtlMoveMemory(pBuffer + 3 + X224_DataHeaderSize + Len, pBuffer + 3 + X224_DataHeaderSize, EncodeLength - 3); for (i = 1; i <= Len; i++) { pBuffer[3 + X224_DataHeaderSize + Len - i] = (BYTE)EncodeLength; EncodeLength >>= 8; } // We already included one byte of the length encoding above, but // now we need to also skip the length escape and the encoded length. TotalSize += Len; } // Set up X224 header based on the final size of the packet. CreateX224DataHeader(pBuffer, TotalSize + UserDataLength, TRUE); *pNBytesConsumed = X224_DataHeaderSize + TotalSize; } #ifdef MCS_Future BOOLEAN __fastcall HandleConnectResponse( PDomain pDomain, BYTE *Frame, unsigned BytesLeft, unsigned *pNBytesConsumed) { //MCS FUTURE: This will be needed to handle the future case where we initiate //connections for joins/invites. ErrOut(pDomain->pContext, "Connect Response PDU received, " "this should never happen"); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_MCS_UnsupportedConnectPDU, Frame, BytesLeft); // Consume all the data given to us. *pNBytesConsumed = BytesLeft; return TRUE; } #endif // MCS_Future /* * PDU 103 * * Connect-Additional ::= [APPLICATION 103] IMPLICIT SEQUENCE { * calledConnectId INTEGER (0..MAX), * dataPriority DataPriority * } * * No Create() funcion, we never expect to initiate these PDUs. * * We do not handle these PDUs for this Hydra release, since in the Citrix * framework there can be only one connection at a time. Domain parameters * should have been negotiated to only one connection handling all SendData * priorities. */ #ifdef MCS_Future BOOLEAN __fastcall HandleConnectAdditional( PDomain pDomain, BYTE *Frame, unsigned BytesLeft, unsigned *pNBytesConsumed) { ErrOut(pDomain->pContext, "Connect-additional PDU received, " "this should never happen"); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_MCS_UnsupportedConnectPDU, Frame, BytesLeft); // Consume all the data given to us. *pNBytesConsumed = BytesLeft; return TRUE; } #endif // MCS_Future /* * PDU 104 * * Connect-Result ::= [APPLICATION 104] IMPLICIT SEQUENCE { * result Result * } * * No Create() function, we never expect to initiate these PDUs. * * We do not handle these PDUs for this Hydra release, since in the Citrix * framework there can be only one connection at a time. */ #ifdef MCS_Future BOOLEAN __fastcall HandleConnectResult( PDomain pDomain, BYTE *Frame, unsigned BytesLeft, unsigned *pNBytesConsumed) { ErrOut(pDomain->pContext, "Connect-result PDU received, " "this should never happen"); MCSProtocolErrorEvent(pDomain->pContext, pDomain->pStat, Log_MCS_UnsupportedConnectPDU, Frame, BytesLeft); // Consume all the data given to us. *pNBytesConsumed = BytesLeft; return TRUE; } #endif // MCS_Future