Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

847 lines
19 KiB

#include "stdafx.h"
#include "dynarray.h"
#include "q931msg.h"
#include "h323asn1.h"
struct Q931_ENCODE_CONTEXT
{
LPBYTE Pos; // next storage position, MAY EXCEED End!
LPBYTE End; // end of storage buffer
// if returns FALSE, then buffer is in overflow condition
BOOL StoreData (
IN LPBYTE Data,
IN DWORD Length);
BOOL HasOverflowed (void) { return Pos > End; }
// if returns FALSE, then buffer is in overflow condition, or would be
BOOL AllocData (
IN DWORD Length,
OUT LPBYTE * ReturnData);
};
BOOL Q931_ENCODE_CONTEXT::StoreData (
IN LPBYTE Data,
IN DWORD Length)
{
if (Pos + Length > End) {
Pos += Length;
return FALSE;
}
memcpy (Pos, Data, Length);
Pos += Length;
return TRUE;
}
BOOL Q931_ENCODE_CONTEXT::AllocData (
IN DWORD Length,
OUT LPBYTE * ReturnData)
{
if (Pos + Length > End) {
Pos += Length;
*ReturnData = NULL;
return FALSE;
}
else {
*ReturnData = Pos;
Pos += Length;
return TRUE;
}
}
#if DBG
void Q931TestDecoder (
IN LPBYTE PduData,
IN DWORD PduLength)
{
Q931_MESSAGE Message;
HRESULT Result;
Q931_MESSAGE NewMessage;
BYTE NewData [0x400];
DWORD NewLength;
Debug (_T("Q931TestDecoder --------------------------------------------------------------------\n"));
DebugF (_T("- processing Q.931 PDU, length %d, contents:\n"), PduLength);
DumpMemory (PduData, PduLength);
Result = Message.AttachDecodePdu (PduData, PduLength, FALSE);
if (Result != S_OK) {
DebugError (Result, _T("- failed to decode Q.931 PDU\n"));
return;
}
Debug (_T("- successfully decoded Q.931 PDU\n"));
// now, try to re-encode the same PDU
if (Message.MessageType == Q931_MESSAGE_TYPE_SETUP) {
// there is an issue with decoding and re-encoding ASN.1 UUIE for Setup from TAPI
// long, boring story
Debug (_T("- it's a Setup PDU, will not attempt to re-encode (due to ASN.1 compatability issue)\n"));
}
else {
Debug (_T("- will now attempt to re-encode\n"));
NewLength = 0x400;
Result = Message.EncodePdu (NewData, &NewLength);
if (Result == S_OK) {
DebugF (_T("- successfully re-encoded copy of Q.931 PDU, length %d, contents:\n"), NewLength);
if (PduLength != NewLength) {
DebugF (_T("- *** warning: original pdu length (%d) is different from re-encoded pdu length (%d), re-encoded contents:\n"),
PduLength, NewLength);
DumpMemory (NewData, NewLength);
}
else {
if (memcmp (PduData, NewData, NewLength) != 0) {
DebugF (_T("- *** warning: original pdu contents differ from re-encoded pdu contents, which follow:\n"));
DumpMemory (NewData, NewLength);
}
else {
DebugF (_T("- re-encoded pdu is identical to original pdu -- success!\n"));
}
}
Debug (_T("- will now attempt to decode re-encoded PDU\n"));
Result = NewMessage.AttachDecodePdu (NewData, NewLength, FALSE);
if (Result == S_OK) {
Debug (_T("- successfully decoded copy of Q.931 PDU\n"));
}
else {
DebugError (Result, _T("- failed to decode copy of Q.931 PDU\n"));
}
}
else {
DebugError (Result, _T("- failed to re-encode Q.931 PDU\n"));
}
}
Message.Detach();
NewMessage.Detach();
Debug (_T("\n"));
}
#endif
// Q931_MESSAGE -----------------------------------------------------------------------------
Q931_MESSAGE::Q931_MESSAGE (void)
{
Buffer = NULL;
BufferLength = 0;
}
Q931_MESSAGE::~Q931_MESSAGE (void)
{
Detach();
assert (!InfoElementArray.m_Length);
assert (!Buffer);
}
void Q931_MESSAGE::Detach (void)
{
FreeInfoElementArray();
if (Buffer) {
if (BufferIsOwner) {
LocalFree (Buffer);
}
Buffer = NULL;
BufferLength = 0;
BufferIsOwner = FALSE;
}
}
HRESULT Q931_MESSAGE::Detach (
OUT LPBYTE * ReturnBuffer,
OUT DWORD * ReturnBufferLength)
{
HRESULT Result;
assert (ReturnBuffer);
assert (ReturnBufferLength);
if (Buffer) {
*ReturnBuffer = Buffer;
*ReturnBufferLength = BufferLength;
Result = S_OK;
}
else {
Result = S_FALSE;
}
Detach();
return Result;
}
void Q931_MESSAGE::FreeInfoElementArray (void)
{
Q931_IE * Pos;
Q931_IE * End;
InfoElementArray.GetExtents (&Pos, &End);
for (; Pos < End; Pos++) {
FreeInfoElement (Pos);
}
InfoElementArray.Clear();
}
void Q931_MESSAGE::FreeInfoElement (Q931_IE * InfoElement)
{
assert (InfoElement);
switch (InfoElement -> Identifier) {
case Q931_IE_USER_TO_USER:
assert (InfoElement -> Data.UserToUser.PduStructure);
if (InfoElement -> Data.UserToUser.IsOwner) {
H225FreePdu_H323_UserInformation (
InfoElement -> Data.UserToUser.PduStructure);
}
break;
}
}
HRESULT Q931_MESSAGE::DecodeInfoElement (
IN OUT LPBYTE * ArgPos,
IN LPBYTE End,
OUT Q931_IE * ReturnInfoElement)
{
LPBYTE Pos;
BYTE Identifier;
DWORD LengthLength; // length of the IE length element, in bytes!
LPBYTE VariableData; // payload of variable-length data
DWORD VariableDataLength;
BYTE FixedData; // payload of fixed-length data
HRESULT Result;
assert (ArgPos);
assert (End);
Pos = *ArgPos;
if (Pos >= End) {
Debug (_T("Q931_MESSAGE::DecodeInfoElement: should never have been called\n"));
return E_INVALIDARG;
}
Identifier = *Pos;
Pos++;
// is it a single-byte IE?
// if so, then bit 7 of the first byte = 1
if (Identifier & 0x80) {
// there are two types of single-byte IEs
// Type 1 has a four-bit identifier and a four-bit value
// Type 2 has only an identifier, and no value
switch (Identifier & 0xF0) {
case Q931_IE_MORE_DATA:
case Q931_IE_SENDING_COMPLETE:
// these IEs have an identifier, but no value
ReturnInfoElement -> Identifier = (Q931_IE_IDENTIFIER) Identifier;
DebugF (_T("Q931_MESSAGE::DecodeInfoElement: fixed-length IE, id %02XH, no value\n"),
Identifier);
break;
default:
// the other single-byte IEs have a value in the lower four bits
ReturnInfoElement -> Identifier = (Q931_IE_IDENTIFIER) (Identifier & 0xF0);
ReturnInfoElement -> Data.UnknownFixed.Value = Identifier & 0x0F;
DebugF (_T("Q931_MESSAGE::DecodeInfoElement: fixed-length IE, id %02XH value %01XH\n"),
ReturnInfoElement -> Identifier,
ReturnInfoElement -> Data.UnknownFixed.Value);
break;
}
// we don't currently parse any fixed-length IEs
Result = S_OK;
}
else {
// the next byte indicates the length of the info element
// unfortunately, the number of octets that make up the length
// depends on the identifier.
// -XXX- is this because I don't understand the octet extension mechanism?
ReturnInfoElement -> Identifier = (Q931_IE_IDENTIFIER) Identifier;
switch (Identifier) {
case Q931_IE_USER_TO_USER:
LengthLength = 2;
break;
default:
LengthLength = 1;
break;
}
if (Pos + LengthLength > End) {
Debug (_T("Q931_MESSAGE::DecodeInfoElement: insufficient data for header of variable-length IE\n"));
return E_INVALIDARG;
}
if (LengthLength == 1) {
VariableDataLength = *Pos;
}
else {
VariableDataLength = Pos [1] + (((WORD) Pos [0]) << 8);
}
Pos += LengthLength;
if (Pos + VariableDataLength > End) {
Debug (_T("Q931_MESSAGE::DecodeInfoElement: insufficient data for body of variable-length IE\n"));
return E_INVALIDARG;
}
VariableData = (LPBYTE) Pos;
Pos += VariableDataLength;
// DebugF (_T("Q931_MESSAGE::DecodeInfoElement: variable-length IE, id %02XH length %d\n"),
// Identifier, VariableDataLength);
ReturnInfoElement -> Data.UnknownVariable.Data = VariableData;
ReturnInfoElement -> Data.UnknownVariable.Length = VariableDataLength;
Result = ParseIE (ReturnInfoElement);
if (Result != S_OK) {
DebugError (Result, _T("Q931_MESSAGE::DecodeInfoElement: IE was located, but failed to parse\n"));
}
}
*ArgPos = Pos;
return Result;
}
HRESULT Q931_MESSAGE::AppendInfoElement (
IN Q931_IE * InfoElement)
{
Q931_IE * ArrayEntry;
ArrayEntry = InfoElementArray.AllocAtEnd();
if (ArrayEntry) {
*ArrayEntry = *InfoElement;
return S_OK;
}
else {
Debug (_T("Q931_MESSAGE::AppendInfoElement: allocation failure\n"));
return E_OUTOFMEMORY;
}
}
HRESULT Q931_MESSAGE::ParseIE_UUIE (
IN Q931_IE * InfoElement)
{
LPBYTE Data;
DWORD Length;
DWORD Status;
// be careful to copy out all parameters from one branch of the union
// before you start stomping on another branch
Data = InfoElement -> Data.UnknownVariable.Data;
Length = InfoElement -> Data.UnknownVariable.Length;
if (Length < 1) {
Debug (_T("Q931_MESSAGE::ParseIE_UUIE: IE payload is too short to contain UUIE\n"));
return E_INVALIDARG;
}
InfoElement -> Data.UserToUser.Type = (Q931_UUIE_TYPE) *Data++;
Length--;
InfoElement -> Data.UserToUser.PduStructure = NULL;
Status = H225DecodePdu_H323_UserInformation (Data, Length,
&InfoElement -> Data.UserToUser.PduStructure);
if (Status != ERROR_SUCCESS) {
if (InfoElement -> Data.UserToUser.PduStructure) {
// return value was a warning, not error
H225FreePdu_H323_UserInformation (InfoElement -> Data.UserToUser.PduStructure);
InfoElement -> Data.UserToUser.PduStructure = NULL;
}
InfoElement -> Data.UserToUser.PduStructure = NULL;
DebugError (Status, _T("Q931_MESSAGE::ParseIE_UUIE: failed to decode UUIE / ASN.1\n"));
return E_FAIL;
}
InfoElement -> Data.UserToUser.IsOwner = TRUE;
// Debug (_T("Q931_MESSAGE::ParseIE_UUIE: successfully decoded UUIE\n"));
return S_OK;
}
HRESULT Q931_MESSAGE::ParseIE (
IN Q931_IE * InfoElement)
{
assert (InfoElement);
switch (InfoElement -> Identifier) {
case Q931_IE_USER_TO_USER:
return ParseIE_UUIE (InfoElement);
break;
case Q931_IE_CAUSE:
// Debug (_T("Q931_MESSAGE::ParseInfoElement: Q931_IE_CAUSE\n"));
break;
case Q931_IE_DISPLAY:
// Debug (_T("Q931_MESSAGE::ParseInfoElement: Q931_IE_DISPAY\n"));
break;
case Q931_IE_BEARER_CAPABILITY:
// Debug (_T("Q931_MESSAGE::ParseInfoElement: Q931_IE_BEARER_CAPABILITY\n"));
break;
default:
DebugF (_T("Q931_MESSAGE::ParseInfoElement: unknown IE identifier (%02XH), no interpretation will be imposed\n"),
InfoElement -> Identifier);
break;
}
return S_OK;
}
HRESULT Q931_MESSAGE::AttachDecodePdu (
IN LPBYTE Data,
IN DWORD Length,
IN BOOL IsDataOwner)
{
LPBYTE Pos;
LPBYTE End;
HRESULT Result;
Q931_IE * ArrayEntry;
assert (Data);
Detach();
if (Length < 5) {
DebugF (_T("Q931_MESSAGE::Decode: header is too short (%d)\n"), Length);
return E_INVALIDARG;
}
// octet 0 is the Protocol Discriminator
if (Data [0] != Q931_PROTOCOL_DISCRIMINATOR) {
DebugF (_T("Q931_MESSAGE::Decode: the pdu is not a Q.931 pdu, protocol discriminator = %02XH\n"),
Data [0]);
return E_INVALIDARG;
}
// octet 1: bits 0-3 contain the length, in octets of the Call Reference Value
// octet 1: bits 4-7 should be zero
if (Data [1] & 0xF0) {
DebugF (_T("Q931_MESSAGE::Decode: the pdu has non-zero bits in octet 1: %02XH\n"),
Data [1]);
}
// according to H.225, the Call Reference Value must be two octets in length
if ((Data [1] & 0x0F) != 2) {
DebugF (_T("Q931_MESSAGE::Decode: the call reference value size is invalid (%d), should be 2\n"),
Data [1] & 0x0F);
return E_INVALIDARG;
}
// since the Call Reference Value size is 2 octets, octets 2 and 3 are the CRV
// octets are in network (big-endian) order.
CallReferenceValue = (((WORD) Data [2]) << 8) | Data [3];
// DebugF (_T("Q931_MESSAGE::Decode: crv %04XH\n"), CallReferenceValue);
// Message Type is at octet offset 4
if (Data [4] & 0x80) {
DebugF (_T("Q931_MESSAGE::Decode: message type is invalid (%02XH)\n"), Data [4]);
return E_INVALIDARG;
}
MessageType = (Q931_MESSAGE_TYPE) Data [4];
// enumerate the Information Elements and extract the ones that we will use
Pos = Data + 5;
End = Data + Length;
Result = S_OK;
while (Pos < End) {
ArrayEntry = InfoElementArray.AllocAtEnd();
if (!ArrayEntry) {
Result = E_OUTOFMEMORY;
Debug (_T("Q931_MESSAGE::Decode: allocation failure\n"));
break;
}
Result = DecodeInfoElement (&Pos, End, ArrayEntry);
if (Result != S_OK) {
DebugError (Result, _T("Q931_MESSAGE::Decode: failed to decode IE, packet may be corrupt, terminating (but not failing) decode\n"));
Result = S_OK;
InfoElementArray.DeleteEntry (ArrayEntry);
break;
}
}
if (Result == S_OK) {
assert (!Buffer);
Buffer = Data;
BufferLength = Length;
BufferIsOwner = IsDataOwner;
}
else {
Detach();
}
return ERROR_SUCCESS;
}
HRESULT Q931_MESSAGE::EncodePdu (
IN OUT LPBYTE Data,
IN OUT LPDWORD Length)
{
Q931_ENCODE_CONTEXT Context;
Q931_IE * IePos;
Q931_IE * IeEnd;
HRESULT Result;
DWORD EncodeLength;
assert (Data);
assert (Length);
Context.Pos = Data;
Context.End = Data + *Length;
SortInfoElementArray();
Result = EncodeHeader (&Context);
if (Result != S_OK)
return Result;
// walk IE array
InfoElementArray.GetExtents (&IePos, &IeEnd);
for (; IePos < IeEnd; IePos++) {
Result = EncodeInfoElement (&Context, IePos);
if (Result != S_OK) {
return Result;
}
}
EncodeLength = (DWORD)(Context.Pos - Data);
if (Context.HasOverflowed()) {
Result = HRESULT_FROM_WIN32 (ERROR_MORE_DATA);
}
else {
Result = S_OK;
}
*Length = EncodeLength;
return Result;
}
HRESULT Q931_MESSAGE::EncodeHeader (
IN Q931_ENCODE_CONTEXT * Context)
{
BYTE Header [5];
Header [0] = Q931_PROTOCOL_DISCRIMINATOR;
Header [1] = 2;
Header [2] = (CallReferenceValue >> 8) & 0xFF;
Header [3] = CallReferenceValue & 0xFF;
Header [4] = MessageType;
Context -> StoreData (Header, 5);
return S_OK;
}
HRESULT Q931_MESSAGE::EncodeInfoElement (
IN Q931_ENCODE_CONTEXT * Context,
IN Q931_IE * InfoElement)
{
BYTE Header [0x10];
WORD Length;
DWORD LengthLength; // length of Length, in bytes
LPBYTE LengthInsertionPoint;
LPBYTE IeContents;
DWORD IeContentsLength;
DWORD ShiftCount;
HRESULT Result;
if (InfoElement -> Identifier & 0x80) {
// single-byte IE
switch (InfoElement -> Identifier & 0xF0) {
case Q931_IE_MORE_DATA:
case Q931_IE_SENDING_COMPLETE:
// these IEs have an identifier, but no value
Header [0] = (BYTE) InfoElement -> Identifier;
break;
default:
// these IEs have an identifier and a value, combined in a single byte
Header [0] = (((BYTE) InfoElement -> Identifier) & 0xF0)
| (InfoElement -> Data.UnknownFixed.Value & 0x0F);
break;
}
Context -> StoreData (Header, 1);
Result = S_OK;
}
else {
// variable-length IE
Header [0] = (BYTE) InfoElement -> Identifier;
Context -> StoreData (Header, 1);
// allocate data for the insertion point
Context -> AllocData (2, &LengthInsertionPoint);
// record the current buffer position, for use below in storing the content length
IeContents = Context -> Pos;
switch (InfoElement -> Identifier) {
case Q931_IE_USER_TO_USER:
Result = EncodeIE_UUIE (Context, InfoElement);
break;
default:
Context -> StoreData (
InfoElement -> Data.UnknownVariable.Data,
InfoElement -> Data.UnknownVariable.Length);
if (InfoElement -> Data.UnknownVariable.Length >= 0x10000) {
DebugF (_T("Q931_MESSAGE::EncodeInfoElement: payload is waaaaay too big (%d %08XH)\n"),
InfoElement -> Data.UnknownVariable.Length,
InfoElement -> Data.UnknownVariable.Length);
Result = E_INVALIDARG;
}
else {
Result = S_OK;
}
break;
}
if (Result == S_OK) {
IeContentsLength = (DWORD)(Context -> Pos - IeContents);
// this is such a hack
// with little or no justification for when LengthLength = 1 and when LengthLength = 2
// the octet group extension mechanism is poorly defined in Q.931
if (InfoElement -> Identifier == Q931_IE_USER_TO_USER)
LengthLength = 2;
else
LengthLength = 1;
// if the storage context has not overflowed,
// and if it is necessary to resize the Length parameter (we guessed pessimistically
// that it would be 2), then move the buffer down one byte
ShiftCount = 2 - LengthLength;
if (ShiftCount > 0) {
if (!Context -> HasOverflowed()) {
memmove (
LengthInsertionPoint + LengthLength, // destination, where IE contents should be
IeContents, // source, where IE contents were actually stored
IeContentsLength); // length of the contents
}
// pull back the storage context's position pointer
Context -> Pos -= ShiftCount;
}
// now store the actual count
switch (LengthLength) {
case 1:
assert (IeContentsLength < 0x100);
LengthInsertionPoint [0] = (BYTE) IeContentsLength;
break;
case 2:
assert (IeContentsLength < 0x10000);
LengthInsertionPoint [0] = (BYTE) (IeContentsLength >> 8);
LengthInsertionPoint [1] = (BYTE) (IeContentsLength & 0xFF);
break;
default:
assert (FALSE);
}
}
}
return Result;
}
HRESULT Q931_MESSAGE::EncodeIE_UUIE (
IN Q931_ENCODE_CONTEXT * Context,
IN Q931_IE * InfoElement)
{
DWORD Status;
LPBYTE Buffer;
DWORD Length;
BYTE ProtocolDiscriminator;
assert (Context);
assert (InfoElement);
assert (InfoElement -> Data.UserToUser.PduStructure);
// store the UUIE protocol discriminator
ProtocolDiscriminator = InfoElement -> Data.UserToUser.Type;
Context -> StoreData (&ProtocolDiscriminator, 1);
Buffer = NULL;
Length = 0;
Status = H225EncodePdu_H323_UserInformation (
InfoElement -> Data.UserToUser.PduStructure,
&Buffer, &Length);
if (Status == ERROR_SUCCESS) {
Context -> StoreData (Buffer, Length);
H225FreeBuffer (Buffer);
return S_OK;
}
else {
// Status is not a real Win32 error code
// it is an ASN.1 enum (
#if DBG
// we pull this in so source debuggers can show actual symbolic enum name
tagASN1error_e AsnError = (tagASN1error_e) Status;
DebugF (_T("Q931_MESSAGE::EncodeIE_UUIE: failed to encode ASN.1 structure (%d)\n"),
AsnError);
#endif
// -XXX- one day, i'm going to convince Lon to use real Win32 error codes for ASN.1 return values
// -XXX- on that day, the return value should reflect the actual ASN.1 error code
return DIGSIG_E_ENCODE;
}
}
void Q931_MESSAGE::SortInfoElementArray (void)
{
InfoElementArray.QuickSort (CompareInfoElement);
}
// static
INT __cdecl Q931_MESSAGE::CompareInfoElement (
const Q931_IE * ComparandA,
const Q931_IE * ComparandB)
{
if (ComparandA -> Identifier < ComparandB -> Identifier) return -1;
if (ComparandA -> Identifier > ComparandB -> Identifier) return 1;
return 0;
}
HRESULT Q931_MESSAGE::FindInfoElement (
IN Q931_IE_IDENTIFIER Identifier,
OUT Q931_IE ** ReturnInfoElement)
{
DWORD Index;
assert (ReturnInfoElement);
if (InfoElementArray.BinarySearch ((SEARCH_FUNC_Q931_IE)InfoElementSearchFunc, &Identifier, &Index)) {
*ReturnInfoElement = InfoElementArray.m_Array + Index;
return S_OK;
}
else {
*ReturnInfoElement = NULL;
return E_FAIL;
}
}
// static
INT Q931_MESSAGE::InfoElementSearchFunc (
IN const Q931_IE_IDENTIFIER * SearchKey,
IN const Q931_IE * Comparand)
{
Q931_IE_IDENTIFIER Identifier;
assert (SearchKey);
assert (Comparand);
Identifier = * (Q931_IE_IDENTIFIER *) SearchKey;
if (Identifier < Comparand -> Identifier) return -1;
if (Identifier > Comparand -> Identifier) return 1;
return 0;
}