|
|
#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
if ( LengthInsertionPoint != NULL ) {
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; }
|