//=============================================================================
//  FILE: SPParser.cpp
//
//  Description: DirectPlay Service Provider Parser
//
//
//  Modification History:
//
//  Michael Milirud      08/Aug/2000    Created
//=============================================================================

//#define FRAME_NAMES
//#define FRAME_DROPS

//==================//
// Standard headers //
//==================//
#include <winsock2.h>
#include <wsipx.h>
#include <tchar.h>

//=====================//
// Proprietary headers //
//=====================//

#include "dpaddr.h"	// DPNA_DEFAULT_PORT definition

// Prototypes
#include "SPParser.hpp"

namespace DPlaySP
{

	// SP protocol header
	#include "MessageStructures.h"

} // DPlaySP namespace


namespace
{
	HPROTOCOL  g_hSPProtocol;


	//=============================//
	// Return Address Family field //-----------------------------------------------------------------------------------
	//=============================//
	LABELED_BYTE arr_RetAddrFamilyByteLabels[] = { { AF_IPX,  "IPX protocol" },
												   { AF_INET, "IP protocol"	 } };

	SET LabeledRetAddrFamilyByteSet = { sizeof(arr_RetAddrFamilyByteLabels) / sizeof(LABELED_BYTE), arr_RetAddrFamilyByteLabels };


	
	//================//
	// Data Tag field //------------------------------------------------------------------------------------------------
	//================//
	LABELED_BYTE arr_CommandByteLabels[] = { { ESCAPED_USER_DATA_KIND,	 "DPlay v8 Transport data follows" },
											 { ENUM_DATA_KIND,			 "Enumeration Query"			   },
										     { ENUM_RESPONSE_DATA_KIND,  "Response to Enumeration Query"   },
											 { PROXIED_ENUM_DATA_KIND,	 "Proxied Enumeration Query"	   } };

	SET LabeledCommandByteSet = { sizeof(arr_CommandByteLabels) / sizeof(LABELED_BYTE), arr_CommandByteLabels };


	
	////////////////////////////////
	// Custom Property Formatters //=====================================================================================
	////////////////////////////////

	
	// DESCRIPTION: Custom description formatter for the Service Provider packet summary
	//
	// ARGUMENTS: io_pProperyInstance - Data of the property's instance
	//
	// RETURNS: NOTHING
	//
	VOID WINAPIV FormatPropertyInstance_SPSummary( LPPROPERTYINST io_pProperyInstance )
	{
		using namespace DPlaySP;

		// Check what SP frame we are dealing with
		PREPEND_BUFFER&	rSPFrame = *reinterpret_cast<PREPEND_BUFFER*>(io_pProperyInstance->lpData);
		//
		switch ( rSPFrame.GenericHeader.bSPCommandByte )
		{
		case ENUM_DATA_KIND:			// Service Provider Query
			{
				strcpy(io_pProperyInstance->szPropertyText, "Enumeration Request");
				break;
			}
		case ENUM_RESPONSE_DATA_KIND:	// Service Provider Response
			{
				strcpy(io_pProperyInstance->szPropertyText, "Enumeration Response");
				break;
			}

		case PROXIED_ENUM_DATA_KIND:	// Service Provider Proxied Query
			{
				strcpy(io_pProperyInstance->szPropertyText, "Proxied Enumeration Request");
				break;
			}

		case ESCAPED_USER_DATA_KIND:	// DPlay v8 Transport protocol frame follows
		default:
			{
				strcpy(io_pProperyInstance->szPropertyText, "User Data");
				break;
			}
		}

	} // FormatPropertyInstance_SPSummary


	//==================//
	// Properties table //-----------------------------------------------------------------------------------------------
	//==================//
	
