#include #include #include #include #include "misc.h" #include "torrent.h" extern char mypeerid[]; extern char *port; void preppieceslist(Torrent *tor) { /* temp is used for operations with many uvlongs, because not enough registers to handle them in one go. */ uvlong fullpieces, temp; uvlong piecelength; int *offset; uvlong *length; Piece *lister; length = tor->filelength; piecelength = tor->piecelength; tor->pieceslist = emalloc(sizeof(Piece)); lister = tor->pieceslist; for (int i=0; ipiecesnb-1; i++){ lister->index = i; lister->status = 0; lister->next = emalloc(sizeof(Piece)); lister = lister->next; } lister->index = tor->piecesnb-1; lister->status = 0; lister->next = nil; tor->firstoffset = emalloc(tor->filesnb * sizeof(int)); offset = tor->firstoffset; offset[0] = 0; tor->fileborder = emalloc(tor->filesnb*sizeof(int)); tor->fileborder[0] = 0; for (int i=0; ifilesnb; i++){ if(length[i] < piecelength){ /* file is smaller than a piece */ if(offset[i] == 0 && i < tor->filesnb - 1){ lister = tor->pieceslist; while ((lister != nil) && (lister->index != tor->fileborder[i])) lister = lister->next; lister->status |= BTshar; } if(i < tor->filesnb - 1){ tor->fileborder[i+1] = tor->fileborder[i]; temp = offset[i] + length[i]; offset[i+1] = temp; } else tor->lastpiece = length[i]; // => lastpiece == length if data is an exact number of pieces } else{ if(offset[i] != 0){ temp = length[i]; temp -= piecelength - offset[i]; fullpieces = temp / piecelength; temp = length[i]; temp -= fullpieces * piecelength; if(i < tor->filesnb - 1){ tor->fileborder[i+1] = tor->fileborder[i] + fullpieces + 1; offset[i+1] = temp - (piecelength - offset[i]); } else tor->lastpiece = temp - (piecelength - offset[i]); // => lastpiece == 0 if data is an exact number of pieces } else{ fullpieces = length[i] / piecelength; temp = length[i]; if(i < tor->filesnb - 1){ tor->fileborder[i+1] = tor->fileborder[i] + fullpieces; offset[i+1] = temp - fullpieces * piecelength; } else tor->lastpiece = temp - fullpieces * piecelength; // => lastpiece == 0 if data is an exact number of pieces } if(i < tor->filesnb - 1 && offset[i+1] != 0){ lister = tor->pieceslist; while ((lister != nil) && (lister->index != tor->fileborder[i+1])) lister = lister->next; lister->status |= BTshar; } } } } /* * creates our bitfield, mainly from tor->pieceslist * also creates the data files if needed */ //TODO: we need to do some more precise resource protection in here, instead of calling a big fat qlock in the caller void scanpieces(Torrent *tor, char *datadir) { int fd, n, piecelength, index, offset, bitfieldsize, byteindex, fileindex, piecepart, keepgoing, needsopen; char *filename, *data, *buf, *bitfield; char bitindex, mask; Piece *lister; piecelength = tor->piecelength; if(tor->piecesnb % 8 != 0) bitfieldsize = tor->piecesnb / 8 + 1; else bitfieldsize = tor->piecesnb / 8; tor->bitfieldsize = bitfieldsize; // create data files when needed tor->datafiles = emalloc(tor->filesnb * sizeof(char *)); for (int i=0; ifilesnb; i++){ tor->datafiles[i] = emalloc(strlen(datadir) + 1 + strlen(propername(tor->filepath[i], 0))+ 1); tor->datafiles[i] = strcpy(tor->datafiles[i], datadir); tor->datafiles[i] = strcat(tor->datafiles[i], "/"); tor->datafiles[i] = strcat(tor->datafiles[i], propername(tor->filepath[i], 0)); dbgprint(1, "tor->datafiles[%d]: %s\n", i, tor->datafiles[i]); createpath(tor->datafiles[i]); } data = emalloc(piecelength); free(tor->bitfield); tor->bitfield = emallocz(bitfieldsize*sizeof(char), 1); bitfield = tor->bitfield; lister = tor->pieceslist; needsopen = 1; //TODO: better algo while (lister != nil){ // find fileindex index = lister->index; fileindex = 0; for (int i=tor->filesnb-1; i>=0; i--) if (index > tor->fileborder[i]){ fileindex = i; break; } filename = tor->datafiles[fileindex]; if (needsopen) { if((fd = open(filename, OREAD)) < 0) error("torrent.c: scanpieces: open"); needsopen = 0; if (fileindex == 0) offset = 0; else offset = piecelength - tor->firstoffset[fileindex]; } if (((lister->status)&BTshar) != 0){ // shared piece piecepart = tor->firstoffset[fileindex+1]; buf = emalloc(piecepart); n = pread(fd, buf, piecepart, offset); memmove(data, buf, piecepart); offset = piecepart; keepgoing = 1; if (n != piecepart){ // just skip the rest of the piece since we already know this part is not yet dled close(fd); needsopen = 1; free(buf); lister = lister->next; continue; } // now deal with rest of the piece while (keepgoing == 1){ close(fd); fileindex++; filename = tor->datafiles[fileindex]; if((fd = open(filename, OREAD)) < 0) error("torrent.c: scanpieces: open"); if (tor->filelength[fileindex] < piecelength - tor->firstoffset[fileindex]){ // file smaller than the rest of the piece piecepart = (int) tor->filelength[fileindex]; if (fileindex < tor->filesnb - 1) // loop to next file in the piece keepgoing = 1; else // last file of last piece, don't loop again keepgoing = 0; } else { // file takes all the rest of the piece piecepart = piecelength - tor->firstoffset[fileindex]; keepgoing = 0; } free(buf); buf = emallocz(piecepart, 1); n = read(fd, buf, piecepart); //TODO: skip to next piece if read not complete (not a problem though, checkpiece will catch that) memmove(&data[offset], buf, piecepart); offset += piecepart; } close(fd); needsopen = 1; free(buf); } else { // non shared piece if (index == tor->piecesnb - 1) // last piece piecelength = tor->lastpiece; n = pread(fd, data, piecelength, offset); offset += piecelength; if(n != piecelength){ lister = lister->next; continue; } } if(checkpiece(data, tor, index) >= 0){ byteindex = index / 8; bitindex = index % 8 + 1; mask = 1 << (8 - bitindex); bitfield[byteindex] |= mask; } lister = lister->next; } lister = tor->pieceslist; while (lister != nil){ index = lister->index; byteindex = index / 8; bitindex = index % 8 + 1; mask = 1 << (8 - bitindex); if ((bitfield[byteindex] & mask) != 0) lister->status |= BThas; lister = lister->next; } free(data); } void preppeerspieces(Torrent *tor, Peer *peer) { char *bitfield; Piece *lister; Piece *rimmer; Piece *kryten; int index; int byteindex; char bitindex; char mask; bitfield = peer->bitfield; lister = tor->pieceslist; peer->pieceslist = emalloc(sizeof(Piece)); rimmer = peer->pieceslist; kryten = rimmer; peer->piecesnb = 0; while (lister != nil){ index = lister->index; byteindex = index / 8; bitindex = index % 8 + 1; mask = 1 << (8 - bitindex); if ((bitfield[byteindex] & mask) != 0){ rimmer->index = index; rimmer->status = lister->status; rimmer->next = emalloc(sizeof(Piece)); kryten = rimmer; rimmer = rimmer->next; peer->piecesnb++; } lister = lister->next; } free(rimmer); kryten->next = nil; } char * forgerequest(Torrent *tor, char *req) { char *announce; int length; char *baseurl; char infohash[3*HASHSIZE+1]; char peerid[3*PEERIDLEN+1]; int len = strlen(tor->announce); char *buf = emalloc((512)*sizeof(char)); /* Is it really fair to bail out if announce url doesn't contain "announce"? */ if((announce = strstr(tor->announce, "announce")) == 0) error("weird announce url"); length = strlen(announce); len = len - length; baseurl = emalloc((len+1)*sizeof(char)); baseurl = strncpy(baseurl,tor->announce,len); baseurl[len] = '\0'; for (int i = 0; iinfohash[i]); } infohash[3*HASHSIZE] = '\0'; for (int i = 0; i<20; i++){ peerid[3*i] = '%'; sprint(&(peerid[3*i+1]),"%.2x", mypeerid[i]); } infohash[3*PEERIDLEN] = '\0'; if (strcmp(req, "scrape") == 0){ /* No need to realloc precisely, just use a big enough buffer redo it like for announce buf = erealloc(buf,(len+7+10+3*HASHSIZE+1)*sizeof(char)); buf = strcpy(buf, baseurl); buf = strcat(buf,"scrape?info_hash="); buf = strcat(buf,infohash); */ } else if (strcmp(req, "announce") == 0){ buf = strcpy(buf, baseurl); buf = strcat(buf, announce); buf = strcat(buf,"?info_hash="); buf = strcat(buf,infohash); buf = strcat(buf,"&port="); buf = strcat(buf,port); buf = strcat(buf,"&uploaded=0&downloaded=0&left=0&event=started"); /* sending compact=0 will in fact result in compact replies! */ //buf = strcat(buf,"&compact=0"); //buf = strcat(buf,"&ip=127.0.0.1"); buf = strcat(buf,"&peer_id="); buf = strcat(buf,peerid); } free(baseurl); return buf; } void writedata(ulong index, char *data, Torrent *tor, Peer *peer) { int m, fd, fileindex, towrite, offset, piecelength; uvlong *filelength; Piece *lister; Piece *remover; fileindex = 0; for (int i=tor->filesnb-1; i>=0; i--) if (index > tor->fileborder[i]){ fileindex = i; break; } piecelength = tor->piecelength; filelength = tor->filelength; //TODO: case when file is smaller than piece is still missing if (tor->firstoffset[fileindex] != 0) offset = piecelength - tor->firstoffset[fileindex] + (index - tor->fileborder[fileindex] - 1) * piecelength; else offset = (index - tor->fileborder[fileindex]) * piecelength; if ((fd = open(tor->datafiles[fileindex], OWRITE)) < 0) sysfatal("couldn't open %s: %r", tor->datafiles[fileindex]); dbgprint(1, "offset in file: %d\n", offset); lister = tor->pieceslist; while ((lister != nil) && (lister->index != index)) lister = lister->next; if (((lister->status)&BTshar) != 0){ /* piece at junction of two (or more) files */ towrite = tor->firstoffset[fileindex+1]; if ((m = pwrite(fd, data, towrite, offset)) != m) error("torrent.c: writedata(): %r"); close(fd); dbgprint(1, "shared piece: %ld\n", index); dbgprint(1, "wrote %d bytes at offset %d \n", towrite, offset); for(;;){ fileindex++; if ((fd = open(tor->datafiles[fileindex], OWRITE)) < 0) error("couldn't open file: %r"); offset = 0; if ((index == tor->piecesnb-1) && (fileindex == tor->filesnb-1)){ /* last piece of torrent */ towrite = tor->lastpiece; if ((m = pwrite(fd, &data[tor->firstoffset[fileindex]], towrite, offset)) != m) error("torrent.c: writedata(): %r"); dbgprint(1, "then wrote %d bytes at %d\n", towrite, offset); break; } else{ /* not last piece */ if(tor->fileborder[fileindex+1] == index){ /* file smaller than what's left of piece */ towrite = filelength[fileindex]; if ((m = pwrite(fd, &data[tor->firstoffset[fileindex]], towrite, offset)) != m) error("torrent.c: writedata(): %r"); dbgprint(1, "then wrote %d bytes at %d\n", towrite, offset); close(fd); } else{ /* file bigger than what's left of piece */ towrite = piecelength - tor->firstoffset[fileindex]; if ((m = pwrite(fd, &data[tor->firstoffset[fileindex]], towrite, offset)) != m) error("torrent.c: writedata(): %r"); dbgprint(1, "then wrote %d bytes at %d\n", towrite, offset); break; } } } } else{ /* common case: piece not shared */ if (index == tor->piecesnb-1){ if ((m = pwrite(fd, data, tor->lastpiece, offset)) != m) error("torrent.c: writedata(): %r"); } else{ towrite = piecelength; if ((m = pwrite(fd, data, towrite, offset)) != m) error("torrent.c: writedata(): %r"); } } close(fd); /* update the list */ lister = tor->pieceslist; while ((lister != nil) && (lister->index != index)) lister = lister->next; lister->status |= BThas; /* now for the peer's list */ lister = peer->pieceslist; remover = lister; while ((lister != nil) && (lister->index != index)){ remover = lister; lister = lister->next; } if (lister == peer->pieceslist) peer->pieceslist = lister->next; remover->next = lister->next; free(lister); peer->piecesnb--; dbgprint(1, "wrote piece: %ld\n", index); } int checkpiece(char *data, Torrent *tor, int index) { uchar *digest; uchar *buf; int datasize; Piece *lister; if (index == tor->piecesnb-1){ lister = tor->pieceslist; while ((lister != nil) && (lister->index != index)) lister = lister->next; /* if piece is shared, lastpiece is actually not the size of the whole piece. Too lazy to figure out the right algo right now, so assume it's all good. */ if ((lister->status & BTshar) != 0) return 1; else datasize = tor->lastpiece; } else datasize = tor->piecelength; digest = emalloc(SHA1dlen); buf = emalloc(datasize); memmove(buf, data, datasize); sha1(buf, datasize, digest, nil); for (int i=0; isha1list[index][i]){ free(digest); free(buf); return -1; } } //dbgprint(1, "\n"); free(digest); free(buf); return index; } //TODO: merge with writedata (make function that does pread or pwrite depending on the arg) void readdata(ulong index, char *data, Torrent *tor, Peer *peer) { int m, fd, fileindex, toread, offset, piecelength; uvlong *filelength; Piece *lister; fileindex = 0; for (int i=tor->filesnb-1; i>=0; i--) if (index > tor->fileborder[i]){ fileindex = i; break; } piecelength = tor->piecelength; filelength = tor->filelength; if ((fd = open(tor->datafiles[fileindex], ORDWR)) < 0) error("couldn't open file: %r"); dbgprint(1, "opened file: %s\n", tor->datafiles[fileindex]); if (tor->firstoffset[fileindex] != 0) offset = piecelength - tor->firstoffset[fileindex] + (index - tor->fileborder[fileindex] - 1) * piecelength; else offset = (index - tor->fileborder[fileindex]) * piecelength; dbgprint(1, "offset in file: %d\n", offset); lister = tor->pieceslist; while ((lister != nil) && (lister->index != index)) lister = lister->next; if (((lister->status)&BTshar) != 0){ /* piece at the junction of two files */ toread = tor->firstoffset[fileindex+1]; if ((m = pread(fd, data, toread, offset)) != toread) error("torrent.c: readdata(): %r"); close(fd); dbgprint(1, "shared piece: %ld\n", index); dbgprint(1, "read %d bytes at offset %d \n", toread, offset); for(;;){ fileindex++; if ((fd = open(tor->datafiles[fileindex], ORDWR)) < 0) error("couldn't open file: %r"); offset = 0; if ((index == tor->piecesnb-1) && (fileindex == tor->filesnb-1)){ /* last piece */ toread = tor->lastpiece; if ((m = pread(fd, &data[tor->firstoffset[fileindex]], toread, offset)) != toread) error("torrent.c: readdata(): %r"); dbgprint(1, "then read %d bytes at %d\n", toread, offset); break; //TODO: go on, stopped here. } else{ /* not last piece */ if(tor->fileborder[fileindex+1] == index){ /* file smaller than what's left of piece */ toread = filelength[fileindex]; if ((m = pread(fd, &data[tor->firstoffset[fileindex]], toread, offset)) != toread) error("torrent.c: readdata(): %r"); dbgprint(1, "then read %d bytes at %d\n", toread, offset); close(fd); } else{ /* file bigger than what's left of piece */ toread = piecelength - tor->firstoffset[fileindex]; if ((m = pread(fd, &data[tor->firstoffset[fileindex]], toread, offset)) != toread) error("torrent.c: readdata(): %r"); dbgprint(1, "then read %d bytes at %d\n", toread, offset); break; } } } } else{ /* common case; piece not shared */ if (index == tor->piecesnb-1){ if ((m = pread(fd, data, tor->lastpiece, offset)) != tor->lastpiece) error("could not read piece from disk: %r"); } else{ toread = piecelength; if ((m = pread(fd, data, toread, offset)) != toread) error("could not read piece from disk: %r"); } } close(fd); //TODO: keep track of what pieces the peer has dbgprint(1, "piece read: %ld\n", index); } /* * adds or removes a piece to the peers' pieceslist. returns 1 if the * operation was successfull, otherwise 0. */ int updatepeerspieces(Torrent *tor, Peer *peer, int index, char op) { Piece *lister, *rimmer; char status = 0; if (op == '+'){ // insert in the list lister = tor->pieceslist; // do not add it if we already have it while (lister != nil){ if (lister->index == index){ if ((lister->status & BThas) != 0) return 0; if ((lister->status & BTshar) != 0) status |= BTshar; } lister = lister->next; } lister = peer->pieceslist; rimmer = lister; while (lister != nil){ if (lister->index == index){ // somehow it was already there. do nothing. return 0; } if (lister->index > index){ // insert here rimmer->next = emalloc(sizeof(Piece)); rimmer->next->index = index; rimmer->next->status = status; rimmer->next->next = lister; peer->piecesnb++; return 1; } rimmer = lister; lister = lister->next; } // end of the list. append. rimmer->next = emalloc(sizeof(Piece)); rimmer->next->index = index; rimmer->next->status = status; rimmer->next->next = lister; peer->piecesnb++; return 1; } return 0; } void freepeer(Peer *peer) { Piece *lister, *rimmer; free(peer->peerinfo->address); free(peer->peerinfo->id); free(peer->peerinfo); free(peer->bitfield); lister = peer->pieceslist; rimmer = lister; while(lister != nil){ lister = rimmer->next; free(rimmer); rimmer = lister; } free(peer); }