]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/cmd/ip/torrent.c
ip/ipconfig: use ewrite() to enable routing command for sendra
[plan9front.git] / sys / src / cmd / ip / torrent.c
index 5e86c27e6b1ca7e1d9fcf1e349d2ec18b7ac03f9..54d631e0399db898002e206c426a9b0b5443785c 100644 (file)
@@ -44,14 +44,18 @@ struct Stats
 
 enum {
        MAXIO = 16*1024,
+       SRVPROCS = 16,
+       CLIPROCS = 16,
 };
 
 int debug;
+int nproc = 1;
 int killgroup = -1;
 int port = 6881;
 char *deftrack = "http://exodus.desync.com/announce";
 char *mntweb = "/mnt/web";
-uchar infohash[20];
+char *useragent = "torrent";
+uchar infohash[SHA1dlen];
 uchar peerid[20];
 int blocksize;
 
@@ -189,11 +193,11 @@ rwpiece(int wr, int index, uchar *data, int len, int poff)
        int n, m;
        File *f;
 
-       if(len <= 0 || poff >= pieces[index].len)
+       if(len <= 0 || poff < 0 || poff >= pieces[index].len)
                return 0;
        if(len+poff > pieces[index].len)
                len = pieces[index].len - poff;
-       off = (vlong)index * blocksize;
+       off = (vlong)index * (vlong)blocksize;
        off += poff;
        for(f = files; f; f = f->next)
                if((f->off+f->len) > off)
@@ -208,9 +212,9 @@ rwpiece(int wr, int index, uchar *data, int len, int poff)
 }
 
 int
-havepiece(int x)
+havepiece(int x, char *from)
 {
-       uchar *p, m, hash[20];
+       uchar *p, m, hash[SHA1dlen];
        int n;
 
        m = 0x80>>(x&7);
@@ -224,8 +228,11 @@ havepiece(int x)
        }
        sha1(p, n, hash, nil);
        free(p);
-       if(memcmp(hash, pieces[x].hash, 20))
+       if(memcmp(hash, pieces[x].hash, sizeof(hash))){
+               if(debug && from != nil)
+                       fprint(2, "peer %s: damaged piece %d\n", from, x);
                return 0;
+       }
        lock(&stats);
        if((havemap[x>>3] & m) == 0){
                havemap[x>>3] |= m;
@@ -233,6 +240,8 @@ havepiece(int x)
                stats.left -= pieces[x].len;
        }
        unlock(&stats);
+       if(debug && from != nil)
+               fprint(2, "peer %s: completed piece %d\n", from, x);
        return 1;
 }
 
@@ -378,7 +387,7 @@ peer(int fd, int incoming, char *addr)
        uchar buf[64+MAXIO], *map, *told, *p, m;
        int mechoking, hechoking;
        int mewant, hewant;
-       int workpiece;
+       int workpiece, workoffset;
        int i, o, l, x, n;
 
        if(debug) fprint(2, "peer %s: %s connected\n", addr, incoming ? "incoming" : "outgoing");
@@ -415,6 +424,8 @@ peer(int fd, int incoming, char *addr)
        mewant = 0;
        hewant = 0;
        workpiece = -1;
+       workoffset = 0;
+
        map = mallocz(nhavemap, 1);
        told = malloc(nhavemap);
 
