Developers.Code.CG1Packet
CG1Packet
While Shareaza is running, it has a CG1Packet object to represent each Gnutella packet it is going to send.
The CG1Packet class inherits from CPacket. This gives it a payload buffer, and methods to read and write the bytes there. It also inherits methods to read and write ASCII text, and do compression.
The Gnutella packet header
There are a handful of different types of Gnutella packets, but they all start with the same header. When a Gnutella program receives a packet, it can read this header to find out what kind of packet it has, and how long the payload after the header is.
The easiest way for a program read a Gnutella packet header is to define it as a structure, and then cast the memory where the header should be to that structure. In Shareaza, this structure is called GNUTELLAPACKET:
<source lang="c"> // We can cast a pointer as a GNUTELLAPACKET structure to easily read the parts of the Gnutella packet header typedef struct {
// These are the parts of a Gnutella packet header, in the right order, with each part the right size GGUID m_pGUID; // At 0, length 16, the globally unique identifier of this packet BYTE m_nType; // At 16, the byte that identifies what kind of packet this is, like ping or pong BYTE m_nTTL; // At 17, the number of hops this packet can travel across the Internet from here BYTE m_nHops; // At 18, the number of hops this packet has traveled across the Internet to get here LONG m_nLength; // At 19, length 4, for a total size 23 bytes, the length of the packet payload
} GNUTELLAPACKET; </source>
We can see this cast happen in CG1Neighbour::ProcessPackets:
<source lang="c"> // Look at the input buffer as a Gnutella packet GNUTELLAPACKET* pPacket = (GNUTELLAPACKET*)pInput->m_pBuffer; // Hopefully a packet starts right there if ( pInput->m_nLength < sizeof(*pPacket) ) break; // Make sure the whole payload is here </source>
Information from the Gnutella packet header
CG1Packet has member variables that mirror information in the Gnutella header:
<source lang="c"> // Data in the packet GGUID m_pGUID; // The globally unique identifier of this packet BYTE m_nType; // The type of this packet, like ping or pong BYTE m_nTTL; // The number of hops this packet can travel across the Internet from here BYTE m_nHops; // The number of hops this packet has travelled across the Internet to get here </source>
When the remote computer sends us packets, their data goes from the socket to the input buffer. The New method takes a pointer to data in the input buffer where a packet begins. The method call casts the pointer to the Gnutella header structure. Code then copies values from the input buffer into the corresponding member variables:
<source lang="c"> // Takes a Gnutella packet header structure // Gets a new packet from the pool and fills it with values from the header structure // Returns a pointer to the prepared packet in the pool inline static CG1Packet* New(GNUTELLAPACKET* pSource) {
// Get a blank packet from the pool CG1Packet* pPacket = (CG1Packet*)POOL.New();
// Fill it with information from the given Gnutella packet header structure pPacket->m_pGUID = pSource->m_pGUID; pPacket->m_nType = pSource->m_nType; pPacket->m_nTTL = pSource->m_nTTL; pPacket->m_nHops = pSource->m_nHops;
</source>
Packet type as a byte and an index
Shareaza uses two different systems to keep track of what type, like ping or pong, a packet is. The first is the packet type byte. This byte is 16 bytes into the Gnutella header, and travels with the packet across the Internet. What the bytes mean is defined here:
<source lang="c"> // Gnutella packet type codes, m_nType in the header will be one of these values to show the type
- define G1_PACKET_PING 0x00 // Ping packet
- define G1_PACKET_PONG 0x01 // Pong packet, response to a ping
- define G1_PACKET_BYE 0x02 // Goodbye packet
- define G1_PACKET_QUERY_ROUTE 0x30 // Packet about query routing table
- define G1_PACKET_VENDOR 0x31 // Vendor-specific packets
- define G1_PACKET_VENDOR_APP 0x32
- define G1_PACKET_PUSH 0x40 // Packet asking that we push open a connection
- define G1_PACKET_QUERY 0x80 // Search query
- define G1_PACKET_HIT 0x81 // Response to search query, a hit
</source>
The second way Shareaza keeps packet type information is with an index:
<source lang="c"> // Packet type indices, another enumeration for Gnutella packets
- define G1_PACKTYPE_UNKNOWN 0
- define G1_PACKTYPE_PING 1
- define G1_PACKTYPE_PONG 2
- define G1_PACKTYPE_BYE 3
- define G1_PACKTYPE_QUERY_ROUTE 4
- define G1_PACKTYPE_VENDOR 5
- define G1_PACKTYPE_PUSH 6
- define G1_PACKTYPE_QUERY 7
- define G1_PACKTYPE_HIT 8
- define G1_PACKTYPE_MAX 9 // There are 9 packet type indices, with values 0 through 8
</source>
If you have a type byte and want the corresponding type index, call GnutellaTypeToIndex. The New method takes care of this, making the type easy to read in both formats:
<source lang="c"> // Also record the type as an index pPacket->m_nTypeIndex = GnutellaTypeToIndex( pPacket->m_nType ); </source>
Why does Sharaza do the same thing two different ways? Because the index is easier to work with in some situations. For instance, the index 0 means we don't know what kind of packet this is, while the byte 0 means Ping. A loop can use the ++ operator to cycle through all the types by index. The byte codes don't let us do that.