From 365a888511b386b9e9447ae69e303d1dda7125ed Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Thu, 3 Apr 2025 01:06:04 +0100 Subject: [PATCH] Hash verification works for torrents with a single file --- .gitignore | 2 +- src/main.cpp | 113 ++++++++++++++++++++++++++++++++++----------------- 2 files changed, 76 insertions(+), 39 deletions(-) diff --git a/.gitignore b/.gitignore index 7c2caf9..fd85b3e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ tags -subprojects/libstriezel/ +subprojects/libstriezel compile_commands.json diff --git a/src/main.cpp b/src/main.cpp index bf6addd..9b8cfec 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,9 +10,11 @@ #include #include #include +#include +#include #include #include -#include +#include namespace { std::string load_file (const std::string& path) { @@ -107,8 +109,9 @@ struct PrintVisitor { bool skip_first; }; -struct FindStringVisitor : boost::static_visitor { - FindStringVisitor (std::string_view search_path) { +template +struct FindTVisitor : boost::static_visitor { + FindTVisitor (std::string_view search_path) { auto split = dincore::split(search_path, '/', true, true); search.reserve(split.size()); for (const auto& itm : split) { @@ -116,28 +119,41 @@ struct FindStringVisitor : boost::static_visitor { } } + void reset() { + search_error = false; + search_match_count = 0; + } + bool is_path_found() const { return search.size() == search_match_count; } - std::string_view operator() (long long value) { - return {}; - } - - std::string_view operator() (const duck::TorrentStringType& value) { - if (is_path_found()) + T operator() (long long value) { + if constexpr (std::is_same::value) return value; - else - return {}; - } - - std::string_view operator() (const std::vector&) { + search_error = true; return {}; } - std::string_view operator() (const std::map& value) { - if (is_path_found()) + T operator() (const duck::TorrentStringType& value) { + if constexpr (std::is_same::value) { + if (is_path_found()) + return value; + } + search_error = true; + return {}; + } + + T operator() (const std::vector&) { + search_error = true; + return {}; + } + + T operator() (const std::map& value) { + if (is_path_found()) { + search_error = true; return {}; + } auto it_found = value.find(search[search_match_count]); if (value.end() == it_found) @@ -149,6 +165,7 @@ struct FindStringVisitor : boost::static_visitor { std::vector search; std::size_t search_match_count{0}; + bool search_error{false}; }; std::vector> collect_hashes (std::string_view hashes) { @@ -178,19 +195,28 @@ std::vector> collect_hashes (std::string_view hashe return retval; } -std::string_view find_item(std::string_view path, const std::vector& values) { - FindStringVisitor visitor(path); +template +T find_item(std::string_view path, const std::vector& values) { + FindTVisitor visitor(path); for (const auto& value : values) { - std::string_view found = boost::apply_visitor(visitor, value); - if (not found.empty()) + visitor.reset(); + T found = boost::apply_visitor(visitor, value); + if (not visitor.search_error) return found; } return {}; } + +SHA1::MessageDigest to_hash160_digest (const std::array& arr) { + SHA1::MessageDigest hash_found; + std::copy_n(arr.begin(), 5, hash_found.hash); + return hash_found; +} } //unnamed namespace int main(int argc, const char* argv[]) { using duck::parse_torrent; + typedef duck::TorrentStringType torrent_string; if (argc != 2) { std::cerr << "Wrong number of parameters. Usage:\n" @@ -207,27 +233,38 @@ int main(int argc, const char* argv[]) { boost::apply_visitor(visitor, value); } - { - auto source_hashes = find_item("/info/pieces", values); - std::cout << "Got source_hashes with size " << source_hashes.size() << '\n'; - auto hashes = collect_hashes(source_hashes); - std::cout << "Got " << hashes.size() << " hashes\n"; + auto source_hashes = find_item("/info/pieces", values); + std::cout << "Got source_hashes with size " << source_hashes.size() << '\n'; + auto hashes = collect_hashes(source_hashes); + std::cout << "Got " << hashes.size() << " hashes\n"; - if (not hashes.empty()) { - SHA1::MessageDigest hash_found; - std::copy_n(hashes.front().begin(), 5, hash_found.hash); - std::cout << hash_found.toHexString() << '\n'; - } - } - - std::string_view file_name = find_item("/info/name", values); + std::string file_name = std::string{find_item("/info/name", values)}; std::cout << "Found file name \"" << file_name << "\"\n"; { - SHA1::MessageDigest hash160; - SHA1::FileSource file_source; - file_source.open(std::string{file_name}); - hash160 = SHA1::computeFromSource(file_source); - std::cout << hash160.toHexString() << " " << file_name << '\n'; + const auto piece_length = find_item("/info/piece length", values); + std::ifstream istream(file_name, std::ios::in|std::ios::binary); + + if (istream.is_open()) { + std::vector buff(piece_length); + std::size_t hash_index = 0; + std::size_t match_count = 0; + + while (istream.read(reinterpret_cast(buff.data()), piece_length).gcount() > 0) { + SHA1::BufferSource buff_source(buff.data(), istream.gcount() * CHAR_BIT); + SHA1::MessageDigest calculated_hash = SHA1::computeFromSource(buff_source); + SHA1::MessageDigest stored_hash = to_hash160_digest(hashes[hash_index]); + std::cout << stored_hash.toHexString() << " " << + calculated_hash.toHexString() << " " << file_name << '\n'; + ++hash_index; + if (stored_hash == calculated_hash) + ++match_count; + } + + std::cout << "Hash verified " << match_count << '/' << hashes.size() << '\n'; + } + else { + std::cout << "Error: opening \"" << file_name << "\" failed\n"; + } } }