forked from premiere/premiere-libtorrent
implemented support for BEP 51 (#1652)
This commit is contained in:
parent
147d996160
commit
48ef3b6bf7
|
@ -151,6 +151,7 @@ set(kademlia_sources
|
|||
get_peers
|
||||
get_item
|
||||
ed25519
|
||||
sample_infohashes
|
||||
)
|
||||
|
||||
# -- ed25519 --
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
* implemented support for DHT infohash indexing, BEP51
|
||||
* removed deprecated support for file_base in file_storage
|
||||
* added support for running separate DHT nodes on each network interface
|
||||
* added support for establishing UTP connections on any network interface
|
||||
|
|
1
Jamfile
1
Jamfile
|
@ -693,6 +693,7 @@ KADEMLIA_SOURCES =
|
|||
get_item
|
||||
put_data
|
||||
ed25519
|
||||
sample_infohashes
|
||||
;
|
||||
|
||||
ED25519_SOURCES =
|
||||
|
|
|
@ -235,4 +235,5 @@ nobase_include_HEADERS = \
|
|||
kademlia/ed25519.hpp \
|
||||
kademlia/item.hpp \
|
||||
kademlia/get_item.hpp \
|
||||
kademlia/sample_infohashes.hpp \
|
||||
kademlia/get_peers.hpp
|
||||
|
|
|
@ -2590,11 +2590,56 @@ namespace libtorrent {
|
|||
virtual std::string message() const override;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT dht_sample_infohashes_alert final : alert
|
||||
{
|
||||
dht_sample_infohashes_alert(aux::stack_allocator& alloc
|
||||
, udp::endpoint const& endp
|
||||
, time_duration interval
|
||||
, int num
|
||||
, std::vector<sha1_hash> const& samples
|
||||
, std::vector<std::pair<sha1_hash, udp::endpoint>> const& nodes);
|
||||
|
||||
static const int static_category = alert::dht_operation_notification;
|
||||
TORRENT_DEFINE_ALERT(dht_sample_infohashes_alert, 93)
|
||||
|
||||
virtual std::string message() const override;
|
||||
|
||||
aux::noexcept_movable<udp::endpoint> endpoint;
|
||||
|
||||
time_duration const interval;
|
||||
|
||||
// This field indicates how many infohash keys are currently in the node's storage.
|
||||
// If the value is larger than the number of returned samples it indicates that the
|
||||
// indexer may obtain additional samples after waiting out the interval.
|
||||
int const num_infohashes;
|
||||
|
||||
int num_samples() const;
|
||||
std::vector<sha1_hash> samples() const;
|
||||
|
||||
// The total number of nodes returned by ``nodes()``.
|
||||
int num_nodes() const;
|
||||
|
||||
// This is the set of more DHT nodes returned by the request.
|
||||
//
|
||||
// The information is included so that indexing nodes can perform a keyspace
|
||||
// traversal with a single RPC per node by adjusting the target value for each RPC.
|
||||
std::vector<std::pair<sha1_hash, udp::endpoint>> nodes() const;
|
||||
|
||||
private:
|
||||
std::reference_wrapper<aux::stack_allocator> m_alloc;
|
||||
int const m_num_samples;
|
||||
aux::allocation_slot m_samples_idx;
|
||||
int m_v4_num_nodes = 0;
|
||||
int m_v6_num_nodes = 0;
|
||||
aux::allocation_slot m_v4_nodes_idx;
|
||||
aux::allocation_slot m_v6_nodes_idx;
|
||||
};
|
||||
|
||||
#undef TORRENT_DEFINE_ALERT_IMPL
|
||||
#undef TORRENT_DEFINE_ALERT
|
||||
#undef TORRENT_DEFINE_ALERT_PRIO
|
||||
|
||||
enum { num_alert_types = 93 }; // this enum represents "max_alert_index" + 1
|
||||
enum { num_alert_types = 94 }; // this enum represents "max_alert_index" + 1
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -403,6 +403,7 @@ namespace aux {
|
|||
void dht_announce(sha1_hash const& info_hash, int port = 0, int flags = 0);
|
||||
|
||||
void dht_live_nodes(sha1_hash const& nid);
|
||||
void dht_sample_infohashes(udp::endpoint const& ep, sha1_hash const& target);
|
||||
|
||||
void dht_direct_request(udp::endpoint const& ep, entry& e
|
||||
, void* userdata = nullptr);
|
||||
|
|
|
@ -100,6 +100,11 @@ namespace libtorrent { namespace dht {
|
|||
void announce(sha1_hash const& ih, int listen_port, int flags
|
||||
, std::function<void(std::vector<tcp::endpoint> const&)> f);
|
||||
|
||||
void sample_infohashes(udp::endpoint const& ep, sha1_hash const& target
|
||||
, std::function<void(time_duration
|
||||
, int, std::vector<sha1_hash>
|
||||
, std::vector<std::pair<sha1_hash, udp::endpoint>>)> f);
|
||||
|
||||
void get_item(sha1_hash const& target
|
||||
, std::function<void(item const&)> cb);
|
||||
|
||||
|
|
|
@ -149,6 +149,11 @@ public:
|
|||
, std::function<void(item const&, int)> f
|
||||
, std::function<void(item&)> data_cb);
|
||||
|
||||
void sample_infohashes(udp::endpoint const& ep, sha1_hash const& target
|
||||
, std::function<void(time_duration
|
||||
, int, std::vector<sha1_hash>
|
||||
, std::vector<std::pair<sha1_hash, udp::endpoint>>)> f);
|
||||
|
||||
bool verify_token(string_view token, sha1_hash const& info_hash
|
||||
, udp::endpoint const& addr) const;
|
||||
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2017, Arvid Norberg, Alden Torres
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef TORRENT_SAMPLE_INFOHASHES_HPP
|
||||
#define TORRENT_SAMPLE_INFOHASHES_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <libtorrent/kademlia/traversal_algorithm.hpp>
|
||||
#include <libtorrent/time.hpp>
|
||||
|
||||
namespace libtorrent { namespace dht
|
||||
{
|
||||
|
||||
class sample_infohashes final : public traversal_algorithm
|
||||
{
|
||||
public:
|
||||
|
||||
using data_callback = std::function<void(time_duration
|
||||
, int, std::vector<sha1_hash>
|
||||
, std::vector<std::pair<sha1_hash, udp::endpoint>>)>;
|
||||
|
||||
sample_infohashes(node& dht_node
|
||||
, node_id const& target
|
||||
, data_callback const& dcallback);
|
||||
|
||||
virtual char const* name() const override;
|
||||
|
||||
void got_samples(time_duration interval
|
||||
, int num, std::vector<sha1_hash> samples
|
||||
, std::vector<std::pair<sha1_hash, udp::endpoint>> nodes);
|
||||
|
||||
protected:
|
||||
|
||||
data_callback m_data_callback;
|
||||
};
|
||||
|
||||
class sample_infohashes_observer final : public traversal_observer
|
||||
{
|
||||
public:
|
||||
|
||||
sample_infohashes_observer(std::shared_ptr<traversal_algorithm> const& algorithm
|
||||
, udp::endpoint const& ep, node_id const& id);
|
||||
|
||||
virtual void reply(msg const&) override;
|
||||
};
|
||||
|
||||
}} // namespace libtorrent::dht
|
||||
|
||||
#endif // TORRENT_SAMPLE_INFOHASHES_HPP
|
|
@ -235,12 +235,15 @@ namespace libtorrent {
|
|||
dht_get_out,
|
||||
dht_put_in,
|
||||
dht_put_out,
|
||||
dht_sample_infohashes_in,
|
||||
dht_sample_infohashes_out,
|
||||
|
||||
dht_invalid_announce,
|
||||
dht_invalid_get_peers,
|
||||
dht_invalid_find_node,
|
||||
dht_invalid_put,
|
||||
dht_invalid_get,
|
||||
dht_invalid_sample_infohashes,
|
||||
|
||||
// uTP counters.
|
||||
utp_packet_loss,
|
||||
|
|
|
@ -452,6 +452,14 @@ namespace libtorrent {
|
|||
// posted, regardless of the alert mask.
|
||||
void dht_live_nodes(sha1_hash const& nid);
|
||||
|
||||
// Query the DHT node specified by ``ep`` to retrieve a sample of the
|
||||
// infohashes that the node currently have in their storage.
|
||||
// The ``target`` is included for iterative lookups so that indexing nodes
|
||||
// can perform a keyspace traversal with a single RPC per node by adjusting
|
||||
// the target value for each RPC. It has no effect on the returned sample value.
|
||||
// The result is posted as a ``dht_sample_infohashes_alert``.
|
||||
void dht_sample_infohashes(udp::endpoint const& ep, sha1_hash const& target);
|
||||
|
||||
// Send an arbitrary DHT request directly to the specified endpoint. This
|
||||
// function is intended for use by plugins. When a response is received
|
||||
// or the request times out, a dht_direct_response_alert will be posted
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace libtorrent {
|
|||
namespace detail {
|
||||
|
||||
template <class Proto>
|
||||
size_t address_size(Proto p)
|
||||
std::size_t address_size(Proto p)
|
||||
{
|
||||
TORRENT_UNUSED(p);
|
||||
#if TORRENT_USE_IPV6
|
||||
|
|
|
@ -22,6 +22,7 @@ KADEMLIA_SOURCES = \
|
|||
kademlia/get_item.cpp \
|
||||
kademlia/item.cpp \
|
||||
kademlia/ed25519.cpp \
|
||||
kademlia/sample_infohashes.cpp \
|
||||
../ed25519/src/add_scalar.cpp \
|
||||
../ed25519/src/fe.cpp \
|
||||
../ed25519/src/ge.cpp \
|
||||
|
|
|
@ -2275,4 +2275,62 @@ namespace {
|
|||
return stats_header;
|
||||
}
|
||||
|
||||
dht_sample_infohashes_alert::dht_sample_infohashes_alert(aux::stack_allocator& alloc
|
||||
, udp::endpoint const& endp
|
||||
, time_duration _interval
|
||||
, int _num
|
||||
, std::vector<sha1_hash> const& samples
|
||||
, std::vector<std::pair<sha1_hash, udp::endpoint>> const& nodes)
|
||||
: endpoint(endp)
|
||||
, interval(_interval)
|
||||
, num_infohashes(_num)
|
||||
, m_alloc(alloc)
|
||||
, m_num_samples(aux::numeric_cast<int>(samples.size()))
|
||||
{
|
||||
m_samples_idx = alloc.allocate(m_num_samples * 20);
|
||||
|
||||
char *ptr = alloc.ptr(m_samples_idx);
|
||||
std::memcpy(ptr, samples.data(), samples.size() * 20);
|
||||
|
||||
std::tie(m_v4_num_nodes, m_v4_nodes_idx, m_v6_num_nodes, m_v6_nodes_idx)
|
||||
= write_nodes(alloc, nodes);
|
||||
}
|
||||
|
||||
std::string dht_sample_infohashes_alert::message() const
|
||||
{
|
||||
char msg[200];
|
||||
std::snprintf(msg, sizeof(msg)
|
||||
, "incoming dht sample_infohashes reply from: %s, samples %d"
|
||||
, print_endpoint(endpoint).c_str(), m_num_samples);
|
||||
return msg;
|
||||
}
|
||||
|
||||
int dht_sample_infohashes_alert::num_samples() const
|
||||
{
|
||||
return m_num_samples;
|
||||
}
|
||||
|
||||
std::vector<sha1_hash> dht_sample_infohashes_alert::samples() const
|
||||
{
|
||||
aux::vector<sha1_hash> samples;
|
||||
samples.resize(m_num_samples);
|
||||
|
||||
const char *ptr = m_alloc.get().ptr(m_samples_idx);
|
||||
std::memcpy(samples.data(), ptr, samples.size() * 20);
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
int dht_sample_infohashes_alert::num_nodes() const
|
||||
{
|
||||
return m_v4_num_nodes + m_v6_num_nodes;
|
||||
}
|
||||
|
||||
std::vector<std::pair<sha1_hash, udp::endpoint>> dht_sample_infohashes_alert::nodes() const
|
||||
{
|
||||
return read_nodes(m_alloc.get()
|
||||
, m_v4_num_nodes, m_v4_nodes_idx
|
||||
, m_v6_num_nodes, m_v6_nodes_idx);
|
||||
}
|
||||
|
||||
} // namespace libtorrent
|
||||
|
|
|
@ -322,6 +322,20 @@ namespace libtorrent { namespace dht {
|
|||
n.second.dht.announce(ih, listen_port, flags, f);
|
||||
}
|
||||
|
||||
void dht_tracker::sample_infohashes(udp::endpoint const& ep, sha1_hash const& target
|
||||
, std::function<void(time_duration
|
||||
, int, std::vector<sha1_hash>
|
||||
, std::vector<std::pair<sha1_hash, udp::endpoint>>)> f)
|
||||
{
|
||||
for (auto& n : m_nodes)
|
||||
{
|
||||
if (ep.protocol() != (n.first->get_external_address().is_v4() ? udp::v4() : udp::v6()))
|
||||
continue;
|
||||
n.second.dht.sample_infohashes(ep, target, f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct get_immutable_item_ctx
|
||||
|
|
|
@ -64,6 +64,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/kademlia/get_item.hpp"
|
||||
#include "libtorrent/kademlia/msg.hpp"
|
||||
#include <libtorrent/kademlia/put_data.hpp>
|
||||
#include <libtorrent/kademlia/sample_infohashes.hpp>
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
|
@ -565,6 +566,38 @@ void node::put_item(public_key const& pk, std::string const& salt
|
|||
ta->start();
|
||||
}
|
||||
|
||||
void node::sample_infohashes(udp::endpoint const& ep, sha1_hash const& target
|
||||
, std::function<void(time_duration
|
||||
, int, std::vector<sha1_hash>
|
||||
, std::vector<std::pair<sha1_hash, udp::endpoint>>)> f)
|
||||
{
|
||||
#ifndef TORRENT_DISABLE_LOGGING
|
||||
if (m_observer != nullptr && m_observer->should_log(dht_logger::node))
|
||||
{
|
||||
m_observer->log(dht_logger::node, "starting sample_infohashes for [ node: %s, target: %s ]"
|
||||
, print_endpoint(ep).c_str(), aux::to_hex(target).c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
// not an actual traversal
|
||||
auto ta = std::make_shared<dht::sample_infohashes>(*this, node_id(), std::move(f));
|
||||
|
||||
auto o = m_rpc.allocate_observer<sample_infohashes_observer>(ta, ep, node_id());
|
||||
if (!o) return;
|
||||
#if TORRENT_USE_ASSERTS
|
||||
o->m_in_constructor = false;
|
||||
#endif
|
||||
|
||||
entry e;
|
||||
|
||||
e["q"] = "sample_infohashes";
|
||||
e["a"]["target"] = target;
|
||||
|
||||
stats_counters().inc_stats_counter(counters::dht_sample_infohashes_out);
|
||||
|
||||
m_rpc.invoke(e, ep, o);
|
||||
}
|
||||
|
||||
struct ping_observer : observer
|
||||
{
|
||||
ping_observer(
|
||||
|
@ -1104,6 +1137,30 @@ void node::incoming_request(msg const& m, entry& e)
|
|||
, reply);
|
||||
}
|
||||
}
|
||||
else if (query == "sample_infohashes")
|
||||
{
|
||||
key_desc_t const msg_desc[] = {
|
||||
{"target", bdecode_node::string_t, 20, 0},
|
||||
{"want", bdecode_node::list_t, 0, key_desc_t::optional},
|
||||
};
|
||||
|
||||
bdecode_node msg_keys[2];
|
||||
if (!verify_message(arg_ent, msg_desc, msg_keys, error_string))
|
||||
{
|
||||
m_counters.inc_stats_counter(counters::dht_invalid_sample_infohashes);
|
||||
incoming_error(e, error_string);
|
||||
return;
|
||||
}
|
||||
|
||||
m_counters.inc_stats_counter(counters::dht_sample_infohashes_in);
|
||||
sha1_hash const target(msg_keys[0].string_ptr());
|
||||
|
||||
// TODO: keep the returned value to pass as a limit
|
||||
// to write_nodes_entries when implemented
|
||||
m_storage.get_infohashes_sample(reply);
|
||||
|
||||
write_nodes_entries(target, msg_keys[1], reply);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we don't recognize the message but there's a
|
||||
|
|
|
@ -43,6 +43,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <libtorrent/kademlia/dht_observer.hpp>
|
||||
#include <libtorrent/kademlia/direct_request.hpp>
|
||||
#include <libtorrent/kademlia/get_item.hpp>
|
||||
#include <libtorrent/kademlia/sample_infohashes.hpp>
|
||||
|
||||
#include <libtorrent/socket_io.hpp> // for print_endpoint
|
||||
#include <libtorrent/hasher.hpp>
|
||||
|
@ -143,6 +144,7 @@ using observer_storage = aux::aligned_union<1
|
|||
, get_item_observer
|
||||
, get_peers_observer
|
||||
, obfuscated_get_peers_observer
|
||||
, sample_infohashes_observer
|
||||
, null_observer
|
||||
, traversal_observer>::type;
|
||||
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2017, Arvid Norberg, Alden Torres
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <libtorrent/kademlia/sample_infohashes.hpp>
|
||||
#include <libtorrent/kademlia/dht_observer.hpp>
|
||||
#include <libtorrent/kademlia/node.hpp>
|
||||
#include <libtorrent/kademlia/io.hpp>
|
||||
#include <libtorrent/performance_counters.hpp>
|
||||
#include <libtorrent/aux_/numeric_cast.hpp>
|
||||
#include <libtorrent/socket_io.hpp>
|
||||
|
||||
namespace libtorrent { namespace dht
|
||||
{
|
||||
|
||||
sample_infohashes::sample_infohashes(node& dht_node
|
||||
, node_id const& target
|
||||
, data_callback const& dcallback)
|
||||
: traversal_algorithm(dht_node, target)
|
||||
, m_data_callback(dcallback) {}
|
||||
|
||||
char const* sample_infohashes::name() const { return "sample_infohashes"; }
|
||||
|
||||
void sample_infohashes::got_samples(time_duration interval
|
||||
, int num, std::vector<sha1_hash> samples
|
||||
, std::vector<std::pair<sha1_hash, udp::endpoint>> nodes)
|
||||
{
|
||||
if (m_data_callback)
|
||||
{
|
||||
m_data_callback(interval, num, std::move(samples), std::move(nodes));
|
||||
m_data_callback = nullptr;
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
sample_infohashes_observer::sample_infohashes_observer(
|
||||
std::shared_ptr<traversal_algorithm> const& algorithm
|
||||
, udp::endpoint const& ep, node_id const& id)
|
||||
: traversal_observer(algorithm, ep, id) {}
|
||||
|
||||
void sample_infohashes_observer::reply(msg const& m)
|
||||
{
|
||||
bdecode_node r = m.message.dict_find_dict("r");
|
||||
if (!r)
|
||||
{
|
||||
#ifndef TORRENT_DISABLE_LOGGING
|
||||
get_observer()->log(dht_logger::traversal, "[%u] missing response dict"
|
||||
, algorithm()->id());
|
||||
#endif
|
||||
timeout();
|
||||
return;
|
||||
}
|
||||
|
||||
// look for nodes
|
||||
std::vector<std::pair<sha1_hash, udp::endpoint>> nodes;
|
||||
udp const protocol = algorithm()->get_node().protocol();
|
||||
int const protocol_size = int(detail::address_size(protocol));
|
||||
char const* nodes_key = algorithm()->get_node().protocol_nodes_key();
|
||||
bdecode_node const n = r.dict_find_string(nodes_key);
|
||||
if (n)
|
||||
{
|
||||
char const* ptr = n.string_ptr();
|
||||
char const* end = ptr + n.string_length();
|
||||
|
||||
while (end - ptr >= 20 + protocol_size + 2)
|
||||
{
|
||||
node_endpoint nep = read_node_endpoint(protocol, ptr);
|
||||
nodes.emplace_back(nep.id, nep.ep);
|
||||
}
|
||||
}
|
||||
|
||||
std::int64_t const interval = r.dict_find_int_value("interval", -1);
|
||||
if (interval < 0 || interval > 21600) // TODO: put constant in a common place
|
||||
{
|
||||
#ifndef TORRENT_DISABLE_LOGGING
|
||||
get_observer()->log(dht_logger::traversal, "[%u] wrong or missing interval value"
|
||||
, algorithm()->id());
|
||||
#endif
|
||||
timeout();
|
||||
return;
|
||||
}
|
||||
|
||||
std::int64_t const num = r.dict_find_int_value("num", -1);
|
||||
if (num < 0 || num > std::numeric_limits<int>::max())
|
||||
{
|
||||
#ifndef TORRENT_DISABLE_LOGGING
|
||||
get_observer()->log(dht_logger::traversal, "[%u] wrong or missing num value"
|
||||
, algorithm()->id());
|
||||
#endif
|
||||
timeout();
|
||||
return;
|
||||
}
|
||||
|
||||
bdecode_node samples = r.dict_find_string("samples");
|
||||
if (samples && (samples.string_length() % 20 == 0))
|
||||
{
|
||||
std::vector<sha1_hash> v(aux::numeric_cast<std::size_t>(samples.string_length() / 20));
|
||||
std::memcpy(v.data(), samples.string_ptr(), v.size() * 20);
|
||||
|
||||
static_cast<sample_infohashes*>(algorithm())->got_samples(
|
||||
seconds(interval), int(num), std::move(v), std::move(nodes));
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef TORRENT_DISABLE_LOGGING
|
||||
get_observer()->log(dht_logger::traversal, "[%u] wrong or missing samples value"
|
||||
, algorithm()->id());
|
||||
#endif
|
||||
timeout();
|
||||
}
|
||||
|
||||
traversal_observer::reply(m);
|
||||
// this is necessary to play nice with
|
||||
// observer::abort(), observer::done() and observer::timeout()
|
||||
flags |= flag_done;
|
||||
}
|
||||
|
||||
}} // namespace libtorrent::dht
|
|
@ -623,6 +623,16 @@ namespace {
|
|||
#endif
|
||||
}
|
||||
|
||||
void session_handle::dht_sample_infohashes(udp::endpoint const& ep, sha1_hash const& target)
|
||||
{
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
async_call(&session_impl::dht_sample_infohashes, ep, target);
|
||||
#else
|
||||
TORRENT_UNUSED(ep);
|
||||
TORRENT_UNUSED(target);
|
||||
#endif
|
||||
}
|
||||
|
||||
void session_handle::dht_direct_request(udp::endpoint const& ep, entry const& e, void* userdata)
|
||||
{
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
|
|
|
@ -5954,6 +5954,19 @@ namespace {
|
|||
m_alerts.emplace_alert<dht_live_nodes_alert>(nid, nodes);
|
||||
}
|
||||
|
||||
void session_impl::dht_sample_infohashes(udp::endpoint const& ep, sha1_hash const& target)
|
||||
{
|
||||
if (!m_dht) return;
|
||||
m_dht->sample_infohashes(ep, target, [this, &ep](time_duration interval
|
||||
, int num, std::vector<sha1_hash> samples
|
||||
, std::vector<std::pair<sha1_hash, udp::endpoint>> nodes)
|
||||
{
|
||||
if (m_alerts.should_post<dht_sample_infohashes_alert>())
|
||||
m_alerts.emplace_alert<dht_sample_infohashes_alert>(ep
|
||||
, interval, num, samples, nodes);
|
||||
});
|
||||
}
|
||||
|
||||
void session_impl::dht_direct_request(udp::endpoint const& ep, entry& e, void* userdata)
|
||||
{
|
||||
if (!m_dht) return;
|
||||
|
|
|
@ -472,6 +472,8 @@ namespace {
|
|||
METRIC(dht, dht_get_out)
|
||||
METRIC(dht, dht_put_in)
|
||||
METRIC(dht, dht_put_out)
|
||||
METRIC(dht, dht_sample_infohashes_in)
|
||||
METRIC(dht, dht_sample_infohashes_out)
|
||||
|
||||
// the number of failed incoming DHT requests by kind of request
|
||||
METRIC(dht, dht_invalid_announce)
|
||||
|
@ -479,6 +481,7 @@ namespace {
|
|||
METRIC(dht, dht_invalid_find_node)
|
||||
METRIC(dht, dht_invalid_put)
|
||||
METRIC(dht, dht_invalid_get)
|
||||
METRIC(dht, dht_invalid_sample_infohashes)
|
||||
|
||||
// uTP counters. Each counter represents the number of time each event
|
||||
// has occurred.
|
||||
|
|
|
@ -160,10 +160,11 @@ TORRENT_TEST(alerts_types)
|
|||
TEST_ALERT_TYPE(session_error_alert, 90, 0, alert::error_notification);
|
||||
TEST_ALERT_TYPE(dht_live_nodes_alert, 91, 0, alert::dht_notification);
|
||||
TEST_ALERT_TYPE(session_stats_header_alert, 92, 0, alert::stats_notification);
|
||||
TEST_ALERT_TYPE(dht_sample_infohashes_alert, 93, 0, alert::dht_operation_notification);
|
||||
|
||||
#undef TEST_ALERT_TYPE
|
||||
|
||||
TEST_EQUAL(num_alert_types, 93);
|
||||
TEST_EQUAL(num_alert_types, 94);
|
||||
TEST_EQUAL(num_alert_types, count_alert_types);
|
||||
}
|
||||
|
||||
|
@ -263,3 +264,60 @@ TORRENT_TEST(session_stats_alert)
|
|||
TEST_CHECK(v != nullptr);
|
||||
TEST_CHECK(v->message().find("session stats (") != std::string::npos);
|
||||
}
|
||||
|
||||
TORRENT_TEST(dht_sample_infohashes_alert)
|
||||
{
|
||||
alert_manager mgr(1, dht_sample_infohashes_alert::static_category);
|
||||
|
||||
TEST_EQUAL(mgr.should_post<dht_sample_infohashes_alert>(), true);
|
||||
|
||||
udp::endpoint const endpoint = rand_udp_ep();
|
||||
time_duration const interval = seconds(10);
|
||||
int const num = 100;
|
||||
sha1_hash const h1 = rand_hash();
|
||||
sha1_hash const h2 = rand_hash();
|
||||
sha1_hash const h3 = rand_hash();
|
||||
sha1_hash const h4 = rand_hash();
|
||||
sha1_hash const h5 = rand_hash();
|
||||
|
||||
std::vector<sha1_hash> const v = {h1, h2, h3, h4, h5};
|
||||
|
||||
sha1_hash const nh1 = rand_hash();
|
||||
sha1_hash const nh2 = rand_hash();
|
||||
sha1_hash const nh3 = rand_hash();
|
||||
sha1_hash const nh4 = rand_hash();
|
||||
sha1_hash const nh5 = rand_hash();
|
||||
udp::endpoint const nep1 = rand_udp_ep(rand_v4);
|
||||
udp::endpoint const nep2 = rand_udp_ep(rand_v4);
|
||||
udp::endpoint const nep3 = rand_udp_ep(rand_v4);
|
||||
#if TORRENT_USE_IPV6
|
||||
udp::endpoint const nep4 = rand_udp_ep(rand_v6);
|
||||
udp::endpoint const nep5 = rand_udp_ep(rand_v6);
|
||||
#else
|
||||
udp::endpoint const nep4 = rand_udp_ep(rand_v4);
|
||||
udp::endpoint const nep5 = rand_udp_ep(rand_v4);
|
||||
#endif
|
||||
std::vector<std::pair<sha1_hash, udp::endpoint>> nv;
|
||||
nv.emplace_back(nh1, nep1);
|
||||
nv.emplace_back(nh2, nep2);
|
||||
nv.emplace_back(nh3, nep3);
|
||||
nv.emplace_back(nh4, nep4);
|
||||
nv.emplace_back(nh5, nep5);
|
||||
|
||||
mgr.emplace_alert<dht_sample_infohashes_alert>(endpoint, interval, num, v, nv);
|
||||
|
||||
auto const* a = alert_cast<dht_sample_infohashes_alert>(mgr.wait_for_alert(seconds(0)));
|
||||
TEST_CHECK(a != nullptr);
|
||||
|
||||
TEST_EQUAL(a->endpoint, endpoint);
|
||||
TEST_CHECK(a->interval == interval);
|
||||
TEST_EQUAL(a->num_infohashes, num);
|
||||
TEST_EQUAL(a->num_samples(), 5);
|
||||
TEST_CHECK(a->samples() == v);
|
||||
TEST_EQUAL(a->num_nodes(), 5);
|
||||
|
||||
auto nodes = a->nodes();
|
||||
std::sort(nv.begin(), nv.end());
|
||||
std::sort(nodes.begin(), nodes.end());
|
||||
TEST_CHECK(nv == nodes);
|
||||
}
|
||||
|
|
|
@ -246,6 +246,19 @@ struct msg_args
|
|||
msg_args& peers(std::set<tcp::endpoint> const& p)
|
||||
{ if (!p.empty()) a.dict()["values"] = write_peers(p); return *this; }
|
||||
|
||||
msg_args& interval(time_duration interval)
|
||||
{ a["interval"] = total_seconds(interval); return *this; }
|
||||
|
||||
msg_args& num(int num)
|
||||
{ a["num"] = num; return *this; }
|
||||
|
||||
msg_args& samples(std::vector<sha1_hash> const& samples)
|
||||
{
|
||||
a["samples"] = span<char const>(
|
||||
reinterpret_cast<char const*>(samples.data()), samples.size() * 20);
|
||||
return *this;
|
||||
}
|
||||
|
||||
entry a;
|
||||
};
|
||||
|
||||
|
@ -600,6 +613,15 @@ dht::key_desc_t const put_mutable_item_desc[] = {
|
|||
{"v", bdecode_node::none_t, 0, key_desc_t::last_child},
|
||||
};
|
||||
|
||||
dht::key_desc_t const sample_infohashes_desc[] = {
|
||||
{"y", bdecode_node::string_t, 1, 0},
|
||||
{"t", bdecode_node::string_t, 2, 0},
|
||||
{"q", bdecode_node::string_t, 17, 0},
|
||||
{"a", bdecode_node::dict_t, 0, key_desc_t::parse_children},
|
||||
{"id", bdecode_node::string_t, 20, 0},
|
||||
{"target", bdecode_node::string_t, 20, key_desc_t::last_child},
|
||||
};
|
||||
|
||||
void print_state(std::ostream& os, routing_table const& table)
|
||||
{
|
||||
std::vector<char> buf(2048);
|
||||
|
@ -3593,6 +3615,74 @@ TORRENT_TEST(dht_state)
|
|||
TEST_CHECK(s2.nodes.empty());
|
||||
}
|
||||
|
||||
TORRENT_TEST(sample_infohashes)
|
||||
{
|
||||
dht_test_setup t(rand_udp_ep());
|
||||
bdecode_node response;
|
||||
|
||||
g_sent_packets.clear();
|
||||
|
||||
udp::endpoint initial_node = rand_udp_ep();
|
||||
t.dht_node.m_table.add_node(node_entry{initial_node});
|
||||
|
||||
// nodes
|
||||
sha1_hash const h1 = rand_hash();
|
||||
sha1_hash const h2 = rand_hash();
|
||||
udp::endpoint const ep1 = rand_udp_ep(rand_v4);
|
||||
udp::endpoint const ep2 = rand_udp_ep(rand_v4);
|
||||
|
||||
t.dht_node.sample_infohashes(initial_node, items[0].target,
|
||||
[h1, ep1, h2, ep2](time_duration interval, int num
|
||||
, std::vector<sha1_hash> samples
|
||||
, std::vector<std::pair<sha1_hash, udp::endpoint>> const& nodes)
|
||||
{
|
||||
TEST_EQUAL(total_seconds(interval), 10);
|
||||
TEST_EQUAL(num, 2);
|
||||
TEST_EQUAL(samples.size(), 1);
|
||||
TEST_EQUAL(samples[0], to_hash("1000000000000000000000000000000000000001"));
|
||||
TEST_EQUAL(nodes.size(), 2);
|
||||
TEST_EQUAL(nodes[0].first, h1);
|
||||
TEST_EQUAL(nodes[0].second, ep1);
|
||||
TEST_EQUAL(nodes[1].first, h2);
|
||||
TEST_EQUAL(nodes[1].second, ep2);
|
||||
});
|
||||
|
||||
TEST_EQUAL(g_sent_packets.size(), 1);
|
||||
if (g_sent_packets.empty()) return;
|
||||
TEST_EQUAL(g_sent_packets.front().first, initial_node);
|
||||
|
||||
lazy_from_entry(g_sent_packets.front().second, response);
|
||||
bdecode_node sample_infohashes_keys[6];
|
||||
bool const ret = verify_message(response
|
||||
, sample_infohashes_desc, sample_infohashes_keys, t.error_string);
|
||||
if (ret)
|
||||
{
|
||||
TEST_EQUAL(sample_infohashes_keys[0].string_value(), "q");
|
||||
TEST_EQUAL(sample_infohashes_keys[2].string_value(), "sample_infohashes");
|
||||
TEST_EQUAL(sample_infohashes_keys[5].string_value(), items[0].target.to_string());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::printf(" invalid sample_infohashes request: %s\n", print_entry(response).c_str());
|
||||
TEST_ERROR(t.error_string);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<node_entry> nodes;
|
||||
nodes.emplace_back(h1, ep1);
|
||||
nodes.emplace_back(h2, ep2);
|
||||
|
||||
g_sent_packets.clear();
|
||||
send_dht_response(t.dht_node, response, initial_node
|
||||
, msg_args()
|
||||
.interval(seconds(10))
|
||||
.num(2)
|
||||
.samples({to_hash("1000000000000000000000000000000000000001")})
|
||||
.nodes(nodes));
|
||||
|
||||
TEST_CHECK(g_sent_packets.empty());
|
||||
}
|
||||
|
||||
// TODO: test obfuscated_get_peers
|
||||
|
||||
#else
|
||||
|
|
Loading…
Reference in New Issue