	PROPERTYINFO g_arr_SPProperties[] = 
	{

		// SP packet summary property (SP_SUMMARY)
	    {
		    0,									// handle placeholder (MBZ)
		    0,									// reserved (MBZ)
		    "",									// label
		    "DPlay Service Provider 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_SPSummary	// generic formatter
		},

		// Leading Zero property (SP_LEADZERO)
	    {
			0,									// handle placeholder (MBZ)
			0,									// reserved (MBZ)
			"Leading zero tag",					// label
			"Leading zero tag field",			// status-bar comment
			PROP_TYPE_BYTE,						// data type
			PROP_QUAL_NONE,						// data type qualifier.
			NULL,								// labeled byte set 
			64,									// description's maximum length
			FormatPropertyInstance				// generic formatter
		},

		// Data Tag property (SP_COMMAND)
	    {
			0,									// handle placeholder (MBZ)
			0,									// reserved (MBZ)
			"Command",							// label
			"Command field",					// status-bar comment
			PROP_TYPE_BYTE,						// data type
			PROP_QUAL_LABELED_SET,				// data type qualifier.
			&LabeledCommandByteSet,				// labeled byte set 
			512,								// description's maximum length
			FormatPropertyInstance				// generic formatter
		},

		// Enumeration Key property (SP_ENUMPAYLOAD)
	    {
			0,									// handle placeholder (MBZ)
			0,									// reserved (MBZ)
			"Enum Payload",						// label
			"Enumeration Payload field",		// status-bar comment
			PROP_TYPE_WORD,						// data type
			PROP_QUAL_NONE,						// data type qualifier.
			NULL,								// labeled byte set 
			64,									// description's maximum length
			FormatPropertyInstance				// generic formatter
		},

		// Enumeration Key property (SP_ENUMKEY)
	    {
			0,									// handle placeholder (MBZ)
			0,									// reserved (MBZ)
			"Enum Key",							// label
			"Enumeration Key field",			// status-bar comment
			PROP_TYPE_WORD,						// data type
			PROP_QUAL_NONE,						// data type qualifier.
			NULL,								// labeled byte set 
			64,									// description's maximum length
			FormatPropertyInstance				// generic formatter
		},

		// Enumeration Response Key property (SP_ENUMRESPKEY)
	    {
			0,									// handle placeholder (MBZ)
			0,									// reserved (MBZ)
			"Enum Response Key",				// label
			"Enumeration Response Key",			// status-bar comment
			PROP_TYPE_WORD,						// data type
			PROP_QUAL_NONE,						// data type qualifier.
			NULL,								// labeled byte set 
			64,									// description's maximum length
			FormatPropertyInstance				// generic formatter
		},

		// Enumeration Response Key property (SP_RTTINDEX)
	    {
			0,									// handle placeholder (MBZ)
			0,									// reserved (MBZ)
			"RTT Index",						// label
			"RTT Index field",					// status-bar comment
			PROP_TYPE_WORD,						// data type
			PROP_QUAL_NONE,						// data type qualifier.
			NULL,								// labeled byte set 
			64,									// description's maximum length
			FormatPropertyInstance				// generic formatter
		},
		
		// Size of the return address property (SP_RETADDRSIZE)
	    {
			0,									// handle placeholder (MBZ)
			0,									// reserved (MBZ)
			"Return Address's Size",			// label
			"Size of the return address",		// status-bar comment
			PROP_TYPE_BYTE,						// data type
			PROP_QUAL_NONE,						// data type qualifier.
			NULL,								// labeled byte set 
			64,									// description's maximum length
			FormatPropertyInstance				// generic formatter
		},

		// Return Address Socket Family property (SP_RETADDRFAMILY)
	    {
			0,									// handle placeholder (MBZ)
			0,									// reserved (MBZ)
			"Socket Family",					// label
			"Socket Family field",				// status-bar comment
			PROP_TYPE_WORD,						// data type
			PROP_QUAL_LABELED_SET,				// data type qualifier.
			&LabeledRetAddrFamilyByteSet,		// labeled byte set 
			512,								// description's maximum length
			FormatPropertyInstance				// generic formatter
		},

		// Return Address Socket Family property (SP_RETADDR_IPX)
	    {
			0,									// handle placeholder (MBZ)
			0,									// reserved (MBZ)
			"IPX Address",						// label
			"IPX Address field",				// status-bar comment
			PROP_TYPE_IPX_ADDRESS,				// data type
			PROP_QUAL_NONE,						// data type qualifier.
			NULL,								// labeled byte set 
			64,									// description's maximum length
			FormatPropertyInstance				// generic formatter
		},

		// Return Address Socket (SP_RETADDRSOCKET_IPX)
	    {
			0,									// handle placeholder (MBZ)
			0,									// reserved (MBZ)
			"Socket",							// label
			"Socket field",						// status-bar comment
			PROP_TYPE_WORD,						// data type
			PROP_QUAL_NONE,						// data type qualifier.
			NULL,								// labeled byte set 
			64,									// description's maximum length
			FormatPropertyInstance				// generic formatter
		},
	
		// Return Address Socket Family property (SP_RETADDR_IP)
	    {
			0,									// handle placeholder (MBZ)
			0,									// reserved (MBZ)
			"IP Address",						// label
			"IP Address field",					// status-bar comment
			PROP_TYPE_IP_ADDRESS,				// data type
			PROP_QUAL_NONE,						// data type qualifier.
			NULL,								// labeled byte set 
			64,									// description's maximum length
			FormatPropertyInstance				// generic formatter
		},

		// Return Address Socket (SP_RETADDRPORT_IP)
	    {
			0,									// handle placeholder (MBZ)
			0,									// reserved (MBZ)
			"Port",								// label
			"Port field",						// status-bar comment
			PROP_TYPE_WORD,						// data type
			PROP_QUAL_NONE,						// data type qualifier.
			NULL,								// labeled byte set 
			64,									// description's maximum length
			FormatPropertyInstance				// generic formatter
		},

		// User Data (SP_USERDATA)
	    {
		    0,									// handle placeholder (MBZ)
		    0,									// reserved (MBZ)
		    "User Data",						// label
		    "User Data",						// status-bar comment
		    PROP_TYPE_RAW_DATA,					// data type
		    PROP_QUAL_NONE,						// data type qualifier.
		    NULL,								// labeled bit set 
		    64,									// description's maximum length
		    FormatPropertyInstance				// generic formatter
		}

	};

