eDonkey

SS 14Mar2004: eDonkey is the name of a file sharing network, actually it's also the non formal name of the protocol is uses. The eDonkey protocol is a binary protocol that works over TCP, usually port 4662. It also uses UDP in other ports for non-vital stuff. I wrote the following code as an exercise when I was learning Tcl (well, I'm still learning it, as all the simple things that can be combined in may ways it's infinite). It's not a complete implementation, but it's able to connect to another client, ask it for some info like username, server/port used, ID, and try to get the list of shared files (if the client is configured to allow it).

The usage is trivial. The program takes two arguments, one is the target host, and the second the target port (4662 should work). You can use it to make sure the file listing feature of your client is not turned on, or as a proof-of-concept of the fact that even with dynamic IP address is possible to identify people on internet using fixed information the software the user is running will report for free. Btw, that's the code:

 #!/bin/sh
 # the next line restarts using tclsh \
 exec tclsh "$0" "$@"

 # Some initial part of the client-side Emule protocol
 # implementation.
 #
 # Copyright (C) 2003/2004 Salvatore Sanfilippo
 # Under the same license as Tcl/Tk 8.4
 #
 # This code is already usable to connect to another client IP/Port
 # (port is usually 4662) and show it's username, server, ID,
 # and if available the list of shared files.
 #
 # Note that I'll never finish this code, this was written when
 # I was learning some initial Tcl and liked to play with
 # binary stuff.

 proc write {fd data} {
     puts -nonewline $fd $data
 }

 proc protostr proto {
     switch $proto {
         e3 {format eDonkey}
         c5 {format {eMule extensions}}
         d4 {format {eMule compressed}}
         default {format "Unknown ($proto)"}
     }
 }

 proc cmdstr cmd {
     switch $cmd {
         4b {format {View files answer}}
         4c {format {Hello answer}}
         4e {format {Message}}
         58 {format {File name request}}
         default {format "Unknown (0x$cmd)"}
     }
 }

 proc mtagstr mtag {
     switch $mtag {
         0 {format Undefined}
         1 {format Hash}
         2 {format String}
         3 {format DWord}
         4 {format Float}
         5 {format Bool}
         6 {format {Bool array}}
         7 {format Blob}
         default {format "Unknown ($mtag)"}
     }
 }

 proc stagstr stag {
     switch $stag {
         1 {format Name}
         2 {format {Size of file}}
         3 {format Type}
         4 {format Format}
         5 {format Collection}
         6 {format {Part Path}}
         7 {format {Part Hash}}
         8 {format Copied}
         9 {format {Gap start}}
         10 {format {Gap end}}
         11 {format Description}
         12 {format Ping}
         13 {format Fail}
         14 {format Preference}
         15 {format Port}
         16 {format Ip}
         17 {format Version}
         18 {format TempFile}
         19 {format Priority}
         20 {format Status}
         21 {format Availability}
         22 {format QTime}
         23 {format Parts}
         default {format "Unknown $stag"}
     }
 }

 # Start
 if {$argc != 1 && $argc != 2} {
     puts stderr {Usage: edinfo <host> [port]}
     exit 1
 }
 if {$argc == 1} {lappend argv 4662}
 foreach {targethost targetport} $argv break

 proc readPacket {fd protovar lenvar pktvar} {
     upvar $protovar proto $lenvar len $pktvar pkt
     # Read the header
     set hdr [read $fd 5]
     puts "Header length: [string length $hdr]"
     binary scan $hdr H2i proto len
     puts "Protocol: $proto ([protostr $proto])"
     puts "Length  : $len bytes"

     # Read the actual packet
     set pkt [read $fd $len]
 }

 proc buildPacket data {
     set len [string length $data]
     append pkt "\xe3"; # protocol (eDonkey)
     append pkt [binary format i $len]
     append pkt $data
 }

 proc sendHelo fd {
     puts "> Helo"
     # Build the Hello request
     append hello "\x01"; # command (Hello)
     append hello "\x10"; # user hash size (16 bytes)
     append hello "\x24\x0f\xf8\x30\xdd\x4b\x4e\x50"; # userhash first 8 bytes
     append hello "\x56\x91\xac\xeb\xae\x52\x4c\x9e"; # userhash last 8 bytes 
     append hello "\xaa\xbb\x00\x00"; # user id = Our IP for High ID
     append hello "\x36\x12"; # our ports
     append hello "\x02\x00\x00\x00"; # tag count (must be <= 7)
     append hello "\x03\x01\x00\x11\x3c\x00\x00\x00"; # Version tag
     append hello "\x02\x01\x00\x01\x07\x00panzutu"; # Name tag
     append hello "\x42\x6F\x2B\x50"; # server IP (0 = none)
     append hello "\x92\x10"; # server port (0 = none)

     # Send the Hello request
     set hellopkt [buildPacket $hello]
     write $fd $hellopkt

     # Read the packet
     readPacket $fd proto len reply

     # Porcess the reply
     binary scan $reply "cH32H2H2H2H2sia*" cmd userhash x1 x2 x3 x4 port tagcount reply
     set userid [expr 0x$x4$x3$x2$x1]
     set ip [expr 0x$x1].[expr 0x$x2].[expr 0x$x3].[expr 0x$x4]
     if {$userid <= 0xFFFFFF} {set ip "Low ID"}
     puts "Command : [format %02x $cmd] ([cmdstr [format %02x $cmd]])"
     puts "UserHash: $userhash"
     puts "UserId  : $userid ($ip)"
     puts "Port    : $port"
     puts "Tags    : $tagcount"

     # Read the tags
     while {[incr tagcount -1] >= 0} {
         binary scan $reply "csa*" mtag taglen reply
         if {$taglen == 1} {
             binary scan $reply "ca*" stag reply
             set tagname [stagstr $stag]
         } else {
             binary scan $reply [format "%s%s" a$taglen a*] tagname reply
         }
         puts -nonewline "  [mtagstr $mtag] $taglen $tagname: "
         switch $mtag {
             2 {
                 binary scan $reply "sa*" strlen reply
                 append fmt "a$strlen" "a*"
                 binary scan $reply $fmt str reply
                 puts "$str ($strlen bytes)"
             }
             3 {
                 binary scan $reply "H2H2H2H2a*" x1 x2 x3 x4 reply
                 set dw [expr 0x$x4$x3$x2$x1]
                 puts $dw
             }
             default {
                 puts "Unable to handle this TAG"
                 exit 1
             }
         }
     }

     # Read Server and Port
     binary scan $reply "H2H2H2H2sa*" x1 x2 x3 x4 servport reply
     set ip [expr 0x$x1].[expr 0x$x2].[expr 0x$x3].[expr 0x$x4]
     puts "Server IP: $ip"
     puts "Serv Port: $servport"
     if {[string length $reply]} {
         puts -nonewline "WARNING: Spurious data at end of reply:"
         binary scan $reply H* spurious
         puts " \[$spurious\]"
     }
 }

 proc hexdump data {
     set bytesperline 16
     set idx 0
     set l [string length $data]
     while {$l} {
         if {$l < $bytesperline} {
             set c $l
         } else {
             set c $bytesperline
         }
         set hexrepr {}
         set asciirepr {}
         for {set i 0} {$i < $c} {incr i} {
             binary scan $data "aa*" byte data
             binary scan $byte "H2" hexbyte
             append hexrepr "$hexbyte "
             if {[string is print $byte]} {
                 append asciirepr $byte
             } else {
                 append asciirepr .
             }
         }
         puts [format "%08d: %-50.50s|%-18.18s|" $idx $hexrepr $asciirepr]
         incr l -$c
         incr idx $c
     }
 }

 proc showGenericReply fd {
     readPacket $fd proto len reply
     binary scan $reply "ca*" cmd reply
     puts "Command : [format %02x $cmd] ([cmdstr [format %02x $cmd]])"
     puts "Data dump follows:"
     hexdump $reply
 }

 proc sendViewFiles fd {
     puts "> View Files"
     # Build the View Files request
     append pkt "\x4a"; # command (View Files)
     set hpkt [buildPacket $pkt]
     write $fd $hpkt

     # Read the packet
     showGenericReply $fd
 }

 proc sendMessage {fd msg} {
     puts "> Message \"$msg\""
     append pkt "\x4e" [binary format "s" [string length $msg]] $msg
     set hpkt [buildPacket $pkt]
     write $fd $hpkt

     # Read the packet
     # showGenericReply $fd
 }

 proc sendFileStatusRequest {fd hash} {
     puts "> File Status Request"
     # Build the File Status Request
     append pkt "\x4f"; # command (File Request)
     append pkt $hash; # file hash
     set hpkt [buildPacket $pkt]
     write $fd $hpkt

     # Read the packet
     readPacket $fd proto len reply
     puts "File Satatus reply length $len"
 }

 proc sendFileNameRequest {fd hash} {
     puts "> File Name Request"
     # Build the Files Request request
     append pkt "\x58"; # command (File Request)
     append pkt $hash; # file hash
     # black magic string
     append pkt "\x4b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04"

     # Send the request
     set hpkt [buildPacket $pkt]
     write $fd $hpkt

     # Read the packet
     readPacket $fd proto len reply
     puts "File Name Request reply length $len"
 }

 set fd [socket $targethost $targetport]
 fconfigure $fd -encoding binary -buffering none
 sendHelo $fd
 #sendMessage $fd Hello
 sendViewFiles $fd
 #sendFileStatusRequest $fd 0123456789012345
 close $fd

 # vim: filetype=tcl softtabstop=4 shiftwidth=4

Category Internet

Weblinks : http://www.edonkey2000-france.com/