星期五, 12月 17, 2004

超迷你 P2P 程式, 最短只有 8 行代碼...

TinyP2P是一個實用的點對點檔共用應用程式,由Ed Felten博士用Python語言編寫而成。而它令人稱奇之處,是它僅僅包含了15行代碼!作者稱,他寫TinyP2P只是為了說明編寫點對點應用程式的難度罷了。P2P程式可以非常簡單,一個熟練的程式師隨便都可以寫出來,所以,任何阻止他們創作的行為都將是徒勞無功的。

  (每一行最多只有80個字母。第一行不算--它只起注釋之用,電腦在讀入的時候會忽略它)

  當然,作者編寫這一程式並非為了方便別人侵犯版權,他自己也不能寬恕侵犯他人版權的舉動。這一程式的設計初衷並沒有協助檔共用之意,它存在價值主要在於它是一個概念的實現。一個更為實用的程式速度必須更快,更安全,對連接失敗的處理機制更為完善,但那一目標的實現需要更多行代碼。

  工作原理:程式創建了一個小型的網路,可以用於一群朋友或者商業夥伴之間共用檔。它不適用於非常大的網路;相反,許多小型網路可以共存。每一個網路由一個密碼保護;只有知道網路密碼的人才可以訪問它。(但是網路對於竊聽資訊的攻擊者而言是毫無安全性可言的。)

  程式使用標準的通訊協定:HTTP和XML-RPC。HTTP就是在網頁流覽器中普遍使用的協議,XML-RPC則在提供網頁服務方面被廣泛應用。

  程式可作為服務端,也可作為用戶端。當作為服務端運行時,程式與一個網路中的其他服務端聯接,共用當前目錄中的所有檔,以供網路用戶下載。

  儘管如此,TinyP2P畢竟還使用了一個現成的XML-RPC服務端類庫。另一個檔共用程式--MoleSter更令人吃驚:它僅僅由8行Perl代碼構成,而且只應用了TCP協議。

TinyP2P 網址
http://www.freedom-to-tinker.com/tinyp2p.html

MoleSter 網址
http://ansuz.sooke.bc.ca/software/molester/index.php

MoleSter 的源代碼附註解網址:
http://ansuz.sooke.bc.ca/software/molester/molester

MoleSter 的源代碼網址:
http://ansuz.sooke.bc.ca/software/molester/molester-min

TinyP2P的源代碼:
# tinyp2p.py 1.0 (documentation at http://freedom-to-tinker.com/tinyp2p.html)
import sys, os, SimpleXMLRPCServer, xmlrpclib, re, hmac # (C) 2004, E.W. Felten
ar,pw,res = (sys.argv,lambda u:hmac.new(sys.argv[1],u).hexdigest(),re.search)
pxy,xs = (xmlrpclib.ServerProxy,SimpleXMLRPCServer.SimpleXMLRPCServer)
def ls(p=""):return filter(lambda n:(p=="")or res(p,n),os.listdir(os.getcwd()))
if ar[2]!="client": # license: http://creativecommons.org/licenses/by-nc-sa/2.0
myU,prs,srv = ("http://"+ar[3]+":"+ar[4], ar[5:],lambda x:x.serve_forever())
def pr(x=[]): return ([(y in prs) or prs.append(y) for y in x] or 1) and prs
def c(n): return ((lambda f: (f.read(), f.close()))(file(n)))[0]
f=lambda p,n,a:(p==pw(myU))and(((n==0)and pr(a))or((n==1)and [ls(a)])or c(a))
def aug(u): return ((u==myU) and pr()) or pr(pxy(u).f(pw(u),0,pr([myU])))
pr() and [aug(s) for s in aug(pr()[0])]
(lambda sv:sv.register_function(f,"f") or srv(sv))(xs((ar[3],int(ar[4]))))
for url in pxy(ar[3]).f(pw(ar[3]),0,[]):
for fn in filter(lambda n:not n in ls(), (pxy(url).f(pw(url),1,ar[4]))[0]):
(lambda fi:fi.write(pxy(url).f(pw(url),2,fn)) or fi.close())(file(fn,"wc"))