@@ -447,19 +458,19 @@ peer(int fd, int incoming, char *addr)
                }
                if(!hechoking && mewant){
                        x = workpiece;
-                       if(x >= 0 && pieces[x].brk < pieces[x].len)
-                               {}
-                       else x = pickpiece(map);
+                       if(x < 0 || (havemap[x>>3]&(0x80>>(x&7))) != 0 || workoffset >= pieces[x].len)
+                               x = pickpiece(map);
                        if(x >= 0){
-                               o = pieces[x].brk;
+                               o = workpiece != x ? pieces[x].brk : workoffset;
                                l = pieces[x].len - o;
                                if(l > MAXIO)
                                        l = MAXIO;
+                               workpiece = x;
+                               workoffset = o + l; 
                                if(debug) fprint(2, "peer %s: -> request %d %d %d\n", addr, x, o, l);
                                n = pack(buf, sizeof(buf), "lblll", 1+4+4+4, 0x06, x, o, l);
                                if(write(fd, buf, n) != n)
                                        goto Out;
-                               workpiece = x;
                        }
                }
                if(mechoking && hewant){
@@ -547,13 +558,23 @@ peer(int fd, int incoming, char *addr)
                        if(debug) fprint(2, "peer %s: <- piece %d %d %d\n", addr, x, o, n);
                        if(x < 0 || x >= npieces)
                                continue;
-                       if((pieces[x].brk != o) || (havemap[x>>3]&(0x80>>(x&7))))
+                       if((havemap[x>>3]&(0x80>>(x&7))) != 0)
                                continue;
-                       if(rwpiece(1, x, p, n, o) == n){
-                               if((pieces[x].brk = o+n) == pieces[x].len){
-                                       if(!havepiece(x))
-                                               pieces[x].brk = 0;
-                               }
+                       if(o < 0 || o >= pieces[x].len)
+                               continue;
+                       if(o+n > pieces[x].len)
+                               n = o - pieces[x].len;
+                       if((o > pieces[x].brk) || (o+n <= pieces[x].brk))
+                               continue;
+                       n = rwpiece(1, x, p, n, o);
+                       if(n <= 0)
+                               continue;
+                       pieces[x].brk = o+n;
+                       if(o+n >= pieces[x].len && !havepiece(x, addr)){
+                               pieces[x].brk = 0;
+                               /* backoff from this piece for a while */
+                               if(x == workpiece)
+                                       workpiece = -1;
                        }
                        break;
                case 0x08:      // Cancel <index> <begin> <length>
@@ -579,10 +600,11 @@ void
 server(void)
 {
        char addr[64], adir[40], ldir[40];
-       int afd, lfd, dfd;
+       int afd, lfd, dfd, pid, nprocs;
        NetConnInfo *ni;
 
        afd = -1;
+       nprocs = 0;
        for(port=6881; port<6890; port++){
                snprint(addr, sizeof(addr), "tcp!*!%d", port);
                if((afd = announce(addr, adir)) >= 0)
@@ -599,7 +621,13 @@ server(void)
                        fprint(2, "listen: %r");
                        break;
                }
-               if(rfork(RFFDG|RFPROC|RFMEM)){
+               while(nprocs >= SRVPROCS)
+                       if(waitpid() > 0)
+                               nprocs--;
+               nprocs++;
+               if(pid = rfork(RFFDG|RFPROC|RFMEM)){
+                       if(pid < 0)
+                               nprocs--;
                        close(lfd);
                        continue;
                }
@@ -609,8 +637,8 @@ server(void)
                }
                ni = getnetconninfo(ldir, dfd);
                peer(dfd, 1, ni ? ni->raddr : "???");
-               if(ni) freenetconninfo(ni);     
-               break;
+               if(ni) freenetconninfo(ni);
+               break;  
        }
        exits(0);
 }
@@ -618,11 +646,12 @@ server(void)
 void
 client(char *ip, char *port)
 {
-       static Dict *peers;
+       static Dict *peerqh, *peerqt;
        static QLock peerslk;
-       int try, fd;
+       static int nprocs;
        char *addr;
        Dict *d;
+       int fd;
 
        if(ip == nil || port == nil)
                return;
@@ -630,7 +659,7 @@ client(char *ip, char *port)
        d = mallocz(sizeof(*d) + 64, 1);
        snprint(addr = d->str, 64, "tcp!%s!%s", ip, port);
        qlock(&peerslk);
-       if(dlook(peers, addr)){
+       if(dlook(peerqh, addr)){
                qunlock(&peerslk);
                free(d);
                return;
@@ -638,23 +667,44 @@ client(char *ip, char *port)
        d->len = strlen(addr);
        d->typ = 'd';
        d->val = d;
-       d->next = peers;
-       peers = d;
+       /* enqueue to front */
+       if((d->next = peerqh) == nil)
+               peerqt = d;
+       peerqh = d;
+       if(nprocs >= CLIPROCS){
+               qunlock(&peerslk);
+               return;
+       }
+       nprocs++;
        qunlock(&peerslk);
-
-       if(debug) fprint(2, "client %s\n", addr);
-
-       if(rfork(RFFDG|RFPROC|RFMEM))
+       if(rfork(RFFDG|RFPROC|RFMEM|RFNOWAIT))
                return;
-       for(try = 0; try < 10; try++){
+
+       for(;;){
+               qlock(&peerslk);
+               /* dequeue and put to tail */
+               if(d = peerqh){
+                       if((peerqh = d->next) == nil)
+                               peerqt = nil;
+                       d->next = nil;
+                       if(peerqt)
+                               peerqt->next = d;
+                       else
+                               peerqh = d;
+                       peerqt = d;
+               } else
+                       nprocs--;
+               qunlock(&peerslk);
+               if(d == nil)
+                       exits(0);
+               addr = d->str;
+               if(debug) fprint(2, "client %s\n", addr);
                if((fd = dial(addr, nil, nil, nil)) >= 0){
-                       if(!peer(fd, 0, addr))
-                               break;
+                       peer(fd, 0, addr);
                        close(fd);
                }
-               sleep((1000<<try)+nrand(5000));
+               sleep(1000+nrand(5000));
        }
-       exits(0);
 }
 
 int
@@ -682,6 +732,10 @@ hopen(char *url, ...)
                close(ctlfd);
                return -1;
        }
+       if(useragent != nil && useragent[0] != '\0'){
+               n = snprint(buf, sizeof buf, "useragent %s", useragent);
+               write(ctlfd, buf, n);
+       }
        snprint(buf, sizeof buf, "%s/%d/body", mntweb, conn);
        if((fd = open(buf, OREAD)) < 0)
                goto ErrOut;
@@ -748,7 +802,7 @@ Error:
                        o += m;
                        n -= m;
                        p = 0;
-                       if(havepiece(x++))
+                       if(havepiece(x++, w->str))
                                continue;
                        if(++err > 10){
                                close(fd);
@@ -757,8 +811,9 @@ Error:
                        }
                }
        }
-       havepiece(off / blocksize);
-       havepiece(f->off / blocksize);
+       if(off < f->off + f->len)
+               havepiece(off / blocksize, w->str);
+       havepiece(f->off / blocksize, w->str);
        close(fd);
        exits(0);
 }
