#include "precomp.h"
DEBUG_FILEZONE(ZONE_T120_MCSNC);
/*
 *	omcscode.cpp
 *	
 *	Copyright (c) 1993 - 1996 by DataBeam Corporation, Lexington, KY
 *
 *	Abstract:
 *		This is the implementation file for the CMCSCoder class.  This class
 *		is responsible for encoding and decoding T.125 PDU's using ASN.1 
 *		encoding rules via the ASN.1 toolkit.  This class is also capable
 *		of determining the size of both the encoded and decoded PDU's and is 
 *		capable of making copies of each of the PDU's. 
 *
 *	Private Instance Variables:
 *		Encoding_Rules_Type
 *			This variable holds the type of encoding rules which are currently
 *			being utilized.
 *
 *	Private Member Functions:
 *
 *		Copy***
 *			Private member functions exist which are capable of making complete
 *			copies of the decoded "Send Data" PDU data structures.  These 
 *			routines copy not only the data contained within the structure but 
 *			also any data which is referenced by the pointers held within the 
 *			structure.
 *
 *		SetEncodingRules
 *			This routine is used to switch between using Basic Encoding Rules
 *			(BER) and Packed Encoding Rules (PER).  This routine also updates
 *			the private instance variables used to hold values for the minimum
 *			and maximum amount of overhead associated with the "send data" PDUs.
 *
 *	Caveats:
 *		None.
 *
 *	Author:
 *		John B. O'Nan
 */

#include "omcscode.h"

/*
 * Macros.
 */
#define		BOOLEAN_TAG						0x01
#define		INTEGER_TAG						0x02
#define		BIT_STRING_TAG	   				0x03
#define		OCTET_STRING_TAG	   			0x04
#define		ENUMERATED_TAG	   				0x0a

#define		SEQUENCE						0x30
#define		SETOF							0x31
#define		INDEFINITE_LENGTH				0x80
#define		ONE_BYTE_LENGTH					0x81
#define		TWO_BYTE_LENGTH					0x82
#define		THREE_BYTE_LENGTH				0x83
#define		FOUR_BYTE_LENGTH				0x84
#define		END_OF_CONTENTS					0x00

#define		CONSTRUCTED_TAG_ZERO			0xa0	
#define		CONSTRUCTED_TAG_ONE				0xa1	
#define		CONSTRUCTED_TAG_TWO				0xa2	
#define		CONSTRUCTED_TAG_THREE			0xa3	
#define		CONSTRUCTED_TAG_FOUR			0xa4

/*
 *	This is a global variable that has a pointer to the one MCS coder that
 *	is instantiated by the MCS Controller.  Most objects know in advance 
 *	whether they need to use the MCS or the GCC coder, so, they do not need
 *	this pointer in their constructors.
 */
CMCSCoder	*g_MCSCoder;

/*
 *	The following array contains a template for the X.224 data header.
 *	The 5 of the 7 bytes that it initializes are actually sent to the
 *	wire.  Bytes 3 and 4 will be set to contain the size of the PDU.
 *	The array is only used when we encode a data PDU.
 */
UChar g_X224Header[] = { 3, 0, 0, 0, 2, DATA_PACKET, EOT_BIT };

/*
 *	CMCSCoder ()
 *
 *	Public
 *
 *	Functional Description:
 *		This is the constructor for the CMCSCoder class.  It initializes
 *		the ASN.1 encoder/decoder, saves the current encoding rules type,
 *		and sets values for the highest and lowest overhead in the "send data"
 *		PDU's.
 */
CMCSCoder::CMCSCoder ()
:m_pEncInfo(NULL),
 m_pDecInfo(NULL)
{
	Encoding_Rules_Type = BASIC_ENCODING_RULES;
// lonchanc: We should move Init out of constructor. However,
// to minimize the changes in the GCC/MCS code, we put it here for now.
// Otherwise, we need to change MCS and Packet interfaces.
// We will move it out and call Init() separately.
	Init();
}

