6 typedef struct Dict Dict;
7 typedef struct Piece Piece;
8 typedef struct File File;
9 typedef struct Stats Stats;
13 char typ; // i, d, s, l
49 int debug, sflag, pflag, vflag;
52 char *mntweb = "/mnt/web";
79 bparse(char *s, char *e, Dict **dp)
101 s = bparse(s, e, dp);
105 d = *dp = mallocz(sizeof(*d), 1);
109 s = bparse(s, e, &d->val);
120 if((s = memchr(x, 'e', e - x)) == nil)
126 if((x = memchr(s, ':', e - s)) == nil)
129 if((n = atoi(s)) < 0)
132 if((s > e) || (s < x)){
138 d = mallocz(sizeof(*d) + n+1, 1);
140 memmove(d->str, x, d->len = n);
149 if(d && (d->typ == 's' || d->typ == 'i'))
155 dlook(Dict *d, char *s)
157 for(; d && d->typ == 'd'; d = d->next)
158 if(d->len && strcmp(d->str, s) == 0)
164 readall(int fd, char **p)
170 while(*p = realloc(*p, n+1024)){
171 if((r = read(fd, *p+n, 1024)) <= 0)
179 rwpiece(int wr, int index, uchar *data, int len, int poff)
185 if(len <= 0 || poff >= pieces[index].len)
187 if(len+poff > pieces[index].len)
188 len = pieces[index].len - poff;
189 off = (vlong)index * blocksize;
191 for(f = files; f; f = f->next)
192 if((f->off+f->len) > off)
195 n = ((off + len) > f->len) ? f->len - off : len;
196 if((n = (wr ? pwrite(f->fd, data, n, off) : pread(f->fd, data, n, off))) <= 0)
198 if((m = rwpiece(wr, index, data + n, len - n, poff + n)) < 0)
206 uchar *p, m, hash[20];
210 if(havemap[x>>3] & m)
212 p = malloc(blocksize);
214 if(rwpiece(0, x, p, n, 0) != n){
218 sha1(p, n, hash, nil);
220 if(memcmp(hash, pieces[x].hash, 20))
223 if((havemap[x>>3] & m) == 0){
226 stats.left -= pieces[x].len;
233 pickpiece(uchar *map)
240 for(i = 0; i<nhavemap; i++){
243 for(x = i<<3, m = 0x80; m; m >>= 1, x++){
244 if((~map[i] | havemap[i]) & m)
254 unpack(uchar *s, int n, char *fmt, ...)
268 if(s+1 > e) goto Err;
269 *va_arg(arg, int*) = *s++;
272 if(s+4 > e) goto Err;
273 *va_arg(arg, int*) = s[0]<<24 | s[1]<<16 | s[2]<<8 | s[3];
286 pack(uchar *s, int n, char *fmt, ...)
301 i = va_arg(arg, int);
303 if(s+1 > e) goto Err;
307 i = va_arg(arg, int);
308 if(s+4 > e) goto Err;
309 *s++ = (i>>24) & 0xFF;
310 *s++ = (i>>16) & 0xFF;
311 *s++ = (i>>8) & 0xFF;
315 i = va_arg(arg, int);
316 if(s+i > e) goto Err;
317 memmove(s, va_arg(arg, uchar*), i);
332 peer(int fd, int incoming, char *addr)
334 uchar buf[64+MAXIO], *map, *told, *p, m;
335 int mechoking, hechoking;
340 if(debug) fprint(2, "peer %s: %s connected\n", addr, incoming ? "incoming" : "outgoing");
343 if((incoming && i) || (!incoming && !i)){
344 if(debug) fprint(2, "peer %s: -> handshake\n", addr);
345 n = pack(buf, sizeof(buf), "*________**",
346 20, "\x13BitTorrent protocol",
347 sizeof(infohash), infohash,
348 sizeof(peerid), peerid);
349 if(write(fd, buf, n) != n)
352 if((incoming && !i) || (!incoming && i)){
353 n = 20 + 8 + sizeof(infohash);
354 if((n = readn(fd, buf, n)) != n)
356 if(memcmp(buf, "\x13BitTorrent protocol", 20))
358 if(debug) fprint(2, "peer %s: <- handshake\n", addr);
359 if(memcmp(infohash, buf + 20 + 8, sizeof(infohash)))
363 if(readn(fd, buf, sizeof(peerid)) != sizeof(peerid))
365 if(memcmp(peerid, buf, sizeof(peerid)) == 0)
367 if(debug) fprint(2, "peer %s: peerid %.*s\n", addr, sizeof(peerid), (char*)buf);
374 map = mallocz(nhavemap, 1);
375 told = malloc(nhavemap);
377 if(debug) fprint(2, "peer %s: -> bitfield %d\n", addr, nhavemap);
378 memmove(told, havemap, nhavemap);
379 n = pack(buf, sizeof(buf), "lb*", nhavemap+1, 0x05, nhavemap, havemap);
380 if(write(fd, buf, n) != n)
384 for(i=0; i<nhavemap; i++){
385 if(told[i] != havemap[i]){
386 for(x = i<<3, m = 0x80; m; m >>= 1, x++){
387 if((~havemap[i] | told[i] | map[i]) & m)
390 if(debug) fprint(2, "peer %s: -> have %d\n", addr, x);
391 n = pack(buf, sizeof(buf), "lbl", 1+4, 0x04, x);
392 if(write(fd, buf, n) != n)
396 if(!mewant && (map[i] & ~havemap[i])){
398 if(debug) fprint(2, "peer %s: -> interested\n", addr);
399 n = pack(buf, sizeof(buf), "lb", 1, 0x02);
400 if(write(fd, buf, n) != n)
404 if(!hechoking && mewant){
406 if(x >= 0 && pieces[x].brk < pieces[x].len)
408 else x = pickpiece(map);
411 l = pieces[x].len - o;
414 if(debug) fprint(2, "peer %s: -> request %d %d %d\n", addr, x, o, l);
415 n = pack(buf, sizeof(buf), "lblll", 1+4+4+4, 0x06, x, o, l);
416 if(write(fd, buf, n) != n)
421 if(mechoking && hewant){
423 if(debug) fprint(2, "peer %s: -> unchoke\n", addr);
424 n = pack(buf, sizeof(buf), "lb", 1, 0x01);
425 if(write(fd, buf, n) != n)
429 if(readn(fd, buf, 4) != 4)
431 unpack(buf, 4, "l", &n);
432 if(n < 0 || n > sizeof(buf))
436 if(readn(fd, buf, n) != n)
445 if(debug) fprint(2, "peer %s: <- choke\n", addr);
447 case 0x01: // Unchoke
449 if(debug) fprint(2, "peer %s: <- unchoke\n", addr);
451 case 0x02: // Interested
453 if(debug) fprint(2, "peer %s: <- interested\n", addr);
455 case 0x03: // Notinterested
457 if(debug) fprint(2, "peer %s: <- notinterested\n", addr);
459 case 0x04: // Have <piceindex>
460 if(unpack(p, n, "l", &x) < 0)
462 if(debug) fprint(2, "peer %s: <- have %d\n", addr, x);
463 if(x < 0 || x >= npieces)
465 map[x>>3] |= 0x80>>(x&7);
467 case 0x05: // Bitfield
468 if(debug) fprint(2, "peer %s: <- bitfield %d\n", addr, n);
473 case 0x06: // Request <index> <begin> <length>
474 if(unpack(p, n, "lll", &x, &o, &l) < 0)
476 if(debug) fprint(2, "peer %s: <- request %d %d %d\n", addr, x, o, l);
477 if(x < 0 || x >= npieces)
479 if(!hewant || mechoking || (~havemap[x>>3]&(0x80>>(x&7))))
481 if(debug) fprint(2, "peer %s: -> piece %d %d\n", addr, x, o);
485 if((l = rwpiece(0, x, buf + n, l, o)) <= 0)
487 n = pack(buf, sizeof(buf), "lbll", 1+4+4+l, 0x07, x, o);
489 if(write(fd, buf, n) != n)
495 case 0x07: // Piece <index> <begin> <block>
496 if(unpack(p, n, "ll", &x, &o) != 8)
503 if(debug) fprint(2, "peer %s: <- piece %d %d %d\n", addr, x, o, n);
504 if(x < 0 || x >= npieces)
506 if((pieces[x].brk != o) || (havemap[x>>3]&(0x80>>(x&7))))
508 if(rwpiece(1, x, p, n, o) == n){
509 if((pieces[x].brk = o+n) == pieces[x].len){
515 case 0x08: // Cancel <index> <begin> <length>
516 if(unpack(p, n, "lll", &x, &o, &l) < 0)
518 if(debug) fprint(2, "peer %s: <- cancel %d %d %d\n", addr, x, o, l);
520 case 0x09: // Port <port>
521 if(unpack(p, n, "l", &x) < 0)
523 if(debug) fprint(2, "peer %s: <- port %d\n", addr, x);
537 char addr[64], adir[40], ldir[40];
542 for(port=6881; port<6890; port++){
543 snprint(addr, sizeof(addr), "tcp!*!%d", port);
544 if((afd = announce(addr, adir)) >= 0)
548 fprint(2, "announce: %r");
551 if(rfork(RFFDG|RFPROC|RFMEM))
554 if((lfd = listen(adir, ldir)) < 0){
555 fprint(2, "listen: %r");
558 if(rfork(RFFDG|RFPROC|RFMEM)){
562 if((dfd = accept(lfd, ldir)) < 0){
563 fprint(2, "accept: %r");
566 ni = getnetconninfo(ldir, dfd);
567 peer(dfd, 1, ni ? ni->raddr : "???");
568 if(ni) freenetconninfo(ni);
575 client(char *ip, char *port)
578 static QLock peerslk;
583 if(ip == nil || port == nil)
586 d = mallocz(sizeof(*d) + 64, 1);
587 snprint(addr = d->str, 64, "tcp!%s!%s", ip, port);
589 if(dlook(peers, addr)){
594 d->len = strlen(addr);
601 if(debug) fprint(2, "client %s\n", addr);
603 if(rfork(RFFDG|RFPROC|RFMEM))
605 for(try = 0; try < 10; try++){
606 if((fd = dial(addr, nil, nil, nil)) >= 0){
607 if(!peer(fd, 0, addr))
611 sleep((1000<<try)+nrand(5000));
617 hopen(char *url, ...)
619 int conn, ctlfd, fd, n;
623 snprint(buf, sizeof buf, "%s/clone", mntweb);
624 if((ctlfd = open(buf, ORDWR)) < 0)
626 if((n = read(ctlfd, buf, sizeof buf-1)) <= 0){
634 n = 4+vsnprint(buf+4, sizeof(buf)-4, url, arg);
636 if(write(ctlfd, buf, n) != n){
641 snprint(buf, sizeof buf, "%s/%d/body", mntweb, conn);
642 if((fd = open(buf, OREAD)) < 0)
651 static Dict *trackers;
652 static QLock trackerslk;
662 if(dlook(trackers, url)){
663 qunlock(&trackerslk);
667 d = mallocz(sizeof(*d) + n+1, 1);
675 qunlock(&trackerslk);
677 if(debug) fprint(2, "tracker %s\n", url);
679 if(rfork(RFPROC|RFMEM))
683 vlong up, down, left;
692 if((fd = hopen("%s?info_hash=%.*H&peer_id=%.*H&port=%d&"
693 "uploaded=%lld&downloaded=%lld&left=%lld&"
695 url, sizeof(infohash), infohash, sizeof(peerid), peerid, port,
696 up, down, left)) >= 0){
702 if(debug) fprint(2, "tracker %s: %r\n", url);
704 if(l = dlook(d, "peers")){
710 for(; b+6 <= e; b += 6){
711 char ip[16], port[6];
713 snprint(ip, sizeof(ip), "%d.%d.%d.%d", b[0], b[1], b[2], b[3]);
714 snprint(port, sizeof(port), "%d", b[4]<<8 | b[5]);
717 } else for(; l && l->typ == 'l'; l = l->next)
718 client(dstr(dlook(l->val, "ip")), dstr(dlook(l->val, "port")));
721 if(p = dstr(dlook(d, "interval")))
723 if(n < 10 | n > 60*60)
726 sleep(n * 1000 + nrand(5000));
734 s = va_arg(f->args, uchar*);
735 if(f->flags & FmtPrec)
738 e = s + strlen((char*)s);
740 if(fmtprint(f, ((*s >= '0' && *s <= '9') ||
741 (*s >= 'a' && *s <= 'z') ||
742 (*s >= 'A' && *s <= 'Z') ||
743 strchr(".-_~", *s)) ? "%c" : "%%%.2x", *s) < 0)
749 killnote(void *, char *)
751 postnote(PNGROUP, killgroup, "kill");
758 fprint(2, "usage: %s [ -vsdp ] [ -m mtpt ] [ torrentfile ]\n", argv0);
763 main(int argc, char *argv[])
765 Dict *info, *torrent, *d;
771 fmtinstall('H', Hfmt);
775 mntweb = EARGF(usage());
795 if((fd = open(*argv, OREAD)) < 0)
796 sysfatal("open torrent: %r");
797 if((n = readall(fd, &p)) <= 0)
798 sysfatal("read torrent: %r");
799 bparse(p, p+n, &torrent);
800 if((d = info = dlook(torrent, "info")) == nil)
801 sysfatal("no meta info in torrent");
802 for(s = e = d->start; d && d->typ == 'd'; d = d->next)
804 sha1((uchar*)s, e - s, (uchar*)infohash, nil);
808 if(d = dlook(info, "files")){
809 for(; d && d->typ == 'l'; d = d->next){
812 if((s = dstr(dlook(d->val, "length"))) == nil)
814 f = mallocz(sizeof(*f), 1);
816 f->name = dstr(dlook(info, "name"));
817 for(di = dlook(d->val, "path"); di && di->typ == 'l'; di = di->next)
818 if(s = dstr(di->val))
819 f->name = f->name ? smprint("%s/%s", f->name, s) : s;
823 } else if(s = dstr(dlook(info, "length"))){
824 f = mallocz(sizeof(*f), 1);
826 f->name = dstr(dlook(info, "name"));
830 for(f = files; f; f = f->next){
831 if(f->name == nil || f->len <= 0)
832 sysfatal("bogus file entry in meta info");
833 if(vflag) fprint(pflag ? 2 : 1, "%s\n", f->name);
834 if((f->fd = open(f->name, ORDWR)) < 0)
835 if((f->fd = create(f->name, ORDWR, 0666)) < 0)
836 sysfatal("create: %r");
841 sysfatal("no files in torrent");
843 if((s = dstr(dlook(info, "piece length"))) == nil)
844 sysfatal("missing piece length in meta info");
845 if((blocksize = atoi(s)) <= 0)
846 sysfatal("bogus piece length in meta info");
847 d = dlook(info, "pieces");
848 if(d == nil || d->typ != 's' || d->len <= 0 || d->len % 20)
849 sysfatal("bad or no pices in meta info");
850 npieces = d->len / 20;
851 pieces = mallocz(sizeof(Piece) * npieces, 1);
852 nhavemap = (npieces+7) / 8;
853 havemap = mallocz(nhavemap, 1);
854 for(i = 0; i<npieces; i++){
855 pieces[i].hash = (uchar*)d->str + i*20;
859 pieces[i].len = blocksize;
860 len -= pieces[i].len;
861 stats.left += pieces[i].len;
864 sysfatal("pieces do not match file length");
866 for(i = 0; i<npieces; i++)
870 atnotify(killnote, 1);
871 switch(i = rfork(RFPROC|RFMEM|RFNOTEG)){
873 sysfatal("fork: %r");
875 memmove(peerid, "-NF9001-", 8);
876 for(i=8; i<sizeof(peerid); i++)
877 peerid[i] = nrand(10)+'0';
879 tracker(dstr(dlook(torrent, "announce")));
880 for(d = dlook(torrent, "announce-list"); d && d->typ == 'l'; d = d->next)
881 if(d->val && d->val->typ == 'l')
882 tracker(dstr(d->val->val));
883 while(waitpid() != -1)
888 while((nhavepieces < npieces) || sflag){
890 print("%d %d\n", nhavepieces, npieces);
894 postnote(PNGROUP, killgroup, "kill");