|
|
//=============================================================================
// FILE: TransportParser.cpp
//
// Description: DPlay v8 Transport protocol parser
//
//
// Modification History:
//
// Michael Milirud 08/Aug/2000 Created
//=============================================================================
// uncomment to enable full parsing of DPlay Transport layer
//#define PARSE_DPLAY_TRANSPORT
//==================//
// Standard headers //
//==================//
#include <string>
//=====================//
// Proprietary headers //
//=====================//
// Prototypes
#include "TransportParser.hpp"
// Transport protocol header
#include "Frames.h"
typedef UNALIGNED struct dataframe_big DFBIG, *PDFBIG; typedef UNALIGNED struct sackframe_big8 SFBIG8, *PSFBIG8;
struct dataframe_big { BYTE bCommand; BYTE bControl; BYTE bSeq; BYTE bNRcv; ULONG rgMask[4]; };
struct sackframe_big8 { BYTE bCommand; // As above
BYTE bExtOpcode; // As above
BYTE bFlags; // Additional flags for sack frame
BYTE bRetry; BYTE bNSeq; // Since this frame has no sequence number, this is the next Seq we
BYTE bNRcv; // As above
BYTE bReserved1; // We shipped DX8 with bad packing, so these were actually there
BYTE bReserved2; // We shipped DX8 with bad packing, so these were actually there
DWORD tTimestamp; // Local stamp when packet arrived
ULONG rgMask[4]; };
namespace { HPROTOCOL g_hTransportProtocol;
typedef __int64 QWORD;
//================//
// DCommand field //-------------------------------------------------------------------------------------------------
//================//
LABELED_BIT g_arr_DCommandBitLabels[] = { { 0, "INVALID", "Dataframe" }, // PACKET_COMMAND_DATA
{ 1, "Unreliable", "Reliable" }, // PACKET_COMMAND_RELIABLE
{ 2, "Nonsequenced", "Sequenced" }, // PACKET_COMMAND_SEQUENTIAL
{ 3, "ACK can be delayed", "ACK now" }, // PACKET_COMMAND_POLL
{ 4, "Not the first fragment of the message", "First fragment of the message" }, // PACKET_COMMAND_NEW_MSG
{ 5, "Not the last fragment of the message", "Last fragment of the message" }, // PACKET_COMMAND_END_MSG
{ 6, "User packet", "DirectPlay packet" }, // PACKET_COMMAND_USER_1
{ 7, "Data packet", "Voice packet" } }; // PACKET_COMMAND_USER_2
SET g_LabeledDCommandBitSet = { sizeof(g_arr_DCommandBitLabels) / sizeof(LABELED_BIT), g_arr_DCommandBitLabels };
//===============//
// Control field //--------------------------------------------------------------------------------------------------
//===============//
LABELED_BIT g_arr_ControlBitLabels[] = { { 0, "Original (not a retry)", "Retry" }, // PACKET_CONTROL_RETRY
{ 1, "Don't correlate", "Correlate" }, // PACKET_CONTROL_CORRELATE
{ 2, "Not a correlation response", "Correlation response" }, // PACKET_CONTROL_RESPONSE
{ 3, "Not the last packet in the stream", "Last packet in the stream" }, // PACKET_CONTROL_END_STREAM
{ 4, "Low DWORD of the RCVD mask is zero", "Low DWORD of the RCVD mask is nonzero" }, // PACKET_CONTROL_SACK_MASK1
{ 5, "High DWORD of the RCVD mask is zero", "High DWORD of the RCVD mask is nonzero" }, // PACKET_CONTROL_SACK_MASK2
{ 6, "Low DWORD of the DON'T CARE mask is zero", "Low DWORD of the DON'T CARE mask is nonzero" }, // PACKET_CONTROL_SEND_MASK1
{ 7, "High DWORD of the DON'T CARE mask is zero", "High DWORD of the DON'T CARE mask is nonzero" } }; // PACKET_CONTROL_SEND_MASK2
SET g_LabeledControlBitSet = { sizeof(g_arr_ControlBitLabels) / sizeof(LABELED_BIT), g_arr_ControlBitLabels };
//================//
// CCommand field //-------------------------------------------------------------------------------------------------
//================//
LABELED_BIT g_arr_CCommandBitLabels[] = { { 0, "Command Frame (1/2)", "INVALID" }, // PACKET_COMMAND_DATA
{ 1, "Unreliable", "Reliable" }, // PACKET_COMMAND_RELIABLE
{ 2, "Nonsequenced", "Sequenced" }, // PACKET_COMMAND_SEQUENTIAL
{ 3, "ACK can be delayed", "ACK now" }, // PACKET_COMMAND_POLL
{ 4, "RESERVED", "RESERVED" }, { 5, "RESERVED", "RESERVED" }, { 6, "RESERVED", "RESERVED" }, { 7, "INVALID", "Command Frame (2/2)" } };
SET g_LabeledCCommandBitSet = { sizeof(g_arr_CCommandBitLabels) / sizeof(LABELED_BIT), g_arr_CCommandBitLabels };
//=======================//
// Extended Opcode field //------------------------------------------------------------------------------------------
//=======================//
LABELED_BYTE g_arr_ExOpcodeByteLabels[] = { { FRAME_EXOPCODE_CONNECT, "Establish a connection" }, { FRAME_EXOPCODE_CONNECTED, "Connection request has been accepted" }, { FRAME_EXOPCODE_HARD_DISCONNECT, "Connection has been hard disconnected" }, { FRAME_EXOPCODE_SACK, "Selective Acknowledgement" } };
SET g_LabeledExOpcodeByteSet = { sizeof(g_arr_ExOpcodeByteLabels) / sizeof(LABELED_BYTE), g_arr_ExOpcodeByteLabels };
//==================//
// SACK flags field //-----------------------------------------------------------------------------------------------
//==================//
LABELED_BIT g_arr_SACKFlagsBitLabels[] = { { 0, "Retry and/or Timestamp fields are invalid", "Retry and Timestamp fields are valid" }, // SACK_FLAGS_RESPONSE
{ 1, "Low DWORD of the RCVD mask is not present", "Low DWORD of the RCVD mask is present" }, // SACK_FLAGS_SACK_MASK1
{ 2, "High DWORD of the RCVD mask is not present", "High DWORD of the RCVD mask is present" }, // SACK_FLAGS_SACK_MASK2
{ 3, "Low DWORD of the DON'T CARE mask is not present", "Low DWORD of the DON'T CARE mask is present" }, // SACK_FLAGS_SEND_MASK1
{ 4, "High DWORD of the DON'T CARE mask is not present", "High DWORD of the DON'T CARE mask is present" } }; // SACK_FLAGS_SEND_MASK2
SET g_LabeledSACKFlagsBitSet = { sizeof(g_arr_SACKFlagsBitLabels) / sizeof(LABELED_BIT), g_arr_SACKFlagsBitLabels };
//==================//
// Helper functions //===========================================================================
//==================//
enum BitmaskPart { LOW = 0, HIGH, ENTIRE }; enum BitmaskType { RCVD, DONTCARE }; std::string InterpretRCVDBitmask( BitmaskPart i_Part, BYTE i_byBase, UNALIGNED DWORD* i_pdwBitmask ) { std::string strSummary = "Received Seq=";
// [i_bBase+1 .. i_bBase+1+LENGTH]
// RCVD bitmask doesn't include the base value, since receiver can't claim it didn't receive the next dataframe to be received (NRcv);
if ( i_Part == HIGH ) { // NOTE: +1 is needed to cross from MSB of the first DWORD TO LSB of the second
i_byBase += 8*sizeof(DWORD)+1; // shift to LSB of the second DWORD
} else { ++i_byBase; }
QWORD qwBitMask = *i_pdwBitmask; if ( i_Part == ENTIRE ) { qwBitMask |= *(i_pdwBitmask+1); }
strSummary += "{";
bool bFirst = true; // Processing from LSB to MSB
for ( ; qwBitMask; qwBitMask >>= 1, ++i_byBase ) { if ( qwBitMask & 1 ) { if ( bFirst ) { bFirst = false; } else { strSummary += ", "; } char arr_cBuffer[10]; strSummary += _itoa(i_byBase, arr_cBuffer, 16); } } strSummary += "}"; return strSummary; }// InterpretRCVDBitmask
std::string InterpretDONTCAREBitmask( BitmaskPart i_Part, BYTE i_byBase, UNALIGNED DWORD* i_pdwBitmask ) { std::string strSummary = "Cancelling Seq=";
// [i_bBase-1-LENGTH .. i_bBase-1]
// DON'T CARE doesn't include the base value, since transmitter can't resend/refuse resending a dataframe which is about to be sent next (NSeq).
if ( i_Part == LOW ) { i_byBase -= 8*sizeof(DWORD); // shift to MSB of the first DWORD
} else { i_byBase -= 8*sizeof(QWORD); // shift to MSB of the second DWORD
}
QWORD qwBitMask = *i_pdwBitmask; if ( i_Part == ENTIRE ) { qwBitMask |= *(i_pdwBitmask+1); } else { // QWORD.High = QWORD.Low; QWORD.Low = 0;
qwBitMask <<= 8*sizeof(DWORD); }
strSummary += "{"; bool bFirst = true; // Processing from MSB to LSB
for ( ; qwBitMask; ++i_byBase, qwBitMask <<= 1 ) { if ( qwBitMask & 0x8000000000000000 ) { if ( bFirst ) { bFirst = false; } else { strSummary += ", "; } char arr_cBuffer[10]; strSummary += _itoa(i_byBase, arr_cBuffer, 16); } } strSummary += "}"; return strSummary; }// InterpretDONTCAREBitmask
////////////////////////////////
// Custom Property Formatters //======================================================================================
////////////////////////////////
// DESCRIPTION: Custom description formatter for the Transport packet summary
//
// ARGUMENTS: io_pProperyInstance - Data of the property's instance
//
// RETURNS: NOTHING
//
VOID WINAPIV FormatPropertyInstance_TransportSummary( LPPROPERTYINST io_pProperyInstance ) { std::string strSummary; char arr_cBuffer[10];
DFBIG& rDBigFrame = *reinterpret_cast<DFBIG*>(io_pProperyInstance->lpPropertyInstEx->lpData);
if ( (rDBigFrame.bCommand & PACKET_COMMAND_DATA) == PACKET_COMMAND_DATA ) // DFrame
{ if ( *reinterpret_cast<BOOL*>(io_pProperyInstance->lpPropertyInstEx->Dword) ) { strSummary = "KeepAlive"; } else if ( (rDBigFrame.bCommand & PACKET_COMMAND_USER_2) == PACKET_COMMAND_USER_2 ) { strSummary = "Voice"; } else { strSummary = "User data"; }
#if defined(PARSE_DPLAY_TRANSPORT)
strSummary += " : Seq="; strSummary += _itoa(rDBigFrame.bSeq, arr_cBuffer, 16); strSummary += ", NRcv="; strSummary += _itoa(rDBigFrame.bNRcv, arr_cBuffer, 16); #endif // PARSE_DPLAY_TRANSPORT
if ( (rDBigFrame.bCommand & PACKET_COMMAND_NEW_MSG) == PACKET_COMMAND_NEW_MSG ) { if ( (rDBigFrame.bCommand & PACKET_COMMAND_END_MSG) != PACKET_COMMAND_END_MSG ) { strSummary += ", First fragment"; } } else if ( (rDBigFrame.bCommand & PACKET_COMMAND_END_MSG) == PACKET_COMMAND_END_MSG ) { strSummary += ", Last fragment"; } if ( (rDBigFrame.bControl & PACKET_CONTROL_END_STREAM) == PACKET_CONTROL_END_STREAM ) { strSummary += ", End of Stream"; }
if ( (rDBigFrame.bCommand & PACKET_COMMAND_POLL) == PACKET_COMMAND_POLL ) { strSummary += ", ACK now"; }
if ( (rDBigFrame.bControl & PACKET_CONTROL_RETRY) == PACKET_CONTROL_RETRY ) { strSummary += ", Retry"; }
if ( (rDBigFrame.bControl & PACKET_CONTROL_CORRELATE) == PACKET_CONTROL_CORRELATE ) { strSummary += ", Correlate / Keep Alive"; }
int nBitMaskIndex = 0; if ( (rDBigFrame.bControl & PACKET_CONTROL_SACK_MASK1) == PACKET_CONTROL_SACK_MASK1 ) { strSummary += ", "; if ( (rDBigFrame.bControl & PACKET_CONTROL_SACK_MASK2) == PACKET_CONTROL_SACK_MASK2 ) // Entire QWORD
{ strSummary += InterpretRCVDBitmask(ENTIRE, rDBigFrame.bNRcv, &rDBigFrame.rgMask[nBitMaskIndex]); nBitMaskIndex += 2; } else // Low DWORD only
{ strSummary += InterpretRCVDBitmask(LOW, rDBigFrame.bNRcv, &rDBigFrame.rgMask[nBitMaskIndex]); ++nBitMaskIndex; } } else if ( (rDBigFrame.bControl & PACKET_CONTROL_SACK_MASK2) == PACKET_CONTROL_SACK_MASK2 ) // High DWORD only
{ strSummary += ", " + InterpretRCVDBitmask(HIGH, rDBigFrame.bNRcv, &rDBigFrame.rgMask[nBitMaskIndex]); ++nBitMaskIndex; } if ( (rDBigFrame.bControl & PACKET_CONTROL_SEND_MASK1) == PACKET_CONTROL_SEND_MASK1 ) { strSummary += ", "; if ( (rDBigFrame.bControl & PACKET_CONTROL_SEND_MASK2) == PACKET_CONTROL_SEND_MASK2 ) // Entire QWORD
{ strSummary += InterpretDONTCAREBitmask(ENTIRE, rDBigFrame.bSeq, &rDBigFrame.rgMask[nBitMaskIndex]); } else // Low DWORD only
{ strSummary += InterpretDONTCAREBitmask(LOW, rDBigFrame.bSeq, &rDBigFrame.rgMask[nBitMaskIndex]); } } else if ( (rDBigFrame.bControl & PACKET_CONTROL_SEND_MASK2) == PACKET_CONTROL_SEND_MASK2 ) // High DWORD only
{ strSummary += ", " + InterpretDONTCAREBitmask(HIGH, rDBigFrame.bSeq, &rDBigFrame.rgMask[nBitMaskIndex]); }
} else { CFRAME& rCFrame = *reinterpret_cast<CFRAME*>(io_pProperyInstance->lpPropertyInstEx->lpData);
if ( rCFrame.bExtOpcode == FRAME_EXOPCODE_SACK ) // SACK CFrame
{ SFBIG8* pSBigFrame = reinterpret_cast<SFBIG8*>(&rCFrame);
enum { SACK_FLAGS_ALL_MASKS = SACK_FLAGS_SACK_MASK1 | SACK_FLAGS_SACK_MASK2 | SACK_FLAGS_SEND_MASK1 | SACK_FLAGS_SEND_MASK2 }; if ( pSBigFrame->bFlags & SACK_FLAGS_ALL_MASKS ) // at least one bitmask field is present
{ strSummary = "Selective Acknowledgement"; } else // if not a single bitmask is present
{ strSummary = "Acknowledgement"; }
#if defined(PARSE_DPLAY_TRANSPORT)
strSummary += " : NSeq="; strSummary += _itoa(pSBigFrame->bNSeq, arr_cBuffer, 16); strSummary += ", NRcv="; strSummary += _itoa(pSBigFrame->bNRcv, arr_cBuffer, 16);
#endif // PARSE_DPLAY_TRANSPORT
if ( (pSBigFrame->bCommand & PACKET_COMMAND_POLL) == PACKET_COMMAND_POLL ) { strSummary += ", ACK now"; }
if ( ((pSBigFrame->bFlags & SACK_FLAGS_RESPONSE) == SACK_FLAGS_RESPONSE) && pSBigFrame->bRetry ) { strSummary += ", Retry"; }
int nBitMaskIndex = 0; UNALIGNED ULONG* pulMasks = 0;
// This is a Protocol version 1.0 frame
pulMasks = pSBigFrame->rgMask;
if ( (pSBigFrame->bFlags & SACK_FLAGS_SACK_MASK1) == SACK_FLAGS_SACK_MASK1 ) { strSummary += ", "; if ( (pSBigFrame->bFlags & SACK_FLAGS_SACK_MASK2) == SACK_FLAGS_SACK_MASK2 ) // Entire QWORD
{ strSummary += InterpretRCVDBitmask(ENTIRE, pSBigFrame->bNRcv, &pulMasks[nBitMaskIndex]); nBitMaskIndex += 2; } else // Low DWORD only
{ strSummary += InterpretRCVDBitmask(LOW, pSBigFrame->bNRcv, &pulMasks[nBitMaskIndex]); ++nBitMaskIndex; } } else if ( (pSBigFrame->bFlags & SACK_FLAGS_SACK_MASK2) == SACK_FLAGS_SACK_MASK2 ) // High DWORD only
{ strSummary += ", " + InterpretRCVDBitmask(HIGH, pSBigFrame->bNRcv, &pulMasks[nBitMaskIndex]); ++nBitMaskIndex; }
if ( (pSBigFrame->bFlags & SACK_FLAGS_SEND_MASK1) == SACK_FLAGS_SEND_MASK1 ) { strSummary += ", "; if ( (pSBigFrame->bFlags & SACK_FLAGS_SEND_MASK2) == SACK_FLAGS_SEND_MASK2 ) // Entire QWORD
{ strSummary += InterpretDONTCAREBitmask(ENTIRE, pSBigFrame->bNSeq, &pulMasks[nBitMaskIndex]); } else // Low DWORD only
{ strSummary += InterpretDONTCAREBitmask(LOW, pSBigFrame->bNSeq, &pulMasks[nBitMaskIndex]); } } else if ( (pSBigFrame->bFlags & SACK_FLAGS_SEND_MASK2) == SACK_FLAGS_SEND_MASK2 ) // High DWORD only
{ strSummary += ", " + InterpretDONTCAREBitmask(HIGH, pSBigFrame->bNSeq, &pulMasks[nBitMaskIndex]); } } else // Connection Control CFrame
{ strSummary = "Connection Control - ";
for ( int n = 0; n < sizeof(g_arr_ExOpcodeByteLabels) / sizeof(LABELED_BYTE); ++ n ) { if ( g_arr_ExOpcodeByteLabels[n].Value == rCFrame.bExtOpcode ) { strSummary += g_arr_ExOpcodeByteLabels[n].Label; break; } }
if ( (rCFrame.bCommand & PACKET_COMMAND_POLL) == PACKET_COMMAND_POLL ) { strSummary += " : ACK now"; } } }
strcpy(io_pProperyInstance->szPropertyText, strSummary.c_str()); } // FormatPropertyInstance_TransportSummary
// DESCRIPTION: Custom description formatter for the dataframe's Command field summary
//
// ARGUMENTS: io_pProperyInstance - Data of the property's instance
//
// RETURNS: NOTHING
//
VOID WINAPIV FormatPropertyInstance_DCommandSummary( LPPROPERTYINST io_pProperyInstance ) { BYTE bCommand = *reinterpret_cast<BYTE*>(io_pProperyInstance->lpData);
std::string strSummary = "Command: ";
strSummary += ( (bCommand & PACKET_COMMAND_RELIABLE) == PACKET_COMMAND_RELIABLE ) ? "Reliable" : "Unreliable"; strSummary += ( (bCommand & PACKET_COMMAND_SEQUENTIAL) == PACKET_COMMAND_SEQUENTIAL ) ? ", Sequenced" : ", Nonsequenced"; strSummary += ( (bCommand & PACKET_COMMAND_POLL) == PACKET_COMMAND_POLL ) ? ", Must be ACK'ed immediately" : ", ACK can be delayed";
if ( (bCommand & PACKET_COMMAND_NEW_MSG) == PACKET_COMMAND_NEW_MSG ) { if ( (bCommand & PACKET_COMMAND_END_MSG) != PACKET_COMMAND_END_MSG ) { strSummary += ", First fragment of the message"; } } else if ( (bCommand & PACKET_COMMAND_END_MSG) == PACKET_COMMAND_END_MSG ) { strSummary += ", Last fragment of the message"; }
strSummary += ( (bCommand & PACKET_COMMAND_USER_1) == PACKET_COMMAND_USER_1 ) ? ", DirectPlay packet" : ", User packet"; strSummary += ( (bCommand & PACKET_COMMAND_USER_2) == PACKET_COMMAND_USER_2 ) ? ", Voice packet" : ", Data packet";
strcpy(io_pProperyInstance->szPropertyText, strSummary.c_str());
} // FormatPropertyInstance_DCommandSummary
// DESCRIPTION: Custom description formatter for the Command Frame's Command field summary
//
// ARGUMENTS: io_pProperyInstance - Data of the property's instance
//
// RETURNS: NOTHING
//
VOID WINAPIV FormatPropertyInstance_CCommandSummary( LPPROPERTYINST io_pProperyInstance ) {
BYTE bCommand = *reinterpret_cast<BYTE*>(io_pProperyInstance->lpData);
std::string strSummary = "Command: ";
strSummary += ( (bCommand & PACKET_COMMAND_RELIABLE) == PACKET_COMMAND_RELIABLE ) ? "Reliable" : "Unreliable"; strSummary += ( (bCommand & PACKET_COMMAND_SEQUENTIAL) == PACKET_COMMAND_SEQUENTIAL ) ? ", Sequenced" : ", Nonsequenced"; strSummary += ( (bCommand & PACKET_COMMAND_POLL) == PACKET_COMMAND_POLL ) ? ", Must be ACK'ed immediately" : ", ACK can be delayed"; strcpy(io_pProperyInstance->szPropertyText, strSummary.c_str());
} // FormatPropertyInstance_CCommandSummary
// DESCRIPTION: Custom description formatter for the dataframe's Control field summary
//
// ARGUMENTS: io_pProperyInstance - Data of the property's instance
//
// RETURNS: NOTHING
//
VOID WINAPIV FormatPropertyInstance_ControlSummary( LPPROPERTYINST io_pProperyInstance ) {
BYTE bControl = *reinterpret_cast<BYTE*>(io_pProperyInstance->lpData);
std::string strSummary = "Control: ";
if ( (bControl & PACKET_CONTROL_RETRY) == PACKET_CONTROL_RETRY ) { strSummary += "Retry"; } else { strSummary += "Original"; }
if ( (bControl & PACKET_CONTROL_CORRELATE) == PACKET_CONTROL_CORRELATE ) { strSummary += ", Correlate / KeepAlive"; }
if ( (bControl & PACKET_CONTROL_END_STREAM) == PACKET_CONTROL_END_STREAM ) { strSummary += ", Last packet in the stream"; }
if ( ( (bControl & PACKET_CONTROL_SACK_MASK1) == PACKET_CONTROL_SACK_MASK1 ) || ( (bControl & PACKET_CONTROL_SACK_MASK2) == PACKET_CONTROL_SACK_MASK2 ) ) { strSummary += "RCVD bitmask is nonzero"; }
if ( ( (bControl & PACKET_CONTROL_SEND_MASK1) == PACKET_CONTROL_SEND_MASK1 ) || ( (bControl & PACKET_CONTROL_SEND_MASK2) == PACKET_CONTROL_SEND_MASK2 ) ) { strSummary += ", DON'T CARE bitmask is nonzero"; } strcpy(io_pProperyInstance->szPropertyText, strSummary.c_str());
} // FormatPropertyInstance_ControlSummary
// DESCRIPTION: Custom description formatter for the Command Frame's SACK Flags field summary
//
// ARGUMENTS: io_pProperyInstance - Data of the property's instance
//
// RETURNS: NOTHING
//
VOID WINAPIV FormatPropertyInstance_SACKFlagsSummary( LPPROPERTYINST io_pProperyInstance ) {
BYTE bSACKFlags = *reinterpret_cast<BYTE*>(io_pProperyInstance->lpData);
std::string strSummary = "SACK Flags: "; strSummary += ( (bSACKFlags & SACK_FLAGS_RESPONSE) == SACK_FLAGS_RESPONSE ) ? "Retry and Timestamp fields are valid" : "Retry and/or Timestamp fields are invalid";
if ( ( (bSACKFlags & SACK_FLAGS_SACK_MASK1) == SACK_FLAGS_SACK_MASK1 ) || ( (bSACKFlags & SACK_FLAGS_SACK_MASK2) == SACK_FLAGS_SACK_MASK2 ) ) { strSummary += ", RCVD bitmask is nonzero"; } else { strSummary += ", no RCVD bitmask"; }
if ( ( (bSACKFlags & SACK_FLAGS_SEND_MASK1) == SACK_FLAGS_SEND_MASK1 ) || ( (bSACKFlags & SACK_FLAGS_SEND_MASK2) == SACK_FLAGS_SEND_MASK2 ) ) { strSummary += ", DON'T CARE bitmask is nonzero"; } else { strSummary += ", no DON'T CARE bitmask"; }
strcpy(io_pProperyInstance->szPropertyText, strSummary.c_str());
} // FormatPropertyInstance_SACKFlagsSummary
struct SSACKBitmaskContext { BYTE byBase; BitmaskPart Part; BitmaskType Type; BYTE byBit; };
// DESCRIPTION: Custom description formatter for the Selective Acknowledgement Frame's RCVD bitmask's low/high DWORD summary
//
// ARGUMENTS: io_pProperyInstance - Data of the property's instance
//
// RETURNS: NOTHING
//
VOID WINAPIV FormatPropertyInstance_SACKBitmaskDWORDSummary( LPPROPERTYINST io_pProperyInstance ) { DWORD dwBitmask = *reinterpret_cast<DWORD*>(io_pProperyInstance->lpPropertyInstEx->lpData);
SSACKBitmaskContext& rBitmaskContext = *reinterpret_cast<SSACKBitmaskContext*>(io_pProperyInstance->lpPropertyInstEx->Byte);
std::string strSummary = ( rBitmaskContext.Part == LOW ? "Low" : "High" ); strSummary += " DWORD of "; strSummary += ( rBitmaskContext.Type == RCVD ? "RCVD" : "DON'T CARE" ); strSummary += " bitmask: ";
strSummary += ( ( rBitmaskContext.Type == RCVD ) ? InterpretRCVDBitmask(rBitmaskContext.Part, rBitmaskContext.byBase, &dwBitmask) : InterpretDONTCAREBitmask(rBitmaskContext.Part, rBitmaskContext.byBase, &dwBitmask) );
strcpy(io_pProperyInstance->szPropertyText, strSummary.c_str());
} // FormatPropertyInstance_SACKBitmaskDWORDSummary
// DESCRIPTION: Custom description formatter for the bitmask's low/high DWORD summary
//
// ARGUMENTS: io_pProperyInstance - Data of the property's instance
//
// RETURNS: NOTHING
//
VOID WINAPIV FormatPropertyInstance_DWORDBitmaskEntry( LPPROPERTYINST io_pProperyInstance ) { DWORD dwBitmask = *reinterpret_cast<DWORD*>(io_pProperyInstance->lpPropertyInstEx->lpData);
SSACKBitmaskContext& rBitmaskContext = *reinterpret_cast<SSACKBitmaskContext*>(io_pProperyInstance->lpPropertyInstEx->Byte);
BYTE byBase = rBitmaskContext.byBase; BYTE byBit = rBitmaskContext.byBit; switch ( rBitmaskContext.Type ) { case RCVD: { // [i_bBase+1 .. i_bBase+1+LENGTH]
// RCVD bitmask doesn't include the base value, since receiver can't claim it didn't receive the next dataframe to be received (NRcv);
if ( rBitmaskContext.Part == HIGH ) { // NOTE: +1 is needed to cross from MSB of the first DWORD TO LSB of the second
byBase += 8*sizeof(DWORD)+1; // shift to LSB of the second DWORD
} else { ++byBase; }
byBase += byBit; break; }
case DONTCARE: { // [i_bBase-1-LENGTH .. i_bBase-1]
// DON'T CARE doesn't include the base value, since transmitter can't resend/refuse resending a dataframe which is about to be sent next (NSeq).
if ( rBitmaskContext.Part == HIGH ) { byBase -= 8*sizeof(DWORD); // shift to MSB of the first DWORD
} else { --byBase; }
byBase -= byBit; break; }
default: // TODO: ASSERT HERE (SHOULD NEVER HAPPEN)
break; }
static DWORD arr_dwFlags[] = { 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000, 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000, 0x00800000, 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000 }; char arr_cBuffer[100]; char arr_cTemplate[] = "................................ = %s %d (%d%c%d)";
arr_cTemplate[31-byBit] = ( (dwBitmask & arr_dwFlags[byBit]) ? '1' : '0' ); switch ( rBitmaskContext.Type ) { case RCVD: { sprintf(arr_cBuffer, arr_cTemplate, ((dwBitmask & arr_dwFlags[byBit]) ? "Received" : "Did not receive"), byBase, rBitmaskContext.byBase, '+', byBit+1); ++byBase; break; }
case DONTCARE: { sprintf(arr_cBuffer, arr_cTemplate, ((dwBitmask & arr_dwFlags[byBit]) ? "Cancelling" : "Successfully transmitted"), byBase, rBitmaskContext.byBase, '-', byBit+1); --byBase; break; } }
strcpy(io_pProperyInstance->szPropertyText, arr_cBuffer);
} // FormatPropertyInstance_DWORDBitmaskEntry
//==================//
// Properties table //-----------------------------------------------------------------------------------------------
//==================//
PROPERTYINFO g_arr_TransportProperties[] = {
// Transport packet summary property (TRANSPORT_SUMMARY)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"", // label
"DPlay Direct Network packet", // status-bar comment
PROP_TYPE_SUMMARY, // data type
PROP_QUAL_NONE, // data type qualifier
NULL, // labeled bit set
512, // description's maximum length
FormatPropertyInstance_TransportSummary // generic formatter
},
// DCommand field summary property (TRANSPORT_DCOMMAND_SUMMARY)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"", // label
"Command field summary", // status-bar comment
PROP_TYPE_SUMMARY, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // labeled bit set
512, // description's maximum length
FormatPropertyInstance_DCommandSummary // generic formatter
},
// DCommand field property (TRANSPORT_DCOMMAND)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"", // label
"Command field", // status-bar comment
PROP_TYPE_BYTE, // data type
PROP_QUAL_FLAGS, // data type qualifier.
&g_LabeledDCommandBitSet, // labeled bit set
512, // description's maximum length
FormatPropertyInstance // generic formatter
},
// Control field summary property (TRANSPORT_CONTROL_SUMMARY)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"", // label
"Control field summary", // status-bar comment
PROP_TYPE_SUMMARY, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // labeled bit set
512, // description's maximum length
FormatPropertyInstance_ControlSummary // generic formatter
},
// Control field property (TRANSPORT_CONTROL)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"Control", // label
"Control field", // status-bar comment
PROP_TYPE_BYTE, // data type
PROP_QUAL_FLAGS, // data type qualifier.
&g_LabeledControlBitSet, // labeled byte set
512, // description's maximum length
FormatPropertyInstance // generic formatter
},
// Packet sequence number property (TRANSPORT_SEQNUM)
//
// INFO: This number is incremented for each _new_ packet sent. If an endpoint retransmits
// a packet, it uses the same sequence number it did the first time it sent it (base value for the DON'T CARE bitmask).
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"Seq: Highest dataframe # sent (base value for the DON'T CARE bitmask)", // label
"Highest dataframe # sent field", // status-bar comment
PROP_TYPE_BYTE, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // no data qualifiers
512, // description's maximum length
FormatPropertyInstance // generic formatter
},
// Next receive number property (TRANSPORT_NEXTRECVNUM)
//
// INFO: Acknowledges every packet with a sequence number up to but not including this number (base value for the RCVD bitmask)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"NRcv: Next dataframe # to be received (base value for the RCVD bitmask)", // label
"Next dataframe # to be received field", // status-bar comment
PROP_TYPE_BYTE, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // no data qualifiers
512, // description's maximum length
FormatPropertyInstance // generic formatter
},
// CCommand field property (TRANSPORT_CCOMMAND_SUMMARY)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"", // label
"Command field summary", // status-bar comment
PROP_TYPE_SUMMARY, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // labeled bit set
512, // description's maximum length
FormatPropertyInstance_CCommandSummary // generic formatter
},
// CCommand field property (TRANSPORT_CCOMMAND)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"", // label
"Command field", // status-bar comment
PROP_TYPE_BYTE, // data type
PROP_QUAL_FLAGS, // data type qualifier.
&g_LabeledCCommandBitSet, // labeled bit set
512, // description's maximum length
FormatPropertyInstance // generic formatter
},
// Extended opcode field property (TRANSPORT_EXOPCODE)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"Extended opcode", // label
"Extended opcode field", // status-bar comment
PROP_TYPE_BYTE, // data type
PROP_QUAL_LABELED_SET, // data type qualifier.
&g_LabeledExOpcodeByteSet, // labeled byte set
512, // description's maximum length
FormatPropertyInstance // generic formatter
},
// Message ID field property (TRANSPORT_MSGID)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"Message ID", // label
"Message ID field", // status-bar comment
PROP_TYPE_BYTE, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // no data qualifiers
512, // description's maximum length
FormatPropertyInstance // generic formatter
},
// Response ID field propery (TRANSPORT_RSPID)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"Response ID", // label
"Response ID field", // status-bar comment
PROP_TYPE_BYTE, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // no data qualifiers
512, // description's maximum length
FormatPropertyInstance // generic formatter
},
// Protocol version field property (TRANSPORT_VERSION)
//
// INFO: Makes sure both endpoints use the same version of the protocol.
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"Version", // label
"Version field", // status-bar comment
PROP_TYPE_DWORD, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // no data qualifiers
512, // description's maximum length
FormatPropertyInstance // generic formatter
},
// Session ID field property (TRANSPORT_SESSIONID)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"Session ID", // label
"Session ID field", // status-bar comment
PROP_TYPE_DWORD, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // no data qualifiers
512, // description's maximum length
FormatPropertyInstance // generic formatter
},
// Time stamp field property (TRANSPORT_TIMESTAMP)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"Time stamp", // label
"Time stamp field", // status-bar comment
PROP_TYPE_DWORD, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // no data qualifiers
512, // description's maximum length
FormatPropertyInstance // generic formatter
},
// SACK flags field property (TRANSPORT_SACKFIELDS_SUMMARY)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"", // label
"SACK flags summary", // status-bar comment
PROP_TYPE_SUMMARY, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // labeled byte set
512, // description's maximum length
FormatPropertyInstance_SACKFlagsSummary // generic formatter
},
// SACK flags field property (TRANSPORT_SACKFIELDS)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"", // label
"SACK flags field", // status-bar comment
PROP_TYPE_BYTE, // data type
PROP_QUAL_FLAGS, // data type qualifier.
&g_LabeledSACKFlagsBitSet, // labeled byte set
512, // description's maximum length
FormatPropertyInstance // generic formatter
},
// Retry field property (TRANSPORT_RETRY)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"Retry", // label
"Retry field", // status-bar comment
PROP_TYPE_BYTE, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // no data qualifiers
512, // description's maximum length
FormatPropertyInstance // generic formatter
},
// Next sequence number field property (TRANSPORT_NEXTSEQNUM)
//
// INFO: Sequence number of the next DFrame to be sent (base value for the DON'T CARE bitmask)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"NSeq: Next dataframe # to be sent (base value for the DON'T CARE bitmask)", // label
"Next dataframe # to be sent field", // status-bar comment
PROP_TYPE_BYTE, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // no data qualifiers
512, // description's maximum length
FormatPropertyInstance // generic formatter
},
// Low DWORD of the Selective-ACK RCVD Mask summary (TRANSPORT_RCVDMASK1_SUMMARY)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"", // label
"Low DWORD of the RCVD mask summary", // status-bar comment
PROP_TYPE_SUMMARY, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // labeled byte set
512, // description's maximum length
FormatPropertyInstance_SACKBitmaskDWORDSummary // generic formatter
},
// Low DWORD of the Selective-ACK RCVD Mask property (TRANSPORT_RCVDMASK1)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"Low DWORD of the RCVD mask", // label
"Low DWORD of the RCVD mask field", // status-bar comment
PROP_TYPE_DWORD, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // labeled bit set
3072, // description's maximum length
FormatPropertyInstance_DWORDBitmaskEntry // generic formatter
},
// High DWORD of the Selective-ACK RCVD Mask summary (TRANSPORT_RCVDMASK2_SUMMARY)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"", // label
"High DWORD of the RCVD mask summary", // status-bar comment
PROP_TYPE_SUMMARY, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // labeled byte set
512, // description's maximum length
FormatPropertyInstance_SACKBitmaskDWORDSummary // generic formatter
},
// High DWORD of Selective-ACK RCVD Mask property (TRANSPORT_RCVDMASK2)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"High DWORD of the RCVD mask", // label
"High DWORD of the RCVD mask field", // status-bar comment
PROP_TYPE_DWORD, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // labeled bit set
3072, // description's maximum length
FormatPropertyInstance_DWORDBitmaskEntry // generic formatter
},
// Low DWORD of the Selective-ACK DON'T CARE Mask summary (TRANSPORT_DONTCAREMASK1_SUMMARY)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"", // label
"Low DWORD of the DON'T CARE mask summary", // status-bar comment
PROP_TYPE_SUMMARY, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // labeled byte set
512, // description's maximum length
FormatPropertyInstance_SACKBitmaskDWORDSummary // generic formatter
},
// Low DWORD of the Selective-ACK DON'T CARE Mask property (TRANSPORT_DONTCAREMASK1)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"Low DWORD of the DON'T CARE mask", // label
"Low DWORD of the DON'T CARE mask field", // status-bar comment
PROP_TYPE_DWORD, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // labeled bit set
3072, // description's maximum length
FormatPropertyInstance_DWORDBitmaskEntry // generic formatter
},
// High DWORD of the Selective-ACK DON'T CARE Mask summary (TRANSPORT_DONTCAREMASK2_SUMMARY)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"", // label
"High DWORD of the DON'T CARE mask summary", // status-bar comment
PROP_TYPE_SUMMARY, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // labeled byte set
512, // description's maximum length
FormatPropertyInstance_SACKBitmaskDWORDSummary // generic formatter
},
// High DWORD of the Selective-ACK DON'T CARE Mask property (TRANSPORT_DONTCAREMASK2)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"High DWORD of the DON'T CARE mask", // label
"High DWORD of the DON'T CARE mask field", // status-bar comment
PROP_TYPE_DWORD, // data type
PROP_QUAL_NONE, // data type qualifier.
NULL, // labeled bit set
3072, // description's maximum length
FormatPropertyInstance_DWORDBitmaskEntry // generic formatter
},
// Compression Type property (VOICE_USERDATA)
{ 0, // handle placeholder (MBZ)
0, // reserved (MBZ)
"User Data", // label
"User Data", // status-bar comment
PROP_TYPE_RAW_DATA, // data type (GUID)
PROP_QUAL_NONE, // data type qualifier.
NULL, // labeled bit set
64, // description's maximum length
FormatPropertyInstance // generic formatter
}
};
enum { nNUM_OF_TRANSPORT_PROPS = sizeof(g_arr_TransportProperties) / sizeof(PROPERTYINFO) };
// Properties' indices
enum { TRANSPORT_SUMMARY = 0, TRANSPORT_DCOMMAND_SUMMARY, TRANSPORT_DCOMMAND, TRANSPORT_CONTROL_SUMMARY, TRANSPORT_CONTROL, TRANSPORT_SEQNUM, TRANSPORT_NEXTRECVNUM, TRANSPORT_CCOMMAND_SUMMARY, TRANSPORT_CCOMMAND, TRANSPORT_EXOPCODE, TRANSPORT_MSGID, TRANSPORT_RSPID, TRANSPORT_VERSION, TRANSPORT_SESSIONID, TRANSPORT_TIMESTAMP, TRANSPORT_SACKFIELDS_SUMMARY, TRANSPORT_SACKFIELDS, TRANSPORT_RETRY, TRANSPORT_NEXTSEQNUM, TRANSPORT_RCVDMASK1_SUMMARY, TRANSPORT_RCVDMASK1, TRANSPORT_RCVDMASK2_SUMMARY, TRANSPORT_RCVDMASK2, TRANSPORT_DONTCAREMASK1_SUMMARY, TRANSPORT_DONTCAREMASK1, TRANSPORT_DONTCAREMASK2_SUMMARY, TRANSPORT_DONTCAREMASK2, TRANSPORT_USERDATA };
} // anonymous namespace
// DESCRIPTION: Creates and fills-in a properties database for the protocol.
// Network Monitor uses this database to determine which properties the protocol supports.
//
// ARGUMENTS: i_hTransportProtocol - The handle of the protocol provided by the Network Monitor.
//
// RETURNS: NOTHING
//
DPLAYPARSER_API VOID BHAPI TransportRegister( HPROTOCOL i_hTransportProtocol ) {
CreatePropertyDatabase(i_hTransportProtocol, nNUM_OF_TRANSPORT_PROPS);
// Add the properties to the database
for( int nProp=0; nProp < nNUM_OF_TRANSPORT_PROPS; ++nProp ) { AddProperty(i_hTransportProtocol, &g_arr_TransportProperties[nProp]); }
} // TransportRegister
// DESCRIPTION: Frees the resources used to create the protocol property database.
//
// ARGUMENTS: i_hTransportProtocol - The handle of the protocol provided by the Network Monitor.
//
// RETURNS: NOTHING
//
DPLAYPARSER_API VOID WINAPI TransportDeregister( HPROTOCOL i_hProtocol ) {
DestroyPropertyDatabase(i_hProtocol);
} // TransportDeregister
namespace {
// DESCRIPTION: Parses the Transport frame to find its size (in bytes) NOT including the user data
//
// ARGUMENTS: i_pbTransportFrame - Pointer to the start of the unclaimed data. Typically, the unclaimed data is located
// in the middle of a frame because a previous parser has claimed data before this parser.
//
// RETURNS: Size of the specified Transport frame (in bytes)
//
int TransportHeaderSize( BYTE* i_pbTransportFrame ) {
int arr_nNumOfBits[] = { /*00 = 0x0000*/ 0, /*01 = 0x0001*/ 1, /*02 = 0x0010*/ 1, /*03 = 0x0011*/ 2, /*04 = 0x0100*/ 1, /*05 = 0x0101*/ 2, /*06 = 0x0110*/ 2, /*07 = 0x0111*/ 3, /*08 = 0x1000*/ 1, /*09 = 0x1001*/ 2, /*10 = 0x1010*/ 2, /*11 = 0x1011*/ 3, /*12 = 0x1100*/ 2, /*13 = 0x1101*/ 3, /*14 = 0x1110*/ 3, /*15 = 0x1111*/ 4 }; const DFRAME& rDFrame = *reinterpret_cast<DFRAME*>(i_pbTransportFrame);
if ( (rDFrame.bCommand & PACKET_COMMAND_DATA) == PACKET_COMMAND_DATA ) // DFrame
{ return sizeof(rDFrame) + sizeof(DWORD)*arr_nNumOfBits[rDFrame.bControl >> 4]; } else { const CFRAME& rCFrame = *reinterpret_cast<CFRAME*>(i_pbTransportFrame);
if ( rCFrame.bExtOpcode == FRAME_EXOPCODE_SACK ) // SACK CFrame
{ const SFBIG8* pSFrame = reinterpret_cast<SFBIG8*>(i_pbTransportFrame); ULONG ulMaskSize = sizeof(DWORD)*arr_nNumOfBits[(pSFrame->bFlags >> 1) & 0x0F];
// This is a Protocol version 1.0 frame
return sizeof(SACKFRAME8) + ulMaskSize; } else // Connection Control CFrame
{ return sizeof(rCFrame); } }
} // TransportHeaderSize
} // Anonymous namespace
// DESCRIPTION: Indicates whether a piece of data is recognized as the protocol that the parser detects.
//
// ARGUMENTS: i_hFrame - The handle to the frame that contains the data.
// i_pbMacFrame - The pointer to the first byte of the frame; the pointer provides a way to view
// the data that the other parsers recognize.
// i_pbTransportFrame - Pointer to the start of the unclaimed data. Typically, the unclaimed data is located
// in the middle of a frame because a previous parser has claimed data before this parser.
// i_dwMacType - MAC value of the first protocol in a frame. Typically, the i_dwMacType value is used
// when the parser must identify the first protocol in the frame. Can be one of the following:
// MAC_TYPE_ETHERNET = 802.3, MAC_TYPE_TOKENRING = 802.5, MAC_TYPE_FDDI ANSI = X3T9.5.
// i_dwBytesLeft - The remaining number of bytes from a location in the frame to the end of the frame.
// i_hPrevProtocol - Handle of the previous protocol.
// i_dwPrevProtOffset - Offset of the previous protocol (from the beginning of the frame).
// o_pdwProtocolStatus - Protocol status indicator. Must be one of the following: PROTOCOL_STATUS_RECOGNIZED,
// PROTOCOL_STATUS_NOT_RECOGNIZED, PROTOCOL_STATUS_CLAIMED, PROTOCOL_STATUS_NEXT_PROTOCOL.
// o_phNextProtocol - Placeholder for the handle of the next protocol. This parameter is set when the parser identifies
// the protocol that follows its own protocol.
// io_pdwptrInstData - On input, a pointer to the instance data from the previous protocol.
// On output, a pointer to the instance data for the current protocol.
//
// RETURNS: If the function is successful, the return value is a pointer to the first byte after the recognized parser data.
// If the parser claims all the remaining data, the return value is NULL. If the function is unsuccessful, the return
// value is the initial value of the i_pbTransportFrame parameter.
//
DPLAYPARSER_API BYTE* BHAPI TransportRecognizeFrame( HFRAME i_hFrame, ULPBYTE i_upbMacFrame, ULPBYTE i_upbTransportFrame, DWORD i_dwMacType, DWORD i_dwBytesLeft, HPROTOCOL i_hPrevProtocol, DWORD i_dwPrevProtOffset, LPDWORD o_pdwProtocolStatus, LPHPROTOCOL o_phNextProtocol, PDWORD_PTR io_pdwptrInstData ) {
// Validate the amount of unclaimed data
enum { nMIN_TransportHeaderSize = min(min(sizeof(DFRAME), sizeof(CFRAME)), sizeof(SACKFRAME8)) };
// Validate the packet as DPlay Transport type
if ( i_dwBytesLeft < nMIN_TransportHeaderSize ) { // Assume the unclaimed data is not recognizable
*o_pdwProtocolStatus = PROTOCOL_STATUS_NOT_RECOGNIZED; return i_upbTransportFrame; }
// Check if we are dealing with a DPlay Voice packet
enum { PACKET_COMMAND_SESSION = PACKET_COMMAND_DATA | PACKET_COMMAND_USER_1, PACKET_COMMAND_VOICE = PACKET_COMMAND_DATA | PACKET_COMMAND_USER_1 | PACKET_COMMAND_USER_2 };
const DFRAME& rDFrame = *reinterpret_cast<DFRAME*>(i_upbTransportFrame);
*o_pdwProtocolStatus = PROTOCOL_STATUS_NEXT_PROTOCOL;
// Let upper protocol's parser know if the message is a non-initial fragment of a fragmented message
*io_pdwptrInstData = ((rDFrame.bCommand & PACKET_COMMAND_NEW_MSG) == PACKET_COMMAND_NEW_MSG);
if ( (rDFrame.bCommand & PACKET_COMMAND_VOICE) == PACKET_COMMAND_VOICE ) { // Notify NetMon about the handoff protocol
*o_phNextProtocol = GetProtocolFromName("DPLAYVOICE"); } else if ( (rDFrame.bCommand & PACKET_COMMAND_SESSION) == PACKET_COMMAND_SESSION ) { // Notify NetMon about the handoff protocol
*o_phNextProtocol = GetProtocolFromName("DPLAYSESSION"); } else { *o_pdwProtocolStatus = PROTOCOL_STATUS_RECOGNIZED; *o_phNextProtocol = NULL; }
return i_upbTransportFrame + TransportHeaderSize(i_upbTransportFrame);
} // TransportRecognizeFrame
//=======================================//
// Attaching properties helper functions //
//=======================================//
namespace {
// DESCRIPTION: Maps the DWORD bitmask properties on a per-bit basis.
//
// ARGUMENTS: i_hFrame - Handle of the frame that is being parsed.
// i_nPropertyIndex - Index of the property in the global properties table.
// i_pdwBitmask - Pointer to the value to which the property is being attached.
// i_byBase - Base value from which the entry value is calculated.
// i_Part - LOW or HIGH part of the QWORD bitmask.
// i_Type - RCVD or DONTCARE type of the bitmask.
// i_byLevel - Level in the detail pane tree.
//
// RETURNS: NOTHING
//
VOID WINAPIV AttachBitmaskDWORDProperties( HFRAME i_hFrame, int i_nPropertyIndex, UNALIGNED DWORD* i_pdwBitmask, BYTE i_byBase, BitmaskPart i_Part, BitmaskType i_Type, BYTE i_byLevel ) { for ( BYTE byBit = 0; byBit < 32; ++byBit ) { SSACKBitmaskContext BitmaskContext = { i_byBase, i_Part, i_Type, byBit };
AttachPropertyInstanceEx(i_hFrame, g_arr_TransportProperties[i_nPropertyIndex].hProperty, sizeof(*i_pdwBitmask), i_pdwBitmask, sizeof(BitmaskContext), &BitmaskContext, 0, i_byLevel, 0); } } // AttachBitmaskDWORDProperties
// DESCRIPTION: Maps the Data-Frame properties that exist in a piece of recognized data to specific locations.
//
// ARGUMENTS: i_hFrame - Handle of the frame that is being parsed.
// i_pbDFrame - Pointer to the start of the recognized data.
//
// RETURNS: NOTHING
//
void AttachDFRAMEProperties( HFRAME i_hFrame, BYTE* i_pbDFrame ) {
//=======================================//
// Processing the core dataframe fields //
//=======================================//
//
DFRAME& rDFrame = *reinterpret_cast<DFRAME*>(i_pbDFrame); // DCommand summary
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_DCOMMAND_SUMMARY].hProperty, sizeof(rDFrame.bCommand), &rDFrame.bCommand, 0, 1, 0); // DCommand field
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_DCOMMAND].hProperty, sizeof(rDFrame.bCommand), &rDFrame.bCommand, 0, 2, 0);
// Control summary
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_CONTROL_SUMMARY].hProperty, sizeof(rDFrame.bControl), &rDFrame.bControl, 0, 1, 0); // Control field
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_CONTROL].hProperty, sizeof(rDFrame.bControl), &rDFrame.bControl, 0, 2, 0);
// Sequence number field
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_SEQNUM].hProperty, sizeof(rDFrame.bSeq), &rDFrame.bSeq, 0, 1, 0);
// Next receive number field
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_NEXTRECVNUM].hProperty, sizeof(rDFrame.bNRcv), &rDFrame.bNRcv, 0, 1, 0);
//==================================================//
// Processing the optional dataframe bitmask fields //
//==================================================//
//
UNALIGNED DFBIG& rDBigFrame = *reinterpret_cast<UNALIGNED DFBIG *>(i_pbDFrame); int nBitMaskIndex = 0;
if ( (rDFrame.bControl & PACKET_CONTROL_SACK_MASK1) == PACKET_CONTROL_SACK_MASK1 ) { SSACKBitmaskContext LowRCVDContext = { rDBigFrame.bNRcv, LOW, RCVD, NULL }; // Low DWORD of Selective-ACK RCVD Mask summary (TRANSPORT_RCVDMASK1_SUMMARY)
AttachPropertyInstanceEx(i_hFrame, g_arr_TransportProperties[TRANSPORT_RCVDMASK1_SUMMARY].hProperty, sizeof(rDBigFrame.rgMask[nBitMaskIndex]), &rDBigFrame.rgMask[nBitMaskIndex], sizeof(LowRCVDContext), &LowRCVDContext, 0, 1, 0);
// Low DWORD of Selective-ACK RCVD Mask field (TRANSPORT_RCVDMASK1)
AttachBitmaskDWORDProperties(i_hFrame, TRANSPORT_RCVDMASK1, &rDBigFrame.rgMask[nBitMaskIndex], rDBigFrame.bNRcv, LOW, RCVD, 2);
++nBitMaskIndex; }
if ( (rDFrame.bControl & PACKET_CONTROL_SACK_MASK2) == PACKET_CONTROL_SACK_MASK2 ) { SSACKBitmaskContext HighRCVDContext = { rDBigFrame.bNRcv, HIGH, RCVD, NULL }; // High DWORD of Selective-ACK RCVD Mask summary (TRANSPORT_RCVDMASK2_SUMMARY)
AttachPropertyInstanceEx(i_hFrame, g_arr_TransportProperties[TRANSPORT_RCVDMASK2_SUMMARY].hProperty, sizeof(rDBigFrame.rgMask[nBitMaskIndex]), &rDBigFrame.rgMask[nBitMaskIndex], sizeof(HighRCVDContext), &HighRCVDContext, 0, 1, 0);
// High DWORD of Selective-ACK RCVD Mask field (TRANSPORT_RCVDMASK2)
AttachBitmaskDWORDProperties(i_hFrame, TRANSPORT_RCVDMASK2, &rDBigFrame.rgMask[nBitMaskIndex], rDBigFrame.bNRcv, HIGH, RCVD, 2);
++nBitMaskIndex; }
if ( (rDFrame.bControl & PACKET_CONTROL_SEND_MASK1) == PACKET_CONTROL_SEND_MASK1 ) { SSACKBitmaskContext LowDONTCAREContext = { rDBigFrame.bSeq, LOW, DONTCARE, NULL };
// Low DWORD of Selective-ACK DON'T CARE Mask summary (TRANSPORT_DONTCAREMASK1_SUMMARY)
AttachPropertyInstanceEx(i_hFrame, g_arr_TransportProperties[TRANSPORT_DONTCAREMASK1_SUMMARY].hProperty, sizeof(rDBigFrame.rgMask[nBitMaskIndex]), &rDBigFrame.rgMask[nBitMaskIndex], sizeof(LowDONTCAREContext), &LowDONTCAREContext, 0, 1, 0);
// Low DWORD of Selective-ACK RCVD Mask field (TRANSPORT_DONTCAREMASK1)
AttachBitmaskDWORDProperties(i_hFrame, TRANSPORT_RCVDMASK1, &rDBigFrame.rgMask[nBitMaskIndex], rDBigFrame.bSeq, LOW, DONTCARE, 2);
++nBitMaskIndex; }
if ( (rDFrame.bControl & PACKET_CONTROL_SEND_MASK2) == PACKET_CONTROL_SEND_MASK2 ) { SSACKBitmaskContext HighDONTCAREContext = { rDBigFrame.bSeq, HIGH, DONTCARE, NULL }; // High DWORD of Selective-ACK DON'T CARE Mask summary (TRANSPORT_DONTCAREMASK2_SUMMARY)
AttachPropertyInstanceEx(i_hFrame, g_arr_TransportProperties[TRANSPORT_DONTCAREMASK2_SUMMARY].hProperty, sizeof(rDBigFrame.rgMask[nBitMaskIndex]), &rDBigFrame.rgMask[nBitMaskIndex], sizeof(HighDONTCAREContext), &HighDONTCAREContext, 0, 1, 0);
// High DWORD of Selective-ACK RCVD Mask field (TRANSPORT_DONTCAREMASK2)
AttachBitmaskDWORDProperties(i_hFrame, TRANSPORT_RCVDMASK2, &rDBigFrame.rgMask[nBitMaskIndex], rDBigFrame.bSeq, HIGH, DONTCARE, 2);
}
} // AttachDFRAMEProperties
// DESCRIPTION: Maps the Command-Frame properties that exist in a piece of recognized data to specific locations.
//
// ARGUMENTS: i_hFrame - Handle of the frame that is being parsed.
// i_pbCFrame - Pointer to the start of the recognized data.
//
// RETURNS: NOTHING
//
void AttachCFRAMEProperties( HFRAME i_hFrame, BYTE* i_pbCFrame) {
// Processing the core command frame fields
//
CFRAME& rCFrame = *reinterpret_cast<CFRAME*>(i_pbCFrame);
// CCommand summary
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_CCOMMAND_SUMMARY].hProperty, sizeof(rCFrame.bCommand), &rCFrame.bCommand, 0, 1, 0); // CCommand field
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_CCOMMAND].hProperty, sizeof(rCFrame.bCommand), &rCFrame.bCommand, 0, 2, 0);
// ExtOpcode field
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_EXOPCODE].hProperty, sizeof(rCFrame.bExtOpcode), &rCFrame.bExtOpcode, 0, 1, 0);
if ( rCFrame.bExtOpcode == FRAME_EXOPCODE_SACK ) { //=======================================================//
// Processing the Selective Acknowledgement Command frame fields //
//=======================================================//
//
SFBIG8* pSFrame = reinterpret_cast<SFBIG8*>(i_pbCFrame);
// SACK flags summary
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_SACKFIELDS_SUMMARY].hProperty, sizeof(pSFrame->bFlags), &pSFrame->bFlags, 0, 1, 0); // SACK flags field
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_SACKFIELDS].hProperty, sizeof(pSFrame->bFlags), &pSFrame->bFlags, 0, 2, 0);
// Retry field
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_RETRY].hProperty, sizeof(pSFrame->bRetry), &pSFrame->bRetry, 0, 1, 0);
// Next sequence number field
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_NEXTSEQNUM].hProperty, sizeof(pSFrame->bNSeq), &pSFrame->bNSeq, 0, 1, 0); // Next receive number field
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_NEXTRECVNUM].hProperty, sizeof(pSFrame->bNRcv), &pSFrame->bNRcv, 0, 1, 0);
UNALIGNED ULONG* pulMasks = 0;
// This is a Protocol version 1.0 frame
// Timestamp field
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_TIMESTAMP].hProperty, sizeof(pSFrame->tTimestamp), &pSFrame->tTimestamp, 0, 1, 0); pulMasks = pSFrame->rgMask;
//================================================================================//
// Processing the optional Selective Acknowledgement Command frame bitmask fields //
//================================================================================//
//
int nBitMaskIndex = 0;
if ( (pSFrame->bFlags & SACK_FLAGS_SACK_MASK1) == SACK_FLAGS_SACK_MASK1 ) { SSACKBitmaskContext LowRCVDContext = { pSFrame->bNRcv, LOW, RCVD, NULL }; // Low DWORD of Selective-ACK RCVD Mask summary (TRANSPORT_RCVDMASK1_SUMMARY)
AttachPropertyInstanceEx(i_hFrame, g_arr_TransportProperties[TRANSPORT_RCVDMASK1_SUMMARY].hProperty, sizeof(pulMasks[nBitMaskIndex]), &pulMasks[nBitMaskIndex], sizeof(LowRCVDContext), &LowRCVDContext, 0, 1, 0);
// Low DWORD of Selective-ACK RCVD Mask field (TRANSPORT_RCVDMASK1)
AttachBitmaskDWORDProperties(i_hFrame, TRANSPORT_RCVDMASK1, &pulMasks[nBitMaskIndex], pSFrame->bNRcv, LOW, RCVD, 2); ++nBitMaskIndex; }
if ( (pSFrame->bFlags & SACK_FLAGS_SACK_MASK2) == SACK_FLAGS_SACK_MASK2 ) { SSACKBitmaskContext HighRCVDContext = { pSFrame->bNRcv, HIGH, RCVD, NULL }; // High DWORD of Selective-ACK RCVD Mask summary (TRANSPORT_RCVDMASK2_SUMMARY)
AttachPropertyInstanceEx(i_hFrame, g_arr_TransportProperties[TRANSPORT_RCVDMASK2_SUMMARY].hProperty, sizeof(pulMasks[nBitMaskIndex]), &pulMasks[nBitMaskIndex], sizeof(HighRCVDContext), &HighRCVDContext, 0, 1, 0);
// High DWORD of Selective-ACK RCVD Mask field (TRANSPORT_RCVDMASK2)
AttachBitmaskDWORDProperties(i_hFrame, TRANSPORT_RCVDMASK2, &pulMasks[nBitMaskIndex], pSFrame->bNRcv, HIGH, RCVD, 2);
++nBitMaskIndex; }
if ( (pSFrame->bFlags & SACK_FLAGS_SEND_MASK1) == SACK_FLAGS_SEND_MASK1 ) { SSACKBitmaskContext LowDONTCAREContext = { pSFrame->bNSeq, LOW, DONTCARE, NULL }; // Low DWORD of Selective-ACK DON'T CARE Mask summary (TRANSPORT_DONTCAREMASK1_SUMMARY)
AttachPropertyInstanceEx(i_hFrame, g_arr_TransportProperties[TRANSPORT_DONTCAREMASK1_SUMMARY].hProperty, sizeof(pulMasks[nBitMaskIndex]), &pulMasks[nBitMaskIndex], sizeof(LowDONTCAREContext), &LowDONTCAREContext, 0, 1, 0);
// Low DWORD of Selective-ACK RCVD Mask field (TRANSPORT_DONTCAREMASK1)
AttachBitmaskDWORDProperties(i_hFrame, TRANSPORT_RCVDMASK1, &pulMasks[nBitMaskIndex], pSFrame->bNSeq, LOW, DONTCARE, 2); ++nBitMaskIndex; }
if ( (pSFrame->bFlags & SACK_FLAGS_SEND_MASK2) == SACK_FLAGS_SEND_MASK2 ) { SSACKBitmaskContext HighDONTCAREContext = { pSFrame->bNSeq, HIGH, DONTCARE, NULL }; // High DWORD of Selective-ACK DON'T CARE Mask summary (TRANSPORT_DONTCAREMASK2_SUMMARY)
AttachPropertyInstanceEx(i_hFrame, g_arr_TransportProperties[TRANSPORT_DONTCAREMASK2_SUMMARY].hProperty, sizeof(pulMasks[nBitMaskIndex]), &pulMasks[nBitMaskIndex], sizeof(HighDONTCAREContext), &HighDONTCAREContext, 0, 1, 0);
// High DWORD of Selective-ACK RCVD Mask field (TRANSPORT_DONTCAREMASK2)
AttachBitmaskDWORDProperties(i_hFrame, TRANSPORT_RCVDMASK2, &pulMasks[nBitMaskIndex], pSFrame->bNSeq, HIGH, DONTCARE, 2); ++nBitMaskIndex; } } else { //========================================================//
// Processing the Connection Control Command frame fields //
//========================================================//
// Message ID field
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_MSGID].hProperty, sizeof(rCFrame.bMsgID), &rCFrame.bMsgID, 0, 1, 0);
// Response ID field
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_RSPID].hProperty, sizeof(rCFrame.bRspID), &rCFrame.bRspID, 0, 1, 0);
// Version number field
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_VERSION].hProperty, sizeof(rCFrame.dwVersion), &rCFrame.dwVersion, 0, 1, 0);
// Session ID field
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_SESSIONID].hProperty, sizeof(rCFrame.dwSessID), &rCFrame.dwSessID, 0, 1, 0); // Timestamp field
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_TIMESTAMP].hProperty, sizeof(rCFrame.tTimestamp), &rCFrame.tTimestamp, 0, 1, 0); }
} // AttachCFRAMEProperties
// Platform independent memory accessor of big endian words
inline WORD ReadBigEndianWord( BYTE* i_pbData ) { return (*i_pbData << 8) | *(i_pbData+1); }
} // Anonymous namespace
// DESCRIPTION: Maps the properties that exist in a piece of recognized data to specific locations.
//
// ARGUMENTS: i_hFrame - Handle of the frame that is being parsed.
// i_pbMacFram - Pointer to the first byte in the frame.
// i_pbTransportFrame - Pointer to the start of the recognized data.
// i_dwMacType - MAC value of the first protocol in a frame. Typically, the i_dwMacType value is used
// when the parser must identify the first protocol in the frame. Can be one of the following:
// MAC_TYPE_ETHERNET = 802.3, MAC_TYPE_TOKENRING = 802.5, MAC_TYPE_FDDI ANSI = X3T9.5.
// i_dwBytesLeft - The remaining number of bytes in a frame (starting from the beginning of the recognized data).
// i_hPrevProtocol - Handle of the previous protocol.
// i_dwPrevProtOffset - Offset of the previous protocol (starting from the beginning of the frame).
// i_dwptrInstData - Pointer to the instance data that the previous protocol provides.
//
// RETURNS: Must return NULL
//
DPLAYPARSER_API BYTE* BHAPI TransportAttachProperties( HFRAME i_hFrame, ULPBYTE i_upbMacFrame, ULPBYTE i_upbTransportFrame, DWORD i_dwMacType, DWORD i_dwBytesLeft, HPROTOCOL i_hPrevProtocol, DWORD i_dwPrevProtOffset, DWORD_PTR i_dwptrInstData ) { // TODO: Use HelpID in AttachPropertyInstance
// Check if the packet is a KeepAlive
const size_t sztTransportHeaderSize = TransportHeaderSize(i_upbTransportFrame); const DFRAME& rDFrame = *reinterpret_cast<DFRAME*>(i_upbTransportFrame);
size_t sztTransportFrameSize = i_dwptrInstData; // If an empty dataframe and not the last packet in the stream, than it's a KeepAlive
BOOL bKeepAlive = ( (sztTransportHeaderSize == sztTransportFrameSize) && ((rDFrame.bControl & PACKET_CONTROL_END_STREAM) != PACKET_CONTROL_END_STREAM) );
//===================//
// Attach Properties //
//===================//
//
// Transport summary line
AttachPropertyInstanceEx(i_hFrame, g_arr_TransportProperties[TRANSPORT_SUMMARY].hProperty, sztTransportHeaderSize, i_upbTransportFrame, sizeof(BOOL), &bKeepAlive, 0, 0, 0);
#if defined(PARSE_DPLAY_TRANSPORT)
if ( (rDFrame.bCommand & PACKET_COMMAND_DATA) == PACKET_COMMAND_DATA ) { AttachDFRAMEProperties(i_hFrame, i_upbTransportFrame);
enum { USERDATA_BITMASK = ~(PACKET_COMMAND_USER_1 | PACKET_COMMAND_USER_2) };
if ( (rDFrame.bCommand | USERDATA_BITMASK) == USERDATA_BITMASK ) { // User data (TRANSPORT_USERDATA)
AttachPropertyInstance(i_hFrame, g_arr_TransportProperties[TRANSPORT_USERDATA].hProperty, sztTransportFrameSize - sztTransportHeaderSize, i_upbTransportFrame + sztTransportHeaderSize, 0, 1, 0); } } else { AttachCFRAMEProperties(i_hFrame, i_upbTransportFrame); }
#endif // PARSE_DPLAY_TRANSPORT
return NULL;
} // TransportAttachProperties
// DESCRIPTION: Formats the data that is displayed in the details pane of the Network Monitor UI.
//
// ARGUMENTS: i_hFrame - Handle of the frame that is being parsed.
// i_pbMacFrame - Pointer to the first byte of a frame.
// i_pbCoreFrame - Pointer to the beginning of the protocol data in a frame.
// i_dwPropertyInsts - Number of PROPERTYINST structures provided by lpPropInst.
// i_pPropInst - Pointer to an array of PROPERTYINST structures.
//
// RETURNS: If the function is successful, the return value is a pointer to the first byte after the recognized data in a frame,
// or NULL if the recognized data is the last piece of data in a frame. If the function is unsuccessful, the return value
// is the initial value of i_pbTransportFrame.
//
DPLAYPARSER_API DWORD BHAPI TransportFormatProperties( HFRAME i_hFrame, ULPBYTE i_upbMacFrame, ULPBYTE i_upbTransportFrame, DWORD i_dwPropertyInsts, LPPROPERTYINST i_pPropInst ) { // Loop through the property instances...
while( i_dwPropertyInsts-- > 0) { // ...and call the formatter for each
reinterpret_cast<FORMAT>(i_pPropInst->lpPropertyInfo->InstanceData)(i_pPropInst); ++i_pPropInst; }
return NMERR_SUCCESS; }
// DESCRIPTION: Notifies Network Monitor that Transport protocol parser exists.
//
// ARGUMENTS: NONE
//
// RETURNS: TRUE - success, FALSE - failure
//
bool CreateTransportProtocol( void ) { // The entry points to the export functions that Network Monitor uses to operate the parser
ENTRYPOINTS TransportEntryPoints = { // TransportParser Entry Points
TransportRegister, TransportDeregister, TransportRecognizeFrame, TransportAttachProperties, TransportFormatProperties }; // The first active instance of this parser needs to register with the kernel
g_hTransportProtocol = CreateProtocol("DPLAYTRANSPORT", &TransportEntryPoints, ENTRYPOINTS_SIZE);
return (g_hTransportProtocol ? TRUE : FALSE);
} // CreateTransportProtocol
// DESCRIPTION: Removes the Transport protocol parser from the Network Monitor's database of parsers
//
// ARGUMENTS: NONE
//
// RETURNS: NOTHING
//
void DestroyTransportProtocol( void ) {
DestroyProtocol(g_hTransportProtocol);
} // DestroyTransportProtocol
|