#include #include #include #include #include #include #include #include #include #ifdef __APPLE__ #include "./endian.h" #else #include #endif #include "./cascade.h" #include "./sha256.h" char tracker_ip[IP_LEN]; char tracker_port[PORT_LEN]; char my_ip[IP_LEN]; char my_port[PORT_LEN]; csc_file_t* cascade_files[64]; int cascade_amount; /* * Frees global resources that are malloc'ed during peer downloads. */ void free_resources(csc_file_t* casc_file) { free(casc_file->peers); csc_free_file(casc_file); } /* * Gets a sha256 hash of a specified file, sourcefile. The hash itself is * placed into the given variable 'hash'. Any size can be created, but a * a normal size for the hash would be given by the global variable * 'SHA256_HASH_SIZE', that has been defined in sha256.h */ void get_file_sha(const char* sourcefile, uint8_t hash[32], int size) { int casc_file_size; FILE* fp = fopen(sourcefile, "rb"); if (fp == 0) { printf("Failed to open source: %s\n", sourcefile); return; } fseek(fp, 0L, SEEK_END); casc_file_size = ftell(fp); fseek(fp, 0L, SEEK_SET); char buffer[casc_file_size]; fread(buffer, casc_file_size, 1, fp); fclose(fp); get_data_sha(buffer, hash, casc_file_size, size); } csc_file_t* check_blocks(csc_file_t* casc_file_data) { casc_file_data->completed = 1; char* destination = casc_file_data->outputfile; FILE* fp = fopen(destination, "a+w"); if (fp == NULL) { printf("Failed to open destination file %s\n", destination); csc_free_file(casc_file_data); return NULL; } void* buffer = malloc(casc_file_data->blocksize); if (buffer == NULL) { printf("No block buffer asigned: %ld\n", casc_file_data->blocksize); csc_free_file(casc_file_data); fclose(fp); return NULL; } SHA256_CTX shactx; for(unsigned long long i = 0; i < casc_file_data->blockcount; i++) { uint8_t* shabuffer = malloc(sizeof(uint8_t) * SHA256_HASH_SIZE); unsigned long long size = casc_file_data->blocks[i].length; if (fread(buffer, size, 1, fp) != 1) { break; } sha256_init(&shactx); sha256_update(&shactx, buffer, size); sha256_final(&shactx, shabuffer); if (memcmp((&(&casc_file_data->blocks[i])->hash)->x, shabuffer, 32) == 0) { (&casc_file_data->blocks[i])->completed = 1; } else { (&casc_file_data->blocks[i])->completed = 0; casc_file_data->completed = 0; } } free(buffer); if (casc_file_data->completed) { rewind(fp); buffer = malloc(casc_file_data->targetsize); uint8_t* shabuffer = malloc(sizeof(uint8_t) * SHA256_HASH_SIZE); fread(buffer, casc_file_data->targetsize, 1, fp); sha256_init(&shactx); sha256_update(&shactx, buffer, casc_file_data->targetsize); sha256_final(&shactx, shabuffer); if (!(memcmp((&casc_file_data->targethash)->x, shabuffer, 32) == 0)) { casc_file_data->completed = 0; for (unsigned long long i = 0;i < casc_file_data->blockcount;i++) { (&casc_file_data->blocks[i])->completed = 0; } } free(buffer); } fclose(fp); return casc_file_data; } /* * Gets a sha256 hash of specified data, sourcedata. The hash itself is * placed into the given variable 'hash'. Any size can be created, but a * a normal size for the hash would be given by the global variable * 'SHA256_HASH_SIZE', that has been defined in sha256.h */ void get_data_sha(const char* sourcedata, unsigned char* hash, uint32_t data_size, int hash_size) { SHA256_CTX shactx; uint8_t* shabuffer = malloc(sizeof(uint8_t) * hash_size); sha256_init(&shactx); sha256_update(&shactx, sourcedata, data_size); sha256_final(&shactx, shabuffer); for (int i=0; i= '0' && c <= '9') value = (c - '0'); else if (c >= 'A' && c <= 'F') value = (10 + (c - 'A')); else if (c >= 'a' && c <= 'f') value = (10 + (c - 'a')); else { free(data); return NULL; } data[(index/2)] += value << (((index + 1) % 2) * 4); index++; } return data; } /* * Parses a cascade file, given the sourcepath input and destination, which may or may not exist. * Returns a pointer to a datastructure describing the file, or NULL if the file could not be parsed */ csc_file_t* csc_parse_file(const char* sourcefile) { FILE* fp = fopen(sourcefile, "rb"); if (fp == 0) { printf("Failed to open source: %s\n", sourcefile); return NULL; } const int FILE_HEADER_SIZE = 8+8+8+8+32; char header[FILE_HEADER_SIZE]; if (fread(header, 1, FILE_HEADER_SIZE, fp) != FILE_HEADER_SIZE) { printf("Failed to read magic 8 bytes header from file\n"); fclose(fp); return NULL; } if (memcmp(header, "CASCADE1", 8) != 0) { printf("File does not contain magic 8 bytes in header\n"); fclose(fp); return NULL; } csc_file_t* casc_file_data = (csc_file_t*)malloc(sizeof(csc_file_t)); uint8_t hash_buf[32]; get_file_sha(sourcefile, hash_buf, 32); memcpy(casc_file_data->cascadehash,hash_buf,32); char* outputfile = malloc(strlen(sourcefile)-7); memcpy(outputfile, sourcefile, strlen(sourcefile)-8); outputfile[strlen(sourcefile)-8] = '\0'; casc_file_data->outputfile = outputfile; printf("Downloading to: %s\n", casc_file_data->outputfile); casc_file_data->targetsize = be64toh(*((unsigned long long*)&header[16])); casc_file_data->blocksize = be64toh(*((unsigned long long*)&header[24])); uint8_t x[32]; for (int i = 0; i < 32; i++) { x[i] = (uint8_t)(*((unsigned long long*)&header[32+i])); } csc_hashdata_t* hash = malloc(sizeof(csc_hashdata_t)); memcpy(hash->x,x,SHA256_HASH_SIZE); casc_file_data->targethash = *hash; casc_file_data->blockcount = 1 + floor( (casc_file_data->targetsize - 1.0)/casc_file_data->blocksize ); casc_file_data->trailblocksize = casc_file_data->targetsize - ( (casc_file_data->blockcount - 1) * casc_file_data->blocksize ); casc_file_data->completed = 0; csc_block_t* block_list = malloc( sizeof(csc_block_t) * casc_file_data->blockcount ); casc_file_data->blocks = block_list; for (unsigned long long b = 0;b < casc_file_data->blockcount; b++) { csc_block_t* block = &(block_list[b]); block->index = b; block->offset = b * casc_file_data->blocksize; if (b == casc_file_data->blockcount - 1 ) { block->length = casc_file_data->trailblocksize; } else { block->length = casc_file_data->blocksize; } block->completed = 0; uint8_t block_x[32]; if (fread(block_x, 1, 32, fp) != 32) { printf("Cascade file not readable\n"); fclose(fp); return NULL; } csc_hashdata_t* hash = malloc(sizeof(csc_hashdata_t)); memcpy(hash->x,block_x,SHA256_HASH_SIZE); block->hash = *hash; } fclose(fp); return check_blocks(casc_file_data); } /* * Releases the memory allocated by a file datastructure */ void csc_free_file(csc_file_t* file) { free(file->blocks); file->blocks = NULL; free(file); } size_t readbytes(int sock, uint8_t* buffer, size_t count) { size_t remaining = count; while(remaining > 0) { size_t read = recv(sock, buffer, remaining, 0); if (read == 0) return 0; buffer += read; remaining -= read; } return count; } /* * Get a specified block from a peer on the network. The block is retrieved and then inserted directly into * the appropriate data file at the appropriate location. */ void get_block(csc_block_t* block, csc_peer_t peer, unsigned char* hash, char* output_file) { printf("Attempting to get block %ld from %s:%s for %s\n", block->index, peer.ip, peer.port, output_file); int buffer_size; if (block->length + PEER_RESPONSE_HEADER_SIZE > MAX_LINE) { buffer_size = block->length + PEER_RESPONSE_HEADER_SIZE; } else { buffer_size = MAX_LINE; } char rio_buf[buffer_size]; rio_t rio; int peer_socket; peer_socket = Open_clientfd(peer.ip, peer.port); Rio_readinitb(&rio, peer_socket); struct ClientRequest request; memcpy(request.protocol, "CASCADE1", 8); for (int i = 0;i<16;i++) { request.reserved[i] = 0; } request.block_num = be64toh(block->index); memcpy(request.hash, hash, 32); memcpy(rio_buf, &request, PEER_REQUEST_HEADER_SIZE); Rio_writen(peer_socket, rio_buf, PEER_REQUEST_HEADER_SIZE); Rio_readnb(&rio, rio_buf, buffer_size); char reply_header[PEER_RESPONSE_HEADER_SIZE]; memcpy(reply_header, rio_buf, PEER_RESPONSE_HEADER_SIZE); uint64_t msglen = be64toh(*((unsigned long long*)&reply_header[1])); if (msglen == 0) { Close(peer_socket); return; } if (reply_header[0] != 0) { char* error_buf = malloc(msglen + 1); if (error_buf == NULL) { printf("Tracker error %d and out-of-memory reading error\n", reply_header[0]); Close(peer_socket); return; } memset(error_buf, 0, msglen + 1); memcpy(error_buf, &rio_buf[PEER_RESPONSE_HEADER_SIZE], msglen); printf("Peer gave error: %d - %s\n", reply_header[0], error_buf); free(error_buf); Close(peer_socket); return; } uint8_t* shabuffer = malloc(sizeof(uint8_t) * SHA256_HASH_SIZE); SHA256_CTX shactx; sha256_init(&shactx); sha256_update(&shactx, &rio_buf[PEER_RESPONSE_HEADER_SIZE], msglen); sha256_final(&shactx, shabuffer); if (memcmp(shabuffer, (&block->hash)->x, SHA256_HASH_SIZE) != 0) { printf("Not the same hash\n"); Close(peer_socket); return; } FILE* fp = fopen(output_file, "rb+"); if (fp == 0) { printf("Failed to open destination: %s\n", output_file); Close(peer_socket); return; } fseek(fp, block->offset, SEEK_SET); fwrite(&rio_buf[PEER_RESPONSE_HEADER_SIZE],msglen,1,fp); printf("Got block %ld. Wrote from %ld to %ld\n", block->index, block->offset, block->offset+msglen-1); Close(peer_socket); fclose(fp); } /* * Get a list of peers on the network from a tracker. Note that this query is doing double duty according to * the protocol, and by asking for a list of peers we are also enrolling on the network ourselves. */ int send_tracker_request(csc_peer_t** peers, uint8_t hash[32], int command) { rio_t rio; uint8_t rio_buf[MAX_LINE]; int tracker_socket; tracker_socket = Open_clientfd(tracker_ip, tracker_port); Rio_readinitb(&rio, tracker_socket); struct RequestHeader request_header; memcpy(request_header.protocol, "CASC", 4); request_header.version = htonl(1); request_header.command = htonl(command); request_header.length = htonl(BODY_SIZE); memcpy(rio_buf, &request_header, HEADER_SIZE); struct RequestBody request_body; memcpy(request_body.hash, hash, 32); inet_aton(my_ip, &request_body.ip); request_body.port = be16toh(atol(my_port)); memcpy(&rio_buf[HEADER_SIZE], &request_body, BODY_SIZE); Rio_writen(tracker_socket, rio_buf, MESSAGE_SIZE); Rio_readnb(&rio, rio_buf, MAX_LINE); char reply_header[REPLY_HEADER_SIZE]; memcpy(reply_header, rio_buf, REPLY_HEADER_SIZE); uint32_t msglen = ntohl(*(uint32_t*)&reply_header[1]); if (msglen == 0) { Close(tracker_socket); return 0; } if (reply_header[0] != 0) { char* error_buf = malloc(msglen + 1); if (error_buf == NULL) { printf("Tracker error %d and out-of-memory reading error\n", reply_header[0]); Close(tracker_socket); return 0; } memset(error_buf, 0, msglen + 1); memcpy(error_buf, &rio_buf[REPLY_HEADER_SIZE], msglen); // Fixed by Rune printf("Tracker gave error: %d - %s\n", reply_header[0], error_buf); free(error_buf); Close(tracker_socket); return 0; } if (msglen % 12 != 0) { printf("LIST response from tracker was length %u but should be evenly divisible by 12\n", msglen); Close(tracker_socket); return 0; } int peercount = msglen/12; *peers = malloc(peercount * sizeof(csc_peer_t)); for (int i = 0;irequest, 32); if (memcmp(request_header, "CASCADE1", 8) != 0) { char response[9]; response[0] = 4; for (int j = 1 ; j < 9; j++) { response[j] = 0; } Rio_writen(request->socket,&response,9); close(request->socket); return; } for (int i = 8;i<16;i++) { if ((int)request_header[i] != 0) { char response[9]; response[0] = 4; for (int j = 1 ; j < 9; j++) { response[j] = 0; } Rio_writen(request->socket,&response,9); close(request->socket); return; } } uint64_t block_number = be64toh(*((unsigned long long*)&request_header[24])); unsigned char hash[32]; memcpy(hash, &(request->request[32]), 32); csc_file_t* casc_file = NULL; for (int i = 0; i < cascade_amount; i++){ if (memcmp(cascade_files[i]->cascadehash,hash,32) == 0) { casc_file = cascade_files[i]; break; } } if (casc_file == NULL) { char response[9]; response[0] = 1; for (int j = 1 ; j < 9; j++) { response[j] = 0; } Rio_writen(request->socket,&response,9); close(request->socket); return; } if (block_number >= casc_file->blockcount) { char response[9]; response[0] = 3; for (int j = 1 ; j < 9; j++) { response[j] = 0; } Rio_writen(request->socket,&response,9); close(request->socket); return; } csc_block_t* block = (&casc_file->blocks[block_number]); pthread_mutex_lock(&casc_file->mutex); if (!block->completed) { char response[9]; response[0] = 2; for (int j = 1 ; j < 9; j++) { response[j] = 0; } Rio_writen(request->socket,&response,9); pthread_mutex_unlock(&casc_file->mutex); close(request->socket); return; } FILE* fp = fopen(casc_file->outputfile, "rb+"); fseek(fp, block->offset, SEEK_SET); char buffer[block->length]; fread(&buffer,1,block->length,fp); fclose(fp); pthread_mutex_unlock(&casc_file->mutex); char response[9 + block->length]; response[0] = 0; char* block_length = be64toh(block->length); memcpy(&response[1],&block_length,8); memcpy(&response[9],buffer,block->length); Rio_writen(request->socket, &response, 9+block->length); close(request->socket); } void server_mt() { char rio_buf[64]; int socket = open_listenfd(my_port); struct sockaddr* peer_address = malloc(sizeof(struct sockaddr)); int address_length; while (1) { rio_t rio; int new_socket = Accept(socket, peer_address, &address_length); Rio_readinitb(&rio, new_socket); Rio_readnb(&rio, rio_buf, 64); pthread_t request_handler; block_request_t* request = malloc(sizeof(block_request_t)); memcpy(request->request,rio_buf,64); request->peer_address = peer_address; request->rio = rio; request->socket = new_socket; pthread_create(&request_handler, NULL, service_block_request, (void*)request); //pthread_join(&request_handler, NULL); } } void client_mt(void* arg) { csc_file_t* casc_file = (csc_file_t*)arg; printf("Starting client for %s.\n", casc_file->outputfile); csc_block_t** queue = malloc(casc_file->blockcount * sizeof(csc_block_t*)); while (!casc_file->completed){ while (casc_file->peercount == 0) { casc_file->peercount = send_tracker_request(&(casc_file->peers), casc_file->cascadehash, 1); if (casc_file->peercount == 0) { printf("No peers were found. Will try again in %d seconds\n", PEER_REQUEST_DELAY); fflush(stdout); sleep(PEER_REQUEST_DELAY); } } printf("Found %d peer(s)\n", casc_file->peercount); int uncomp_count = 0; for (unsigned long long i = 0;iblockcount;i++) { if ((&casc_file->blocks[i])->completed == 0) { queue[uncomp_count] = &casc_file->blocks[i]; uncomp_count++; } } csc_peer_t peer = (casc_file->peers[0]); // Get a good peer if one is available for (int i=0; ipeercount; i++) { if (casc_file->peers[i].good) { peer = (casc_file->peers[i]); } } for (int i=0; imutex); get_block(queue[i], peer, casc_file->cascadehash, casc_file->outputfile); pthread_mutex_unlock(&casc_file->mutex); } printf("\n"); pthread_mutex_lock(&casc_file->mutex); casc_file = check_blocks(casc_file); pthread_mutex_unlock(&casc_file->mutex); if (!casc_file->completed) { send_tracker_request(&(casc_file->peers), casc_file->cascadehash,1); } } free(casc_file->peers); free(queue); printf("Fully downloaded %s\n",casc_file->outputfile); } void start_peer(char* cascade_file){ printf("Managing download only for: %s\n", cascade_file); if (access(cascade_file, F_OK ) != 0 ) { fprintf(stderr, ">> File %s does not exist\n", cascade_file); exit(EXIT_FAILURE); } csc_file_t *casc_file; casc_file = csc_parse_file(cascade_file); cascade_files[cascade_amount] = casc_file; cascade_amount++; // start client and server on separate threads pthread_t client; // subscribe to peer casc_file->peercount = send_tracker_request(&(casc_file->peers), casc_file->cascadehash, 2); pthread_create(&client, NULL, client_mt, (void*)casc_file); } /* * The entry point for the code. Parses command line arguments and starts up the appropriate peer code. */ int main(int argc, char **argv) { if (argc != MAIN_ARGNUM + 1) { fprintf(stderr, "Usage: %s .\n", argv[0]); exit(EXIT_FAILURE); } else if (!is_valid_ip(argv[2])) { fprintf(stderr, ">> Invalid tracker IP: %s\n", argv[2]); exit(EXIT_FAILURE); } else if (!is_valid_port(argv[3])) { fprintf(stderr, ">> Invalid tracker port: %s\n", argv[3]); exit(EXIT_FAILURE); } else if (!is_valid_ip(argv[4])) { fprintf(stderr, ">> Invalid peer IP: %s\n", argv[4]); exit(EXIT_FAILURE); } else if (!is_valid_port(argv[5])) { fprintf(stderr, ">> Invalid peer port: %s\n", argv[5]); exit(EXIT_FAILURE); } snprintf(tracker_ip, IP_LEN, argv[2]); snprintf(tracker_port, PORT_LEN, argv[3]); snprintf(my_ip, IP_LEN, argv[4]); snprintf(my_port, PORT_LEN, argv[5]); char cas_str[strlen(argv[1])]; snprintf(cas_str, strlen(argv[1])+1, argv[1]); char delim[] = ":"; int casc_count = count_occurences(argv[1], ':') + 1; char *cascade_files[casc_count]; char *ptr = strtok(cas_str, delim); int i = 0; while (ptr != NULL) { if (strstr(ptr, ".cascade") != NULL) { cascade_files[i++] = ptr; ptr = strtok(NULL, delim); } else { printf("Abort on %s\n", ptr); fprintf(stderr, ">> Invalid cascade file: %s\n", ptr); exit(EXIT_FAILURE); } } pthread_t server; pthread_create(&server, NULL, server_mt, NULL); for (int j=0; j