BOOL CMCSCoder::Init ( void )
{
	BOOL fRet = FALSE;
	MCSPDU_Module_Startup();
	if (MCSPDU_Module != NULL)
	{
		if (ASN1_CreateEncoder(
                            MCSPDU_Module,	// ptr to mdule
                            &m_pEncInfo,	// ptr to encoder info
                            NULL,			// buffer ptr
                            0,				// buffer size
                            NULL)			// parent ptr
			== ASN1_SUCCESS)
		{
			ASSERT(m_pEncInfo != NULL);
			m_pEncInfo->cbExtraHeader = PROTOCOL_OVERHEAD_X224;
			fRet = (ASN1_CreateDecoder(
                                MCSPDU_Module,	// ptr to mdule
                                &m_pDecInfo,	// ptr to decoder info
                                NULL,			// buffer ptr
                                0,				// buffer size
                                NULL)			// parent ptr
					== ASN1_SUCCESS);
			ASSERT(fRet && m_pDecInfo != NULL);
		}
	}
	ASSERT(fRet);
	return fRet;
}

/*
 *	~CMCSCoder ()
 *
 *	Public
 *
 *	Functional Description:
 *		This is a virtual destructor.  It is used to clean up after ASN.1.
 */
CMCSCoder::~CMCSCoder ()
{
	if (MCSPDU_Module != NULL)
	{
		ASN1_CloseEncoder(m_pEncInfo);
		ASN1_CloseDecoder(m_pDecInfo);
	    MCSPDU_Module_Cleanup();
	}
}

/*
 *	void	Encode ()
 *
 *	Public
 *
 *	Functional Description:
 *		This function encodes MCS protocol data units (PDU's) into ASN.1 
 *		compliant byte streams using the ASN.1 toolkit.
 *		The encode happens in a coder-allocated buffer.
 */
