Mod protocol

From EMule Wiki
Revision as of 14:13, 12 July 2007 by Leuk he (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 receive by e eMule mod that is capable of processing extensions then that mod will send the options it is cabpale of in the response. and the first cleitn 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_MODINFO 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;
	const UINT uReserved			= 1; // mod bit 
	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) |
				(uReserved				<<  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;
					m_NeoModProtVersion		= (temptag.GetInt() >>  6) & 0x01; //  modbit 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){
		ASSERT(0);
		return;
	}

	CSafeMemFile data(128);

	uint32 tagcount=2+1;

#ifdef NATTUNNELING // NEO: NATT - [NatTraversal]
	if(theApp.IsFirewalled() && Kademlia::CKademlia::IsConnected())
		tagcount += 1; 
#endif //NATTUNNELING // NEO: NATT END

	data.WriteUInt32(tagcount); // nr. of tags


	/************************************************************************************
	* Note: The meaning of the bit fields in the current sample							*
	* have to be reworked for the final release version numbers are almost useless,		*
	* support (or newver version) should be anounced only by single bit flags			*
	************************************************************************************/

#ifdef NATTUNNELING // NEO: NATT - [NatTraversal]
	const UINT uNatT = NeoPrefs.IsNatTraversalEnabled();
#endif //NATTUNNELING // NEO: NATT END
	uint32 uNeoFeatures =	
				//	((0 & 0xff)		<< 24) | // Unused
				//	((0 & 0x0f)		<< 20) | // Reserved
#ifdef NATTUNNELING // NEO: NATT - [NatTraversal]
				//	((0 & 0x03)		<< 18) | // Reserved for nat-t
					((1 & 0x01)		<< 17) | // NEO: RTP - [ReuseTCPPort]
					((uNatT & 0x01) << 16) | // Support Nat Traversal
				//	((0 & 0x01)		<< 15) | // NEO: XSB - [XSBuddy]
#endif //NATTUNNELING // NEO: NATT END
					((1 & 0x07)		<< 12) | // NEO: NXS - [NeoXS]
				//	((0 & 0x03)		<< 10) | // Reserved
					((1 & 0x01)		<< 9 ) | // LowID UDP Ping Support (notifyes a fix form xman1 that allow the remote low ID to use udp reasks) // NEO: MOD - [NewUploadState]
					((1 & 0x01)		<< 8 ) | // Unsolicited Part Status (allows our client ro recieve filestatus at any time) // NEO: USPS - [UnSolicitedPartStatus]
				//	((0 & 0x0f)		<< 4 ) | // Reserved
					((0	& 0x0f)		<< 0 );  
	CTag tagNeoModProt(CT_NEO_FEATURES, uNeoFeatures);	
	tagNeoModProt.WriteTagToFile(&data); 

	uint32 nSCT		= NeoPrefs.UseSubChunkTransfer()		? 1 : 0; // version 4 bit
	uint32 ICSv2	= NeoPrefs.UseIncompletePartStatus()	? 2 : 0; // version 3 bit
	// X-ToDo:
	//uint32 RPS		= NeoPrefs.UseRealPartStatus()			? 1 : 0; // version 3 bit
	//uint32 HPS		= NeoPrefs.UseRealPartStatus()			? 1 : 0; // flag 1 bit
	//uint32 BPS		= NeoPrefs.UseRealPartStatus()			? 1 : 0; // flag 1 bit

	uint32 uFileStatus =	
					//	((0     & 0xff)	<< 24) | // unused
					//	((0     & 0xff)	<< 16) | // reserved
					//	((BPS   & 0x01)	<< 15) | // RPS Flag Blocked Parts
					//	((HPS   & 0x01)	<< 14) | // RPS Flag Hiden Parts
					//	((RPS   & 0x07)	<< 11) | // RPS VErsion
						((ICSv2 & 0x07)	<< 8 ) | // ICSv2 Version
					//	((0     & 0x0f)	<< 4 ) | // reserved
						((nSCT  & 0x0f)	<< 0 );  // SCT version
	CTag tagPartStatus(CT_EXT_FILESTATUS,uFileStatus);
	tagPartStatus.WriteNewEd2kTag(&data);

