6 typedef struct Dict Dict;
7 typedef struct Piece Piece;
8 typedef struct File File;
9 typedef struct Stats Stats;
17 char typ; // i, d, s, l
52 char *deftrack = "http://exodus.desync.com/announce";
53 char *mntweb = "/mnt/web";
71 return nhavepieces >= npieces;
86 bparse(char *s, char *e, Dict **dp)
108 s = bparse(s, e, dp);
112 d = *dp = mallocz(sizeof(*d), 1);
116 s = bparse(s, e, &d->val);
127 if((s = memchr(x, 'e', e - x)) == nil)
133 if((x = memchr(s, ':', e - s)) == nil)
136 if((n = atoi(s)) < 0)
139 if((s > e) || (s < x)){
145 d = mallocz(sizeof(*d) + n+1, 1);
147 memmove(d->str, x, d->len = n);
156 if(d && (d->typ == 's' || d->typ == 'i'))
162 dlook(Dict *d, char *s)
164 for(; d && d->typ == 'd'; d = d->next)
165 if(d->len && strcmp(d->str, s) == 0)
171 readall(int fd, char **p)
177 while(*p = realloc(*p, n+1024)){
178 if((r = read(fd, *p+n, 1024)) <= 0)
186 rwpiece(int wr, int index, uchar *data, int len, int poff)
192 if(len <= 0 || poff >= pieces[index].len)
194 if(len+poff > pieces[index].len)
195 len = pieces[index].len - poff;
196 off = (vlong)index * blocksize;
198 for(f = files; f; f = f->next)
199 if((f->off+f->len) > off)
202 n = ((off + len) > f->len) ? f->len - off : len;
203 if((n = (wr ? pwrite(f->fd, data, n, off) : pread(f->fd, data, n, off))) <= 0)
205 if((m = rwpiece(wr, index, data + n, len - n, poff + n)) < 0)
213 uchar *p, m, hash[20];
217 if(havemap[x>>3] & m)
219 p = malloc(blocksize);
221 if(rwpiece(0, x, p, n, 0) != n){
225 sha1(p, n, hash, nil);
227 if(memcmp(hash, pieces[x].hash, 20))
230 if((havemap[x>>3] & m) == 0){
233 stats.left -= pieces[x].len;
240 pickpiece(uchar *map)
247 for(i = 0; i<nhavemap; i++){
250 for(x = i<<3, m = 0x80; m; m >>= 1, x++){
251 if((~map[i] | havemap[i]) & m)
261 unpack(uchar *s, int n, char *fmt, ...)
275 if(s+1 > e) goto Err;
276 *va_arg(arg, int*) = *s++;
279 if(s+4 > e) goto Err;
280 *va_arg(arg, int*) = s[0]<<24 | s[1]<<16 | s[2]<<8 | s[3];
293 pack(uchar *s, int n, char *fmt, ...)
308 i = va_arg(arg, int);
310 if(s+1 > e) goto Err;
314 i = va_arg(arg, int);
315 if(s+4 > e) goto Err;
316 *s++ = (i>>24) & 0xFF;
317 *s++ = (i>>16) & 0xFF;
318 *s++ = (i>>8) & 0xFF;
322 i = va_arg(arg, int);
323 if(s+i > e) goto Err;
324 memmove(s, va_arg(arg, uchar*), i);
339 peer(int fd, int incoming, char *addr)
341 uchar buf[64+MAXIO], *map, *told, *p, m;
342 int mechoking, hechoking;
347 if(debug) fprint(2, "peer %s: %s connected\n", addr, incoming ? "incoming" : "outgoing");
350 if((incoming && i) || (!incoming && !i)){
351 if(debug) fprint(2, "peer %s: -> handshake\n", addr);
352 n = pack(buf, sizeof(buf), "*________**",
353 20, "\x13BitTorrent protocol",
354 sizeof(infohash), infohash,
355 sizeof(peerid), peerid);
356 if(write(fd, buf, n) != n)
359 if((incoming && !i) || (!incoming && i)){
360 n = 20 + 8 + sizeof(infohash);
361 if((n = readn(fd, buf, n)) != n)
363 if(memcmp(buf, "\x13BitTorrent protocol", 20))
365 if(memcmp(infohash, buf + 20 + 8, sizeof(infohash)))
367 if(debug) fprint(2, "peer %s: <- handshake\n", addr);
370 if(readn(fd, buf, sizeof(peerid)) != sizeof(peerid))
372 if(memcmp(peerid, buf, sizeof(peerid)) == 0)
374 if(debug) fprint(2, "peer %s: peerid %.*s\n", addr, sizeof(peerid), (char*)buf);
381 map = mallocz(nhavemap, 1);
382 told = malloc(nhavemap);
384 if(debug) fprint(2, "peer %s: -> bitfield %d\n", addr, nhavemap);
385 memmove(told, havemap, nhavemap);
386 n = pack(buf, sizeof(buf), "lb*", nhavemap+1, 0x05, nhavemap, told);
387 if(write(fd, buf, n) != n)
391 for(i=0; i<nhavemap; i++){
392 if(told[i] != havemap[i]){
393 for(x = i<<3, m = 0x80; m; m >>= 1, x++){
394 if((~havemap[i] | told[i] | map[i]) & m)
397 if(debug) fprint(2, "peer %s: -> have %d\n", addr, x);
398 n = pack(buf, sizeof(buf), "lbl", 1+4, 0x04, x);
399 if(write(fd, buf, n) != n)
403 if(!mewant && (map[i] & ~havemap[i])){
405 if(debug) fprint(2, "peer %s: -> interested\n", addr);
406 n = pack(buf, sizeof(buf), "lb", 1, 0x02);
407 if(write(fd, buf, n) != n)
411 if(!hechoking && mewant){
413 if(x >= 0 && pieces[x].brk < pieces[x].len)
415 else x = pickpiece(map);
418 l = pieces[x].len - o;
421 if(debug) fprint(2, "peer %s: -> request %d %d %d\n", addr, x, o, l);
422 n = pack(buf, sizeof(buf), "lblll", 1+4+4+4, 0x06, x, o, l);
423 if(write(fd, buf, n) != n)
428 if(mechoking && hewant){
430 if(debug) fprint(2, "peer %s: -> unchoke\n", addr);
431 n = pack(buf, sizeof(buf), "lb", 1, 0x01);
432 if(write(fd, buf, n) != n)
436 if(readn(fd, buf, 4) != 4)
438 unpack(buf, 4, "l", &n);
439 if(n < 0 || n > sizeof(buf))
443 if(readn(fd, buf, n) != n)
452 if(debug) fprint(2, "peer %s: <- choke\n", addr);
454 case 0x01: // Unchoke
456 if(debug) fprint(2, "peer %s: <- unchoke\n", addr);
458 case 0x02: // Interested
460 if(debug) fprint(2, "peer %s: <- interested\n", addr);
462 case 0x03: // Notinterested
464 if(debug) fprint(2, "peer %s: <- notinterested\n", addr);
466 case 0x04: // Have <piceindex>
467 if(unpack(p, n, "l", &x) < 0)
469 if(debug) fprint(2, "peer %s: <- have %d\n", addr, x);
470 if(x < 0 || x >= npieces)
472 map[x>>3] |= 0x80>>(x&7);
474 case 0x05: // Bitfield
475 if(debug) fprint(2, "peer %s: <- bitfield %d\n", addr, n);
480 case 0x06: // Request <index> <begin> <length>
481 if(unpack(p, n, "lll", &x, &o, &l) < 0)
483 if(debug) fprint(2, "peer %s: <- request %d %d %d\n", addr, x, o, l);
484 if(x < 0 || x >= npieces)
486 if(!hewant || mechoking || (~havemap[x>>3]&(0x80>>(x&7))))
488 if(debug) fprint(2, "peer %s: -> piece %d %d\n", addr, x, o);
492 if((l = rwpiece(0, x, buf + n, l, o)) <= 0)
494 n = pack(buf, sizeof(buf), "lbll", 1+4+4+l, 0x07, x, o);
496 if(write(fd, buf, n) != n)
502 case 0x07: // Piece <index> <begin> <block>
503 if(unpack(p, n, "ll", &x, &o) != 8)
510 if(debug) fprint(2, "peer %s: <- piece %d %d %d\n", addr, x, o, n);
511 if(x < 0 || x >= npieces)
513 if((pieces[x].brk != o) || (havemap[x>>3]&(0x80>>(x&7))))
515 if(rwpiece(1, x, p, n, o) == n){
516 if((pieces[x].brk = o+n) == pieces[x].len){
522 case 0x08: // Cancel <index> <begin> <length>
523 if(unpack(p, n, "lll", &x, &o, &l) < 0)
525 if(debug) fprint(2, "peer %s: <- cancel %d %d %d\n", addr, x, o, l);
527 case 0x09: // Port <port>
528 if(unpack(p, n, "l", &x) < 0)
530 if(debug) fprint(2, "peer %s: <- port %d\n", addr, x);
544 char addr[64], adir[40], ldir[40];
549 for(port=6881; port<6890; port++){
550 snprint(addr, sizeof(addr), "tcp!*!%d", port);
551 if((afd = announce(addr, adir)) >= 0)
555 fprint(2, "announce: %r");
558 if(rfork(RFFDG|RFPROC|RFMEM))
561 if((lfd = listen(adir, ldir)) < 0){
562 fprint(2, "listen: %r");
565 if(rfork(RFFDG|RFPROC|RFMEM)){
569 if((dfd = accept(lfd, ldir)) < 0){
570 fprint(2, "accept: %r");
573 ni = getnetconninfo(ldir, dfd);
574 peer(dfd, 1, ni ? ni->raddr : "???");
575 if(ni) freenetconninfo(ni);
582 client(char *ip, char *port)
585 static QLock peerslk;
590 if(ip == nil || port == nil)
593 d = mallocz(sizeof(*d) + 64, 1);
594 snprint(addr = d->str, 64, "tcp!%s!%s", ip, port);
596 if(dlook(peers, addr)){
601 d->len = strlen(addr);
608 if(debug) fprint(2, "client %s\n", addr);
610 if(rfork(RFFDG|RFPROC|RFMEM))
612 for(try = 0; try < 10; try++){
613 if((fd = dial(addr, nil, nil, nil)) >= 0){
614 if(!peer(fd, 0, addr))
618 sleep((1000<<try)+nrand(5000));
624 hopen(char *url, ...)
626 int conn, ctlfd, fd, n;
630 snprint(buf, sizeof buf, "%s/clone", mntweb);
631 if((ctlfd = open(buf, ORDWR)) < 0)
633 if((n = read(ctlfd, buf, sizeof buf-1)) <= 0){
641 n = 4+vsnprint(buf+4, sizeof(buf)-4, url, arg);
643 if(write(ctlfd, buf, n) != n){
648 snprint(buf, sizeof buf, "%s/%d/body", mntweb, conn);
649 if((fd = open(buf, OREAD)) < 0)
656 webseed(Dict *w, File *f)
659 int fd, n, m, r, p, x, y, err;
664 if(w == nil || f == nil || finished())
666 if(rfork(RFPROC|RFMEM))
670 if(debug) fprint(2, "webseed %s %s\n", w->str, f->name);
671 s = strrchr(w->str, '/');
673 fd = hopen("%s%s", w->str, f->name);
675 fd = hopen("%s", w->str);
677 if(debug) fprint(2, "webseed %s %s: %r\n", w->str, f->name);
680 if((w = w->next) == w0)
691 if((f->len - off) < n)
693 if((n = read(fd, buf, n)) <= 0)
696 x = woff / blocksize;
698 y = (f->off + off) / blocksize;
699 p = woff - x*blocksize;
702 r = pieces[x].len - p;
705 if((havemap[x>>3] & (0x80>>(x&7))) == 0)
706 if(rwpiece(1, x, buf+m, r, p) != r)
715 fprint(2, "webseed %s %s: corrupt\n", w->str, f->name);
721 havepiece(f->off / blocksize);
722 havepiece((f->off+f->len) / blocksize);
731 static Dict *trackers;
732 static QLock trackerslk;
742 if(dlook(trackers, url)){
743 qunlock(&trackerslk);
747 d = mallocz(sizeof(*d) + n+1, 1);
755 qunlock(&trackerslk);
757 if(debug) fprint(2, "tracker %s\n", url);
759 if(rfork(RFPROC|RFMEM))
762 event = "&event=started";
764 vlong up, down, left;
773 if((fd = hopen("%s?info_hash=%.*H&peer_id=%.*H&port=%d&"
774 "uploaded=%lld&downloaded=%lld&left=%lld&compact=1&no_peer_id=1%s",
775 url, sizeof(infohash), infohash, sizeof(peerid), peerid, port,
776 up, down, left, event)) >= 0){
782 } else if(debug) fprint(2, "tracker %s: %r\n", url);
783 if(l = dlook(d, "peers")){
789 for(; b+6 <= e; b += 6){
790 char ip[16], port[6];
792 snprint(ip, sizeof(ip), "%d.%d.%d.%d", b[0], b[1], b[2], b[3]);
793 snprint(port, sizeof(port), "%d", b[4]<<8 | b[5]);
796 } else for(; l && l->typ == 'l'; l = l->next)
797 client(dstr(dlook(l->val, "ip")), dstr(dlook(l->val, "port")));
800 if(p = dstr(dlook(d, "interval")))
802 if(n < 10 | n > 60*60)
805 sleep(n * 1000 + nrand(5000));
813 s = va_arg(f->args, uchar*);
814 if(f->flags & FmtPrec)
817 e = s + strlen((char*)s);
819 if(fmtprint(f, *s && ((*s >= '0' && *s <= '9') ||
820 (*s >= 'a' && *s <= 'z') ||
821 (*s >= 'A' && *s <= 'Z') ||
822 strchr(".-_~", *s)) ? "%c" : "%%%.2x", *s) < 0)
828 mktorrent(int fd, Dict *alist, Dict *wlist)
834 if((d = dirfstat(fd)) == nil)
836 if(d->qid.type & QTDIR){
838 werrstr("file is a directory");
843 werrstr("empty file");
847 for(blocksize = 256*1024;;blocksize<<=1){
848 npieces = (d->length + blocksize-1) / blocksize;
849 if(npieces <= 8*1024 || blocksize >= 2*1024*1024)
853 print("8:announce%ld:%s", strlen(alist->str), alist->str);
855 print("13:announce-listl");
856 print("l%ld:%se", strlen(alist->str), alist->str);
857 for(alist = alist->next; alist; alist = alist->next)
858 print("l%ld:%se", strlen(alist->str), alist->str);
863 print("8:url-listl");
864 for(; wlist; wlist = wlist->next)
865 print("%ld:%s", strlen(wlist->str), wlist->str);
868 print("8:url-list%ld:%s", strlen(wlist->str), wlist->str);
872 print("4:name%ld:%s", strlen(d->name), d->name);
873 print("6:lengthi%llde", d->length);
874 print("12:piece lengthi%de", blocksize);
875 print("6:pieces%d:", npieces*sizeof(h));
877 b = malloc(blocksize);
878 while((n = readn(fd, b, blocksize)) > 0){
880 if(write(1, h, sizeof(h)) != sizeof(h)){
888 werrstr("read failed: %r");
902 if(access(s, AEXIST) == 0)
904 for(p=strchr(s+1, '/'); p; p=strchr(p+1, '/')){
906 if(access(s, AEXIST)){
907 if((f = create(s, OREAD, DMDIR | 0777)) < 0){
929 s += chartorune(&r, s);
932 if((n + runelen(r)) >= l){
936 n += runetochar(d + n, &r);
943 killnote(void *, char *)
945 postnote(PNGROUP, killgroup, "kill");
952 fprint(2, "usage: %s [ -vsdpc ] [ -m mtpt ] [ -t tracker-url ] "
953 "[ -w webseed-url ] [ file ]\n", argv0);
958 scons(char *s, Dict *t)
964 for(l = t; l; l = l->next)
965 if(strcmp(l->str, s) == 0)
967 l = mallocz(sizeof(*l) + strlen(s)+1, 1);
974 main(int argc, char *argv[])
976 int sflag, pflag, vflag, cflag, fd, i, n;
977 Dict *alist, *wlist, *info, *torrent, *d, *l;
982 fmtinstall('H', Hfmt);
984 sflag = pflag = vflag = cflag = 0;
987 mntweb = EARGF(usage());
990 alist = scons(EARGF(usage()), alist);
993 wlist = scons(EARGF(usage()), wlist);
1016 if((fd = open(*argv, OREAD)) < 0)
1017 sysfatal("open: %r");
1020 alist = scons(deftrack, alist);
1021 if(mktorrent(fd, alist, wlist) < 0)
1025 if((n = readall(fd, &p)) <= 0)
1026 sysfatal("read torrent: %r");
1027 bparse(p, p+n, &torrent);
1029 alist = scons(dstr(dlook(torrent, "announce")), alist);
1030 for(d = dlook(torrent, "announce-list"); d && d->typ == 'l'; d = d->next)
1031 for(l = d->val; l && l->typ == 'l'; l = l->next)
1032 alist = scons(dstr(l->val), alist);
1034 if(d = dlook(torrent, "url-list")){
1036 wlist = scons(dstr(d->val), wlist);
1037 else for(l = d; l && l->typ == 'l'; l = l->next)
1038 wlist = scons(dstr(l->val), wlist);
1039 /* make wlist into a ring */
1040 for(l = wlist; l && l->next; l = l->next)
1042 if(l) l->next = wlist;
1045 if(alist == nil && wlist == nil)
1046 sysfatal("no trackers or webseeds in torrent");
1048 if((d = info = dlook(torrent, "info")) == nil)
1049 sysfatal("no meta info in torrent");
1050 for(s = e = d->start; d && d->typ == 'd'; d = d->next)
1052 sha1((uchar*)s, e - s, (uchar*)infohash, nil);
1056 if(d = dlook(info, "files")){
1057 for(; d && d->typ == 'l'; d = d->next){
1060 if((s = dstr(dlook(d->val, "length"))) == nil)
1062 f = mallocz(sizeof(*f), 1);
1064 f->name = dstr(dlook(info, "name"));
1065 for(di = dlook(d->val, "path"); di && di->typ == 'l'; di = di->next)
1066 if(s = dstr(di->val))
1067 f->name = f->name ? smprint("%s/%s", f->name, s) : s;
1071 } else if(s = dstr(dlook(info, "length"))){
1072 f = mallocz(sizeof(*f), 1);
1074 f->name = dstr(dlook(info, "name"));
1078 for(f = files; f; f = f->next){
1079 if(f->name == nil || f->len <= 0)
1080 sysfatal("bogus file entry in meta info");
1081 s = fixnamedup(f->name);
1082 if(vflag) fprint(pflag ? 2 : 1, "%s\n", s);
1083 if((f->fd = open(s, ORDWR)) < 0){
1085 sysfatal("mkdirs: %r");
1086 if((f->fd = create(s, ORDWR, 0666)) < 0)
1087 sysfatal("create: %r");
1093 sysfatal("no files in torrent");
1095 if((s = dstr(dlook(info, "piece length"))) == nil)
1096 sysfatal("missing piece length in meta info");
1097 if((blocksize = atoi(s)) <= 0)
1098 sysfatal("bogus piece length in meta info");
1099 d = dlook(info, "pieces");
1100 if(d == nil || d->typ != 's' || d->len <= 0 || d->len % 20)
1101 sysfatal("bad or no pices in meta info");
1102 npieces = d->len / 20;
1103 pieces = mallocz(sizeof(Piece) * npieces, 1);
1104 nhavemap = (npieces+7) / 8;
1105 havemap = mallocz(nhavemap, 1);
1106 for(i = 0; i<npieces; i++){
1107 pieces[i].hash = (uchar*)d->str + i*20;
1109 pieces[i].len = len;
1111 pieces[i].len = blocksize;
1112 len -= pieces[i].len;
1113 stats.left += pieces[i].len;
1116 sysfatal("pieces do not match file length");
1118 for(i = 0; i<npieces; i++)
1122 atnotify(killnote, 1);
1123 switch(i = rfork(RFPROC|RFMEM|RFNOTEG)){
1125 sysfatal("fork: %r");
1127 memmove(peerid, "-NF9001-", 8);
1128 for(i=8; i<sizeof(peerid); i++)
1129 peerid[i] = nrand(10)+'0';
1131 for(; alist; alist = alist->next)
1132 tracker(alist->str);
1133 for(f = files, l = wlist; f && l; f = f->next, l = l->next)
1135 while(waitpid() != -1)
1143 print("%d %d\n", nhavepieces, npieces);
1144 } while(!finished() || sflag);
1146 postnote(PNGROUP, killgroup, "kill");