BOOL	CMCSCoder::Encode(LPVOID			pdu_structure,
							int				pdu_type,
							UINT			rules_type,
							LPBYTE			*encoding_buffer,
							UINT			*encoding_buffer_length)
{
	BOOL					fRet = TRUE;
	BOOL					send_data_pdu = FALSE;
	int						return_value;

	//UINT					encoding_length;
	UShort					initiator;
	LPBYTE					buffer_pointer;
	PSendDataRequestPDU		send_data;
	UINT					PDUChoice;
	BOOL					bBufferAllocated;
	PMemory					memory;

	/*
	 * Check to make sure the encoding rules type is properly set.
	 */
	ASSERT(rules_type == PACKED_ENCODING_RULES || pdu_type == CONNECT_MCS_PDU);
	if (pdu_type == DOMAIN_MCS_PDU)
	{
		/*
		 *	Set PDUChoice to the type of MCS PDU we need to encode.
		 *	Also, determine if this is a data PDU.
		 */
		PDUChoice = (unsigned int) ((PDomainMCSPDU) pdu_structure)->choice;
		if ((PDUChoice == SEND_DATA_REQUEST_CHOSEN) ||
			(PDUChoice == SEND_DATA_INDICATION_CHOSEN) ||
			(PDUChoice == UNIFORM_SEND_DATA_REQUEST_CHOSEN) ||
			(PDUChoice == UNIFORM_SEND_DATA_INDICATION_CHOSEN)) {
			send_data_pdu = TRUE;
			send_data = &((PDomainMCSPDU) pdu_structure)->u.send_data_request;
			bBufferAllocated = (*encoding_buffer == NULL);
			if (bBufferAllocated) {
				// We have to allocate the encoded buffer.
				DBG_SAVE_FILE_LINE
				memory = AllocateMemory (NULL,
								send_data->user_data.length + MAXIMUM_PROTOCOL_OVERHEAD,
								SEND_PRIORITY);
				if (memory != NULL) {
					buffer_pointer = *encoding_buffer = (LPBYTE) memory->GetPointer();
				}
				else {
					WARNING_OUT (("CMCSCoder::Encode: Failed to allocate space for "
								"encoded data PDU for send."));
					fRet = FALSE;
					ASSERT (*encoding_buffer == NULL);
					goto MyExit;
				}
			}
			else {
				// All the space needed here has been pre-allocated
				buffer_pointer = *encoding_buffer;
			}
		}

		/*
		 *	Check if this is a data PDU
		 */
		if (send_data_pdu)
		{
#ifdef ENABLE_BER
			
			/*
			 * If we are currently using Basic Encoding Rules.
			 */
			if (Encoding_Rules_Type == BASIC_ENCODING_RULES)
			{
				/*
				 * The long variant of length must be used if the octet string
				 * is longer than 127 bytes.  The upper bit of the length byte
				 * is set and the lower bits indicate the number of length bytes 
				 * which will follow.
				 */
				*(buffer_pointer--) = (UChar)send_data->user_data.length;
				if (send_data->user_data.length > 127)
				{
					*(buffer_pointer--) = (UChar)(send_data->user_data.length >> 8);
					*(buffer_pointer--) = TWO_BYTE_LENGTH;
					encoding_length = 3;
				}
				else 
				{
					encoding_length = 1;
				}

				/*
				 * Encode the "user data" octet string.
				 */										
				*(buffer_pointer--) = OCTET_STRING_TAG;

				/*
				 * Encode the "segmentation" bit string field.  The identifier
				 * is followed by a length of 2 and a byte indicating that 6
				 * bits are unused in the actual bit string byte.
				 */
				*(buffer_pointer--) = (UChar) send_data->segmentation;
				*(buffer_pointer--) = 0x06;
				*(buffer_pointer--) = 0x02;
				*(buffer_pointer--) = BIT_STRING_TAG;

				/* 
				 * Encode the enumerated "data priority" field.
				 */
				*(buffer_pointer--) = (UChar)send_data->data_priority;
				*(buffer_pointer--) = 0x01;
				*(buffer_pointer--) = ENUMERATED_TAG;

				/*
				 * Encode the integer "channel ID" field.
				 */
				*(buffer_pointer--) = (UChar)send_data->channel_id;
				if (send_data->channel_id < 128)
				{
					*(buffer_pointer--) = 0x01;
					encoding_length += 10;
				}
				else if (send_data->channel_id < 32768L)
				{
					*(buffer_pointer--) = (UChar)(send_data->channel_id >> 8);
					*(buffer_pointer--) = 0x02;
					encoding_length += 11;
				}
				else
				{
					*(buffer_pointer--) = (UChar)(send_data->channel_id >> 8);
					*(buffer_pointer--) = (UChar)(send_data->channel_id >> 16);
					*(buffer_pointer--) = 0x03;
					encoding_length += 12;
				}
				*(buffer_pointer--) = INTEGER_TAG;

				/*
				 * Encode the integer "initiator" field.
				 */
				*(buffer_pointer--) = (UChar)send_data->initiator;
				*(buffer_pointer--) = (UChar)(send_data->initiator >> 8);
				if (send_data->initiator < 32768L)
				{
					*(buffer_pointer--) = 0x02;
					encoding_length += 4;
				}
				else
				{
					*(buffer_pointer--) = (UChar)(send_data->initiator >> 16);
					*(buffer_pointer--) = 0x03;
					encoding_length += 5;
				}
				*(buffer_pointer--) = INTEGER_TAG;

				*(buffer_pointer--) = INDEFINITE_LENGTH; 
				
				switch (PDUChoice)
				{	
					case SEND_DATA_REQUEST_CHOSEN:
						*buffer_pointer = SEND_DATA_REQUEST;
						break;
					case SEND_DATA_INDICATION_CHOSEN:
						*buffer_pointer = SEND_DATA_INDICATION;
						break;
					case UNIFORM_SEND_DATA_REQUEST_CHOSEN:
						*buffer_pointer = UNIFORM_SEND_DATA_REQUEST;
						break;
					case UNIFORM_SEND_DATA_INDICATION_CHOSEN:
						*buffer_pointer = UNIFORM_SEND_DATA_INDICATION;
						break;
				}

				// Set the returned pointer to the beginning of the encoded packet
				PUChar	temp = *encoding_buffer;
				*encoding_buffer = buffer_pointer;

				/*
				 * Encode the end-of-contents marker for the "Send Data" PDU.
				 * This goes after the data, at the end of the PDU.
				 */
				buffer_pointer = temp + (send_data->user_data.length + 
								(MAXIMUM_PROTOCOL_OVERHEAD_FRONT + 1));
				*(buffer_pointer++) = END_OF_CONTENTS;
				*buffer_pointer = END_OF_CONTENTS;

				// Set the returned length of the encoded packet
				*encoding_buffer_length = 
							encoding_length + send_data->user_data.length + 5;
			}
			/*
			 * If we are currently using Packed Encoding Rules.
			 */
			else
#endif // ENABLE_BER
			{	
				// Move the ptr past the X.224 header.
				buffer_pointer += sizeof(X224_DATA_PACKET);
				
				switch (PDUChoice)
				{
					case SEND_DATA_REQUEST_CHOSEN:
						*buffer_pointer = PER_SEND_DATA_REQUEST;
						break;
					case SEND_DATA_INDICATION_CHOSEN:
						*buffer_pointer = PER_SEND_DATA_INDICATION;
						break;
					case UNIFORM_SEND_DATA_REQUEST_CHOSEN:
						*buffer_pointer = PER_UNIFORM_SEND_DATA_REQUEST;
						break;
					case UNIFORM_SEND_DATA_INDICATION_CHOSEN:
						*buffer_pointer = PER_UNIFORM_SEND_DATA_INDICATION;
						break;
				}
				buffer_pointer++;

				/*
				 * Encode the integer "initiator" field.  The lower bound must
				 * first be subtracted from the value to encode.
				 */
				initiator = send_data->initiator - INITIATOR_LOWER_BOUND;
				*(buffer_pointer++) = (UChar) (initiator >> 8);
				*(buffer_pointer++) = (UChar) initiator;

				/*
				 * Encode the integer "channel ID" field.
				 */
				*(buffer_pointer++) = (UChar)(send_data->channel_id >> 8);
				*(buffer_pointer++) = (UChar)(send_data->channel_id);

				/*
				 * Encode the "priority" and "segmentation" fields.
				 */
				*(buffer_pointer++) = (UChar)((send_data->data_priority << 6) |
										(send_data->segmentation >> 2));
				
				/* 
				 * Encode the "user data" octet string.  The octet strings are
				 * encoded differently depending upon their length.
				 */
				ASSERT (send_data->user_data.length < 16384);

				if (send_data->user_data.length <= 127)
				{
					*encoding_buffer_length = MAXIMUM_PROTOCOL_OVERHEAD - 1;
				}
				else
				{
					*(buffer_pointer++) = (UChar)(send_data->user_data.length >> 8) | 
											INDEFINITE_LENGTH;
					*encoding_buffer_length = MAXIMUM_PROTOCOL_OVERHEAD;
				}
				*buffer_pointer++ = (UChar)send_data->user_data.length;

				initiator = (UShort) (*encoding_buffer_length + send_data->user_data.length);
				
				// Set the returned length of the encoded PDU.
				if (bBufferAllocated || (send_data->segmentation & SEGMENTATION_BEGIN)) {
					/*
					 *	If the Encode operation allocates the space needed, or the space
					 *	was allocated by MCSGetBufferRequest (by a client) and this is
					 *	the 1st segment of the data in the buffer, the whole encoded PDU
					 *	is in contiguous space.  The total PDU size is returned in this
					 *	case.
					 *	However, in the case where the space was allocated by MCSGetBufferRequest,
					 *	the PDUs after the 1st one, will put the X.224 and MCS headers in 
					 *	a separate piece of memory (whose length is returned here), and the
					 *	data is still in the pre-allocated space.
					 */
					*encoding_buffer_length = (UINT) initiator;
				}

				/*
				 *	If the space was not preallocated, we need to copy the data
				 *	into the space allocated by the encoder.
				 */
				if (bBufferAllocated) {
					// We now need to copy the data into the encoded data PDU.
					memcpy (buffer_pointer, send_data->user_data.value,
							send_data->user_data.length);
					// Update the data ptr in the data packet
					send_data->user_data.value = (ASN1octet_t *) buffer_pointer;
				}
			}
		}
	}

	if (send_data_pdu == FALSE)
	{
		SetEncodingRules (rules_type);
		return_value = ASN1_Encode(m_pEncInfo,	// ptr to encoder info
									 pdu_structure,	// pdu data structure
									 pdu_type,		// pdu id
									 ASN1ENCODE_ALLOCATEBUFFER, // flags
									 NULL,			// do not provide buffer
									 0);			// buffer size if provided
		if (ASN1_FAILED(return_value))
		{
			ERROR_OUT(("CMCSCoder::Encode: ASN1_Encode failed, err=%d", return_value));
			ASSERT(FALSE);
			fRet = FALSE;
			goto MyExit;
		}
		ASSERT(return_value == ASN1_SUCCESS);
		/*
		 *	The encoded buffers returned by ASN.1 have preallocated the space
		 *	needed for the X.224 header.
		 */
		// len of encoded data in buffer
		*encoding_buffer_length = m_pEncInfo->len;
		initiator = (UShort) *encoding_buffer_length;
		// buffer to encode into
		*encoding_buffer = m_pEncInfo->buf;
	}

	// Now, add the X.224 header
	buffer_pointer = *encoding_buffer;
	memcpy (buffer_pointer, g_X224Header, PROTOCOL_OVERHEAD_X224);
	AddRFCSize (buffer_pointer, initiator);

MyExit:

	return fRet;
}

