diff --git a/libi2pd_client/Torrents.cpp b/libi2pd_client/Torrents.cpp index bcc7c9b4..d529e023 100644 --- a/libi2pd_client/Torrents.cpp +++ b/libi2pd_client/Torrents.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include "Log.h" #include "FS.h" #include "ClientContext.h" @@ -43,14 +42,9 @@ namespace torrents bool Piece::VerifyHash () const { if (!m_Data) return false; - EVP_MD_CTX * mdctx = EVP_MD_CTX_new (); - EVP_DigestInit_ex (mdctx, EVP_sha1 (), nullptr); - EVP_DigestUpdate (mdctx, m_Data, m_Size); uint8_t digest[SHA_DIGEST_LENGTH]; - unsigned int l = SHA_DIGEST_LENGTH; - EVP_DigestFinal_ex (mdctx, digest, &l); - EVP_MD_CTX_free (mdctx); - return l == SHA_DIGEST_LENGTH && !memcmp (m_Hash, digest, SHA_DIGEST_LENGTH); + SHA1 (m_Data, m_Size, digest); + return !memcmp (m_Hash, digest, SHA_DIGEST_LENGTH); } bool Piece::IsAvailable (int block) const @@ -81,6 +75,18 @@ namespace torrents { size_t l = ParseInfo (buf); if (!l) break; + // calculate info hash + uint8_t digest[SHA_DIGEST_LENGTH]; + SHA1 ((const uint8_t *)buf.data (), l, digest); + // convert info hash to hex chars + BIGNUM * bn = BN_bin2bn (digest, SHA_DIGEST_LENGTH, nullptr); + char * str = BN_bn2hex (bn); + if (str) + { + m_InfoHash = str; + OPENSSL_free (str); + } + BN_free (bn); buf = buf.substr (l); } else @@ -232,6 +238,14 @@ namespace torrents return ret; } + i2p::http::HTTPReq Torrent::GetTrackerRequest () const + { + i2p::http::HTTPReq req; + req.parse (m_Announce); + req.AddHeader ("info_hash", m_InfoHash); + return req; + } + Peer::Peer (i2p::client::I2PService * owner, std::string_view address): i2p::client::I2PServiceHandler (owner), m_Address (i2p::client::context.GetAddressBook().GetAddress (address)) @@ -295,5 +309,84 @@ namespace torrents else LogPrint (eLogError, "Torrents: Can't open file ", path); } + + void TorrentsTunnel::RequestTracker (std::shared_ptr torrent) + { + if (!torrent) return; + i2p::http::HTTPReq req = torrent->GetTrackerRequest (); + i2p::http::URL reqURL; + reqURL.parse (req.uri); +#if __cplusplus >= 202002L // C++20 + if (!reqURL.host.ends_with (".i2p")) +#else + if (reqURL.host.find(".i2p") == name.npos) +#endif + { + LogPrint (eLogWarning, "Torrents: Non-I2P address ", reqURL.host, " for torrent ", torrent->GetName ()); + return; + } + req.AddHeader ("peer_id", m_PeerID); + req.AddHeader ("ip", GetLocalDestination ()->GetIdentHash ().ToBase32 () + ".b32.i2p"); + req.AddHeader ("port", std::to_string (6881)); + req.AddHeader ("uploaded", "0"); // TODO + req.AddHeader ("downloaded", "0"); // TODO + req.AddHeader ("left", "1"); // TODO + req.AddHeader ("compact", "1"); // TODO + req.AddHeader ("numwant", "0"); // TODO + CreateStream ([req, this](std::shared_ptr stream) + { + if (stream) + { + auto reqStr = req.to_string (); + stream->Send ((const uint8_t *)reqStr.data (), reqStr.length ()); + ReceiveFromTracker (stream, std::make_shared(), 0); + } + }, reqURL.host, reqURL.port); + } + + void TorrentsTunnel::ReceiveFromTracker (std::shared_ptr stream, + std::shared_ptr buf, size_t offset) + { + if (!stream || !buf) return; + if (stream->GetStatus () == i2p::stream::eStreamStatusNew || + stream->GetStatus () == i2p::stream::eStreamStatusOpen) // regular + { + stream->AsyncReceive (boost::asio::buffer (buf->data() + offset, TRACKER_RESPONSE_BUFFER_SIZE - offset), + [this, stream, buf, offset](const boost::system::error_code& ecode, size_t bytes_transferred) + { + if (ecode) + { + if (ecode != boost::asio::error::operation_aborted && bytes_transferred > 0) + HandleTrackerResponse (buf, offset + bytes_transferred); + stream->Close (); + } + else + { + size_t offset1 = offset + bytes_transferred; + if (stream->IsOpen () && offset1 < TRACKER_RESPONSE_BUFFER_SIZE) + ReceiveFromTracker (stream, buf, offset1); + else + HandleTrackerResponse (buf, offset1); + } + }, TRACKER_RESPONSE_TIMEOUT); + } + else // closed by peer + { + // get remaining data + auto len = stream->ReadSome (buf->data() + offset, TRACKER_RESPONSE_BUFFER_SIZE - offset); + if (len > 0) // still some data + { + offset += len; + HandleTrackerResponse (buf, offset); + } + else // no more data + stream->Close (); + } + } + + void TorrentsTunnel::HandleTrackerResponse (std::shared_ptr buf, size_t len) + { + // TODO + } } } diff --git a/libi2pd_client/Torrents.h b/libi2pd_client/Torrents.h index 1d5807c5..e86e9ea9 100644 --- a/libi2pd_client/Torrents.h +++ b/libi2pd_client/Torrents.h @@ -14,9 +14,12 @@ #include #include #include +#include #include #include #include +#include "Streaming.h" +#include "HTTP.h" #include "I2PService.h" #include "AddressBook.h" @@ -25,6 +28,8 @@ namespace i2p namespace torrents { constexpr size_t REQUEST_BLOCK_SIZE = 16384; + constexpr int TRACKER_RESPONSE_TIMEOUT = 8; // in seconds + constexpr size_t TRACKER_RESPONSE_BUFFER_SIZE = 65535; class Piece final { @@ -49,6 +54,8 @@ namespace torrents public: Torrent (std::string_view buf); + i2p::http::HTTPReq GetTrackerRequest () const; + const std::string& GetName () const { return m_Name; } private: @@ -60,7 +67,7 @@ namespace torrents private: - std::string m_Name, m_Announce; + std::string m_Name, m_Announce, m_InfoHash; // 40 hex chars size_t m_Length, m_PieceLength; std::vector m_Pieces; }; @@ -78,6 +85,8 @@ namespace torrents class TorrentsTunnel: public i2p::client::I2PService { + using TrackerResponseBuffer = std::array; + public: TorrentsTunnel (std::shared_ptr localDestination, std::string_view torrentsDir); @@ -90,6 +99,9 @@ namespace torrents private: void ReadTorrentFile (const std::string& path); + void RequestTracker (std::shared_ptr torrent); + void ReceiveFromTracker (std::shared_ptr stream, std::shared_ptr buf, size_t offset); + void HandleTrackerResponse (std::shared_ptr buf, size_t len); private: