예제 #1
0
파일: hw4.c 프로젝트: raghavan/networking
void start_peers() {

    CURL *curl;
    CURLcode res;

    curl = curl_easy_init();
    {
        int bytes_left = 0;
        bytes_left = missing_blocks() * piece_length;
        if(piece_status[get_total_pieces_available()-1] == PIECE_EMPTY){
            bytes_left -= piece_length;
            bytes_left += file_length%piece_length;
        }
    printf("Announce URL: %s\n",announce_url);
    }
    if(curl) {
        curl_easy_setopt(curl, CURLOPT_URL, announce_url);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1);
        curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 1);

        FILE *anno = fopen("/tmp/anno.tmp","w+");
        if(!anno) {
            perror("couldn't create temporary file\n");
        }

        int attempts=0;
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, anno); 
        while((res = curl_easy_perform(curl)) !=CURLE_OK && 
                    attempts < 5) {
            fprintf(stderr, "curl_easy_perform() failed: %s\n",
                            curl_easy_strerror(res));
            attempts++;
        }
        fclose(anno);

        if (attempts<5) {
            struct stat anno_stat;
            if(stat("/tmp/anno.tmp",&anno_stat)) {
                perror("couldn't stat /tmp/anno.tmp");
                exit(1);
            }
            // the announcement document is in /tmp/anno.tmp. 
            // so map that into memory, then call handle_announcement on the returned pointer
            handle_announcement(mmap(0,anno_stat.st_size,PROT_READ,MAP_SHARED,open("/tmp/anno.tmp",O_RDONLY),0),anno_stat.st_size);
        }
        curl_easy_cleanup(curl);
    }
}
예제 #2
0
파일: hw4.c 프로젝트: raghavan/networking
void listen_from_peers() 
{
    struct peer_state* peer = peers;
    unsigned int msglen = 0;
    while(missing_blocks() > 0)
    {
        fd_set fdrset_clone;
        fd_set fdwset_clone;
        FD_COPY(&fdrset,&fdrset_clone);
        FD_COPY(&fdwset,&fdwset_clone);
        int rdy = select(FD_SETSIZE,&fdrset_clone,&fdwset_clone,0, 0);
        if(rdy <= 0) {
            continue;
        }
        peer = peers;
        if(active_peers() == 0){
            break;
        }
        while(peer){
        
            if(FD_ISSET(peer->socket,&fdwset_clone)){
                if(peer->connected == 0){
                    //to send the handshake if it is not connected
                    send_handshake(peer);
                }
                else if(peer->send_count > 0) {
                    //to send the have/interested/piece messages to peers
                    send_buffed_msg(peer);
                }
            }

            if(FD_ISSET(peer->socket,&fdrset_clone)) 
            {
                int newbytes = recv(peer->socket,peer->incoming+peer->count,BUFSIZE-peer->count,0);
                if(newbytes <= 0){ 
                    peer->trying_to_connect = 0;
                    if(newbytes == 0){
                        piece_status[peer->requested_piece] = PIECE_EMPTY;
                    }
                    disconnet_peer(peer);
                    reconnect_peer(peer);
                    peer = peer->next;
                    continue; 
                } 
                peer->count += newbytes;
                if(!peer->handshaked){                    
                    peer->count -= peer->incoming[0]+49;
                    if(peer->count) {
                        memmove(peer->incoming,peer->incoming + peer->incoming[0]+49,peer->count);
                    }
                    peer->handshaked = 1;
                }

               if(memcmp(peer->incoming+peer->incoming[0]+8+20,"-UICCS450-",strlen("-UICCS450-"))==0) {
                    fprintf(stderr,"Caught a CS450 peer, exiting.\n");
                    disconnet_peer(peer);
                    continue;
                }   

                while(peer->count >= (ntohl(((int*)peer->incoming)[0])) + 4){ 
                msglen = ntohl(((int*)peer->incoming)[0]);

                   switch(peer->incoming[4]) {
                        // CHOKE
                        case 0: {
                                    if(debug)
                                        fprintf(stderr,"Choke\n");
                                    peer->choked = 1;
                                    piece_status[peer->requested_piece]=PIECE_EMPTY;
                                    peer->requested_piece = -1;
                                    break;
                                }
                                // UNCHOKE
                        case 1: {
                                    if(debug)
                                        fprintf(stderr,"Unchoke\n");
                                    peer->choked = 0;
                                    peer->requested_piece = next_piece(-1,peer);    
                                    request_block(peer,peer->requested_piece,0);
                                    break;
                                }
                                //Interested
                        case 2: {
                                    //dev_notifier();
                                    send_choke_unchoke_msg(peer,0); //ischoked = 0
                                    peer->not_interested = 0;
                                    break;
                                }
                                //Not interested
                        case 3: {
                                    //dev_notifier();
                                    peer->not_interested = 1;
                                    break;
                                }
                                // HAVE -- update the bitfield for this peer
                        case 4: {
                                    int piece = ntohl(*((int*)&peer->incoming[5]));
                                    int bitfield_byte = piece/8;
                                    int bitfield_bit = piece%8;
                                    if(debug)
                                        fprintf(stderr,"Have %d\n",piece);

                                    peer->bitfield[bitfield_byte] |= 1 << (7 - bitfield_bit);
                                    piece_occurence_value[piece]++;
                                    send_interested(peer);
                                    break;
                                }
                                // BITFIELD -- set the bitfield for this peer
                        case 5:
                                //peer->choked = 0; //commenting it according to prof's note in class
                                if(debug) 
                                    printf("Bitfield of length %d\n",msglen-1);
                                int fieldlen = msglen - 1;
                                if(fieldlen != (file_length/piece_length/8+1)) {
                                    disconnet_peer(peer);
                                    if(active_peers() == 0){
                                        break;
                                    }
                                    peer = peer->next;
                                    continue;
                                }               
                                memcpy(peer->bitfield,peer->incoming+5,fieldlen);
                                read_bit_maps(peer->bitfield,fieldlen);
                                send_interested(peer);
                                break;
                                //Request piece
                        case 6: { 
                                if(peer->i_choked_it == 1) 
                                    break;
                                decode_request_send_piece_to_peer(peer);
                                }       
                                break;                         
                                // PIECE
                        case 7: {
                                    //make the tit for tatter
                                    peer->send_recv_balancer++;
                                    if(peer->i_choked_it && peer->send_recv_balancer == 0){
                                        peer->i_choked_it = 0;
                                        send_choke_unchoke_msg(peer,0); //unchoke peer
                                    }
                                    int piece = ntohl(*((int*)&peer->incoming[5]));
                                    int offset = ntohl(*((int*)&peer->incoming[9]));
                                    int datalen = msglen - 9;

                                    fprintf(stderr,"Writing piece %d, offset %d, ending at %d\n",piece,offset,piece*piece_length+offset+datalen);
                                    write_block(peer->incoming+13,piece,offset,datalen,1);
                                    draw_state();
                                    offset+=datalen;
                                    if(offset==piece_length || (piece*piece_length+offset == file_length) ) {
                                        broadcast_have_msg(piece);
                                        draw_state();
                                        if(debug) 
                                            fprintf(stderr,"Reached end of piece %d at offset %d\n",piece,offset);

                                        peer->requested_piece=next_piece(piece,peer);
                                        offset = 0;
                                    }
                                    request_block(peer,peer->requested_piece,offset);
                                    break;                                  
                                }
                    }
                    drop_message(peer);      
                }
            }
            peer = peer->next;
        }
    }
    return;
}
예제 #3
0
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; 							
}
}