@@ -809,6 +864,15 @@ webtracker(char *url)
                        bparse(p, p+n, &d);
                        free(p);
                } else if(debug) fprint(2, "tracker %s: %r\n", url);
+               /* check errors and warnings */
+               if(p = dstr(dlook(d, "failure reason"))) {
+                       if(debug)
+                               fprint(2, "tracker failure: %s\n", p);
+                       exits(0);
+               }
+               if(p = dstr(dlook(d, "warning message")))
+                       if(debug)
+                               fprint(2, "tracker warning: %s\n", p);
                if(l = dlook(d, "peers")){
                        if(l->typ == 's')
                                clients4((uchar*)l->str, l->len);
@@ -843,7 +907,7 @@ udpaddr(char addr[64], int naddr, char *url)
                if((x = strchr(url, '/')) == nil)
                        x = strchr(url, 0);
        }
-       snprint(addr, naddr, "udp!%.*s!%d", (int)(x-url), url, port);
+       snprint(addr, naddr, "udp!%.*s!%d", utfnlen(url, x-url), url, port);
        return 0;
 }
 
@@ -975,7 +1039,7 @@ Hfmt(Fmt *f)
 int
 mktorrent(int fd, Dict *alist, Dict *wlist)
 {
-       uchar *b, h[20];
+       uchar *b, h[SHA1dlen];
        Dir *d;
        int n;
 
@@ -991,7 +1055,6 @@ mktorrent(int fd, Dict *alist, Dict *wlist)
                werrstr("empty file");
                return -1;
        }
