mirror of
https://github.com/Hizenberg469/btClient.git
synced 2026-04-19 17:52:24 +03:00
first commit
This commit is contained in:
588
src/BProtocol.cpp
Normal file
588
src/BProtocol.cpp
Normal file
@@ -0,0 +1,588 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <BProtocol.hpp>
|
||||
#include <torrent_structure.hpp>
|
||||
|
||||
#include <debug.hpp>
|
||||
|
||||
|
||||
/*************************Decode Functions Implementation*************************/
|
||||
|
||||
long long _read_file(const std::string& torrentFile, char*& raw_data) {
|
||||
|
||||
long long size;
|
||||
|
||||
std::ifstream t_file(torrentFile, std::ios::in | std::ios::binary | std::ios::ate);
|
||||
|
||||
if (t_file.is_open()) {
|
||||
|
||||
size = (std::streampos)t_file.tellg();
|
||||
|
||||
//std::cout << "Size of file : " << size << " bytes\n";
|
||||
|
||||
raw_data = new char[size+1LL];
|
||||
t_file.seekg(0, std::ios::beg);
|
||||
|
||||
t_file.read(raw_data, size);
|
||||
|
||||
t_file.close();
|
||||
|
||||
std::cout << "Raw Data reading from torrent file complete!!!!\n";
|
||||
|
||||
return size;
|
||||
}
|
||||
else {
|
||||
|
||||
std::cout << "Unable to open torrent file to read raw data\n";
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
long long int integer_decode(char*& raw_data, long long int &size)
|
||||
{
|
||||
long long int res = 0;
|
||||
|
||||
while ((*raw_data) != 'e')
|
||||
{
|
||||
res = res * 10 + ((*raw_data) - 48);
|
||||
raw_data++;
|
||||
size--;
|
||||
}
|
||||
|
||||
//now I am at e , since it is useless lets increment this too;
|
||||
raw_data++;
|
||||
size--;
|
||||
|
||||
return res; //retrun the converted data;
|
||||
|
||||
|
||||
}
|
||||
|
||||
std::string string_decode(char*& raw_data, long long int& size) {
|
||||
//to parse the string we first need to extract the length
|
||||
|
||||
int length = 0;
|
||||
while ((*(raw_data)) != ':')
|
||||
{
|
||||
length = length * 10 + ((*raw_data) - 48);
|
||||
//std::cout << ((*raw_data));
|
||||
(raw_data)++;
|
||||
size--;
|
||||
}
|
||||
|
||||
//std::cout << "Length:" << length << std::endl;
|
||||
|
||||
|
||||
//now I have length;
|
||||
(raw_data)++; //skipping the ":"
|
||||
size--;
|
||||
std::string value = "";
|
||||
|
||||
while (length--)
|
||||
{
|
||||
value = value + (*raw_data);
|
||||
(raw_data)++;
|
||||
size--;
|
||||
}
|
||||
|
||||
//(raw_data)++;
|
||||
//size--;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bNode* b_decode(char* &raw_data, long long &size) {
|
||||
|
||||
if (size == 0) {
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bNode* curNode = NULL;
|
||||
bNode* head;
|
||||
//bNode* value = NULL;
|
||||
|
||||
switch (*raw_data) {
|
||||
|
||||
case 'd': {
|
||||
|
||||
|
||||
curNode = new bNode;
|
||||
curNode->type = B_DICTIONARY;
|
||||
curNode->value.dict = new bDictionary;
|
||||
|
||||
|
||||
//for skipping 'd' character...
|
||||
|
||||
(raw_data)++;
|
||||
size--;
|
||||
//now to decode the value , but value can be anything
|
||||
|
||||
while ((*raw_data) != 'e') {
|
||||
/* Parsing key */
|
||||
std::string key = string_decode((raw_data), size);
|
||||
|
||||
bDictionaryNode* res = new bDictionaryNode;
|
||||
|
||||
res->key = key;
|
||||
|
||||
res->value = b_decode(raw_data, size);
|
||||
|
||||
if (curNode->value.dict->count == 0) {
|
||||
curNode->value.dict->head = res;
|
||||
curNode->value.dict->tail = res;
|
||||
curNode->value.dict->count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
curNode->value.dict->tail->next = res;
|
||||
curNode->value.dict->tail = curNode->value.dict->tail->next;
|
||||
curNode->value.dict->count++;
|
||||
|
||||
}
|
||||
|
||||
//for skipping 'e'
|
||||
|
||||
|
||||
(raw_data)++;
|
||||
size--;
|
||||
|
||||
return curNode;
|
||||
|
||||
break;
|
||||
}
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
{
|
||||
std::string res = string_decode((raw_data), size);
|
||||
curNode = new bNode;
|
||||
curNode->type = B_STRING;
|
||||
curNode->value.str = res;
|
||||
|
||||
|
||||
|
||||
return curNode;
|
||||
break;
|
||||
}
|
||||
case 'l': {
|
||||
|
||||
//for skipping 'l' character...
|
||||
(raw_data)++;
|
||||
size--;
|
||||
|
||||
curNode = new bNode;
|
||||
curNode->type = B_LIST;
|
||||
curNode->value.list = new bList;
|
||||
|
||||
while ((*raw_data) != 'e') {
|
||||
bListNode* res = new bListNode;
|
||||
|
||||
res->value = b_decode(raw_data, size);
|
||||
|
||||
if (curNode->value.list->count == 0) {
|
||||
curNode->value.list->head = res;
|
||||
curNode->value.list->tail = res;
|
||||
curNode->value.list->count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
curNode->value.list->tail->next = res;
|
||||
curNode->value.list->tail = curNode->value.list->tail->next;
|
||||
curNode->value.list->count++;
|
||||
|
||||
}
|
||||
|
||||
//Escaping the 'e' character...
|
||||
raw_data++;
|
||||
size--;
|
||||
|
||||
return curNode;
|
||||
break;
|
||||
}
|
||||
case 'i': {
|
||||
//now I have encountered an i;
|
||||
raw_data++; //now I have skipped i;
|
||||
size--;
|
||||
long long int integer = integer_decode(raw_data, size);
|
||||
|
||||
curNode = new bNode;
|
||||
curNode->type = B_INTEGER;
|
||||
curNode->value.number = integer;
|
||||
|
||||
|
||||
return curNode;
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
error_msg("data-type found is unknow while parsing.\n",
|
||||
__FUNCTION__);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return curNode;
|
||||
}
|
||||
|
||||
/*************************Encode Functions Implementation*************************/
|
||||
|
||||
|
||||
void encode_integer(char*& e_data, long long& size, long long num) {
|
||||
|
||||
|
||||
std::string res = "";
|
||||
|
||||
res += 'i';
|
||||
res += std::to_string(num);
|
||||
res += 'e';
|
||||
|
||||
long long sz = (long long)res.size();
|
||||
|
||||
for (long long i = 0; i < sz; i++) {
|
||||
e_data[size++] = res[i];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void encode_string(char*& e_data, long long& size, const std::string& str) {
|
||||
|
||||
long long len = (long long)str.size();
|
||||
|
||||
std::string res = "";
|
||||
|
||||
|
||||
res += std::to_string(len);
|
||||
res += ':';
|
||||
res += str;
|
||||
|
||||
long long sz = (long long)res.size();
|
||||
|
||||
for (long long i = 0; i < sz; i++) {
|
||||
e_data[size++] = res[i];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int b_encode(bNode* tFile, char*& e_data, long long& size) {
|
||||
|
||||
b_type ty = tFile->type;
|
||||
switch (ty) {
|
||||
|
||||
case B_DICTIONARY: {
|
||||
|
||||
e_data[size] = 'd';
|
||||
size++;
|
||||
|
||||
bDictionaryNode* curr = tFile->value.dict->head;
|
||||
std::string key;
|
||||
|
||||
while (curr != NULL) {
|
||||
|
||||
key = curr->key;
|
||||
encode_string(e_data,size, key);
|
||||
|
||||
b_encode(curr->value, e_data, size);
|
||||
curr = curr->next;
|
||||
|
||||
}
|
||||
|
||||
e_data[size] = 'e';
|
||||
size++;
|
||||
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
case B_LIST: {
|
||||
|
||||
e_data[size] = 'l';
|
||||
size++;
|
||||
|
||||
bListNode* curr = tFile->value.list->head;
|
||||
|
||||
while (curr != NULL) {
|
||||
|
||||
b_encode(curr->value, e_data, size);
|
||||
curr = curr->next;
|
||||
|
||||
}
|
||||
|
||||
e_data[size] = 'e';
|
||||
size++;
|
||||
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
case B_STRING: {
|
||||
|
||||
encode_string(e_data, size, tFile->value.str);
|
||||
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
case B_INTEGER: {
|
||||
|
||||
encode_integer(e_data, size, tFile->value.number);
|
||||
|
||||
return 1;
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/*************************Encode Functions Implementation*************************/
|
||||
|
||||
|
||||
//void TorrentInfoDump(bNode* res, TorrentInfo*& torrentInfo)
|
||||
//{
|
||||
// b_type ty = res->type;
|
||||
// switch (ty)
|
||||
// {
|
||||
// case B_DICTIONARY:
|
||||
// {
|
||||
// bDictionaryNode* curr = res->value.dict->head;
|
||||
// std::string key;
|
||||
//
|
||||
// while (curr != NULL)
|
||||
// {
|
||||
// //announce would always be a string!!!
|
||||
// key = curr->key;
|
||||
//
|
||||
// if (key == "info")
|
||||
// {
|
||||
// torrentInfo->start = curr->value->value.start;
|
||||
// torrentInfo->end = curr->value->value.end;
|
||||
// }
|
||||
//
|
||||
// if (key == "announce") {
|
||||
// torrentInfo->announce = curr->value->value.str;
|
||||
// }
|
||||
// if (key == "comment")
|
||||
// {
|
||||
// torrentInfo->comment = curr->value->value.str;
|
||||
// }
|
||||
//
|
||||
// if (key == "created by")
|
||||
// {
|
||||
// torrentInfo->created_by = curr->value->value.str;
|
||||
// }
|
||||
//
|
||||
// if (key == "creation date")
|
||||
// {
|
||||
// torrentInfo->creation_date = curr->value->value.number;
|
||||
//
|
||||
// }
|
||||
//
|
||||
// if (key == "encoding")
|
||||
// {
|
||||
// torrentInfo->encoding = curr->value->value.str;
|
||||
// }
|
||||
//
|
||||
// if (key == "piece length")
|
||||
// {
|
||||
// torrentInfo->pieceSize = curr->value->value.number;
|
||||
// }
|
||||
//
|
||||
// if (key == "length")
|
||||
// {
|
||||
// torrentInfo->length = curr->value->value.number;
|
||||
// }
|
||||
//
|
||||
// if (key == "pieces")
|
||||
// {
|
||||
// long long int num_pieces = (torrentInfo->length / torrentInfo->pieceSize) + 1;
|
||||
//
|
||||
// torrentInfo->pieceHashes = new char* [num_pieces];
|
||||
//
|
||||
// std::cout << "NumPieces:" << num_pieces << "\n";
|
||||
//
|
||||
// for (long long int i = 0; i < num_pieces; i++)
|
||||
// {
|
||||
// torrentInfo->pieceHashes[i] = new char[20];
|
||||
// char* src = ((char*)(&curr->value->value.str) + (20 * i));
|
||||
// for (long long int j = 0; j < 20; j++) {
|
||||
// torrentInfo->pieceHashes[i][j] = curr->value->value.str[j + 20 * i];
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // if(key=="")
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// TorrentInfoDump(curr->value, torrentInfo);
|
||||
// curr = curr->next;
|
||||
// }
|
||||
// return;
|
||||
//
|
||||
// }
|
||||
// case B_STRING:
|
||||
// {
|
||||
//
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// case B_LIST: {
|
||||
// bListNode* curr = res->value.list->head;
|
||||
// while (curr != NULL)
|
||||
// {
|
||||
// //torrentInfo->Files=curr->value->value.
|
||||
// TorrentInfoDump(curr->value, torrentInfo);
|
||||
// curr = curr->next;
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
// default: {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// }
|
||||
//}
|
||||
//
|
||||
|
||||
//
|
||||
//void TorrentInfoDump(bNode* res, TorrentInfo*& torrentInfo)
|
||||
//{
|
||||
// b_type ty = res->type;
|
||||
// switch (ty)
|
||||
// {
|
||||
// case B_DICTIONARY:
|
||||
// {
|
||||
// bDictionaryNode* curr = res->value.dict->head;
|
||||
// std::string key;
|
||||
//
|
||||
// while (curr != NULL)
|
||||
// {
|
||||
// //announce would always be a string!!!
|
||||
// key = curr->key;
|
||||
//
|
||||
// if (key == "info")
|
||||
// {
|
||||
// torrentInfo->start = curr->value->value.start;
|
||||
// torrentInfo->end = curr->value->value.end;
|
||||
// }
|
||||
//
|
||||
// if (key == "announce") {
|
||||
// torrentInfo->announce = curr->value->value.str;
|
||||
// }
|
||||
// if (key == "comment")
|
||||
// {
|
||||
// torrentInfo->comment = curr->value->value.str;
|
||||
// }
|
||||
//
|
||||
// if (key == "created by")
|
||||
// {
|
||||
// torrentInfo->created_by = curr->value->value.str;
|
||||
// }
|
||||
//
|
||||
// if (key == "creation date")
|
||||
// {
|
||||
// torrentInfo->creation_date = curr->value->value.number;
|
||||
//
|
||||
// }
|
||||
//
|
||||
// if (key == "encoding")
|
||||
// {
|
||||
// torrentInfo->encoding = curr->value->value.str;
|
||||
// }
|
||||
//
|
||||
// if (key == "piece length")
|
||||
// {
|
||||
// torrentInfo->pieceSize = curr->value->value.number;
|
||||
// }
|
||||
//
|
||||
// if (key == "length")
|
||||
// {
|
||||
// torrentInfo->length = curr->value->value.number;
|
||||
// }
|
||||
//
|
||||
// if (key == "pieces")
|
||||
// {
|
||||
// long long int num_pieces = (torrentInfo->length / torrentInfo->pieceSize) + 1;
|
||||
//
|
||||
// torrentInfo->pieceHashes = new char* [num_pieces];
|
||||
//
|
||||
// std::cout << "NumPieces:" << num_pieces << "\n";
|
||||
//
|
||||
// for (long long int i = 0; i < num_pieces; i++)
|
||||
// {
|
||||
// torrentInfo->pieceHashes[i] = new char[20];
|
||||
// char* src = ((char*)(&curr->value->value.str) + (20 * i));
|
||||
// for (long long int j = 0; j < 20; j++) {
|
||||
// torrentInfo->pieceHashes[i][j] = curr->value->value.str[j + 20 * i];
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // if(key=="")
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// TorrentInfoDump(curr->value, torrentInfo);
|
||||
// curr = curr->next;
|
||||
// }
|
||||
// return;
|
||||
//
|
||||
// }
|
||||
// case B_STRING:
|
||||
// {
|
||||
//
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// case B_LIST: {
|
||||
// bListNode* curr = res->value.list->head;
|
||||
// while (curr != NULL)
|
||||
// {
|
||||
// //torrentInfo->Files=curr->value->value.
|
||||
// TorrentInfoDump(curr->value, torrentInfo);
|
||||
// curr = curr->next;
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
// default: {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//
|
||||
37
src/CMakeLists.txt
Normal file
37
src/CMakeLists.txt
Normal file
@@ -0,0 +1,37 @@
|
||||
set(TORRENT_STRUCTURE_SRC
|
||||
"torrent_structure.cpp")
|
||||
|
||||
set(BPROTOCOL_SRC
|
||||
"BProtocol.cpp")
|
||||
|
||||
set(TORRENT_SRC
|
||||
"Torrent.cpp")
|
||||
|
||||
set(TRACKER_SRC
|
||||
"Tracker.cpp")
|
||||
|
||||
set(NETWORK_SRC
|
||||
"Network.cpp")
|
||||
|
||||
|
||||
|
||||
add_library(${TORRENT_LIB} STATIC
|
||||
${TORRENT_STRUCTURE_SRC}
|
||||
${BPROTOCOL_SRC}
|
||||
${TORRENT_SRC}
|
||||
${TRACKER_SRC}
|
||||
${NETWORK_SRC})
|
||||
|
||||
target_link_libraries(${TORRENT_LIB} PUBLIC
|
||||
${OPENSSL_LIBRARIES}
|
||||
${OPENSSL_CRYPTO_LIBRARY}
|
||||
${OPENSSL_SSL_LIBRARY}
|
||||
${CURL_LIBRARIES}
|
||||
eventpp::eventpp)
|
||||
|
||||
|
||||
target_include_directories(${TORRENT_LIB} PUBLIC
|
||||
${HEADER_DIR}
|
||||
${OPENSSL_INCLUDE_DIR}
|
||||
${CURL_INCLUDE_DIRS}
|
||||
${EVENTPP_HEADER_DIR})
|
||||
210
src/Network.cpp
Normal file
210
src/Network.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
#include "Network.hpp"
|
||||
#include<string>
|
||||
#include<iostream>
|
||||
|
||||
|
||||
/*HANDHSAKE BUILD*/
|
||||
char* buildHandShake(Torrent* torrent)
|
||||
{
|
||||
|
||||
//handshake: <pstrlen><pstr><reserved><info_hash><peer_id> [wikitheory.org]
|
||||
|
||||
const char* pstr = "BitTorrent protocol";
|
||||
unsigned char pstrlen = strlen(pstr);
|
||||
const char reserved[8] = { 0 };
|
||||
const char* g_local_peer_id = "-MY0001-123456654321";
|
||||
|
||||
size_t bufflen = 1 + pstrlen + sizeof(reserved) + 20 + strlen(g_local_peer_id);
|
||||
|
||||
off_t off = 0;
|
||||
char* buff = (char*)malloc(bufflen);
|
||||
|
||||
buff[0] = pstrlen;
|
||||
off++;
|
||||
|
||||
memcpy(buff + off, pstr, pstrlen);
|
||||
off += pstrlen;
|
||||
assert(off == 20);
|
||||
|
||||
memcpy(buff + off, reserved, sizeof(reserved));
|
||||
off += sizeof(reserved);
|
||||
assert(off == 28);
|
||||
|
||||
memcpy(buff + off, torrent->Infohash, 20);
|
||||
|
||||
off += 20;
|
||||
memcpy(buff + off, g_local_peer_id, strlen(g_local_peer_id));
|
||||
|
||||
|
||||
|
||||
|
||||
return buff;
|
||||
|
||||
|
||||
}
|
||||
|
||||
int sendData(int sockfd,char handshake[],ssize_t len)
|
||||
{
|
||||
ssize_t tot_sent = 0;
|
||||
// ssize_t len = 68;
|
||||
|
||||
while (tot_sent < len) {
|
||||
ssize_t sent = send(sockfd, handshake, len - tot_sent, 0);
|
||||
if (sent < 0)
|
||||
|
||||
{
|
||||
std::cout << "No data sent\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::cout << "Sent:" << sent << '\n';
|
||||
|
||||
tot_sent += sent;
|
||||
handshake += sent;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int receiveData(int sockfd, ssize_t len, char* buff, int flag)
|
||||
{
|
||||
unsigned tot_recv = 0;
|
||||
ssize_t nb=0;
|
||||
// char buff[len];
|
||||
if (len == 0) {
|
||||
std::cout << "No data available" << '\n';
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
assert(len - tot_recv > 0);
|
||||
nb = recv(sockfd, buff + tot_recv, len - tot_recv, 0);
|
||||
if (nb <= 0) {
|
||||
std::cout << "No data received" << '\n';
|
||||
return -1;
|
||||
}
|
||||
std::cout << "Received in chunks :" << nb << "\n";
|
||||
|
||||
tot_recv += nb;
|
||||
|
||||
} while (nb > 0 && tot_recv < len);
|
||||
|
||||
if (tot_recv == len) {
|
||||
std::cout << "Received All" << '\n';
|
||||
return 1;
|
||||
// return;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void start_client_handshake(int& sockfd,char handshake[],Torrent* torrent)
|
||||
{
|
||||
struct sockaddr_in server_addr;
|
||||
|
||||
char buffer[68];
|
||||
// Create a socket
|
||||
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
perror("Socket creation error");
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize server address structure
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_port = htons(DEST_PORT);
|
||||
|
||||
// Convert IP address from text to binary form
|
||||
if (inet_pton(AF_INET, SERVER_IP_ADDRESS, &server_addr.sin_addr) <= 0) {
|
||||
perror("Invalid address/ Address not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
// Connect to the server
|
||||
if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
|
||||
perror("Connection failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the handshake message
|
||||
|
||||
|
||||
assert(sendData(sockfd, handshake, 68) == 1);
|
||||
|
||||
printf("Handshake message sent successfully\n");
|
||||
|
||||
|
||||
char* buf = (char*)malloc(68);
|
||||
int data = receiveData(sockfd, 68,buf,0);
|
||||
assert(data==1);
|
||||
|
||||
std::cout << "Received Handshake"<<'\n';
|
||||
|
||||
//Receive BitField
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
bt_msg_t Recvmsg;
|
||||
|
||||
int n_pieces = torrent->PieceCount();
|
||||
int sizeRmsg = sizeof(Recvmsg.length) + sizeof(Recvmsg.bt_type) + (sizeof(char) * n_pieces);
|
||||
|
||||
std::cout << "sizeRmsg:" << sizeRmsg << '\n';
|
||||
|
||||
// Interested Message
|
||||
bt_msg_t Sendmsg;
|
||||
Sendmsg.length = sizeof(Sendmsg.bt_type);
|
||||
Sendmsg.bt_type = BT_INTERSTED;
|
||||
assert(sendData(sockfd, (char*)&Sendmsg, sizeof(Sendmsg)) == 1);
|
||||
|
||||
|
||||
|
||||
//Piece-Request
|
||||
Sendmsg;
|
||||
Sendmsg.bt_type = BT_REQUEST;
|
||||
Sendmsg.payload.request.begin = 0;
|
||||
Sendmsg.payload.request.index = 0;
|
||||
Sendmsg.payload.request.length = 4;
|
||||
Sendmsg.length = sizeof(Sendmsg.bt_type)+sizeof(Sendmsg.payload);
|
||||
|
||||
|
||||
assert(sendData(sockfd, (char*)&Sendmsg, sizeof(Sendmsg)) == 1);
|
||||
//PieceFetch
|
||||
do {
|
||||
receiveData(sockfd, 8, buffer, 1);
|
||||
memcpy(&Recvmsg, buffer, sizeof(Sendmsg));
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
} while (Recvmsg.bt_type!=BT_PIECE);
|
||||
|
||||
std::cout << "\nTYPE:" << (int)Recvmsg.bt_type;
|
||||
std::cout <<"\nsize:" << Recvmsg.payload.bitfield.size;
|
||||
|
||||
// Close the socket
|
||||
close(sockfd);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
830
src/Torrent.cpp
Normal file
830
src/Torrent.cpp
Normal file
@@ -0,0 +1,830 @@
|
||||
#include <Torrent.hpp>
|
||||
#include <debug.hpp>
|
||||
#include <assert.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/buffer.h>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
/**********************************************************/
|
||||
|
||||
static std::string encodeURl(std::string Info)
|
||||
{
|
||||
//Info Hash is 20byte long , so no stress of extra character at end
|
||||
std::string encoded = "";
|
||||
|
||||
encoded += "%";
|
||||
for (int i = 0; i < Info.size();)
|
||||
{
|
||||
if (i + 1 < Info.size())
|
||||
{
|
||||
encoded += Info[i];
|
||||
encoded += Info[i + 1];
|
||||
}
|
||||
encoded += "%";
|
||||
i += 2;
|
||||
}
|
||||
encoded.pop_back();
|
||||
return encoded;
|
||||
}
|
||||
|
||||
/**********************************************************/
|
||||
|
||||
static std::vector<unsigned char> sha1(const char* input,
|
||||
long long int size);
|
||||
|
||||
FileItem::FileItem() {
|
||||
|
||||
this->Path = "";
|
||||
this->Size = -1;
|
||||
this->Offset = -1;
|
||||
this->FormattedSize = "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FileItem::set_FormattedSize() {
|
||||
this->FormattedSize = std::to_string(Size);
|
||||
}
|
||||
|
||||
std::string Torrent::FileDirectory() {
|
||||
return this->Files.size() > 1 ? this->Name +
|
||||
DIR_SEPARATOR : "";
|
||||
}
|
||||
|
||||
long long int Torrent::TotalSize() {
|
||||
long long int totalSize = 0;
|
||||
|
||||
|
||||
for (int i = 0; i < (int)Files.size(); i++) {
|
||||
totalSize += Files[i].Size;
|
||||
}
|
||||
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
std::string Torrent::FormattedPieceSize() {
|
||||
return std::to_string(PieceSize);
|
||||
}
|
||||
|
||||
std::string Torrent::FormattedTotalSize() {
|
||||
return std::to_string(this->TotalSize());
|
||||
}
|
||||
|
||||
int Torrent::PieceCount() {
|
||||
return (int)this->PieceHashes.size();
|
||||
}
|
||||
|
||||
std::string Torrent::VerifiedPiecesString() {
|
||||
std::string verificationString = "";
|
||||
|
||||
for (int i = 0; i < IsPieceVerified.size(); i++) {
|
||||
verificationString += IsPieceVerified[i] ? "1" : "0";
|
||||
}
|
||||
|
||||
return verificationString;
|
||||
}
|
||||
|
||||
int Torrent::VerifiedPieceCount() {
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < (int)IsPieceVerified.size(); i++) {
|
||||
count += IsPieceVerified[i] ? 1 : 0;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
double Torrent::VerifiedRatio() {
|
||||
return this->VerifiedPieceCount() / (double)this->PieceCount();
|
||||
}
|
||||
|
||||
bool Torrent::IsCompleted() {
|
||||
return this->VerifiedPieceCount() == this->PieceCount();
|
||||
}
|
||||
|
||||
bool Torrent::IsStarted() {
|
||||
return this->VerifiedPieceCount() > 0;
|
||||
}
|
||||
|
||||
|
||||
long long int Torrent::Downloaded() {
|
||||
return this->PieceSize * this->VerifiedPieceCount();
|
||||
}
|
||||
|
||||
long long int Torrent::Left() {
|
||||
return this->TotalSize() - this->Downloaded();
|
||||
}
|
||||
|
||||
|
||||
//To encode string into Url safe string.
|
||||
static std::string url_encode(const std::string& decoded)
|
||||
{
|
||||
const auto encoded_value = curl_easy_escape(nullptr, decoded.c_str(), static_cast<int>(decoded.length()));
|
||||
std::string result(encoded_value);
|
||||
curl_free(encoded_value);
|
||||
return result;
|
||||
}
|
||||
|
||||
//To decode string into Url safe string.
|
||||
static std::string url_decode(const std::string& encoded)
|
||||
{
|
||||
int output_length;
|
||||
const auto decoded_value = curl_easy_unescape(nullptr, encoded.c_str(), static_cast<int>(encoded.length()), &output_length);
|
||||
std::string result(decoded_value, output_length);
|
||||
curl_free(decoded_value);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Torrent::HexStringInfoHash() {
|
||||
std::ostringstream oss;
|
||||
for (unsigned char byte : this->Infohash) {
|
||||
oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(byte);
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string Torrent::UrlSafeStringInfoHash() {
|
||||
std::string infoHash(this->HexStringInfoHash());
|
||||
infoHash = url_encode(infoHash);
|
||||
return encodeURl(infoHash);
|
||||
}
|
||||
|
||||
Torrent::Torrent() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
Torrent::Torrent(std::string name, std::string location,
|
||||
std::vector<FileItem> files,
|
||||
std::vector<std::string> trackers,
|
||||
int pieceSize, char* pieceHashes = NULL,
|
||||
int blockSize = 16384,
|
||||
int isPrivate = -1) {
|
||||
|
||||
this->Name = name;
|
||||
this->DownloadDirectory = location;
|
||||
this->Files = files;
|
||||
|
||||
|
||||
//locking....
|
||||
|
||||
this->Trackers.resize(trackers.size());
|
||||
|
||||
if ((int)trackers.size() > 0) {
|
||||
Tracker* tracker = NULL;
|
||||
int i = 0;
|
||||
for (std::string url : trackers) {
|
||||
tracker = new Tracker(url);
|
||||
tracker->PeerListUpdated.appendListener("peerlist",
|
||||
[this](std::vector<std::string>& peerList) {
|
||||
this->PeerListUpdated.dispatch("peerlist",
|
||||
peerList);
|
||||
});
|
||||
this->Trackers[i] = tracker;
|
||||
i++;
|
||||
|
||||
tracker = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this->PieceSize = pieceSize;
|
||||
this->BlockSize = blockSize;
|
||||
this->IsPrivate = isPrivate;
|
||||
|
||||
int count = (int)(std::ceil(this->TotalSize() /
|
||||
(double)this->PieceSize));
|
||||
|
||||
PieceHashes.resize(count);
|
||||
IsPieceVerified.resize(count);
|
||||
IsBlockAcquired.resize(count);
|
||||
|
||||
for (int i = 0; i < this->PieceCount(); i++) {
|
||||
IsBlockAcquired[i].resize(this->GetBlockCount(i));
|
||||
}
|
||||
|
||||
if (pieceHashes == NULL) {
|
||||
|
||||
//this is a new torrent so we have to create the hashes form
|
||||
//the files...
|
||||
for (int i = 0; i < this->PieceCount(); i++) {
|
||||
this->PieceHashes[i] = this->GetHash(i);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
for (int i = 0; i < this->PieceCount(); i++) {
|
||||
this->PieceHashes[i].resize(20);
|
||||
|
||||
for (int j = 20 * i; j < 20 + 20 * i; j++) {
|
||||
this->PieceHashes[i][j-20*i] = *(pieceHashes + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bDictionary* dict = this->TorrentInfoToBEncodingObject(*this);
|
||||
bNode* info = new bNode();
|
||||
info->type = B_DICTIONARY;
|
||||
info->value.dict = dict;
|
||||
|
||||
//_dump_parsed_torrent_file_data(info);
|
||||
|
||||
char* buf = new char[1048576]; //1MB .torrent file only
|
||||
long long int size = 0;
|
||||
|
||||
b_encode(info, buf, size);
|
||||
char* buffer = new char[size + 1];
|
||||
|
||||
for (int i = 0; i <=size; i++) {
|
||||
buffer[i] = buf[i];
|
||||
}
|
||||
|
||||
delete[] buf;
|
||||
|
||||
std::vector<unsigned char> hash = sha1(buffer,size);
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
this->Infohash[i] = hash[i];
|
||||
}
|
||||
|
||||
//for (int i = 0; i < this->PieceCount(); i++) {
|
||||
// this->Verify(i);
|
||||
//}
|
||||
}
|
||||
|
||||
static bool isHashEqual(const std::vector<unsigned char>& hash1,
|
||||
const std::vector<unsigned char>& hash2) {
|
||||
|
||||
assert(hash1.size() == hash2.size());
|
||||
|
||||
for (int i = 0; i < hash1.size(); i++) {
|
||||
if (hash1[i] != hash2[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool areAllBlockPieceAcquired(std::vector<bool>&
|
||||
IsBlockAcquired) {
|
||||
|
||||
for (int i = 0; i < IsBlockAcquired.size(); i++) {
|
||||
if (!IsBlockAcquired[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Torrent::Verify(int piece) {
|
||||
|
||||
std::vector<unsigned char> hash = GetHash(piece);
|
||||
|
||||
bool isVerfied = (hash.size() > 0 &&
|
||||
isHashEqual(hash, this->PieceHashes[piece]));
|
||||
|
||||
if (isVerfied) {
|
||||
this->IsPieceVerified[piece] = true;
|
||||
|
||||
for (int i = 0; i < this->IsBlockAcquired[piece].size(); i++) {
|
||||
this->IsBlockAcquired[piece][i] = true;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
IsPieceVerified[piece] = false;
|
||||
|
||||
//reload the entire piece..
|
||||
if (areAllBlockPieceAcquired(this->IsBlockAcquired[piece])) {
|
||||
|
||||
for (int j = 0; j < this->IsBlockAcquired[piece].size(); j++) {
|
||||
this->IsBlockAcquired[piece][j] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Torrent::GetPieceSize(int piece) {
|
||||
|
||||
if (piece == this->PieceCount() - 1) {
|
||||
|
||||
int remainder = this->TotalSize() % this->PieceSize;
|
||||
|
||||
if( remainder != 0 )
|
||||
return remainder;
|
||||
}
|
||||
|
||||
return this->PieceSize;
|
||||
}
|
||||
|
||||
int Torrent::GetBlockCount(int piece) {
|
||||
|
||||
return (int)(std::ceil(this->GetPieceSize(piece) /
|
||||
(double)this->BlockSize));
|
||||
}
|
||||
|
||||
static std::vector<unsigned char> sha1(const char* input,
|
||||
long long int size) {
|
||||
// Buffer to hold the hash
|
||||
std::vector<unsigned char> hash(SHA_DIGEST_LENGTH);
|
||||
|
||||
// Perform the SHA1 hash
|
||||
SHA1((const unsigned char*)input, size, hash.data());
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> Torrent::GetHash(int piece) {
|
||||
|
||||
std::string data = this->ReadPiece(piece);
|
||||
|
||||
if (data == "") {
|
||||
error_msg("Piece Reading went wrong!", __FUNCTION__);
|
||||
return {};
|
||||
}
|
||||
|
||||
return sha1((const char*)data.c_str(),
|
||||
(long long int)data.size());
|
||||
}
|
||||
|
||||
std::string Torrent::ReadPiece(int piece) {
|
||||
return Read(1LL * piece * this->PieceSize, this->GetPieceSize(piece));
|
||||
}
|
||||
|
||||
std::string Torrent::Read(long long int start, int length) {
|
||||
|
||||
long long int end = start + length;
|
||||
|
||||
std::string resbuffer;
|
||||
char* buffer = new char[length];
|
||||
|
||||
for (int i = 0; i < (int)Files.size(); i++)
|
||||
{
|
||||
if ((start < Files[i].Offset && end < Files[i].Offset) ||
|
||||
(start > Files[i].Offset + Files[i].Size && end > Files[i].Offset + Files[i].Size))
|
||||
continue;
|
||||
|
||||
std::string filePath = DownloadDirectory +
|
||||
DIR_SEPARATOR +
|
||||
FileDirectory() + Files[i].Path;
|
||||
|
||||
if (!std::filesystem::exists(filePath)) {
|
||||
std::cout << "filePath: " << filePath << "\n";
|
||||
error_msg("File don't exist.\
|
||||
Something went wrong!", __FUNCTION__);
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
long long int fstart = std::max(0LL,
|
||||
start - Files[i].Offset);
|
||||
long long int fend = std::min(end -
|
||||
Files[i].Offset, Files[i].Size);
|
||||
int flength = fend - fstart;
|
||||
int bStart = std::max(0LL,
|
||||
Files[i].Offset - start);
|
||||
|
||||
std::ifstream file(filePath, std::ios::in);
|
||||
|
||||
if (file.is_open()) {
|
||||
file.seekg(fstart, std::ios::beg);
|
||||
file.read(buffer, flength);
|
||||
file.close();
|
||||
}
|
||||
else {
|
||||
error_msg("File didn't open", __FUNCTION__);
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
resbuffer.assign(buffer);
|
||||
delete[] buffer;
|
||||
|
||||
return resbuffer;
|
||||
}
|
||||
|
||||
|
||||
bNode* Torrent::TorrentToBEncodingObject(Torrent torrent) {
|
||||
|
||||
bNode* dict = new bNode();
|
||||
dict->type = B_DICTIONARY;
|
||||
dict->value.dict = new bDictionary();
|
||||
|
||||
if ((int)torrent.Trackers.size() == 0) {
|
||||
error_msg("Torrent class doesn't contain\
|
||||
any tracker right now!!", __FUNCTION__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bDictionaryNode* dictNode = NULL;
|
||||
dictNode = new bDictionaryNode();
|
||||
dictNode->key = "announce";
|
||||
dictNode->value = new bNode();
|
||||
dictNode->value->type = B_STRING;
|
||||
dictNode->value->value.str = Trackers[0]->Address;
|
||||
|
||||
dict->value.dict->head =
|
||||
dict->value.dict->tail = dictNode;
|
||||
dict->value.dict->count++;
|
||||
dictNode = NULL;
|
||||
|
||||
if ((int)torrent.Trackers.size() > 1) {
|
||||
for (int i = 1; i < (int)torrent.Trackers.size(); i++) {
|
||||
dictNode = new bDictionaryNode();
|
||||
dictNode->key = "announce";
|
||||
dictNode->value = new bNode();
|
||||
dictNode->value->type = B_STRING;
|
||||
dictNode->value->value.str = Trackers[i]->Address;
|
||||
|
||||
dict->value.dict->tail->next = dictNode;
|
||||
dict->value.dict->tail =
|
||||
dict->value.dict->tail->next;
|
||||
dict->value.dict->count++;
|
||||
dictNode = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
dictNode = new bDictionaryNode();
|
||||
dictNode->key = "comment";
|
||||
dictNode->value = new bNode();
|
||||
dictNode->value->type = B_STRING;
|
||||
dictNode->value->value.str = torrent.Comment;
|
||||
|
||||
dict->value.dict->tail->next = dictNode;
|
||||
dict->value.dict->tail =
|
||||
dict->value.dict->tail->next;
|
||||
dict->value.dict->count++;
|
||||
dictNode = NULL;
|
||||
|
||||
dictNode = new bDictionaryNode();
|
||||
dictNode->key = "created by";
|
||||
dictNode->value = new bNode();
|
||||
dictNode->value->type = B_STRING;
|
||||
dictNode->value->value.str = torrent.CreatedBy;
|
||||
|
||||
dict->value.dict->tail->next = dictNode;
|
||||
dict->value.dict->tail =
|
||||
dict->value.dict->tail->next;
|
||||
dict->value.dict->count++;
|
||||
dictNode = NULL;
|
||||
|
||||
dictNode = new bDictionaryNode();
|
||||
dictNode->key = "creation date";
|
||||
dictNode->value = new bNode();
|
||||
dictNode->value->type = B_STRING;
|
||||
dictNode->value->value.str = torrent.CreationDate;
|
||||
|
||||
dict->value.dict->tail->next = dictNode;
|
||||
dict->value.dict->tail =
|
||||
dict->value.dict->tail->next;
|
||||
dict->value.dict->count++;
|
||||
dictNode = NULL;
|
||||
|
||||
|
||||
dictNode = new bDictionaryNode();
|
||||
dictNode->key = "encoding";
|
||||
dictNode->value = new bNode();
|
||||
dictNode->value->type = B_STRING;
|
||||
dictNode->value->value.str = torrent.Encoding;
|
||||
|
||||
dict->value.dict->tail->next = dictNode;
|
||||
dict->value.dict->tail =
|
||||
dict->value.dict->tail->next;
|
||||
dict->value.dict->count++;
|
||||
dictNode = NULL;
|
||||
|
||||
dictNode = new bDictionaryNode();
|
||||
dictNode->key = "info";
|
||||
dictNode->value = new bNode();
|
||||
dictNode->value->type = B_DICTIONARY;
|
||||
dictNode->value->value.dict =
|
||||
this->TorrentInfoToBEncodingObject(torrent);
|
||||
|
||||
dict->value.dict->tail->next = dictNode;
|
||||
dict->value.dict->tail =
|
||||
dict->value.dict->tail->next;
|
||||
dict->value.dict->count++;
|
||||
dictNode = NULL;
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
bDictionary* Torrent::TorrentInfoToBEncodingObject(Torrent torrent) {
|
||||
|
||||
bDictionary* dict = new bDictionary();
|
||||
|
||||
bDictionaryNode* node = new bDictionaryNode();
|
||||
|
||||
if (torrent.Files.size() == 0) {
|
||||
error_msg("No file mentioned!", __FUNCTION__);
|
||||
return NULL;
|
||||
}
|
||||
else if (torrent.Files.size() == 1) {
|
||||
|
||||
//node = new bDictionaryNode();
|
||||
node->key = "length";
|
||||
node->value = new bNode();
|
||||
node->value->type = B_INTEGER;
|
||||
node->value->value.number = torrent.Files[0].Size;
|
||||
|
||||
dict->head = dict->tail = node;
|
||||
dict->count++;
|
||||
node = NULL;
|
||||
|
||||
|
||||
|
||||
|
||||
node = new bDictionaryNode();
|
||||
node->key = "name";
|
||||
node->value = new bNode();
|
||||
node->value->type = B_STRING;
|
||||
node->value->value.str = torrent.Files[0].Path;
|
||||
|
||||
dict->tail->next = node;
|
||||
dict->tail = dict->tail->next;
|
||||
dict->count++;
|
||||
node = NULL;
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
node = new bDictionaryNode();
|
||||
node->key = "files";
|
||||
node->value = new bNode();
|
||||
node->value->type = B_LIST;
|
||||
bList* list = new bList();
|
||||
|
||||
bListNode* l_node = NULL;
|
||||
bDictionaryNode* n_node = NULL;
|
||||
|
||||
for (int i = 0; i < torrent.Files.size(); i++) {
|
||||
l_node = new bListNode();
|
||||
l_node->value = new bNode();
|
||||
l_node->value->type = B_DICTIONARY;
|
||||
l_node->value->value.dict = new bDictionary();
|
||||
|
||||
n_node = new bDictionaryNode();
|
||||
n_node->key = "length";
|
||||
n_node->value = new bNode();
|
||||
n_node->value->type = B_INTEGER;
|
||||
n_node->value->value.number = torrent.Files[i].Size;
|
||||
|
||||
|
||||
l_node->value->value.dict->tail = n_node;
|
||||
l_node->value->value.dict->tail =
|
||||
l_node->value->value.dict->tail->next;
|
||||
l_node->value->value.dict->count++;
|
||||
n_node = NULL;
|
||||
|
||||
n_node = new bDictionaryNode();
|
||||
n_node->key = "path";
|
||||
n_node->value = new bNode();
|
||||
n_node->value->type = B_STRING;
|
||||
n_node->value->value.str = torrent.Files[i].Path;
|
||||
|
||||
|
||||
l_node->value->value.dict->head =
|
||||
l_node->value->value.dict->tail = n_node;
|
||||
l_node->value->value.dict->count++;
|
||||
n_node = NULL;
|
||||
|
||||
if (list->count == 0) {
|
||||
list->head = list->tail = l_node;
|
||||
list->count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
list->tail->next = l_node;
|
||||
list->tail = list->tail->next;
|
||||
list->count++;
|
||||
l_node = NULL;
|
||||
}
|
||||
|
||||
node->value->value.list = list;
|
||||
dict->tail->next = node;
|
||||
dict->tail = dict->tail->next;
|
||||
dict->count++;
|
||||
node = NULL;
|
||||
|
||||
node = new bDictionaryNode();
|
||||
node->key = "name";
|
||||
node->value = new bNode();
|
||||
node->value->type = B_STRING;
|
||||
node->value->value.str = torrent.FileDirectory();
|
||||
|
||||
dict->tail->next = node;
|
||||
dict->tail = dict->tail->next;
|
||||
dict->count++;
|
||||
node = NULL;
|
||||
}
|
||||
|
||||
node = new bDictionaryNode();
|
||||
node->key = "piece length";
|
||||
node->value = new bNode();
|
||||
node->value->type = B_INTEGER;
|
||||
node->value->value.number = torrent.PieceSize;
|
||||
|
||||
dict->tail->next = node;
|
||||
dict->tail = dict->tail->next;
|
||||
dict->count++;
|
||||
node = NULL;
|
||||
|
||||
std::string pieces(20 * torrent.PieceCount(), ' ');
|
||||
for (int i = 0; i < torrent.PieceCount(); i++) {
|
||||
for (int j = 20 * i; j < 20 + 20 * i; j++) {
|
||||
pieces[j] = torrent.PieceHashes[i][j - 20LL * i];
|
||||
}
|
||||
}
|
||||
|
||||
node = new bDictionaryNode();
|
||||
node->key = "pieces";
|
||||
node->value = new bNode();
|
||||
node->value->type = B_STRING;
|
||||
node->value->value.str = pieces;
|
||||
|
||||
dict->tail->next = node;
|
||||
dict->tail = dict->tail->next;
|
||||
dict->count++;
|
||||
node = NULL;
|
||||
|
||||
if (torrent.IsPrivate != -1) {
|
||||
node = new bDictionaryNode();
|
||||
node->key = "private";
|
||||
node->value = new bNode();
|
||||
node->value->type = B_INTEGER;
|
||||
node->value->value.number = torrent.IsPrivate;
|
||||
|
||||
dict->tail->next = node;
|
||||
dict->tail = dict->tail->next;
|
||||
dict->count++;
|
||||
node = NULL;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
Torrent* Torrent::BEncodingObjectToTorrent(bNode* bencoding,
|
||||
std::string name, std::string downloadPath) {
|
||||
|
||||
if (bencoding->type != B_DICTIONARY) {
|
||||
error_msg("Decoding .torrent file is not\
|
||||
in correct format", __FUNCTION__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::map<std::string, bNode*> dict = bencoding->
|
||||
value.dict->to_Stl_map();
|
||||
|
||||
std::vector<std::string> trackers;
|
||||
if (dict.find("announce") != dict.end()) {
|
||||
|
||||
if (dict["announce"]->type == B_STRING) {
|
||||
trackers.push_back(dict["announce"]->value.str);
|
||||
}
|
||||
else if (dict["announce"]->type == B_LIST) {
|
||||
std::vector<bNode*>
|
||||
t_list= dict["announce"]->value.list->to_Stl_list();
|
||||
|
||||
for (int i = 0; i < t_list.size(); i++) {
|
||||
if (t_list[i]->type == B_STRING) {
|
||||
trackers.push_back(t_list[i]->value.str);
|
||||
}
|
||||
else {
|
||||
error_msg("announce list element \
|
||||
is wrong", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dict.find("info") == dict.end()) {
|
||||
error_msg("info section missing decoded bNode \
|
||||
object", __FUNCTION__);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
std::map<std::string, bNode*>
|
||||
info = dict["info"]->value.dict->to_Stl_map();
|
||||
|
||||
std::vector<FileItem> files;
|
||||
|
||||
if (info.find("name") != info.end() &&
|
||||
info.find("length") != info.end()) {
|
||||
FileItem item;
|
||||
item.Path = info["name"]->value.str;
|
||||
|
||||
item.Size = info["length"]->value.number;
|
||||
|
||||
files.push_back(item);
|
||||
}
|
||||
else if( info.find("files") != info.end()){
|
||||
|
||||
long long int running = 0;
|
||||
std::vector<bNode*> f_list = info["files"]->value.list
|
||||
->to_Stl_list();
|
||||
|
||||
|
||||
|
||||
for (bNode* item : f_list) {
|
||||
std::map<std::string, bNode*>
|
||||
dict = item->value.dict->to_Stl_map();
|
||||
|
||||
if (dict.find("path") == dict.end() &&
|
||||
dict.find("length") == dict.end()) {
|
||||
|
||||
error_msg("Incorrent .torrent file specification",
|
||||
__FUNCTION__);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
std::string path = "";
|
||||
|
||||
std::vector<bNode*> l_path = dict["path"]->value
|
||||
.list->to_Stl_list();
|
||||
|
||||
for (bNode* l_item : l_path) {
|
||||
|
||||
path += (DIR_SEPARATOR +
|
||||
l_item->value.str);
|
||||
}
|
||||
|
||||
long long int size = (long long int)dict["length"]->value
|
||||
.number;
|
||||
|
||||
|
||||
FileItem fileItem;
|
||||
fileItem.Path = path;
|
||||
fileItem.Size = size;
|
||||
fileItem.Offset = running;
|
||||
|
||||
files.push_back(fileItem);
|
||||
|
||||
running += size;
|
||||
}
|
||||
}
|
||||
else {
|
||||
error_msg("No file specified in .torrent file",
|
||||
__FUNCTION__);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (info.find("piece length") == info.end()) {
|
||||
error_msg("piece length no mentioned in torrent file",
|
||||
__FUNCTION__);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int pieceSize = info["piece length"]->value.number;
|
||||
|
||||
if (info.find("pieces") == info.end()) {
|
||||
error_msg("pieces not mentioned in torrent file",
|
||||
__FUNCTION__);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
std::string pieceHashes = info["pieces"]->value.str;
|
||||
|
||||
int IsPrivate = -1;
|
||||
if (info.find("private") != info.end()) {
|
||||
IsPrivate = info["private"]->value.number;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Torrent* torrent = new Torrent(name, downloadPath,
|
||||
files, trackers, pieceSize, (char*)pieceHashes.c_str(),
|
||||
16384, IsPrivate);
|
||||
|
||||
if (dict.find("comment") != dict.end()) {
|
||||
torrent->Comment = dict["comment"]->value.str;
|
||||
}
|
||||
|
||||
if (dict.find("created by") != dict.end()) {
|
||||
torrent->CreatedBy = dict["created by"]->value.str;
|
||||
}
|
||||
|
||||
return torrent;
|
||||
|
||||
}
|
||||
|
||||
void Torrent::UpdateTrackers(TrackerEvent ev, std::string id,
|
||||
int port) {
|
||||
|
||||
for (Tracker* t : this->Trackers) {
|
||||
t->Update(*this, ev, id, port);
|
||||
}
|
||||
}
|
||||
|
||||
void Torrent::ResetTrackerLastRequest() {
|
||||
for (Tracker* t : this->Trackers) {
|
||||
t->ResetLastRequest();
|
||||
}
|
||||
}
|
||||
|
||||
Torrent* LoadFromFile(std::string filePath, std::string downloadPath) {
|
||||
|
||||
}
|
||||
182
src/Tracker.cpp
Normal file
182
src/Tracker.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
#include <stdio.h>
|
||||
#include <cstring>
|
||||
#include <torrent_structure.hpp>
|
||||
#include <BProtocol.hpp>
|
||||
#include <debug.hpp>
|
||||
|
||||
|
||||
Tracker::Tracker(std::string address) {
|
||||
this->Address = address;
|
||||
this->LastPeerRequested = { 0,0 };
|
||||
this->PeerRequestInterval = 1800;
|
||||
this->httpWebRequest = "";
|
||||
}
|
||||
|
||||
void Tracker::Update(Torrent torrent, TrackerEvent ev,
|
||||
std::string id, int port) {
|
||||
struct timespec curr_time;
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &curr_time);
|
||||
|
||||
long long int time_check = ((double)curr_time.tv_sec +
|
||||
(double)curr_time.tv_nsec / 1.0e9);
|
||||
|
||||
long long int time_limit = ((double)LastPeerRequested.tv_sec +
|
||||
(double)LastPeerRequested.tv_nsec / 1.0e9) +
|
||||
this->PeerRequestInterval;
|
||||
|
||||
if (ev == Started && time_check < time_limit)
|
||||
return;
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &(this->LastPeerRequested));
|
||||
|
||||
std::string eventEnum;
|
||||
|
||||
switch (ev) {
|
||||
case Started: {
|
||||
eventEnum.assign("started");
|
||||
break;
|
||||
}
|
||||
case Paused: {
|
||||
eventEnum.assign("paused");
|
||||
break;
|
||||
}
|
||||
case Stopped: {
|
||||
eventEnum.assign("stopped");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
error_msg("TrackerEvent ev have undefined value\n",
|
||||
__FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char url[400];
|
||||
sprintf(url, "%s?info_hash=%s&peer_id=%s\
|
||||
&port=%d&uploaded=%lld&\
|
||||
downloaded=%lld&left=%lld&event=%s&compact=1",
|
||||
(char*)(this->Address).c_str(), (char*)(torrent.UrlSafeStringInfoHash()).c_str(),
|
||||
(char*)id.c_str(), port,
|
||||
torrent.Uploaded, torrent.Downloaded(), torrent.Left(),
|
||||
(char*)eventEnum.c_str());
|
||||
|
||||
std::string Url(url);
|
||||
|
||||
this->Request((char*)Url.c_str());
|
||||
this->Handle_response();
|
||||
}
|
||||
|
||||
// Callback function to write the response data
|
||||
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
|
||||
size_t totalSize = size * nmemb;
|
||||
std::string* str = (std::string*)userp;
|
||||
str->append((char*)contents, totalSize);
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
void Tracker::Request(char* url) {
|
||||
|
||||
CURL* curl;
|
||||
CURLcode res;
|
||||
//std::string readBuffer;
|
||||
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
curl = curl_easy_init();
|
||||
if (curl) {
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); // 30 seconds timeout
|
||||
//curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &(this->httpWebRequest));
|
||||
// Increase timeout
|
||||
|
||||
// Perform the request
|
||||
res = curl_easy_perform(curl);
|
||||
|
||||
// Check for errors
|
||||
if (res != CURLE_OK) {
|
||||
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
|
||||
}
|
||||
/*else {
|
||||
std::cout << this->httpWebRequest << std::endl;
|
||||
}*/
|
||||
|
||||
// Cleanup
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
curl_global_cleanup();
|
||||
}
|
||||
|
||||
void Tracker::Handle_response() {
|
||||
|
||||
long long int sz_response = (long long int)this->httpWebRequest.size();
|
||||
char* response = new char[sz_response];
|
||||
|
||||
if (this->httpWebRequest.size() == 0) {
|
||||
error_msg("Tracker didn't reply any response!!",
|
||||
__FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
strncpy(response, this->httpWebRequest.c_str(),
|
||||
sz_response);
|
||||
|
||||
|
||||
bNode* info = b_decode(response,
|
||||
sz_response);
|
||||
|
||||
if (sz_response > 0) {
|
||||
error_msg("b_decode() didn't parse the\
|
||||
complete responce\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
bDictionary* dict = info->value.dict;
|
||||
|
||||
std::map<std::string, bNode*>
|
||||
dictionary = dict->to_Stl_map();
|
||||
|
||||
this->PeerRequestInterval = dictionary["interval"]->value.number;
|
||||
const char* peerInfo = dictionary["peers"]->value.str.c_str();
|
||||
|
||||
std::vector<std::string> peers;
|
||||
|
||||
int offset;
|
||||
std::string address;
|
||||
|
||||
uint8_t ip[4];
|
||||
uint16_t ip_port;
|
||||
|
||||
for (int i = 0; i < strlen(peerInfo) / 6; i++) {
|
||||
|
||||
offset = i * 6;
|
||||
memcpy(ip, peerInfo + offset, 4);
|
||||
memcpy(&ip_port, peerInfo + 4, 2);
|
||||
ip_port = ntohs(ip_port); //convert port number..
|
||||
char ip_str[INET_ADDRSTRLEN];
|
||||
if (inet_ntop(AF_INET, ip, ip_str, INET_ADDRSTRLEN) == NULL) {
|
||||
error_msg("inet_ntop failed", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
address.assign(ip_str);
|
||||
std::stringstream ss;
|
||||
ss << ip_port;
|
||||
|
||||
peers.push_back(address + "::" + ss.str());
|
||||
|
||||
address.clear();
|
||||
}
|
||||
|
||||
this->PeerListUpdated.dispatch("peerlist", peers);
|
||||
}
|
||||
|
||||
void Tracker::ResetLastRequest() {
|
||||
LastPeerRequested = { 0,0 };
|
||||
}
|
||||
|
||||
std::string Tracker::returnTracker() {
|
||||
std::string trackerAddress = "[Tracker : " + this->Address + " ]";
|
||||
return trackerAddress;
|
||||
}
|
||||
81
src/torrent_structure.cpp
Normal file
81
src/torrent_structure.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include <torrent_structure.hpp>
|
||||
#include <assert.h>
|
||||
|
||||
/*************************** (.torrent) content structure for parsing*************************/
|
||||
|
||||
bNode::bNode() {
|
||||
this->size = 0;
|
||||
this->type = B_UNKNOWN;
|
||||
}
|
||||
|
||||
bNode::val::val() {
|
||||
this->str = "";
|
||||
this->number = 0;
|
||||
this->dict = NULL;
|
||||
this->list = NULL;
|
||||
}
|
||||
|
||||
bList::bList() {
|
||||
this->head = NULL;
|
||||
this->tail = NULL;
|
||||
this->count = 0;
|
||||
}
|
||||
|
||||
std::vector<bNode*>
|
||||
bList::to_Stl_list() {
|
||||
|
||||
bListNode* curr = this->head;
|
||||
assert(curr != NULL);
|
||||
|
||||
std::vector<bNode*> resList;
|
||||
|
||||
for (; curr != NULL; curr = curr->next) {
|
||||
resList.push_back(curr->value);
|
||||
}
|
||||
|
||||
return resList;
|
||||
}
|
||||
|
||||
bListNode::bListNode() {
|
||||
this->value = NULL;
|
||||
this->next = NULL;
|
||||
}
|
||||
|
||||
bDictionary::bDictionary() {
|
||||
head = tail = NULL;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
std::map<std::string, bNode*>
|
||||
bDictionary::to_Stl_map() {
|
||||
|
||||
bDictionaryNode* curr = this->head;
|
||||
assert(curr != NULL);
|
||||
|
||||
std::map<std::string, bNode*> resMap;
|
||||
|
||||
for (; curr != NULL; curr = curr->next) {
|
||||
resMap[curr->key] = curr->value;
|
||||
}
|
||||
|
||||
return resMap;
|
||||
}
|
||||
|
||||
bDictionaryNode::bDictionaryNode() {
|
||||
this->key = "";
|
||||
this->value = NULL;
|
||||
this->next = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*************************** Torrent info structure for processing****************************/
|
||||
//FileItem::FileItem(std::string path, long long size,
|
||||
// long long offset) {
|
||||
//
|
||||
// this->Path = path;
|
||||
// this->size = size;
|
||||
// this->offset = offset;
|
||||
// this->FormattedSize = std::to_string(size);
|
||||
//}
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user