enum {
MAXIO = 16*1024,
+ SRVPROCS = 16,
+ CLIPROCS = 16,
};
int debug;
+int nproc = 1;
int killgroup = -1;
int port = 6881;
char *deftrack = "http://exodus.desync.com/announce";
char *mntweb = "/mnt/web";
-uchar infohash[20];
+char *useragent = "torrent";
+uchar infohash[SHA1dlen];
uchar peerid[20];
int blocksize;
int n, m;
File *f;
- if(len <= 0 || poff >= pieces[index].len)
+ if(len <= 0 || poff < 0 || poff >= pieces[index].len)
return 0;
if(len+poff > pieces[index].len)
len = pieces[index].len - poff;
- off = (vlong)index * blocksize;
+ off = (vlong)index * (vlong)blocksize;
off += poff;
for(f = files; f; f = f->next)
if((f->off+f->len) > off)
}
int
-havepiece(int x)
+havepiece(int x, char *from)
{
- uchar *p, m, hash[20];
+ uchar *p, m, hash[SHA1dlen];
int n;
m = 0x80>>(x&7);
}
sha1(p, n, hash, nil);
free(p);
- if(memcmp(hash, pieces[x].hash, 20))
+ if(memcmp(hash, pieces[x].hash, sizeof(hash))){
+ if(debug && from != nil)
+ fprint(2, "peer %s: damaged piece %d\n", from, x);
return 0;
+ }
lock(&stats);
if((havemap[x>>3] & m) == 0){
havemap[x>>3] |= m;
stats.left -= pieces[x].len;
}
unlock(&stats);
+ if(debug && from != nil)
+ fprint(2, "peer %s: completed piece %d\n", from, x);
return 1;
}
uchar buf[64+MAXIO], *map, *told, *p, m;
int mechoking, hechoking;
int mewant, hewant;
- int workpiece;
+ int workpiece, workoffset;
int i, o, l, x, n;
if(debug) fprint(2, "peer %s: %s connected\n", addr, incoming ? "incoming" : "outgoing");
mewant = 0;
hewant = 0;
workpiece = -1;
+ workoffset = 0;
+
map = mallocz(nhavemap, 1);
told = malloc(nhavemap);
}
if(!hechoking && mewant){
x = workpiece;
- if(x >= 0 && pieces[x].brk < pieces[x].len)
- {}
- else x = pickpiece(map);
+ if(x < 0 || (havemap[x>>3]&(0x80>>(x&7))) != 0 || workoffset >= pieces[x].len)
+ x = pickpiece(map);
if(x >= 0){
- o = pieces[x].brk;
+ o = workpiece != x ? pieces[x].brk : workoffset;
l = pieces[x].len - o;
if(l > MAXIO)
l = MAXIO;
+ workpiece = x;
+ workoffset = o + l;
if(debug) fprint(2, "peer %s: -> request %d %d %d\n", addr, x, o, l);
n = pack(buf, sizeof(buf), "lblll", 1+4+4+4, 0x06, x, o, l);
if(write(fd, buf, n) != n)
goto Out;
- workpiece = x;
}
}
if(mechoking && hewant){
if(debug) fprint(2, "peer %s: <- piece %d %d %d\n", addr, x, o, n);
if(x < 0 || x >= npieces)
continue;
- if((pieces[x].brk != o) || (havemap[x>>3]&(0x80>>(x&7))))
+ if((havemap[x>>3]&(0x80>>(x&7))) != 0)
continue;
- if(rwpiece(1, x, p, n, o) == n){
- if((pieces[x].brk = o+n) == pieces[x].len){
- if(!havepiece(x))
- pieces[x].brk = 0;
- }
+ if(o < 0 || o >= pieces[x].len)
+ continue;
+ if(o+n > pieces[x].len)
+ n = o - pieces[x].len;
+ if((o > pieces[x].brk) || (o+n <= pieces[x].brk))
+ continue;
+ n = rwpiece(1, x, p, n, o);
+ if(n <= 0)
+ continue;
+ pieces[x].brk = o+n;
+ if(o+n >= pieces[x].len && !havepiece(x, addr)){
+ pieces[x].brk = 0;
+ /* backoff from this piece for a while */
+ if(x == workpiece)
+ workpiece = -1;
}
break;
case 0x08: // Cancel <index> <begin> <length>
server(void)
{
char addr[64], adir[40], ldir[40];
- int afd, lfd, dfd;
+ int afd, lfd, dfd, pid, nprocs;
NetConnInfo *ni;
afd = -1;
+ nprocs = 0;
for(port=6881; port<6890; port++){
snprint(addr, sizeof(addr), "tcp!*!%d", port);
if((afd = announce(addr, adir)) >= 0)
fprint(2, "listen: %r");
break;
}
- if(rfork(RFFDG|RFPROC|RFMEM)){
+ while(nprocs >= SRVPROCS)
+ if(waitpid() > 0)
+ nprocs--;
+ nprocs++;
+ if(pid = rfork(RFFDG|RFPROC|RFMEM)){
+ if(pid < 0)
+ nprocs--;
close(lfd);
continue;
}
}
ni = getnetconninfo(ldir, dfd);
peer(dfd, 1, ni ? ni->raddr : "???");
- if(ni) freenetconninfo(ni);
- break;
+ if(ni) freenetconninfo(ni);
+ break;
}
exits(0);
}
void
client(char *ip, char *port)
{
- static Dict *peers;
+ static Dict *peerqh, *peerqt;
static QLock peerslk;
- int try, fd;
+ static int nprocs;
char *addr;
Dict *d;
+ int fd;
if(ip == nil || port == nil)
return;
d = mallocz(sizeof(*d) + 64, 1);
snprint(addr = d->str, 64, "tcp!%s!%s", ip, port);
qlock(&peerslk);
- if(dlook(peers, addr)){
+ if(dlook(peerqh, addr)){
qunlock(&peerslk);
free(d);
return;
d->len = strlen(addr);
d->typ = 'd';
d->val = d;
- d->next = peers;
- peers = d;
+ /* enqueue to front */
+ if((d->next = peerqh) == nil)
+ peerqt = d;
+ peerqh = d;
+ if(nprocs >= CLIPROCS){
+ qunlock(&peerslk);
+ return;
+ }
+ nprocs++;
qunlock(&peerslk);
-
- if(debug) fprint(2, "client %s\n", addr);
-
- if(rfork(RFFDG|RFPROC|RFMEM))
+ if(rfork(RFFDG|RFPROC|RFMEM|RFNOWAIT))
return;
- for(try = 0; try < 10; try++){
+
+ for(;;){
+ qlock(&peerslk);
+ /* dequeue and put to tail */
+ if(d = peerqh){
+ if((peerqh = d->next) == nil)
+ peerqt = nil;
+ d->next = nil;
+ if(peerqt)
+ peerqt->next = d;
+ else
+ peerqh = d;
+ peerqt = d;
+ } else
+ nprocs--;
+ qunlock(&peerslk);
+ if(d == nil)
+ exits(0);
+ addr = d->str;
+ if(debug) fprint(2, "client %s\n", addr);
if((fd = dial(addr, nil, nil, nil)) >= 0){
- if(!peer(fd, 0, addr))
- break;
+ peer(fd, 0, addr);
close(fd);
}
- sleep((1000<<try)+nrand(5000));
+ sleep(1000+nrand(5000));
}
- exits(0);
}
int
close(ctlfd);
return -1;
}
+ if(useragent != nil && useragent[0] != '\0'){
+ n = snprint(buf, sizeof buf, "useragent %s", useragent);
+ write(ctlfd, buf, n);
+ }
snprint(buf, sizeof buf, "%s/%d/body", mntweb, conn);
if((fd = open(buf, OREAD)) < 0)
goto ErrOut;
o += m;
n -= m;
p = 0;
- if(havepiece(x++))
+ if(havepiece(x++, w->str))
continue;
if(++err > 10){
close(fd);
}
}
}
- havepiece(off / blocksize);
- havepiece(f->off / blocksize);
+ if(off < f->off + f->len)
+ havepiece(off / blocksize, w->str);
+ havepiece(f->off / blocksize, w->str);
close(fd);
exits(0);
}
bparse(p, p+n, &d);
free(p);
} else if(debug) fprint(2, "tracker %s: %r\n", url);
+ /* check errors and warnings */
+ if(p = dstr(dlook(d, "failure reason"))) {
+ if(debug)
+ fprint(2, "tracker failure: %s\n", p);
+ exits(0);
+ }
+ if(p = dstr(dlook(d, "warning message")))
+ if(debug)
+ fprint(2, "tracker warning: %s\n", p);
if(l = dlook(d, "peers")){
if(l->typ == 's')
clients4((uchar*)l->str, l->len);
if((x = strchr(url, '/')) == nil)
x = strchr(url, 0);
}
- snprint(addr, naddr, "udp!%.*s!%d", (int)(x-url), url, port);
+ snprint(addr, naddr, "udp!%.*s!%d", utfnlen(url, x-url), url, port);
return 0;
}
int
mktorrent(int fd, Dict *alist, Dict *wlist)
{
- uchar *b, h[20];
+ uchar *b, h[SHA1dlen];
Dir *d;
int n;
werrstr("empty file");
return -1;
}
- npieces = 1;
for(blocksize = 256*1024;;blocksize<<=1){
npieces = (d->length + blocksize-1) / blocksize;
if(npieces <= 8*1024 || blocksize >= 2*1024*1024)
usage(void)
{
fprint(2, "usage: %s [ -vsdpc ] [ -m mtpt ] [ -t tracker-url ] "
- "[ -w webseed-url ] [ file ]\n", argv0);
+ "[ -w webseed-url ] [ -i peerid ] [ -A useragent ] [ file ]\n", argv0);
exits("usage");
}
case 'd':
debug++;
break;
+ case 'i':
+ strncpy((char*)peerid, EARGF(usage()), sizeof(peerid));
+ break;
+ case 'A':
+ useragent = EARGF(usage());
+ break;
default:
usage();
} ARGEND;
+ if((s = getenv("NPROC")) != 0){
+ if((nproc = atoi(s)) <= 0)
+ nproc = 1;
+ free(s);
+ }
+
fd = 0;
if(*argv)
if((fd = open(*argv, OREAD)) < 0)
if((blocksize = atoi(s)) <= 0)
sysfatal("bogus piece length in meta info");
d = dlook(info, "pieces");
- if(d == nil || d->typ != 's' || d->len <= 0 || d->len % 20)
+ if(d == nil || d->typ != 's' || d->len <= 0 || d->len % SHA1dlen)
sysfatal("bad or no pices in meta info");
- npieces = d->len / 20;
+ npieces = d->len / SHA1dlen;
pieces = mallocz(sizeof(Piece) * npieces, 1);
nhavemap = (npieces+7) / 8;
havemap = mallocz(nhavemap, 1);
for(i = 0; i<npieces; i++){
- pieces[i].hash = (uchar*)d->str + i*20;
+ pieces[i].hash = (uchar*)d->str + i*SHA1dlen;
if(len < blocksize)
pieces[i].len = len;
else
if(len)
sysfatal("pieces do not match file length");
- for(i = 0; i<npieces; i++)
- havepiece(i);
+ for(i=0; i<nproc; i++){
+ switch(rfork(RFPROC|RFMEM)){
+ case -1:
+ sysfatal("fork: %r");
+ case 0:
+ for(; i<npieces; i+=nproc)
+ havepiece(i, nil);
+ exits(0);
+ }
+ }
+ while(waitpid() >= 0)
+ ;
+
+ if(finished() && !sflag)
+ exits(0);
- srand(time(0));
+ srand(truerand());
atnotify(catch, 1);
switch(i = rfork(RFPROC|RFMEM|RFNOTEG)){
case -1:
sysfatal("fork: %r");
case 0:
- memmove(peerid, "-NF9001-", 8);
- for(i=8; i<sizeof(peerid); i++)
+ if(peerid[0] == 0)
+ strncpy((char*)peerid, "-NF9001-", 9);
+ for(i=sizeof(peerid)-1; i >= 0 && peerid[i] == 0; i--)
peerid[i] = nrand(10)+'0';
server();
for(; alist; alist = alist->next)