mirror of
https://github.com/transmission/transmission
synced 2025-02-21 13:46:52 +00:00
refactor: misc-use-anonymous-namespace pt. 4 (#4550)
This commit is contained in:
parent
9678b26984
commit
cb10255ef1
12 changed files with 1495 additions and 1455 deletions
|
@ -41,16 +41,9 @@
|
||||||
|
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
|
|
||||||
/****
|
namespace
|
||||||
***** ANNOUNCE
|
|
||||||
****/
|
|
||||||
|
|
||||||
[[nodiscard]] static constexpr std::string_view get_event_string(tr_announce_request const& req)
|
|
||||||
{
|
{
|
||||||
return req.partial_seed && (req.event != TR_ANNOUNCE_EVENT_STOPPED) ? "paused"sv : tr_announce_event_get_string(req.event);
|
void verboseLog(std::string_view description, tr_direction direction, std::string_view message)
|
||||||
}
|
|
||||||
|
|
||||||
static void verboseLog(std::string_view description, tr_direction direction, std::string_view message)
|
|
||||||
{
|
{
|
||||||
auto& out = std::cerr;
|
auto& out = std::cerr;
|
||||||
static bool const verbose = tr_env_key_exists("TR_CURL_VERBOSE");
|
static bool const verbose = tr_env_key_exists("TR_CURL_VERBOSE");
|
||||||
|
@ -76,7 +69,270 @@ static void verboseLog(std::string_view description, tr_direction direction, std
|
||||||
out << std::endl << "[b64]"sv << direction_sv << tr_base64_encode(message) << std::endl;
|
out << std::endl << "[b64]"sv << direction_sv << tr_base64_encode(message) << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto constexpr MaxBencDepth = 8;
|
auto constexpr MaxBencDepth = 8;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
/****
|
||||||
|
***** ANNOUNCE
|
||||||
|
****/
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
namespace announce_helpers
|
||||||
|
{
|
||||||
|
[[nodiscard]] constexpr std::string_view get_event_string(tr_announce_request const& req)
|
||||||
|
{
|
||||||
|
return req.partial_seed && (req.event != TR_ANNOUNCE_EVENT_STOPPED) ? "paused"sv : tr_announce_event_get_string(req.event);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct http_announce_data
|
||||||
|
{
|
||||||
|
http_announce_data(tr_sha1_digest_t info_hash_in, tr_announce_response_func on_response_in, std::string_view log_name_in)
|
||||||
|
: info_hash{ info_hash_in }
|
||||||
|
, on_response{ std::move(on_response_in) }
|
||||||
|
, log_name{ log_name_in }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
tr_sha1_digest_t info_hash = {};
|
||||||
|
std::optional<tr_announce_response> previous_response;
|
||||||
|
|
||||||
|
tr_announce_response_func on_response;
|
||||||
|
bool http_success = false;
|
||||||
|
|
||||||
|
uint8_t requests_sent_count = {};
|
||||||
|
uint8_t requests_answered_count = {};
|
||||||
|
|
||||||
|
std::string log_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool handleAnnounceResponse(tr_web::FetchResponse const& web_response, tr_announce_response* const response)
|
||||||
|
{
|
||||||
|
auto const& [status, body, did_connect, did_timeout, vdata] = web_response;
|
||||||
|
auto const& log_name = static_cast<http_announce_data const*>(vdata)->log_name;
|
||||||
|
|
||||||
|
response->did_connect = did_connect;
|
||||||
|
response->did_timeout = did_timeout;
|
||||||
|
tr_logAddTrace("Got announce response", log_name);
|
||||||
|
|
||||||
|
if (status != HTTP_OK)
|
||||||
|
{
|
||||||
|
auto const* const response_str = tr_webGetResponseStr(status);
|
||||||
|
response->errmsg = fmt::format(FMT_STRING("Tracker HTTP response {:d} ({:s}"), status, response_str);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr_announcerParseHttpAnnounceResponse(*response, body, log_name);
|
||||||
|
|
||||||
|
if (!std::empty(response->pex6))
|
||||||
|
{
|
||||||
|
tr_logAddTrace(fmt::format("got a peers6 length of {}", std::size(response->pex6)), log_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!std::empty(response->pex))
|
||||||
|
{
|
||||||
|
tr_logAddTrace(fmt::format("got a peers length of {}", std::size(response->pex)), log_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void onAnnounceDone(tr_web::FetchResponse const& web_response)
|
||||||
|
{
|
||||||
|
auto const& [status, body, did_connect, did_timeout, vdata] = web_response;
|
||||||
|
auto* data = static_cast<struct http_announce_data*>(vdata);
|
||||||
|
|
||||||
|
++data->requests_answered_count;
|
||||||
|
|
||||||
|
// If another request already succeeded (or we don't have a registered callback),
|
||||||
|
// skip processing this response:
|
||||||
|
if (!data->http_success && data->on_response)
|
||||||
|
{
|
||||||
|
tr_announce_response response;
|
||||||
|
response.info_hash = data->info_hash;
|
||||||
|
|
||||||
|
data->http_success = handleAnnounceResponse(web_response, &response);
|
||||||
|
|
||||||
|
if (data->http_success)
|
||||||
|
{
|
||||||
|
data->on_response(response);
|
||||||
|
}
|
||||||
|
else if (data->requests_answered_count == data->requests_sent_count)
|
||||||
|
{
|
||||||
|
auto const* response_used = &response;
|
||||||
|
|
||||||
|
// All requests have been answered, but none were successful.
|
||||||
|
// Choose the one that went further to report.
|
||||||
|
if (data->previous_response && !response.did_connect && !response.did_timeout)
|
||||||
|
{
|
||||||
|
response_used = &*data->previous_response;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->on_response(*response_used);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// There is still one request pending that might succeed, so store
|
||||||
|
// the response for later. There is only room for 1 previous response,
|
||||||
|
// because there can be at most 2 requests.
|
||||||
|
TR_ASSERT(!data->previous_response);
|
||||||
|
data->previous_response = std::move(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tr_logAddTrace("Ignoring redundant announce response", data->log_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free data if no more responses are expected:
|
||||||
|
if (data->requests_answered_count == data->requests_sent_count)
|
||||||
|
{
|
||||||
|
delete data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void announce_url_new(tr_urlbuf& url, tr_session const* session, tr_announce_request const& req)
|
||||||
|
{
|
||||||
|
url.clear();
|
||||||
|
auto out = std::back_inserter(url);
|
||||||
|
|
||||||
|
auto escaped_info_hash = tr_urlbuf{};
|
||||||
|
tr_urlPercentEncode(std::back_inserter(escaped_info_hash), req.info_hash);
|
||||||
|
|
||||||
|
fmt::format_to(
|
||||||
|
out,
|
||||||
|
"{url}"
|
||||||
|
"{sep}info_hash={info_hash}"
|
||||||
|
"&peer_id={peer_id}"
|
||||||
|
"&port={port}"
|
||||||
|
"&uploaded={uploaded}"
|
||||||
|
"&downloaded={downloaded}"
|
||||||
|
"&left={left}"
|
||||||
|
"&numwant={numwant}"
|
||||||
|
"&key={key}"
|
||||||
|
"&compact=1"
|
||||||
|
"&supportcrypto=1",
|
||||||
|
fmt::arg("url", req.announce_url),
|
||||||
|
fmt::arg("sep", tr_strvContains(req.announce_url.sv(), '?') ? '&' : '?'),
|
||||||
|
fmt::arg("info_hash", std::data(escaped_info_hash)),
|
||||||
|
fmt::arg("peer_id", std::string_view{ std::data(req.peer_id), std::size(req.peer_id) }),
|
||||||
|
fmt::arg("port", req.port.host()),
|
||||||
|
fmt::arg("uploaded", req.up),
|
||||||
|
fmt::arg("downloaded", req.down),
|
||||||
|
fmt::arg("left", req.leftUntilComplete),
|
||||||
|
fmt::arg("numwant", req.numwant),
|
||||||
|
fmt::arg("key", req.key));
|
||||||
|
|
||||||
|
if (session->encryptionMode() == TR_ENCRYPTION_REQUIRED)
|
||||||
|
{
|
||||||
|
fmt::format_to(out, "&requirecrypto=1");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.corrupt != 0)
|
||||||
|
{
|
||||||
|
fmt::format_to(out, "&corrupt={}", req.corrupt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto const str = get_event_string(req); !std::empty(str))
|
||||||
|
{
|
||||||
|
fmt::format_to(out, "&event={}", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!std::empty(req.tracker_id))
|
||||||
|
{
|
||||||
|
fmt::format_to(out, "&trackerid={}", req.tracker_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::string format_ip_arg(std::string_view ip)
|
||||||
|
{
|
||||||
|
return fmt::format("&ip={:s}", ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace announce_helpers
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void tr_tracker_http_announce(
|
||||||
|
tr_session const* session,
|
||||||
|
tr_announce_request const& request,
|
||||||
|
tr_announce_response_func on_response)
|
||||||
|
{
|
||||||
|
using namespace announce_helpers;
|
||||||
|
|
||||||
|
auto* const d = new http_announce_data{ request.info_hash, std::move(on_response), request.log_name };
|
||||||
|
|
||||||
|
/* There are two alternative techniques for announcing both IPv4 and
|
||||||
|
IPv6 addresses. Previous version of BEP-7 suggests adding "ipv4="
|
||||||
|
and "ipv6=" parameters to the announce URL, while OpenTracker and
|
||||||
|
newer version of BEP-7 requires that peers announce once per each
|
||||||
|
public address they want to use.
|
||||||
|
|
||||||
|
We should ensure that we send the announce both via IPv6 and IPv4,
|
||||||
|
and to be safe we also add the "ipv6=" and "ipv4=" parameters, if
|
||||||
|
we already have them. Our global IPv6 address is computed for the
|
||||||
|
LTEP handshake, so this comes for free. Our public IPv4 address
|
||||||
|
may have been returned from a previous announce and stored in the
|
||||||
|
session.
|
||||||
|
*/
|
||||||
|
auto url = tr_urlbuf{};
|
||||||
|
announce_url_new(url, session, request);
|
||||||
|
auto options = tr_web::FetchOptions{ url.sv(), onAnnounceDone, d };
|
||||||
|
options.timeout_secs = TR_ANNOUNCE_TIMEOUT_SEC;
|
||||||
|
options.sndbuf = 4096;
|
||||||
|
options.rcvbuf = 4096;
|
||||||
|
|
||||||
|
auto do_make_request = [&](std::string_view const& protocol_name, tr_web::FetchOptions&& opt)
|
||||||
|
{
|
||||||
|
tr_logAddTrace(fmt::format("Sending {} announce to libcurl: '{}'", protocol_name, opt.url), request.log_name);
|
||||||
|
session->fetch(std::move(opt));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto const [ipv6, ipv6_is_any] = session->publicAddress(TR_AF_INET6);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Before Curl 7.77.0, if we explicitly choose the IP version we want
|
||||||
|
* to use, it is still possible that the wrong one is used. The workaround
|
||||||
|
* is expensive (disabling DNS cache), so instead we have to make do with
|
||||||
|
* a request that we don't know if will go through IPv6 or IPv4.
|
||||||
|
*/
|
||||||
|
static bool const use_curl_workaround = curl_version_info(CURLVERSION_NOW)->version_num < CURL_VERSION_BITS(7, 77, 0);
|
||||||
|
if (use_curl_workaround)
|
||||||
|
{
|
||||||
|
if (session->useAnnounceIP())
|
||||||
|
{
|
||||||
|
options.url += format_ip_arg(session->announceIP());
|
||||||
|
}
|
||||||
|
|
||||||
|
d->requests_sent_count = 1;
|
||||||
|
do_make_request(""sv, std::move(options));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (session->useAnnounceIP() || ipv6_is_any)
|
||||||
|
{
|
||||||
|
if (session->useAnnounceIP())
|
||||||
|
{
|
||||||
|
options.url += format_ip_arg(session->announceIP());
|
||||||
|
}
|
||||||
|
d->requests_sent_count = 1;
|
||||||
|
do_make_request(""sv, std::move(options));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
d->requests_sent_count = 2;
|
||||||
|
|
||||||
|
// First try to send the announce via IPv4:
|
||||||
|
auto ipv4_options = options;
|
||||||
|
ipv4_options.ip_proto = tr_web::FetchOptions::IPProtocol::V4;
|
||||||
|
do_make_request("IPv4"sv, std::move(ipv4_options));
|
||||||
|
|
||||||
|
// Then try to send via IPv6:
|
||||||
|
options.ip_proto = tr_web::FetchOptions::IPProtocol::V6;
|
||||||
|
do_make_request("IPv6"sv, std::move(options));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::string_view benc, std::string_view log_name)
|
void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::string_view benc, std::string_view log_name)
|
||||||
{
|
{
|
||||||
|
@ -216,263 +472,113 @@ void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct http_announce_data
|
// ---
|
||||||
|
|
||||||
|
namespace
|
||||||
{
|
{
|
||||||
http_announce_data(tr_sha1_digest_t info_hash_in, tr_announce_response_func on_response_in, std::string_view log_name_in)
|
namespace scrape_helpers
|
||||||
: info_hash{ info_hash_in }
|
{
|
||||||
, on_response{ std::move(on_response_in) }
|
class scrape_data
|
||||||
, log_name{ log_name_in }
|
{
|
||||||
|
public:
|
||||||
|
scrape_data(tr_scrape_response_func response_func, std::string_view log_name)
|
||||||
|
: response_func_{ std::move(response_func) }
|
||||||
|
, log_name_{ log_name }
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
tr_sha1_digest_t info_hash = {};
|
[[nodiscard]] constexpr auto& response() noexcept
|
||||||
std::optional<tr_announce_response> previous_response;
|
{
|
||||||
|
return response_;
|
||||||
|
}
|
||||||
|
|
||||||
tr_announce_response_func on_response;
|
[[nodiscard]] constexpr auto const& log_name() const noexcept
|
||||||
bool http_success = false;
|
{
|
||||||
|
return log_name_;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t requests_sent_count = {};
|
void invoke_callback() const
|
||||||
uint8_t requests_answered_count = {};
|
{
|
||||||
|
if (response_func_)
|
||||||
|
{
|
||||||
|
response_func_(response_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string log_name;
|
private:
|
||||||
|
tr_scrape_response response_ = {};
|
||||||
|
tr_scrape_response_func response_func_ = {};
|
||||||
|
std::string log_name_;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool handleAnnounceResponse(tr_web::FetchResponse const& web_response, tr_announce_response* const response)
|
void onScrapeDone(tr_web::FetchResponse const& web_response)
|
||||||
{
|
{
|
||||||
auto const& [status, body, did_connect, did_timeout, vdata] = web_response;
|
auto const& [status, body, did_connect, did_timeout, vdata] = web_response;
|
||||||
auto const& log_name = static_cast<http_announce_data const*>(vdata)->log_name;
|
auto* const data = static_cast<scrape_data*>(vdata);
|
||||||
|
|
||||||
response->did_connect = did_connect;
|
auto& response = data->response();
|
||||||
response->did_timeout = did_timeout;
|
response.did_connect = did_connect;
|
||||||
tr_logAddTrace("Got announce response", log_name);
|
response.did_timeout = did_timeout;
|
||||||
|
|
||||||
|
auto const scrape_url_sv = response.scrape_url.sv();
|
||||||
|
tr_logAddTrace(fmt::format("Got scrape response for '{}'", scrape_url_sv), data->log_name());
|
||||||
|
|
||||||
if (status != HTTP_OK)
|
if (status != HTTP_OK)
|
||||||
{
|
{
|
||||||
auto const* const response_str = tr_webGetResponseStr(status);
|
auto const* const response_str = tr_webGetResponseStr(status);
|
||||||
response->errmsg = fmt::format(FMT_STRING("Tracker HTTP response {:d} ({:s}"), status, response_str);
|
response.errmsg = fmt::format(FMT_STRING("Tracker HTTP response {:d} ({:s})"), status, response_str);
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
else if (!std::empty(body))
|
||||||
tr_announcerParseHttpAnnounceResponse(*response, body, log_name);
|
|
||||||
|
|
||||||
if (!std::empty(response->pex6))
|
|
||||||
{
|
{
|
||||||
tr_logAddTrace(fmt::format("got a peers6 length of {}", std::size(response->pex6)), log_name);
|
tr_announcerParseHttpScrapeResponse(response, body, data->log_name());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!std::empty(response->pex))
|
data->invoke_callback();
|
||||||
{
|
delete data;
|
||||||
tr_logAddTrace(fmt::format("got a peers length of {}", std::size(response->pex)), log_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onAnnounceDone(tr_web::FetchResponse const& web_response)
|
void scrape_url_new(tr_pathbuf& scrape_url, tr_scrape_request const& req)
|
||||||
{
|
{
|
||||||
auto const& [status, body, did_connect, did_timeout, vdata] = web_response;
|
scrape_url = req.scrape_url.sv();
|
||||||
auto* data = static_cast<struct http_announce_data*>(vdata);
|
char delimiter = tr_strvContains(scrape_url, '?') ? '&' : '?';
|
||||||
|
|
||||||
++data->requests_answered_count;
|
for (int i = 0; i < req.info_hash_count; ++i)
|
||||||
|
|
||||||
// If another request already succeeded (or we don't have a registered callback),
|
|
||||||
// skip processing this response:
|
|
||||||
if (!data->http_success && data->on_response)
|
|
||||||
{
|
{
|
||||||
tr_announce_response response;
|
scrape_url.append(delimiter, "info_hash=");
|
||||||
response.info_hash = data->info_hash;
|
tr_urlPercentEncode(std::back_inserter(scrape_url), req.info_hash[i]);
|
||||||
|
delimiter = '&';
|
||||||
data->http_success = handleAnnounceResponse(web_response, &response);
|
|
||||||
|
|
||||||
if (data->http_success)
|
|
||||||
{
|
|
||||||
data->on_response(response);
|
|
||||||
}
|
|
||||||
else if (data->requests_answered_count == data->requests_sent_count)
|
|
||||||
{
|
|
||||||
auto const* response_used = &response;
|
|
||||||
|
|
||||||
// All requests have been answered, but none were successful.
|
|
||||||
// Choose the one that went further to report.
|
|
||||||
if (data->previous_response && !response.did_connect && !response.did_timeout)
|
|
||||||
{
|
|
||||||
response_used = &*data->previous_response;
|
|
||||||
}
|
|
||||||
|
|
||||||
data->on_response(*response_used);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// There is still one request pending that might succeed, so store
|
|
||||||
// the response for later. There is only room for 1 previous response,
|
|
||||||
// because there can be at most 2 requests.
|
|
||||||
TR_ASSERT(!data->previous_response);
|
|
||||||
data->previous_response = std::move(response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tr_logAddTrace("Ignoring redundant announce response", data->log_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free data if no more responses are expected:
|
|
||||||
if (data->requests_answered_count == data->requests_sent_count)
|
|
||||||
{
|
|
||||||
delete data;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} // namespace scrape_helpers
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace tr_tracker_announce_helpers
|
void tr_tracker_http_scrape(tr_session const* session, tr_scrape_request const& request, tr_scrape_response_func on_response)
|
||||||
{
|
{
|
||||||
|
using namespace scrape_helpers;
|
||||||
|
|
||||||
void announce_url_new(tr_urlbuf& url, tr_session const* session, tr_announce_request const& req)
|
auto* d = new scrape_data{ std::move(on_response), request.log_name };
|
||||||
{
|
|
||||||
url.clear();
|
|
||||||
auto out = std::back_inserter(url);
|
|
||||||
|
|
||||||
auto escaped_info_hash = tr_urlbuf{};
|
auto& response = d->response();
|
||||||
tr_urlPercentEncode(std::back_inserter(escaped_info_hash), req.info_hash);
|
response.scrape_url = request.scrape_url;
|
||||||
|
response.row_count = request.info_hash_count;
|
||||||
fmt::format_to(
|
for (int i = 0; i < response.row_count; ++i)
|
||||||
out,
|
|
||||||
"{url}"
|
|
||||||
"{sep}info_hash={info_hash}"
|
|
||||||
"&peer_id={peer_id}"
|
|
||||||
"&port={port}"
|
|
||||||
"&uploaded={uploaded}"
|
|
||||||
"&downloaded={downloaded}"
|
|
||||||
"&left={left}"
|
|
||||||
"&numwant={numwant}"
|
|
||||||
"&key={key}"
|
|
||||||
"&compact=1"
|
|
||||||
"&supportcrypto=1",
|
|
||||||
fmt::arg("url", req.announce_url),
|
|
||||||
fmt::arg("sep", tr_strvContains(req.announce_url.sv(), '?') ? '&' : '?'),
|
|
||||||
fmt::arg("info_hash", std::data(escaped_info_hash)),
|
|
||||||
fmt::arg("peer_id", std::string_view{ std::data(req.peer_id), std::size(req.peer_id) }),
|
|
||||||
fmt::arg("port", req.port.host()),
|
|
||||||
fmt::arg("uploaded", req.up),
|
|
||||||
fmt::arg("downloaded", req.down),
|
|
||||||
fmt::arg("left", req.leftUntilComplete),
|
|
||||||
fmt::arg("numwant", req.numwant),
|
|
||||||
fmt::arg("key", req.key));
|
|
||||||
|
|
||||||
if (session->encryptionMode() == TR_ENCRYPTION_REQUIRED)
|
|
||||||
{
|
{
|
||||||
fmt::format_to(out, "&requirecrypto=1");
|
response.rows[i].info_hash = request.info_hash[i];
|
||||||
|
response.rows[i].seeders = -1;
|
||||||
|
response.rows[i].leechers = -1;
|
||||||
|
response.rows[i].downloads = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.corrupt != 0)
|
auto scrape_url = tr_pathbuf{};
|
||||||
{
|
scrape_url_new(scrape_url, request);
|
||||||
fmt::format_to(out, "&corrupt={}", req.corrupt);
|
tr_logAddTrace(fmt::format("Sending scrape to libcurl: '{}'", scrape_url), request.log_name);
|
||||||
}
|
auto options = tr_web::FetchOptions{ scrape_url, onScrapeDone, d };
|
||||||
|
options.timeout_secs = TR_SCRAPE_TIMEOUT_SEC;
|
||||||
if (auto const str = get_event_string(req); !std::empty(str))
|
|
||||||
{
|
|
||||||
fmt::format_to(out, "&event={}", str);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!std::empty(req.tracker_id))
|
|
||||||
{
|
|
||||||
fmt::format_to(out, "&trackerid={}", req.tracker_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] std::string format_ip_arg(std::string_view ip)
|
|
||||||
{
|
|
||||||
return fmt::format("&ip={:s}", ip);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace tr_tracker_announce_helpers
|
|
||||||
|
|
||||||
void tr_tracker_http_announce(
|
|
||||||
tr_session const* session,
|
|
||||||
tr_announce_request const& request,
|
|
||||||
tr_announce_response_func on_response)
|
|
||||||
{
|
|
||||||
using namespace tr_tracker_announce_helpers;
|
|
||||||
|
|
||||||
auto* const d = new http_announce_data{ request.info_hash, std::move(on_response), request.log_name };
|
|
||||||
|
|
||||||
/* There are two alternative techniques for announcing both IPv4 and
|
|
||||||
IPv6 addresses. Previous version of BEP-7 suggests adding "ipv4="
|
|
||||||
and "ipv6=" parameters to the announce URL, while OpenTracker and
|
|
||||||
newer version of BEP-7 requires that peers announce once per each
|
|
||||||
public address they want to use.
|
|
||||||
|
|
||||||
We should ensure that we send the announce both via IPv6 and IPv4,
|
|
||||||
and to be safe we also add the "ipv6=" and "ipv4=" parameters, if
|
|
||||||
we already have them. Our global IPv6 address is computed for the
|
|
||||||
LTEP handshake, so this comes for free. Our public IPv4 address
|
|
||||||
may have been returned from a previous announce and stored in the
|
|
||||||
session.
|
|
||||||
*/
|
|
||||||
auto url = tr_urlbuf{};
|
|
||||||
announce_url_new(url, session, request);
|
|
||||||
auto options = tr_web::FetchOptions{ url.sv(), onAnnounceDone, d };
|
|
||||||
options.timeout_secs = TR_ANNOUNCE_TIMEOUT_SEC;
|
|
||||||
options.sndbuf = 4096;
|
options.sndbuf = 4096;
|
||||||
options.rcvbuf = 4096;
|
options.rcvbuf = 4096;
|
||||||
|
session->fetch(std::move(options));
|
||||||
auto do_make_request = [&](std::string_view const& protocol_name, tr_web::FetchOptions&& opt)
|
|
||||||
{
|
|
||||||
tr_logAddTrace(fmt::format("Sending {} announce to libcurl: '{}'", protocol_name, opt.url), request.log_name);
|
|
||||||
session->fetch(std::move(opt));
|
|
||||||
};
|
|
||||||
|
|
||||||
auto const [ipv6, ipv6_is_any] = session->publicAddress(TR_AF_INET6);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Before Curl 7.77.0, if we explicitly choose the IP version we want
|
|
||||||
* to use, it is still possible that the wrong one is used. The workaround
|
|
||||||
* is expensive (disabling DNS cache), so instead we have to make do with
|
|
||||||
* a request that we don't know if will go through IPv6 or IPv4.
|
|
||||||
*/
|
|
||||||
static bool const use_curl_workaround = curl_version_info(CURLVERSION_NOW)->version_num < CURL_VERSION_BITS(7, 77, 0);
|
|
||||||
if (use_curl_workaround)
|
|
||||||
{
|
|
||||||
if (session->useAnnounceIP())
|
|
||||||
{
|
|
||||||
options.url += format_ip_arg(session->announceIP());
|
|
||||||
}
|
|
||||||
|
|
||||||
d->requests_sent_count = 1;
|
|
||||||
do_make_request(""sv, std::move(options));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (session->useAnnounceIP() || ipv6_is_any)
|
|
||||||
{
|
|
||||||
if (session->useAnnounceIP())
|
|
||||||
{
|
|
||||||
options.url += format_ip_arg(session->announceIP());
|
|
||||||
}
|
|
||||||
d->requests_sent_count = 1;
|
|
||||||
do_make_request(""sv, std::move(options));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
d->requests_sent_count = 2;
|
|
||||||
|
|
||||||
// First try to send the announce via IPv4:
|
|
||||||
auto ipv4_options = options;
|
|
||||||
ipv4_options.ip_proto = tr_web::FetchOptions::IPProtocol::V4;
|
|
||||||
do_make_request("IPv4"sv, std::move(ipv4_options));
|
|
||||||
|
|
||||||
// Then try to send via IPv6:
|
|
||||||
options.ip_proto = tr_web::FetchOptions::IPProtocol::V6;
|
|
||||||
do_make_request("IPv6"sv, std::move(options));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/****
|
|
||||||
*****
|
|
||||||
***** SCRAPE
|
|
||||||
*****
|
|
||||||
****/
|
|
||||||
|
|
||||||
void tr_announcerParseHttpScrapeResponse(tr_scrape_response& response, std::string_view benc, std::string_view log_name)
|
void tr_announcerParseHttpScrapeResponse(tr_scrape_response& response, std::string_view benc, std::string_view log_name)
|
||||||
{
|
{
|
||||||
verboseLog("Scrape response:", TR_DOWN, benc);
|
verboseLog("Scrape response:", TR_DOWN, benc);
|
||||||
|
@ -576,100 +682,3 @@ void tr_announcerParseHttpScrapeResponse(tr_scrape_response& response, std::stri
|
||||||
tr_error_clear(&error);
|
tr_error_clear(&error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class scrape_data
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
scrape_data(tr_scrape_response_func response_func, std::string_view log_name)
|
|
||||||
: response_func_{ std::move(response_func) }
|
|
||||||
, log_name_{ log_name }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto& response() noexcept
|
|
||||||
{
|
|
||||||
return response_;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto const& log_name() const noexcept
|
|
||||||
{
|
|
||||||
return log_name_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void invoke_callback() const
|
|
||||||
{
|
|
||||||
if (response_func_)
|
|
||||||
{
|
|
||||||
response_func_(response_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
tr_scrape_response response_ = {};
|
|
||||||
tr_scrape_response_func response_func_ = {};
|
|
||||||
std::string log_name_;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void onScrapeDone(tr_web::FetchResponse const& web_response)
|
|
||||||
{
|
|
||||||
auto const& [status, body, did_connect, did_timeout, vdata] = web_response;
|
|
||||||
auto* const data = static_cast<scrape_data*>(vdata);
|
|
||||||
|
|
||||||
auto& response = data->response();
|
|
||||||
response.did_connect = did_connect;
|
|
||||||
response.did_timeout = did_timeout;
|
|
||||||
|
|
||||||
auto const scrape_url_sv = response.scrape_url.sv();
|
|
||||||
tr_logAddTrace(fmt::format("Got scrape response for '{}'", scrape_url_sv), data->log_name());
|
|
||||||
|
|
||||||
if (status != HTTP_OK)
|
|
||||||
{
|
|
||||||
auto const* const response_str = tr_webGetResponseStr(status);
|
|
||||||
response.errmsg = fmt::format(FMT_STRING("Tracker HTTP response {:d} ({:s})"), status, response_str);
|
|
||||||
}
|
|
||||||
else if (!std::empty(body))
|
|
||||||
{
|
|
||||||
tr_announcerParseHttpScrapeResponse(response, body, data->log_name());
|
|
||||||
}
|
|
||||||
|
|
||||||
data->invoke_callback();
|
|
||||||
delete data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void scrape_url_new(tr_pathbuf& scrape_url, tr_scrape_request const& req)
|
|
||||||
{
|
|
||||||
scrape_url = req.scrape_url.sv();
|
|
||||||
char delimiter = tr_strvContains(scrape_url, '?') ? '&' : '?';
|
|
||||||
|
|
||||||
for (int i = 0; i < req.info_hash_count; ++i)
|
|
||||||
{
|
|
||||||
scrape_url.append(delimiter, "info_hash=");
|
|
||||||
tr_urlPercentEncode(std::back_inserter(scrape_url), req.info_hash[i]);
|
|
||||||
delimiter = '&';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void tr_tracker_http_scrape(tr_session const* session, tr_scrape_request const& request, tr_scrape_response_func on_response)
|
|
||||||
{
|
|
||||||
auto* d = new scrape_data{ std::move(on_response), request.log_name };
|
|
||||||
|
|
||||||
auto& response = d->response();
|
|
||||||
response.scrape_url = request.scrape_url;
|
|
||||||
response.row_count = request.info_hash_count;
|
|
||||||
for (int i = 0; i < response.row_count; ++i)
|
|
||||||
{
|
|
||||||
response.rows[i].info_hash = request.info_hash[i];
|
|
||||||
response.rows[i].seeders = -1;
|
|
||||||
response.rows[i].leechers = -1;
|
|
||||||
response.rows[i].downloads = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto scrape_url = tr_pathbuf{};
|
|
||||||
scrape_url_new(scrape_url, request);
|
|
||||||
tr_logAddTrace(fmt::format("Sending scrape to libcurl: '{}'", scrape_url), request.log_name);
|
|
||||||
auto options = tr_web::FetchOptions{ scrape_url, onScrapeDone, d };
|
|
||||||
options.timeout_secs = TR_SCRAPE_TIMEOUT_SEC;
|
|
||||||
options.sndbuf = 4096;
|
|
||||||
options.rcvbuf = 4096;
|
|
||||||
session->fetch(std::move(options));
|
|
||||||
}
|
|
||||||
|
|
|
@ -43,23 +43,20 @@ using namespace std::literals;
|
||||||
#define tr_logAddDebugTier(tier, msg) tr_logAddDebug(msg, (tier)->buildLogName())
|
#define tr_logAddDebugTier(tier, msg) tr_logAddDebug(msg, (tier)->buildLogName())
|
||||||
#define tr_logAddTraceTier(tier, msg) tr_logAddTrace(msg, (tier)->buildLogName())
|
#define tr_logAddTraceTier(tier, msg) tr_logAddTrace(msg, (tier)->buildLogName())
|
||||||
|
|
||||||
/* unless the tracker says otherwise, rescrape this frequently */
|
|
||||||
static auto constexpr DefaultScrapeIntervalSec = int{ 60 * 30 };
|
|
||||||
|
|
||||||
/* the value of the 'numwant' argument passed in tracker requests. */
|
|
||||||
static auto constexpr Numwant = int{ 80 };
|
|
||||||
|
|
||||||
/* how often to announce & scrape */
|
|
||||||
static auto constexpr MaxAnnouncesPerUpkeep = int{ 20 };
|
|
||||||
static auto constexpr MaxScrapesPerUpkeep = int{ 20 };
|
|
||||||
|
|
||||||
/* how many infohashes to remove when we get a scrape-too-long error */
|
|
||||||
static auto constexpr TrMultiscrapeStep = int{ 5 };
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
/* unless the tracker says otherwise, rescrape this frequently */
|
||||||
|
auto constexpr DefaultScrapeIntervalSec = int{ 60 * 30 };
|
||||||
|
|
||||||
|
/* the value of the 'numwant' argument passed in tracker requests. */
|
||||||
|
auto constexpr Numwant = int{ 80 };
|
||||||
|
|
||||||
|
/* how often to announce & scrape */
|
||||||
|
auto constexpr MaxAnnouncesPerUpkeep = int{ 20 };
|
||||||
|
auto constexpr MaxScrapesPerUpkeep = int{ 20 };
|
||||||
|
|
||||||
|
/* how many infohashes to remove when we get a scrape-too-long error */
|
||||||
|
auto constexpr TrMultiscrapeStep = int{ 5 };
|
||||||
|
|
||||||
struct StopsCompare
|
struct StopsCompare
|
||||||
{
|
{
|
||||||
|
@ -643,22 +640,6 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static tr_tier* getTier(tr_announcer_impl* announcer, tr_sha1_digest_t const& info_hash, int tier_id)
|
|
||||||
{
|
|
||||||
if (announcer == nullptr)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* const tor = announcer->session->torrents().get(info_hash);
|
|
||||||
if (tor == nullptr || tor->torrent_announcer == nullptr)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tor->torrent_announcer->getTier(tier_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- PUBLISH
|
// --- PUBLISH
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
@ -773,7 +754,11 @@ time_t tr_announcerNextManualAnnounce(tr_torrent const* tor)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tr_logAddTrace_tier_announce_queue(tr_tier const* tier)
|
namespace
|
||||||
|
{
|
||||||
|
namespace announce_helpers
|
||||||
|
{
|
||||||
|
void tr_logAddTrace_tier_announce_queue(tr_tier const* tier)
|
||||||
{
|
{
|
||||||
if (!tr_logLevelIsActive(TR_LOG_TRACE) || std::empty(tier->announce_events))
|
if (!tr_logLevelIsActive(TR_LOG_TRACE) || std::empty(tier->announce_events))
|
||||||
{
|
{
|
||||||
|
@ -792,7 +777,7 @@ static void tr_logAddTrace_tier_announce_queue(tr_tier const* tier)
|
||||||
}
|
}
|
||||||
|
|
||||||
// higher priorities go to the front of the announce queue
|
// higher priorities go to the front of the announce queue
|
||||||
static void tier_update_announce_priority(tr_tier* tier)
|
void tier_update_announce_priority(tr_tier* tier)
|
||||||
{
|
{
|
||||||
int priority = -1;
|
int priority = -1;
|
||||||
|
|
||||||
|
@ -804,7 +789,7 @@ static void tier_update_announce_priority(tr_tier* tier)
|
||||||
tier->announce_event_priority = priority;
|
tier->announce_event_priority = priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tier_announce_remove_trailing(tr_tier* tier, tr_announce_event e)
|
void tier_announce_remove_trailing(tr_tier* tier, tr_announce_event e)
|
||||||
{
|
{
|
||||||
while (!std::empty(tier->announce_events) && tier->announce_events.back() == e)
|
while (!std::empty(tier->announce_events) && tier->announce_events.back() == e)
|
||||||
{
|
{
|
||||||
|
@ -814,7 +799,7 @@ static void tier_announce_remove_trailing(tr_tier* tier, tr_announce_event e)
|
||||||
tier_update_announce_priority(tier);
|
tier_update_announce_priority(tier);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tier_announce_event_push(tr_tier* tier, tr_announce_event e, time_t announce_at)
|
void tier_announce_event_push(tr_tier* tier, tr_announce_event e, time_t announce_at)
|
||||||
{
|
{
|
||||||
TR_ASSERT(tier != nullptr);
|
TR_ASSERT(tier != nullptr);
|
||||||
|
|
||||||
|
@ -852,7 +837,7 @@ static void tier_announce_event_push(tr_tier* tier, tr_announce_event e, time_t
|
||||||
tr_logAddTraceTier(tier, fmt::format("announcing in {} seconds", difftime(announce_at, tr_time())));
|
tr_logAddTraceTier(tier, fmt::format("announcing in {} seconds", difftime(announce_at, tr_time())));
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto tier_announce_event_pull(tr_tier* tier)
|
auto tier_announce_event_pull(tr_tier* tier)
|
||||||
{
|
{
|
||||||
auto const e = tier->announce_events.front();
|
auto const e = tier->announce_events.front();
|
||||||
tier->announce_events.pop_front();
|
tier->announce_events.pop_front();
|
||||||
|
@ -860,104 +845,7 @@ static auto tier_announce_event_pull(tr_tier* tier)
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void torrentAddAnnounce(tr_torrent* tor, tr_announce_event e, time_t announce_at)
|
bool isUnregistered(char const* errmsg)
|
||||||
{
|
|
||||||
// tell each tier to announce
|
|
||||||
for (auto& tier : tor->torrent_announcer->tiers)
|
|
||||||
{
|
|
||||||
tier_announce_event_push(&tier, e, announce_at);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void tr_announcer_impl::startTorrent(tr_torrent* tor)
|
|
||||||
{
|
|
||||||
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_STARTED, tr_time());
|
|
||||||
}
|
|
||||||
|
|
||||||
void tr_announcerManualAnnounce(tr_torrent* tor)
|
|
||||||
{
|
|
||||||
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_NONE, tr_time());
|
|
||||||
}
|
|
||||||
|
|
||||||
void tr_announcer_impl::stopTorrent(tr_torrent* tor)
|
|
||||||
{
|
|
||||||
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_STOPPED, tr_time());
|
|
||||||
}
|
|
||||||
|
|
||||||
void tr_announcerTorrentCompleted(tr_torrent* tor)
|
|
||||||
{
|
|
||||||
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_COMPLETED, tr_time());
|
|
||||||
}
|
|
||||||
|
|
||||||
void tr_announcerChangeMyPort(tr_torrent* tor)
|
|
||||||
{
|
|
||||||
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_STARTED, tr_time());
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
void tr_announcerAddBytes(tr_torrent* tor, int type, uint32_t n_bytes)
|
|
||||||
{
|
|
||||||
TR_ASSERT(tr_isTorrent(tor));
|
|
||||||
TR_ASSERT(type == TR_ANN_UP || type == TR_ANN_DOWN || type == TR_ANN_CORRUPT);
|
|
||||||
|
|
||||||
for (auto& tier : tor->torrent_announcer->tiers)
|
|
||||||
{
|
|
||||||
tier.byteCounts[type] += n_bytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---
|
|
||||||
|
|
||||||
[[nodiscard]] static tr_announce_request create_announce_request(
|
|
||||||
tr_announcer_impl const* const announcer,
|
|
||||||
tr_torrent* const tor,
|
|
||||||
tr_tier const* const tier,
|
|
||||||
tr_announce_event const event)
|
|
||||||
{
|
|
||||||
auto const* const current_tracker = tier->currentTracker();
|
|
||||||
TR_ASSERT(current_tracker != nullptr);
|
|
||||||
|
|
||||||
auto req = tr_announce_request{};
|
|
||||||
req.port = announcer->session->advertisedPeerPort();
|
|
||||||
req.announce_url = current_tracker->announce_url;
|
|
||||||
req.tracker_id = current_tracker->tracker_id;
|
|
||||||
req.info_hash = tor->infoHash();
|
|
||||||
req.peer_id = tr_torrentGetPeerId(tor);
|
|
||||||
req.up = tier->byteCounts[TR_ANN_UP];
|
|
||||||
req.down = tier->byteCounts[TR_ANN_DOWN];
|
|
||||||
req.corrupt = tier->byteCounts[TR_ANN_CORRUPT];
|
|
||||||
req.leftUntilComplete = tor->hasMetainfo() ? tor->totalSize() - tor->hasTotal() : INT64_MAX;
|
|
||||||
req.event = event;
|
|
||||||
req.numwant = event == TR_ANNOUNCE_EVENT_STOPPED ? 0 : Numwant;
|
|
||||||
req.key = tor->announce_key();
|
|
||||||
req.partial_seed = tor->isPartialSeed();
|
|
||||||
tier->buildLogName(req.log_name, sizeof(req.log_name));
|
|
||||||
return req;
|
|
||||||
}
|
|
||||||
|
|
||||||
void tr_announcer_impl::removeTorrent(tr_torrent* tor)
|
|
||||||
{
|
|
||||||
// FIXME(ckerr)
|
|
||||||
auto* const ta = tor->torrent_announcer;
|
|
||||||
if (ta == nullptr)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto const& tier : ta->tiers)
|
|
||||||
{
|
|
||||||
if (tier.isRunning && tier.lastAnnounceSucceeded)
|
|
||||||
{
|
|
||||||
stops_.emplace(create_announce_request(this, tor, &tier, TR_ANNOUNCE_EVENT_STOPPED));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tor->torrent_announcer = nullptr;
|
|
||||||
delete ta;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isUnregistered(char const* errmsg)
|
|
||||||
{
|
{
|
||||||
auto const lower = tr_strlower(errmsg != nullptr ? errmsg : "");
|
auto const lower = tr_strlower(errmsg != nullptr ? errmsg : "");
|
||||||
|
|
||||||
|
@ -966,8 +854,10 @@ static bool isUnregistered(char const* errmsg)
|
||||||
return std::any_of(std::begin(Keys), std::end(Keys), [&lower](auto const& key) { return tr_strvContains(lower, key); });
|
return std::any_of(std::begin(Keys), std::end(Keys), [&lower](auto const& key) { return tr_strvContains(lower, key); });
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_announce_error(tr_tier* tier, char const* err, tr_announce_event e)
|
void on_announce_error(tr_tier* tier, char const* err, tr_announce_event e)
|
||||||
{
|
{
|
||||||
|
using namespace announce_helpers;
|
||||||
|
|
||||||
/* increment the error count */
|
/* increment the error count */
|
||||||
auto* current_tracker = tier->currentTracker();
|
auto* current_tracker = tier->currentTracker();
|
||||||
if (current_tracker != nullptr)
|
if (current_tracker != nullptr)
|
||||||
|
@ -1002,12 +892,69 @@ static void on_announce_error(tr_tier* tier, char const* err, tr_announce_event
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] tr_announce_request create_announce_request(
|
||||||
|
tr_announcer_impl const* const announcer,
|
||||||
|
tr_torrent* const tor,
|
||||||
|
tr_tier const* const tier,
|
||||||
|
tr_announce_event const event)
|
||||||
|
{
|
||||||
|
auto const* const current_tracker = tier->currentTracker();
|
||||||
|
TR_ASSERT(current_tracker != nullptr);
|
||||||
|
|
||||||
|
auto req = tr_announce_request{};
|
||||||
|
req.port = announcer->session->advertisedPeerPort();
|
||||||
|
req.announce_url = current_tracker->announce_url;
|
||||||
|
req.tracker_id = current_tracker->tracker_id;
|
||||||
|
req.info_hash = tor->infoHash();
|
||||||
|
req.peer_id = tr_torrentGetPeerId(tor);
|
||||||
|
req.up = tier->byteCounts[TR_ANN_UP];
|
||||||
|
req.down = tier->byteCounts[TR_ANN_DOWN];
|
||||||
|
req.corrupt = tier->byteCounts[TR_ANN_CORRUPT];
|
||||||
|
req.leftUntilComplete = tor->hasMetainfo() ? tor->totalSize() - tor->hasTotal() : INT64_MAX;
|
||||||
|
req.event = event;
|
||||||
|
req.numwant = event == TR_ANNOUNCE_EVENT_STOPPED ? 0 : Numwant;
|
||||||
|
req.key = tor->announce_key();
|
||||||
|
req.partial_seed = tor->isPartialSeed();
|
||||||
|
tier->buildLogName(req.log_name, sizeof(req.log_name));
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] tr_tier* getTier(tr_announcer_impl* announcer, tr_sha1_digest_t const& info_hash, int tier_id)
|
||||||
|
{
|
||||||
|
if (announcer == nullptr)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* const tor = announcer->session->torrents().get(info_hash);
|
||||||
|
if (tor == nullptr || tor->torrent_announcer == nullptr)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tor->torrent_announcer->getTier(tier_id);
|
||||||
|
}
|
||||||
|
} // namespace announce_helpers
|
||||||
|
|
||||||
|
void torrentAddAnnounce(tr_torrent* tor, tr_announce_event e, time_t announce_at)
|
||||||
|
{
|
||||||
|
using namespace announce_helpers;
|
||||||
|
|
||||||
|
// tell each tier to announce
|
||||||
|
for (auto& tier : tor->torrent_announcer->tiers)
|
||||||
|
{
|
||||||
|
tier_announce_event_push(&tier, e, announce_at);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void tr_announcer_impl::onAnnounceDone(
|
void tr_announcer_impl::onAnnounceDone(
|
||||||
int tier_id,
|
int tier_id,
|
||||||
tr_announce_event event,
|
tr_announce_event event,
|
||||||
bool is_running_on_success,
|
bool is_running_on_success,
|
||||||
tr_announce_response const& response)
|
tr_announce_response const& response)
|
||||||
{
|
{
|
||||||
|
using namespace announce_helpers;
|
||||||
using namespace publish_helpers;
|
using namespace publish_helpers;
|
||||||
|
|
||||||
auto* const tier = getTier(this, response.info_hash, tier_id);
|
auto* const tier = getTier(this, response.info_hash, tier_id);
|
||||||
|
@ -1193,54 +1140,92 @@ void tr_announcer_impl::onAnnounceDone(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tierAnnounce(tr_announcer_impl* announcer, tr_tier* tier)
|
void tr_announcer_impl::startTorrent(tr_torrent* tor)
|
||||||
{
|
{
|
||||||
TR_ASSERT(!tier->isAnnouncing);
|
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_STARTED, tr_time());
|
||||||
TR_ASSERT(!std::empty(tier->announce_events));
|
}
|
||||||
TR_ASSERT(tier->currentTracker() != nullptr);
|
|
||||||
|
|
||||||
auto const now = tr_time();
|
void tr_announcerManualAnnounce(tr_torrent* tor)
|
||||||
|
{
|
||||||
|
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_NONE, tr_time());
|
||||||
|
}
|
||||||
|
|
||||||
tr_torrent* tor = tier->tor;
|
void tr_announcer_impl::stopTorrent(tr_torrent* tor)
|
||||||
auto const event = tier_announce_event_pull(tier);
|
{
|
||||||
auto const req = create_announce_request(announcer, tor, tier, event);
|
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_STOPPED, tr_time());
|
||||||
|
}
|
||||||
|
|
||||||
tier->isAnnouncing = true;
|
void tr_announcerTorrentCompleted(tr_torrent* tor)
|
||||||
tier->lastAnnounceStartTime = now;
|
{
|
||||||
|
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_COMPLETED, tr_time());
|
||||||
|
}
|
||||||
|
|
||||||
auto tier_id = tier->id;
|
void tr_announcerChangeMyPort(tr_torrent* tor)
|
||||||
auto is_running_on_success = tor->isRunning;
|
{
|
||||||
|
torrentAddAnnounce(tor, TR_ANNOUNCE_EVENT_STARTED, tr_time());
|
||||||
|
}
|
||||||
|
|
||||||
announcer->announce(
|
// ---
|
||||||
req,
|
|
||||||
[session = announcer->session, announcer, tier_id, event, is_running_on_success](tr_announce_response const& response)
|
void tr_announcerAddBytes(tr_torrent* tor, int type, uint32_t n_bytes)
|
||||||
|
{
|
||||||
|
TR_ASSERT(tr_isTorrent(tor));
|
||||||
|
TR_ASSERT(type == TR_ANN_UP || type == TR_ANN_DOWN || type == TR_ANN_CORRUPT);
|
||||||
|
|
||||||
|
for (auto& tier : tor->torrent_announcer->tiers)
|
||||||
|
{
|
||||||
|
tier.byteCounts[type] += n_bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---
|
||||||
|
|
||||||
|
void tr_announcer_impl::removeTorrent(tr_torrent* tor)
|
||||||
|
{
|
||||||
|
using namespace announce_helpers;
|
||||||
|
|
||||||
|
// FIXME(ckerr)
|
||||||
|
auto* const ta = tor->torrent_announcer;
|
||||||
|
if (ta == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& tier : ta->tiers)
|
||||||
|
{
|
||||||
|
if (tier.isRunning && tier.lastAnnounceSucceeded)
|
||||||
{
|
{
|
||||||
if (session->announcer_)
|
stops_.emplace(create_announce_request(this, tor, &tier, TR_ANNOUNCE_EVENT_STOPPED));
|
||||||
{
|
}
|
||||||
announcer->onAnnounceDone(tier_id, event, is_running_on_success, response);
|
}
|
||||||
}
|
|
||||||
});
|
tor->torrent_announcer = nullptr;
|
||||||
|
delete ta;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- SCRAPE
|
// --- SCRAPE
|
||||||
|
|
||||||
static bool multiscrape_too_big(std::string_view errmsg)
|
namespace
|
||||||
|
{
|
||||||
|
namespace on_scrape_done_helpers
|
||||||
|
{
|
||||||
|
[[nodiscard]] TR_CONSTEXPR20 bool multiscrape_too_big(std::string_view errmsg)
|
||||||
{
|
{
|
||||||
/* Found a tracker that returns some bespoke string for this case?
|
/* Found a tracker that returns some bespoke string for this case?
|
||||||
Add your patch here and open a PR */
|
Add your patch here and open a PR */
|
||||||
auto constexpr TooLongErrors = std::array<std::string_view, 3>{
|
auto too_long_errors = std::array<std::string_view, 3>{
|
||||||
"Bad Request",
|
"Bad Request",
|
||||||
"GET string too long",
|
"GET string too long",
|
||||||
"Request-URI Too Long",
|
"Request-URI Too Long",
|
||||||
};
|
};
|
||||||
|
|
||||||
return std::any_of(
|
return std::any_of(
|
||||||
std::begin(TooLongErrors),
|
std::begin(too_long_errors),
|
||||||
std::end(TooLongErrors),
|
std::end(too_long_errors),
|
||||||
[&errmsg](auto const& substr) { return tr_strvContains(errmsg, substr); });
|
[&errmsg](auto const& substr) { return tr_strvContains(errmsg, substr); });
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_scrape_error(tr_session const* /*session*/, tr_tier* tier, char const* errmsg)
|
void on_scrape_error(tr_session const* /*session*/, tr_tier* tier, char const* errmsg)
|
||||||
{
|
{
|
||||||
// increment the error count
|
// increment the error count
|
||||||
auto* current_tracker = tier->currentTracker();
|
auto* current_tracker = tier->currentTracker();
|
||||||
|
@ -1265,7 +1250,7 @@ static void on_scrape_error(tr_session const* /*session*/, tr_tier* tier, char c
|
||||||
tier->scheduleNextScrape(interval);
|
tier->scheduleNextScrape(interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void checkMultiscrapeMax(tr_announcer_impl* announcer, tr_scrape_response const& response)
|
void checkMultiscrapeMax(tr_announcer_impl* announcer, tr_scrape_response const& response)
|
||||||
{
|
{
|
||||||
if (!multiscrape_too_big(response.errmsg))
|
if (!multiscrape_too_big(response.errmsg))
|
||||||
{
|
{
|
||||||
|
@ -1303,9 +1288,12 @@ static void checkMultiscrapeMax(tr_announcer_impl* announcer, tr_scrape_response
|
||||||
multiscrape_max = n;
|
multiscrape_max = n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} // namespace on_scrape_done_helpers
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void tr_announcer_impl::onScrapeDone(tr_scrape_response const& response)
|
void tr_announcer_impl::onScrapeDone(tr_scrape_response const& response)
|
||||||
{
|
{
|
||||||
|
using namespace on_scrape_done_helpers;
|
||||||
using namespace publish_helpers;
|
using namespace publish_helpers;
|
||||||
|
|
||||||
auto const now = tr_time();
|
auto const now = tr_time();
|
||||||
|
@ -1404,7 +1392,9 @@ void tr_announcer_impl::onScrapeDone(tr_scrape_response const& response)
|
||||||
checkMultiscrapeMax(this, response);
|
checkMultiscrapeMax(this, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void multiscrape(tr_announcer_impl* announcer, std::vector<tr_tier*> const& tiers)
|
namespace
|
||||||
|
{
|
||||||
|
void multiscrape(tr_announcer_impl* announcer, std::vector<tr_tier*> const& tiers)
|
||||||
{
|
{
|
||||||
auto const now = tr_time();
|
auto const now = tr_time();
|
||||||
auto requests = std::array<tr_scrape_request, MaxScrapesPerUpkeep>{};
|
auto requests = std::array<tr_scrape_request, MaxScrapesPerUpkeep>{};
|
||||||
|
@ -1471,7 +1461,9 @@ static void multiscrape(tr_announcer_impl* announcer, std::vector<tr_tier*> cons
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int compareAnnounceTiers(tr_tier const* a, tr_tier const* b)
|
namespace upkeep_helpers
|
||||||
|
{
|
||||||
|
int compareAnnounceTiers(tr_tier const* a, tr_tier const* b)
|
||||||
{
|
{
|
||||||
/* prefer higher-priority events */
|
/* prefer higher-priority events */
|
||||||
if (auto const priority_a = a->announce_event_priority, priority_b = b->announce_event_priority; priority_a != priority_b)
|
if (auto const priority_a = a->announce_event_priority, priority_b = b->announce_event_priority; priority_a != priority_b)
|
||||||
|
@ -1510,9 +1502,40 @@ static int compareAnnounceTiers(tr_tier const* a, tr_tier const* b)
|
||||||
return a < b ? -1 : 1;
|
return a < b ? -1 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void scrapeAndAnnounceMore(tr_announcer_impl* announcer)
|
void tierAnnounce(tr_announcer_impl* announcer, tr_tier* tier)
|
||||||
{
|
{
|
||||||
time_t const now = tr_time();
|
using namespace announce_helpers;
|
||||||
|
|
||||||
|
TR_ASSERT(!tier->isAnnouncing);
|
||||||
|
TR_ASSERT(!std::empty(tier->announce_events));
|
||||||
|
TR_ASSERT(tier->currentTracker() != nullptr);
|
||||||
|
|
||||||
|
auto const now = tr_time();
|
||||||
|
|
||||||
|
tr_torrent* tor = tier->tor;
|
||||||
|
auto const event = tier_announce_event_pull(tier);
|
||||||
|
auto const req = create_announce_request(announcer, tor, tier, event);
|
||||||
|
|
||||||
|
tier->isAnnouncing = true;
|
||||||
|
tier->lastAnnounceStartTime = now;
|
||||||
|
|
||||||
|
auto tier_id = tier->id;
|
||||||
|
auto is_running_on_success = tor->isRunning;
|
||||||
|
|
||||||
|
announcer->announce(
|
||||||
|
req,
|
||||||
|
[session = announcer->session, announcer, tier_id, event, is_running_on_success](tr_announce_response const& response)
|
||||||
|
{
|
||||||
|
if (session->announcer_)
|
||||||
|
{
|
||||||
|
announcer->onAnnounceDone(tier_id, event, is_running_on_success, response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void scrapeAndAnnounceMore(tr_announcer_impl* announcer)
|
||||||
|
{
|
||||||
|
auto const now = tr_time();
|
||||||
|
|
||||||
/* build a list of tiers that need to be announced */
|
/* build a list of tiers that need to be announced */
|
||||||
auto announce_me = std::vector<tr_tier*>{};
|
auto announce_me = std::vector<tr_tier*>{};
|
||||||
|
@ -1557,9 +1580,13 @@ static void scrapeAndAnnounceMore(tr_announcer_impl* announcer)
|
||||||
tierAnnounce(announcer, tier);
|
tierAnnounce(announcer, tier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} // namespace upkeep_helpers
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void tr_announcer_impl::upkeep()
|
void tr_announcer_impl::upkeep()
|
||||||
{
|
{
|
||||||
|
using namespace upkeep_helpers;
|
||||||
|
|
||||||
auto const lock = session->unique_lock();
|
auto const lock = session->unique_lock();
|
||||||
|
|
||||||
// maybe send out some "stopped" messages for closed torrents
|
// maybe send out some "stopped" messages for closed torrents
|
||||||
|
@ -1576,7 +1603,11 @@ void tr_announcer_impl::upkeep()
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
static tr_tracker_view trackerView(tr_torrent const& tor, size_t tier_index, tr_tier const& tier, tr_tracker const& tracker)
|
namespace
|
||||||
|
{
|
||||||
|
namespace tracker_view_helpers
|
||||||
|
{
|
||||||
|
[[nodiscard]] auto trackerView(tr_torrent const& tor, size_t tier_index, tr_tier const& tier, tr_tracker const& tracker)
|
||||||
{
|
{
|
||||||
auto const now = tr_time();
|
auto const now = tr_time();
|
||||||
auto view = tr_tracker_view{};
|
auto view = tr_tracker_view{};
|
||||||
|
@ -1666,6 +1697,8 @@ static tr_tracker_view trackerView(tr_torrent const& tor, size_t tier_index, tr_
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
} // namespace tracker_view_helpers
|
||||||
|
} // namespace
|
||||||
|
|
||||||
size_t tr_announcerTrackerCount(tr_torrent const* tor)
|
size_t tr_announcerTrackerCount(tr_torrent const* tor)
|
||||||
{
|
{
|
||||||
|
@ -1682,6 +1715,8 @@ size_t tr_announcerTrackerCount(tr_torrent const* tor)
|
||||||
|
|
||||||
tr_tracker_view tr_announcerTracker(tr_torrent const* tor, size_t nth)
|
tr_tracker_view tr_announcerTracker(tr_torrent const* tor, size_t nth)
|
||||||
{
|
{
|
||||||
|
using namespace tracker_view_helpers;
|
||||||
|
|
||||||
TR_ASSERT(tr_isTorrent(tor));
|
TR_ASSERT(tr_isTorrent(tor));
|
||||||
TR_ASSERT(tor->torrent_announcer != nullptr);
|
TR_ASSERT(tor->torrent_announcer != nullptr);
|
||||||
|
|
||||||
|
@ -1709,6 +1744,8 @@ tr_tracker_view tr_announcerTracker(tr_torrent const* tor, size_t nth)
|
||||||
// so announcer needs to update the tr_tier / tr_trackers to match
|
// so announcer needs to update the tr_tier / tr_trackers to match
|
||||||
void tr_announcer_impl::resetTorrent(tr_torrent* tor)
|
void tr_announcer_impl::resetTorrent(tr_torrent* tor)
|
||||||
{
|
{
|
||||||
|
using namespace announce_helpers;
|
||||||
|
|
||||||
// make a new tr_announcer_tier
|
// make a new tr_announcer_tier
|
||||||
auto* const older = tor->torrent_announcer;
|
auto* const older = tor->torrent_announcer;
|
||||||
tor->torrent_announcer = new tr_torrent_announcer{ this, tor };
|
tor->torrent_announcer = new tr_torrent_announcer{ this, tor };
|
||||||
|
|
|
@ -27,11 +27,9 @@
|
||||||
#include "tr-assert.h"
|
#include "tr-assert.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
/***
|
namespace
|
||||||
****
|
{
|
||||||
***/
|
void log_openssl_error(char const* file, int line)
|
||||||
|
|
||||||
static void log_openssl_error(char const* file, int line)
|
|
||||||
{
|
{
|
||||||
unsigned long const error_code = ERR_get_error();
|
unsigned long const error_code = ERR_get_error();
|
||||||
|
|
||||||
|
@ -64,7 +62,7 @@ static void log_openssl_error(char const* file, int line)
|
||||||
|
|
||||||
#define log_error() log_openssl_error(__FILE__, __LINE__)
|
#define log_error() log_openssl_error(__FILE__, __LINE__)
|
||||||
|
|
||||||
static bool check_openssl_result(int result, int expected_result, bool expected_equal, char const* file, int line)
|
bool check_openssl_result(int result, int expected_result, bool expected_equal, char const* file, int line)
|
||||||
{
|
{
|
||||||
bool const ret = (result == expected_result) == expected_equal;
|
bool const ret = (result == expected_result) == expected_equal;
|
||||||
|
|
||||||
|
@ -77,13 +75,8 @@ static bool check_openssl_result(int result, int expected_result, bool expected_
|
||||||
}
|
}
|
||||||
|
|
||||||
#define check_result(result) check_openssl_result((result), 1, true, __FILE__, __LINE__)
|
#define check_result(result) check_openssl_result((result), 1, true, __FILE__, __LINE__)
|
||||||
#define check_result_neq(result, x_result) check_openssl_result((result), (x_result), false, __FILE__, __LINE__)
|
|
||||||
|
|
||||||
/***
|
namespace sha_helpers
|
||||||
****
|
|
||||||
***/
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
{
|
||||||
|
|
||||||
class ShaHelper
|
class ShaHelper
|
||||||
|
@ -196,55 +189,26 @@ private:
|
||||||
ShaHelper helper_{ EVP_sha256 };
|
ShaHelper helper_{ EVP_sha256 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace sha_helpers
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
// --- sha
|
||||||
|
|
||||||
std::unique_ptr<tr_sha1> tr_sha1::create()
|
std::unique_ptr<tr_sha1> tr_sha1::create()
|
||||||
{
|
{
|
||||||
|
using namespace sha_helpers;
|
||||||
|
|
||||||
return std::make_unique<Sha1Impl>();
|
return std::make_unique<Sha1Impl>();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<tr_sha256> tr_sha256::create()
|
std::unique_ptr<tr_sha256> tr_sha256::create()
|
||||||
{
|
{
|
||||||
|
using namespace sha_helpers;
|
||||||
|
|
||||||
return std::make_unique<Sha256Impl>();
|
return std::make_unique<Sha256Impl>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
// --- x509
|
||||||
****
|
|
||||||
***/
|
|
||||||
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x0090802fL
|
|
||||||
|
|
||||||
static EVP_CIPHER_CTX* openssl_evp_cipher_context_new()
|
|
||||||
{
|
|
||||||
auto* const handle = new EVP_CIPHER_CTX{};
|
|
||||||
|
|
||||||
if (handle != nullptr)
|
|
||||||
{
|
|
||||||
EVP_CIPHER_CTX_init(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void openssl_evp_cipher_context_free(EVP_CIPHER_CTX* handle)
|
|
||||||
{
|
|
||||||
if (handle == nullptr)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
EVP_CIPHER_CTX_cleanup(handle);
|
|
||||||
delete handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define EVP_CIPHER_CTX_new() openssl_evp_cipher_context_new()
|
|
||||||
#define EVP_CIPHER_CTX_free(x) openssl_evp_cipher_context_free((x))
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/***
|
|
||||||
****
|
|
||||||
***/
|
|
||||||
|
|
||||||
tr_x509_store_t tr_ssl_get_x509_store(tr_ssl_ctx_t handle)
|
tr_x509_store_t tr_ssl_get_x509_store(tr_ssl_ctx_t handle)
|
||||||
{
|
{
|
||||||
|
@ -288,9 +252,7 @@ void tr_x509_cert_free(tr_x509_cert_t handle)
|
||||||
X509_free(static_cast<X509*>(handle));
|
X509_free(static_cast<X509*>(handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
// --- rand
|
||||||
****
|
|
||||||
***/
|
|
||||||
|
|
||||||
bool tr_rand_buffer_crypto(void* buffer, size_t length)
|
bool tr_rand_buffer_crypto(void* buffer, size_t length)
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,7 +15,9 @@
|
||||||
#include "tr-macros.h"
|
#include "tr-macros.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
static char* tr_strvDup(std::string_view in)
|
namespace
|
||||||
|
{
|
||||||
|
[[nodiscard]] char* tr_strvdup(std::string_view in)
|
||||||
{
|
{
|
||||||
auto const n = std::size(in);
|
auto const n = std::size(in);
|
||||||
auto* const ret = new char[n + 1];
|
auto* const ret = new char[n + 1];
|
||||||
|
@ -23,6 +25,7 @@ static char* tr_strvDup(std::string_view in)
|
||||||
ret[n] = '\0';
|
ret[n] = '\0';
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void tr_error_free(tr_error* error)
|
void tr_error_free(tr_error* error)
|
||||||
{
|
{
|
||||||
|
@ -43,7 +46,7 @@ void tr_error_set(tr_error** error, int code, std::string_view message)
|
||||||
}
|
}
|
||||||
|
|
||||||
TR_ASSERT(*error == nullptr);
|
TR_ASSERT(*error == nullptr);
|
||||||
*error = new tr_error{ code, tr_strvDup(message) };
|
*error = new tr_error{ code, tr_strvdup(message) };
|
||||||
}
|
}
|
||||||
|
|
||||||
void tr_error_propagate(tr_error** new_error, tr_error** old_error)
|
void tr_error_propagate(tr_error** new_error, tr_error** old_error)
|
||||||
|
@ -86,7 +89,7 @@ void tr_error_prefix(tr_error** error, char const* prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* err = *error;
|
auto* err = *error;
|
||||||
auto* const new_message = tr_strvDup(fmt::format(FMT_STRING("{:s}{:s}"), prefix, err->message));
|
auto* const new_message = tr_strvdup(fmt::format(FMT_STRING("{:s}{:s}"), prefix, err->message));
|
||||||
delete[] err->message;
|
delete[] err->message;
|
||||||
err->message = new_message;
|
err->message = new_message;
|
||||||
}
|
}
|
||||||
|
|
1
libtransmission/makelog
Normal file
1
libtransmission/makelog
Normal file
|
@ -0,0 +1 @@
|
||||||
|
ninja: error: loading 'build.ninja': No such file or directory
|
|
@ -467,6 +467,16 @@ public:
|
||||||
pool_is_all_seeds_.reset();
|
pool_is_all_seeds_.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] peer_atom* get_existing_atom(tr_address const& addr)
|
||||||
|
{
|
||||||
|
auto const test = [&addr](auto const& atom)
|
||||||
|
{
|
||||||
|
return atom.addr == addr;
|
||||||
|
};
|
||||||
|
auto const it = std::find_if(std::begin(pool), std::end(pool), test);
|
||||||
|
return it != std::end(pool) ? &*it : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
Handshakes outgoing_handshakes;
|
Handshakes outgoing_handshakes;
|
||||||
|
|
||||||
uint16_t interested_count = 0;
|
uint16_t interested_count = 0;
|
||||||
|
@ -559,6 +569,12 @@ struct tr_peerMgr
|
||||||
void refillUpkeep() const;
|
void refillUpkeep() const;
|
||||||
void makeNewPeerConnections(size_t max);
|
void makeNewPeerConnections(size_t max);
|
||||||
|
|
||||||
|
[[nodiscard]] tr_swarm* get_existing_swarm(tr_sha1_digest_t const& hash) const
|
||||||
|
{
|
||||||
|
auto* const tor = session->torrents().get(hash);
|
||||||
|
return tor == nullptr ? nullptr : tor->swarm;
|
||||||
|
}
|
||||||
|
|
||||||
tr_session* const session;
|
tr_session* const session;
|
||||||
Handshakes incoming_handshakes;
|
Handshakes incoming_handshakes;
|
||||||
|
|
||||||
|
@ -623,24 +639,6 @@ tr_peer::~tr_peer()
|
||||||
***
|
***
|
||||||
**/
|
**/
|
||||||
|
|
||||||
static tr_swarm* getExistingSwarm(tr_peerMgr* manager, tr_sha1_digest_t const& hash)
|
|
||||||
{
|
|
||||||
auto* const tor = manager->session->torrents().get(hash);
|
|
||||||
|
|
||||||
return tor == nullptr ? nullptr : tor->swarm;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct peer_atom* getExistingAtom(tr_swarm const* cswarm, tr_address const& addr)
|
|
||||||
{
|
|
||||||
auto* swarm = const_cast<tr_swarm*>(cswarm);
|
|
||||||
auto const test = [&addr](auto const& atom)
|
|
||||||
{
|
|
||||||
return atom.addr == addr;
|
|
||||||
};
|
|
||||||
auto const it = std::find_if(std::begin(swarm->pool), std::end(swarm->pool), test);
|
|
||||||
return it != std::end(swarm->pool) ? &*it : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool peerIsInUse(tr_swarm const* swarm, struct peer_atom const* atom)
|
static bool peerIsInUse(tr_swarm const* swarm, struct peer_atom const* atom)
|
||||||
{
|
{
|
||||||
return atom->is_connected || swarm->outgoing_handshakes.count(atom->addr) != 0U ||
|
return atom->is_connected || swarm->outgoing_handshakes.count(atom->addr) != 0U ||
|
||||||
|
@ -701,7 +699,7 @@ static void atomSetSeed(tr_swarm* swarm, peer_atom& atom)
|
||||||
|
|
||||||
static bool tr_peerMgrPeerIsSeed(tr_torrent const* tor, tr_address const& addr)
|
static bool tr_peerMgrPeerIsSeed(tr_torrent const* tor, tr_address const& addr)
|
||||||
{
|
{
|
||||||
if (auto const* atom = getExistingAtom(tor->swarm, addr); atom != nullptr)
|
if (auto const* atom = tor->swarm->get_existing_atom(addr); atom != nullptr)
|
||||||
{
|
{
|
||||||
return atom->isSeed();
|
return atom->isSeed();
|
||||||
}
|
}
|
||||||
|
@ -711,7 +709,7 @@ static bool tr_peerMgrPeerIsSeed(tr_torrent const* tor, tr_address const& addr)
|
||||||
|
|
||||||
void tr_peerMgrSetUtpSupported(tr_torrent* tor, tr_address const& addr)
|
void tr_peerMgrSetUtpSupported(tr_torrent* tor, tr_address const& addr)
|
||||||
{
|
{
|
||||||
if (auto* const atom = getExistingAtom(tor->swarm, addr); atom != nullptr)
|
if (auto* const atom = tor->swarm->get_existing_atom(addr); atom != nullptr)
|
||||||
{
|
{
|
||||||
atom->flags |= ADDED_F_UTP_FLAGS;
|
atom->flags |= ADDED_F_UTP_FLAGS;
|
||||||
}
|
}
|
||||||
|
@ -719,7 +717,7 @@ void tr_peerMgrSetUtpSupported(tr_torrent* tor, tr_address const& addr)
|
||||||
|
|
||||||
void tr_peerMgrSetUtpFailed(tr_torrent* tor, tr_address const& addr, bool failed)
|
void tr_peerMgrSetUtpFailed(tr_torrent* tor, tr_address const& addr, bool failed)
|
||||||
{
|
{
|
||||||
if (auto* const atom = getExistingAtom(tor->swarm, addr); atom != nullptr)
|
if (auto* const atom = tor->swarm->get_existing_atom(addr); atom != nullptr)
|
||||||
{
|
{
|
||||||
atom->utp_failed = failed;
|
atom->utp_failed = failed;
|
||||||
}
|
}
|
||||||
|
@ -1046,7 +1044,7 @@ static struct peer_atom* ensureAtomExists(
|
||||||
TR_ASSERT(addr.is_valid());
|
TR_ASSERT(addr.is_valid());
|
||||||
TR_ASSERT(from < TR_PEER_FROM__MAX);
|
TR_ASSERT(from < TR_PEER_FROM__MAX);
|
||||||
|
|
||||||
struct peer_atom* a = getExistingAtom(s, addr);
|
struct peer_atom* a = s->get_existing_atom(addr);
|
||||||
|
|
||||||
if (a == nullptr)
|
if (a == nullptr)
|
||||||
{
|
{
|
||||||
|
@ -1097,7 +1095,7 @@ static bool on_handshake_done(tr_peerMgr* manager, tr_handshake::Result const& r
|
||||||
bool const ok = result.is_connected;
|
bool const ok = result.is_connected;
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
tr_swarm* const s = getExistingSwarm(manager, result.io->torrent_hash());
|
auto* const s = manager->get_existing_swarm(result.io->torrent_hash());
|
||||||
|
|
||||||
auto const [addr, port] = result.io->socket_address();
|
auto const [addr, port] = result.io->socket_address();
|
||||||
|
|
||||||
|
@ -1116,7 +1114,7 @@ static bool on_handshake_done(tr_peerMgr* manager, tr_handshake::Result const& r
|
||||||
{
|
{
|
||||||
if (s != nullptr)
|
if (s != nullptr)
|
||||||
{
|
{
|
||||||
struct peer_atom* atom = getExistingAtom(s, addr);
|
struct peer_atom* atom = s->get_existing_atom(addr);
|
||||||
|
|
||||||
if (atom != nullptr)
|
if (atom != nullptr)
|
||||||
{
|
{
|
||||||
|
|
|
@ -67,13 +67,11 @@
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "platform-quota.h"
|
#include "platform-quota.h"
|
||||||
|
|
||||||
/***
|
namespace
|
||||||
****
|
{
|
||||||
***/
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
|
|
||||||
static char const* getdev(std::string_view path)
|
[[nodiscard]] char const* getdev(std::string_view path)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_GETMNTENT
|
#ifdef HAVE_GETMNTENT
|
||||||
|
|
||||||
|
@ -141,7 +139,7 @@ static char const* getdev(std::string_view path)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static char const* getfstype(std::string_view device)
|
[[nodiscard]] char const* getfstype(std::string_view device)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_GETMNTENT
|
#ifdef HAVE_GETMNTENT
|
||||||
|
|
||||||
|
@ -209,7 +207,7 @@ static char const* getfstype(std::string_view device)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string getblkdev(std::string_view path)
|
std::string getblkdev(std::string_view path)
|
||||||
{
|
{
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
|
@ -235,7 +233,7 @@ extern "C"
|
||||||
#include <quota.h>
|
#include <quota.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
struct tr_disk_space getquota(char const* device)
|
[[nodiscard]] tr_disk_space getquota(char const* device)
|
||||||
{
|
{
|
||||||
struct quotahandle* qh;
|
struct quotahandle* qh;
|
||||||
struct quotakey qk;
|
struct quotakey qk;
|
||||||
|
@ -285,7 +283,7 @@ struct tr_disk_space getquota(char const* device)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static struct tr_disk_space getquota(char const* device)
|
[[nodiscard]] tr_disk_space getquota(char const* device)
|
||||||
{
|
{
|
||||||
#if defined(__DragonFly__)
|
#if defined(__DragonFly__)
|
||||||
struct ufs_dqblk dq = {};
|
struct ufs_dqblk dq = {};
|
||||||
|
@ -371,7 +369,7 @@ static struct tr_disk_space getquota(char const* device)
|
||||||
|
|
||||||
#ifdef HAVE_XQM
|
#ifdef HAVE_XQM
|
||||||
|
|
||||||
static struct tr_disk_space getxfsquota(char const* device)
|
[[nodiscard]] tr_disk_space getxfsquota(char const* device)
|
||||||
{
|
{
|
||||||
struct tr_disk_space disk_space = { -1, -1 };
|
struct tr_disk_space disk_space = { -1, -1 };
|
||||||
struct fs_disk_quota dq;
|
struct fs_disk_quota dq;
|
||||||
|
@ -410,7 +408,7 @@ static struct tr_disk_space getxfsquota(char const* device)
|
||||||
|
|
||||||
#endif /* _WIN32 */
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
static tr_disk_space getQuotaSpace([[maybe_unused]] tr_device_info const& info)
|
[[nodiscard]] tr_disk_space getQuotaSpace([[maybe_unused]] tr_device_info const& info)
|
||||||
{
|
{
|
||||||
struct tr_disk_space ret = { -1, -1 };
|
struct tr_disk_space ret = { -1, -1 };
|
||||||
|
|
||||||
|
@ -432,7 +430,7 @@ static tr_disk_space getQuotaSpace([[maybe_unused]] tr_device_info const& info)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct tr_disk_space getDiskSpace(char const* path)
|
[[nodiscard]] tr_disk_space getDiskSpace(char const* path)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
@ -468,6 +466,8 @@ static struct tr_disk_space getDiskSpace(char const* path)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
tr_device_info tr_device_info_create(std::string_view path)
|
tr_device_info tr_device_info_create(std::string_view path)
|
||||||
{
|
{
|
||||||
auto out = tr_device_info{};
|
auto out = tr_device_info{};
|
||||||
|
|
|
@ -45,13 +45,10 @@
|
||||||
|
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
|
|
||||||
/***
|
namespace
|
||||||
**** PATHS
|
{
|
||||||
***/
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
std::string win32_get_known_folder_ex(REFKNOWNFOLDERID folder_id, DWORD flags)
|
||||||
static std::string win32_get_known_folder_ex(REFKNOWNFOLDERID folder_id, DWORD flags)
|
|
||||||
{
|
{
|
||||||
if (PWSTR path; SHGetKnownFolderPath(folder_id, flags | KF_FLAG_DONT_UNEXPAND, nullptr, &path) == S_OK)
|
if (PWSTR path; SHGetKnownFolderPath(folder_id, flags | KF_FLAG_DONT_UNEXPAND, nullptr, &path) == S_OK)
|
||||||
{
|
{
|
||||||
|
@ -63,14 +60,13 @@ static std::string win32_get_known_folder_ex(REFKNOWNFOLDERID folder_id, DWORD f
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto win32_get_known_folder(REFKNOWNFOLDERID folder_id)
|
auto win32_get_known_folder(REFKNOWNFOLDERID folder_id)
|
||||||
{
|
{
|
||||||
return win32_get_known_folder_ex(folder_id, KF_FLAG_DONT_VERIFY);
|
return win32_get_known_folder_ex(folder_id, KF_FLAG_DONT_VERIFY);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static std::string getHomeDir()
|
std::string getHomeDir()
|
||||||
{
|
{
|
||||||
if (auto dir = tr_env_get_string("HOME"sv); !std::empty(dir))
|
if (auto dir = tr_env_get_string("HOME"sv); !std::empty(dir))
|
||||||
{
|
{
|
||||||
|
@ -100,7 +96,7 @@ static std::string getHomeDir()
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string xdgConfigHome()
|
std::string xdgConfigHome()
|
||||||
{
|
{
|
||||||
if (auto dir = tr_env_get_string("XDG_CONFIG_HOME"sv); !std::empty(dir))
|
if (auto dir = tr_env_get_string("XDG_CONFIG_HOME"sv); !std::empty(dir))
|
||||||
{
|
{
|
||||||
|
@ -110,6 +106,51 @@ static std::string xdgConfigHome()
|
||||||
return fmt::format("{:s}/.config"sv, getHomeDir());
|
return fmt::format("{:s}/.config"sv, getHomeDir());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string getXdgEntryFromUserDirs(std::string_view key)
|
||||||
|
{
|
||||||
|
auto content = std::vector<char>{};
|
||||||
|
if (auto const filename = fmt::format("{:s}/{:s}"sv, xdgConfigHome(), "user-dirs.dirs"sv);
|
||||||
|
!tr_sys_path_exists(filename) || !tr_loadFile(filename, content) || std::empty(content))
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// search for key="val" and extract val
|
||||||
|
auto const search = fmt::format(FMT_STRING("{:s}=\""), key);
|
||||||
|
auto begin = std::search(std::begin(content), std::end(content), std::begin(search), std::end(search));
|
||||||
|
if (begin == std::end(content))
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
std::advance(begin, std::size(search));
|
||||||
|
auto const end = std::find(begin, std::end(content), '"');
|
||||||
|
if (end == std::end(content))
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto val = std::string{ begin, end };
|
||||||
|
|
||||||
|
// if val contains "$HOME", replace that with getHomeDir()
|
||||||
|
auto constexpr Home = "$HOME"sv;
|
||||||
|
if (auto const it = std::search(std::begin(val), std::end(val), std::begin(Home), std::end(Home)); it != std::end(val))
|
||||||
|
{
|
||||||
|
val.replace(it, it + std::size(Home), getHomeDir());
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool isWebClientDir(std::string_view path)
|
||||||
|
{
|
||||||
|
auto const filename = tr_pathbuf{ path, '/', "index.html"sv };
|
||||||
|
bool const found = tr_sys_path_exists(filename);
|
||||||
|
tr_logAddTrace(fmt::format(FMT_STRING("Searching for web interface file '{:s}'"), filename));
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// ---
|
||||||
|
|
||||||
std::string tr_getDefaultConfigDir(std::string_view appname)
|
std::string tr_getDefaultConfigDir(std::string_view appname)
|
||||||
{
|
{
|
||||||
if (std::empty(appname))
|
if (std::empty(appname))
|
||||||
|
@ -149,40 +190,6 @@ size_t tr_getDefaultConfigDirToBuf(char const* appname, char* buf, size_t buflen
|
||||||
return tr_strvToBuf(tr_getDefaultConfigDir(appname != nullptr ? appname : ""), buf, buflen);
|
return tr_strvToBuf(tr_getDefaultConfigDir(appname != nullptr ? appname : ""), buf, buflen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string getXdgEntryFromUserDirs(std::string_view key)
|
|
||||||
{
|
|
||||||
auto content = std::vector<char>{};
|
|
||||||
if (auto const filename = fmt::format("{:s}/{:s}"sv, xdgConfigHome(), "user-dirs.dirs"sv);
|
|
||||||
!tr_sys_path_exists(filename) || !tr_loadFile(filename, content) || std::empty(content))
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// search for key="val" and extract val
|
|
||||||
auto const search = fmt::format(FMT_STRING("{:s}=\""), key);
|
|
||||||
auto begin = std::search(std::begin(content), std::end(content), std::begin(search), std::end(search));
|
|
||||||
if (begin == std::end(content))
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
std::advance(begin, std::size(search));
|
|
||||||
auto const end = std::find(begin, std::end(content), '"');
|
|
||||||
if (end == std::end(content))
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
auto val = std::string{ begin, end };
|
|
||||||
|
|
||||||
// if val contains "$HOME", replace that with getHomeDir()
|
|
||||||
auto constexpr Home = "$HOME"sv;
|
|
||||||
if (auto const it = std::search(std::begin(val), std::end(val), std::begin(Home), std::end(Home)); it != std::end(val))
|
|
||||||
{
|
|
||||||
val.replace(it, it + std::size(Home), getHomeDir());
|
|
||||||
}
|
|
||||||
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string tr_getDefaultDownloadDir()
|
std::string tr_getDefaultDownloadDir()
|
||||||
{
|
{
|
||||||
if (auto dir = getXdgEntryFromUserDirs("XDG_DOWNLOAD_DIR"sv); !std::empty(dir))
|
if (auto dir = getXdgEntryFromUserDirs("XDG_DOWNLOAD_DIR"sv); !std::empty(dir))
|
||||||
|
@ -209,17 +216,7 @@ size_t tr_getDefaultDownloadDirToBuf(char* buf, size_t buflen)
|
||||||
return tr_strvToBuf(tr_getDefaultDownloadDir(), buf, buflen);
|
return tr_strvToBuf(tr_getDefaultDownloadDir(), buf, buflen);
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
// ---
|
||||||
****
|
|
||||||
***/
|
|
||||||
|
|
||||||
static bool isWebClientDir(std::string_view path)
|
|
||||||
{
|
|
||||||
auto const filename = tr_pathbuf{ path, '/', "index.html"sv };
|
|
||||||
bool const found = tr_sys_path_exists(filename);
|
|
||||||
tr_logAddTrace(fmt::format(FMT_STRING("Searching for web interface file '{:s}'"), filename));
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string tr_getWebClientDir([[maybe_unused]] tr_session const* session)
|
std::string tr_getWebClientDir([[maybe_unused]] tr_session const* session)
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,7 +35,6 @@
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
enum class UpnpState
|
enum class UpnpState
|
||||||
{
|
{
|
||||||
Idle,
|
Idle,
|
||||||
|
@ -45,29 +44,6 @@ enum class UpnpState
|
||||||
WillMap, // next action is UPNP_AddPortMapping()
|
WillMap, // next action is UPNP_AddPortMapping()
|
||||||
WillUnmap // next action is UPNP_DeletePortMapping()
|
WillUnmap // next action is UPNP_DeletePortMapping()
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr auto portFwdState(UpnpState upnp_state, bool is_mapped)
|
|
||||||
{
|
|
||||||
switch (upnp_state)
|
|
||||||
{
|
|
||||||
case UpnpState::WillDiscover:
|
|
||||||
case UpnpState::Discovering:
|
|
||||||
return TR_PORT_UNMAPPED;
|
|
||||||
|
|
||||||
case UpnpState::WillMap:
|
|
||||||
return TR_PORT_MAPPING;
|
|
||||||
|
|
||||||
case UpnpState::WillUnmap:
|
|
||||||
return TR_PORT_UNMAPPING;
|
|
||||||
|
|
||||||
case UpnpState::Idle:
|
|
||||||
return is_mapped ? TR_PORT_MAPPED : TR_PORT_UNMAPPED;
|
|
||||||
|
|
||||||
default: // UpnpState::FAILED:
|
|
||||||
return TR_PORT_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
struct tr_upnp
|
struct tr_upnp
|
||||||
|
@ -102,25 +78,31 @@ struct tr_upnp
|
||||||
std::optional<std::future<UPNPDev*>> discover_future;
|
std::optional<std::future<UPNPDev*>> discover_future;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
namespace
|
||||||
***
|
|
||||||
**/
|
|
||||||
|
|
||||||
tr_upnp* tr_upnpInit()
|
|
||||||
{
|
{
|
||||||
return new tr_upnp();
|
constexpr auto port_fwd_state(UpnpState upnp_state, bool is_mapped)
|
||||||
|
{
|
||||||
|
switch (upnp_state)
|
||||||
|
{
|
||||||
|
case UpnpState::WillDiscover:
|
||||||
|
case UpnpState::Discovering:
|
||||||
|
return TR_PORT_UNMAPPED;
|
||||||
|
|
||||||
|
case UpnpState::WillMap:
|
||||||
|
return TR_PORT_MAPPING;
|
||||||
|
|
||||||
|
case UpnpState::WillUnmap:
|
||||||
|
return TR_PORT_UNMAPPING;
|
||||||
|
|
||||||
|
case UpnpState::Idle:
|
||||||
|
return is_mapped ? TR_PORT_MAPPED : TR_PORT_UNMAPPED;
|
||||||
|
|
||||||
|
default: // UpnpState::FAILED:
|
||||||
|
return TR_PORT_ERROR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void tr_upnpClose(tr_upnp* handle)
|
[[nodiscard]] UPNPDev* upnp_discover(int msec, char const* bindaddr)
|
||||||
{
|
|
||||||
delete handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*** Wrappers for miniupnpc functions
|
|
||||||
**/
|
|
||||||
|
|
||||||
static struct UPNPDev* tr_upnpDiscover(int msec, char const* bindaddr)
|
|
||||||
{
|
{
|
||||||
UPNPDev* ret = nullptr;
|
UPNPDev* ret = nullptr;
|
||||||
auto have_err = bool{};
|
auto have_err = bool{};
|
||||||
|
@ -148,7 +130,7 @@ static struct UPNPDev* tr_upnpDiscover(int msec, char const* bindaddr)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tr_upnpGetSpecificPortMappingEntry(tr_upnp const* handle, char const* proto)
|
[[nodiscard]] int get_specific_port_mapping_entry(tr_upnp const* handle, char const* proto)
|
||||||
{
|
{
|
||||||
auto int_client = std::array<char, 16>{};
|
auto int_client = std::array<char, 16>{};
|
||||||
auto int_port = std::array<char, 16>{};
|
auto int_port = std::array<char, 16>{};
|
||||||
|
@ -191,7 +173,7 @@ static int tr_upnpGetSpecificPortMappingEntry(tr_upnp const* handle, char const*
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tr_upnpAddPortMapping(tr_upnp const* handle, char const* proto, tr_port port, char const* desc)
|
[[nodiscard]] int upnp_add_port_mapping(tr_upnp const* handle, char const* proto, tr_port port, char const* desc)
|
||||||
{
|
{
|
||||||
int const old_errno = errno;
|
int const old_errno = errno;
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
@ -230,17 +212,13 @@ static int tr_upnpAddPortMapping(tr_upnp const* handle, char const* proto, tr_po
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tr_upnpDeletePortMapping(tr_upnp const* handle, char const* proto, tr_port port)
|
void tr_upnpDeletePortMapping(tr_upnp const* handle, char const* proto, tr_port port)
|
||||||
{
|
{
|
||||||
auto const port_str = fmt::format(FMT_STRING("{:d}"), port.host());
|
auto const port_str = fmt::format(FMT_STRING("{:d}"), port.host());
|
||||||
|
|
||||||
UPNP_DeletePortMapping(handle->urls.controlURL, handle->data.first.servicetype, port_str.c_str(), proto, nullptr);
|
UPNP_DeletePortMapping(handle->urls.controlURL, handle->data.first.servicetype, port_str.c_str(), proto, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
***
|
|
||||||
**/
|
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
UPNP_IGD_NONE = 0,
|
UPNP_IGD_NONE = 0,
|
||||||
|
@ -249,19 +227,32 @@ enum
|
||||||
UPNP_IGD_INVALID = 3
|
UPNP_IGD_INVALID = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
static auto* discoverThreadfunc(std::string bindaddr) // NOLINT performance-unnecessary-value-param
|
auto* discover_thread_func(std::string bindaddr) // NOLINT performance-unnecessary-value-param
|
||||||
{
|
{
|
||||||
// If multicastif is not NULL, it will be used instead of the default
|
// If multicastif is not NULL, it will be used instead of the default
|
||||||
// multicast interface for sending SSDP discover packets.
|
// multicast interface for sending SSDP discover packets.
|
||||||
char const* multicastif = std::empty(bindaddr) ? nullptr : bindaddr.c_str();
|
char const* multicastif = std::empty(bindaddr) ? nullptr : bindaddr.c_str();
|
||||||
return tr_upnpDiscover(2000, multicastif);
|
return upnp_discover(2000, multicastif);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static bool isFutureReady(std::future<T> const& future)
|
bool is_future_ready(std::future<T> const& future)
|
||||||
{
|
{
|
||||||
return future.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
|
return future.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
|
||||||
}
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// ---
|
||||||
|
|
||||||
|
tr_upnp* tr_upnpInit()
|
||||||
|
{
|
||||||
|
return new tr_upnp();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tr_upnpClose(tr_upnp* handle)
|
||||||
|
{
|
||||||
|
delete handle;
|
||||||
|
}
|
||||||
|
|
||||||
tr_port_forwarding_state tr_upnpPulse(tr_upnp* handle, tr_port port, bool is_enabled, bool do_port_check, std::string bindaddr)
|
tr_port_forwarding_state tr_upnpPulse(tr_upnp* handle, tr_port port, bool is_enabled, bool do_port_check, std::string bindaddr)
|
||||||
{
|
{
|
||||||
|
@ -269,7 +260,7 @@ tr_port_forwarding_state tr_upnpPulse(tr_upnp* handle, tr_port port, bool is_ena
|
||||||
{
|
{
|
||||||
TR_ASSERT(!handle->discover_future);
|
TR_ASSERT(!handle->discover_future);
|
||||||
|
|
||||||
auto task = std::packaged_task<UPNPDev*(std::string)>{ discoverThreadfunc };
|
auto task = std::packaged_task<UPNPDev*(std::string)>{ discover_thread_func };
|
||||||
handle->discover_future = task.get_future();
|
handle->discover_future = task.get_future();
|
||||||
handle->state = UpnpState::Discovering;
|
handle->state = UpnpState::Discovering;
|
||||||
|
|
||||||
|
@ -277,7 +268,7 @@ tr_port_forwarding_state tr_upnpPulse(tr_upnp* handle, tr_port port, bool is_ena
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_enabled && handle->state == UpnpState::Discovering && handle->discover_future &&
|
if (is_enabled && handle->state == UpnpState::Discovering && handle->discover_future &&
|
||||||
isFutureReady(*handle->discover_future))
|
is_future_ready(*handle->discover_future))
|
||||||
{
|
{
|
||||||
auto* const devlist = handle->discover_future->get();
|
auto* const devlist = handle->discover_future->get();
|
||||||
handle->discover_future.reset();
|
handle->discover_future.reset();
|
||||||
|
@ -309,8 +300,8 @@ tr_port_forwarding_state tr_upnpPulse(tr_upnp* handle, tr_port port, bool is_ena
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_enabled && handle->isMapped && do_port_check &&
|
if (is_enabled && handle->isMapped && do_port_check &&
|
||||||
((tr_upnpGetSpecificPortMappingEntry(handle, "TCP") != UPNPCOMMAND_SUCCESS) ||
|
((get_specific_port_mapping_entry(handle, "TCP") != UPNPCOMMAND_SUCCESS) ||
|
||||||
(tr_upnpGetSpecificPortMappingEntry(handle, "UDP") != UPNPCOMMAND_SUCCESS)))
|
(get_specific_port_mapping_entry(handle, "UDP") != UPNPCOMMAND_SUCCESS)))
|
||||||
{
|
{
|
||||||
tr_logAddInfo(fmt::format(_("Port {port} is not forwarded"), fmt::arg("port", handle->port.host())));
|
tr_logAddInfo(fmt::format(_("Port {port} is not forwarded"), fmt::arg("port", handle->port.host())));
|
||||||
handle->isMapped = false;
|
handle->isMapped = false;
|
||||||
|
@ -347,8 +338,8 @@ tr_port_forwarding_state tr_upnpPulse(tr_upnp* handle, tr_port port, bool is_ena
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto const desc = fmt::format(FMT_STRING("Transmission at {:d}"), port.host());
|
auto const desc = fmt::format(FMT_STRING("Transmission at {:d}"), port.host());
|
||||||
int const err_tcp = tr_upnpAddPortMapping(handle, "TCP", port, desc.c_str());
|
int const err_tcp = upnp_add_port_mapping(handle, "TCP", port, desc.c_str());
|
||||||
int const err_udp = tr_upnpAddPortMapping(handle, "UDP", port, desc.c_str());
|
int const err_udp = upnp_add_port_mapping(handle, "UDP", port, desc.c_str());
|
||||||
|
|
||||||
handle->isMapped = err_tcp == 0 || err_udp == 0;
|
handle->isMapped = err_tcp == 0 || err_udp == 0;
|
||||||
}
|
}
|
||||||
|
@ -374,5 +365,5 @@ tr_port_forwarding_state tr_upnpPulse(tr_upnp* handle, tr_port port, bool is_ena
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return portFwdState(handle->state, handle->isMapped);
|
return port_fwd_state(handle->state, handle->isMapped);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,9 @@
|
||||||
|
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
|
|
||||||
static void handle_sigchld(int /*i*/)
|
namespace
|
||||||
|
{
|
||||||
|
void handle_sigchld(int /*i*/)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
|
@ -40,7 +42,7 @@ static void handle_sigchld(int /*i*/)
|
||||||
/* FIXME: Call old handler, if any */
|
/* FIXME: Call old handler, if any */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_system_error(tr_error** error, int code, std::string_view what)
|
void set_system_error(tr_error** error, int code, std::string_view what)
|
||||||
{
|
{
|
||||||
if (error == nullptr)
|
if (error == nullptr)
|
||||||
{
|
{
|
||||||
|
@ -50,7 +52,7 @@ static void set_system_error(tr_error** error, int code, std::string_view what)
|
||||||
tr_error_set(error, code, fmt::format(FMT_STRING("{:s} failed: {:s} ({:d})"), what, tr_strerror(code), code));
|
tr_error_set(error, code, fmt::format(FMT_STRING("{:s} failed: {:s} ({:d})"), what, tr_strerror(code), code));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool tr_spawn_async_in_child(
|
[[nodiscard]] bool tr_spawn_async_in_child(
|
||||||
char const* const* cmd,
|
char const* const* cmd,
|
||||||
std::map<std::string_view, std::string_view> const& env,
|
std::map<std::string_view, std::string_view> const& env,
|
||||||
std::string_view work_dir)
|
std::string_view work_dir)
|
||||||
|
@ -82,7 +84,7 @@ static bool tr_spawn_async_in_child(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool tr_spawn_async_in_parent(int pipe_fd, tr_error** error)
|
[[nodiscard]] bool tr_spawn_async_in_parent(int pipe_fd, tr_error** error)
|
||||||
{
|
{
|
||||||
int child_errno = 0;
|
int child_errno = 0;
|
||||||
ssize_t count = 0;
|
ssize_t count = 0;
|
||||||
|
@ -114,6 +116,7 @@ static bool tr_spawn_async_in_parent(int pipe_fd, tr_error** error)
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
bool tr_spawn_async(
|
bool tr_spawn_async(
|
||||||
char const* const* cmd,
|
char const* const* cmd,
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -73,10 +73,12 @@ void tr_utpClose(tr_session* /*session*/)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
/* Greg says 50ms works for them. */
|
/* Greg says 50ms works for them. */
|
||||||
static auto constexpr UtpInterval = 50ms;
|
auto constexpr UtpInterval = 50ms;
|
||||||
|
|
||||||
static void utp_on_accept(tr_session* const session, UTPSocket* const utp_sock)
|
void utp_on_accept(tr_session* const session, UTPSocket* const utp_sock)
|
||||||
{
|
{
|
||||||
auto from_storage = sockaddr_storage{};
|
auto from_storage = sockaddr_storage{};
|
||||||
auto* const from = (struct sockaddr*)&from_storage;
|
auto* const from = (struct sockaddr*)&from_storage;
|
||||||
|
@ -102,7 +104,7 @@ static void utp_on_accept(tr_session* const session, UTPSocket* const utp_sock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void utp_send_to(
|
void utp_send_to(
|
||||||
tr_session const* const ss,
|
tr_session const* const ss,
|
||||||
uint8_t const* const buf,
|
uint8_t const* const buf,
|
||||||
size_t const buflen,
|
size_t const buflen,
|
||||||
|
@ -112,7 +114,7 @@ static void utp_send_to(
|
||||||
ss->udp_core_->sendto(buf, buflen, to, tolen);
|
ss->udp_core_->sendto(buf, buflen, to, tolen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64 utp_callback(utp_callback_arguments* args)
|
uint64 utp_callback(utp_callback_arguments* args)
|
||||||
{
|
{
|
||||||
auto* const session = static_cast<tr_session*>(utp_context_get_userdata(args->context));
|
auto* const session = static_cast<tr_session*>(utp_context_get_userdata(args->context));
|
||||||
|
|
||||||
|
@ -139,7 +141,7 @@ static uint64 utp_callback(utp_callback_arguments* args)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void reset_timer(tr_session* session)
|
void reset_timer(tr_session* session)
|
||||||
{
|
{
|
||||||
auto interval = std::chrono::milliseconds{};
|
auto interval = std::chrono::milliseconds{};
|
||||||
auto const random_percent = tr_rand_int(1000U) / 1000.0;
|
auto const random_percent = tr_rand_int(1000U) / 1000.0;
|
||||||
|
@ -167,7 +169,7 @@ static void reset_timer(tr_session* session)
|
||||||
session->utp_timer->startSingleShot(interval);
|
session->utp_timer->startSingleShot(interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void timer_callback(void* vsession)
|
void timer_callback(void* vsession)
|
||||||
{
|
{
|
||||||
auto* session = static_cast<tr_session*>(vsession);
|
auto* session = static_cast<tr_session*>(vsession);
|
||||||
|
|
||||||
|
@ -177,6 +179,7 @@ static void timer_callback(void* vsession)
|
||||||
utp_check_timeouts(session->utp_context);
|
utp_check_timeouts(session->utp_context);
|
||||||
reset_timer(session);
|
reset_timer(session);
|
||||||
}
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void tr_utpInit(tr_session* session)
|
void tr_utpInit(tr_session* session)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue