#!/usr/bin/perl # MoleSter tiny P2P file sharing program # Version 0.0.4 # This file is released to the public domain by its author, Matthew Skala. # Contact mskala@ansuz.sooke.bc.ca # Home page for this project is http://ansuz.sooke.bc.ca/software/molester/ # MoleSter should rhyme with "pollster" # Thanks to Rob Kinyon and John Bokma for bug fixes/golfing tips # A "minimal" version of this file also exists, which attempts to be the # same code with some debugging messages removed and shaved down to as few # bytes as possible; see the home page. The minimal version also defines # subroutines in a different order so as to put line breaks in convenient # places. # To set up a MoleSter peer: # perl molester password 192.168.1.1:2222 10.2.2.2:3333 # replace password with the password for your network, 192.168.1.1:2222 with # the local address and port to run on, 10.2.2.2:3333 with the remote # address and port of another MoleSter peer; if the other peer doesn't # exist, your peer will still go up, but you'll have to wait for others to # connect to you. # There is a built-in hard limit on file size. Connect to a peer and send # more than 1e6 bytes and it'll just disconnect. Files are limited to # just under a megabyte as a result. If you want to change that, you could # replace the string '1e6' with '1e7' (just under 10 megabytes), '1e8' (just # under 100 megabytes) or whatever, to taste. # Addresses must be specified as dotted quad decimal, as shown above. # You can add commands to get files or advertise your presence by adding # additional command line arguments of the form / # Example, to connect to a net, discover nearby peers, and request a file: # perl molester password 192.168.1.1:2222 10.2.2.2:3333 \ # h/ gkernel-sources.tar.bz2/ # The files will magically appear in your directory as they're downloaded. # Note that you probably should not really use this to distribute the kernel # sources, unless you have a LOT of memory and bandwidth to spare. # Peer discovery is now semi-automatic (every command includes an implicit # i/ and every g/ includes an implicit h/) so the need for explicit commands # to discover peers is much reduced. You should still do an h/ at initial # connect, though. # command reference: # i/ advertises your presence to the peer, which is a nice thing to do if # you plan to be up for a while # g/ requests a filename # h/ gets all your peers' peer lists and merges those into yours # f broadcasts the message to the peer's peers, useful for casting # a wider net if the peer doesn't have the file you want, e.g.: # fgfilename/ # f may be used multiple times, but it's friendlier to the network to just # use h/ a bunch of times so that you'll peer with more of the network # Note that if you want to run a share-only peer that can send but not # receive files, (might be useful to prevent disk-space DoS attack, or # illegal-material "hot potato" attacks), you can do it by deleting the # subroutine named "e" below, and changing [e-i] in the regular expression # inside the while loop, to [f-i]. # read entire files; $_ is undef at this point, so it's cheaper than undef$/ $/=$_; # parse command line arguments # $, = password, using $, so we can say $,eq without needing a space # $a = my address, as 127.0.0.1:31416 $,=shift; $w=$a=shift; # $w gets address too because &a looks in $w # load the first peer $k{+shift}=1; # open a listening socket # S = filehandle of listening socket # 2 = PF_INET # 1 = SOCK_STREAM # 6 = tcp # I'm pretty sure those numbers are universal, but if not, this may be # Linux-specific :-( # The die $! isn't in the minimal version, but is useful for debugging socket(S,2,1,6) || die $!; bind(S,&a) || die $!; # loop for commands; some stuff is moved into the header to save # semicolons. &i is a do-nothing function also used for command processing; # using it for the signal handler too saves a few bytes. for(listen(S,5) || die $!;$SIG{ALRM}=\&i; # now imagine these lines actually AFTER the syntactic body of the loop... # now command is in $_, parse it m!^ (\S+) ([e-i])([^/]*)/!s # check for correct password, remove peer if incorrect, set $w to peer && ($k{$w=$1}=$, eq $`) # delete next line to remove debugging output - not included in minimal && (print("$a: $` $1 $2$3/ (".join(',',keys %k).")\n")|| 1) # call handler subroutine - it takes its arguments from $1, $3, and $' && &$2 ){ # we will wait up to nine seconds for a remote input alarm 9; # and then if accept() returns a socket instead of timing out... (accept(C,S),alarm 0)? # ...then read up to 1000000 bytes from the socket read C,$_,1e6 # otherwise get from the command line :($_="$, $a f".shift) # socket C is implicitly closed next time we use handle C } # helper function, A for Address # does what sock_addr($port,inet_aton($addr)) does. may be Linux-specific, # but at least should not be Intel-specific. # takes its parameter in $w sub a { $w=~/:/; pack'CxnC4x8',2,$',split'\.',$` } # helper function, T for send a Telegram # y'see, S for Send would clash with s() for substitution - thanks # Rob Kinyon for the clue on this # takes one parameter, the message to send; sends to address in $w # this function must be defined before the functions below that use it sub t { socket C,2,1,6; $k{$w}&&=(connect C,&a)?print C"$, ".pop:$0; # must close C here because the peer is waiting and we might end up # waiting for the peer (deadlock) close C } # subroutines to actually do stuff # the parameters for these are passed in the following special vars: # $1 and $w - peer's address # $3 - filename # $' - file data # E: Expect an incoming file sub e { # using three-arg open costs a byte, but good for security open C,'>',$3; print C $' # C is implicitly closed next time we use it } # F: Forward this request to your peers sub f { $w=$_,t"$1 $3/"for keys%k } # G: Give me a file sub g { # see above about 3-arg open open(C,'<',$3) &&t"$a e$3/".; # help the peer find others; needed so that it will get incoming requests # after receiving the file, and therefore reuse the C handle and close # the file (this is cheaper than having the receiver do an explicit close) &h } # H: Help me find peers sub h { t"$_ i/"for keys%k } # I: I am a peer sub i { # do nothing - this is now implicit; we still need a do-nothing # function, though, for signal handler }