]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/torrent.c
0770ac2265d3c30260f366a65f082e4768f9e2ab
[plan9front.git] / sys / src / cmd / ip / torrent.c
1 #include <u.h>
2 #include <libc.h>
3 #include <mp.h>
4 #include <libsec.h>
5
6 typedef struct Dict Dict;
7 typedef struct Piece Piece;
8 typedef struct File File;
9 typedef struct Stats Stats;
10
11 struct Dict
12 {
13         Dict    *val;
14         Dict    *next;
15         char    *start, *end;
16         int     len;
17         char    typ;    // i, d, s, l
18         char    str[];
19 };
20
21 struct Piece
22 {
23         uchar   *hash;
24         int     len;
25         int     brk;
26 };
27
28 struct File
29 {
30         File    *next;
31         char    *name;
32         int     fd;
33         vlong   off;
34         vlong   len;
35 };
36
37 struct Stats
38 {
39         Lock;
40         vlong   up;
41         vlong   down;
42         vlong   left;
43 };
44
45 enum {
46         MAXIO = 16*1024,
47 };
48
49 int debug;
50 int killgroup = -1;
51 int port = 6881;
52 char *deftrack = "http://exodus.desync.com/announce";
53 char *mntweb = "/mnt/web";
54 uchar infohash[20];
55 uchar peerid[20];
56 int blocksize;
57
58 int npieces;
59 Piece *pieces;
60
61 int nhavemap;
62 uchar *havemap;
63 int nhavepieces;
64
65 File *files;
66 Stats stats;
67
68 int
69 finished(void)
70 {
71         return nhavepieces >= npieces;
72 }
73
74 void
75 freedict(Dict *d)
76 {
77         if(d){
78                 if(d->val != d)
79                         freedict(d->val);
80                 freedict(d->next);
81                 free(d);
82         }
83 }
84
85 char*
86 bparse(char *s, char *e, Dict **dp)
87 {
88         char *x, t;
89         Dict *d;
90         int n;
91
92         *dp = nil;
93         if(s >= e)
94                 return e;
95
96         t = *s;
97         switch(t){
98         case 'd':
99         case 'l':
100                 x = s++;
101                 d = nil;
102                 while(s < e){
103                         if(*s == 'e'){
104                                 s++;
105                                 break;
106                         }
107                         if(t == 'd'){
108                                 s = bparse(s, e, dp);
109                                 if((d = *dp) == nil)
110                                         break;
111                         } else
112                                 d = *dp = mallocz(sizeof(*d), 1);
113                         d->typ = t;
114                         d->start = x;
115                         if(s < e){
116                                 s = bparse(s, e, &d->val);
117                                 dp = &d->next;
118                                 d->end = s;
119                         }
120                         x = s;
121                 }
122                 if(d)
123                         d->end = s;
124                 return s;
125         case 'i':
126                 x = ++s;
127                 if((s = memchr(x, 'e', e - x)) == nil)
128                         return e;
129                 n = s - x;
130                 s++;
131                 break;
132         default:
133                 if((x = memchr(s, ':', e - s)) == nil)
134                         return e;
135                 x++;
136                 if((n = atoi(s)) < 0)
137                         return e;
138                 s = x + n;
139                 if((s > e) || (s < x)){
140                         n = e - x;
141                         s = e;
142                 }
143                 t = 's';
144         }
145         d = mallocz(sizeof(*d) + n+1, 1);
146         d->typ = t;
147         memmove(d->str, x, d->len = n);
148         d->str[n] = 0;
149         *dp = d;
150         return s;
151 }
152
153 char*
154 dstr(Dict *d)
155 {
156         if(d && (d->typ == 's' || d->typ == 'i'))
157                 return d->str;
158         return nil;
159 }
160
161 Dict*
162 dlook(Dict *d, char *s)
163 {
164         for(; d && d->typ == 'd'; d = d->next)
165                 if(d->len && strcmp(d->str, s) == 0)
166                         return d->val;
167         return nil;
168 }
169
170 int
171 readall(int fd, char **p)
172 {
173         int n, r;
174
175         n = 0;
176         *p = nil;
177         while(*p = realloc(*p, n+1024)){
178                 if((r = read(fd, *p+n, 1024)) <= 0)
179                         break;
180                 n += r;
181         }
182         return n;
183 }
184
185 int
186 rwpiece(int wr, int index, uchar *data, int len, int poff)
187 {
188         vlong off;
189         int n, m;
190         File *f;
191
192         if(len <= 0 || poff >= pieces[index].len)
193                 return 0;
194         if(len+poff > pieces[index].len)
195                 len = pieces[index].len - poff;
196         off = (vlong)index * blocksize;
197         off += poff;
198         for(f = files; f; f = f->next)
199                 if((f->off+f->len) > off)
200                         break;
201         off -= f->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)
204                 return -1;
205         if((m = rwpiece(wr, index, data + n, len - n, poff + n)) < 0)
206                 return -1;
207         return n+m;
208 }
209
210 int
211 havepiece(int x)
212 {
213         uchar *p, m, hash[20];
214         int n;
215
216         m = 0x80>>(x&7);
217         if(havemap[x>>3] & m)
218                 return 1;
219         p = malloc(blocksize);
220         n = pieces[x].len;
221         if(rwpiece(0, x, p, n, 0) != n){
222                 free(p);
223                 return 0;
224         }
225         sha1(p, n, hash, nil);
226         free(p);
227         if(memcmp(hash, pieces[x].hash, 20))
228                 return 0;
229         lock(&stats);
230         if((havemap[x>>3] & m) == 0){
231                 havemap[x>>3] |= m;
232                 nhavepieces++;
233                 stats.left -= pieces[x].len;
234         }
235         unlock(&stats);
236         return 1;
237 }
238
239 int
240 pickpiece(uchar *map)
241 {
242         int i, x, r, k;
243         uchar m;
244
245         r = -1;
246         k = 0;
247         for(i = 0; i<nhavemap; i++){
248                 if(map[i] == 0)
249                         continue;
250                 for(x = i<<3, m = 0x80; m; m >>= 1, x++){
251                         if((~map[i] | havemap[i]) & m)
252                                 continue;
253                         if(nrand(++k) == 0)
254                                 r = x;
255                 }
256         }
257         return r;
258 }
259
260 int
261 unpack(uchar *s, int n, char *fmt, ...)
262 {
263         va_list arg;
264         uchar *b, *e;
265
266         b = s;
267         e = b + n;
268         va_start(arg, fmt);
269         for(; *fmt; fmt++) {
270                 switch(*fmt){
271                 case '_':
272                         s++;
273                         break;
274                 case 'b':
275                         if(s+1 > e) goto Err;
276                         *va_arg(arg, int*) = *s++;
277                         break;
278                 case 'l':
279                         if(s+4 > e) goto Err;
280                         *va_arg(arg, int*) = s[0]<<24 | s[1]<<16 | s[2]<<8 | s[3];
281                         s += 4;
282                         break;
283                 }
284         }
285         va_end(arg);
286         return s - b;
287 Err:
288         va_end(arg);
289         return -1;
290 }
291
292 int
293 pack(uchar *s, int n, char *fmt, ...)
294 {
295         va_list arg;
296         uchar *b, *e;
297         int i;
298
299         b = s;
300         e = b + n;
301         va_start(arg, fmt);
302         for(; *fmt; fmt++) {
303                 switch(*fmt){
304                 case '_':
305                         i = 0;
306                         if(0){
307                 case 'b':
308                         i = va_arg(arg, int);
309                         }
310                         if(s+1 > e) goto Err;
311                         *s++ = i & 0xFF;
312                         break;
313                 case 'l':
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;
319                         *s++ = i & 0xFF;
320                         break;
321                 case '*':
322                         i = va_arg(arg, int);
323                         if(s+i > e) goto Err;
324                         memmove(s, va_arg(arg, uchar*), i);
325                         s += i;
326                         break;
327                 }
328         }
329         va_end(arg);
330         return s - b;
331 Err:
332         va_end(arg);
333         return -1;
334 }
335
336
337
338 int
339 peer(int fd, int incoming, char *addr)
340 {
341         uchar buf[64+MAXIO], *map, *told, *p, m;
342         int mechoking, hechoking;
343         int mewant, hewant;
344         int workpiece;
345         int i, o, l, x, n;
346
347         if(debug) fprint(2, "peer %s: %s connected\n", addr, incoming ? "incoming" : "outgoing");
348
349         for(i=0; i<2; i++){
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)
357                                 return 1;
358                 }
359                 if((incoming && !i) || (!incoming && i)){
360                         n = 20 + 8 + sizeof(infohash);
361                         if((n = readn(fd, buf, n)) != n)
362                                 return 1;
363                         if(memcmp(buf, "\x13BitTorrent protocol", 20))
364                                 return 0;
365                         if(memcmp(infohash, buf + 20 + 8, sizeof(infohash)))
366                                 return 0;
367                         if(debug) fprint(2, "peer %s: <- handshake\n", addr);
368                 }
369         }
370         if(readn(fd, buf, sizeof(peerid)) != sizeof(peerid))
371                 return 1;
372         if(memcmp(peerid, buf, sizeof(peerid)) == 0)
373                 return 0;
374         if(debug) fprint(2, "peer %s: peerid %.*s\n", addr, sizeof(peerid), (char*)buf);
375
376         mechoking = 1;
377         hechoking = 1;
378         mewant = 0;
379         hewant = 0;
380         workpiece = -1;
381         map = mallocz(nhavemap, 1);
382         told = malloc(nhavemap);
383
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)
388                 goto Out;
389
390         for(;;){
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)
395                                                 continue;
396                                         told[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)
400                                                 goto Out;
401                                 }
402                         }
403                         if(!mewant && (map[i] & ~havemap[i])){
404                                 mewant = 1;
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)
408                                         goto Out;
409                         }
410                 }
411                 if(!hechoking && mewant){
412                         x = workpiece;
413                         if(x >= 0 && pieces[x].brk < pieces[x].len)
414                                 {}
415                         else x = pickpiece(map);
416                         if(x >= 0){
417                                 o = pieces[x].brk;
418                                 l = pieces[x].len - o;
419                                 if(l > MAXIO)
420                                         l = MAXIO;
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)
424                                         goto Out;
425                                 workpiece = x;
426                         }
427                 }
428                 if(mechoking && hewant){
429                         mechoking = 0;
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)
433                                 goto Out;
434                 }
435
436                 if(readn(fd, buf, 4) != 4)
437                         break;
438                 unpack(buf, 4, "l", &n);
439                 if(n < 0 || n > sizeof(buf))
440                         break;
441                 if(n == 0)
442                         continue;
443                 if(readn(fd, buf, n) != n)
444                         break;
445
446                 n--;
447                 p = buf+1;
448                 switch(*buf){
449                 case 0x00:      // Choke
450                         hechoking = 1;
451                         workpiece = -1;
452                         if(debug) fprint(2, "peer %s: <- choke\n", addr);
453                         break;
454                 case 0x01:      // Unchoke
455                         hechoking = 0;
456                         if(debug) fprint(2, "peer %s: <- unchoke\n", addr);
457                         break;
458                 case 0x02:      // Interested
459                         hewant = 1;
460                         if(debug) fprint(2, "peer %s: <- interested\n", addr);
461                         break;
462                 case 0x03:      // Notinterested
463                         hewant = 0;
464                         if(debug) fprint(2, "peer %s: <- notinterested\n", addr);
465                         break;
466                 case 0x04:      // Have <piceindex>
467                         if(unpack(p, n, "l", &x) < 0)
468                                 goto Out;
469                         if(debug) fprint(2, "peer %s: <- have %d\n", addr, x);
470                         if(x < 0 || x >= npieces)
471                                 continue;
472                         map[x>>3] |= 0x80>>(x&7);
473                         break;
474                 case 0x05:      // Bitfield
475                         if(debug) fprint(2, "peer %s: <- bitfield %d\n", addr, n);
476                         if(n != nhavemap)
477                                 continue;
478                         memmove(map, p, n);
479                         break;
480                 case 0x06:      // Request <index> <begin> <length>
481                         if(unpack(p, n, "lll", &x, &o, &l) < 0)
482                                 goto Out;
483                         if(debug) fprint(2, "peer %s: <- request %d %d %d\n", addr, x, o, l);
484                         if(x < 0 || x >= npieces)
485                                 continue;
486                         if(!hewant || mechoking || (~havemap[x>>3]&(0x80>>(x&7))))
487                                 continue;
488                         if(debug) fprint(2, "peer %s: -> piece %d %d\n", addr, x, o);
489                         n = 4+1+4+4;
490                         if(l > MAXIO)
491                                 l = MAXIO;
492                         if((l = rwpiece(0, x, buf + n, l, o)) <= 0)
493                                 continue;
494                         n = pack(buf, sizeof(buf), "lbll", 1+4+4+l, 0x07, x, o);
495                         n += l;
496                         if(write(fd, buf, n) != n)
497                                 goto Out;
498                         lock(&stats);
499                         stats.up += n;
500                         unlock(&stats);
501                         break;
502                 case 0x07:      // Piece <index> <begin> <block>
503                         if(unpack(p, n, "ll", &x, &o) != 8)
504                                 goto Out;
505                         p += 8;
506                         n -= 8;
507                         lock(&stats);
508                         stats.down += n;
509                         unlock(&stats);
510                         if(debug) fprint(2, "peer %s: <- piece %d %d %d\n", addr, x, o, n);
511                         if(x < 0 || x >= npieces)
512                                 continue;
513                         if((pieces[x].brk != o) || (havemap[x>>3]&(0x80>>(x&7))))
514                                 continue;
515                         if(rwpiece(1, x, p, n, o) == n){
516                                 if((pieces[x].brk = o+n) == pieces[x].len){
517                                         if(!havepiece(x))
518                                                 pieces[x].brk = 0;
519                                 }
520                         }
521                         break;
522                 case 0x08:      // Cancel <index> <begin> <length>
523                         if(unpack(p, n, "lll", &x, &o, &l) < 0)
524                                 goto Out;
525                         if(debug) fprint(2, "peer %s: <- cancel %d %d %d\n", addr, x, o, l);
526                         break;
527                 case 0x09:      // Port <port>
528                         if(unpack(p, n, "l", &x) < 0)
529                                 goto Out;
530                         if(debug) fprint(2, "peer %s: <- port %d\n", addr, x);
531                         break;
532                 }
533         }
534
535 Out:
536         free(told);
537         free(map);
538         return 1;
539 }
540
541 void
542 server(void)
543 {
544         char addr[64], adir[40], ldir[40];
545         int afd, lfd, dfd;
546         NetConnInfo *ni;
547
548         afd = -1;
549         for(port=6881; port<6890; port++){
550                 snprint(addr, sizeof(addr), "tcp!*!%d", port);
551                 if((afd = announce(addr, adir)) >= 0)
552                         break;
553         }
554         if(afd < 0){
555                 fprint(2, "announce: %r");
556                 return;
557         }
558         if(rfork(RFFDG|RFPROC|RFMEM))
559                 return;
560         for(;;){
561                 if((lfd = listen(adir, ldir)) < 0){
562                         fprint(2, "listen: %r");
563                         break;
564                 }
565                 if(rfork(RFFDG|RFPROC|RFMEM)){
566                         close(lfd);
567                         continue;
568                 }
569                 if((dfd = accept(lfd, ldir)) < 0){
570                         fprint(2, "accept: %r");
571                         break;
572                 }
573                 ni = getnetconninfo(ldir, dfd);
574                 peer(dfd, 1, ni ? ni->raddr : "???");
575                 if(ni) freenetconninfo(ni);     
576                 break;
577         }
578         exits(0);
579 }
580
581 void
582 client(char *ip, char *port)
583 {
584         static Dict *peers;
585         static QLock peerslk;
586         int try, fd;
587         char *addr;
588         Dict *d;
589
590         if(ip == nil || port == nil)
591                 return;
592
593         d = mallocz(sizeof(*d) + 64, 1);
594         snprint(addr = d->str, 64, "tcp!%s!%s", ip, port);
595         qlock(&peerslk);
596         if(dlook(peers, addr)){
597                 qunlock(&peerslk);
598                 free(d);
599                 return;
600         }
601         d->len = strlen(addr);
602         d->typ = 'd';
603         d->val = d;
604         d->next = peers;
605         peers = d;
606         qunlock(&peerslk);
607
608         if(debug) fprint(2, "client %s\n", addr);
609
610         if(rfork(RFFDG|RFPROC|RFMEM))
611                 return;
612         for(try = 0; try < 10; try++){
613                 if((fd = dial(addr, nil, nil, nil)) >= 0){
614                         if(!peer(fd, 0, addr))
615                                 break;
616                         close(fd);
617                 }
618                 sleep((1000<<try)+nrand(5000));
619         }
620         exits(0);
621 }
622
623 int
624 hopen(char *url, ...)
625 {
626         int conn, ctlfd, fd, n;
627         char buf[1024+1];
628         va_list arg;
629
630         snprint(buf, sizeof buf, "%s/clone", mntweb);
631         if((ctlfd = open(buf, ORDWR)) < 0)
632                 return -1;
633         if((n = read(ctlfd, buf, sizeof buf-1)) <= 0){
634                 close(ctlfd);
635                 return -1;
636         }
637         buf[n] = 0;
638         conn = atoi(buf);
639         va_start(arg, url);
640         strcpy(buf, "url ");
641         n = 4+vsnprint(buf+4, sizeof(buf)-4, url, arg);
642         va_end(arg);
643         if(write(ctlfd, buf, n) != n){
644         ErrOut:
645                 close(ctlfd);
646                 return -1;
647         }
648         snprint(buf, sizeof buf, "%s/%d/body", mntweb, conn);
649         if((fd = open(buf, OREAD)) < 0)
650                 goto ErrOut;
651         close(ctlfd);
652         return fd;
653 }
654
655 void
656 webseed(Dict *w, File *f)
657 {
658         vlong off, woff;
659         int fd, n, m, r, p, x, y, err;
660         uchar buf[MAXIO];
661         Dict *w0;
662         char *s;
663
664         if(w == nil || f == nil || finished())
665                 return;
666         if(rfork(RFPROC|RFMEM))
667                 return;
668         w0 = w;
669 Retry:
670         if(debug) fprint(2, "webseed %s %s\n", w->str, f->name);
671         s = strrchr(w->str, '/');
672         if(s && s[1] == 0)
673                 fd = hopen("%s%s", w->str, f->name);
674         else
675                 fd = hopen("%s", w->str);
676         if(fd < 0){
677                 if(debug) fprint(2, "webseed %s %s: %r\n", w->str, f->name);
678                 if(finished())
679                         exits(0);
680                 if((w = w->next) == w0)
681                         exits(0);
682                 goto Retry;
683         }
684
685         off = 0;
686         err = 0;
687         while(off < f->len){
688                 if(finished())
689                         break;
690                 n = MAXIO;
691                 if((f->len - off) < n)
692                         n = (f->len - off);
693                 if((n = read(fd, buf, n)) <= 0)
694                         break;
695                 woff = f->off + off;
696                 x = woff / blocksize;
697                 off += n;
698                 y = (f->off + off) / blocksize;
699                 p = woff - x*blocksize;
700                 m = 0;
701                 while(m < n){
702                         r = pieces[x].len - p;
703                         if(r > (n-m))
704                                 r = n-m;
705                         if((havemap[x>>3] & (0x80>>(x&7))) == 0)
706                                 if(rwpiece(1, x, buf+m, r, p) != r)
707                                         goto Err;
708                         if(x == y)
709                                 break;
710                         m += r;
711                         p = 0;
712                         if(havepiece(x++))
713                                 continue;
714                         if(++err > 10){
715                                 fprint(2, "webseed %s %s: corrupt\n", w->str, f->name);
716                                 goto Err;
717                         }
718                 }
719         }
720
721         havepiece(f->off / blocksize);
722         havepiece((f->off+f->len) / blocksize);
723 Err:
724         close(fd);
725         exits(0);
726 }
727
728 void
729 tracker(char *url)
730 {
731         static Dict *trackers;
732         static QLock trackerslk;
733
734         char *event, *p;
735         Dict *d, *l;
736         int n, fd;
737
738         if(url == nil)
739                 return;
740
741         qlock(&trackerslk);
742         if(dlook(trackers, url)){
743                 qunlock(&trackerslk);
744                 return;
745         }
746         n = strlen(url);
747         d = mallocz(sizeof(*d) + n+1, 1);
748         strcpy(d->str, url);
749         d->len = n;
750         d->typ = 'd';
751         d->val = d;
752         d->next = trackers;
753         trackers = d;
754         url = d->str;
755         qunlock(&trackerslk);
756
757         if(debug) fprint(2, "tracker %s\n", url);
758
759         if(rfork(RFPROC|RFMEM))
760                 return;
761
762         event = "&event=started";
763         for(;;){
764                 vlong up, down, left;
765
766                 lock(&stats);
767                 up = stats.up;
768                 down = stats.down;
769                 left = stats.left;
770                 unlock(&stats);
771
772                 d = nil;
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){
777                         event = "";
778                         n = readall(fd, &p);
779                         close(fd);
780                         bparse(p, p+n, &d);
781                         free(p);
782                 } else if(debug) fprint(2, "tracker %s: %r\n", url);
783                 if(l = dlook(d, "peers")){
784                         if(l->typ == 's'){
785                                 uchar *b, *e;
786
787                                 b = (uchar*)l->str;
788                                 e = b + l->len;
789                                 for(; b+6 <= e; b += 6){
790                                         char ip[16], port[6];
791
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]);
794                                         client(ip, port);
795                                 }
796                         } else for(; l && l->typ == 'l'; l = l->next)
797                                 client(dstr(dlook(l->val, "ip")), dstr(dlook(l->val, "port")));
798                 }
799                 n = 0;
800                 if(p = dstr(dlook(d, "interval")))
801                         n = atoi(p);
802                 if(n < 10 | n > 60*60)
803                         n = 2*60;
804                 freedict(d);
805                 sleep(n * 1000 + nrand(5000));
806         }
807 }
808
809 int
810 Hfmt(Fmt *f)
811 {
812         uchar *s, *e;
813         s = va_arg(f->args, uchar*);
814         if(f->flags & FmtPrec)
815                 e = s + f->prec;
816         else
817                 e = s + strlen((char*)s);
818         for(; s < e; 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)
823                         return -1;
824         return 0;
825 }
826
827 int
828 mktorrent(int fd, Dict *alist, Dict *wlist)
829 {
830         uchar *b, h[20];
831         Dir *d;
832         int n;
833
834         if((d = dirfstat(fd)) == nil)
835                 return -1;
836         if(d->qid.type & QTDIR){
837                 free(d);
838                 werrstr("file is a directory");
839                 return -1;
840         }
841         if(d->length == 0){
842                 free(d);
843                 werrstr("empty file");
844                 return -1;
845         }
846         npieces = 1;
847         for(blocksize = 256*1024;;blocksize<<=1){
848                 npieces = (d->length + blocksize-1) / blocksize;
849                 if(npieces <= 8*1024 || blocksize >= 2*1024*1024)
850                         break;
851         }
852         print("d");
853         print("8:announce%ld:%s", strlen(alist->str), alist->str);
854         if(alist->next){
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);
859                 print("e");
860         }
861         if(wlist){
862                 if(wlist->next){
863                         print("8:url-listl");
864                         for(; wlist; wlist = wlist->next)
865                                 print("%ld:%s", strlen(wlist->str), wlist->str);
866                         print("e");
867                 } else
868                         print("8:url-list%ld:%s", strlen(wlist->str), wlist->str);
869         }
870         print("4:info");
871         print("d");
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));
876         free(d);
877         b = malloc(blocksize);
878         while((n = readn(fd, b, blocksize)) > 0){
879                 sha1(b, n, h, nil);
880                 if(write(1, h, sizeof(h)) != sizeof(h)){
881                         free(b);
882                         return -1;
883                 }
884                 npieces--;
885         }
886         free(b);
887         if(npieces){
888                 werrstr("read failed: %r");
889                 return -1;
890         }
891         print("e");
892         print("e");
893         return 0;
894 }
895
896 int
897 mkdirs(char *s)
898 {
899         char *p;
900         int f;
901
902         if(access(s, AEXIST) == 0)
903                 return 0;
904         for(p=strchr(s+1, '/'); p; p=strchr(p+1, '/')){
905                 *p = 0;
906                 if(access(s, AEXIST)){
907                         if((f = create(s, OREAD, DMDIR | 0777)) < 0){
908                                 *p = '/';
909                                 return -1;
910                         }
911                         close(f);
912                 }
913                 *p = '/';
914         }
915         return 0;
916 }
917
918 char*
919 fixnamedup(char *s)
920 {
921         int n, l;
922         char *d;
923         Rune r;
924
925         n = 0;
926         d = strdup(s);
927         l = strlen(d);
928         while(*s){
929                 s += chartorune(&r, s);
930                 if(r == ' ')
931                         r = 0xa0;
932                 if((n + runelen(r)) >= l){
933                         l += 64;
934                         d = realloc(d, l);
935                 }
936                 n += runetochar(d + n, &r);
937         }
938         d[n] = 0;
939         return cleanname(d);
940 }
941
942 int
943 killnote(void *, char *)
944 {
945         postnote(PNGROUP, killgroup, "kill");
946         return 0;
947 }
948
949 void
950 usage(void)
951 {
952         fprint(2, "usage: %s [ -vsdpc ] [ -m mtpt ] [ -t tracker-url ] "
953                   "[ -w webseed-url ] [ file ]\n", argv0);
954         exits("usage");
955 }
956
957 Dict*
958 scons(char *s, Dict *t)
959 {
960         Dict *l;
961
962         if(s == nil)
963                 return t;
964         for(l = t; l; l = l->next)
965                 if(strcmp(l->str, s) == 0)
966                         return t;
967         l = mallocz(sizeof(*l) + strlen(s)+1, 1);
968         l->next = t;
969         strcpy(l->str, s);
970         return l;
971 }
972
973 void
974 main(int argc, char *argv[])
975 {
976         int sflag, pflag, vflag, cflag, fd, i, n;
977         Dict *alist, *wlist, *info, *torrent, *d, *l;
978         char *p, *s, *e;
979         File **fp, *f;
980         vlong len;
981
982         fmtinstall('H', Hfmt);
983         alist = wlist = nil;
984         sflag = pflag = vflag = cflag = 0;
985         ARGBEGIN {
986         case 'm':
987                 mntweb = EARGF(usage());
988                 break;
989         case 't':
990                 alist = scons(EARGF(usage()), alist);
991                 break;
992         case 'w':
993                 wlist = scons(EARGF(usage()), wlist);
994                 break;
995         case 's':
996                 sflag = 1;
997                 break;
998         case 'p':
999                 pflag = 1;
1000                 break;
1001         case 'v':
1002                 vflag = 1;
1003                 break;
1004         case 'c':
1005                 cflag = 1;
1006                 break;
1007         case 'd':
1008                 debug++;
1009                 break;
1010         default:
1011                 usage();
1012         } ARGEND;
1013
1014         fd = 0;
1015         if(*argv)
1016                 if((fd = open(*argv, OREAD)) < 0)
1017                         sysfatal("open: %r");
1018         if(cflag){
1019                 if(alist == nil)
1020                         alist = scons(deftrack, alist);
1021                 if(mktorrent(fd, alist, wlist) < 0)
1022                         sysfatal("%r");
1023                 exits(0);
1024         }
1025         if((n = readall(fd, &p)) <= 0)
1026                 sysfatal("read torrent: %r");
1027         bparse(p, p+n, &torrent);
1028
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);
1033
1034         if(d = dlook(torrent, "url-list")){
1035                 if(d->typ == 's')
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)
1041                         ;
1042                 if(l) l->next = wlist;
1043         }
1044
1045         if(alist == nil && wlist == nil)
1046                 sysfatal("no trackers or webseeds in torrent");
1047
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)
1051                 e = d->end;
1052         sha1((uchar*)s, e - s, (uchar*)infohash, nil);
1053         free(p);
1054
1055         fp = &files;
1056         if(d = dlook(info, "files")){           
1057                 for(; d && d->typ == 'l'; d = d->next){
1058                         Dict *di;
1059
1060                         if((s = dstr(dlook(d->val, "length"))) == nil)
1061                                 continue;
1062                         f = mallocz(sizeof(*f), 1);
1063                         f->len = atoll(s);
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;
1068                         *fp = f;
1069                         fp = &f->next;
1070                 }
1071         } else if(s = dstr(dlook(info, "length"))){
1072                 f = mallocz(sizeof(*f), 1);
1073                 f->len = atoll(s);
1074                 f->name = dstr(dlook(info, "name"));
1075                 *fp = f;
1076         }
1077         len = 0;
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){
1084                         if(mkdirs(s) < 0)
1085                                 sysfatal("mkdirs: %r");
1086                         if((f->fd = create(s, ORDWR, 0666)) < 0)
1087                                 sysfatal("create: %r");
1088                 }
1089                 f->off = len;
1090                 len += f->len;
1091         }
1092         if(len <= 0)
1093                 sysfatal("no files in torrent");
1094
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;
1108                 if(len < blocksize)
1109                         pieces[i].len = len;
1110                 else
1111                         pieces[i].len = blocksize;
1112                 len -= pieces[i].len;
1113                 stats.left += pieces[i].len;
1114         }
1115         if(len)
1116                 sysfatal("pieces do not match file length");
1117
1118         for(i = 0; i<npieces; i++)
1119                 havepiece(i);
1120
1121         srand(time(0));
1122         atnotify(killnote, 1);
1123         switch(i = rfork(RFPROC|RFMEM|RFNOTEG)){
1124         case -1:
1125                 sysfatal("fork: %r");
1126         case 0:
1127                 memmove(peerid, "-NF9001-", 8);
1128                 for(i=8; i<sizeof(peerid); i++)
1129                         peerid[i] = nrand(10)+'0';
1130                 server();
1131                 for(; alist; alist = alist->next)
1132                         tracker(alist->str);
1133                 for(f = files, l = wlist; f && l; f = f->next, l = l->next)
1134                         webseed(l, f);
1135                 while(waitpid() != -1)
1136                         ;
1137                 break;
1138         default:
1139                 killgroup = i;
1140                 do {
1141                         sleep(1000);
1142                         if(pflag)
1143                                 print("%d %d\n", nhavepieces, npieces);
1144                 } while(!finished() || sflag);
1145         }
1146         postnote(PNGROUP, killgroup, "kill");
1147         exits(0);
1148 }