MoleSter 的源代碼:
$/=$_;$,=shift;$w=$a=shift;sub g{open(F,'<',$4)&&t($2,$a,"e$4",);close
F}shift=~/()(.*)/;&i;socket S,2,1,6;bind S,&a;sub e{open F,'>',$4;print F
$';close F}sub h{t($2,$_,id)for keys%k}sub i{$k{$2}=1}sub f{t($_,$2,$4)for
keys%k}sub a{$w=~/:/;pack'CxnC4x8',2,$',split'\.',$`}for(listen
S,5;$SIG{ALRM}=sub{};m!^(.*?) (.*?) ([e-i])([^/]*)/!s&&$,eq$1&&&$3){alarm
9;(accept(C,S),alarm 0)?read C,$_,1e6:($_="$, $a f".shift);close
C}sub t{socket X,2,1,6;$w=shift;$k{$w}=(connect X,&a)?print
X"$, $_[0] $_[1]/".pop:$/;close X}

MoleSter 的源代碼附註解:
#!/usr/bin/perl

# MoleSter tiny P2P file sharing program

# Version 0.0.3
# 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.

# 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.

# The port-number protection has been removed, so be cautious! Nobody can
# make your peer connect out to random addresses without knowing your
# password, but even so, I don't really recommend running this on the
# public Internet. Note that I considered it worthwhile to fix the
# huge-input security problem while trading off this one, because the
# huge-input attack could be mounted by an attacker who didn't have the
# password.

# New in this version is huge-input protection. 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.

# New in this version: stacking commands on the command line actually works.
# Sorry it didn't before.

# 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/ h/ h/ fgkernel-sources.tar.bz2/

# The files will magically appear in your directory as they're downloaded.
# Command-line parameters are processed in a 9-second cycle, so in the
# above example, you need to wait for 36 seconds (during which, hopefully,
# the peer discovery will have done its magic) before the file request is
# sent.

# if you want to share files too, include i/ on the command line

# 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.

# 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 - &i looks in $2 so must load that
shift=~/()(.*)/;&i;

# 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.
for(listen(S,5) die $!;$SIG{ALRM}=sub{};
# now imagine these lines actually AFTER the syntactic body of the loop...
# now command is in $_, parse it
m!^(.*?) (.*?) ([e-i])([^/]*)/!s

# check for correct password
&& $, eq $1

# delete next line to remove debugging output - not included in minimal
&& (print("$a: $1 $2 $3$4/ (".join(',',keys %k).")\n") 1)

# call handler subroutine - it takes its arguments from $2, $4, and $'
&& &$3
){
# 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);

# close socket - harmlessly fails if there was none
close C
}

# subroutines to actually do stuff
# the parameters for these are passed in the following special vars:
# $2 - peer's address
# $4 - filename
# $' - file data

# E: Expect an incoming file
sub e {
# using three-arg open costs a byte, but good for security
open F,'>',$4;
print F $';
close F
}

# F: Forward this request to your peers
sub f {
# note we don't pass "data" ($') so can't do recursive e/ anymore
t($_,$2,$4)for keys%k
}

# G: Give me a file
sub g {
# see above about 3-arg open
open(F,'<',$4) &&t($2,$a,"e$4",);
close F
}

# H: Help me find peers
sub h {
# bare word id is cheaper than 'i' and the d is ignored by recipients
t($2,$_,id)for keys%k
}

# I: I am a peer
sub i {
$k{$2}=1
}

# 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
# usage destaddr, sourceaddr, req.filename, data
sub t {
socket X,2,1,6;
$w=shift;
# ?: is cheaper than if..else, and $k only needs to be defined or not
# note that $/=undef but is cheaper
$k{$w}=(connect X,&a)?print X"$, $_[0] $_[1]/".pop:$/;
close X
}

0 Comments:

發佈留言

<< Home