#ifdef NATTUNNELING // NEO: NATT - [NatTraversal]
	if(theApp.IsFirewalled() && Kademlia::CKademlia::IsConnected())
	{
		// Note: to connect a cleint over his buddy or to request a calback we need not only his buddy IP/Port but also his Buddy ID
		// So to get this realy to work we need to transmitt the buddy ID to
		Kademlia::CUInt128 uBuddyID(true);
		uBuddyID.Xor(Kademlia::CKademlia::GetPrefs()->GetKadID());
		byte cID[16];
		uBuddyID.ToByteArray(cID);
		CTag tagBuddyID(CT_EMULE_BUDDYID, cID);
		tagBuddyID.WriteNewEd2kTag(&data);
	}
#endif //NATTUNNELING // NEO: NATT END

	// Named tag test begin
	LPCTSTR FunnyStr[3];
	FunnyStr[0] = _T("Vote for the Pirate Party of your country");
	FunnyStr[1] = _T("Have a nice day ;)");
	FunnyStr[2] = _T("Confucius says: If you dont upload 10 GB/day good will punish you");
	CTag tagMyMail("Mod:Hello",FunnyStr[rand()%ARRSIZE(FunnyStr)]);
	tagMyMail.WriteNewEd2kTag(&data);
	// Named tag test end

	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);
}

delay ConnectionEstablished & send mod info packet

listensocket.cpp