/*
 *	void 	Decode ()
 *
 *	Public
 *
 *	Functional Description:
 *		This function decodes ASN.1 compliant byte streams into the
 *		appropriate MCS PDU structures using the ASN.1 toolkit.
 *
 *	NOTE: For data PDUs, do NOT access pdecoding_buffer_length. It's set
 *			to NULL.
 */
BOOL	CMCSCoder::Decode(LPBYTE			encoded_buffer,
							UINT			encoded_buffer_length,
							int				pdu_type,
							UINT			rules_type,
							LPVOID			*pdecoding_buffer,
							UINT			*pdecoding_buffer_length)
{
	BOOL				fRet = TRUE;
	BOOL				send_data_pdu = FALSE;
    ASN1optionparam_s   OptParam;

	/*
	 * Check to make sure the encoding rules type is properly set.
	 */
	ASSERT(rules_type == PACKED_ENCODING_RULES || pdu_type == CONNECT_MCS_PDU);

	if (pdu_type == DOMAIN_MCS_PDU)
	{
			UChar					length;
			unsigned int			short_data;
			PUChar					buffer_pointer;
			PSendDataRequestPDU		send_data;
			PDomainMCSPDU			decoding_pdu;
			ASN1choice_t			choice;
			
		buffer_pointer = encoded_buffer;
		
#ifdef ENABLE_BER
		/*
		 * If we are currently using Basic Encoding Rules.
		 */
		if (Encoding_Rules_Type == BASIC_ENCODING_RULES)
		{
			switch (*(buffer_pointer++))
			{
				case SEND_DATA_REQUEST:
					((PDomainMCSPDU) decoding_buffer)->choice =
							SEND_DATA_REQUEST_CHOSEN;
					send_data_pdu = TRUE;
					break;

				case SEND_DATA_INDICATION:
					((PDomainMCSPDU) decoding_buffer)->choice =
							SEND_DATA_INDICATION_CHOSEN;
					send_data_pdu = TRUE;
					break;

				case UNIFORM_SEND_DATA_REQUEST:
					((PDomainMCSPDU) decoding_buffer)->choice =
							UNIFORM_SEND_DATA_REQUEST_CHOSEN;
					send_data_pdu = TRUE;
					break;

				case UNIFORM_SEND_DATA_INDICATION:
					((PDomainMCSPDU) decoding_buffer)->choice =
							UNIFORM_SEND_DATA_INDICATION_CHOSEN;
					send_data_pdu = TRUE;
					break;
			}

			if (send_data_pdu )
			{
				/*
				 * Get the pointer to the "Send Data" PDU.
				 */
				send_data = &((PDomainMCSPDU) decoding_buffer)->
												u.send_data_request;

				/*
				 * Retrieve one byte for the length and check to see which 
				 * length variant is being used.  If the long variant is being 
				 * used, move the buffer pointer past the length and set the 
				 * flag indicating that the indefinite length is not being used.
				 */
				length = *buffer_pointer;

				switch (length)
				{
					case ONE_BYTE_LENGTH: 
							buffer_pointer += 3;
							break;
					case TWO_BYTE_LENGTH: 
							buffer_pointer += 4;
							break;		
					case THREE_BYTE_LENGTH:
							buffer_pointer += 5;
							break;
					case FOUR_BYTE_LENGTH:
							buffer_pointer += 6;
							break;
					default:
							buffer_pointer += 2;
				}

				/*
				 * Decode the integer "initiator" field.  Increment the data 
				 * pointer past the integer identifier and retrieve the length 
				 * of the integer.
				 */
				length = *(buffer_pointer++);

				ASSERT ((length == 1) || (length == 2));
				if (length == 1)
					send_data->initiator = (UserID) *(buffer_pointer++);
				else if (length == 2)
				{
					send_data->initiator = ((UserID) *(buffer_pointer++)) << 8;
					send_data->initiator |= (UserID) *(buffer_pointer++);
				}
				else 
				{
					TRACE_OUT(("CMCSCoder::Decode: initiator field is longer than 2 bytes (%d bytes) in MCS Data packet.",  (UINT) length));
				}

				/*
				 * Decode the integer "channel ID" field.  Increment the data 
				 * pointer past the integer identifier and retrieve the length 
				 * of the integer.
				 */
				buffer_pointer++;
				length = *(buffer_pointer++);

				ASSERT ((length == 1) || (length == 2));
				if (length == 1)
					send_data->channel_id = (ChannelID) *(buffer_pointer++);
				else if (length == 2)
				{
					send_data->channel_id = ((ChannelID) *buffer_pointer++) << 8;
					send_data->channel_id |= (ChannelID) *(buffer_pointer++);
				}
				else 
				{
					TRACE_OUT(("CMCSCoder::Decode: channel_id field is longer than 2 bytes (%d bytes) in MCS Data packet.", (UINT) length));
				}

				/*
				 * Decode the enumerated "data priority" field.  Increment the
				 * data pointer past the identifier and length.
				 */
				buffer_pointer+=2;
				send_data->data_priority =(PDUPriority)*buffer_pointer;

				/*
				 * Decode the bit string "segmentation" field.  Increment the 
				 * data pointer past the bit string identifier, length, and the 
				 * "unused bits" byte and retrieve the "segmentation" flag.
				 */
				buffer_pointer += 4;
				send_data->segmentation = *buffer_pointer;

				/*
				 * Decode the "user data" octet string.	 Increment the data 
				 * pointer past the identifier.
				 */
				buffer_pointer += 2;

				/*
				 * Check to see which variant of the length is being used and
				 * then retrieve the length.
				 */
				length = *(buffer_pointer++);

				if (length & INDEFINITE_LENGTH)
				{
					if (length == ONE_BYTE_LENGTH)
						send_data->user_data.length = (unsigned int) *(buffer_pointer++);
					/*
					 * A length identifier of 0x82 indicates that two bytes are
					 * being used to hold the actual length so retrieve the two
					 * bytes to form the length.
					 */
					else if (length == TWO_BYTE_LENGTH)
					{
						send_data->user_data.length = 
								((unsigned int) *(buffer_pointer++)) << 8;
						send_data->user_data.length |= 
								(unsigned int) *(buffer_pointer++);
					}
				}
				else
					send_data->user_data.length = (unsigned int) length;

				// buffer_pointer now points to the 1st data byte
				send_data->user_data.value = buffer_pointer;
				*pulDataOffset = buffer_pointer - encoded_buffer;
			}
		}
		/*
		 * If we are currently using Packed Encoding Rules.
		 */
		else
#endif // ENABLE_BER
		{
			switch (*(buffer_pointer++))
			{
				case PER_SEND_DATA_REQUEST:
					choice = SEND_DATA_REQUEST_CHOSEN;
					send_data_pdu = TRUE;
					break;

				case PER_SEND_DATA_INDICATION:
					choice = SEND_DATA_INDICATION_CHOSEN;
					send_data_pdu = TRUE;
					break;

				case PER_UNIFORM_SEND_DATA_REQUEST:
					choice = UNIFORM_SEND_DATA_REQUEST_CHOSEN;
					send_data_pdu = TRUE;
					break;

				case PER_UNIFORM_SEND_DATA_INDICATION:
					choice = UNIFORM_SEND_DATA_INDICATION_CHOSEN;
					send_data_pdu = TRUE;
					break;
			}

			if (send_data_pdu)
			{
				decoding_pdu = (PDomainMCSPDU) pdecoding_buffer; 

				// Store the choice field
				decoding_pdu->choice = choice;
				/*
				 * Get the pointer to the "Send Data" PDU.
				 */
				send_data = &decoding_pdu->u.send_data_request;

				/*
				 * Decode the integer "initiator" field.
				 */
				short_data = ((unsigned int) *(buffer_pointer++)) << 8;
				short_data |= (unsigned int) *(buffer_pointer++);
				send_data->initiator = (UserID) short_data + INITIATOR_LOWER_BOUND;

				/*
				 * Decode the integer "channel ID" field. 
				 */
				send_data->channel_id = ((ChannelID) *(buffer_pointer++)) << 8;
				send_data->channel_id |= (ChannelID) *(buffer_pointer++);

				/*
				 * Decode the enumerated "data priority" field and the
				 * "segmentation" field.
				 */
				send_data->data_priority = 
						(PDUPriority)((*buffer_pointer >> 6) & 0x03);
				send_data->segmentation = (*(buffer_pointer++) << 2) & 0xc0; 

				/*
				 * Decode the "user data" octet string.	 Check to see which 
				 * variant of the length is being used and then retrieve the 
				 * length.
				 */
				length = *(buffer_pointer++);

				if (length & INDEFINITE_LENGTH)
				{
					ASSERT ((length & 0x40) == 0);
					
					/*
					 * If bit 7 is set the length is greater than 127 but
					 * less than 16K.
					 *
					 *	ChristTs: We no longer handle the case where the data length
					 *	was higher than 16K. Our Max PDU size is 4K.
					 */
					short_data = (unsigned int) ((length & 0x3f) << 8);
					send_data->user_data.length = 
								short_data | ((unsigned int) *(buffer_pointer++));
				}
				/*
				 * If bit 7 is not set then the length is less than 128 and is
				 * contained in the retrieved byte.
				 */
				else
				{
					send_data->user_data.length = (UShort) length;
				}

				// buffer_pointer now points to the 1st data byte
				send_data->user_data.value = buffer_pointer;
			}
		}
	}
	
	if (send_data_pdu == FALSE)
	{
		int 	return_value;
		//void	*pDecodedData;

		SetEncodingRules (rules_type);

		return_value = ASN1_Decode(m_pDecInfo,// ptr to decoder info
							pdecoding_buffer,		// destination buffer
							pdu_type,				// pdu type
							ASN1DECODE_SETBUFFER,	// flags
							encoded_buffer,			// source buffer
							encoded_buffer_length);	// source buffer size
		if (ASN1_FAILED(return_value))
		{
			ERROR_OUT(("CMCSCoder::Decode: ASN1_Decode failed, err=%d", return_value));
			ASSERT(FALSE);
			fRet = FALSE;
			goto MyExit;
		}

        OptParam.eOption = ASN1OPT_GET_DECODED_BUFFER_SIZE;
		return_value = ASN1_GetDecoderOption(m_pDecInfo, &OptParam);
		if (ASN1_FAILED(return_value))
		{
			ERROR_OUT(("CMCSCoder::Decode: ASN1_GetDecoderOption failed, err=%d", return_value));
			ASSERT(FALSE);
			fRet = FALSE;
			goto MyExit;
		}
        *pdecoding_buffer_length = OptParam.cbRequiredDecodedBufSize;

		ASSERT((return_value == ASN1_SUCCESS) && (*pdecoding_buffer_length > 0));
	}

MyExit:

	return fRet;
}