-       npieces = 1;
        for(blocksize = 256*1024;;blocksize<<=1){
                npieces = (d->length + blocksize-1) / blocksize;
                if(npieces <= 8*1024 || blocksize >= 2*1024*1024)
@@ -1105,7 +1168,7 @@ void
 usage(void)
 {
        fprint(2, "usage: %s [ -vsdpc ] [ -m mtpt ] [ -t tracker-url ] "
-                 "[ -w webseed-url ] [ file ]\n", argv0);
+                 "[ -w webseed-url ] [ -i peerid ] [ -A useragent ] [ file ]\n", argv0);
        exits("usage");
 }
 
@@ -1162,10 +1225,22 @@ main(int argc, char *argv[])
        case 'd':
                debug++;
                break;
+       case 'i':
+               strncpy((char*)peerid, EARGF(usage()), sizeof(peerid));
+               break;
+       case 'A':
+               useragent = EARGF(usage());
+               break;
        default:
                usage();
        } ARGEND;
 
+       if((s = getenv("NPROC")) != 0){
+               if((nproc = atoi(s)) <= 0)
+                       nproc = 1;
+               free(s);
+       }
+
        fd = 0;
        if(*argv)
                if((fd = open(*argv, OREAD)) < 0)
@@ -1252,14 +1327,14 @@ main(int argc, char *argv[])
        if((blocksize = atoi(s)) <= 0)
                sysfatal("bogus piece length in meta info");
        d = dlook(info, "pieces");
-       if(d == nil || d->typ != 's' || d->len <= 0 || d->len % 20)
+       if(d == nil || d->typ != 's' || d->len <= 0 || d->len % SHA1dlen)
                sysfatal("bad or no pices in meta info");
-       npieces = d->len / 20;
+       npieces = d->len / SHA1dlen;
        pieces = mallocz(sizeof(Piece) * npieces, 1);
        nhavemap = (npieces+7) / 8;
        havemap = mallocz(nhavemap, 1);
        for(i = 0; i<npieces; i++){
-               pieces[i].hash = (uchar*)d->str + i*20;
+               pieces[i].hash = (uchar*)d->str + i*SHA1dlen;
                if(len < blocksize)
                        pieces[i].len = len;
                else
@@ -1270,17 +1345,31 @@ main(int argc, char *argv[])
        if(len)
                sysfatal("pieces do not match file length");
 
-       for(i = 0; i<npieces; i++)
-               havepiece(i);
+       for(i=0; i<nproc; i++){
+               switch(rfork(RFPROC|RFMEM)){
+               case -1:
+                       sysfatal("fork: %r");
+               case 0:
+                       for(; i<npieces; i+=nproc)
+                               havepiece(i, nil);
+                       exits(0);
+               }
+       }
+       while(waitpid() >= 0)
+               ;
+
+       if(finished() && !sflag)
+               exits(0);
 
-       srand(time(0));
+       srand(truerand());
        atnotify(catch, 1);
        switch(i = rfork(RFPROC|RFMEM|RFNOTEG)){
        case -1:
                sysfatal("fork: %r");
        case 0:
-               memmove(peerid, "-NF9001-", 8);
-               for(i=8; i<sizeof(peerid); i++)
+               if(peerid[0] == 0)
+                       strncpy((char*)peerid, "-NF9001-", 9);
+               for(i=sizeof(peerid)-1; i >= 0 && peerid[i] == 0; i--)
                        peerid[i] = nrand(10)+'0';
                server();
                for(; alist; alist = alist->next)