bool CClientReqSocket::ProcessPacket(const BYTE* packet, uint32 size, UINT opcode)
{
	try
	{
		try
		{
			if (!client && opcode != OP_HELLO)
			{
				theStats.AddDownDataOverheadOther(size);
				throw GetResString(IDS_ERR_NOHELLO);
			}
			else if (client && opcode != OP_HELLO && opcode != OP_HELLOANSWER)
				client->CheckHandshakeFinished();
			switch(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->GetNeoModProtVersion() == 0)	
					// 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->GetNeoModProtVersion()) 
							client->SendModInfoPacket();
						else // we will call ConnectionEstablished when we recieve the Mod Info
						// NEO: NMP END <-- Xanatos --
						{
							client->ConnectionEstablished();
							theApp.emuledlg->transferwnd->clientlistctrl.RefreshClient(client);
						}
					}
					break;
				}
				case OP_HELLO:
				{
					theStats.AddDownDataOverheadOther(size);

					[...........................]

					client->SendHelloAnswer();

					if (client)
					{
						// NEO: NMP - [NeoModProt] -- Xanatos -->
						if(client->GetNeoModProtVersion())
							client->SendModInfoPacket();
						else // we will call ConnectionEstablished when we recieve the Mod Info
						// NEO: NMP END <-- Xanatos --
							client->ConnectionEstablished();
					}

					ASSERT( client );
					if(client)
					{
						// NEO: NMP - [NeoModProt] -- Xanatos -->
						// we will start the SUI when we recieve the mod info packet
						if(client->GetNeoModProtVersion() == 0) 
						// 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);

	uint32 tagcount = data.ReadUInt32();
	if (bDbgInfo)
		m_strModInfo.AppendFormat(_T("  Tags=%u"), (UINT)tagcount);
	for (uint32 i = 0; i < tagcount; i++)
	{
		CTag temptag(&data, false);
		switch (temptag.GetNameID())
		{
			case CT_NEO_FEATURES:
				if (temptag.IsInt()){
					//							= (uint8)(temptag.GetInt() >> 24) & 0xff;
					//							= (uint8)(temptag.GetInt() >> 20) & 0x0f;
#ifdef NATTUNNELING // NEO: NATT - [NatTraversal]
					//							= (uint8)(temptag.GetInt() >> 18) & 0x03;
					m_PublicPortReport			= (uint8)(temptag.GetInt() >> 17) & 0x01; // NEO: RTP - [ReuseTCPPort]
					m_SupportsNatTraversal 		= (uint8)(temptag.GetInt() >> 16) & 0x01;
					//			 				= (uint8)(temptag.GetInt() >> 15) & 0x01; // NEO: XSB - [XSBuddy]
#endif //NATTUNNELING // NEO: NATT END
					m_NeoXSVersion				= (uint8)(temptag.GetInt() >> 12) & 0x07; // NEO: NXS - [NeoXS]
					//							= (uint8)(temptag.GetInt() >> 10) & 0x03;
					m_LowIDUDPPingSupport		= (uint8)(temptag.GetInt() >> 9 ) & 0x01;
					m_UnsolicitedPartStatus		= (uint8)(temptag.GetInt() >> 8 ) & 0x01;
					//							= (uint8)(temptag.GetInt() >> 4 ) & 0x0f;
					//							= (uint8)(temptag.GetInt() >> 0 ) & 0x0f;
				}else if (bDbgInfo)
					m_strHelloInfo.AppendFormat(_T("\n  ***UnkType=%s"), temptag.GetFullInfo());
				break;

			case CT_EXT_FILESTATUS:
				if (temptag.IsInt()){

					// X-ToDo
					//							= (uint8)(temptag.GetInt() >> 24) & 0xff;
					//							= (uint8)(temptag.GetInt() >> 16) & 0xff;
					//uint8 BlockedPartStatus		= (uint8)(temptag.GetInt() >> 15) & 0x01;
					//uint8 HidenPartStatus		= (uint8)(temptag.GetInt() >> 14) & 0x01;
					//uint8 RealPartStatusVer		= (uint8)(temptag.GetInt() >> 11) & 0x07;
					m_IncompletePartVer			= (uint8)(temptag.GetInt() >> 8 ) & 0x07;
					//							= (uint8)(temptag.GetInt() >> 4 ) & 0x0f;
					m_SubChunksVer				= (uint8)(temptag.GetInt() >> 0 ) & 0x0f;

					//ASSERT(BlockedPartStatus || HidenPartStatus || !RealPartStatusVer);
					// Note: RPC version without one or booth RPS flags is invalid, 
					//       the cleint must always say wha is he doint gith the parts 'blocking and hiding' or 'only hiding', 
					//       or he can booth depanding on the situation
					// Booth RPS versions have always teh same version as the writing functions are identical 
					//		 just other opcodes are used and other part states notifyed
					//if(BlockedPartStatus)
					//	m_BlockedPartStatusVer = RealPartStatusVer;
					//if(HidenPartStatus)
					//	m_HidenPartStatusVer = RealPartStatusVer;
					
				}else if (bDbgInfo)
					m_strModInfo.AppendFormat(_T("\n  ***UnkType=%s"), temptag.GetFullInfo());
				break;
#ifdef NATTUNNELING // NEO: NATT - [NatTraversal]
			case CT_EMULE_BUDDYID:
				if(temptag.IsHash())
				{
					SetBuddyID(temptag.GetHash());
				}
				else{
					if (bDbgInfo)
						m_strModInfo.AppendFormat(_T("\n  ***UnkType=%s"), temptag.GetFullInfo());
				}
				break;
#endif //NATTUNNELING // NEO: NATT END
			default:

				// Named tag test begin
				if(strcmp(temptag.GetName(),"Mod:Hello") == 0)
				{
					if(temptag.IsStr())
						Log(LOG_SUCCESS,_T("*** Greathings from %s: %s"),GetUserName(),temptag.GetStr());
				}
				else
				// Named tag test end

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







proces mod data example

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

		// NEO: NMP - [NeoModProt] -- Xanatos -->
		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
		{
			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;
				}	

				default:
					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 --


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 alos want to move it to the mod protocol


OPCODES

OP_MODPROT 0x57
OP_MODPACKEDPROT 0x58
OP_MODINFOPACKET 0x01

TAGS defines

TAGNAME VALUE Meaning Reserved by / source code
MISC_PROTOCOL_EXTENSIONS 'M' 4D (etc..)
FILE_STATUS_EXTENSIONS 'S' 53 (SCT, ICS, etc..)
NEO_PROTOCOL_EXTENSIONS 'N' 4E (NatT, NeoXS, etc..)
EMULE_BUDDYID '@' 40 -
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)

See also

References to emule protocol...

Personal tools