]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/torrent.c
ip/torrent: fix size check in 64-bit "v" unpack (thanks pr)
[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         SRVPROCS = 16,
48         CLIPROCS = 16,
49 };
50
51 int debug;
52 int nproc = 1;
53 int killgroup = -1;
54 int port = 6881;
55 char *deftrack = "http://exodus.desync.com/announce";
56 char *mntweb = "/mnt/web";
57 char *useragent = "torrent";
58 uchar infohash[SHA1dlen];
59 uchar peerid[20];
60 int blocksize;
61
62 int npieces;
63 Piece *pieces;
64
65 int nhavemap;
66 uchar *havemap;
67 int nhavepieces;
68
69 File *files;
70 Stats stats;
71
72 int
73 finished(void)
74 {
75         return nhavepieces >= npieces;
76 }
77
78 void
79 freedict(Dict *d)
80 {
81         if(d){
82                 if(d->val != d)
83                         freedict(d->val);
84                 freedict(d->next);
85                 free(d);
86         }
87 }
88
89 char*
90 bparse(char *s, char *e, Dict **dp)
91 {
92         char *x, t;
93         Dict *d;
94         int n;
95
96         *dp = nil;
97         if(s >= e)
98                 return e;
99
100         t = *s;
101         switch(t){
102         case 'd':
103         case 'l':
104                 x = s++;
105                 d = nil;
106                 while(s < e){
107                         if(*s == 'e'){
108                                 s++;
109                                 break;
110                         }
111                         if(t == 'd'){
112                                 s = bparse(s, e, dp);
113                                 if((d = *dp) == nil)
114                                         break;
115                         } else
116                                 d = *dp = mallocz(sizeof(*d), 1);
117                         d->typ = t;
118                         d->start = x;
119                         if(s < e){
120                                 s = bparse(s, e, &d->val);
121                                 dp = &d->next;
122                                 d->end = s;
123                         }
124                         x = s;
125                 }
126                 if(d)
127                         d->end = s;
128                 return s;
129         case 'i':
130                 x = ++s;
131                 if((s = memchr(x, 'e', e - x)) == nil)
132                         return e;
133                 n = s - x;
134                 s++;
135                 break;
136         default:
137                 if((x = memchr(s, ':', e - s)) == nil)
138                         return e;
139                 x++;
140                 if((n = atoi(s)) < 0)
141                         return e;
142                 s = x + n;
143                 if((s > e) || (s < x)){
144                         n = e - x;
145                         s = e;
146                 }
147                 t = 's';
148         }
149         d = mallocz(sizeof(*d) + n+1, 1);
150         d->typ = t;
151         memmove(d->str, x, d->len = n);
152         d->str[n] = 0;
153         *dp = d;
154         return s;
155 }
156
157 char*
158 dstr(Dict *d)
159 {
160         if(d && (d->typ == 's' || d->typ == 'i'))
161                 return d->str;
162         return nil;
163 }
164
165 Dict*
166 dlook(Dict *d, char *s)
167 {
168         for(; d && d->typ == 'd'; d = d->next)
169                 if(d->len && strcmp(d->str, s) == 0)
170                         return d->val;
171         return nil;
172 }
173
174 int
175 readall(int fd, char **p)
176 {
177         int n, r;
178
179         n = 0;
180         *p = nil;
181         while(*p = realloc(*p, n+1024)){
182                 if((r = read(fd, *p+n, 1024)) <= 0)
183                         break;
184                 n += r;
185         }
186         return n;
187 }
188
189 int
190 rwpiece(int wr, int index, uchar *data, int len, int poff)
191 {
192         vlong off;
193         int n, m;
194         File *f;
195
196         if(len <= 0 || poff < 0 || poff >= pieces[index].len)
197                 return 0;
198         if(len+poff > pieces[index].len)
199                 len = pieces[index].len - poff;
200         off = (vlong)index * (vlong)blocksize;
201         off += poff;
202         for(f = files; f; f = f->next)
203                 if((f->off+f->len) > off)
204                         break;
205         off -= f->off;
206         n = ((off + len) > f->len) ? f->len - off : len;
207         if((n = (wr ? pwrite(f->fd, data, n, off) : pread(f->fd, data, n, off))) <= 0)
208                 return -1;
209         if((m = rwpiece(wr, index, data + n, len - n, poff + n)) < 0)
210                 return -1;
211         return n+m;
212 }
213
214 int
215 havepiece(int x, char *from)
216 {
217         uchar *p, m, hash[SHA1dlen];
218         int n;
219
220         m = 0x80>>(x&7);
221         if(havemap[x>>3] & m)
222                 return 1;
223         p = malloc(blocksize);
224         n = pieces[x].len;
225         if(rwpiece(0, x, p, n, 0) != n){
226                 free(p);
227                 return 0;
228         }
229         sha1(p, n, hash, nil);
230         free(p);
231         if(memcmp(hash, pieces[x].hash, sizeof(hash))){
232                 if(debug && from != nil)
233                         fprint(2, "peer %s: damaged piece %d\n", from, x);
234                 return 0;
235         }
236         lock(&stats);
237         if((havemap[x>>3] & m) == 0){
238                 havemap[x>>3] |= m;
239                 nhavepieces++;
240                 stats.left -= pieces[x].len;
241         }
242         unlock(&stats);
243         if(debug && from != nil)
244                 fprint(2, "peer %s: completed piece %d\n", from, x);
245         return 1;
246 }
247
248 int
249 pickpiece(uchar *map)
250 {
251         int i, x, r, k;
252         uchar m;
253
254         r = -1;
255         k = 0;
256         for(i = 0; i<nhavemap; i++){
257                 if(map[i] == 0)
258                         continue;
259                 for(x = i<<3, m = 0x80; m; m >>= 1, x++){
260                         if((~map[i] | havemap[i]) & m)
261                                 continue;
262                         if(nrand(++k) == 0)
263                                 r = x;
264                 }
265         }
266         return r;
267 }
268
269 int
270 unpack(uchar *s, int n, char *fmt, ...)
271 {
272         va_list arg;
273         uchar *b, *e;
274
275         b = s;
276         e = b + n;
277         va_start(arg, fmt);
278         for(; *fmt; fmt++) {
279                 switch(*fmt){
280                 case '_':
281                         s++;
282                         break;
283                 case 'b':
284                         if(s+1 > e) goto Err;
285                         *va_arg(arg, int*) = *s++;
286                         break;
287                 case 'w':
288                         if(s+2 > e) goto Err;
289                         *va_arg(arg, int*) = s[0]<<8 | s[1];
290                         s += 2;
291                         break;
292                 case 'l':
293                         if(s+4 > e) goto Err;
294                         *va_arg(arg, int*) = s[0]<<24 | s[1]<<16 | s[2]<<8 | s[3];
295                         s += 4;
296                         break;
297                 case 'v':
298                         if(s+8 > e) goto Err;
299                         *va_arg(arg, vlong*) = 
300                                 (vlong)s[0]<<56 | 
301                                 (vlong)s[1]<<48 | 
302                                 (vlong)s[2]<<40 |
303                                 (vlong)s[3]<<32 |
304                                 (vlong)s[4]<<24 |
305                                 (vlong)s[5]<<16 | 
306                                 (vlong)s[6]<<8 | 
307                                 (vlong)s[7];
308                         s += 8;
309                         break;
310                 }
311         }
312         va_end(arg);
313         return s - b;
314 Err:
315         va_end(arg);
316         return -1;
317 }
318
319 int
320 pack(uchar *s, int n, char *fmt, ...)
321 {
322         va_list arg;
323         uchar *b, *e;
324         vlong v;
325         int i;
326
327         b = s;
328         e = b + n;
329         va_start(arg, fmt);
330         for(; *fmt; fmt++) {
331                 switch(*fmt){
332                 case '_':
333                         i = 0;
334                         if(0){
335                 case 'b':
336                         i = va_arg(arg, int);
337                         }
338                         if(s+1 > e) goto Err;
339                         *s++ = i & 0xFF;
340                         break;
341                 case 'w':
342                         i = va_arg(arg, int);
343                         if(s+2 > e) goto Err;
344                         *s++ = (i>>8) & 0xFF;
345                         *s++ = i & 0xFF;
346                         break;
347                 case 'l':
348                         i = va_arg(arg, int);
349                         if(s+4 > e) goto Err;
350                         *s++ = (i>>24) & 0xFF;
351                         *s++ = (i>>16) & 0xFF;
352                         *s++ = (i>>8) & 0xFF;
353                         *s++ = i & 0xFF;
354                         break;
355                 case 'v':
356                         v = va_arg(arg, vlong);
357                         if(s+8 > e) goto Err;
358                         *s++ = (v>>56) & 0xFF;
359                         *s++ = (v>>48) & 0xFF;
360                         *s++ = (v>>40) & 0xFF;
361                         *s++ = (v>>32) & 0xFF;
362                         *s++ = (v>>24) & 0xFF;
363                         *s++ = (v>>16) & 0xFF;
364                         *s++ = (v>>8) & 0xFF;
365                         *s++ = v & 0xFF;
366                         break;
367                 case '*':
368                         i = va_arg(arg, int);
369                         if(s+i > e) goto Err;
370                         memmove(s, va_arg(arg, void*), i);
371                         s += i;
372                         break;
373                 }
374         }
375         va_end(arg);
376         return s - b;
377 Err:
378         va_end(arg);
379         return -1;
380 }
381
382
383
384 int
385 peer(int fd, int incoming, char *addr)
386 {
387         uchar buf[64+MAXIO], *map, *told, *p, m;
388         int mechoking, hechoking;
389         int mewant, hewant;
390         int workpiece, workoffset;
391         int i, o, l, x, n;
392
393         if(debug) fprint(2, "peer %s: %s connected\n", addr, incoming ? "incoming" : "outgoing");
394
395         for(i=0; i<2; i++){
396                 if((incoming && i) || (!incoming && !i)){
397                         if(debug) fprint(2, "peer %s: -> handshake\n", addr);
398                         n = pack(buf, sizeof(buf), "*________**", 
399                                 20, "\x13BitTorrent protocol",
400                                 sizeof(infohash), infohash,
401                                 sizeof(peerid), peerid);
402                         if(write(fd, buf, n) != n)
403                                 return 1;
404                 }
405                 if((incoming && !i) || (!incoming && i)){
406                         n = 20 + 8 + sizeof(infohash);
407                         if((n = readn(fd, buf, n)) != n)
408                                 return 1;
409                         if(memcmp(buf, "\x13BitTorrent protocol", 20))
410                                 return 0;
411                         if(memcmp(infohash, buf + 20 + 8, sizeof(infohash)))
412                                 return 0;
413                         if(debug) fprint(2, "peer %s: <- handshake\n", addr);
414                 }
415         }
416         if(readn(fd, buf, sizeof(peerid)) != sizeof(peerid))
417                 return 1;
418         if(memcmp(peerid, buf, sizeof(peerid)) == 0)
419                 return 0;
420         if(debug) fprint(2, "peer %s: peerid %.*s\n", addr, sizeof(peerid), (char*)buf);
421
422         mechoking = 1;
423         hechoking = 1;
424         mewant = 0;
425         hewant = 0;
426         workpiece = -1;
427         workoffset = 0;
428
429         map = mallocz(nhavemap, 1);
430         told = malloc(nhavemap);
431
432         if(debug) fprint(2, "peer %s: -> bitfield %d\n", addr, nhavemap);
433         memmove(told, havemap, nhavemap);
434         n = pack(buf, sizeof(buf), "lb*", nhavemap+1, 0x05, nhavemap, told);
435         if(write(fd, buf, n) != n)
436                 goto Out;
437
438         for(;;){
439                 for(i=0; i<nhavemap; i++){
440                         if(told[i] != havemap[i]){
441                                 for(x = i<<3, m = 0x80; m; m >>= 1, x++){
442                                         if((~havemap[i] | told[i] | map[i]) & m)
443                                                 continue;
444                                         told[i] |= m;
445                                         if(debug) fprint(2, "peer %s: -> have %d\n", addr, x);
446                                         n = pack(buf, sizeof(buf), "lbl", 1+4, 0x04, x);
447                                         if(write(fd, buf, n) != n)
448                                                 goto Out;
449                                 }
450                         }
451                         if(!mewant && (map[i] & ~havemap[i])){
452                                 mewant = 1;
453                                 if(debug) fprint(2, "peer %s: -> interested\n", addr);
454                                 n = pack(buf, sizeof(buf), "lb", 1, 0x02);
455                                 if(write(fd, buf, n) != n)
456                                         goto Out;
457                         }
458                 }
459                 if(!hechoking && mewant){
460                         x = workpiece;
461                         if(x < 0 || (havemap[x>>3]&(0x80>>(x&7))) != 0 || workoffset >= pieces[x].len)
462                                 x = pickpiece(map);
463                         if(x >= 0){
464                                 o = workpiece != x ? pieces[x].brk : workoffset;
465                                 l = pieces[x].len - o;
466                                 if(l > MAXIO)
467                                         l = MAXIO;
468                                 workpiece = x;
469                                 workoffset = o + l; 
470                                 if(debug) fprint(2, "peer %s: -> request %d %d %d\n", addr, x, o, l);
471                                 n = pack(buf, sizeof(buf), "lblll", 1+4+4+4, 0x06, x, o, l);
472                                 if(write(fd, buf, n) != n)
473                                         goto Out;
474                         }
475                 }
476                 if(mechoking && hewant){
477                         mechoking = 0;
478                         if(debug) fprint(2, "peer %s: -> unchoke\n", addr);
479                         n = pack(buf, sizeof(buf), "lb", 1, 0x01);
480                         if(write(fd, buf, n) != n)
481                                 goto Out;
482                 }
483
484                 if(readn(fd, buf, 4) != 4)
485                         break;
486                 unpack(buf, 4, "l", &n);
487                 if(n < 0 || n > sizeof(buf))
488                         break;
489                 if(n == 0)
490                         continue;
491                 if(readn(fd, buf, n) != n)
492                         break;
493
494                 n--;
495                 p = buf+1;
496                 switch(*buf){
497                 case 0x00:      // Choke
498                         hechoking = 1;
499                         workpiece = -1;
500                         if(debug) fprint(2, "peer %s: <- choke\n", addr);
501                         break;
502                 case 0x01:      // Unchoke
503                         hechoking = 0;
504                         if(debug) fprint(2, "peer %s: <- unchoke\n", addr);
505                         break;
506                 case 0x02:      // Interested
507                         hewant = 1;
508                         if(debug) fprint(2, "peer %s: <- interested\n", addr);
509                         break;
510                 case 0x03:      // Notinterested
511                         hewant = 0;
512                         if(debug) fprint(2, "peer %s: <- notinterested\n", addr);
513                         break;
514                 case 0x04:      // Have <piceindex>
515                         if(unpack(p, n, "l", &x) < 0)
516                                 goto Out;
517                         if(debug) fprint(2, "peer %s: <- have %d\n", addr, x);
518                         if(x < 0 || x >= npieces)
519                                 continue;
520                         map[x>>3] |= 0x80>>(x&7);
521                         break;
522                 case 0x05:      // Bitfield
523                         if(debug) fprint(2, "peer %s: <- bitfield %d\n", addr, n);
524                         if(n != nhavemap)
525                                 continue;
526                         memmove(map, p, n);
527                         break;
528                 case 0x06:      // Request <index> <begin> <length>
529                         if(unpack(p, n, "lll", &x, &o, &l) < 0)
530                                 goto Out;
531                         if(debug) fprint(2, "peer %s: <- request %d %d %d\n", addr, x, o, l);
532                         if(x < 0 || x >= npieces)
533                                 continue;
534                         if(!hewant || mechoking || (~havemap[x>>3]&(0x80>>(x&7))))
535                                 continue;
536                         if(debug) fprint(2, "peer %s: -> piece %d %d\n", addr, x, o);
537                         n = 4+1+4+4;
538                         if(l > MAXIO)
539                                 l = MAXIO;
540                         if((l = rwpiece(0, x, buf + n, l, o)) <= 0)
541                                 continue;
542                         n = pack(buf, sizeof(buf), "lbll", 1+4+4+l, 0x07, x, o);
543                         n += l;
544                         if(write(fd, buf, n) != n)
545                                 goto Out;
546                         lock(&stats);
547                         stats.up += n;
548                         unlock(&stats);
549                         break;
550                 case 0x07:      // Piece <index> <begin> <block>
551                         if(unpack(p, n, "ll", &x, &o) != 8)
552                                 goto Out;
553                         p += 8;
554                         n -= 8;
555                         lock(&stats);
556                         stats.down += n;
557                         unlock(&stats);
558                         if(debug) fprint(2, "peer %s: <- piece %d %d %d\n", addr, x, o, n);
559                         if(x < 0 || x >= npieces)
560                                 continue;
561                         if((havemap[x>>3]&(0x80>>(x&7))) != 0)
562                                 continue;
563                         if(o < 0 || o >= pieces[x].len)
564                                 continue;
565                         if(o+n > pieces[x].len)
566                                 n = o - pieces[x].len;
567                         if((o > pieces[x].brk) || (o+n <= pieces[x].brk))
568                                 continue;
569                         n = rwpiece(1, x, p, n, o);
570                         if(n <= 0)
571                                 continue;
572                         pieces[x].brk = o+n;
573                         if(o+n >= pieces[x].len && !havepiece(x, addr)){
574                                 pieces[x].brk = 0;
575                                 /* backoff from this piece for a while */
576                                 if(x == workpiece)
577                                         workpiece = -1;
578                         }
579                         break;
580                 case 0x08:      // Cancel <index> <begin> <length>
581                         if(unpack(p, n, "lll", &x, &o, &l) < 0)
582                                 goto Out;
583                         if(debug) fprint(2, "peer %s: <- cancel %d %d %d\n", addr, x, o, l);
584                         break;
585                 case 0x09:      // Port <port>
586                         if(unpack(p, n, "l", &x) < 0)
587                                 goto Out;
588                         if(debug) fprint(2, "peer %s: <- port %d\n", addr, x);
589                         break;
590                 }
591         }
592
593 Out:
594         free(told);
595         free(map);
596         return 1;
597 }
598
599 void
600 server(void)
601 {
602         char addr[64], adir[40], ldir[40];
603         int afd, lfd, dfd, pid, nprocs;
604         NetConnInfo *ni;
605
606         afd = -1;
607         nprocs = 0;
608         for(port=6881; port<6890; port++){
609                 snprint(addr, sizeof(addr), "tcp!*!%d", port);
610                 if((afd = announce(addr, adir)) >= 0)
611                         break;
612         }
613         if(afd < 0){
614                 fprint(2, "announce: %r");
615                 return;
616         }
617         if(rfork(RFFDG|RFPROC|RFMEM))
618                 return;
619         for(;;){
620                 if((lfd = listen(adir, ldir)) < 0){
621                         fprint(2, "listen: %r");
622                         break;
623                 }
624                 while(nprocs >= SRVPROCS)
625                         if(waitpid() > 0)
626                                 nprocs--;
627                 nprocs++;
628                 if(pid = rfork(RFFDG|RFPROC|RFMEM)){
629                         if(pid < 0)
630                                 nprocs--;
631                         close(lfd);
632                         continue;
633                 }
634                 if((dfd = accept(lfd, ldir)) < 0){
635                         fprint(2, "accept: %r");
636                         break;
637                 }
638                 ni = getnetconninfo(ldir, dfd);
639                 peer(dfd, 1, ni ? ni->raddr : "???");
640                 if(ni) freenetconninfo(ni);
641                 break;  
642         }
643         exits(0);
644 }
645
646 void
647 client(char *ip, char *port)
648 {
649         static Dict *peerqh, *peerqt;
650         static QLock peerslk;
651         static int nprocs;
652         char *addr;
653         Dict *d;
654         int fd;
655
656         if(ip == nil || port == nil)
657                 return;
658
659         d = mallocz(sizeof(*d) + 64, 1);
660         snprint(addr = d->str, 64, "tcp!%s!%s", ip, port);
661         qlock(&peerslk);
662         if(dlook(peerqh, addr)){
663                 qunlock(&peerslk);
664                 free(d);
665                 return;
666         }
667         d->len = strlen(addr);
668         d->typ = 'd';
669         d->val = d;
670         /* enqueue to front */
671         if((d->next = peerqh) == nil)
672                 peerqt = d;
673         peerqh = d;
674         if(nprocs >= CLIPROCS){
675                 qunlock(&peerslk);
676                 return;
677         }
678         nprocs++;
679         qunlock(&peerslk);
680         if(rfork(RFFDG|RFPROC|RFMEM|RFNOWAIT))
681                 return;
682
683         for(;;){
684                 qlock(&peerslk);
685                 /* dequeue and put to tail */
686                 if(d = peerqh){
687                         if((peerqh = d->next) == nil)
688                                 peerqt = nil;
689                         d->next = nil;
690                         if(peerqt)
691                                 peerqt->next = d;
692                         else
693                                 peerqh = d;
694                         peerqt = d;
695                 } else
696                         nprocs--;
697                 qunlock(&peerslk);
698                 if(d == nil)
699                         exits(0);
700                 addr = d->str;
701                 if(debug) fprint(2, "client %s\n", addr);
702                 if((fd = dial(addr, nil, nil, nil)) >= 0){
703                         peer(fd, 0, addr);
704                         close(fd);
705                 }
706                 sleep(1000+nrand(5000));
707         }
708 }
709
710 int
711 hopen(char *url, ...)
712 {
713         int conn, ctlfd, fd, n;
714         char buf[1024+1];
715         va_list arg;
716
717         snprint(buf, sizeof buf, "%s/clone", mntweb);
718         if((ctlfd = open(buf, ORDWR)) < 0)
719                 return -1;
720         if((n = read(ctlfd, buf, sizeof buf-1)) <= 0){
721                 close(ctlfd);
722                 return -1;
723         }
724         buf[n] = 0;
725         conn = atoi(buf);
726         va_start(arg, url);
727         strcpy(buf, "url ");
728         n = 4+vsnprint(buf+4, sizeof(buf)-4, url, arg);
729         va_end(arg);
730         if(write(ctlfd, buf, n) != n){
731         ErrOut:
732                 close(ctlfd);
733                 return -1;
734         }
735         if(useragent != nil && useragent[0] != '\0'){
736                 n = snprint(buf, sizeof buf, "useragent %s", useragent);
737                 write(ctlfd, buf, n);
738         }
739         snprint(buf, sizeof buf, "%s/%d/body", mntweb, conn);
740         if((fd = open(buf, OREAD)) < 0)
741                 goto ErrOut;
742         close(ctlfd);
743         return fd;
744 }
745
746 void
747 webseed(Dict *w, File *f)
748 {
749         int fd, err, n, m, o, p, x, y;
750         uchar buf[MAXIO];
751         vlong off, len;
752         Dict *w0;
753         char *s;
754
755         if(w == nil || f == nil || finished())
756                 return;
757         if(rfork(RFPROC|RFMEM))
758                 return;
759         w0 = w;
760 Retry:
761         if(debug) fprint(2, "webseed %s %s\n", w->str, f->name);
762         s = strrchr(w->str, '/');
763         if(s && s[1] == 0)
764                 fd = hopen("%s%s", w->str, f->name);
765         else
766                 fd = hopen("%s", w->str);
767         if(fd < 0){
768 Error:
769                 if(debug) fprint(2, "webseed %s %s: %r\n", w->str, f->name);
770                 if(finished())
771                         exits(0);
772                 if((w = w->next) == w0)
773                         exits(0);
774                 goto Retry;
775         }
776
777         err = 0;
778         off = f->off;
779         len = f->len;
780         while(len > 0 && !finished()){
781                 m = sizeof(buf);
782                 if(len < m)
783                         m = len;
784                 if((n = read(fd, buf, m)) <= 0)
785                         break;
786
787                 x = off / blocksize;
788                 p = off - (vlong)x*blocksize;
789                 off += n;
790                 len -= n;
791                 y = off / blocksize;
792
793                 o = 0;
794                 while(n > 0){
795                         m = pieces[x].len - p;
796                         if(m > n)
797                                 m = n;
798                         if((havemap[x>>3] & (0x80>>(x&7))) == 0)
799                                 rwpiece(1, x, buf+o, m, p);
800                         if(x == y)
801                                 break;
802                         o += m;
803                         n -= m;
804                         p = 0;
805                         if(havepiece(x++, w->str))
806                                 continue;
807                         if(++err > 10){
808                                 close(fd);
809                                 werrstr("file corrupted");
810                                 goto Error;
811                         }
812                 }
813         }
814         if(off < f->off + f->len)
815                 havepiece(off / blocksize, w->str);
816         havepiece(f->off / blocksize, w->str);
817         close(fd);
818         exits(0);
819 }
820
821 void
822 clients4(uchar *p, int len)
823 {
824         char ip[16], port[6];
825
826         while(len >= 6){
827                 len -= 6;
828                 snprint(ip, sizeof(ip), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
829                 snprint(port, sizeof(port), "%d", p[4]<<8 | p[5]);
830                 p += 6;
831                 client(ip, port);
832         }
833 }
834
835 void
836 webtracker(char *url)
837 {
838         char *event, *p;
839         Dict *d, *l;
840         int n, fd;
841
842         if(rfork(RFPROC|RFMEM))
843                 return;
844         if(debug) fprint(2, "webtracker %s\n", url);
845
846         event = "&event=started";
847         for(;;){
848                 vlong up, down, left;
849
850                 lock(&stats);
851                 up = stats.up;
852                 down = stats.down;
853                 left = stats.left;
854                 unlock(&stats);
855
856                 d = nil;
857                 if((fd = hopen("%s?info_hash=%.*H&peer_id=%.*H&port=%d&"
858                         "uploaded=%lld&downloaded=%lld&left=%lld&compact=1&no_peer_id=1%s",
859                         url, sizeof(infohash), infohash, sizeof(peerid), peerid, port,
860                         up, down, left, event)) >= 0){
861                         event = "";
862                         n = readall(fd, &p);
863                         close(fd);
864                         bparse(p, p+n, &d);
865                         free(p);
866                 } else if(debug) fprint(2, "tracker %s: %r\n", url);
867                 /* check errors and warnings */
868                 if(p = dstr(dlook(d, "failure reason"))) {
869                         if(debug)
870                                 fprint(2, "tracker failure: %s\n", p);
871                         exits(0);
872                 }
873                 if(p = dstr(dlook(d, "warning message")))
874                         if(debug)
875                                 fprint(2, "tracker warning: %s\n", p);
876                 if(l = dlook(d, "peers")){
877                         if(l->typ == 's')
878                                 clients4((uchar*)l->str, l->len);
879                         else for(; l && l->typ == 'l'; l = l->next)
880                                 client(dstr(dlook(l->val, "ip")), dstr(dlook(l->val, "port")));
881                 }
882                 n = 0;
883                 if(p = dstr(dlook(d, "interval")))
884                         n = atoi(p);
885                 if(n < 10 | n > 60*60)
886                         n = 2*60;
887                 freedict(d);
888                 sleep(n * 1000 + nrand(5000));
889         }
890 }
891
892 int
893 udpaddr(char addr[64], int naddr, char *url)
894 {
895         int port;
896         char *x;
897
898         if((url = strchr(url, ':')) == nil)
899                 return -1;
900         url++;
901         while(*url == '/')
902                 url++;
903         if(x = strchr(url, ':')){
904                 port = atoi(x+1);
905         } else {
906                 port = 80;
907                 if((x = strchr(url, '/')) == nil)
908                         x = strchr(url, 0);
909         }
910         snprint(addr, naddr, "udp!%.*s!%d", utfnlen(url, x-url), url, port);
911         return 0;
912 }
913
914 void
915 udptracker(char *url)
916 {
917         int fd, event, n, m, a, i;
918         int transid, interval;
919         vlong connid;
920         uchar buf[MAXIO];
921         char addr[64];
922
923         if(udpaddr(addr, sizeof(addr), url) < 0)
924                 return;
925         if(rfork(RFPROC|RFMEM))
926                 return;
927         if(debug) fprint(2, "udptracker %s\n", addr);
928
929         event = 1;
930         for(;;){
931                 alarm(30000);
932                 if((fd = dial(addr, 0, 0, 0)) < 0)
933                         goto Sleep;
934
935                 /* connect */
936                 transid = rand();
937                 n = pack(buf, sizeof(buf), "vll", 0x41727101980LL, 0, transid);
938                 if(write(fd, buf, n) != n)
939                         goto Sleep;
940                 for(;;){
941                         if((n = read(fd, buf, sizeof(buf))) <= 0)
942                                 goto Sleep;
943                         if(unpack(buf, n, "llv", &a, &i, &connid) < 0)
944                                 continue;
945                         if(a == 0 && i == transid)
946                                 break;
947                 }
948                 alarm(0);
949
950                 /* announce */
951                 transid = rand();
952                 lock(&stats);
953                 n = pack(buf, sizeof(buf), "vll**vvvl____llw",
954                         connid, 1, transid,
955                         sizeof(infohash), infohash,
956                         sizeof(peerid), peerid,
957                         stats.down,
958                         stats.left,
959                         stats.up,
960                         event,
961                         0, -1,
962                         port);
963                 unlock(&stats);
964
965                 interval = 0;
966                 alarm(30000);
967                 if(write(fd, buf, n) != n)
968                         goto Sleep;
969                 for(;;){
970                         if((n = read(fd, buf, sizeof(buf))) <= 0)
971                                 goto Sleep;
972                         if((m = unpack(buf, n, "lll________", &a, &i, &interval)) < 0)
973                                 continue;
974                         if(a == 1 && i == transid){
975                                 clients4(buf+m, n - m);
976                                 break;
977                         }
978                 }
979                 event = 0;
980 Sleep:
981                 alarm(0);
982                 if(fd >= 0)
983                         close(fd);
984                 if(interval < 10 | interval > 60*60)
985                         interval = 2*60;
986                 sleep(interval * 1000 + nrand(5000));
987         }
988 }
989
990 void
991 tracker(char *url)
992 {
993         static Dict *trackers;
994         static QLock trackerslk;
995         Dict *d;
996         int n;
997
998         if(url == nil)
999                 return;
1000         qlock(&trackerslk);
1001         if(dlook(trackers, url)){
1002                 qunlock(&trackerslk);
1003                 return;
1004         }
1005         n = strlen(url);
1006         d = mallocz(sizeof(*d) + n+1, 1);
1007         strcpy(d->str, url);
1008         d->len = n;
1009         d->typ = 'd';
1010         d->val = d;
1011         d->next = trackers;
1012         trackers = d;
1013         url = d->str;
1014         qunlock(&trackerslk);
1015         if(!cistrncmp(url, "udp:", 4))
1016                 udptracker(url);
1017         else
1018                 webtracker(url);
1019 }
1020
1021 int
1022 Hfmt(Fmt *f)
1023 {
1024         uchar *s, *e;
1025         s = va_arg(f->args, uchar*);
1026         if(f->flags & FmtPrec)
1027                 e = s + f->prec;
1028         else
1029                 e = s + strlen((char*)s);
1030         for(; s < e; s++)
1031                 if(fmtprint(f, *s && ((*s >= '0' && *s <= '9') || 
1032                         (*s >= 'a' && *s <= 'z') ||
1033                         (*s >= 'A' && *s <= 'Z') || 
1034                         strchr(".-_~", *s)) ? "%c" : "%%%.2x", *s) < 0)
1035                         return -1;
1036         return 0;
1037 }
1038
1039 int
1040 mktorrent(int fd, Dict *alist, Dict *wlist)
1041 {
1042         uchar *b, h[SHA1dlen];
1043         Dir *d;
1044         int n;
1045
1046         if((d = dirfstat(fd)) == nil)
1047                 return -1;
1048         if(d->qid.type & QTDIR){
1049                 free(d);
1050                 werrstr("file is a directory");
1051                 return -1;
1052         }
1053         if(d->length == 0){
1054                 free(d);
1055                 werrstr("empty file");
1056                 return -1;
1057         }
1058         for(blocksize = 256*1024;;blocksize<<=1){
1059                 npieces = (d->length + blocksize-1) / blocksize;
1060                 if(npieces <= 8*1024 || blocksize >= 2*1024*1024)
1061                         break;
1062         }
1063
1064         /*
1065          * keys in dictionaries have to be ordered alphabetically
1066          */
1067         print("d8:announce%ld:%s", strlen(alist->str), alist->str);
1068         if(alist->next){
1069                 print("13:announce-listl");
1070                 print("l%ld:%se", strlen(alist->str), alist->str);
1071                 for(alist = alist->next; alist; alist = alist->next)
1072                         print("l%ld:%se", strlen(alist->str), alist->str);
1073                 print("e");
1074         }
1075
1076         print("4:infod");
1077         print("6:lengthi%llde", d->length);
1078         print("4:name%ld:%s", strlen(d->name), d->name);
1079         print("12:piece lengthi%de", blocksize);
1080         print("6:pieces%d:", npieces*sizeof(h));
1081         free(d);
1082         b = malloc(blocksize);
1083         while((n = readn(fd, b, blocksize)) > 0){
1084                 sha1(b, n, h, nil);
1085                 if(write(1, h, sizeof(h)) != sizeof(h)){
1086                         free(b);
1087                         return -1;
1088                 }
1089                 npieces--;
1090         }
1091         if(npieces){
1092                 werrstr("read failed: %r");
1093                 return -1;
1094         }
1095         free(b);
1096         print("e");
1097
1098         if(wlist){
1099                 if(wlist->next){
1100                         print("8:url-listl");
1101                         for(; wlist; wlist = wlist->next)
1102                                 print("%ld:%s", strlen(wlist->str), wlist->str);
1103                         print("e");
1104                 } else
1105                         print("8:url-list%ld:%s", strlen(wlist->str), wlist->str);
1106         }
1107         print("e");
1108
1109         return 0;
1110 }
1111
1112 int
1113 mkdirs(char *s)
1114 {
1115         char *p;
1116         int f;
1117
1118         if(access(s, AEXIST) == 0)
1119                 return 0;
1120         for(p=strchr(s+1, '/'); p; p=strchr(p+1, '/')){
1121                 *p = 0;
1122                 if(access(s, AEXIST)){
1123                         if((f = create(s, OREAD, DMDIR | 0777)) < 0){
1124                                 *p = '/';
1125                                 return -1;
1126                         }
1127                         close(f);
1128                 }
1129                 *p = '/';
1130         }
1131         return 0;
1132 }
1133
1134 char*
1135 fixnamedup(char *s)
1136 {
1137         int n, l;
1138         char *d;
1139         Rune r;
1140
1141         n = 0;
1142         d = strdup(s);
1143         l = strlen(d);
1144         while(*s){
1145                 s += chartorune(&r, s);
1146                 if(r == ' ')
1147                         r = 0xa0;
1148                 if((n + runelen(r)) >= l){
1149                         l += 64;
1150                         d = realloc(d, l);
1151                 }
1152                 n += runetochar(d + n, &r);
1153         }
1154         d[n] = 0;
1155         return cleanname(d);
1156 }
1157
1158 int
1159 catch(void *, char *msg)
1160 {
1161         if(strstr(msg, "alarm"))
1162                 return 1;
1163         postnote(PNGROUP, killgroup, "kill");
1164         return 0;
1165 }
1166
1167 void
1168 usage(void)
1169 {
1170         fprint(2, "usage: %s [ -vsdpc ] [ -m mtpt ] [ -t tracker-url ] "
1171                   "[ -w webseed-url ] [ -i peerid ] [ -A useragent ] [ file ]\n", argv0);
1172         exits("usage");
1173 }
1174
1175 Dict*
1176 scons(char *s, Dict *t)
1177 {
1178         Dict *l;
1179
1180         if(s == nil)
1181                 return t;
1182         for(l = t; l; l = l->next)
1183                 if(strcmp(l->str, s) == 0)
1184                         return t;
1185         l = mallocz(sizeof(*l) + strlen(s)+1, 1);
1186         l->next = t;
1187         strcpy(l->str, s);
1188         return l;
1189 }
1190
1191 void
1192 main(int argc, char *argv[])
1193 {
1194         int sflag, pflag, vflag, cflag, fd, i, n;
1195         Dict *alist, *wlist, *info, *torrent, *d, *l;
1196         char *p, *s, *e;
1197         File **fp, *f;
1198         vlong len;
1199
1200         fmtinstall('H', Hfmt);
1201         alist = wlist = nil;
1202         sflag = pflag = vflag = cflag = 0;
1203         ARGBEGIN {
1204         case 'm':
1205                 mntweb = EARGF(usage());
1206                 break;
1207         case 't':
1208                 alist = scons(EARGF(usage()), alist);
1209                 break;
1210         case 'w':
1211                 wlist = scons(EARGF(usage()), wlist);
1212                 break;
1213         case 's':
1214                 sflag = 1;
1215                 break;
1216         case 'p':
1217                 pflag = 1;
1218                 break;
1219         case 'v':
1220                 vflag = 1;
1221                 break;
1222         case 'c':
1223                 cflag = 1;
1224                 break;
1225         case 'd':
1226                 debug++;
1227                 break;
1228         case 'i':
1229                 strncpy((char*)peerid, EARGF(usage()), sizeof(peerid));
1230                 break;
1231         case 'A':
1232                 useragent = EARGF(usage());
1233                 break;
1234         default:
1235                 usage();
1236         } ARGEND;
1237
1238         if((s = getenv("NPROC")) != 0){
1239                 if((nproc = atoi(s)) <= 0)
1240                         nproc = 1;
1241                 free(s);
1242         }
1243
1244         fd = 0;
1245         if(*argv)
1246                 if((fd = open(*argv, OREAD)) < 0)
1247                         sysfatal("open: %r");
1248         if(cflag){
1249                 if(alist == nil)
1250                         alist = scons(deftrack, alist);
1251                 if(mktorrent(fd, alist, wlist) < 0)
1252                         sysfatal("%r");
1253                 exits(0);
1254         }
1255         if((n = readall(fd, &p)) <= 0)
1256                 sysfatal("read torrent: %r");
1257         bparse(p, p+n, &torrent);
1258
1259         alist = scons(dstr(dlook(torrent, "announce")), alist);
1260         for(d = dlook(torrent, "announce-list"); d && d->typ == 'l'; d = d->next)
1261                 for(l = d->val; l && l->typ == 'l'; l = l->next)
1262                         alist = scons(dstr(l->val), alist);
1263
1264         if(d = dlook(torrent, "url-list")){
1265                 if(d->typ == 's')
1266                         wlist = scons(dstr(d->val), wlist);
1267                 else for(l = d; l && l->typ == 'l'; l = l->next)
1268                         wlist = scons(dstr(l->val), wlist);
1269                 /* make wlist into a ring */
1270                 for(l = wlist; l && l->next; l = l->next)
1271                         ;
1272                 if(l) l->next = wlist;
1273         }
1274
1275         if(alist == nil && wlist == nil)
1276                 sysfatal("no trackers or webseeds in torrent");
1277
1278         if((d = info = dlook(torrent, "info")) == nil)
1279                 sysfatal("no meta info in torrent");
1280         for(s = e = d->start; d && d->typ == 'd'; d = d->next)
1281                 e = d->end;
1282         sha1((uchar*)s, e - s, (uchar*)infohash, nil);
1283         free(p);
1284
1285         fp = &files;
1286         if(d = dlook(info, "files")){           
1287                 for(; d && d->typ == 'l'; d = d->next){
1288                         Dict *di;
1289
1290                         if((s = dstr(dlook(d->val, "length"))) == nil)
1291                                 continue;
1292                         f = mallocz(sizeof(*f), 1);
1293                         f->len = atoll(s);
1294                         f->name = dstr(dlook(info, "name"));
1295                         for(di = dlook(d->val, "path"); di && di->typ == 'l'; di = di->next)
1296                                 if(s = dstr(di->val))
1297                                         f->name = f->name ? smprint("%s/%s", f->name, s) : s;
1298                         *fp = f;
1299                         fp = &f->next;
1300                 }
1301         } else if(s = dstr(dlook(info, "length"))){
1302                 f = mallocz(sizeof(*f), 1);
1303                 f->len = atoll(s);
1304                 f->name = dstr(dlook(info, "name"));
1305                 *fp = f;
1306         }
1307         len = 0;
1308         for(f = files; f; f = f->next){
1309                 if(f->name == nil || f->len <= 0)
1310                         sysfatal("bogus file entry in meta info");
1311                 s = fixnamedup(f->name);
1312                 if(vflag) fprint(pflag ? 2 : 1, "%s\n", s);
1313                 if((f->fd = open(s, ORDWR)) < 0){
1314                         if(mkdirs(s) < 0)
1315                                 sysfatal("mkdirs: %r");
1316                         if((f->fd = create(s, ORDWR, 0666)) < 0)
1317                                 sysfatal("create: %r");
1318                 }
1319                 f->off = len;
1320                 len += f->len;
1321         }
1322         if(len <= 0)
1323                 sysfatal("no files in torrent");
1324
1325         if((s = dstr(dlook(info, "piece length"))) == nil)
1326                 sysfatal("missing piece length in meta info");
1327         if((blocksize = atoi(s)) <= 0)
1328                 sysfatal("bogus piece length in meta info");
1329         d = dlook(info, "pieces");
1330         if(d == nil || d->typ != 's' || d->len <= 0 || d->len % SHA1dlen)
1331                 sysfatal("bad or no pices in meta info");
1332         npieces = d->len / SHA1dlen;
1333         pieces = mallocz(sizeof(Piece) * npieces, 1);
1334         nhavemap = (npieces+7) / 8;
1335         havemap = mallocz(nhavemap, 1);
1336         for(i = 0; i<npieces; i++){
1337                 pieces[i].hash = (uchar*)d->str + i*SHA1dlen;
1338                 if(len < blocksize)
1339                         pieces[i].len = len;
1340                 else
1341                         pieces[i].len = blocksize;
1342                 len -= pieces[i].len;
1343                 stats.left += pieces[i].len;
1344         }
1345         if(len)
1346                 sysfatal("pieces do not match file length");
1347
1348         for(i=0; i<nproc; i++){
1349                 switch(rfork(RFPROC|RFMEM)){
1350                 case -1:
1351                         sysfatal("fork: %r");
1352                 case 0:
1353                         for(; i<npieces; i+=nproc)
1354                                 havepiece(i, nil);
1355                         exits(0);
1356                 }
1357         }
1358         while(waitpid() >= 0)
1359                 ;
1360
1361         if(finished() && !sflag)
1362                 exits(0);
1363
1364         srand(truerand());
1365         atnotify(catch, 1);
1366         switch(i = rfork(RFPROC|RFMEM|RFNOTEG)){
1367         case -1:
1368                 sysfatal("fork: %r");
1369         case 0:
1370                 if(peerid[0] == 0)
1371                         strncpy((char*)peerid, "-NF9001-", 9);
1372                 for(i=sizeof(peerid)-1; i >= 0 && peerid[i] == 0; i--)
1373                         peerid[i] = nrand(10)+'0';
1374                 server();
1375                 for(; alist; alist = alist->next)
1376                         tracker(alist->str);
1377                 for(f = files, l = wlist; f && l; f = f->next, l = l->next)
1378                         webseed(l, f);
1379                 while(waitpid() != -1)
1380                         ;
1381                 break;
1382         default:
1383                 killgroup = i;
1384                 do {
1385                         sleep(1000);
1386                         if(pflag)
1387                                 print("%d %d\n", nhavepieces, npieces);
1388                 } while(!finished() || sflag);
1389         }
1390         postnote(PNGROUP, killgroup, "kill");
1391         exits(0);
1392 }