785 lines
22 KiB
C
785 lines
22 KiB
C
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <netdb.h>
|
|
#include <errno.h>
|
|
#include <sys/socket.h>
|
|
#include <arpa/inet.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <pthread.h>
|
|
|
|
#ifdef __APPLE__
|
|
#include "./endian.h"
|
|
#else
|
|
#include <endian.h>
|
|
#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<hash_size; i++)
|
|
{
|
|
hash[i] = shabuffer[i];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Count how many times a character occurs in a string
|
|
*/
|
|
int count_occurences(char string[], char c)
|
|
{
|
|
size_t i=0;
|
|
int count=0;
|
|
for(i=0; i<strlen(string); i++)
|
|
{
|
|
if(string[i] == c)
|
|
{
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
// Adapted from: https://stackoverflow.com/a/35452093/
|
|
/*
|
|
* Convert a string of hexidecimal into a string of bytes
|
|
*/
|
|
uint8_t* hex_to_bytes(const char* string) {
|
|
|
|
if(string == NULL)
|
|
return NULL;
|
|
|
|
size_t slength = strlen(string);
|
|
if((slength % 2) != 0) // must be even
|
|
return NULL;
|
|
|
|
size_t dlength = slength / 2;
|
|
|
|
uint8_t* data = malloc(dlength);
|
|
memset(data, 0, dlength);
|
|
|
|
size_t index = 0;
|
|
while (index < slength) {
|
|
char c = string[index];
|
|
int value = 0;
|
|
if(c >= '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;i<peercount;i++) {
|
|
csc_peer_t peer;
|
|
uint8_t* peer_data = &rio_buf[REPLY_HEADER_SIZE + 12*i];
|
|
sprintf(peer.ip, "%u.%u.%u.%u", peer_data[0], peer_data[1], peer_data[2], peer_data[3]);
|
|
sprintf(peer.port, "%u", be16toh(*((uint16_t*)&peer_data[4])));
|
|
|
|
(*peers)[i] = peer;
|
|
}
|
|
Close(tracker_socket);
|
|
return peercount;
|
|
}
|
|
|
|
void service_block_request(void* arg) {
|
|
block_request_t* request = (block_request_t*)arg;
|
|
printf("servicing block request.\n");
|
|
|
|
char request_header[32];
|
|
memcpy(request_header, request->request, 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;i<casc_file->blockcount;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; i<casc_file->peercount; i++)
|
|
{
|
|
if (casc_file->peers[i].good)
|
|
{
|
|
peer = (casc_file->peers[i]);
|
|
}
|
|
}
|
|
|
|
for (int i=0; i<uncomp_count; i++)
|
|
{
|
|
pthread_mutex_lock(&casc_file->mutex);
|
|
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 <cascade file(s)> <tracker server ip> <tracker server port> <peer ip> <peer port>.\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<casc_count; j++)
|
|
{
|
|
start_peer(cascade_files[j]);
|
|
}
|
|
|
|
pthread_join(server, NULL);
|
|
|
|
exit(EXIT_SUCCESS);
|
|
}
|