From 7f60738ccef0a3fa92b850ccf5b36705e08ec07d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 28 Jan 2022 12:39:45 -0600 Subject: [PATCH] refactor: use the new benc parser in announcer-http (#2529) --- libtransmission/announcer-http.cc | 323 +++++++++++++++--------------- libtransmission/benc.h | 42 +++- libtransmission/quark.cc | 9 +- libtransmission/quark.h | 7 - 4 files changed, 195 insertions(+), 186 deletions(-) diff --git a/libtransmission/announcer-http.cc b/libtransmission/announcer-http.cc index 9fe46dbb7..682a06599 100644 --- a/libtransmission/announcer-http.cc +++ b/libtransmission/announcer-http.cc @@ -19,6 +19,7 @@ #include "transmission.h" #include "announcer-common.h" +#include "benc.h" #include "crypto-utils.h" #include "error.h" #include "log.h" @@ -28,7 +29,6 @@ #include "torrent.h" #include "trevent.h" /* tr_runInEventThread() */ #include "utils.h" -#include "variant.h" #include "web-utils.h" #include "web.h" @@ -113,67 +113,15 @@ static std::string announce_url_new(tr_session const* session, tr_announce_reque unsigned char const* const ipv6 = tr_globalIPv6(session); if (ipv6 != nullptr) { - char ipv6_readable[INET6_ADDRSTRLEN]; - evutil_inet_ntop(AF_INET6, ipv6, ipv6_readable, INET6_ADDRSTRLEN); + auto ipv6_readable = std::array{}; + evutil_inet_ntop(AF_INET6, ipv6, std::data(ipv6_readable), std::size(ipv6_readable)); evbuffer_add_printf(buf, "&ipv6="); - tr_http_escape(buf, ipv6_readable, true); + tr_http_escape(buf, std::data(ipv6_readable), true); } return evbuffer_free_to_str(buf); } -static auto listToPex(tr_variant* peerList) -{ - size_t n = 0; - size_t const len = tr_variantListSize(peerList); - auto pex = std::vector(len); - - for (size_t i = 0; i < len; ++i) - { - tr_variant* const peer = tr_variantListChild(peerList, i); - - if (peer == nullptr) - { - continue; - } - - auto ip = std::string_view{}; - if (!tr_variantDictFindStrView(peer, TR_KEY_ip, &ip)) - { - continue; - } - - auto addr = tr_address{}; - if (!tr_address_from_string(&addr, ip)) - { - continue; - } - - auto port = int64_t{}; - if (!tr_variantDictFindInt(peer, TR_KEY_port, &port)) - { - continue; - } - - if (port < 0 || port > USHRT_MAX) - { - continue; - } - - if (!tr_address_is_valid_for_peers(&addr, port)) - { - continue; - } - - pex[n].addr = addr; - pex[n].port = htons((uint16_t)port); - ++n; - } - - pex.resize(n); - return pex; -} - struct announce_data { tr_announce_response response; @@ -220,79 +168,115 @@ static void verboseLog(std::string_view description, tr_direction direction, std out << std::endl << "[b64]"sv << direction_sv << tr_base64_encode(message) << std::endl; } -void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::string_view msg) +static auto constexpr MaxBencDepth = 8; + +void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::string_view benc) { - verboseLog("Announce response:", TR_DOWN, msg); + verboseLog("Announce response:", TR_DOWN, benc); - auto benc = tr_variant{}; - auto const variant_loaded = tr_variantFromBuf(&benc, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, msg); - if (!variant_loaded) + struct AnnounceHandler final : public transmission::benc::BasicHandler { - return; - } + using BasicHandler = transmission::benc::BasicHandler; - if (tr_variantIsDict(&benc)) - { - auto i = int64_t{}; - auto sv = std::string_view{}; - tr_variant* tmp = nullptr; + tr_announce_response& response_; + std::optional row_; + tr_pex pex_ = {}; - if (tr_variantDictFindStrView(&benc, TR_KEY_failure_reason, &sv)) + explicit AnnounceHandler(tr_announce_response& response) + : response_{ response } { - response.errmsg = sv; } - if (tr_variantDictFindStrView(&benc, TR_KEY_warning_message, &sv)) + bool StartDict() override { - response.warning = sv; + BasicHandler::StartDict(); + + pex_ = {}; + + return true; } - if (tr_variantDictFindInt(&benc, TR_KEY_interval, &i)) + bool EndDict() override { - response.interval = i; + BasicHandler::EndDict(); + + if (tr_address_is_valid_for_peers(&pex_.addr, pex_.port)) + { + response_.pex.push_back(pex_); + pex_ = {}; + } + + return true; } - if (tr_variantDictFindInt(&benc, TR_KEY_min_interval, &i)) + bool Int64(int64_t value) override { - response.min_interval = i; + auto const key = currentKey(); + + if (key == "interval") + { + response_.interval = value; + } + else if (key == "min interval"sv) + { + response_.min_interval = value; + } + else if (key == "complete"sv) + { + response_.seeders = value; + } + else if (key == "incomplete"sv) + { + response_.leechers = value; + } + else if (key == "downloaded"sv) + { + response_.downloads = value; + } + else if (key == "port"sv) + { + pex_.port = htons(uint16_t(value)); + } + + return true; } - if (tr_variantDictFindStrView(&benc, TR_KEY_tracker_id, &sv)) + bool String(std::string_view value) override { - response.tracker_id = sv; - } + auto const key = currentKey(); - if (tr_variantDictFindInt(&benc, TR_KEY_complete, &i)) - { - response.seeders = i; - } + if (key == "failure reason"sv) + { + response_.errmsg = value; + } + else if (key == "warning message"sv) + { + response_.warning = value; + } + else if (key == "tracker id"sv) + { + response_.tracker_id = value; + } + else if (key == "peers"sv) + { + response_.pex = tr_peerMgrCompactToPex(std::data(value), std::size(value), nullptr, 0); + } + else if (key == "peers6"sv) + { + response_.pex6 = tr_peerMgrCompact6ToPex(std::data(value), std::size(value), nullptr, 0); + } + else if (key == "ip") + { + tr_address_from_string(&pex_.addr, value); + } - if (tr_variantDictFindInt(&benc, TR_KEY_incomplete, &i)) - { - response.leechers = i; + return true; } + }; - if (tr_variantDictFindInt(&benc, TR_KEY_downloaded, &i)) - { - response.downloads = i; - } - - if (tr_variantDictFindStrView(&benc, TR_KEY_peers6, &sv)) - { - response.pex6 = tr_peerMgrCompact6ToPex(std::data(sv), std::size(sv), nullptr, 0); - } - - if (tr_variantDictFindStrView(&benc, TR_KEY_peers, &sv)) - { - response.pex = tr_peerMgrCompactToPex(std::data(sv), std::size(sv), nullptr, 0); - } - else if (tr_variantDictFindList(&benc, TR_KEY_peers, &tmp)) - { - response.pex = listToPex(tmp); - } - } - - tr_variantFree(&benc); + auto stack = transmission::benc::ParserStack{}; + auto handler = AnnounceHandler{ response }; + transmission::benc::parse(benc, stack, handler); } static void on_announce_done( @@ -376,75 +360,86 @@ static void on_scrape_done_eventthread(void* vdata) delete data; } -void tr_announcerParseHttpScrapeResponse(tr_scrape_response& response, std::string_view msg) +void tr_announcerParseHttpScrapeResponse(tr_scrape_response& response, std::string_view benc) { - verboseLog("Scrape response:", TR_DOWN, msg); + verboseLog("Scrape response:", TR_DOWN, benc); - auto top = tr_variant{}; - auto const variant_loaded = tr_variantFromBuf(&top, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, msg); - if (!variant_loaded) + struct ScrapeHandler final : public transmission::benc::BasicHandler { - return; - } + using BasicHandler = transmission::benc::BasicHandler; - if (auto sv = std::string_view{}; tr_variantDictFindStrView(&top, TR_KEY_failure_reason, &sv)) - { - response.errmsg = sv; - } + tr_scrape_response& response_; + std::optional row_; - tr_variant* flags = nullptr; - auto intVal = int64_t{}; - if (tr_variantDictFindDict(&top, TR_KEY_flags, &flags) && - tr_variantDictFindInt(flags, TR_KEY_min_request_interval, &intVal)) - { - response.min_request_interval = intVal; - } - - tr_variant* files = nullptr; - if (tr_variantDictFindDict(&top, TR_KEY_files, &files)) - { - auto key = tr_quark{}; - tr_variant* val = nullptr; - - for (int i = 0; tr_variantDictChild(files, i, &key, &val); ++i) + explicit ScrapeHandler(tr_scrape_response& response) + : response_{ response } { - /* populate the corresponding row in our response array */ - for (int j = 0; j < response.row_count; ++j) + } + + bool Key(std::string_view value) override + { + BasicHandler::Key(value); + + auto needle = tr_sha1_digest_t{}; + if (depth() == 2 && key(1) == "files"sv && std::size(value) == std::size(needle)) { - struct tr_scrape_response_row* row = &response.rows[j]; + std::copy_n(reinterpret_cast(std::data(value)), std::size(value), std::data(needle)); + auto const it = std::find_if( + std::begin(response_.rows), + std::end(response_.rows), + [needle](auto const& row) { return row.info_hash == needle; }); - // TODO(ckerr): ugh, interning info dict hashes is awful - auto const& hash = row->info_hash; - auto const key_sv = tr_quark_get_string_view(key); - if (std::size(hash) == std::size(key_sv) && memcmp(std::data(hash), std::data(key_sv), std::size(hash)) == 0) + if (it == std::end(response_.rows)) { - if (tr_variantDictFindInt(val, TR_KEY_complete, &intVal)) - { - row->seeders = intVal; - } - - if (tr_variantDictFindInt(val, TR_KEY_incomplete, &intVal)) - { - row->leechers = intVal; - } - - if (tr_variantDictFindInt(val, TR_KEY_downloaded, &intVal)) - { - row->downloads = intVal; - } - - if (tr_variantDictFindInt(val, TR_KEY_downloaders, &intVal)) - { - row->downloaders = intVal; - } - - break; + row_.reset(); + } + else + { + row_ = std::distance(std::begin(response_.rows), it); } } - } - } - tr_variantFree(&top); + return true; + } + + bool Int64(int64_t value) override + { + if (row_ && currentKey() == "complete"sv) + { + response_.rows[*row_].seeders = value; + } + else if (row_ && currentKey() == "downloaded"sv) + { + response_.rows[*row_].downloads = value; + } + else if (row_ && currentKey() == "incomplete"sv) + { + response_.rows[*row_].leechers = value; + } + + return true; + } + + bool String(std::string_view value) override + { + if (depth() == 1 && currentKey() == "failure reason"sv) + { + response_.errmsg = value; + } + + return true; + } + }; + + auto stack = transmission::benc::ParserStack{}; + auto handler = ScrapeHandler{ response }; + tr_error* error = nullptr; + transmission::benc::parse(benc, stack, handler, nullptr, &error); + if (error != nullptr) + { + std::cerr << error->message << std::endl; + tr_error_clear(&error); + } } static void on_scrape_done( diff --git a/libtransmission/benc.h b/libtransmission/benc.h index e0f8c9946..0d39befb4 100644 --- a/libtransmission/benc.h +++ b/libtransmission/benc.h @@ -59,35 +59,63 @@ struct BasicHandler : public Handler bool StartDict() override { - keys.emplace_back(); + push(); return true; } bool Key(std::string_view key) override { - keys.back() = key; + keys_[depth_] = key; return true; } bool EndDict() override { - keys.resize(keys.size() - 1); + pop(); return true; } bool StartArray() override { - keys.emplace_back(); + push(); return true; } bool EndArray() override { - keys.resize(keys.size() - 1); + pop(); return true; } - std::array keys; + auto key(size_t i) const + { + return keys_[i]; + } + + auto depth() const + { + return depth_; + } + + auto currentKey() const + { + return key(depth()); + } + +private: + void push() + { + ++depth_; + keys_[depth_] = {}; + } + + void pop() + { + --depth_; + } + + size_t depth_ = 0; + std::array keys_; }; template @@ -125,7 +153,7 @@ struct ParserStack return stack[depth]; } - bool expectingDictKey() const + [[nodiscard]] bool expectingDictKey() const { return depth > 0 && stack[depth].parent_type == ParentType::Dict && (stack[depth].n_children_walked % 2) == 0; } diff --git a/libtransmission/quark.cc b/libtransmission/quark.cc index 2548f200a..674ea0eef 100644 --- a/libtransmission/quark.cc +++ b/libtransmission/quark.cc @@ -18,7 +18,7 @@ using namespace std::literals; namespace { -auto constexpr my_static = std::array{ ""sv, +auto constexpr my_static = std::array{ ""sv, "activeTorrentCount"sv, "activity-date"sv, "activityDate"sv, @@ -108,7 +108,6 @@ auto constexpr my_static = std::array{ ""sv, "errorString"sv, "eta"sv, "etaIdle"sv, - "failure reason"sv, "fields"sv, "file-count"sv, "fileStats"sv, @@ -150,8 +149,6 @@ auto constexpr my_static = std::array{ ""sv, "incomplete-dir-enabled"sv, "info"sv, "inhibit-desktop-hibernation"sv, - "interval"sv, - "ip"sv, "ipv4"sv, "ipv6"sv, "isBackup"sv, @@ -198,7 +195,6 @@ auto constexpr my_static = std::array{ ""sv, "metadata_size"sv, "metainfo"sv, "method"sv, - "min interval"sv, "min_request_interval"sv, "move"sv, "msg_type"sv, @@ -230,7 +226,6 @@ auto constexpr my_static = std::array{ ""sv, "peers"sv, "peers2"sv, "peers2-6"sv, - "peers6"sv, "peersConnected"sv, "peersFrom"sv, "peersGettingFromUs"sv, @@ -370,7 +365,6 @@ auto constexpr my_static = std::array{ ""sv, "torrents"sv, "totalSize"sv, "total_size"sv, - "tracker id"sv, "trackerAdd"sv, "trackerRemove"sv, "trackerReplace"sv, @@ -403,7 +397,6 @@ auto constexpr my_static = std::array{ ""sv, "v"sv, "version"sv, "wanted"sv, - "warning message"sv, "watch-dir"sv, "watch-dir-enabled"sv, "webseeds"sv, diff --git a/libtransmission/quark.h b/libtransmission/quark.h index 723db6f0a..4b2ec3e46 100644 --- a/libtransmission/quark.h +++ b/libtransmission/quark.h @@ -111,7 +111,6 @@ enum TR_KEY_errorString, TR_KEY_eta, TR_KEY_etaIdle, - TR_KEY_failure_reason, TR_KEY_fields, TR_KEY_file_count, TR_KEY_fileStats, @@ -153,8 +152,6 @@ enum TR_KEY_incomplete_dir_enabled, TR_KEY_info, TR_KEY_inhibit_desktop_hibernation, - TR_KEY_interval, - TR_KEY_ip, TR_KEY_ipv4, TR_KEY_ipv6, TR_KEY_isBackup, @@ -201,7 +198,6 @@ enum TR_KEY_metadata_size, TR_KEY_metainfo, TR_KEY_method, - TR_KEY_min_interval, TR_KEY_min_request_interval, TR_KEY_move, TR_KEY_msg_type, @@ -233,7 +229,6 @@ enum TR_KEY_peers, TR_KEY_peers2, TR_KEY_peers2_6, - TR_KEY_peers6, TR_KEY_peersConnected, TR_KEY_peersFrom, TR_KEY_peersGettingFromUs, @@ -373,7 +368,6 @@ enum TR_KEY_torrents, TR_KEY_totalSize, TR_KEY_total_size, - TR_KEY_tracker_id, TR_KEY_trackerAdd, TR_KEY_trackerRemove, TR_KEY_trackerReplace, @@ -406,7 +400,6 @@ enum TR_KEY_v, TR_KEY_version, TR_KEY_wanted, - TR_KEY_warning_message, TR_KEY_watch_dir, TR_KEY_watch_dir_enabled, TR_KEY_webseeds,