first commit

This commit is contained in:
2024-07-08 15:54:04 +05:30
commit f4709c97ab
17 changed files with 2691 additions and 0 deletions

588
src/BProtocol.cpp Normal file
View 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
View 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
View 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
View 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
View 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
View 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);
//}
//