	enum
	{
		nNUM_OF_SP_PROPS = sizeof(g_arr_SPProperties) / sizeof(PROPERTYINFO)
	};


	// Properties' indices
	enum
	{
		SP_SUMMARY = 0,
		SP_LEADZERO,
		SP_COMMAND,
		SP_ENUMPAYLOAD,
		SP_ENUMKEY,
		SP_ENUMRESPKEY,
		SP_RTTINDEX,
		SP_RETADDRSIZE,
		SP_RETADDRFAMILY,
		SP_RETADDR_IPX,
		SP_RETADDRSOCKET_IPX,
		SP_RETADDR_IP,
		SP_RETADDRPORT_IP,
		SP_USERDATA
	};





	// Platform independent memory accessor of big endian words
	inline WORD ReadBigEndianWord( BYTE* i_pbData )
	{
		return (*i_pbData << 8) | *(i_pbData+1);
	}


	// DESCRIPTION: DPlay packet validation predicate.
	//
	// ARGUMENTS: i_hFrame		  - The handle to the frame that contains the data.
	//			  i_hPrevProtocol - Handle of the previous protocol.
	//			  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.
	//
	// RETURNS: DPlay packet = TRUE; NOT a DPlay packet = FALSE
	//
	bool IsDPlayPacket( HFRAME i_hFrame, HPROTOCOL i_hPrevProtocol, LPBYTE i_pbMacFrame )
	{

		LPPROTOCOLINFO pPrevProtocolInfo = GetProtocolInfo(i_hPrevProtocol);
		
		DWORD dwPrevProtocolOffset = GetProtocolStartOffsetHandle(i_hFrame, i_hPrevProtocol);

		WORD wSrcPort, wDstPort;

		if ( strncmp(reinterpret_cast<char*>(pPrevProtocolInfo->ProtocolName), "UDP", sizeof(pPrevProtocolInfo->ProtocolName)) == 0 )
		{
			// Extracting the source and destination ports of the packet from its UDP header
			wSrcPort = ReadBigEndianWord(i_pbMacFrame + dwPrevProtocolOffset);
			wDstPort = ReadBigEndianWord(i_pbMacFrame + dwPrevProtocolOffset + 2);
		}
		else if ( strncmp(reinterpret_cast<char*>(pPrevProtocolInfo->ProtocolName), "IPX", sizeof(pPrevProtocolInfo->ProtocolName)) == 0 )
		{
			// Extracting the source and destination ports of the packet from its IPX header
			wSrcPort = ReadBigEndianWord(i_pbMacFrame + dwPrevProtocolOffset + 16);	// source address socket
			wDstPort = ReadBigEndianWord(i_pbMacFrame + dwPrevProtocolOffset + 28); // destination address socket
		}
		else
		{
			// Should never happen!
			return false;
		}

		
		//===========//
		// Constants //
		//===========//
		//
		static bool bTriedRetrievingUserPorts = false;
		static bool bRetrievedUserPorts = false;

		static DWORD dwMinUserPort, dwMaxUserPort;

		// Retrieval from the registry is attempted only once
		if ( !bTriedRetrievingUserPorts )
		{
			bTriedRetrievingUserPorts = true;
			
			HKEY hKey = NULL;
			if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Software\\Microsoft\\DirectPlay\\Parsers"), 0, KEY_READ, &hKey) == ERROR_SUCCESS)
			{
				DWORD dwType = NULL;
				DWORD dwCount = sizeof(DWORD);
				if (RegQueryValueEx(hKey, _T("MinUserPort"), NULL, &dwType, (LPBYTE)&dwMinUserPort, &dwCount) == ERROR_SUCCESS &&
					RegQueryValueEx(hKey, _T("MaxUserPort"), NULL, &dwType, (LPBYTE)&dwMaxUserPort, &dwCount) == ERROR_SUCCESS )
				{
					bRetrievedUserPorts = true;
				}
				RegCloseKey(hKey);
			}
		}

		if ( bRetrievedUserPorts &&
			((wSrcPort >= dwMinUserPort) && (wSrcPort <= dwMaxUserPort)) &&
		    ((wDstPort >= dwMinUserPort) && (wDstPort <= dwMaxUserPort)) )
		{
			// Is a valid DPlay packet
			return true;
		}


		// Make sure both endpoints are using the SP port range [2302, 2400], or the DPNServer port {6073}, or [MinUsePort, MaxUserPort] (if provided by the user)
		WORD wPort = wSrcPort;
		for ( int nPorts = 0; nPorts < 2; ++nPorts, wPort = wDstPort )
		{
			if (
				 (
				   !bRetrievedUserPorts    ||
				   (wPort < dwMinUserPort) ||
				   (wPort > dwMaxUserPort)
				 )
				 &&
				 (
				   (wPort < BASE_DPLAY8_PORT)  ||
				   (wPort > MAX_DPLAY8_PORT)
				 )
				 &&
				 (
				   wPort != DPNA_DPNSVR_PORT
				 )
			   )
			{
				// Not a valid DPlay packet
				return false;
			}
		}

		// Is a valid DPlay packet
		return true;

	} // IsDPlayPacket

} // 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_hSPProtocol - The handle of the protocol provided by the Network Monitor.
//
// RETURNS: NOTHING
//
DPLAYPARSER_API VOID BHAPI SPRegister( HPROTOCOL i_hSPProtocol ) 
{

	CreatePropertyDatabase(i_hSPProtocol, nNUM_OF_SP_PROPS);

	// Add the properties to the database
	for( int nProp=0; nProp < nNUM_OF_SP_PROPS; ++nProp )
	{
	   AddProperty(i_hSPProtocol, &g_arr_SPProperties[nProp]);
	}

} // SPRegister



