Appearance
KAD Parity Notes (iMule vs rust-mule)
This document is a sanity-check map between iMule's Kademlia-over-I2P implementation and rust-mule's Rust-native implementation.
Goal: follow the wire format + interoperability behavior of iMule, without copying C++ architecture.
Reference Pointers (iMule)
Primary files in source_ref/iMule-2.3.1.5-src/:
- UDP obfuscation (RC4+MD5 framing):
src/EncryptedDatagramSocket.cpp - Verify key derivation:
src/kademlia/kademlia/Prefs.cpp(CPrefs::GetUDPVerifyKey) - UDP listener (opcodes / packet formats / send path):
src/kademlia/net/KademliaUDPListener.cpp - Contact serialization + Kad1/Kad2 contact layouts:
src/kademlia/routing/Contact.cpp(WriteToKad1Contact,WriteToKad2Contact,WriteToFile)src/kademlia/routing/RoutingZone.cpp(WriteFile/ReadFilefornodes.dat)
- I2P dest hash:
src/libs/i2p/CI2PAddress.h(hashCode()= first 4 bytes asuint32)
rust-mule Modules
- Wire formats + opcodes:
src/kad/wire.rs - UDP obfuscation (encrypt/decrypt + verify keys):
src/kad/udp_crypto.rs - Long-lived crawler/service loop:
src/kad/service.rs - Bootstrap probe + minimal responders (startup phase):
src/kad/bootstrap.rs - Routing table (Rust-native, not bucketed yet):
src/kad/routing.rs nodes.datread/write (iMule v2):src/nodes/imule.rs
Confirmed Wire Formats
Kad packet header
- iMule:
OP_KADEMLIAHEADER (0x05)andOP_KADEMLIAPACKEDPROT (0x06) - rust-mule:
KadPacket::{encode, decode}handles both, including zlib inflate for0x06.
- iMule:
KADEMLIA2_BOOTSTRAP_REQ (0x0D): empty payload.
KADEMLIA2_BOOTSTRAP_RES (0x0E):
- iMule (
Process2BootstrapRequest):<sender_id u128><sender_ver u8><sender_tcp_dest 387><count u16><contact>*count> - rust-mule:
decode_kad2_bootstrap_res()matches layout. - Per-contact layout is
WriteToKad2Contact:<ver u8><id u128><udp_dest 387>.
- iMule (
KADEMLIA2_HELLO_REQ/RES (0x0F/0x10):
- Base contact payload is
WriteToKad2Contactfollowed by TagList. - rust-mule:
encode_kad2_hello()+decode_kad2_hello()match this (we parse only numeric tags we care about). - ACK request is
TAG_KADMISCOPTIONS (0x58)with bit0x04set (Kad v8+).
- Base contact payload is
KADEMLIA2_HELLO_RES_ACK (0x12):
- iMule:
<myKadID u128><tagCount u8 (0)> - rust-mule: same.
- iMule:
KADEMLIA2_REQ (0x11):
- iMule (
Search.cpp::SendFindPeersForTarget):<contactCount u8><target u128><check(receiver_id) u128><sender(my_id) u128> - rust-mule:
encode_kad2_req()includes sender id;decode_kad2_req()parses it when present.
- iMule (
KADEMLIA2_RES (0x13):
- iMule:
<target u128><count u8><contact>*countwhere contact isWriteToKad2Contact. - rust-mule:
encode_kad2_res()/decode_kad2_res()match.
- iMule:
Kad1 (deprecated) REQ/RES + HELLO
- iMule uses the same request payload for Kad1+Kad2 searches:
<contactCount u8><target u128><check(receiver_id) u128><sender(my_id) u128>; opcode differs. - rust-mule:
decode_kad1_req()matches; Kad1 HELLO_RES now uses contact type3(matches iMuleWriteToKad1Contactdefault).
- iMule uses the same request payload for Kad1+Kad2 searches:
UDP Obfuscation (Crypto) Parity
- iMule "encrypted datagram" framing is implemented in
src/kad/udp_crypto.rs:- NodeID-key and receiver-key variants (iMule
DecryptReceivedClienttry 0 + try 2). - Magic:
0x395F2EC1(KAD UDP sync client). pad_len = 0(matches iMule's default; padding is allowed but unused).- Verify key derivation matches iMule
CPrefs::GetUDPVerifyKey:udp_verify_key(secret, dest_hash).
- NodeID-key and receiver-key variants (iMule
- I2P dest hash matches iMule
CI2PAddress::hashCode(): first 4 destination bytes reinterpreted as a little-endianu32.
nodes.dat v2 Parity
- iMule
RoutingZone::WriteFilewrites:u32 0(old client guard)u32 2(file version)u32 count- contacts via
CContact::WriteToFile:<kad_ver u8><id u128><udp_dest 387><udp_key u32><udp_key_ip u32><verified u8>
- rust-mule implements exactly that in
src/nodes/imule.rs.
Intentional Differences (Rust-Native)
- Routing table is currently a stable in-memory set (
RoutingTable) with selection helpers, not a full k-bucket tree.- This is a conscious Rust-native stepping stone.
- iMule packet tracking / legacy challenge logic isn't replicated 1:1.
- We use receiver-key validity + observed traffic as the primary "verified" signal.