#!perl -w # (C) 2003-2007 Willem Jan Hengeveld # Web: http://www.xs4all.nl/~itsme/ # http://wiki.xda-developers.com/ # # $Id: $ # use strict; #------------------------------ # with socat and rsync # # # receiver: # torrc: HiddenServicePort 4242 127.0.0.1:4343 # rsyncd.conf: # |[ftp] # | path = /home/ftp # | comment = ftp export area # | read only = false # | use chroot = true # rsync --daemon --address 127.0.0.1 --port 4343 # sender # socat TCP4-LISTEN:4141,fork SOCKS4A:localhost:targethost.onion:4242,socksport=9050 # rsync file rsync://localhost:4141/ftp # #------------------------------ # with 3proxy and ftp # # sender: # in 3proxyrc: # parent 1000 socks4+ 127.0.0.1 9050 # #a) ftppr -i127.0.0.1 -p110 #b) proxy -i127.0.0.1 -p110 # this tool sends a file over tor. # # usage: # # receiver: # perl torxfer.pl -l PORT # this will receive a new file, or complete an existing incomplete file # # sender: # perl torxfer.pl -h hiddenname.onion:PORT [file list] # this will transfer the files to the specified host # # protocol: # sender: # I-HAVE(file, file-sha256, size, timestamp) # receiver: # GIVE-BLOCK(file-sha256, offset, length) # sender: # BLOCK(file-sha256, block-sha256, offset, length, data) # receiver: # FILE-COMPLETE(file-sha256) # .torxfer contains the following: # # sha256 size timestamp filename # .torxfer. contains the following: # the of each block received. # #http://search.cpan.org/~gosha/Net-SC-1.20/lib/Net/SC.pm # or #http://search.cpan.org/~clintdw/SOCKS-0.03/lib/Net/SOCKS.pm # ( the much simpler one ) # receiver function: # # open normal socket, listen on specified port # # wait_for_connection # # receive_message # switch(msg) { # case I-HAVE: # if (.torxfer contains file with matching sha256) { # if (file-is-complete) # reply FILE-COMPLETE(file-sha256) # } # else { # add entry to .torxfer # } # request GIVE-BLOCK(find_next_needed_block) # case BLOCK: # save blockdata to file # update .torxfer.sha256 file # } # sender function: # # connect to host using socks # # send "I-HAVE(file, ...)" # receive_message() # switch(msg) # { # case FILE-COMPLETE: # next file # case GIVE-BLOCK: # reply BLOCK(block) # } # } use Getopt::Long; sub usage { return <<__EOF__ receive: torxfer -l PORTNR transmit: torxfer -h hiddensvr.onion:PORTNR __EOF__ } my $listenport; my $targethostport; GetOptions( "l=s"=> \$listenport, "h=s"=> \$targethostport, ) or die usage(); die usage() if ($listenport && $targethost); die usage() if ($listenport && @ARGV); die usage() if ($targethostport && !@ARGV); die usage() if ($targethostport && !$targethostport =~ /^(\S+):(\d+)$/); my ($targethost, $targetport)= ($1, $2); if ($listenport) { receiver($listenport); } elsif ($targethostport) { sender($targethost, $targetport, @ARGV); } use IO::Socket; use threads; sub receiver { my ($port)= @_; my $listen = IO::Socket::INET->new( Listen => 5, LocalAddr => 'localhost', LocalPort => $port, Proto => 'tcp'); my $lt= async { # listen thread while ($sock = $listen->accept()) { my $st= async { # handler thread receive_file($sock); }; $st->detach(); } }; $lt->detach(); } sub sender { my ($host, $port, @files)= @_; my %info; for my $fn (@files) { if (!-e $fn) { warn "$fn: file does not exist\n"; next; } if (!-r $fn) { warn "$fn: file is not readable\n"; next; } my $filesize= -s $fn; my $filetime= (stat($filename))[9]; my $sha = Digest::SHA->new($alg); $info{$sha}= { size=>$filesize, time=>$filetime, name=>$fn, }; } my $sock= IO::Socks->new( PeerAddr => $host, PeerPort => $port, Proto => 'tcp'); send_files($sock, \%info); $sock->close(); } use Digest::SHA; sub calc_file_sha256 { my ($filename)= @_; my $sha = Digest::SHA->new($alg); $sha->addfile($filename); return $sha->digest; } sub send_file { my ($sock, $info)= @_; for my $sha ( keys %$info ) { xfer_i_have($info->{$sha}{name}, $sha, $info->{$sha}{size}, $info->{$sha}{time}); } while (1) { my $msg= receive_message(); if ($msg->is_complete) { break; } elsif ($msg->is_give_block) { give_block($info, $msg->filesha, $msg->offset, $msg->length); } else { warn "unsupported msg\n"; } } } sub give_block { my ($info, $sha, $offset, $length)= @_; if (!exists $info->{$sha}) { return xfer_err_nofile($sha); } my $fileinfo= $info->{$sha}; if ($offset > $fileinfo->{size} || $offset+$length > $fileinfo->{size}) { return xfer_err_invofs($sha); } my $block= read_file_block($fileinfo->{name}, $offset, $length); xfer_send_block($sha, sha256($block), $offset, length($block), $block); }