void handle_message(cjdnsadmin_t *adm, char *buffer, ssize_t len) { // TODO: fix memory leak struct bencode *b = ben_decode(buffer, len); if (!b) { fprintf(stderr, "bencode error: %lu\n",len); printf("message from cjdns: \"%*s\"\n", (int)len, buffer); return; } // Get IPs struct bencode *table = ben_dict_get_by_str(b, "routingTable"); size_t i, num_items = ben_list_len(table); for (i = 0; i < num_items; i++) { struct bencode *item = ben_list_get(table, i); struct bencode *ip = ben_dict_get_by_str(item, "ip"); if (ben_is_str(ip)) { const char *ip_str = ben_str_val(ip); if (adm->on_found_ip) { (*adm->on_found_ip)(adm->on_found_ip_obj, ip_str); } } } // check if there is more struct bencode *more = ben_dict_get_by_str(b, "more"); int more_int = more && ben_is_int(more) && ben_int_val(more); if (more_int == 1) { // get the next page of the routing table adm->fetch_peers_page++; cjdnsadmin_fetch_peers(adm); } else { // start from the first page next time adm->fetch_peers_page = 0; } ben_free(b); }
/* handle_announcement reads an announcement document to find some peers to download from. Puts em in benPeers, and we then handle the peer scanning or whatever in the main function. */ void handle_announcement(char *ptr, size_t size) { struct bencode* anno = ben_decode(ptr,size); printf("Torrent has %lld seeds and %lld downloading peers. \n", ((struct bencode_int*)ben_dict_get_by_str(anno,"complete"))->ll, ((struct bencode_int*)ben_dict_get_by_str(anno,"incomplete"))->ll); benPeers = (struct bencode_list*)ben_dict_get_by_str(anno,"peers"); }
/* handle_announcement reads an announcement document to find some peers to download from. start a new tread for each peer. */ void handle_announcement(char *ptr, size_t size) { struct bencode* anno = ben_decode(ptr,size); printf("Torrent has %lld seeds and %lld downloading peers. \n", ((struct bencode_int*)ben_dict_get_by_str(anno,"complete"))->ll, ((struct bencode_int*)ben_dict_get_by_str(anno,"incomplete"))->ll); struct bencode_list *peers = (struct bencode_list*)ben_dict_get_by_str(anno,"peers"); // handle the binary case if(peers->type == BENCODE_STR) { printf("Got binary list of peers\n"); struct peer_addr *peerlist = (struct peer_addr*)((struct bencode_str*)peers)->s; for(int i=0;i<((struct bencode_str*)peers)->len/6;i++) { struct in_addr a; a.s_addr = peerlist[i].addr; printf("Found peer %s:%d\n",inet_ntoa(a),ntohs(peerlist[i].port)); connect_all_peers(&peerlist[i]); } } // handle the bencoded case else { for(int i=0;i<peers->n;i++) { printf("Got bencoded list of peers\n"); struct bencode *peer = peers->values[i]; char *address = ((struct bencode_str*)ben_dict_get_by_str(peer,"ip"))->s; unsigned short port = ((struct bencode_int*)ben_dict_get_by_str(peer,"port"))->ll; printf("Found peer %s:%d\n",address,port); struct peer_addr *peeraddr = malloc(sizeof(struct peer_addr)); peeraddr->addr=inet_addr(address); peeraddr->port=htons(port); connect_all_peers(peeraddr); } } listen_from_peers(); }
void write_block(char* data, int piece, int offset, int len, int acquire_lock) { FILE *outfile; //dev_notifier(); int accumulated_file_length = 0; int block_start = piece*piece_length+offset; struct bencode_list* files = (struct bencode_list*)ben_dict_get_by_str(info,"files"); // multi-file case if(files) { for(int i=0;i<files->n;i++) { struct bencode* file = files->values[i]; struct bencode_list* path = (struct bencode_list*)ben_dict_get_by_str(file,"path"); int file_length=((struct bencode_int*)ben_dict_get_by_str(file,"length"))->ll; printf("start %d len %d accum %d filelen %d\n",block_start,len,accumulated_file_length,file_length); // at least part of the block belongs in this file if((block_start >= accumulated_file_length) && (block_start < accumulated_file_length+file_length)) { char filename[255]; mkdir(((struct bencode_str*)ben_dict_get_by_str(info,"name"))->s,0777); chmod(((struct bencode_str*)ben_dict_get_by_str(info,"name"))->s,07777); sprintf(filename,"%s/",((struct bencode_str*)ben_dict_get_by_str(info,"name"))->s); for(int j=0;j<path->n;j++) { if(j<(path->n-1)) { sprintf(filename+strlen(filename),"%s/",((struct bencode_str*)path->values[j])->s); mkdir(filename,0777); chmod(filename,07777); } else sprintf(filename+strlen(filename),"%s",((struct bencode_str*)path->values[j])->s); } int outfile = open(filename,O_RDWR|O_CREAT,0777); if(outfile == -1) { fprintf(stderr,"filename: %s\n",filename); perror("Couldn't open file for writing"); exit(1); } int offset_into_file = block_start - accumulated_file_length; int remaining_file_length = file_length - offset_into_file; lseek(outfile,offset_into_file,SEEK_SET); if(remaining_file_length > len) { write(outfile,data,len); close(outfile); goto cleanup; } else { write(outfile,data,remaining_file_length); close(outfile); write_block(data+remaining_file_length,piece,offset+remaining_file_length,len-remaining_file_length,0); goto cleanup; } } accumulated_file_length+=file_length; } } // single-file case else { struct bencode_str* name = (struct bencode_str*)ben_dict_get_by_str(info,"name"); if(name) { FILE *outfile = fopen(name->s,"r+"); file_length = ((struct bencode_int*)ben_dict_get_by_str(info,"length"))->ll; // write the data to the right spot in the file fseek(outfile,piece*piece_length+offset,SEEK_SET); fwrite(data,1,len,outfile); fclose(outfile); } else { printf("No name?\n"); exit(1); } } cleanup: return; }
int main(int argc, char** argv) { //setvbuf(stdout,screenbuf,_IOFBF,10000); setbuf(stdout, NULL); setbuf(stderr, NULL); // create a global peer_id for this session. Every peer_id should include -CS450-. for(int i=strlen(peer_id);i<20;i++) peer_id[i]='0'+random()%('Z'-'0'); // random numbers/letters between 0 and Z // make sure the torrent file exists struct stat file_stat; if(stat(argv[1],&file_stat)) { perror("Error opening file."); exit(1); } // map .torrent file into memory, and parse contents int fd = open(argv[1],O_RDONLY); char *buf = mmap(0,file_stat.st_size,PROT_READ,MAP_SHARED,fd,0); if(buf==(void*)-1) { perror("couldn't mmap file"); exit(1); } size_t off = 0; int error = 0; torrent = (struct bencode_dict*)ben_decode2(buf,file_stat.st_size,&off,&error); if(!torrent) { printf("Got error %d, perhaps a malformed torrent file?\n",error); exit(1); } // pull out the .info part, which has stuff about the file we're downloading info = (struct bencode*)ben_dict_get_by_str((struct bencode*)torrent,"info"); struct bencode_list* files = (struct bencode_list*)ben_dict_get_by_str(info,"files"); // multi-file case if(files) { for(int i=0;i<files->n;i++) { struct bencode* file = files->values[i]; struct bencode_list* path = (struct bencode_list*)ben_dict_get_by_str(file,"path"); printf("Filename %s/%s\n",((struct bencode_str*)ben_dict_get_by_str(info,"name"))->s,((struct bencode_str*)path->values[0])->s); // accumulate a total length so we know how many pieces there are file_length+=((struct bencode_int*)ben_dict_get_by_str(file,"length"))->ll; } } // single-file case else { FILE *outfile; struct bencode_str* name = (struct bencode_str*)ben_dict_get_by_str(info,"name"); if(name) { outfile = fopen(name->s,"r+"); file_length = ((struct bencode_int*)ben_dict_get_by_str(info,"length"))->ll; } } piece_length = ((struct bencode_int*)ben_dict_get_by_str(info,"piece length"))->ll; piece_status =calloc(1,sizeof(int)*(int)(file_length/piece_length+1)); /* compute the message digest and info_hash from the "info" field in the torrent */ size_t len; char info_hash[100]; char* encoded = ben_encode(&len,(struct bencode*)info); SHA1(encoded,len,digest); // digest is a global that holds the raw 20 bytes piece_occurence_value = calloc(1,sizeof(int)*(int)(file_length/piece_length+1)); // info_hash is a stringified version of the digest, for use in the announce URL memset(info_hash,0,100); for(int i=0;i<20;i++) sprintf(info_hash+3*i,"%%%02x",digest[i]); // compile a suitable announce URL for our document sprintf(announce_url,"%s?info_hash=%s&peer_id=%s&port=6881&left=%d",((struct bencode_str*)ben_dict_get_by_str((struct bencode*)torrent,"announce"))->s,info_hash,peer_id,file_length); printf("Announce URL: %s\n",announce_url); start_peers(); }
int main(int argc, char** argv) { if(argc<2) { fprintf(stderr,"Usage: ./hw4 <torrent file>\n"); exit(1); } setvbuf(stdout,screenbuf,_IOFBF,10000); // create a global peer_id for this session. Every peer_id should include -OBCS342-. for(int i=strlen(peer_id);i<20;i++) peer_id[i]='0'+random()%('Z'-'0'); // random numbers/letters between 0 and Z // make sure the torrent file exists struct stat file_stat; if(stat(argv[1],&file_stat)) { perror("Error opening file."); exit(1); } // map .torrent file into memory, and parse contents int fd = open(argv[1],O_RDONLY); char *buf = mmap(0,file_stat.st_size,PROT_READ,MAP_SHARED,fd,0); if(buf==(void*)-1) { perror("couldn't mmap file"); exit(1); } size_t off = 0; int error = 0; torrent = (struct bencode_dict*)ben_decode2(buf,file_stat.st_size,&off,&error); if(!torrent) { printf("Got error %d, perhaps a malformed torrent file?\n",error); exit(1); } // pull out the .info part, which has stuff about the file we're downloading info = (struct bencode*)ben_dict_get_by_str((struct bencode*)torrent,"info"); struct bencode_list* files = (struct bencode_list*)ben_dict_get_by_str(info,"files"); // multi-file case if(files) { for(int i=0;i<files->n;i++) { struct bencode* file = files->values[i]; struct bencode_list* path = (struct bencode_list*)ben_dict_get_by_str(file,"path"); printf("Filename %s/%s\n",((struct bencode_str*)ben_dict_get_by_str(info,"name"))->s,((struct bencode_str*)path->values[0])->s); // accumulate a total length so we know how many pieces there are file_length+=((struct bencode_int*)ben_dict_get_by_str(file,"length"))->ll; } } // single-file case else { struct bencode_str* name = (struct bencode_str*)ben_dict_get_by_str(info,"name"); if(name) { file_length = ((struct bencode_int*)ben_dict_get_by_str(info,"length"))->ll; } } fflush(stdout); piece_length = ((struct bencode_int*)ben_dict_get_by_str(info,"piece length"))->ll; // create our output file, and set up a piece_status array piece_status = calloc(1,sizeof(int)*(int)(file_length/piece_length+1)); //start with an empty bitfield /* compute the message digest and info_hash from the "info" field in the torrent */ size_t len; char info_hash[100]; char* encoded = ben_encode(&len,(struct bencode*)info); SHA1(encoded,len,digest); // digest is a global that holds the raw 20 bytes // info_hash is a stringified version of the digest, for use in the announce URL memset(info_hash,0,100); for(int i=0;i<20;i++) sprintf(info_hash+3*i,"%%%02x",digest[i]); // compile a suitable announce URL for our document sprintf(announce_url,"%s?info_hash=%s&peer_id=%s&port=6881&left=%d",((struct bencode_str*)ben_dict_get_by_str((struct bencode*)torrent,"announce"))->s,info_hash,peer_id,file_length); printf("Announce URL: %s\n",announce_url); fflush(stdout); start_peers(); // now benPeers has the peers list from handle_announcement; let's handle this // handle the binary case FD_ZERO(&readset); FD_ZERO(&writeset); // zero out the sets before adding things to them if(benPeers->type == BENCODE_STR) { printf("Got binary list of peers\n"); // replace all occurrences of pa with peerlist[i] struct peer_addr *peerlist = (struct peer_addr*)((struct bencode_str*)benPeers)->s; for(int i=0;i<((struct bencode_str*)benPeers)->len/6;i++) { struct in_addr a; a.s_addr = peerlist[i].addr; printf("Found peer %s:%d\n",inet_ntoa(a),ntohs(peerlist[i].port)); fprintf(stderr,"Connecting...\n"); struct peer_addr* peeraddr = (struct peer_addr*)&peerlist[i]; if(peer_connected(peeraddr->addr)) { fprintf(stderr,"Already connected\n"); } else { // open the socket int s = socket(AF_INET, SOCK_STREAM,0); if(s < 0){ perror("Error creating socket: "); } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = peeraddr->addr; addr.sin_port = peeraddr->port; // set up the timeout struct timeval tv; tv.tv_sec = 60; if(setsockopt(s,SOL_SOCKET,SO_RCVTIMEO, (char *)&tv, sizeof tv)) perror("setsockopt"); int res = connect(s, (struct sockaddr*)&addr, sizeof(addr)); if(res == -1) { fprintf(stderr,"Couldn't connect to %d \n", peeraddr->port); fflush(stderr); perror("Error while connecting: "); } else { printf("Connected!\n"); // for debugging purposes // register the new peer in our list of peers struct peer_state* peer = calloc(1,sizeof(struct peer_state)); peer->socket = s; // socket is current socket peer->ip= peeraddr->addr; // address is the address of the current peer peer->next = peers; // stick it at the front of the linked list peer->connected = 0; // we're not connected until we've done handshaking peer->choked = 1; peer->incoming = malloc(BUFSIZE); peer->bitfield = calloc(1,file_length/piece_length/8+1); // start with an empty bitfield peers = peer; // make me the head of the LL char protocol[] = "BitTorrent protocol"; unsigned char pstrlen = strlen(protocol); unsigned char msg[pstrlen+49]; msg[0] = pstrlen; memcpy(msg+1,protocol,pstrlen); memcpy(msg+1+pstrlen+8,digest,20); memcpy(msg+1+pstrlen+8+20,peer_id,20); // put handshaking msg into all peers' outgoing peer->outgoing_count = 0; peer->outgoing = malloc(BUFSIZE); memcpy(peer->outgoing+peer->outgoing_count, msg, sizeof msg); peer->outgoing_count += sizeof msg; FD_SET(s, &readset); // add s (fd of peer's socket) to the read and write sets FD_SET(s, &writeset); } } } } // handle the bencoded case else { for(int i=0;i<benPeers->n;i++) { printf("Got bencoded list of peers\n"); struct bencode *peer = benPeers->values[i]; char *address = ((struct bencode_str*)ben_dict_get_by_str(peer,"ip"))->s; unsigned short port = ((struct bencode_int*)ben_dict_get_by_str(peer,"port"))->ll; printf("Found peer %s:%d\n",address,port); fprintf(stderr,"Connecting...\n"); struct peer_addr *peeraddr = malloc(sizeof(struct peer_addr)); peeraddr->addr=inet_addr(address); peeraddr->port=htons(port); if(peer_connected(peeraddr->addr)) { fprintf(stderr,"Already connected\n"); } else { // open the socket int s = socket(AF_INET, SOCK_STREAM,0); if(s < 0){ perror("Error creating socket: "); } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = peeraddr->addr; addr.sin_port = peeraddr->port; // set up the timeout struct timeval tv; tv.tv_sec = 60; if(setsockopt(s,SOL_SOCKET,SO_RCVTIMEO, (char *)&tv, sizeof tv)) perror("setsockopt"); // in a more ideal world, this would break out of the current iteration of the loop int res = connect(s, (struct sockaddr*)&addr, sizeof(addr)); if(res == -1) { fprintf(stderr,"Couldn't connect to %d \n", peeraddr->port); fflush(stderr); perror("Error while connecting: "); } else { printf("Connected!\n"); // for debugging purposes // register the new peer in our list of peers struct peer_state* peer = calloc(1,sizeof(struct peer_state)); peer->socket = s; // socket is current socket peer->ip= peeraddr->addr; // address is the address of the current peer peer->next = peers; // stick it at the front of the linked list peer->connected = 0; // we're not connected until we've done handshaking peer->choked = 1; peer->incoming = malloc(BUFSIZE); peer->bitfield = calloc(1,file_length/piece_length/8+1); // start with an empty bitfield peers = peer; // make me the head of the LL char protocol[] = "BitTorrent protocol"; unsigned char pstrlen = strlen(protocol); unsigned char msg[pstrlen+49]; msg[0] = pstrlen; memcpy(msg+1,protocol,pstrlen); memcpy(msg+1+pstrlen+8,digest,20); memcpy(msg+1+pstrlen+8+20,peer_id,20); // put handshaking msg into all peers' outgoing peer->outgoing_count = 0; peer->outgoing = malloc(BUFSIZE); memcpy(peer->outgoing+peer->outgoing_count, msg, sizeof msg); peer->outgoing_count += sizeof msg; FD_SET(s, &readset); // add s (fd of peer's socket) to the read and write sets FD_SET(s, &writeset); } } } } while(1) { fd_set rtemp, wtemp; FD_ZERO(&rtemp); FD_ZERO(&wtemp); // zero out the temp sets memcpy(&rtemp, &readset, sizeof readset); memcpy(&wtemp, &writeset, sizeof writeset); // timeout for the select call? probably too high struct timeval tv; tv.tv_sec = 0; int selected = select(FD_SETSIZE, &rtemp, &wtemp, NULL, &tv); struct peer_state* peerTemp = peers; while(peerTemp) { //printf("Current peer name is %s\n", inet_ntoa(*(struct in_addr *)&peerTemp->ip)); if(FD_ISSET(peerTemp->socket,&rtemp)) { // if we're reading if(!peerTemp->connected) { recv_handshake(peerTemp); // can all receive at once? or not? FD_SET(peerTemp->socket, &readset); } else { int ret = receive_message(peerTemp); // we can just use the current receive_message fcn if(ret == 2) { handle_message(peerTemp); } // else just keep on chuggin } //FD_SET(peerTemp->socket, &readset); // then add it to the readset, always } if(FD_ISSET(peerTemp->socket,&wtemp)) { // if we're writing send_message(peerTemp); } peerTemp = peerTemp->next; } // if we don't have any missing blocks, breeaaaakk if(missing_blocks() == 0) break; } }
/* This needs to be fixed to work properly for multi-file torrents. specifically, it needs to create the proper directory structure, rather than just concatenate directory and file names. */ void write_block(char* data, int piece, int offset, int len, int acquire_lock) { FILE *outfile; int accumulated_file_length = 0; int block_start = piece*piece_length+offset; struct bencode_list* files = (struct bencode_list*)ben_dict_get_by_str(info,"files"); // multi-file case if(files) { for(int i=0;i<files->n;i++) { struct bencode* file = files->values[i]; struct bencode_list* path = (struct bencode_list*)ben_dict_get_by_str(file,"path"); // printf("Filename %s/%s\n",((struct bencode_str*)ben_dict_get_by_str(info,"name"))->s,((struct bencode_str*)path->values[0])->s); // accumulate a total length so we know how many pieces there are int file_length=((struct bencode_int*)ben_dict_get_by_str(file,"length"))->ll; printf("start %d len %d accum %d filelen %d\n",block_start,len,accumulated_file_length,file_length); fflush(stdout); // at least part of the block belongs in this file if((block_start >= accumulated_file_length) && (block_start < accumulated_file_length+file_length)) { char filename[255]; mkdir(((struct bencode_str*)ben_dict_get_by_str(info,"name"))->s,0777); chmod(((struct bencode_str*)ben_dict_get_by_str(info,"name"))->s,07777); sprintf(filename,"%s/",((struct bencode_str*)ben_dict_get_by_str(info,"name"))->s); for(int j=0;j<path->n;j++) { if(j<(path->n-1)) { sprintf(filename+strlen(filename),"%s/",((struct bencode_str*)path->values[j])->s); mkdir(filename,0777); chmod(filename,07777); } else sprintf(filename+strlen(filename),"%s",((struct bencode_str*)path->values[j])->s); } int outfile = open(filename,O_RDWR|O_CREAT,0777); if(outfile == -1) { fprintf(stderr,"filename: %s\n",filename); perror("Couldn't open file for writing"); exit(1); } int offset_into_file = block_start - accumulated_file_length; int remaining_file_length = file_length - offset_into_file; lseek(outfile,offset_into_file,SEEK_SET); if(remaining_file_length > len) { write(outfile,data,len); close(outfile); } else { if(debug) { fprintf(stderr,"Uh-oh, write crossing file boundaries... watch out!\n"); fprintf(stderr,"Len %d offset %d filelen %d remaining file len %d\n",len,offset_into_file,file_length,remaining_file_length); fflush(stdout); } write(outfile,data,remaining_file_length); close(outfile); write_block(data+remaining_file_length,piece,offset+remaining_file_length,len-remaining_file_length,0); } } accumulated_file_length+=file_length; } } // single-file case else { struct bencode_str* name = (struct bencode_str*)ben_dict_get_by_str(info,"name"); if(name) { int outfile = open(name->s,O_RDWR|O_CREAT,0777); file_length = ((struct bencode_int*)ben_dict_get_by_str(info,"length"))->ll; // write the data to the right spot in the file lseek(outfile,piece*piece_length+offset,SEEK_SET); write(outfile, data,len); close(outfile); } else { printf("No name?\n"); exit(1); } } }