Mod protocol

From EMule Wiki
Revision as of 08:42, 21 July 2007 by DavidXanatos (Talk | contribs)
Jump to: navigation, search

Contents

Under construction

THIS IS NOT FINAL.. feel free to edit.

Introduction

Modprot is needed for more clean protocol extensions by eMule modders....

The main idea is to set a bit in CT_EMULE_MISCOPTIONS2 in the initial hello. If that bit is set and received by a eMule mod that is capable of processing extensions, then that mod will send the options it is capable of in the response and the first client will also send back the protocol extensions it is capable of.

Important:

Clients that are not capable of processing the mod protocol (like official eMule/amule) will not set the mod bit in CT_EMULE_MISCOPTIONS2 and will never receive extended protocol information.

Documentation

All mod communication is to be done over the OP_MODPROT/OP_MODPACKEDPROT, When a hello(answer) with a set mod bit is received ConnectionEstablished is to be delayed, and instead the mod info packet with the opcode OP_MODINFOPACKET is ti be sent, ConnectionEstablished is to be triggered when the mod info packet is received.

A               B
Hello   --->    MB Set (see ModBit is set)
MB Set  <---    Hello Answer
ConEst  <---    MInfo (Mod Info)
MInfo   --->    ConEst (call ConnectionEstablished)

The mod info packet have to contain eEmuleTags that announces supported features:

<uint 32> tag count
<emule tag 1>
<emule tag 2>
...
<emule tag n>

Only mod features are allowed to use that what was announces by both sides, after the capability exchange all reminding opcodes > 0x01 (0x01 is mod info packet) are allowed to be used by clients in any way they want. All further communication is not part of this specification.

Protocol

Hello

Set the modbit ot in in CT_EMULE_MISCOPTIONS2

in baseclient.cpp, void CUpDownClient::SendHelloTypePacket(CSafeMemFile* data)

	// eMule Misc. Options #2
	const UINT uKadVersion			= KADEMLIA_VERSION;
	const UINT uSupportLargeFiles	= 1;
	const UINT uExtMultiPacket		= 1;
	// Set the Mod Bit
	const UINT uModProt				= 1; // mod bit // NEO: NMP - [NeoModProt] <-- Xanatos --
	const UINT uSupportsCryptLayer	= thePrefs.IsClientCryptLayerSupported() ? 1 : 0;
	const UINT uRequestsCryptLayer	= thePrefs.IsClientCryptLayerRequested() ? 1 : 0;
	const UINT uRequiresCryptLayer	= thePrefs.IsClientCryptLayerRequired() ? 1 : 0;
	const UINT uSupportsSourceEx2	= 1;

	CTag tagMisOptions2(CT_EMULE_MISCOPTIONS2, 
//				(RESERVED				     )
				(uSupportsSourceEx2		<< 10) |
				(uRequiresCryptLayer	<<  9) |
				(uRequestsCryptLayer	<<  8) |
				(uSupportsCryptLayer	<<  7) |
				(uModProt				<<  6) |
				(uExtMultiPacket		<<  5) |
				(uSupportLargeFiles		<<  4) |
				(uKadVersion			<<  0) 
				);
	tagMisOptions2.WriteTagToFile(data);

Process hello.

