Mod protocol
m (spellchack.) |
|||
(41 intermediate revisions by 8 users not shown) | |||
Line 1: | Line 1: | ||
− | =Under construction = | + | = Under construction = |
THIS IS NOT FINAL.. feel free to edit. | THIS IS NOT FINAL.. feel free to edit. | ||
== Introduction == | == Introduction == | ||
− | Modprot is needed for more clean protocol extensions by eMule modders.... | + | The 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 packet. If that bit is set and received by an 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 == | == 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 | + | All mod communication is to be done over the OP_MODPROT/OP_MODPACKEDPROT. |
− | ConnectionEstablished | + | When a hello(answer) with a set mod bit is received the ConnectionEstablished has to be delayed. Instead of that the mod info packet with the opcode OP_MODINFOPACKET is to be sent. |
+ | ConnectionEstablished will be triggered when the mod info packet is received. | ||
<pre> | <pre> | ||
A B | A B | ||
− | Hello ---> MB Set (see ModBit is set) | + | Hello ---> MB Set (see if ModBit is set) |
MB Set <--- Hello Answer | MB Set <--- Hello Answer | ||
ConEst <--- MInfo (Mod Info) | ConEst <--- MInfo (Mod Info) | ||
MInfo ---> ConEst (call ConnectionEstablished) | MInfo ---> ConEst (call ConnectionEstablished) | ||
</pre> | </pre> | ||
− | The mod info packet | + | The mod info packet has to contain eMuleTags that announce the supported features. The packet is made up this way: |
<pre> | <pre> | ||
<uint 32> tag count | <uint 32> tag count | ||
Line 25: | Line 32: | ||
<emule tag n> | <emule tag n> | ||
</pre> | </pre> | ||
− | + | It's only allowed to use mod features it their capability was announced by both sides. | |
− | + | After the capability exchange via OP_MODINFOPACKET all remaining opcodes are allowed to be used by clients in any way they want. | |
− | All | + | All further communication is not part of this specification. |
− | + | ||
+ | == Protocol == | ||
− | === Hello === | + | === Sending Hello(answer) packets === |
− | + | Setting the modbit in CT_EMULE_MISCOPTIONS2 | |
in baseclient.cpp, void CUpDownClient::SendHelloTypePacket(CSafeMemFile* data) | in baseclient.cpp, void CUpDownClient::SendHelloTypePacket(CSafeMemFile* data) | ||
Line 41: | Line 48: | ||
const UINT uSupportLargeFiles = 1; | const UINT uSupportLargeFiles = 1; | ||
const UINT uExtMultiPacket = 1; | const UINT uExtMultiPacket = 1; | ||
− | const UINT | + | // Set the Mod Bit |
+ | const UINT uModProt = 1; // mod bit // NEO: NMP - [NeoModProt] <-- Xanatos -- | ||
const UINT uSupportsCryptLayer = thePrefs.IsClientCryptLayerSupported() ? 1 : 0; | const UINT uSupportsCryptLayer = thePrefs.IsClientCryptLayerSupported() ? 1 : 0; | ||
const UINT uRequestsCryptLayer = thePrefs.IsClientCryptLayerRequested() ? 1 : 0; | const UINT uRequestsCryptLayer = thePrefs.IsClientCryptLayerRequested() ? 1 : 0; | ||
Line 53: | Line 61: | ||
(uRequestsCryptLayer << 8) | | (uRequestsCryptLayer << 8) | | ||
(uSupportsCryptLayer << 7) | | (uSupportsCryptLayer << 7) | | ||
− | ( | + | (uModProt << 6) | |
(uExtMultiPacket << 5) | | (uExtMultiPacket << 5) | | ||
(uSupportLargeFiles << 4) | | (uSupportLargeFiles << 4) | | ||
Line 61: | Line 69: | ||
</pre> | </pre> | ||
+ | === Processing hello(answer) packets === | ||
− | + | Read out the modbit from CT_EMULE_MISCOPTIONS2 | |
baseclient.cpp bool CUpDownClient::ProcessHelloTypePacket(CSafeMemFile* data) | baseclient.cpp bool CUpDownClient::ProcessHelloTypePacket(CSafeMemFile* data) | ||
Line 81: | Line 90: | ||
m_fRequestsCryptLayer = (temptag.GetInt() >> 8) & 0x01; | m_fRequestsCryptLayer = (temptag.GetInt() >> 8) & 0x01; | ||
m_fSupportsCryptLayer = (temptag.GetInt() >> 7) & 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_fExtMultiPacket = (temptag.GetInt() >> 5) & 0x01; | ||
m_fSupportsLargeFiles = (temptag.GetInt() >> 4) & 0x01; | m_fSupportsLargeFiles = (temptag.GetInt() >> 4) & 0x01; | ||
Line 92: | Line 102: | ||
</pre> | </pre> | ||
− | + | == Sending hello answer and modprot info == | |
− | == | + | |
− | + | ||
Listensocket.cpp bool CClientReqSocket::ProcessPacket(const BYTE* packet, uint32 size, UINT opcode) | Listensocket.cpp bool CClientReqSocket::ProcessPacket(const BYTE* packet, uint32 size, UINT opcode) | ||
Line 110: | Line 118: | ||
− | Example of | + | Example of SendModInfoPacket: |
<pre> | <pre> | ||
void CUpDownClient::SendModInfoPacket(){ | void CUpDownClient::SendModInfoPacket(){ | ||
− | if (socket == NULL){ | + | 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); | ASSERT(0); | ||
return; | return; | ||
Line 121: | Line 129: | ||
CSafeMemFile data(128); | 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 | data.WriteUInt32(tagcount); // nr. of tags | ||
− | + | CTag tagPartStatus(0x55,_T("TestMod")); | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | CTag | + | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
tagPartStatus.WriteNewEd2kTag(&data); | 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")); | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | CTag tagMyMail("Mod: | + | |
tagMyMail.WriteNewEd2kTag(&data); | 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* packet = new Packet(&data,OP_MODPROT); | ||
packet->opcode = OP_MODINFOPACKET; | packet->opcode = OP_MODINFOPACKET; | ||
Line 205: | Line 152: | ||
DebugSend("OP__ModInfoPacket", this); | DebugSend("OP__ModInfoPacket", this); | ||
theStats.AddUpDataOverheadOther(packet->size); | theStats.AddUpDataOverheadOther(packet->size); | ||
− | socket->SendPacket(packet,true,true); | + | socket->SendPacket(packet,true,true); // we send the packet |
} | } | ||
</pre> | </pre> | ||
− | === | + | === Delaying ConnectionEstablished & sending the mod info packet === |
listensocket.cpp | listensocket.cpp | ||
+ | bool CClientReqSocket::ProcessPacket(const BYTE* packet, uint32 size, UINT opcode) | ||
<pre> | <pre> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
case OP_HELLOANSWER: | case OP_HELLOANSWER: | ||
{ | { | ||
Line 238: | Line 171: | ||
// NEO: NMP - [NeoModProt] -- Xanatos --> | // NEO: NMP - [NeoModProt] -- Xanatos --> | ||
− | if(client-> | + | if(client->SupportsModProt() == 0) // check for mod prot |
// we will start the SUI when we recieve the mod info packet | // we will start the SUI when we recieve the mod info packet | ||
// NEO: NMP END <-- Xanatos -- | // 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) | if (client) | ||
{ | { | ||
// NEO: NMP - [NeoModProt] -- Xanatos --> | // NEO: NMP - [NeoModProt] -- Xanatos --> | ||
− | if (client-> | + | if (client->SupportsModProt()) // if this cleint uses mod protocol extensions |
− | client->SendModInfoPacket(); | + | client->SendModInfoPacket(); // we send him the mod info packet with our rxtensions |
− | else // we will call ConnectionEstablished when we recieve the Mod Info | + | 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 -- | // NEO: NMP END <-- Xanatos -- | ||
{ | { | ||
Line 262: | Line 197: | ||
break; | break; | ||
} | } | ||
+ | </pre> | ||
+ | |||
+ | listensocket.cpp | ||
+ | bool CClientReqSocket::ProcessPacket(const BYTE* packet, uint32 size, UINT opcode) | ||
+ | <pre> | ||
case OP_HELLO: | case OP_HELLO: | ||
{ | { | ||
− | + | [..................] | |
− | + | ||
− | [ | + | |
− | + | ||
− | + | ||
if (client) | if (client) | ||
{ | { | ||
// NEO: NMP - [NeoModProt] -- Xanatos --> | // NEO: NMP - [NeoModProt] -- Xanatos --> | ||
− | if(client-> | + | if(client->SupportsModProt()) // if this cleint uses mod protocol extensions |
− | client->SendModInfoPacket(); | + | client->SendModInfoPacket(); // we send him the mod info packet with our rxtensions |
− | else // we will call ConnectionEstablished when we recieve the Mod Info | + | 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 -- | // NEO: NMP END <-- Xanatos -- | ||
client->ConnectionEstablished(); | client->ConnectionEstablished(); | ||
Line 284: | Line 221: | ||
{ | { | ||
// NEO: NMP - [NeoModProt] -- Xanatos --> | // NEO: NMP - [NeoModProt] -- Xanatos --> | ||
+ | if(client->SupportsModProt() == 0) // check for mod prot | ||
// we will start the SUI when we recieve the mod info packet | // we will start the SUI when we recieve the mod info packet | ||
− | |||
// NEO: NMP END <-- Xanatos -- | // 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() ) | if( client->GetKadPort() ) | ||
Line 298: | Line 237: | ||
break; | break; | ||
} | } | ||
− | |||
</pre> | </pre> | ||
− | === | + | === Processing the modinfo packet === |
listensocket.cpp | listensocket.cpp | ||
Line 360: | Line 298: | ||
CSafeMemFile data(pachPacket, nSize); | CSafeMemFile data(pachPacket, nSize); | ||
+ | // we read now the amount of tags that the sender put in th epacket | ||
uint32 tagcount = data.ReadUInt32(); | uint32 tagcount = data.ReadUInt32(); | ||
if (bDbgInfo) | if (bDbgInfo) | ||
m_strModInfo.AppendFormat(_T(" Tags=%u"), (UINT)tagcount); | m_strModInfo.AppendFormat(_T(" Tags=%u"), (UINT)tagcount); | ||
+ | // now we are going to read all of them | ||
for (uint32 i = 0; i < tagcount; i++) | for (uint32 i = 0; i < tagcount; i++) | ||
{ | { | ||
CTag temptag(&data, false); | CTag temptag(&data, false); | ||
− | switch (temptag.GetNameID()) | + | switch (temptag.GetNameID()) // here we distinguish only tags with an uint8 ID, all named tags are handled below |
{ | { | ||
− | case | + | case 0x55: |
− | if (temptag. | + | if (temptag.IsStr()) |
− | + | Log(_T("ModString %s", temptag.GetStr())); | |
− | + | else if (bDbgInfo) | |
− | + | m_strModInfo.AppendFormat(_T("\n ***UnkType=%s"), temptag.GetFullInfo()); | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
break; | break; | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
default: | 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(strcmp(temptag.GetName(),"Mod:Hello") == 0) | ||
{ | { | ||
Line 436: | Line 324: | ||
} | } | ||
else | else | ||
− | |||
if (bDbgInfo) | if (bDbgInfo) | ||
Line 442: | Line 329: | ||
} | } | ||
} | } | ||
+ | |||
+ | // some debug output | ||
+ | if (bDbgInfo && data.GetPosition() < data.GetLength()){ | ||
+ | m_strModInfo.AppendFormat(_T("\n ***AddData: %u bytes"), data.GetLength() - data.GetPosition()); | ||
+ | } | ||
+ | } | ||
</pre> | </pre> | ||
+ | === Processing mod data example === | ||
− | + | EMSocket.cpp | |
− | + | void CEMSocket::OnReceive(int nErrorCode) | |
− | + | <pre> | |
− | + | 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; | ||
+ | } | ||
+ | </pre> | ||
Line 467: | Line 367: | ||
<pre> | <pre> | ||
// NEO: NMP - [NeoModProt] -- Xanatos --> | // NEO: NMP - [NeoModProt] -- Xanatos --> | ||
+ | // dispatch mod packets to the right processing functions | ||
case OP_MODPACKEDPROT: | case OP_MODPACKEDPROT: | ||
if (!packet->UnPackPacket()){ | if (!packet->UnPackPacket()){ | ||
Line 486: | Line 387: | ||
try | try | ||
{ | { | ||
+ | // we must have a cleint at this point !always! | ||
if (!client) | if (!client) | ||
{ | { | ||
Line 494: | Line 396: | ||
ASSERT_VALID(client); | ASSERT_VALID(client); | ||
− | switch(opcode) | + | 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: | case OP_MODINFOPACKET: | ||
{ | { | ||
Line 502: | Line 405: | ||
theStats.AddDownDataOverheadOther(uRawSize); | theStats.AddDownDataOverheadOther(uRawSize); | ||
− | if(client-> | + | if(client->SupportsModProt() == 0) // just pure formality |
throw CString(_T("Recieved Neo Mod Info Packet from a cleint whitch doe snot support this feature!")); | throw CString(_T("Recieved Neo Mod Info Packet from a cleint whitch doe snot support this feature!")); | ||
+ | // Process the packet | ||
client->ProcessModInfoPacket(packet,size); | client->ProcessModInfoPacket(packet,size); | ||
Line 517: | Line 421: | ||
} | } | ||
− | default: | + | default: // handle unknown packets |
+ | |||
theStats.AddDownDataOverheadOther(uRawSize); | theStats.AddDownDataOverheadOther(uRawSize); | ||
PacketToDebugLogLine(_T("ModProt"), packet, size, opcode); | PacketToDebugLogLine(_T("ModProt"), packet, size, opcode); | ||
Line 544: | Line 449: | ||
} | } | ||
return true; | return true; | ||
− | + | ||
// NEO: NMP END <-- Xanatos -- | // NEO: NMP END <-- Xanatos -- | ||
</pre> | </pre> | ||
+ | === Additional modifications === | ||
+ | |||
+ | Packets.cpp | ||
+ | void Packet::PackPacket() | ||
+ | <pre> | ||
+ | // 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; | ||
+ | </pre> | ||
+ | |||
+ | Packets.cpp | ||
+ | bool Packet::UnPackPacket(UINT uMaxDecompressedSize) | ||
+ | <pre> | ||
+ | // 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; | ||
+ | </pre> | ||
+ | |||
+ | == Backward compatibility == | ||
+ | |||
+ | The modstring is still send via hello(answer) packets but '''only''' if the remote client is using a mod and does not support the mod prot extension to maintain backward compatibility. Usage of CT_MOD_VERSION is proposed to move it to the mod protocol. A mod using the modbit MUST identify itself either via CT_MOD_VERSION in the hello(answer) packets or via modstring in the modinfo packet. | ||
+ | '''NOTE:''' a mod that uses extensions but refuses to identify itself will most likely be considered a bad mod by some antileecher systems | ||
+ | |||
+ | The proposed check to use would be like | ||
+ | <pre> | ||
+ | void CUpDownClient::SendHelloTypePacket(CSafeMemFile* data) | ||
+ | { | ||
+ | ... | ||
+ | ... | ||
+ | uint32 tagcount = 6; | ||
+ | |||
+ | if( theApp.clientlist->GetBuddy() && theApp.IsFirewalled() ) | ||
+ | tagcount += 2; | ||
+ | |||
+ | //>>> MODPROT::Send Modstring to old mods only | ||
+ | const bool bSendModString = !SupportsModProt() && (!m_strModVersion.IsEmpty() || !m_pszUsername); | ||
+ | if(bSendModString) | ||
+ | ++tagcount; | ||
+ | //<<< MODPROT::Send Modstring to old mods only | ||
+ | |||
+ | data->WriteUInt32(tagcount); | ||
+ | ... | ||
+ | ... | ||
+ | tagMuleVersion.WriteTagToFile(data); | ||
+ | |||
+ | //>>> MODPROT::Send Modstring to old mods only | ||
+ | if(bSendModString) | ||
+ | { | ||
+ | CTag tagMod(CT_MOD_VERSION, L"My Mod String"); | ||
+ | tagMod.WriteTagToFile(data); | ||
+ | } | ||
+ | //<<< MODPROT::Send Modstring to old mods only | ||
+ | |||
+ | uint32 dwIP; | ||
+ | uint16 nPort; | ||
+ | ... | ||
+ | ... | ||
+ | </pre> | ||
+ | and similar for the info packet. | ||
+ | |||
+ | |||
+ | == OPCODE defines == | ||
+ | <pre> | ||
+ | OP_MODPROT 0x4D | ||
+ | OP_MODPACKEDPROT 0x6D | ||
+ | OP_MODINFOPACKET 0x01 | ||
+ | </pre> | ||
+ | |||
+ | == TAG defines == | ||
+ | |||
+ | Tags used must be reserved in modprot topic at http://forum.emule-project.net/ | ||
+ | Sources must be published within 3 months after claiming a tag. | ||
+ | |||
+ | The range 0x01 - 0x1F is completely reserved. | ||
+ | |||
+ | Tags in the range from 0x20 - 0x7E are reserved for major features, possible objections can be: | ||
+ | #Modder uses too many tags (if you need more than 5 tags you are most likely doing something wrong. Note that string tags are also available - those are not regulated) | ||
+ | #Utterly useless or unimplementable feature (no other mod will ever use it) | ||
+ | #Illegal feature (should be clear) | ||
+ | |||
+ | The range 0x7F-FF is open and you can claim any free tag you want, only objection can be the usage of too many tags (e.g. someone claims to need a range from 0x80 to 0xAF). | ||
+ | |||
+ | {| style="font-size: 85%; text-align: Left;Border="1"; | ||
+ | ! rowspan=2 | TAGNAME | ||
+ | ! rowspan=2 | VALUE | ||
+ | ! rowspan=2 | Meaning | ||
+ | ! rowspan=2 | Reserved by / source code | ||
+ | |- | ||
+ | | | ||
+ | |- | ||
+ | |MISC_PROTOCOL_EXTENSIONS | ||
+ | | 'M' 4D | ||
+ | | used for full mod extensions like ICS, etc.. | ||
+ | | basic modprot | ||
+ | |- | ||
+ | |MISC_PROTOCOL_EXTENSIONS2 | ||
+ | | 'm' 6D | ||
+ | | used for full mod extensions 2, reserved for future use | ||
+ | | basic modprot | ||
+ | |- | ||
+ | |CT_MOD_VERSION | ||
+ | | 'U' 0x55 | ||
+ | | Modstring | ||
+ | | basic modprot | ||
+ | |- | ||
+ | |KAD_EMULE_BUDDYID | ||
+ | | '@' 40 | ||
+ | | [[NatT_protocol|NatT]] | ||
+ | | David Xanatos | ||
+ | |- | ||
+ | |XS_EMULE_BUDDYIP | ||
+ | | 'B' 42 | ||
+ | | [[NatT_protocol|NatT]] | ||
+ | | David Xanatos | ||
+ | |- | ||
+ | |XS_EMULE_BUDDYUDP | ||
+ | | 'b' 62 | ||
+ | | [[NatT_protocol|NatT]] | ||
+ | | David Xanatos | ||
+ | |- | ||
+ | |NEO_PROTOCOL_EXTENSIONS | ||
+ | |'N' 4E | ||
+ | | etc. | ||
+ | | David Xanatos | ||
+ | |- | ||
+ | |NEO_PROTOCOL_EXTENSIONS2 | ||
+ | |'n' 6E | ||
+ | | reserved for future Neo features | ||
+ | | 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] // [[Mod_Multi_Packet#ICS|ICSv2]] New Official Version | ||
+ | [bit 4] // UDP Mod File Status* | ||
+ | [bit 5] // [[NatT_protocol|NATT]] - [NatTraversal], [[NatT_protocol#XS Callback|XSB]] - [XSBuddy], [[NatT_protocol#Port Reporting|RPT]] - [ReuseTCPPort] | ||
+ | [bit 6] // [[NatT_protocol#Neo XS|NXS]] - [NeoXS] | ||
+ | [bit 7] // [[CommentsV2|SF comments v2]] | ||
+ | [bit 8] // [[Mod_Multi_Packet#SCT|SCT]] | ||
+ | [bit 9] // [[L2HAC|L2HAC]] | ||
− | |||
+ | <nowiki>*</nowiki> The UDP Mod File Status bit is not only used for ICS but used in general. It also tells whether Mod Multi Packet with extended mod status should be sent over UDP or not. | ||
+ | == See also == | ||
− | + | References to emule protocol... | |
+ | [[category:Mod_protocol_extensions]] |
Latest revision as of 12:54, 18 November 2010
Contents |
[edit] Under construction
THIS IS NOT FINAL.. feel free to edit.
[edit] Introduction
The 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 packet. If that bit is set and received by an 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.
[edit] 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 the ConnectionEstablished has to be delayed. Instead of that the mod info packet with the opcode OP_MODINFOPACKET is to be sent. ConnectionEstablished will be triggered when the mod info packet is received.
A B Hello ---> MB Set (see if ModBit is set) MB Set <--- Hello Answer ConEst <--- MInfo (Mod Info) MInfo ---> ConEst (call ConnectionEstablished)
The mod info packet has to contain eMuleTags that announce the supported features. The packet is made up this way:
<uint 32> tag count <emule tag 1> <emule tag 2> ... <emule tag n>
It's only allowed to use mod features it their capability was announced by both sides. After the capability exchange via OP_MODINFOPACKET all remaining opcodes are allowed to be used by clients in any way they want. All further communication is not part of this specification.
[edit] Protocol
[edit] Sending Hello(answer) packets
Setting the modbit 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);
[edit] Processing hello(answer) packets
Read out the modbit from CT_EMULE_MISCOPTIONS2
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;
[edit] Sending 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 SendModInfoPacket:
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 }
[edit] Delaying ConnectionEstablished & sending the 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; }
[edit] Processing the 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()); } }
[edit] Processing 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 --
[edit] 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;
[edit] Backward compatibility
The modstring is still send via hello(answer) packets but only if the remote client is using a mod and does not support the mod prot extension to maintain backward compatibility. Usage of CT_MOD_VERSION is proposed to move it to the mod protocol. A mod using the modbit MUST identify itself either via CT_MOD_VERSION in the hello(answer) packets or via modstring in the modinfo packet. NOTE: a mod that uses extensions but refuses to identify itself will most likely be considered a bad mod by some antileecher systems
The proposed check to use would be like
void CUpDownClient::SendHelloTypePacket(CSafeMemFile* data) { ... ... uint32 tagcount = 6; if( theApp.clientlist->GetBuddy() && theApp.IsFirewalled() ) tagcount += 2; //>>> MODPROT::Send Modstring to old mods only const bool bSendModString = !SupportsModProt() && (!m_strModVersion.IsEmpty() || !m_pszUsername); if(bSendModString) ++tagcount; //<<< MODPROT::Send Modstring to old mods only data->WriteUInt32(tagcount); ... ... tagMuleVersion.WriteTagToFile(data); //>>> MODPROT::Send Modstring to old mods only if(bSendModString) { CTag tagMod(CT_MOD_VERSION, L"My Mod String"); tagMod.WriteTagToFile(data); } //<<< MODPROT::Send Modstring to old mods only uint32 dwIP; uint16 nPort; ... ...
and similar for the info packet.
[edit] OPCODE defines
OP_MODPROT 0x4D OP_MODPACKEDPROT 0x6D OP_MODINFOPACKET 0x01
[edit] TAG defines
Tags used must be reserved in modprot topic at http://forum.emule-project.net/ Sources must be published within 3 months after claiming a tag.
The range 0x01 - 0x1F is completely reserved.
Tags in the range from 0x20 - 0x7E are reserved for major features, possible objections can be:
- Modder uses too many tags (if you need more than 5 tags you are most likely doing something wrong. Note that string tags are also available - those are not regulated)
- Utterly useless or unimplementable feature (no other mod will ever use it)
- Illegal feature (should be clear)
The range 0x7F-FF is open and you can claim any free tag you want, only objection can be the usage of too many tags (e.g. someone claims to need a range from 0x80 to 0xAF).
TAGNAME | VALUE | Meaning | Reserved by / source code |
---|---|---|---|
MISC_PROTOCOL_EXTENSIONS | 'M' 4D | used for full mod extensions like ICS, etc.. | basic modprot |
MISC_PROTOCOL_EXTENSIONS2 | 'm' 6D | used for full mod extensions 2, reserved for future use | basic modprot |
CT_MOD_VERSION | 'U' 0x55 | Modstring | basic modprot |
KAD_EMULE_BUDDYID | '@' 40 | NatT | David Xanatos |
XS_EMULE_BUDDYIP | 'B' 42 | NatT | David Xanatos |
XS_EMULE_BUDDYUDP | 'b' 62 | NatT | David Xanatos |
NEO_PROTOCOL_EXTENSIONS | 'N' 4E | etc. | David Xanatos |
NEO_PROTOCOL_EXTENSIONS2 | 'n' 6E | reserved for future Neo features | 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] // ICSv2 New Official Version [bit 4] // UDP Mod File Status* [bit 5] // NATT - [NatTraversal], XSB - [XSBuddy], RPT - [ReuseTCPPort] [bit 6] // NXS - [NeoXS] [bit 7] // SF comments v2 [bit 8] // SCT [bit 9] // L2HAC
* The UDP Mod File Status bit is not only used for ICS but used in general. It also tells whether Mod Multi Packet with extended mod status should be sent over UDP or not.
[edit] See also
References to emule protocol...