mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2026-10-06 14:18:27 +00:00
parse id and infoHash as 20-bytes array. Add node to DHT after receiveing ping. Send reposnse to announce_peer
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
#ifndef NO_TORRENTS
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/dynamic_bitset.hpp>
|
||||
@@ -45,6 +46,17 @@ namespace torrents
|
||||
std::string CreateInteger (int64_t v);
|
||||
std::string CreateDictionary (const std::vector<std::pair<std::string_view, std::string_view> >& items);
|
||||
std::string CreateList (const std::vector<std::string>& items);
|
||||
template<size_t sz>
|
||||
std::pair<size_t, bool> ParseByteArray (std::string_view buf, std::array<uint8_t, sz>& arr)
|
||||
{
|
||||
auto [str, len] = ExtractByteString (buf);
|
||||
if (len && str.size () >= sz)
|
||||
{
|
||||
memcpy (arr.data (), str.data (), sz);
|
||||
return { len, true };
|
||||
}
|
||||
return { len, false };
|
||||
}
|
||||
|
||||
|
||||
constexpr size_t REQUEST_BLOCK_SIZE = 16384;
|
||||
|
||||
@@ -349,12 +349,14 @@ namespace torrents
|
||||
void TorrentsDHT::HandleRawDatagram (const uint8_t * buf, size_t len)
|
||||
{
|
||||
// response or error
|
||||
char type = 0;
|
||||
std::string transactionID, query, id, infoHash;
|
||||
uint64_t token = 0;
|
||||
char type = 0; uint64_t token = 0;
|
||||
bool isMalformed = false;
|
||||
NodeID id; Torrent::InfoHash infoHash;
|
||||
std::string transactionID, query;
|
||||
std::vector<std::string_view> values;
|
||||
ParseDictionary (std::string_view ((const char *)buf, len),
|
||||
[&type, &transactionID, &id, &values, &token, &infoHash, &query](std::string_view key, std::string_view buf)->size_t
|
||||
[&type, &transactionID, &id, &values, &token, &infoHash, &query, &isMalformed]
|
||||
(std::string_view key, std::string_view buf)->size_t
|
||||
{
|
||||
if (key == "y")
|
||||
{
|
||||
@@ -377,12 +379,12 @@ namespace torrents
|
||||
else if (key == "r" || key == "a")
|
||||
{
|
||||
return ParseDictionary (buf,
|
||||
[&id, &values, &token, &infoHash](std::string_view key, std::string_view buf)->size_t
|
||||
[&id, &values, &token, &infoHash, &isMalformed](std::string_view key, std::string_view buf)->size_t
|
||||
{
|
||||
if (key == "id")
|
||||
{
|
||||
auto [value, l] = ExtractByteString (buf);
|
||||
if (l) id = value;
|
||||
auto [l, success] = ParseByteArray (buf, id);
|
||||
if (!success) isMalformed = true;
|
||||
return l;
|
||||
}
|
||||
else if (key == "values")
|
||||
@@ -400,8 +402,8 @@ namespace torrents
|
||||
}
|
||||
else if (key == "info_hash")
|
||||
{
|
||||
auto [value, l] = ExtractByteString (buf);
|
||||
if (l) infoHash = value;
|
||||
auto [l, success] = ParseByteArray (buf, infoHash);
|
||||
if (!success) isMalformed = true;
|
||||
return l;
|
||||
}
|
||||
return 0;
|
||||
@@ -409,6 +411,11 @@ namespace torrents
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
if (isMalformed)
|
||||
{
|
||||
LogPrint (eLogInfo, "TorrentsDHT: Malformed raw datagram received");
|
||||
return;
|
||||
}
|
||||
if (type)
|
||||
{
|
||||
switch (type)
|
||||
@@ -421,7 +428,7 @@ namespace torrents
|
||||
break;
|
||||
case 'q':
|
||||
if (query == "announce_peer")
|
||||
HandleAnnouncePeer (infoHash, token);
|
||||
HandleAnnouncePeer (transactionID, infoHash, token);
|
||||
else
|
||||
LogPrint (eLogError, "TorrentsDHT: Query can't come as raw datagram");
|
||||
break;
|
||||
@@ -436,9 +443,12 @@ namespace torrents
|
||||
{
|
||||
// query
|
||||
char type = 0;
|
||||
std::string transactionID, query, id, infoHash;
|
||||
bool isMalformed = false;
|
||||
std::string transactionID, query;
|
||||
NodeID id; Torrent::InfoHash infoHash;
|
||||
ParseDictionary (std::string_view ((const char *)buf, len),
|
||||
[&type, &transactionID, &query, &id, &infoHash](std::string_view key, std::string_view buf)->size_t
|
||||
[&type, &transactionID, &query, &id, &infoHash, &isMalformed]
|
||||
(std::string_view key, std::string_view buf)->size_t
|
||||
{
|
||||
if (key == "y")
|
||||
{
|
||||
@@ -461,18 +471,18 @@ namespace torrents
|
||||
else if (key == "a")
|
||||
{
|
||||
return ParseDictionary (buf,
|
||||
[&id, &infoHash](std::string_view key, std::string_view buf)->size_t
|
||||
[&id, &infoHash,&isMalformed](std::string_view key, std::string_view buf)->size_t
|
||||
{
|
||||
if (key == "id")
|
||||
{
|
||||
auto [value, l] = ExtractByteString (buf);
|
||||
if (l) id = value;
|
||||
auto [l, success] = ParseByteArray (buf, id);
|
||||
if (!success) isMalformed = true;
|
||||
return l;
|
||||
}
|
||||
else if (key == "info_hash")
|
||||
{
|
||||
auto [value, l] = ExtractByteString (buf);
|
||||
if (l) infoHash = value;
|
||||
auto [l, success] = ParseByteArray (buf, infoHash);
|
||||
if (!success) isMalformed = true;
|
||||
return l;
|
||||
}
|
||||
return 0;
|
||||
@@ -480,6 +490,11 @@ namespace torrents
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
if (isMalformed)
|
||||
{
|
||||
LogPrint (eLogInfo, "TorrentsDHT: Malformed datagram received");
|
||||
return;
|
||||
}
|
||||
if (type == 'q')
|
||||
{
|
||||
if (query == "ping")
|
||||
@@ -494,79 +509,65 @@ namespace torrents
|
||||
}
|
||||
|
||||
void TorrentsDHT::HandlePingQuery (const i2p::data::IdentHash& fromIdent, uint16_t fromPort,
|
||||
std::string_view transactionID, std::string_view id)
|
||||
std::string_view transactionID, const NodeID& nodeID)
|
||||
{
|
||||
LogPrint (eLogDebug, "TorrentsDHT: Ping query msg received");
|
||||
m_RoutingTable->AddNode (nodeID, fromIdent, fromPort);
|
||||
SendPingResponse (transactionID, fromIdent, fromPort + 1); // to rport
|
||||
}
|
||||
|
||||
void TorrentsDHT::HandleGetPeersQuery (const i2p::data::IdentHash& fromIdent, uint16_t fromPort,
|
||||
std::string_view transactionID, std::string_view id, std::string_view infoHash)
|
||||
std::string_view transactionID, const NodeID& nodeID, const Torrent::InfoHash& infoHash)
|
||||
{
|
||||
LogPrint (eLogDebug, "TorrentsDHT: Get peers query msg received");
|
||||
if (id.size () < NodeID::len)
|
||||
{
|
||||
LogPrint (eLogInfo, "TorrentsDHT: received id is too short ", id.size ());
|
||||
return;
|
||||
}
|
||||
NodeID nodeID;
|
||||
memcpy (nodeID.data (), id.data (), NodeID::len);
|
||||
auto node = m_RoutingTable->AddNode (nodeID, fromIdent, fromPort);
|
||||
if (!node)
|
||||
{
|
||||
LogPrint (eLogError, "TorrentsDHT: Failed to add node ", fromIdent.ToBase64 ());
|
||||
return;
|
||||
}
|
||||
Torrent::InfoHash hash;
|
||||
if (infoHash.size () < hash.size ())
|
||||
{
|
||||
LogPrint (eLogInfo, "TorrentsDHT: Requested info hash is too short ", infoHash.size ());
|
||||
return;
|
||||
}
|
||||
memcpy (hash.data (), infoHash.data (), hash.size ());
|
||||
|
||||
std::shared_ptr<DHTTorrent> torrent;
|
||||
auto it = m_Torrents.find (hash);
|
||||
auto it = m_Torrents.find (infoHash);
|
||||
if (it != m_Torrents.end ())
|
||||
torrent = it->second;
|
||||
else
|
||||
{
|
||||
torrent = std::make_shared<DHTTorrent>();
|
||||
m_Torrents.emplace (hash, torrent);
|
||||
m_Torrents.emplace (infoHash, torrent);
|
||||
}
|
||||
uint64_t token = m_Tunnel.GetLocalDestination () ? m_Tunnel.GetLocalDestination ()->GetRng ()() : 1;
|
||||
torrent->AddIncomingGetPeerNode (token, node);
|
||||
|
||||
if (m_RoutingTable)
|
||||
{
|
||||
auto nodes = m_RoutingTable->FindClosestNodes (hash);
|
||||
if (!nodes.empty () && nodes.front ().second < (m_NodeID ^ hash))
|
||||
auto nodes = m_RoutingTable->FindClosestNodes (infoHash);
|
||||
if (!nodes.empty () && nodes.front ().second < (m_NodeID ^ infoHash))
|
||||
SendGetPeersResponse (transactionID, nodes.front ().first, token, fromIdent, fromPort + 1); // to rport
|
||||
else
|
||||
SendGetPeersResponse (transactionID, torrent, token, fromIdent, fromPort + 1); // to rport
|
||||
}
|
||||
}
|
||||
|
||||
void TorrentsDHT::HandleAnnouncePeer (std::string_view infoHash, uint64_t token)
|
||||
void TorrentsDHT::HandleAnnouncePeer (std::string_view transactionID, const Torrent::InfoHash& infoHash, uint64_t token)
|
||||
{
|
||||
LogPrint (eLogDebug, "TorrentsDHT: Announce peer received");
|
||||
Torrent::InfoHash hash;
|
||||
if (infoHash.size () < hash.size ())
|
||||
{
|
||||
LogPrint (eLogInfo, "TorrentsDHT: Announced info hash is too short ", infoHash.size ());
|
||||
return;
|
||||
}
|
||||
memcpy (hash.data (), infoHash.data (), hash.size ());
|
||||
auto it = m_Torrents.find (hash);
|
||||
auto it = m_Torrents.find (infoHash);
|
||||
if (it != m_Torrents.end ())
|
||||
{
|
||||
auto node = it->second->GetIncomingGetPeerNode (token);
|
||||
if (node)
|
||||
{
|
||||
it->second->AddPeer (node->peer);
|
||||
SendResponseMsg (CreateDictionary ({
|
||||
{ "id", CreateByteString (std::string_view ((const char *)node->id.data (), node->id.size ())) },
|
||||
}),
|
||||
transactionID, node->peer, node->port + 1); // to rport
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TorrentsDHT::HandleResponse (std::string_view transactionID, std::string_view id,
|
||||
void TorrentsDHT::HandleResponse (std::string_view transactionID, const NodeID& nodeID,
|
||||
uint64_t token, const std::vector<std::string_view>& values)
|
||||
{
|
||||
LogPrint (eLogDebug, "TorrentsDHT: Response msg received");
|
||||
@@ -576,13 +577,6 @@ namespace torrents
|
||||
auto it = m_Queries.find (t);
|
||||
if (t && it != m_Queries.end ())
|
||||
{
|
||||
if (id.size () < NodeID::len)
|
||||
{
|
||||
LogPrint (eLogInfo, "TorrentsDHT: received id is too short ", id.size ());
|
||||
return;
|
||||
}
|
||||
NodeID nodeID;
|
||||
memcpy (nodeID.data (), id.data (), NodeID::len);
|
||||
if (m_RoutingTable)
|
||||
{
|
||||
const auto& [ident, port, query, torrentw] = it->second;
|
||||
|
||||
@@ -159,12 +159,12 @@ namespace torrents
|
||||
void HandleDatagram (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort,
|
||||
const uint8_t * buf, size_t len, const i2p::util::Mapping * options);
|
||||
void HandlePingQuery (const i2p::data::IdentHash& fromIdent, uint16_t fromPort,
|
||||
std::string_view transactionID, std::string_view id);
|
||||
std::string_view transactionID, const NodeID& nodeID);
|
||||
void HandleGetPeersQuery (const i2p::data::IdentHash& fromIdent, uint16_t fromPort,
|
||||
std::string_view transactionID, std::string_view id, std::string_view infoHash);
|
||||
void HandleResponse (std::string_view transactionID, std::string_view id, uint64_t token,
|
||||
std::string_view transactionID, const NodeID& nodeID, const Torrent::InfoHash& infoHash);
|
||||
void HandleResponse (std::string_view transactionID, const NodeID& nodeID, uint64_t token,
|
||||
const std::vector<std::string_view>& values);
|
||||
void HandleAnnouncePeer (std::string_view infoHash, uint64_t token);
|
||||
void HandleAnnouncePeer (std::string_view transactionID, const Torrent::InfoHash& infoHash, uint64_t token);
|
||||
|
||||
void SendDatagram (std::string_view msg, const i2p::data::IdentHash& toIdent, uint16_t toPort);
|
||||
void SendRawDatagram (std::string_view msg, const i2p::data::IdentHash& toIdent, uint16_t toPort);
|
||||
|
||||
Reference in New Issue
Block a user