/*
 *	PacketCoderError	ReverseDirection ()
 *
 *	Private
 *
 *	Functional Description:
 *		This routine is used to convert data request PDU's into data indication
 *		PDU's and vice versa.
 */
Void CMCSCoder::ReverseDirection (LPBYTE	encoded_buffer)
{
	encoded_buffer += PROTOCOL_OVERHEAD_X224;
	switch (*encoded_buffer)
	{
		case PER_SEND_DATA_REQUEST:
			*encoded_buffer = PER_SEND_DATA_INDICATION;
			break;

		case PER_SEND_DATA_INDICATION:
			*encoded_buffer = PER_SEND_DATA_REQUEST;
			break;

		case PER_UNIFORM_SEND_DATA_REQUEST:
			*encoded_buffer = PER_UNIFORM_SEND_DATA_INDICATION;
			break;

		case PER_UNIFORM_SEND_DATA_INDICATION:
			*encoded_buffer = PER_UNIFORM_SEND_DATA_REQUEST;
			break;
		default:
			ASSERT (FALSE);
			break;
	}
}

/*
 *	void	SetEncodingRules ()
 *
 *	Private
 *
 *	Functional Description:
 *		This function is used to set the type (basic or packed) of encoding
 *		rules to be used.
 */
void CMCSCoder::SetEncodingRules (UINT	rules_type)
{
	/*
	 * If the rules type is changing, set our rules instance variable and reset
	 * the variables which hold the amount of overhead associated with the
	 * "SendData" PDU's.
	 */
	Encoding_Rules_Type = rules_type;
}