// DESCRIPTION: Frees the resources used to create the protocol property database.
//
// ARGUMENTS: i_hSPProtocol - The handle of the protocol provided by the Network Monitor.
//
// RETURNS: NOTHING
//
DPLAYPARSER_API VOID WINAPI SPDeregister( HPROTOCOL i_hProtocol )
{

	DestroyPropertyDatabase(i_hProtocol);

} // SPDeregister




namespace
{

	// DESCRIPTION: Parses the SP frame to find its size (in bytes) NOT including the user data
	//
	// ARGUMENTS: i_pbSPFrame - 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 SP frame (in bytes)
	//
	int SPHeaderSize( LPBYTE i_pbSPFrame )
	{
		using namespace DPlaySP;

		// Check what SP frame we are dealing with
		PREPEND_BUFFER&	rSPFrame = *reinterpret_cast<PREPEND_BUFFER*>(i_pbSPFrame);
		//
		switch ( rSPFrame.GenericHeader.bSPCommandByte )
		{
		case ENUM_DATA_KIND:			// Service Provider Query
			{
				return  sizeof(rSPFrame.EnumDataHeader);
			}
		case ENUM_RESPONSE_DATA_KIND:	// Service Provider Response
			{
				return  sizeof(rSPFrame.EnumResponseDataHeader);
			}

		case PROXIED_ENUM_DATA_KIND:	// Service Provider Proxied Query
			{
				return  sizeof(rSPFrame.ProxiedEnumDataHeader);
			}

		case ESCAPED_USER_DATA_KIND:	// user data starting with zero
			{
				return  sizeof(rSPFrame.EscapedUserDataHeader);
			}

		default:	// user data starting with a nonzero byte
			{
				return 0;	// no header
			}
		}

	} // SPHeaderSize

} // 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_pbSPFrame		  - 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_pbSPFrame parameter.
//
DPLAYPARSER_API LPBYTE BHAPI SPRecognizeFrame( HFRAME        i_hFrame,
											   ULPBYTE        i_upbMacFrame,	
											   ULPBYTE        i_upbSPFrame,
											   DWORD         i_dwMacType,        
											   DWORD         i_dwBytesLeft,      
											   HPROTOCOL     i_hPrevProtocol,  
											   DWORD         i_dwPrevProtOffset,
											   LPDWORD       o_pdwProtocolStatus,
											   LPHPROTOCOL   o_phNextProtocol,
											   PDWORD_PTR    io_pdwptrInstData )
{
	using namespace DPlaySP;

	// Validate the amount of unclaimed data
	enum
	{
		nMIN_SPHeaderSize = sizeof(PREPEND_BUFFER::_GENERIC_HEADER)
	};

	// Validate the packet as DPlay SP type
	if ( (i_dwBytesLeft < nMIN_SPHeaderSize)					 ||
		 !IsDPlayPacket(i_hFrame, i_hPrevProtocol, i_upbMacFrame) )
	{
		// Assume the unclaimed data is not recognizable
		*o_pdwProtocolStatus = PROTOCOL_STATUS_NOT_RECOGNIZED;
		return i_upbSPFrame;
	}


	//==========================//
	// Get the DPlay frame size //
	//==========================//
	LPPROTOCOLINFO pPrevProtocolInfo = GetProtocolInfo(i_hPrevProtocol);
	WORD wDPlayFrameSize = 0;

	if ( strncmp(reinterpret_cast<char*>(pPrevProtocolInfo->ProtocolName), "UDP", sizeof(pPrevProtocolInfo->ProtocolName)) == 0 )
	{
		// Extracting the UDP frame size
		WORD wUDPFrameSize = ReadBigEndianWord(i_upbMacFrame + i_dwPrevProtOffset + 4);

		enum { nUDP_HEADER_SIZE = 8 };
		wDPlayFrameSize = wUDPFrameSize - nUDP_HEADER_SIZE;
	}
	else if ( strncmp(reinterpret_cast<char*>(pPrevProtocolInfo->ProtocolName), "IPX", sizeof(pPrevProtocolInfo->ProtocolName)) == 0 )
	{
		// Extracting the IPX frame size
		WORD wIPXFrameSize = ReadBigEndianWord(i_upbMacFrame + i_dwPrevProtOffset + 2);	// source address socket

		enum { nIPX_HEADER_SIZE = 30 };
		wDPlayFrameSize = wIPXFrameSize - nIPX_HEADER_SIZE;
	}
	else
	{
		; // TODO: ASSERT HERE
	}

	// Pass along the size of the Transport frame
	DWORD_PTR dwptrTransportFrameSize = wDPlayFrameSize - SPHeaderSize(i_upbSPFrame);
	*io_pdwptrInstData = dwptrTransportFrameSize;

	PREPEND_BUFFER&	rSPFrame = *reinterpret_cast<PREPEND_BUFFER*>(i_upbSPFrame);

	if ( rSPFrame.GenericHeader.bSPLeadByte  ==  SP_HEADER_LEAD_BYTE )	// SP packet
	{
	    *o_pdwProtocolStatus = PROTOCOL_STATUS_RECOGNIZED;
		*o_phNextProtocol	 = NULL;

		if ( rSPFrame.GenericHeader.bSPCommandByte  ==  ESCAPED_USER_DATA_KIND )	// user data starting with zero (non-DPlay v8 Transport packet)
		{
			*o_pdwProtocolStatus = PROTOCOL_STATUS_RECOGNIZED;
			return i_upbSPFrame + sizeof(PREPEND_BUFFER::_ESCAPED_USER_DATA_HEADER);
		}
	}
	else // user data (DPlay v8 Transport packet)
	{
		// Notify NetMon about the handoff protocol
		*o_pdwProtocolStatus = PROTOCOL_STATUS_NEXT_PROTOCOL;
		*o_phNextProtocol	 = GetProtocolFromName("DPLAYTRANSPORT");

		return i_upbSPFrame;
	}
	
	// Claim the rest of the data
	return NULL;		

} // SPRecognizeFrame



