1. Pourquoi un atlas du wire protocol
eMule ne possède pas un unique espace d’opcodes. La signification d’un message dépend au minimum du transport, de la famille de protocole et du contexte dans lequel l’opcode est reçu.
Un identifiant utile d’un message est donc plutôt :
transport + famille de protocole + direction/contexte + opcode
Un même nombre d’opcode peut avoir une signification différente dans deux familles distinctes.
2. Octets de famille de protocole
Dans les sources eMule étudiées, les valeurs principales sont :
OP_EDONKEYHEADER = 0xE3
OP_KADEMLIAHEADER = 0xE4
OP_KADEMLIAPACKEDPROT = 0xE5
OP_PACKEDPROT = 0xD4
OP_EMULEPROT = 0xC5
OP_MLDONKEYPROT = 0x00
OP_EDONKEYPROT = OP_EDONKEYHEADERInterprétation :
0xE3 espace historique eDonkey/eD2K
0xC5 extensions eMule
0xD4 paquet générique compressé
0xE4 Kad
0xE5 Kad compressé
0x00 marqueur MLDonkey dans le code étudié
3. Framing TCP
Le framing TCP eD2K/eMule étudié utilise un header de 6 octets :
+----------+----------------+--------+
| protocol | packet length | opcode |
| 1 octet | 4 octets | 1 oct. |
+----------+----------------+--------+
| payload |
+------------------------------------+
Le champ packetlength inclut l’octet opcode et le payload. Autrement dit :
packetlength = 1 + payload_size
wire_size = 5 + packetlength
= 6 + payload_size
TCP étant un flux, ce champ de longueur est nécessaire pour reconstruire les frontières entre messages.
4. Framing UDP
UDP conserve la frontière du datagramme. Le framing est donc conceptuellement plus court :
protocol + opcode + payload
La taille du datagramme fournit déjà la limite extérieure du message.
5. Un opcode n’est pas une identité globale
Exemple important : la valeur 0x01 peut désigner des messages différents selon la famille et le canal.
Un analyseur correct ne doit donc jamais faire :
opcode == 0x01 => message X
sans avoir auparavant déterminé :
E3 ? C5 ? E4 ? E5 ?
TCP ? UDP ?
client ↔ serveur ?
client ↔ client ?
6. Compression de paquet et compression de données fichier
Deux concepts distincts coexistent.
OP_PACKEDPROT (0xD4) désigne un paquet générique dont le contenu protocolaire est compressé, typiquement avec zlib dans les échanges qui le supportent.
Cela ne doit pas être confondu avec OP_COMPRESSEDPART, qui concerne les données transférées d’un fichier.
Donc :
OP_PACKEDPROT
→ compression d'un paquet protocolaire
OP_COMPRESSEDPART
→ compression du contenu d'un bloc/segment de fichier
Les serveurs peuvent annoncer une capability de compression, notamment SRVCAP_ZLIB.
7. MultiPacket
Plusieurs générations de MultiPacket apparaissent dans le code :
OP_MULTIPACKET = 0x92 // ancien / déprécié
OP_MULTIPACKET_EXT = 0xA4 // ancien / déprécié
OP_MULTIPACKET_EXT2 = 0xA9
OP_MULTIPACKETANSWER_EXT2 = 0xB0MULTIPACKET_EXT2 s’appuie sur FileIdentifier.
Un MultiPacket n’est pas simplement plusieurs trames TCP collées dans un seul send(). Il s’agit d’un payload protocolaire regroupant plusieurs sous-commandes logiques liées à un même contexte.
8. Les tags
Les tags constituent une couche de sérialisation placée dans le payload de nombreux messages.
Conceptuellement :
nom/id du tag
+ type
+ valeur
Le protocole historique a connu plusieurs formes :
noms de tags textuels
identifiants numériques courts
entiers 8/16/32/64 bits
chaînes
blobs
chaînes de taille fixe
Les tags expliquent une partie importante de l’extensibilité du protocole : un message peut transporter de nouvelles propriétés sans nécessairement redéfinir toute sa structure fixe.
9. Parsing défensif
L’atlas montre plusieurs surfaces classiques à valider avant d’utiliser les données :
longueur de paquet
nombre de tags
longueur des chaînes
longueur des blobs
type de tag
opcode valide dans la famille courante
payload minimal attendu
compression réellement décompressable
Le protocole ayant accumulé des variantes historiques, un parseur ne peut pas supposer qu’une donnée distante est cohérente uniquement parce qu’elle utilise un opcode connu.
10. Résumé du dossier 19
Le point central est que le wire protocol d’eMule est une superposition de namespaces et d’extensions. Pour comprendre un paquet, il faut lire sa famille, son transport, son opcode puis son payload. Les tags et les MultiPackets ajoutent ensuite des niveaux de sérialisation supplémentaires.