/*
 *	BOOL	IsMCSDataPacket ()
 *
 *	Public
 *
 *	Functional Description:
 *		This function determines whether the encoded packet is an MCS Data packet
 *		or not.
 *
 *	Return value:
 *		TRUE, if the packet is an MCS Data packet. FALSE, otherwise.
 */
BOOL CMCSCoder::IsMCSDataPacket(LPBYTE encoded_buffer, UINT rules_type)
{
	UChar		identifier;

	/*
	 * Retrieve the identifier from the encoded data.
	 */
	identifier = *encoded_buffer;

	if (rules_type == BASIC_ENCODING_RULES)
	{
		if (	(identifier == SEND_DATA_REQUEST) || 
				(identifier == SEND_DATA_INDICATION) || 
				(identifier == UNIFORM_SEND_DATA_REQUEST) || 
				(identifier == UNIFORM_SEND_DATA_INDICATION))
		{
			return TRUE;
		}
	}
	else
	{
		if (	(identifier == PER_SEND_DATA_REQUEST) || 
				(identifier == PER_SEND_DATA_INDICATION) || 
				(identifier == PER_UNIFORM_SEND_DATA_REQUEST) || 
				(identifier == PER_UNIFORM_SEND_DATA_INDICATION))
		{
			return TRUE;
		}
	}

	return FALSE;
}


void CMCSCoder::FreeEncoded(LPBYTE encoded_buffer)
{
    ASN1_FreeEncoded(m_pEncInfo, encoded_buffer);
}

void CMCSCoder::FreeDecoded (int pdu_type, LPVOID decoded_buffer)
{
    ASN1_FreeDecoded(m_pDecInfo, decoded_buffer, pdu_type);
}