// 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_pbSPFrame		 - 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 LPBYTE BHAPI SPAttachProperties( HFRAME      i_hFrame,
												 ULPBYTE      i_upbMacFrame,
												 ULPBYTE      i_upbSPFrame,
												 DWORD       i_dwMacType,
												 DWORD       i_dwBytesLeft,
												 HPROTOCOL   i_hPrevProtocol,
												 DWORD       i_dwPrevProtOffset,
												 DWORD_PTR   i_dwptrInstData )
{
	using namespace DPlaySP;

    //===================//
    // Attach Properties //
    //===================//

    // Summary line
    AttachPropertyInstance(i_hFrame, g_arr_SPProperties[SP_SUMMARY].hProperty,
                           i_dwBytesLeft, i_upbSPFrame, 0, 0, 0);

	// Protection against NetMon
	if ( *i_upbSPFrame )
	{
		return NULL;
	}

    // Check what SP frame we are dealing with
	PREPEND_BUFFER&	rSPFrame = *reinterpret_cast<PREPEND_BUFFER*>(i_upbSPFrame);

	// Leading Zero tag field
    AttachPropertyInstance(i_hFrame, g_arr_SPProperties[SP_LEADZERO].hProperty,
                           sizeof(rSPFrame.GenericHeader.bSPLeadByte), &rSPFrame.GenericHeader.bSPLeadByte, 0, 1, 0);

	// Command field
    AttachPropertyInstance(i_hFrame, g_arr_SPProperties[SP_COMMAND].hProperty,
                           sizeof(rSPFrame.GenericHeader.bSPCommandByte), &rSPFrame.GenericHeader.bSPCommandByte, 0, 1, 0);
	    
	switch ( rSPFrame.GenericHeader.bSPCommandByte )
	{
	case ESCAPED_USER_DATA_KIND:	// user data starting with zero
		{
			break;
		}

	case ENUM_DATA_KIND:			// Service Provider's Enumeration Request
		{
			// Enum payload field
			AttachPropertyInstance(i_hFrame, g_arr_SPProperties[SP_ENUMPAYLOAD].hProperty,
								   sizeof(rSPFrame.EnumDataHeader.wEnumPayload), &rSPFrame.EnumDataHeader.wEnumPayload, 0, 1, 0);
			// Enum Key field
			DWORD dwEnumKey = rSPFrame.EnumDataHeader.wEnumPayload & ~ENUM_RTT_MASK;
			AttachPropertyInstanceEx(i_hFrame, g_arr_SPProperties[SP_ENUMKEY].hProperty,
								   sizeof(rSPFrame.EnumDataHeader.wEnumPayload), &rSPFrame.EnumDataHeader.wEnumPayload,
								   sizeof(dwEnumKey), &dwEnumKey,
								   0, 2, 0);
			
			// RTT index field
			BYTE byRTTIndex = rSPFrame.EnumDataHeader.wEnumPayload & ENUM_RTT_MASK;
			AttachPropertyInstanceEx(i_hFrame, g_arr_SPProperties[SP_RTTINDEX].hProperty,
								   sizeof(rSPFrame.EnumDataHeader.wEnumPayload), &rSPFrame.EnumDataHeader.wEnumPayload,
								   sizeof(byRTTIndex), &byRTTIndex,
								   0, 2, 0);
			break;
		}

	case ENUM_RESPONSE_DATA_KIND:	// Service Provider's Enumeration Response
		{
			// Enum payload field
			AttachPropertyInstance(i_hFrame, g_arr_SPProperties[SP_ENUMPAYLOAD].hProperty,
								   sizeof(rSPFrame.EnumResponseDataHeader.wEnumResponsePayload), &rSPFrame.EnumResponseDataHeader.wEnumResponsePayload, 0, 1, 0);
			
			// Enum Key field
			DWORD dwEnumKey = rSPFrame.EnumResponseDataHeader.wEnumResponsePayload & ~ENUM_RTT_MASK;
			AttachPropertyInstanceEx(i_hFrame, g_arr_SPProperties[SP_ENUMRESPKEY].hProperty,
								   sizeof(rSPFrame.EnumResponseDataHeader.wEnumResponsePayload), &rSPFrame.EnumResponseDataHeader.wEnumResponsePayload,
								   sizeof(dwEnumKey), &dwEnumKey,
								   0, 2, 0);
			
			// RTT index field
			BYTE byRTTIndex = rSPFrame.EnumDataHeader.wEnumPayload & ENUM_RTT_MASK;
			AttachPropertyInstanceEx(i_hFrame, g_arr_SPProperties[SP_RTTINDEX].hProperty,
								   sizeof(rSPFrame.EnumResponseDataHeader.wEnumResponsePayload), &rSPFrame.EnumResponseDataHeader.wEnumResponsePayload,
								   sizeof(byRTTIndex), &byRTTIndex,
								   0, 2, 0);
			break;
		}

	case PROXIED_ENUM_DATA_KIND:	// Service Provider's Proxied Enumeration Query
		{
			// Return Address Size field
			AttachPropertyInstance(i_hFrame, g_arr_SPProperties[SP_RETADDRSIZE].hProperty,
								   sizeof(rSPFrame.ProxiedEnumDataHeader.ReturnAddress), &rSPFrame.ProxiedEnumDataHeader.ReturnAddress, 0, 1, 0);
			
			// Enum Key field
			AttachPropertyInstance(i_hFrame, g_arr_SPProperties[SP_ENUMKEY].hProperty,
								   sizeof(rSPFrame.ProxiedEnumDataHeader.wEnumKey), &rSPFrame.ProxiedEnumDataHeader.wEnumKey, 0, 1, 0);


			// Return Address Socket Address Family field
			AttachPropertyInstance(i_hFrame, g_arr_SPProperties[SP_RETADDRFAMILY].hProperty,
								   sizeof(rSPFrame.ProxiedEnumDataHeader.ReturnAddress.sa_family), &rSPFrame.ProxiedEnumDataHeader.ReturnAddress.sa_family, 0, 1, 0);


			switch ( rSPFrame.ProxiedEnumDataHeader.ReturnAddress.sa_family )
			{				
			case AF_IPX:
				{
					SOCKADDR_IPX& rIPXAddress = *reinterpret_cast<SOCKADDR_IPX*>(&rSPFrame.ProxiedEnumDataHeader.ReturnAddress);

					// Return Address field (IPX Network Number + Node Number)
					AttachPropertyInstance(i_hFrame, g_arr_SPProperties[SP_RETADDR_IPX].hProperty,
										   sizeof(rIPXAddress.sa_netnum) + sizeof(rIPXAddress.sa_nodenum), &rIPXAddress.sa_netnum, 0, 1, 0);

					// Return Address Socket Address IPX Socket Number field
					AttachPropertyInstance(i_hFrame, g_arr_SPProperties[SP_RETADDRSOCKET_IPX].hProperty,
										   sizeof(rIPXAddress.sa_socket), &rIPXAddress.sa_socket, 0, 1, 0);

					break;
				}


			case AF_INET:
				{
					SOCKADDR_IN& rIPAddress = *reinterpret_cast<SOCKADDR_IN*>(&rSPFrame.ProxiedEnumDataHeader.ReturnAddress);

					// Return Address field (IP Address)
					AttachPropertyInstance(i_hFrame, g_arr_SPProperties[SP_RETADDR_IP].hProperty,
										   sizeof(rIPAddress.sin_addr), &rIPAddress.sin_addr, 0, 1, 0);

					// Return Address Port field
					AttachPropertyInstance(i_hFrame, g_arr_SPProperties[SP_RETADDRPORT_IP].hProperty,
										   sizeof(rIPAddress.sin_port), &rIPAddress.sin_port, 0, 1, 0);

					break;
				}

			default:
				{
					// TODO:	DPF(0, "Unknown socket type!");
					break;
				}
			}

			break;
		}
	}


	size_t sztSPHeaderSize = SPHeaderSize(i_upbSPFrame);
	
	if ( i_dwBytesLeft > sztSPHeaderSize )
	{
		size_t sztUserDataSize = i_dwBytesLeft - sztSPHeaderSize;

		// User data
		AttachPropertyInstance(i_hFrame, g_arr_SPProperties[SP_USERDATA].hProperty,
							   sztUserDataSize, i_upbSPFrame + sztSPHeaderSize, 0, 1, 0);
	}

	return NULL;

} // SPAttachProperties





