1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2025-03-13 07:33:02 +00:00

refactor: use the new benc parser in announcer-http (#2529)

This commit is contained in:
Charles Kerr 2022-01-28 12:39:45 -06:00 committed by GitHub
parent b998032305
commit 7f60738cce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 195 additions and 186 deletions

View file

@ -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<char, INET6_ADDRSTRLEN>{};
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<tr_pex>(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<MaxBencDepth>
{
return;
}
using BasicHandler = transmission::benc::BasicHandler<MaxBencDepth>;
if (tr_variantIsDict(&benc))
{
auto i = int64_t{};
auto sv = std::string_view{};
tr_variant* tmp = nullptr;
tr_announce_response& response_;
std::optional<size_t> 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<MaxBencDepth>{};
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<MaxBencDepth>
{
return;
}
using BasicHandler = transmission::benc::BasicHandler<MaxBencDepth>;
if (auto sv = std::string_view{}; tr_variantDictFindStrView(&top, TR_KEY_failure_reason, &sv))
{
response.errmsg = sv;
}
tr_scrape_response& response_;
std::optional<size_t> 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::byte const*>(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<MaxBencDepth>{};
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(

View file

@ -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<std::string_view, MaxDepth> 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<std::string_view, MaxDepth> keys_;
};
template<std::size_t MaxDepth>
@ -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;
}

View file

@ -18,7 +18,7 @@ using namespace std::literals;
namespace
{
auto constexpr my_static = std::array<std::string_view, 390>{ ""sv,
auto constexpr my_static = std::array<std::string_view, 383>{ ""sv,
"activeTorrentCount"sv,
"activity-date"sv,
"activityDate"sv,
@ -108,7 +108,6 @@ auto constexpr my_static = std::array<std::string_view, 390>{ ""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<std::string_view, 390>{ ""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<std::string_view, 390>{ ""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<std::string_view, 390>{ ""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<std::string_view, 390>{ ""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<std::string_view, 390>{ ""sv,
"v"sv,
"version"sv,
"wanted"sv,
"warning message"sv,
"watch-dir"sv,
"watch-dir-enabled"sv,
"webseeds"sv,

View file

@ -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,