baseclient.cpp bool CUpDownClient::ProcessHelloTypePacket(CSafeMemFile* data)

	case CT_EMULE_MISCOPTIONS2:
				//	21 Reserved
				//	 1 Supports SourceExachnge2 Packets, ignores SX1 Packet Version
				//	 1 Requires CryptLayer
				//	 1 Requests CryptLayer
				//	 1 Supports CryptLayer
				//	 1 Reserved (ModBit)
				//   1 Ext Multipacket (Hash+Size instead of Hash)
				//   1 Large Files (includes support for 64bit tags)
				//   4 Kad Version
				if (temptag.IsInt()) {
					m_fSupportsSourceEx2	= (temptag.GetInt() >>  10) & 0x01;
					m_fRequiresCryptLayer	= (temptag.GetInt() >>  9) & 0x01;
					m_fRequestsCryptLayer	= (temptag.GetInt() >>  8) & 0x01;
					m_fSupportsCryptLayer	= (temptag.GetInt() >>  7) & 0x01;
					// read the mod bit
					m_fSupportsModProt		= (temptag.GetInt() >>  6) & 0x01; // NEO: NMP - [NeoModProt] <-- Xanatos --
					m_fExtMultiPacket		= (temptag.GetInt() >>  5) & 0x01;
					m_fSupportsLargeFiles   = (temptag.GetInt() >>  4) & 0x01;
					m_byKadVersion			= (uint8)((temptag.GetInt() >>  0) & 0x0f);
					dwEmuleTags |= 8;
					if (bDbgInfo)
						m_strHelloInfo.AppendFormat(_T("\n  KadVersion=%u, LargeFiles=%u ExtMultiPacket=%u CryptLayerSupport=%u CryptLayerRequest=%u CryptLayerRequires=%u m_fSupportsSourceEx2=%u"), m_byKadVersion, m_fSupportsLargeFiles, m_fExtMultiPacket, m_fSupportsCryptLayer, m_fRequestsCryptLayer, m_fRequiresCryptLayer, m_fSupportsSourceEx2);
					m_fRequestsCryptLayer &= m_fSupportsCryptLayer;
					m_fRequiresCryptLayer &= m_fRequestsCryptLayer;

Send hello answer and modprot info

Listensocket.cpp bool CClientReqSocket::ProcessPacket(const BYTE* packet, uint32 size, UINT opcode)

if (client->GetHashType() == SO_EMULE && !bIsMuleHello)
						client->SendMuleInfoPacket(false);

					client->SendHelloAnswer();

					if (client)
					{
						// NEO: NMP - [NeoModProt] -- Xanatos -->
						if(client->GetNeoModProtVersion())
							client->SendModInfoPacket();


Example of sendmoinfopacket:

void CUpDownClient::SendModInfoPacket(){
	if (socket == NULL){ // first we check if we have a valid socket, actualy we was called form the socket so this check is rather unnesesery, but in case
		ASSERT(0);
		return;
	}

	CSafeMemFile data(128);

	// write the amount of emule tags we intent to send
	// !!! this value must be correct !!! 
	uint32 tagcount=2;

	// write the first tag (Nr.1)
	// in this sample its an normal tag with an uint8 ID, its the modstring 0x55 = 'T'
	data.WriteUInt32(tagcount); // nr. of tags
	CTag tagPartStatus(0x55,_T("TestMod"));
	tagPartStatus.WriteNewEd2kTag(&data);

	// write the secund tag (Nr.2)
	// in this sample its an custom named tag with an string as ID
	CTag tagMyMail("Mod:eMail",_T("User@Domain.com"));
	tagMyMail.WriteNewEd2kTag(&data);
	
	// we are done writing tags

	// now we create a packet and put what we have writen in it
	Packet* packet = new Packet(&data,OP_MODPROT);
	packet->opcode = OP_MODINFOPACKET;
	if (thePrefs.GetDebugClientTCPLevel() > 0)
		DebugSend("OP__ModInfoPacket", this);
	theStats.AddUpDataOverheadOther(packet->size);
	socket->SendPacket(packet,true,true); // we send the packet
}

delay ConnectionEstablished & send mod info packet

listensocket.cpp bool CClientReqSocket::ProcessPacket(const BYTE* packet, uint32 size, UINT opcode)

				case OP_HELLOANSWER:
				{
					theStats.AddDownDataOverheadOther(size);
					client->ProcessHelloAnswer(packet,size);
					if (thePrefs.GetDebugClientTCPLevel() > 0){
						DebugRecv("OP_HelloAnswer", client);
						Debug(_T("  %s\n"), client->DbgGetHelloInfo());
					}

					// NEO: NMP - [NeoModProt] -- Xanatos -->
					if(client->SupportsModProt() == 0)	// check for mod prot
					// we will start the SUI when we recieve the mod info packet
					// NEO: NMP END <-- Xanatos --
					{
						// start secure identification, if
						//  - we have received OP_EMULEINFO and OP_HELLOANSWER (old eMule)
						//	- we have received eMule-OP_HELLOANSWER (new eMule)
						if (client->GetInfoPacketsReceived() == IP_BOTH)
							client->InfoPacketsReceived();
					}

					if (client)
					{
						// NEO: NMP - [NeoModProt] -- Xanatos -->
						if (client->SupportsModProt()) // if this cleint uses mod protocol extensions
							client->SendModInfoPacket(); // we send him the mod info packet with our rxtensions
						else // we *dont* call ConnectionEstablished at this point,
							 // when will call ConnectionEstablished when we recieve the Mod Info frm this cleint
						// NEO: NMP END <-- Xanatos --
						{
							client->ConnectionEstablished();
							theApp.emuledlg->transferwnd->clientlistctrl.RefreshClient(client);
						}
					}
					break;
				}

listensocket.cpp bool CClientReqSocket::ProcessPacket(const BYTE* packet, uint32 size, UINT opcode)

				case OP_HELLO:
				{
					[..................]

					if (client)
					{
						// NEO: NMP - [NeoModProt] -- Xanatos -->
						if(client->SupportsModProt()) // if this cleint uses mod protocol extensions
							client->SendModInfoPacket(); // we send him the mod info packet with our rxtensions
						else // we *dont* call ConnectionEstablished at this point,
							 // when will call ConnectionEstablished when we recieve the Mod Info frm this cleint
						// NEO: NMP END <-- Xanatos --
							client->ConnectionEstablished();
					}

					ASSERT( client );
					if(client)
					{
						// NEO: NMP - [NeoModProt] -- Xanatos -->
						if(client->SupportsModProt() == 0) // check for mod prot
						// we will start the SUI when we recieve the mod info packet
						// NEO: NMP END <-- Xanatos --
						{

							// start secure identification, if
							//	- we have received eMule-OP_HELLO (new eMule)
							if (client->GetInfoPacketsReceived() == IP_BOTH)
								client->InfoPacketsReceived();
						}

						if( client->GetKadPort() )
							Kademlia::CKademlia::Bootstrap(ntohl(client->GetIP()), client->GetKadPort(), (client->GetKadVersion() > 1));
					}
					break;
				}

process modinfo packet

listensocket.cpp

bool CClientReqSocket::ProcessModPacket(const BYTE* packet, uint32 size, UINT opcode, UINT uRawSize)
{
	try
	{
		try
		{
			if (!client)
			{
				theStats.AddDownDataOverheadOther(uRawSize);
				throw GetResString(IDS_ERR_UNKNOWNCLIENTACTION);
			}
			if (thePrefs.m_iDbgHeap >= 2)
				ASSERT_VALID(client);

			switch(opcode)
			{
				case OP_MODINFOPACKET:
				{
					if (thePrefs.GetDebugClientTCPLevel() > 0)
						DebugRecv("OP__ModInfoPacket", client);
					theStats.AddDownDataOverheadOther(uRawSize);

					if(client->GetNeoModProtVersion() == 0)
						throw CString(_T("Recieved Neo Mod Info Packet from a cleint whitch doe snot support this feature!"));

					client->ProcessModInfoPacket(packet,size);

					// Now after we have all informations about the cleint the connection is established
					client->ConnectionEstablished();

					// start secure identification, if
					//  - we have received OP_MODINFOPACKET, (Neo Compatible new eMule mods)
					if (client->GetInfoPacketsReceived() == IP_BOTH)
						client->InfoPacketsReceived();
					break;
				}	

				// NEO: NMPm - [NeoModProtMultiPacket]
				case OP_MODMULTIPACKET: 
				{
...



baseclient.cpp

void CUpDownClient::ProcessModInfoPacket(const uchar* pachPacket, uint32 nSize)
{
	bool bDbgInfo = thePrefs.GetUseDebugDevice();
	CString m_strModInfo;
	CSafeMemFile data(pachPacket, nSize);

	// we read now the amount of tags that the sender put in th epacket
	uint32 tagcount = data.ReadUInt32();
	if (bDbgInfo)
		m_strModInfo.AppendFormat(_T("  Tags=%u"), (UINT)tagcount);
	// now we are going to read all of them
	for (uint32 i = 0; i < tagcount; i++)
	{
		CTag temptag(&data, false);
		switch (temptag.GetNameID()) // here we distinguish only tags with an uint8 ID, all named tags are handled below
		{
			case 0x55:
				if (temptag.IsStr())
					Log(_T("ModString %s", temptag.GetStr()));
				else if (bDbgInfo)
					m_strModInfo.AppendFormat(_T("\n  ***UnkType=%s"), temptag.GetFullInfo());
				break;

			default:

				// now we habdle custom named tags, we use a series of (else)if's and strcmp, note: this is *not* unicide!!!
				if(strcmp(temptag.GetName(),"Mod:Hello") == 0)
				{
					if(temptag.IsStr())
						Log(LOG_SUCCESS,_T("*** Greathings from %s: %s"),GetUserName(),temptag.GetStr());
				}
				else

				if (bDbgInfo)
					m_strModInfo.AppendFormat(_T("\n  ***UnkTag=%s"), temptag.GetFullInfo());
		}
	}

	// some debug output
	if (bDbgInfo && data.GetPosition() < data.GetLength()){
		m_strModInfo.AppendFormat(_T("\n  ***AddData: %u bytes"), data.GetLength() - data.GetPosition());
	}
}

proces mod data example

EMSocket.cpp void CEMSocket::OnReceive(int nErrorCode)

switch (pendingPacket->prot){
				case OP_EDONKEYPROT:
				case OP_PACKEDPROT:
				case OP_EMULEPROT:
				// NEO: NMP - [NeoModProt] -- Xanatos -->
				// We have to accept also mod packet
				case OP_MODPACKEDPROT:
				case OP_MODPROT:
				// NEO: NMP END <-- Xanatos --
					break;
				default:
					EMTrace("CEMSocket::OnReceive ERROR Wrong header");
					delete pendingPacket;
					pendingPacket = NULL;
					OnError(ERR_WRONGHEADER);
					return;
			}


listensocket.cpp bool CClientReqSocket::PacketReceivedCppEH(Packet* packet)

		// NEO: NMP - [NeoModProt] -- Xanatos -->
		// dispatch mod packets to the right processing functions
		case OP_MODPACKEDPROT:
			if (!packet->UnPackPacket()){
				if (thePrefs.GetVerbose())
					AddDebugLogLine(false, _T("Failed to decompress client Mod TCP packet; %s; %s"), DbgGetClientTCPPacket(packet->prot, packet->opcode, packet->size), DbgGetClientInfo());
				bResult = false;
				break;
			}
		case OP_MODPROT:
			bResult = ProcessModPacket((const BYTE*)packet->pBuffer, packet->size, packet->opcode, uRawSize);
			break;
// NEO: NMP - [NeoModProt] -- Xanatos -->
bool CClientReqSocket::ProcessModPacket(const BYTE* packet, uint32 size, UINT opcode, UINT uRawSize)
{
	try
	{
		try
		{
			// we must have a cleint at this point !always!
			if (!client)
			{
				theStats.AddDownDataOverheadOther(uRawSize);
				throw GetResString(IDS_ERR_UNKNOWNCLIENTACTION);
			}
			if (thePrefs.m_iDbgHeap >= 2)
				ASSERT_VALID(client);

			switch(opcode) // dispatch particular packets
			{
				// for the mod prot v1 (capability exchange) only this one packet is relevant, the mod info packet
				case OP_MODINFOPACKET:
				{
					if (thePrefs.GetDebugClientTCPLevel() > 0)
						DebugRecv("OP__ModInfoPacket", client);
					theStats.AddDownDataOverheadOther(uRawSize);

					if(client->SupportsModProt() == 0) // just pure formality
						throw CString(_T("Recieved Neo Mod Info Packet from a cleint whitch doe snot support this feature!"));

					// Process the packet
					client->ProcessModInfoPacket(packet,size);

					// Now after we have all informations about the cleint the connection is established
					client->ConnectionEstablished();

					// start secure identification, if
					//  - we have received OP_MODINFOPACKET, (Neo Compatible new eMule mods)
					if (client->GetInfoPacketsReceived() == IP_BOTH)
						client->InfoPacketsReceived();
					break;
				}	

				default: // handle unknown packets
					
					theStats.AddDownDataOverheadOther(uRawSize);
					PacketToDebugLogLine(_T("ModProt"), packet, size, opcode);
			}
		}
		catch(CFileException* error)
		{
			error->Delete();
			throw GetResString(IDS_ERR_INVALIDPACKAGE);
		}
		catch(CMemoryException* error)
		{
			error->Delete();
			throw CString(_T("Memory exception"));
		}
	}
	catch(CString error)
	{
		if (thePrefs.GetVerbose() && !error.IsEmpty())
			DebugLogWarning(_T("Error: %s - while processing Mod packet: opcode=%s  size=%u; %s"), error, DbgGetMuleClientTCPOpcode(opcode), size, DbgGetClientInfo());
		// Note: don't disconnect cleints on mod prot errors, the extensions are just addons and if thay fail the client will work anyway
		//if (client)
		//	client->SetDownloadState(DS_ERROR, _T("ProcessModPacket error. ") + error);
		//Disconnect(_T("ProcessModPacket error. ") + error); 
		//return false;
	}
	return true;

// NEO: NMP END <-- Xanatos --

additional modifications

Packets.cpp void Packet::PackPacket()

	// NEO: NMP - [NeoModProt] -- Xanatos -->
	if( prot == OP_MODPROT )
		prot = OP_MODPACKEDPROT;
	else
	// NEO: NMP END <-- Xanatos --
	if( prot == OP_KADEMLIAHEADER )
		prot = OP_KADEMLIAPACKEDPROT;
	else
		prot = OP_PACKEDPROT;

Packets.cpp bool Packet::UnPackPacket(UINT uMaxDecompressedSize)

		// NEO: NMP - [NeoModProt] -- Xanatos -->
		if( prot == OP_MODPACKEDPROT )
			prot = OP_MODPROT;
		else
		// NEO: NMP END <-- Xanatos --
		if( prot == OP_KADEMLIAPACKEDPROT )
			prot = OP_KADEMLIAHEADER;
		else
			prot =  OP_EMULEPROT;

backward compatibility

Modstring is still send and replied in hello packet to maintain backward compatibility. However a opcode for modstring will be defined for those who also want to move it to the mod protocol. A mod using the modbit MUST identify itself. (A mod that uses extensions but refuses to identify itslef CAN be considered a bad mod by some antileecher systems)

OPCODES

OP_MODPROT       0x4D
OP_MODPACKEDPROT 0x6D
OP_MODINFOPACKET 0x01

TAGS defines

Tags used must be reserved in modprot topic at emule-project.net. Source must be published withing 3 months for claiming a tag.

The range 0x01 - 0x1F is completely reserved.

Tags in the range from 0x20 - 0x7E are reserved for use full features, objections can be:

  1. Modder uses too many tags (If you need more than 5 you are doing something wrong probably. Note that also string tags are available that are not regulated)
  2. Utterly useless or unimplementable feature, no other mod will ever reuse it.
  3. Illegal feature

The range 0x7F-FF is open and you can take any free tag you want, only object can be using to many tags (e.g. someone claims to need a range of 0x80 till 0xAF).

TAGNAME VALUE Meaning Reserved by / source code
MISC_PROTOCOL_EXTENSIONS 'M' 4D (use full mod extensions like NatT, NeoXS, ICS, SCT, etc..)
KAD_EMULE_BUDDYID '@' 40 (for NatT by david xanatos)
XS_EMULE_BUDDYIP 'B' 42 (for NatT by david xanatos)
XS_EMULE_BUDDYUDP 'b' 62 (for NatT by david xanatos)
CT_MOD_VERSION 'U' 0x55 Modstring. Modstring MUST be sent either in hello OR Modprotocol (note that sending it in mod protocol is not backward compatible)
NEO_PROTOCOL_EXTENSIONS 'N' 4E (for future features by david xanatos)
NEO_PROTOCOL_EXTENSIONS2 'n' 6E (for future features by david xanatos)


  • MISC_PROTOCOL_EXTENSIONS:
   [bit 1]   // Unsolicited Part Status (idea from netfinity, allows our client ro recieve filestatus at any time)
   [bit 2]   // LowID UDP Ping Support (notifyes a fix form xman1 that  allow the remote low ID to use udp reasks)
   [bit 3]   // ICSv1 Regular Version
   [bit 4]   // SF comments v2
  • NEO_PROTOCOL_EXTENSIONS:
   [bit 1]   // NXS - [NeoXS]
   [bit 2]  // XSB - [XSBuddy]
   [bit 3]  // NATT - [NatTraversal]
   [bit 4]  // RTP - [ReuseTCPPort]
   [bit 5]   // Neo SCT
   [bit 6]   // UDP Mod File Status
   [bit 7]   // ICSv2 Neo Version
   [bit 8]   // Real Part Status

See also

References to emule protocol...