// 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_pbSPFrame.
//
DPLAYPARSER_API DWORD BHAPI SPFormatProperties( HFRAME          i_hFrame,
												ULPBYTE          i_upbMacFrame,
												ULPBYTE          i_upbSPFrame,
												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;
    }

	// TODO: MAKE SURE THIS SHOULD NOT BE TRUE
    return NMERR_SUCCESS;

} // SPFormatProperties




// DESCRIPTION: Notifies Network Monitor that DPlay v8 Transport protocol parser exists.
//
// ARGUMENTS: NONE
//
// RETURNS: TRUE - success, FALSE - failure
//
bool CreateSPProtocol( void )
{

	// The entry points to the export functions that Network Monitor uses to operate the parser
	ENTRYPOINTS SPEntryPoints =
	{
		// SPParser Entry Points
		SPRegister,
		SPDeregister,
		SPRecognizeFrame,
		SPAttachProperties,
		SPFormatProperties
	};

    // The first active instance of this parser needs to register with the kernel
    g_hSPProtocol = CreateProtocol("DPLAYSP", &SPEntryPoints, ENTRYPOINTS_SIZE);
	
	return (g_hSPProtocol ? TRUE : FALSE);

} // CreateSPProtocol



// DESCRIPTION: Removes the DPlay v8 Transport protocol parser from the Network Monitor's database of parsers
//
// ARGUMENTS: NONE
//
// RETURNS: NOTHING
//
void DestroySPProtocol( void )
{

	DestroyProtocol(g_hSPProtocol);

} // DestroySPProtocol