6 typedef struct Dict Dict;
7 typedef struct Piece Piece;
8 typedef struct File File;
12 char typ; // i, d, s, l
40 int debug, sflag, pflag, vflag;
43 char *mntweb = "/mnt/web";
68 bparse(char *s, char *e, Dict **dp)
94 d = *dp = mallocz(sizeof(*d), 1);
98 s = bparse(s, e, &d->val);
109 if((s = memchr(x, 'e', e - x)) == nil)
115 if((x = memchr(s, ':', e - s)) == nil)
118 if((n = atoi(s)) < 0)
121 if((s > e) || (s < x)){
127 d = mallocz(sizeof(*d) + n+1, 1);
129 memmove(d->str, x, d->len = n);
138 if(d && (d->typ == 's' || d->typ == 'i'))
144 dlook(Dict *d, char *s)
146 for(; d && d->typ == 'd'; d = d->next)
147 if(d->len && strcmp(d->str, s) == 0)
153 readall(int fd, char **p)
159 while(*p = realloc(*p, n+1024)){
160 if((r = read(fd, *p+n, 1024)) <= 0)
168 rwpiece(int wr, int index, uchar *data, int len, int poff)
174 if(len <= 0 || poff >= pieces[index].len)
176 if(len+poff > pieces[index].len)
177 len = pieces[index].len - poff;
178 off = (vlong)index * blocksize;
180 for(f = files; f; f = f->next)
181 if((f->off+f->len) > off)
184 n = ((off + len) > f->len) ? f->len - off : len;
185 if((n = (wr ? pwrite(f->fd, data, n, off) : pread(f->fd, data, n, off))) <= 0)
187 if((m = rwpiece(wr, index, data + n, len - n, poff + n)) < 0)
195 uchar *p, m, hash[20];
199 if(havemap[x>>3] & m)
201 p = malloc(blocksize);
203 if(rwpiece(0, x, p, n, 0) != n){
207 sha1(p, n, hash, nil);
209 if(memcmp(hash, pieces[x].hash, 20))
216 pickpiece(uchar *map)
223 for(i = 0; i<nhavemap; i++){
226 for(x = i<<3, m = 0x80; m; m >>= 1, x++){
227 if((~map[i] | havemap[i]) & m)
237 unpack(uchar *s, int n, char *fmt, ...)
251 if(s+1 > e) goto Err;
252 *va_arg(arg, int*) = *s++;
255 if(s+4 > e) goto Err;
256 *va_arg(arg, int*) = s[0]<<24 | s[1]<<16 | s[2]<<8 | s[3];
269 pack(uchar *s, int n, char *fmt, ...)
284 i = va_arg(arg, int);
286 if(s+1 > e) goto Err;
290 i = va_arg(arg, int);
291 if(s+4 > e) goto Err;
292 *s++ = (i>>24) & 0xFF;
293 *s++ = (i>>16) & 0xFF;
294 *s++ = (i>>8) & 0xFF;
298 i = va_arg(arg, int);
299 if(s+i > e) goto Err;
300 memmove(s, va_arg(arg, uchar*), i);
313 peer(char *ip, char *port)
316 static QLock peerslk;
318 uchar buf[64+MAXIO], *map, *told, *p, m;
320 int retry, i, o, l, x, n, fd;
321 int mechoking, hechoking;
326 if(ip == nil || port == nil)
329 d = mallocz(sizeof(*d) + 64, 1);
330 snprint(addr = d->str, 64, "tcp!%s!%s", ip, port);
332 if(dlook(peers, addr)){
337 d->len = strlen(addr);
344 if(rfork(RFFDG|RFPROC|RFMEM) <= 0)
349 map = malloc(nhavemap);
350 told = malloc(nhavemap);
354 sleep(10000 + nrand(5000));
359 if(debug) fprint(2, "dial %s\n", addr);
360 if((fd = dial(addr, nil, nil, nil)) < 0)
363 if(debug) fprint(2, "peer %s: -> handshake\n", addr);
364 n = pack(buf, sizeof(buf), "*________**",
365 20, "\x13BitTorrent protocol",
366 sizeof(infohash), infohash,
367 sizeof(peerid), peerid);
368 if(write(fd, buf, n) != n)
371 if(read(fd, buf, 1) != 1)
373 n = buf[0] + 8 + sizeof(infohash) + sizeof(peerid);
374 if((n = readn(fd, buf+1, n)) != n)
376 if(debug) fprint(2, "peer %s: <- handshake %.*s\n", addr, buf[0], (char*)buf+1);
377 if(memcmp(infohash, buf + 1 + buf[0] + 8, sizeof(infohash)))
380 if(debug) fprint(2, "peer %s: -> bitfield %d\n", addr, nhavemap);
381 memmove(told, havemap, nhavemap);
382 n = pack(buf, sizeof(buf), "lb*", nhavemap+1, 0x05, nhavemap, havemap);
383 if(write(fd, buf, n) != n)
391 memset(map, 0, nhavemap);
393 for(i=0; i<nhavemap; i++){
394 if(told[i] != havemap[i]){
395 for(x = i<<3, m = 0x80; m; m >>= 1, x++){
396 if((~havemap[i] | told[i] | map[i]) & m)
399 if(debug) fprint(2, "peer %s: -> have %d\n", addr, x);
400 n = pack(buf, sizeof(buf), "lbl", 1+4, 0x04, x);
401 if(write(fd, buf, n) != n)
405 if(!mewant && (map[i] & ~havemap[i])){
407 if(debug) fprint(2, "peer %s: -> interested\n", addr);
408 n = pack(buf, sizeof(buf), "lb", 1, 0x02);
409 if(write(fd, buf, n) != n)
413 if(!hechoking && mewant){
415 if(x >= 0 && pieces[x].brk < pieces[x].len)
417 else x = pickpiece(map);
420 l = pieces[x].len - o;
423 if(debug) fprint(2, "peer %s: -> request %d %d %d\n", addr, x, o, l);
424 n = pack(buf, sizeof(buf), "lblll", 1+4+4+4, 0x06, x, o, l);
425 if(write(fd, buf, n) != n)
430 if(mechoking && hewant){
432 if(debug) fprint(2, "peer %s: -> unchoke\n", addr);
433 n = pack(buf, sizeof(buf), "lb", 1, 0x01);
434 if(write(fd, buf, n) != n)
438 if(readn(fd, buf, 4) != 4)
440 unpack(buf, 4, "l", &n);
443 if(n < 0 || n > sizeof(buf))
445 if(readn(fd, buf, n) != n)
454 if(debug) fprint(2, "peer %s: <- choke\n", addr);
456 case 0x01: // Unchoke
458 if(debug) fprint(2, "peer %s: <- unchoke\n", addr);
460 case 0x02: // Interested
462 if(debug) fprint(2, "peer %s: <- interested\n", addr);
464 case 0x03: // Notinterested
466 if(debug) fprint(2, "peer %s: <- notinterested\n", addr);
468 case 0x04: // Have <piceindex>
469 if(unpack(p, n, "l", &x) < 0)
471 if(debug) fprint(2, "peer %s: <- have %d\n", addr, x);
472 if(x < 0 || x >= npieces)
474 map[x>>3] |= 0x80>>(x&7);
476 case 0x05: // Bitfield
477 if(debug) fprint(2, "peer %s: <- bitfield %d\n", addr, n);
482 case 0x06: // Request <index> <begin> <length>
483 if(unpack(p, n, "lll", &x, &o, &l) < 0)
485 if(debug) fprint(2, "peer %s: <- request %d %d %d\n", addr, x, o, l);
486 if(x < 0 || x >= npieces)
488 if(!hewant || mechoking || (~havemap[x>>3]&(0x80>>(x&7))))
490 if(debug) fprint(2, "peer %s: -> piece %d %d\n", addr, x, o);
494 if((l = rwpiece(0, x, buf + n, l, o)) <= 0)
496 n = pack(buf, sizeof(buf), "lbll", 1+4+4+l, 0x07, x, o);
498 if(write(fd, buf, n) != n)
501 case 0x07: // Piece <index> <begin> <block>
502 if(unpack(p, n, "ll", &x, &o) != 8)
506 if(debug) fprint(2, "peer %s: <- piece %d %d %d\n", addr, x, o, n);
507 if(x < 0 || x >= npieces)
509 if((pieces[x].brk != o) || (havemap[x>>3]&(0x80>>(x&7))))
511 if(rwpiece(1, x, p, n, o) == n){
512 if((pieces[x].brk = o+n) == pieces[x].len){
518 case 0x08: // Cancel <index> <begin> <length>
519 if(unpack(p, n, "lll", &x, &o, &l) < 0)
521 if(debug) fprint(2, "peer %s: <- cancel %d %d %d\n", addr, x, o, l);
523 case 0x09: // Port <port>
524 if(unpack(p, n, "l", &x) < 0)
526 if(debug) fprint(2, "peer %s: <- port %d\n", addr, x);
537 hopen(char *url, ...)
539 int conn, ctlfd, fd, n;
543 snprint(buf, sizeof buf, "%s/clone", mntweb);
544 if((ctlfd = open(buf, ORDWR)) < 0)
546 if((n = read(ctlfd, buf, sizeof buf-1)) <= 0){
554 n = 4+vsnprint(buf+4, sizeof(buf)-4, url, arg);
556 if(write(ctlfd, buf, n) != n){
561 snprint(buf, sizeof buf, "%s/%d/body", mntweb, conn);
562 if((fd = open(buf, OREAD)) < 0)
571 static Dict *trackers;
572 static QLock trackerslk;
582 if(dlook(trackers, url)){
583 qunlock(&trackerslk);
587 d = mallocz(sizeof(*d) + n+1, 1);
595 qunlock(&trackerslk);
597 if(rfork(RFFDG|RFPROC|RFMEM) <= 0)
602 if((fd = hopen("%s?info_hash=%.*H&peer_id=%.*H&port=%d&compact=1",
603 url, sizeof(infohash), infohash, sizeof(peerid), peerid, port)) >= 0){
609 if(l = dlook(d, "peers")){
615 for(; b+6 <= e; b += 6){
616 char ip[16], port[6];
618 snprint(ip, sizeof(ip), "%d.%d.%d.%d", b[0], b[1], b[2], b[3]);
619 snprint(port, sizeof(port), "%d", b[4]<<8 | b[5]);
622 } else for(; l && l->typ == 'l'; l = l->next)
623 peer(dstr(dlook(l->val, "ip")), dstr(dlook(l->val, "port")));
626 if(p = dstr(dlook(d, "interval")))
628 if(n < 10 | n > 60*60)
631 sleep(n * 1000 + nrand(5000));
639 s = va_arg(f->args, uchar*);
640 if(f->flags & FmtPrec)
643 e = s + strlen((char*)s);
645 if(fmtprint(f, ((*s >= '0' && *s <= '9') ||
646 (*s >= 'a' && *s <= 'z') ||
647 (*s >= 'A' && *s <= 'Z') ||
648 strchr(".-_~", *s)) ? "%c" : "%%%.2x", *s) < 0)
659 for(i=0; i<nhavemap; i++)
660 for(m = 0x80; m; m>>=1)
664 print("%d %d\n", c, npieces);
672 for(i=0;i!=3;i++){ /* It's a long way to the kitchen */
673 postnote(PNGROUP, pidgroup, "kill");
679 catchnote(void *, char *msg)
688 fprint(2, "usage: %s [ -vsdp ] [ -m mtpt ] [ torrentfile ]\n", argv0);
693 main(int argc, char *argv[])
695 Dict *info, *torrent, *d;
701 fmtinstall('H', Hfmt);
705 mntweb = EARGF(usage());
725 if((fd = open(*argv, OREAD)) < 0)
726 sysfatal("open torrent: %r");
727 if((n = readall(fd, &p)) <= 0)
728 sysfatal("read torrent: %r");
729 bparse(p, p+n, &torrent);
730 if((d = info = dlook(torrent, "info")) == nil)
731 sysfatal("no meta info in torrent");
732 for(s = e = d->start; d && d->typ == 'd'; d = d->next)
734 sha1((uchar*)s, e - s, (uchar*)infohash, nil);
738 if(d = dlook(info, "files")){
739 for(; d && d->typ == 'l'; d = d->next){
742 if((s = dstr(dlook(d->val, "length"))) == nil)
744 f = mallocz(sizeof(*f), 1);
746 f->name = dstr(dlook(info, "name"));
747 for(di = dlook(d->val, "path"); di && di->typ == 'l'; di = di->next)
748 if(s = dstr(di->val))
749 f->name = f->name ? smprint("%s/%s", f->name, s) : s;
753 } else if(s = dstr(dlook(info, "length"))){
754 f = mallocz(sizeof(*f), 1);
756 f->name = dstr(dlook(info, "name"));
760 for(f = files; f; f = f->next){
761 if(f->name == nil || f->len <= 0)
762 sysfatal("bogus file entry in meta info");
763 if(vflag) fprint(pflag ? 2 : 1, "%s\n", f->name);
764 if((f->fd = open(f->name, ORDWR)) < 0)
765 if((f->fd = create(f->name, ORDWR, 0666)) < 0)
766 sysfatal("create: %r");
771 sysfatal("no files in torrent");
773 if((s = dstr(dlook(info, "piece length"))) == nil)
774 sysfatal("missing piece length in meta info");
775 if((blocksize = atoi(s)) <= 0)
776 sysfatal("bogus piece length in meta info");
777 d = dlook(info, "pieces");
778 if(d == nil || d->typ != 's' || d->len <= 0 || d->len % 20)
779 sysfatal("bad or no pices in meta info");
780 npieces = d->len / 20;
781 pieces = mallocz(sizeof(Piece) * npieces, 1);
782 nhavemap = (npieces+7) / 8;
783 havemap = mallocz(nhavemap, 1);
784 for(i = 0; i<npieces; i++){
785 pieces[i].hash = (uchar*)d->str + i*20;
789 pieces[i].len = blocksize;
790 len -= pieces[i].len;
793 sysfatal("pieces do not match file length");
795 for(i = 0; i<npieces; i++)
798 switch(i = rfork(RFPROC|RFMEM|RFNOTEG|RFNAMEG)){
800 sysfatal("fork: %r");
802 memmove(peerid, "-NF9001-", 8);
803 genrandom(peerid+8, sizeof(peerid)-8);
804 tracker(dstr(dlook(torrent, "announce")));
805 for(d = dlook(torrent, "announce-list"); d && d->typ == 'l'; d = d->next)
806 if(d->val && d->val->typ == 'l')
807 tracker(dstr(d->val->val));
812 atnotify(catchnote, 1);
813 while(!progress() || sflag)