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