mirror of
https://onedev.site.tesses.net/tesses-framework
synced 2026-02-08 07:45:46 +00:00
Add bencode, create torrent file, read torrent file and random number generator
This commit is contained in:
@@ -62,7 +62,6 @@ src/TF_Init.cpp
|
||||
src/HiddenField.cpp
|
||||
src/BitTorrent/TorrentFile.cpp
|
||||
src/BitTorrent/TorrentStream.cpp
|
||||
src/BitTorrent/TorrentManager.cpp
|
||||
)
|
||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
|
||||
@@ -405,12 +404,6 @@ add_executable(tclearwebseed apps/tclearwebseed.cpp)
|
||||
target_link_libraries(tclearwebseed PUBLIC tessesframework)
|
||||
install(TARGETS tclearwebseed DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||
|
||||
add_executable(tdownloadsingletorrent apps/tdownloadsingletorrent.cpp)
|
||||
|
||||
target_link_libraries(tdownloadsingletorrent PUBLIC tessesframework)
|
||||
install(TARGETS tdownloadsingletorrent DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||
|
||||
|
||||
add_executable(trng apps/trng.cpp)
|
||||
|
||||
target_link_libraries(trng PUBLIC tessesframework)
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
#include "TessesFramework/TessesFramework.hpp"
|
||||
using namespace Tesses::Framework;
|
||||
using namespace Tesses::Framework::Streams;
|
||||
using namespace Tesses::Framework::Filesystem;
|
||||
using namespace Tesses::Framework::Serialization::Bencode;
|
||||
using namespace Tesses::Framework::TextStreams;
|
||||
using namespace Tesses::Framework::BitTorrent;
|
||||
int percent(ActiveTorrent& torrent)
|
||||
{
|
||||
if(torrent.torrentSize == 0) return 100;
|
||||
auto left = torrent.getLeft();
|
||||
return 100-(int)(((double)left / (double)torrent.torrentSize)*100.0);
|
||||
|
||||
|
||||
}
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
TF_Init();
|
||||
if(argc < 2)
|
||||
{
|
||||
printf("USAGE: %s /path/to/torrent/file\n",argv[0]);
|
||||
return 1;
|
||||
}
|
||||
auto strm = LocalFS->OpenFile((std::string)argv[1],"rb");
|
||||
auto bencode = Bencode::Load(strm);
|
||||
if(std::holds_alternative<BeDictionary>(bencode))
|
||||
{
|
||||
|
||||
TorrentFile file(std::get<BeDictionary>(bencode));
|
||||
|
||||
VFSPath path;
|
||||
path.relative=true;
|
||||
ActiveTorrent active(file,Filesystem::LocalFS,path,GeneratePeerId());
|
||||
int lastPercent = 0;
|
||||
std::cout << "0%" << std::endl;
|
||||
while(TF_IsRunning())
|
||||
{
|
||||
active.process();
|
||||
|
||||
int cur=percent(active);
|
||||
if(cur > lastPercent)
|
||||
{
|
||||
lastPercent = cur;
|
||||
std::cout << "\r" << std::to_string(cur) << "%";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
TF_Quit();
|
||||
return 0;
|
||||
}
|
||||
@@ -11,25 +11,5 @@ int main(int argc, char** argv)
|
||||
std::cout << Tesses::Framework::Http::BasicAuthServer::GetCreds(ctx,user,pass) << std::endl;
|
||||
std::cout << user << std::endl;
|
||||
std::cout << pass << std::endl;
|
||||
|
||||
std::cout << "Bitfield test" << std::endl;
|
||||
Tesses::Framework::BitTorrent::TorrentBitField bf(16);
|
||||
bf.set(7,false);
|
||||
bf.set(6,false);
|
||||
bf.set(5,true);
|
||||
bf.set(4,false);
|
||||
bf.set(3,true);
|
||||
bf.set(2,false);
|
||||
bf.set(1,true);
|
||||
bf.set(0,false);
|
||||
bf.set(15,false);
|
||||
bf.set(14,true);
|
||||
bf.set(13,false);
|
||||
bf.set(12,true);
|
||||
bf.set(11,false);
|
||||
bf.set(10,false);
|
||||
bf.set(9,false);
|
||||
bf.set(8,true);
|
||||
|
||||
std::cout << std::string(bf.data().begin(),bf.data().end()) << std::endl;
|
||||
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
#pragma once
|
||||
#include "TorrentFile.hpp"
|
||||
#include <queue>
|
||||
#include "../Threading/Thread.hpp"
|
||||
#include "../Random.hpp"
|
||||
namespace Tesses::Framework::BitTorrent
|
||||
{
|
||||
|
||||
class ActiveTorrent;
|
||||
|
||||
class TorrentBitField {
|
||||
std::vector<uint8_t> bits;
|
||||
size_t no_bits=0;
|
||||
public:
|
||||
TorrentBitField();
|
||||
TorrentBitField(size_t bits);
|
||||
bool get(size_t index);
|
||||
void set(size_t index, bool val);
|
||||
void zero();
|
||||
size_t size();
|
||||
void resize(size_t len);
|
||||
std::vector<uint8_t>& data();
|
||||
bool allone();
|
||||
};
|
||||
struct CancelRequest {
|
||||
uint32_t piece;
|
||||
uint32_t begin;
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
class TorrentPeer {
|
||||
public:
|
||||
bool isChokingMe;
|
||||
bool isChoked;
|
||||
bool intrested;
|
||||
std::shared_ptr<Tesses::Framework::Streams::NetworkStream> stream;
|
||||
std::vector<CancelRequest> cancel_requests;
|
||||
std::vector<uint8_t> messages;
|
||||
TorrentBitField has;
|
||||
|
||||
std::vector<std::optional<TorrentBitField>> blocksRequested;
|
||||
std::string ip;
|
||||
uint16_t port;
|
||||
};
|
||||
|
||||
class ActiveTorrent {
|
||||
public:
|
||||
Random rng;
|
||||
ActiveTorrent(TorrentFile file, std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs,Tesses::Framework::Filesystem::VFSPath directory, std::string peer_id);
|
||||
std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs;
|
||||
std::map<std::string,uint64_t> udp_connection_ids;
|
||||
int64_t downloaded;
|
||||
int64_t uploaded;
|
||||
int64_t getLeft();
|
||||
void addPeer(std::string ip, uint16_t port);
|
||||
Tesses::Framework::Serialization::Bencode::BeString info_hash;
|
||||
std::string peer_id;
|
||||
time_t lastTime;
|
||||
bool mustAnnounce();
|
||||
void udpAnounce(Tesses::Framework::Http::Uri uri);
|
||||
void httpAnounce(std::string url);
|
||||
Tesses::Framework::Threading::Mutex mtx;
|
||||
std::vector<std::optional<TorrentBitField>> blocksAquired;
|
||||
TorrentBitField has;
|
||||
TorrentFile file;
|
||||
int64_t torrentSize; //to make it more efficient
|
||||
Tesses::Framework::Filesystem::VFSPath directory;
|
||||
std::vector<std::shared_ptr<TorrentPeer>> connections;
|
||||
std::shared_ptr<ReadWriteAt> torrent_disk;
|
||||
|
||||
size_t pieceSize(size_t piece);
|
||||
std::array<uint8_t,1024> buffer;
|
||||
void process();
|
||||
bool processMessages(std::shared_ptr<TorrentPeer> peer);
|
||||
};
|
||||
class TorrentManager
|
||||
{
|
||||
std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs;
|
||||
Tesses::Framework::Filesystem::VFSPath defaultDirectory;
|
||||
std::vector<std::shared_ptr<ActiveTorrent>> downloading;
|
||||
std::vector<std::shared_ptr<ActiveTorrent>> seeding;
|
||||
std::queue<std::pair<TorrentFile,Tesses::Framework::Filesystem::VFSPath>> torrentQueue;
|
||||
int torrentCount;
|
||||
std::atomic<bool> running=false;
|
||||
public:
|
||||
TorrentManager(std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, Tesses::Framework::Filesystem::VFSPath defaultDirectory, int torrentCount);
|
||||
void AddTorrent(TorrentFile file);
|
||||
void AddTorrent(TorrentFile file, Tesses::Framework::Filesystem::VFSPath directory);
|
||||
void Start();
|
||||
void Stop();
|
||||
~TorrentManager();
|
||||
};
|
||||
std::string GeneratePeerId();
|
||||
|
||||
}
|
||||
@@ -44,5 +44,5 @@
|
||||
#include "Platform/Process.hpp"
|
||||
#include "Serialization/BitConverter.hpp"
|
||||
#include "Args.hpp"
|
||||
#include "BitTorrent/TorrentManager.hpp"
|
||||
#include "BitTorrent/TorrentFile.hpp"
|
||||
#include "Random.hpp"
|
||||
@@ -1,657 +0,0 @@
|
||||
#include "TessesFramework/BitTorrent/TorrentManager.hpp"
|
||||
#include "TessesFramework/Serialization/BitConverter.hpp"
|
||||
#include "TessesFramework/Crypto/Crypto.hpp"
|
||||
#include "TessesFramework/Random.hpp"
|
||||
|
||||
namespace Tesses::Framework::BitTorrent
|
||||
{
|
||||
|
||||
TorrentBitField::TorrentBitField()
|
||||
{
|
||||
|
||||
}
|
||||
TorrentBitField::TorrentBitField(size_t len)
|
||||
{
|
||||
resize(len);
|
||||
}
|
||||
size_t TorrentBitField::size()
|
||||
{
|
||||
return this->no_bits;
|
||||
}
|
||||
void TorrentBitField::resize(size_t len)
|
||||
{
|
||||
this->no_bits = len;
|
||||
size_t no = len / 8;
|
||||
if((len % 8) != 0)
|
||||
no++;
|
||||
this->bits.resize(no);
|
||||
}
|
||||
bool TorrentBitField::get(size_t bit)
|
||||
{
|
||||
size_t byte = bit / 8;
|
||||
size_t byteBit = 7-bit % 8;
|
||||
|
||||
return (this->bits.at(byte) >> byteBit) & 1;
|
||||
}
|
||||
bool TorrentBitField::allone()
|
||||
{
|
||||
|
||||
for(size_t i = 0; i < this->no_bits; i++)
|
||||
{
|
||||
size_t byte = i / 8;
|
||||
size_t byteBit = i % 8;
|
||||
if(((this->bits.at(byte) >> byteBit) & 1) == 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void TorrentBitField::set(size_t bit, bool val)
|
||||
{
|
||||
size_t byte = bit / 8;
|
||||
size_t byteBit = 7-bit % 8;
|
||||
|
||||
|
||||
if(val)
|
||||
{
|
||||
this->bits.at(byte) |= 1 << byteBit;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->bits.at(byte) &= ~(1 << byteBit);
|
||||
}
|
||||
}
|
||||
void TorrentBitField::zero()
|
||||
{
|
||||
std::fill(this->bits.begin(),this->bits.end(),0);
|
||||
}
|
||||
std::vector<uint8_t>& TorrentBitField::data()
|
||||
{
|
||||
return this->bits;
|
||||
}
|
||||
|
||||
|
||||
void TorrentManager::Start()
|
||||
{
|
||||
|
||||
}
|
||||
bool ActiveTorrent::mustAnnounce()
|
||||
{
|
||||
time_t curTime = time(NULL);
|
||||
if(curTime < lastTime || (lastTime + 1800) <= curTime)
|
||||
{
|
||||
std::cout << "Must anounce" << std::endl;
|
||||
this->lastTime = curTime;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
std::string bt_handshake_str = "BitTorrent protocol";
|
||||
void ActiveTorrent::addPeer(std::string ip, uint16_t port)
|
||||
{
|
||||
std::cout << "Got peer: " << ip << ":" << port << std::endl;
|
||||
if(port == 0) return;
|
||||
for(auto conn : this->connections)
|
||||
{
|
||||
if(conn->ip == ip && conn->port == port) return;
|
||||
}
|
||||
auto peer = std::make_shared<TorrentPeer>();
|
||||
peer->ip = ip;
|
||||
peer->port = port;
|
||||
peer->isChoked=true;
|
||||
peer->isChokingMe=true;
|
||||
peer->intrested=false;
|
||||
peer->has.resize(this->has.size());
|
||||
peer->blocksRequested.resize(this->has.size());
|
||||
peer->stream = std::make_shared<Tesses::Framework::Streams::NetworkStream>(ip,port,false,false,false);
|
||||
|
||||
std::vector<uint8_t> handshake;
|
||||
handshake.resize(49+bt_handshake_str.size());
|
||||
handshake[0] = (uint8_t)bt_handshake_str.size();
|
||||
std::copy(bt_handshake_str.begin(),bt_handshake_str.end(),handshake.begin()+1);
|
||||
auto off = bt_handshake_str.size()+1;
|
||||
for(size_t i = 0; i < 8; i++)
|
||||
handshake[off+i] = 0;
|
||||
std::copy(this->info_hash.data.begin(),this->info_hash.data.end(),handshake.begin()+off+8);
|
||||
std::copy(this->peer_id.begin(),this->peer_id.end(),handshake.begin()+off+28);
|
||||
peer->stream->WriteBlock(handshake.data(),handshake.size());
|
||||
handshake.resize(5+this->has.data().size());
|
||||
Serialization::BitConverter::FromUint32BE(handshake[0],this->has.data().size());
|
||||
handshake[4] = 5;
|
||||
|
||||
std::copy(this->has.data().begin(),this->has.data().end(),handshake.begin()+5);
|
||||
peer->stream->WriteBlock(handshake.data(),handshake.size());
|
||||
|
||||
Serialization::BitConverter::FromUint32BE(handshake[0],1);
|
||||
handshake[4] = 2;
|
||||
|
||||
peer->stream->WriteBlock(handshake.data(),5);
|
||||
std::cout << "Handshake send complete for: " << ip << ":" << port << std::endl;
|
||||
|
||||
|
||||
this->connections.push_back(peer);
|
||||
}
|
||||
void ActiveTorrent::udpAnounce(Tesses::Framework::Http::Uri uri)
|
||||
{
|
||||
std::string connUrl = uri.HostPort();
|
||||
std::cout << "udp anouncing via: " << connUrl << std::endl;
|
||||
Streams::NetworkStream strm(uri.host,uri.port,true,false,false);
|
||||
|
||||
auto trans_id = rng.Next(0xFFFFFFFF);
|
||||
if(this->udp_connection_ids.count(connUrl) == 0)
|
||||
{
|
||||
|
||||
|
||||
Serialization::BitConverter::FromUint64BE(this->buffer[0],0x41727101980);
|
||||
Serialization::BitConverter::FromUint32BE(this->buffer[8],0);
|
||||
Serialization::BitConverter::FromUint32BE(this->buffer[12],trans_id);
|
||||
|
||||
std::cout << "writing ze data" << std::endl;
|
||||
strm.Write(this->buffer.data(),16);
|
||||
std::cout << "reading ze data" << std::endl;
|
||||
if(!strm.DataAvailable(5000)) {
|
||||
std::cout << "timeout" << std::endl;
|
||||
return;
|
||||
}
|
||||
if(strm.Read(this->buffer.data(),this->buffer.size()) != 16) return;
|
||||
std::cout << "read ze data" << std::endl;
|
||||
if(Serialization::BitConverter::ToUint32BE(this->buffer[0]) == 0 && Serialization::BitConverter::ToUint32BE(this->buffer[4]) == trans_id)
|
||||
{
|
||||
this->udp_connection_ids[connUrl] = Serialization::BitConverter::ToUint64BE(this->buffer[8]);
|
||||
|
||||
trans_id = rng.Next(0xFFFFFFFF);
|
||||
} else return;
|
||||
}
|
||||
Serialization::BitConverter::FromUint64BE(this->buffer[0],this->udp_connection_ids[connUrl]);
|
||||
Serialization::BitConverter::FromUint32BE(this->buffer[8],1);
|
||||
Serialization::BitConverter::FromUint32BE(this->buffer[12],trans_id);
|
||||
std::copy(this->info_hash.data.begin(),this->info_hash.data.end(),this->buffer.begin()+16);
|
||||
|
||||
std::copy(this->peer_id.begin(),this->peer_id.end(),this->buffer.begin()+36);
|
||||
Serialization::BitConverter::FromUint64BE(this->buffer[56], this->downloaded);
|
||||
Serialization::BitConverter::FromUint64BE(this->buffer[64], this->getLeft());
|
||||
Serialization::BitConverter::FromUint64BE(this->buffer[72], this->uploaded);
|
||||
Serialization::BitConverter::FromUint32BE(this->buffer[80],0);
|
||||
Serialization::BitConverter::FromUint32BE(this->buffer[84],0);
|
||||
Serialization::BitConverter::FromUint32BE(this->buffer[88],0);
|
||||
Serialization::BitConverter::FromUint32BE(this->buffer[92],1); //peers
|
||||
Serialization::BitConverter::FromUint16BE(this->buffer[96],0);
|
||||
strm.Write(this->buffer.data(),98);
|
||||
if(!strm.DataAvailable(5000)) {
|
||||
std::cout << "timeout" << std::endl;
|
||||
return;
|
||||
}
|
||||
size_t read = strm.Read(this->buffer.data(),this->buffer.size());
|
||||
if(read < 20) return;
|
||||
if(Serialization::BitConverter::ToUint32BE(this->buffer[0]) == 1 && Serialization::BitConverter::ToUint32BE(this->buffer[4]) == trans_id)
|
||||
{
|
||||
std::cout << "Found " << ((read - 20) / 6) << " peers" << std::endl;
|
||||
for(size_t peerIdx = 20; peerIdx + 6 <= read; peerIdx+=6)
|
||||
{
|
||||
std::string ip = std::to_string((uint32_t)this->buffer[peerIdx]);
|
||||
ip += "." + std::to_string((uint32_t)this->buffer[peerIdx+1]);
|
||||
ip += "." + std::to_string((uint32_t)this->buffer[peerIdx+2]);
|
||||
ip += "." + std::to_string((uint32_t)this->buffer[peerIdx+3]);
|
||||
|
||||
uint16_t port = Tesses::Framework::Serialization::BitConverter::ToUint16BE(this->buffer[peerIdx+4]);
|
||||
|
||||
addPeer(ip,port);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
int64_t ActiveTorrent::getLeft()
|
||||
{
|
||||
int64_t left = 0;
|
||||
size_t normalPieceSize = pieceSize(0);
|
||||
size_t lastPieceSize = pieceSize(this->has.size()-1);
|
||||
for(size_t i = 0;i < this->has.size(); i++)
|
||||
{
|
||||
if(!this->has.get(i))
|
||||
if(i == this->has.size() - 1)
|
||||
left += lastPieceSize;
|
||||
else
|
||||
left += normalPieceSize;
|
||||
}
|
||||
return left;
|
||||
}
|
||||
void ActiveTorrent::httpAnounce(std::string url)
|
||||
{
|
||||
std::string newUrl = url + "?info_hash=" + Http::HttpUtils::UrlEncode((std::string)this->info_hash) + "&peer_id=" + Http::HttpUtils::UrlEncode(this->peer_id) + "&uploaded=" + std::to_string(this->uploaded) + "&downloaded=" + std::to_string(this->downloaded) + "&left=" + std::to_string(getLeft()) + "&numwant=10&compact=1&no_peer_id=1&port=0";
|
||||
Http::HttpRequest req;
|
||||
req.url= newUrl;
|
||||
req.method = "GET";
|
||||
Http::HttpResponse resp(req);
|
||||
if(resp.statusCode == 200)
|
||||
{
|
||||
auto bencode=Tesses::Framework::Serialization::Bencode::Bencode::Load(resp.ReadAsStream());
|
||||
if(std::holds_alternative<Tesses::Framework::Serialization::Bencode::BeDictionary>(bencode))
|
||||
{
|
||||
auto& dict = std::get<Tesses::Framework::Serialization::Bencode::BeDictionary>(bencode);
|
||||
auto peers = dict.GetValue("peers");
|
||||
if(std::holds_alternative<Tesses::Framework::Serialization::Bencode::BeString>(peers))
|
||||
{
|
||||
//this is compact
|
||||
auto compactPeers=std::get<Tesses::Framework::Serialization::Bencode::BeString>(peers);
|
||||
for(size_t peerIdx = 0; peerIdx + 6 <= compactPeers.data.size(); peerIdx+=6)
|
||||
{
|
||||
std::string ip = std::to_string((uint32_t)compactPeers.data[peerIdx]);
|
||||
ip += "." + std::to_string((uint32_t)compactPeers.data[peerIdx+1]);
|
||||
ip += "." + std::to_string((uint32_t)compactPeers.data[peerIdx+2]);
|
||||
ip += "." + std::to_string((uint32_t)compactPeers.data[peerIdx+3]);
|
||||
|
||||
uint16_t port = Tesses::Framework::Serialization::BitConverter::ToUint16BE(compactPeers.data[peerIdx+4]);
|
||||
|
||||
addPeer(ip,port);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto normalPeers = std::get<Tesses::Framework::Serialization::Bencode::BeArray>(peers);
|
||||
for(auto& item : normalPeers.tokens)
|
||||
{
|
||||
if(std::holds_alternative<Tesses::Framework::Serialization::Bencode::BeDictionary>(item))
|
||||
{
|
||||
auto& dict2=std::get<Tesses::Framework::Serialization::Bencode::BeDictionary>(item);
|
||||
auto ip = dict2.GetValue("ip");
|
||||
auto port = dict2.GetValue("port");
|
||||
if(std::holds_alternative<Tesses::Framework::Serialization::Bencode::BeString>(ip) && std::holds_alternative<int64_t>(port))
|
||||
{
|
||||
addPeer((std::string)std::get<Tesses::Framework::Serialization::Bencode::BeString>(ip),(uint16_t)(uint64_t)std::get<int64_t>(port));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActiveTorrent::process()
|
||||
{
|
||||
if(mustAnnounce())
|
||||
{
|
||||
std::vector<Tesses::Framework::Serialization::Bencode::BeString> announces;
|
||||
if(this->file.announce_list.empty())
|
||||
announces.push_back(this->file.announce);
|
||||
announces.insert(announces.end(),this->file.announce_list.begin(),this->file.announce_list.end());
|
||||
|
||||
for(auto& a : announces)
|
||||
{
|
||||
Tesses::Framework::Http::Uri uri;
|
||||
if(Tesses::Framework::Http::Uri::TryParse(a,uri))
|
||||
{
|
||||
if(uri.scheme == "udp:")
|
||||
{
|
||||
udpAnounce(uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
httpAnounce(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << "end of anounces" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
for(auto item : this->connections)
|
||||
{
|
||||
if(!item->isChokingMe)
|
||||
{
|
||||
for(size_t piece = 0; piece < this->has.size(); piece++)
|
||||
{
|
||||
if(!this->has.get(piece) && item->has.get(piece))
|
||||
{
|
||||
bool hasFound=false;
|
||||
//find this piece in bitfields
|
||||
//find a block we don't have and that we never requested
|
||||
//we need to ensure that both the peer and I have the bitfields for the piece
|
||||
|
||||
size_t _pieceSize = pieceSize(piece);
|
||||
auto blockCount = _pieceSize / 16384;
|
||||
if((_pieceSize % 16384) != 0) blockCount++;
|
||||
if(!this->blocksAquired[piece] )
|
||||
{
|
||||
this->blocksAquired[piece] =TorrentBitField(blockCount);
|
||||
}
|
||||
if(!item->blocksRequested[piece] )
|
||||
{
|
||||
item->blocksRequested[piece] =TorrentBitField(blockCount);
|
||||
}
|
||||
|
||||
for(size_t block = 0; block < blockCount; block++)
|
||||
{
|
||||
if(!this->blocksAquired[piece]->get(block) && !item->blocksRequested[piece]->get(block))
|
||||
{
|
||||
item->blocksRequested[piece]->set(block,true);
|
||||
hasFound=true;
|
||||
|
||||
std::vector<uint8_t> msg((size_t)17);
|
||||
Serialization::BitConverter::FromUint32BE(msg[0],13);
|
||||
msg[4] = 6;
|
||||
Serialization::BitConverter::FromUint32BE(msg[5], (uint32_t)piece);
|
||||
|
||||
Serialization::BitConverter::FromUint32BE(msg[9], (uint32_t)(16384*block));
|
||||
size_t readBlockSize = 16384;
|
||||
if((_pieceSize % 16384) != 0 && block >= blockCount - 1) readBlockSize = _pieceSize % 16384;
|
||||
|
||||
Serialization::BitConverter::FromUint32BE(msg[13], (uint32_t)(readBlockSize));
|
||||
|
||||
item->stream->WriteBlock(msg.data(),msg.size());
|
||||
|
||||
std::cout << "Request block: " << block << " of piece: " << piece << " from peer: " << item->ip << ":" << item->port << std::endl;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(hasFound) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else std::cout << "Is choking" << std::endl;
|
||||
if(item->stream->DataAvailable(1))
|
||||
{
|
||||
std::cout << "Has message" << std::endl;
|
||||
size_t read = item->stream->Read(buffer.data(),buffer.size());
|
||||
item->messages.insert(item->messages.cend(),buffer.begin(),buffer.begin()+read);
|
||||
if(!processMessages(item))
|
||||
{
|
||||
std::cout << "Peer " << item->ip << ":" << item->port << " misbehaved" << std::endl;
|
||||
item->stream=nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(auto idx = this->connections.begin(); idx != this->connections.end(); idx++)
|
||||
{
|
||||
auto item = *idx;
|
||||
bool destroy = true;
|
||||
if(item)
|
||||
{
|
||||
destroy = !item->stream;
|
||||
}
|
||||
if(destroy)
|
||||
{
|
||||
this->connections.erase(idx);
|
||||
idx--;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
size_t ActiveTorrent::pieceSize(size_t piece)
|
||||
{
|
||||
if(piece == this->has.size()-1)
|
||||
{
|
||||
int64_t remainder = this->torrentSize % this->file.info.piece_length;
|
||||
if(remainder != 0)
|
||||
return (size_t)remainder;
|
||||
}
|
||||
return this->file.info.piece_length;
|
||||
}
|
||||
bool ActiveTorrent::processMessages(std::shared_ptr<TorrentPeer> peer)
|
||||
{
|
||||
if(!peer->stream) return false;
|
||||
using namespace Tesses::Framework::Serialization;
|
||||
while(peer->messages.size() >= 4)
|
||||
{
|
||||
auto len = BitConverter::ToUint32BE(peer->messages[0]);
|
||||
if(len + 4 > peer->messages.size()) break;
|
||||
|
||||
if(len > 0)
|
||||
{
|
||||
uint8_t id = peer->messages[4];
|
||||
switch(id)
|
||||
{
|
||||
case 0:
|
||||
peer->isChokingMe=true;
|
||||
std::cout << peer->ip << ":" << peer->port << " choked me :(" << std::endl;
|
||||
break;
|
||||
case 1:
|
||||
peer->isChokingMe=false;
|
||||
|
||||
std::cout << peer->ip << ":" << peer->port << " unchoked me :)" << std::endl;
|
||||
if(this->has.allone())
|
||||
{
|
||||
//send not intrested
|
||||
std::vector<uint8_t> msg((size_t)5);
|
||||
BitConverter::FromUint32BE(msg[0],1);
|
||||
msg[4] = 3;
|
||||
|
||||
peer->stream->WriteBlock(msg.data(),msg.size());
|
||||
}
|
||||
else {
|
||||
std::vector<uint8_t> msg((size_t)5);
|
||||
BitConverter::FromUint32BE(msg[0],1);
|
||||
msg[4] = 2;
|
||||
|
||||
peer->stream->WriteBlock(msg.data(),msg.size());
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
|
||||
std::cout << peer->ip << ":" << peer->port << " is intrested" << std::endl;
|
||||
peer->intrested=true;
|
||||
break;
|
||||
case 3:
|
||||
|
||||
std::cout << peer->ip << ":" << peer->port << " is not intrested" << std::endl;
|
||||
peer->intrested=false;
|
||||
break;
|
||||
case 4:
|
||||
{
|
||||
|
||||
auto pieceIndex = BitConverter::ToUint32BE(peer->messages[5]);
|
||||
if(pieceIndex < peer->has.size())
|
||||
peer->has.set((size_t)pieceIndex,true);
|
||||
else return false;
|
||||
|
||||
std::cout << peer->ip << ":" << peer->port << " has piece " << pieceIndex << std::endl;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
{
|
||||
if(len-1 == peer->has.data().size())
|
||||
{
|
||||
std::copy(peer->messages.begin()+5,peer->messages.begin()+4+len,peer->has.data().begin());
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
{
|
||||
if(peer->isChoked) break;
|
||||
if(len == 13)
|
||||
{
|
||||
auto pieceIndex = BitConverter::ToUint32BE(peer->messages[5]);
|
||||
auto offset = BitConverter::ToUint32BE(peer->messages[9]);
|
||||
auto length = BitConverter::ToUint32BE(peer->messages[13]);
|
||||
|
||||
|
||||
|
||||
std::cout << peer->ip << ":" << peer->port << " wants piece: " << pieceIndex << " offset: " << offset << " length: " << length << std::endl;
|
||||
|
||||
|
||||
|
||||
if(pieceIndex < this->has.size())
|
||||
{
|
||||
size_t _pieceSize = pieceSize((size_t)pieceIndex);
|
||||
if((size_t)offset >= _pieceSize) return false;
|
||||
if(length > 16384) return false;
|
||||
if(length+offset > _pieceSize) return false;
|
||||
|
||||
if(this->has.get((size_t)pieceIndex))
|
||||
{
|
||||
for(auto index = peer->cancel_requests.begin(); index != peer->cancel_requests.end(); index++)
|
||||
{
|
||||
if(index->piece == pieceIndex && index->begin == offset && index->length == length)
|
||||
{
|
||||
peer->cancel_requests.erase(index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> message;
|
||||
message.resize(13+length);
|
||||
BitConverter::FromUint32BE(message[0],9+length);
|
||||
message[4] = 7;
|
||||
BitConverter::FromUint32BE(message[5], pieceIndex);
|
||||
BitConverter::FromUint32BE(message[9], offset);
|
||||
|
||||
this->torrent_disk->ReadBlockAt(pieceIndex * this->file.info.piece_length + offset, message.data()+13 ,message.size()-13);
|
||||
peer->stream->WriteBlock(message.data(),message.size());
|
||||
}
|
||||
}
|
||||
else return false;
|
||||
} else return false;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
{
|
||||
if(len > 9)
|
||||
{
|
||||
auto pieceIndex = BitConverter::ToUint32BE(peer->messages[5]);
|
||||
auto offset = BitConverter::ToUint32BE(peer->messages[9]);
|
||||
auto length = len - 9;
|
||||
|
||||
std::cout << peer->ip << ":" << peer->port << " gave me a piece! piece: " << pieceIndex << " offset: " << offset << " length: " << length << std::endl;
|
||||
|
||||
if(pieceIndex < this->has.size())
|
||||
{
|
||||
size_t _pieceSize = pieceSize((size_t)pieceIndex);
|
||||
if((size_t)offset >= _pieceSize) return false;
|
||||
if(length > 16384) return false;
|
||||
if(length+offset > _pieceSize) return false;
|
||||
if(!this->has.get((size_t)pieceIndex))
|
||||
{
|
||||
this->torrent_disk->WriteBlockAt(pieceIndex * this->file.info.piece_length + offset, peer->messages.data()+9 ,length);
|
||||
if(!this->blocksAquired[pieceIndex] )
|
||||
{
|
||||
auto blockCount = _pieceSize / 16384;
|
||||
if((_pieceSize % 16384) != 0) blockCount++;
|
||||
|
||||
this->blocksAquired[pieceIndex] = Tesses::Framework::BitTorrent::TorrentBitField(blockCount);
|
||||
}
|
||||
|
||||
this->blocksAquired[pieceIndex]->set(offset/16384, true);
|
||||
if(this->blocksAquired[pieceIndex]->allone())
|
||||
{
|
||||
std::vector<uint8_t> data(_pieceSize);
|
||||
if(this->torrent_disk->ReadBlockAt(pieceIndex*this->file.info.piece_length,data.data(),data.size()))
|
||||
{
|
||||
auto hash = Tesses::Framework::Crypto::Sha1::ComputeHash(data.data(),data.size());
|
||||
if(std::equal(hash.begin(),hash.end(),this->file.info.pieces.data.begin()+(pieceIndex*20)))
|
||||
{
|
||||
this->has.set(pieceIndex,true);
|
||||
auto pieceFile = directory / this->file.info.name + ".tftpart";
|
||||
{
|
||||
auto pfs = vfs->OpenFile(pieceFile,"wb");
|
||||
pfs->WriteBlock(has.data().data(),has.data().size());
|
||||
}
|
||||
this->blocksAquired[pieceIndex] = std::nullopt;
|
||||
peer->blocksRequested[pieceIndex] = std::nullopt;
|
||||
{
|
||||
std::vector<uint8_t> msg((size_t)9);
|
||||
BitConverter::FromUint32BE(msg[0],5);
|
||||
msg[4] = 4;
|
||||
BitConverter::FromUint32BE(msg[5],(uint32_t)pieceIndex);
|
||||
for(auto& peer2 : this->connections)
|
||||
{
|
||||
if(peer2->stream)
|
||||
{
|
||||
peer2->stream->WriteBlock(msg.data(),msg.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
if(this->has.allone())
|
||||
{
|
||||
TF_LOG("The torrent: " + (std::string)this->file.info.name + " is completed");
|
||||
std::vector<uint8_t> msg((size_t)5);
|
||||
BitConverter::FromUint32BE(msg[0],1);
|
||||
msg[4] = 3;
|
||||
for(auto& peer2 : this->connections)
|
||||
{
|
||||
if(peer2->stream)
|
||||
{
|
||||
//send not intrested
|
||||
peer2->stream->WriteBlock(msg.data(),msg.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
//CORRUPT
|
||||
this->blocksAquired[pieceIndex]->zero();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//CORRUPT
|
||||
this->blocksAquired[pieceIndex]->zero();
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
{
|
||||
if(len == 13)
|
||||
{
|
||||
auto pieceIndex = BitConverter::ToUint32BE(peer->messages[5]);
|
||||
auto offset = BitConverter::ToUint32BE(peer->messages[9]);
|
||||
auto length = BitConverter::ToUint32BE(peer->messages[13]);
|
||||
CancelRequest r;
|
||||
r.piece = pieceIndex;
|
||||
r.begin = offset;
|
||||
r.length = length;
|
||||
|
||||
peer->cancel_requests.push_back(r);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
peer->messages.erase(peer->messages.begin(),peer->messages.begin()+4+len);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ActiveTorrent::ActiveTorrent(TorrentFile file, std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs,Tesses::Framework::Filesystem::VFSPath directory, std::string peer_id)
|
||||
{
|
||||
this->file = file;
|
||||
this->vfs = vfs;
|
||||
this->directory = directory;
|
||||
this->peer_id = peer_id;
|
||||
this->has.resize(this->file.info.pieces.data.size()/20);
|
||||
this->torrentSize = this->file.info.GetTorrentFileSize();
|
||||
this->info_hash = this->file.info.GetInfoHash();
|
||||
this->lastTime = 0;
|
||||
this->blocksAquired.resize(this->has.size());
|
||||
this->uploaded = 0;
|
||||
this->downloaded = 0;
|
||||
|
||||
auto pieceFile = directory / this->file.info.name + ".tftpart";
|
||||
if(vfs->FileExists(pieceFile))
|
||||
{
|
||||
auto strm = vfs->OpenFile(pieceFile,"rb");
|
||||
strm->ReadBlock(has.data().data(),has.data().size());
|
||||
}
|
||||
this->torrent_disk = this->file.info.GetStreamFromFilesystem(vfs,directory);
|
||||
}
|
||||
std::string lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
std::string GeneratePeerId()
|
||||
{
|
||||
std::string peerId((size_t)20,' ');
|
||||
peerId.insert(0,"TFWT");
|
||||
Random r;
|
||||
for(size_t i = 4; i < peerId.size(); i++)
|
||||
peerId[i] = lookup[(int)r.Next((uint32_t)lookup.size())];
|
||||
return peerId;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user