HandoffSet parsers are more efficient than FollowSet parsers. When a HandoffSet parser returns from its recognize function, it identifies the next protocol for the kernel.
For example, the TCP parser has a Port property that tells it what protocol follows it. Port 20 indicates that the next protocol will be FTP; port 53 indicates that the next protocol will be DNS. Another example: in the SMB parser, if the value in the smb_suwcnt field of a Transact SMB is 0, then the data pointed to by smb_psoff is the data for a Remote API and should be handed to the REMAPI parser.
How a HandoffSet parser determines which protocol follows is implemented in one of two ways: one way handles the TCP/IP kind of HandoffSet, and the other handles the SMB kind of HandoffSet. If you examine the TCPIP.INI file in the parser directory, it looks something like this:
[IP_HandoffSet]
1 = ICMP
6 = TCP
17 = UDP
89 = OSPF
[TCP_HandoffSet]
20 = FTP
21 = FTP
23 = TELNET
53 = DNS
111 = RPC
137 = NBT, 1000
138 = NBT, 1002
139 = NBT, 1001
1024 = NBT, 1001
[UDP_HandoffSet]
53 = DNS
67 = DHCP
68 = DHCP
111 = RPC
137 = NBT, 1000
138 = NBT, 1002
139 = NBT, 1001
520 = RIP
2049 = RPC
[RPC_HandoffSet]
2049 = NFS
The TCP/IP parser reads this file at DLL initialization, and makes a table of numbers and handles to parsers. When TCP gets a port, it reads the table to see if the port is for a known protocol. This method allows a new parser (that the TCP parser can hand off to) to be added without shipping a new TCP parser.
For example, if you wanted to write a Finger protocol parser, you would create the parser, modify the PARSER.INI file to load your parser, and then tell TCP to hand off to you by adding the Finger port as shown here:
[TCP_HandoffSet]
20 = FTP
21 = FTP
23 = TELNET
53 = DNS
79 = FINGER
111 = RPC
137 = NBT, 1000
138 = NBT, 1002
139 = NBT, 1001
1024 = NBT, 1001
The TCP/IP parser reads its initialization data out of an .INI file stored in its directory. It scans the data, converts the text "79" to the number 79, gets the text string "FINGER," and calls a function to find Finger's protocol handle.
hFinger = GetProtocolFromName ("FINGER");
If the kernel has successfully loaded the Finger protocol, a valid non-NULL handle will be returned that can be returned at recognize time to identify the Finger protocol as being the next in line.
The Network Monitor kernel provides some parser helper functions to make this easier. For example, the LLC parser Register function creates and loads its LLC.INI file in the following piece of code by using the BuildINIPath helper API to get the path to the .INI file. It also uses the CreateHandoffTable helper API to load a handoff table.
VOID WINAPI LLCRegister(HPROTOCOL hLLCProtocol)
{
register DWORD i;
char IniFile[INI_PATH_LENGTH];
//===========================================================
// Create the property database.
//===========================================================
CreatePropertyDatabase(hLLCProtocol, nLLCProperties);
for(i = 0; i < nLLCProperties; ++i)
{
AddProperty(hLLCProtocol, &LLCDatabase[i]);
}
if (BuildINIPath(IniFile, "LLC.DLL") == NULL)
{
#ifdef DEBUG
dprintf(" LLC BuldINIPath Failed !");
#endif
}
if ((nSapHOSets = CreateHandoffTable( "SAPS",
IniFile,
&lpSapHOTable,
MAX_SAPS,
16)) == 0)
{
#ifdef DEBUG
dprintf("BuildHandoffTable failed on SAPS Section");
#endif
}
}
Now, in the Recognize function for LLC, for SAPS and ETYPES, it looks up who to hand off to by calling the GetProtocolFromTable helper API:
LPBYTE WINAPI LLCRecognizeFrame(HFRAME hFrame, //... frame handle.
LPBYTE MacFrame, //... Frame pointer.
LPLLC LLCFrame, //... Relative pointer.
DWORD MacType, //... MAC type.
DWORD BytesLeft, //... Bytes left.
HPROTOCOL hPreviousProtocol, //... Previous protocol or NULL if none.
DWORD nPreviousProtocolOffset, //... Offset of previous protocol.
LPDWORD ProtocolStatusCode, //... Pointer to return status code in.
LPHPROTOCOL hNextProtocol, //... Next protocol to call (optional).
LPDWORD InstData) //... Next protocol instance data.
{
//===============================================================
// If the mac frame equals the llc frame this isn't LLC.
//===============================================================
if ( MacFrame != (LPBYTE) LLCFrame )
{
//===========================================================
// If this is ethernet then the LLC frame had better follow the
// mac header.
//===========================================================
if ( MacType == MAC_TYPE_ETHERNET &&
&MacFrame[ETHERNET_HEADER_LENGTH] != (LPBYTE) LLCFrame )
{
*ProtocolStatusCode = PROTOCOL_STATUS_NOT_RECOGNIZED;
return (LPBYTE) LLCFrame;
}
//==========================================================
// Now check for an LLC frame in general.
//
// For this we rule out the global sap pair.
//==========================================================
if ( *((ULPWORD) LLCFrame) != 0xFFFF )
{
register LPBYTE EndOfFrame;
if ( (EndOfFrame = GetEndOfFrame(LLCFrame)) != NULL )
{
*hNextProtocol = GetProtocolFromTable( lpSapHOTable,
LLCFrame->dsap,
InstData);
*ProtocolStatusCode = PROTOCOL_STATUS_NEXT_PROTOCOL;
}
else
{
*ProtocolStatusCode = PROTOCOL_STATUS_CLAIMED;
}
return EndOfFrame;
}
}
*ProtocolStatusCode = PROTOCOL_STATUS_NOT_RECOGNIZED;
return (LPBYTE) LLCFrame;
}