diff --git a/ChangeLog b/ChangeLog index 3aa1414db..7d74b05ce 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * add DHT routing table affinity for BEP 42 nodes * add torrent_info constructor overloads to control torrent file limits * feature to disable DHT, PEX and LSD per torrent * fix issue where trackers from magnet links were not included in create_torrent() diff --git a/fuzzers/src/dht_node.cpp b/fuzzers/src/dht_node.cpp index e3cbdaf5a..a14d18693 100644 --- a/fuzzers/src/dht_node.cpp +++ b/fuzzers/src/dht_node.cpp @@ -42,7 +42,7 @@ POSSIBILITY OF SUCH DAMAGE. using namespace lt; #if LIBTORRENT_VERSION_NUM >= 10200 -dht::dht_settings sett; +dht::settings sett; dht::dht_state state; std::unique_ptr dht_storage(dht::dht_default_storage_constructor(sett)); #else diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index b6ca37460..5c9610718 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -788,6 +788,7 @@ namespace aux { void update_dht(); void update_count_slow(); void update_dht_bootstrap_nodes(); + void update_dht_settings(); void update_socket_buffer_size(); void update_dht_announce_interval(); @@ -1088,7 +1089,7 @@ namespace aux { #ifndef TORRENT_DISABLE_DHT std::unique_ptr m_dht_storage; std::shared_ptr m_dht; - dht::dht_settings m_dht_settings; + dht::settings m_dht_settings; dht::dht_storage_constructor_type m_dht_storage_constructor = dht::dht_default_storage_constructor; diff --git a/include/libtorrent/kademlia/dht_settings.hpp b/include/libtorrent/kademlia/dht_settings.hpp index 60ccfc158..f1232edb3 100644 --- a/include/libtorrent/kademlia/dht_settings.hpp +++ b/include/libtorrent/kademlia/dht_settings.hpp @@ -163,7 +163,15 @@ namespace dht { // same as the tcp interface int service_port = 0; #endif + }; + // internal + struct settings : dht_settings + { + // when this is true, nodes whose IDs are derived from their source IP + // according to BEP 42 (http://bittorrent.org/beps/bep_0042.html) are + // preferred in the routing table. + bool prefer_verified_node_ids = true; }; diff --git a/include/libtorrent/kademlia/dht_tracker.hpp b/include/libtorrent/kademlia/dht_tracker.hpp index 604791b5f..d0a5267f8 100644 --- a/include/libtorrent/kademlia/dht_tracker.hpp +++ b/include/libtorrent/kademlia/dht_tracker.hpp @@ -56,7 +56,7 @@ namespace libtorrent { } namespace libtorrent { namespace dht { - struct dht_settings; + struct settings; struct TORRENT_EXTRA_EXPORT dht_tracker final : socket_manager @@ -69,7 +69,7 @@ namespace libtorrent { namespace dht { dht_tracker(dht_observer* observer , io_service& ios , send_fun_t const& send_fun - , dht_settings const& settings + , dht::settings const& settings , counters& cnt , dht_storage_interface& storage , dht_state&& state); @@ -78,7 +78,7 @@ namespace libtorrent { namespace dht { dht_tracker(dht_observer* observer , io_service& ios , send_fun_t const& send_fun - , dht_settings const& settings + , dht::settings const& settings , counters& cnt , dht_storage_interface& storage , dht_state const& state) = delete; @@ -159,7 +159,7 @@ namespace libtorrent { namespace dht { { tracker_node(io_service& ios , aux::listen_socket_handle const& s, socket_manager* sock - , dht_settings const& settings + , dht::settings const& settings , node_id const& nid , dht_observer* observer, counters& cnt , get_foreign_node_t get_foreign_node @@ -202,7 +202,7 @@ namespace libtorrent { namespace dht { deadline_timer m_key_refresh_timer; deadline_timer m_refresh_timer; - dht_settings const& m_settings; + dht::settings const& m_settings; bool m_running; diff --git a/include/libtorrent/kademlia/node.hpp b/include/libtorrent/kademlia/node.hpp index 7db7c2dd4..34f105b82 100644 --- a/include/libtorrent/kademlia/node.hpp +++ b/include/libtorrent/kademlia/node.hpp @@ -91,7 +91,7 @@ class TORRENT_EXTRA_EXPORT node { public: node(aux::listen_socket_handle const& sock, socket_manager* sock_man - , dht_settings const& settings + , dht::settings const& settings , node_id const& nid , dht_observer* observer, counters& cnt , get_foreign_node_t get_foreign_node @@ -198,7 +198,7 @@ public: void status(libtorrent::session_status& s); #endif - dht_settings const& settings() const { return m_settings; } + dht::settings const& settings() const { return m_settings; } counters& stats_counters() const { return m_counters; } dht_observer* observer() const { return m_observer; } @@ -224,7 +224,7 @@ private: bool lookup_peers(sha1_hash const& info_hash, entry& reply , bool noseed, bool scrape, address const& requester) const; - dht_settings const& m_settings; + dht::settings const& m_settings; std::mutex m_mutex; diff --git a/include/libtorrent/kademlia/node_entry.hpp b/include/libtorrent/kademlia/node_entry.hpp index 323b41446..fc8ca3917 100644 --- a/include/libtorrent/kademlia/node_entry.hpp +++ b/include/libtorrent/kademlia/node_entry.hpp @@ -38,6 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/address.hpp" #include "libtorrent/union_endpoint.hpp" #include "libtorrent/time.hpp" // for time_point +#include "libtorrent/aux_/time.hpp" // for time_now namespace libtorrent { namespace dht { @@ -46,7 +47,7 @@ struct TORRENT_EXTRA_EXPORT node_entry node_entry(node_id const& id_, udp::endpoint const& ep, int roundtriptime = 0xffff , bool pinged = false); explicit node_entry(udp::endpoint const& ep); - node_entry(); + node_entry() = default; void update_rtt(int new_rtt); bool pinged() const { return timeout_count != 0xff; } @@ -59,23 +60,32 @@ struct TORRENT_EXTRA_EXPORT node_entry address addr() const { return endpoint.address(); } int port() const { return endpoint.port; } + // compares which node_entry is "better". Smaller is better + bool operator<(node_entry const& rhs) const + { + return std::make_tuple(!verified, rtt) < std::make_tuple(!rhs.verified, rhs.rtt); + } + #ifndef TORRENT_DISABLE_LOGGING - time_point first_seen; + time_point first_seen = aux::time_now(); #endif // the time we last received a response for a request to this peer - time_point last_queried; + time_point last_queried = min_time(); - node_id id; + node_id id{nullptr}; union_endpoint endpoint; // the average RTT of this node - std::uint16_t rtt; + std::uint16_t rtt = 0xffff; // the number of times this node has failed to // respond in a row - std::uint8_t timeout_count; + // 0xff is a special value to indicate we have not pinged this node yet + std::uint8_t timeout_count = 0xff; + + bool verified = false; }; } } // namespace libtorrent::dht diff --git a/include/libtorrent/kademlia/routing_table.hpp b/include/libtorrent/kademlia/routing_table.hpp index f14801247..f210c941f 100644 --- a/include/libtorrent/kademlia/routing_table.hpp +++ b/include/libtorrent/kademlia/routing_table.hpp @@ -49,7 +49,7 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { namespace dht { -struct dht_settings; +struct settings; struct dht_logger; using bucket_t = aux::vector; @@ -112,7 +112,13 @@ struct ip_set // the most times is replaced. If none of the nodes in the // bucket has failed, then it is put in the replacement // cache (just like in the paper). +// * The routing table bucket sizes are larger towards the "top" of the routing +// table. This is to get closer to the target in fewer round-trips. +// * Nodes with lower RTT are preferred and may replace nodes with higher RTT +// * Nodes that are "verified" (i.e. use a node-ID derived from their IP) are +// preferred and may replace nodes that are not verified. +TORRENT_EXTRA_EXPORT bool mostly_verified_nodes(bucket_t const&); TORRENT_EXTRA_EXPORT bool compare_ip_cidr(address const& lhs, address const& rhs); class TORRENT_EXTRA_EXPORT routing_table @@ -125,7 +131,7 @@ public: routing_table(node_id const& id, udp proto , int bucket_size - , dht_settings const& settings + , dht::settings const& settings , dht_logger* log); routing_table(routing_table const&) = delete; @@ -266,7 +272,7 @@ private: void prune_empty_bucket(); - dht_settings const& m_settings; + dht::settings const& m_settings; // (k-bucket, replacement cache) pairs // the first entry is the bucket the furthest diff --git a/include/libtorrent/settings_pack.hpp b/include/libtorrent/settings_pack.hpp index fb637f68d..423467580 100644 --- a/include/libtorrent/settings_pack.hpp +++ b/include/libtorrent/settings_pack.hpp @@ -729,6 +729,11 @@ namespace libtorrent { // changes are taken in consideration. enable_ip_notifier, + // when this is true, nodes whose IDs are derived from their source IP + // according to BEP 42 (http://bittorrent.org/beps/bep_0042.html) are + // preferred in the routing table. + dht_prefer_verified_node_ids, + max_bool_setting_internal }; diff --git a/simulation/setup_dht.cpp b/simulation/setup_dht.cpp index 9b2481d64..739a205a8 100644 --- a/simulation/setup_dht.cpp +++ b/simulation/setup_dht.cpp @@ -56,8 +56,6 @@ using namespace lt; namespace { - lt::time_point start_time; - // this is the IP address assigned to node 'idx' asio::ip::address addr_from_int(int /* idx */) { @@ -90,7 +88,7 @@ namespace { struct dht_node final : lt::dht::socket_manager { - dht_node(sim::simulation& sim, lt::dht::dht_settings const& sett, lt::counters& cnt + dht_node(sim::simulation& sim, lt::dht::settings const& sett, lt::counters& cnt , int const idx, std::uint32_t const flags) : m_io_service(sim, (flags & dht_network::bind_ipv6) ? addr6_from_int(idx) : addr_from_int(idx)) , m_dht_storage(lt::dht::dht_default_storage_constructor(sett)) diff --git a/simulation/setup_dht.hpp b/simulation/setup_dht.hpp index 4f5c1c46d..e87c0e8f2 100644 --- a/simulation/setup_dht.hpp +++ b/simulation/setup_dht.hpp @@ -66,7 +66,7 @@ private: // used for all the nodes in the network lt::counters m_cnt; - lt::dht::dht_settings m_sett; + lt::dht::settings m_sett; std::list m_nodes; }; diff --git a/simulation/test_dht.cpp b/simulation/test_dht.cpp index 059f0b47f..906d5cf23 100644 --- a/simulation/test_dht.cpp +++ b/simulation/test_dht.cpp @@ -44,7 +44,6 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/deadline_timer.hpp" #include "libtorrent/socket_io.hpp" #include "setup_swarm.hpp" -#include "setup_dht.hpp" #include "libtorrent/kademlia/ed25519.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/kademlia/item.hpp" diff --git a/simulation/test_dht_rate_limit.cpp b/simulation/test_dht_rate_limit.cpp index a16873def..667ae5dab 100644 --- a/simulation/test_dht_rate_limit.cpp +++ b/simulation/test_dht_rate_limit.cpp @@ -112,7 +112,7 @@ TORRENT_TEST(dht_rate_limit) ls->local_endpoint = tcp::endpoint(address_v4::from_string("40.30.20.10"), 8888); error_code ec; sock.bind(udp::endpoint(address_v4::from_string("40.30.20.10"), 8888), ec); - dht::dht_settings dhtsett; + dht::settings dhtsett; dhtsett.block_ratelimit = 100000; // disable the DOS blocker dhtsett.ignore_dark_internet = false; dhtsett.upload_rate_limit = 400; @@ -239,7 +239,7 @@ TORRENT_TEST(dht_delete_socket) ls->external_address.cast_vote(address_v4::from_string("40.30.20.10") , lt::aux::session_interface::source_dht, lt::address()); ls->local_endpoint = tcp::endpoint(address_v4::from_string("40.30.20.10"), 8888); - dht::dht_settings dhtsett; + dht::settings dhtsett; counters cnt; dht::dht_state state; std::unique_ptr dht_storage(dht::dht_default_storage_constructor(dhtsett)); diff --git a/src/kademlia/dht_tracker.cpp b/src/kademlia/dht_tracker.cpp index f2b2b9e2a..b9a6808c3 100644 --- a/src/kademlia/dht_tracker.cpp +++ b/src/kademlia/dht_tracker.cpp @@ -85,7 +85,7 @@ namespace libtorrent { namespace dht { dht_tracker::dht_tracker(dht_observer* observer , io_service& ios , send_fun_t const& send_fun - , dht_settings const& settings + , dht::settings const& settings , counters& cnt , dht_storage_interface& storage , dht_state&& state) @@ -576,7 +576,7 @@ namespace libtorrent { namespace dht { dht_tracker::tracker_node::tracker_node(io_service& ios , aux::listen_socket_handle const& s, socket_manager* sock - , dht_settings const& settings + , dht::settings const& settings , node_id const& nid , dht_observer* observer, counters& cnt , get_foreign_node_t get_foreign_node diff --git a/src/kademlia/node.cpp b/src/kademlia/node.cpp index 9a5f3954a..89260cb59 100644 --- a/src/kademlia/node.cpp +++ b/src/kademlia/node.cpp @@ -106,7 +106,7 @@ void incoming_error(entry& e, char const* msg, int error_code = 203) } // anonymous namespace node::node(aux::listen_socket_handle const& sock, socket_manager* sock_man - , dht_settings const& settings + , dht::settings const& settings , node_id const& nid , dht_observer* observer , counters& cnt diff --git a/src/kademlia/node_entry.cpp b/src/kademlia/node_entry.cpp index 1158fd151..797308343 100644 --- a/src/kademlia/node_entry.cpp +++ b/src/kademlia/node_entry.cpp @@ -43,34 +43,13 @@ namespace libtorrent { namespace dht { , endpoint(ep) , rtt(roundtriptime & 0xffff) , timeout_count(pinged ? 0 : 0xff) + , verified(verify_id(id_, ep.address())) { -#ifndef TORRENT_DISABLE_LOGGING - first_seen = aux::time_now(); -#endif } node_entry::node_entry(udp::endpoint const& ep) - : last_queried(min_time()) - , id(nullptr) - , endpoint(ep) - , rtt(0xffff) - , timeout_count(0xff) - { -#ifndef TORRENT_DISABLE_LOGGING - first_seen = aux::time_now(); -#endif - } - - node_entry::node_entry() - : last_queried(min_time()) - , id(nullptr) - , rtt(0xffff) - , timeout_count(0xff) - { -#ifndef TORRENT_DISABLE_LOGGING - first_seen = aux::time_now(); -#endif - } + : endpoint(ep) + {} void node_entry::update_rtt(int const new_rtt) { diff --git a/src/kademlia/routing_table.cpp b/src/kademlia/routing_table.cpp index ddd45952e..5b31b76bc 100644 --- a/src/kademlia/routing_table.cpp +++ b/src/kademlia/routing_table.cpp @@ -68,7 +68,7 @@ namespace { container.erase(i); } - bool verify_node_address(dht_settings const& settings + bool verify_node_address(dht::settings const& settings , node_id const& id, address const& addr) { // only when the node_id pass the verification, add it to routing table. @@ -100,8 +100,16 @@ void ip_set::erase(address const& addr) erase_one(m_ip4s, addr.to_v4().to_bytes()); } +bool mostly_verified_nodes(bucket_t const& b) +{ + int const num_verified = static_cast(std::count_if(b.begin(), b.end() + , [](node_entry const& e) { return e.verified; })); + if (num_verified == 0 && b.size() > 0) return false; + return num_verified >= static_cast(b.size()) * 2 / 3; +} + routing_table::routing_table(node_id const& id, udp proto, int bucket_size - , dht_settings const& settings + , dht::settings const& settings , dht_logger* log) : #ifndef TORRENT_DISABLE_LOGGING @@ -654,6 +662,8 @@ ip_ok: // split the last bucket bool const can_split = (std::next(i) == m_buckets.end() && m_buckets.size() < 159) + && (m_settings.prefer_verified_node_ids == false + || (e.verified && mostly_verified_nodes(b))) && e.confirmed() && (i == m_buckets.begin() || std::prev(i)->live_nodes.size() > 1); @@ -742,7 +752,7 @@ ip_ok: { j = *std::max_element(nodes.begin(), nodes.end() , [](bucket_t::iterator lhs, bucket_t::iterator rhs) - { return lhs->rtt < rhs->rtt; }); + { return *lhs < *rhs; }); } else { @@ -788,7 +798,7 @@ ip_ok: auto k = std::max_element(nodes.begin(), nodes.end() , [](bucket_t::iterator lhs, bucket_t::iterator rhs) - { return lhs->rtt < rhs->rtt; }); + { return *lhs < *rhs; }); // in this case, we would really rather replace the node even if // the new node has higher RTT, because it fills a new prefix that we otherwise @@ -798,24 +808,24 @@ ip_ok: } else { - j = std::max_element(b.begin(), b.end() - , [](node_entry const& lhs, node_entry const& rhs) - { return lhs.rtt < rhs.rtt; }); + j = std::max_element(b.begin(), b.end()); } } - if (j != b.end() && (force_replace || j->rtt > e.rtt)) + if (j != b.end() && (force_replace || e < *j)) { - m_ips.erase(j->addr()); - *j = e; - m_ips.insert(e.addr()); #ifndef TORRENT_DISABLE_LOGGING if (m_log != nullptr && m_log->should_log(dht_logger::routing_table)) { - m_log->log(dht_logger::routing_table, "replacing node with higher RTT: %s %s" - , aux::to_hex(e.id).c_str(), print_address(e.addr()).c_str()); + m_log->log(dht_logger::routing_table, "replacing node with better one: %s %s %s %dms vs. %s %dms" + , aux::to_hex(e.id).c_str(), print_address(e.addr()).c_str() + , e.verified ? "verified" : "not-verified", e.rtt + , j->verified ? "verified" : "not-verified", j->rtt); } #endif + m_ips.erase(j->addr()); + *j = e; + m_ips.insert(e.addr()); return node_added; } // in order to keep lookup times small, prefer nodes with low RTTs @@ -1056,7 +1066,7 @@ void routing_table::heard_about(node_id const& id, udp::endpoint const& ep) // top of its bucket. the return value indicates if the table needs a refresh. // if true, the node should refresh the table (i.e. do a find_node on its own // id) -bool routing_table::node_seen(node_id const& id, udp::endpoint const& ep, int rtt) +bool routing_table::node_seen(node_id const& id, udp::endpoint const& ep, int const rtt) { return verify_node_address(m_settings, id, ep.address()) && add_node(node_entry(id, ep, rtt, true)); } diff --git a/src/session_impl.cpp b/src/session_impl.cpp index fc459c4ac..92072c7db 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -648,7 +648,7 @@ namespace aux { settings = e->dict_find_dict("dht"); if (settings) { - m_dht_settings = dht::read_dht_settings(settings); + static_cast(m_dht_settings) = dht::read_dht_settings(settings); } } @@ -5346,6 +5346,16 @@ namespace aux { #endif } + void session_impl::update_dht_settings() + { +#ifndef TORRENT_DISABLE_DHT + bool const prefer_verified_nodes = m_settings.get_bool( + settings_pack::dht_prefer_verified_node_ids); + + m_dht_settings.prefer_verified_node_ids = prefer_verified_nodes; +#endif + } + void session_impl::update_count_slow() { error_code ec; @@ -5817,7 +5827,7 @@ namespace aux { void session_impl::set_dht_settings(dht::dht_settings const& settings) { - m_dht_settings = settings; + static_cast(m_dht_settings) = settings; } void session_impl::set_dht_state(dht::dht_state&& state) diff --git a/src/settings_pack.cpp b/src/settings_pack.cpp index 3efb0b3b1..1c25512fe 100644 --- a/src/settings_pack.cpp +++ b/src/settings_pack.cpp @@ -207,6 +207,7 @@ constexpr int CLOSE_FILE_INTERVAL = 0; SET(auto_sequential, true, &session_impl::update_auto_sequential), SET(proxy_tracker_connections, true, nullptr), SET(enable_ip_notifier, true, &session_impl::update_ip_notifier), + SET(dht_prefer_verified_node_ids, true, &session_impl::update_dht_settings), }}); aux::array const int_settings diff --git a/test/test.cpp b/test/test.cpp index dfd425f5a..4122f649a 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -50,7 +50,7 @@ void report_failure(char const* err, char const* file, int line) { char buf[500]; std::snprintf(buf, sizeof(buf), "\x1b[41m***** %s:%d \"%s\" *****\x1b[0m\n", file, line, err); - std::fprintf(stderr, "\n%s\n", buf); + std::printf("\n%s\n", buf); failure_strings.push_back(buf); ++_g_test_failures; } diff --git a/test/test_dht.cpp b/test/test_dht.cpp index 5450a1257..f7249ca05 100644 --- a/test/test_dht.cpp +++ b/test/test_dht.cpp @@ -563,9 +563,9 @@ struct obs : dht::dht_observer #endif }; -dht::dht_settings test_settings() +dht::settings test_settings() { - dht::dht_settings sett; + dht::settings sett; sett.max_torrents = 4; sett.max_dht_items = 4; sett.enforce_node_id = false; @@ -585,7 +585,7 @@ struct dht_test_setup dht_storage->update_node_ids({node_id::min()}); } - dht::dht_settings sett; + dht::settings sett; mock_socket s; std::shared_ptr ls; obs observer; @@ -1585,18 +1585,14 @@ void test_routing_table(address(&rand_addr)()) bdecode_node response; // test kademlia routing table - dht::dht_settings s; + dht::settings s; s.extended_routing_table = false; // s.restrict_routing_ips = false; - node_id id = to_hash("3123456789abcdef01232456789abcdef0123456"); + node_id const nid = to_hash("3123456789abcdef01232456789abcdef0123456"); const int bucket_size = 10; - dht::routing_table table(id, t.source.protocol(), bucket_size, s, &t.observer); - std::vector nodes; + dht::routing_table table(nid, t.source.protocol(), bucket_size, s, &t.observer); TEST_EQUAL(std::get<0>(table.size()), 0); - node_id tmp = id; - node_id diff = to_hash("15764f7459456a9453f8719b09547c11d5f34061"); - address node_addr; address node_near_addr; if (is_v6(t.source)) @@ -1611,9 +1607,11 @@ void test_routing_table(address(&rand_addr)()) } // test a node with the same IP:port changing ID - add_and_replace(tmp, diff); + node_id const tmp = generate_id_impl(node_addr, 1); table.node_seen(tmp, udp::endpoint(node_addr, 4), 10); - table.find_node(id, nodes, 0, 10); + + std::vector nodes; + table.find_node(nid, nodes, 0, 10); TEST_EQUAL(table.bucket_size(0), 1); TEST_EQUAL(std::get<0>(table.size()), 1); TEST_EQUAL(nodes.size(), 1); @@ -1654,7 +1652,7 @@ void test_routing_table(address(&rand_addr)()) // test adding the same node ID again with a different IP (should be ignored) table.node_seen(tmp, udp::endpoint(node_addr, 5), 10); - table.find_node(id, nodes, 0, 10); + table.find_node(nid, nodes, 0, 10); TEST_EQUAL(table.bucket_size(0), 1); if (!nodes.empty()) { @@ -1667,7 +1665,7 @@ void test_routing_table(address(&rand_addr)()) // very close to the current one (should be ignored) // if restrict_routing_ips == true table.node_seen(tmp, udp::endpoint(node_near_addr, 5), 10); - table.find_node(id, nodes, 0, 10); + table.find_node(nid, nodes, 0, 10); TEST_EQUAL(table.bucket_size(0), 1); if (!nodes.empty()) { @@ -1677,9 +1675,11 @@ void test_routing_table(address(&rand_addr)()) } // test adding the same IP:port again with a new node ID (should remove the node) - add_and_replace(tmp, diff); - table.node_seen(tmp, udp::endpoint(node_addr, 4), 10); - table.find_node(id, nodes, 0, 10); + { + auto const id = generate_id_impl(node_addr, 2); + table.node_seen(id, udp::endpoint(node_addr, 4), 10); + table.find_node(id, nodes, 0, 10); + } TEST_EQUAL(table.bucket_size(0), 0); TEST_EQUAL(nodes.size(), 0); @@ -1687,17 +1687,22 @@ void test_routing_table(address(&rand_addr)()) init_rand_address(); - add_and_replace(tmp, diff); - table.node_seen(id, rand_udp_ep(rand_addr), 10); + { + auto const ep = rand_udp_ep(rand_addr); + auto const id = generate_id(ep.address()); + table.node_seen(id, ep, 10); + } nodes.clear(); for (int i = 0; i < 7000; ++i) { - table.node_seen(tmp, rand_udp_ep(rand_addr), 20 + (tmp[19] & 0xff)); - add_and_replace(tmp, diff); + auto const ep = rand_udp_ep(rand_addr); + auto const id = generate_id(ep.address()); + table.node_seen(id, ep, 20 + (id[19] & 0xff)); } std::printf("active buckets: %d\n", table.num_active_buckets()); - TEST_EQUAL(table.num_active_buckets(), 10); + TEST_CHECK(table.num_active_buckets() == 10 + || table.num_active_buckets() == 11); TEST_CHECK(std::get<0>(table.size()) >= 10 * 10); //TODO: 2 test num_global_nodes //TODO: 2 test need_refresh @@ -1710,10 +1715,12 @@ void test_routing_table(address(&rand_addr)()) std::vector temp; - aux::random_bytes(tmp); - table.find_node(tmp, temp, 0, int(nodes.size()) * 2); - std::printf("returned-all: %d\n", int(temp.size())); - TEST_EQUAL(temp.size(), nodes.size()); + { + node_id const id = generate_random_id(); + table.find_node(id, temp, 0, int(nodes.size()) * 2); + std::printf("returned-all: %d\n", int(temp.size())); + TEST_EQUAL(temp.size(), nodes.size()); + } // This makes sure enough of the nodes returned are actually // part of the closest nodes @@ -1723,21 +1730,19 @@ void test_routing_table(address(&rand_addr)()) for (int r = 0; r < reps; ++r) { - aux::random_bytes(tmp); - table.find_node(tmp, temp, 0, bucket_size * 2); - std::printf("returned: %d\n", int(temp.size())); + node_id const id = generate_random_id(); + table.find_node(id, temp, 0, bucket_size * 2); TEST_EQUAL(int(temp.size()), std::min(bucket_size * 2, int(nodes.size()))); std::sort(nodes.begin(), nodes.end(), std::bind(&compare_ref , std::bind(&node_entry::id, _1) - , std::bind(&node_entry::id, _2), tmp)); + , std::bind(&node_entry::id, _2), id)); int expected = std::accumulate(nodes.begin(), nodes.begin() + (bucket_size * 2) - , 0, std::bind(&sum_distance_exp, _1, _2, tmp)); + , 0, std::bind(&sum_distance_exp, _1, _2, id)); int sum_hits = std::accumulate(temp.begin(), temp.end() - , 0, std::bind(&sum_distance_exp, _1, _2, tmp)); + , 0, std::bind(&sum_distance_exp, _1, _2, id)); TEST_EQUAL(bucket_size * 2, int(temp.size())); - std::printf("expected: %d actual: %d\n", expected, sum_hits); TEST_EQUAL(expected, sum_hits); duplicates.clear(); @@ -2667,7 +2672,7 @@ TORRENT_TEST(traversal_done) TORRENT_TEST(dht_dual_stack) { // TODO: 3 use dht_test_setup class to simplify the node setup - dht::dht_settings sett = test_settings(); + dht::settings sett = test_settings(); mock_socket s; auto sock4 = dummy_listen_socket4(); auto sock6 = dummy_listen_socket6(); @@ -2989,10 +2994,13 @@ TORRENT_TEST(verify_message) TORRENT_TEST(routing_table_uniform) { // test routing table - dht::dht_settings sett = test_settings(); + dht::settings sett = test_settings(); obs observer; sett.extended_routing_table = false; + // it's difficult to generate valid nodes with specific node IDs, so just + // turn off that check + sett.prefer_verified_node_ids = false; node_id id = to_hash("1234876923549721020394873245098347598635"); node_id diff = to_hash("15764f7459456a9453f8719b09547c11d5f34061"); @@ -3031,10 +3039,11 @@ TORRENT_TEST(routing_table_uniform) TORRENT_TEST(routing_table_balance) { - dht::dht_settings sett = test_settings(); + dht::settings sett = test_settings(); obs observer; sett.extended_routing_table = false; + sett.prefer_verified_node_ids = false; node_id id = to_hash("1234876923549721020394873245098347598635"); routing_table tbl(id, udp::v4(), 8, sett, &observer); @@ -3054,9 +3063,10 @@ TORRENT_TEST(routing_table_balance) TORRENT_TEST(routing_table_extended) { - dht::dht_settings sett = test_settings(); + dht::settings sett = test_settings(); obs observer; sett.extended_routing_table = true; + sett.prefer_verified_node_ids = false; node_id id = to_hash("1234876923549721020394873245098347598635"); node_id diff = to_hash("15764f7459456a9453f8719b09547c11d5f34061"); @@ -3088,9 +3098,10 @@ void inserter(std::set* nodes, node_entry const& ne) TORRENT_TEST(routing_table_set_id) { - dht::dht_settings sett = test_settings(); + dht::settings sett = test_settings(); sett.enforce_node_id = false; sett.extended_routing_table = false; + sett.prefer_verified_node_ids = false; obs observer; node_id id = to_hash("0000000000000000000000000000000000000000"); @@ -3134,10 +3145,11 @@ TORRENT_TEST(routing_table_set_id) TORRENT_TEST(routing_table_for_each) { - dht::dht_settings sett = test_settings(); + dht::settings sett = test_settings(); obs observer; sett.extended_routing_table = false; + sett.prefer_verified_node_ids = false; node_id id = to_hash("1234876923549721020394873245098347598635"); routing_table tbl(id, udp::v4(), 2, sett, &observer); @@ -3205,7 +3217,7 @@ TORRENT_TEST(node_set_id) TORRENT_TEST(read_only_node) { // TODO: 3 use dht_test_setup class to simplify the node setup - dht::dht_settings sett = test_settings(); + dht::settings sett = test_settings(); sett.read_only = true; mock_socket s; auto ls = dummy_listen_socket4(); @@ -3304,7 +3316,7 @@ TORRENT_TEST(read_only_node) TORRENT_TEST(invalid_error_msg) { // TODO: 3 use dht_test_setup class to simplify the node setup - dht::dht_settings sett = test_settings(); + dht::settings sett = test_settings(); mock_socket s; auto ls = dummy_listen_socket4(); obs observer; @@ -3357,6 +3369,8 @@ struct test_algo : dht::traversal_algorithm TORRENT_TEST(unsorted_traversal_results) { + init_rand_address(); + // make sure the handling of an unsorted tail of nodes is correct in the // traversal algorithm. Initial nodes (that we bootstrap from) remain // unsorted, since we don't know their node IDs @@ -3395,7 +3409,7 @@ TORRENT_TEST(unsorted_traversal_results) TORRENT_TEST(rpc_invalid_error_msg) { // TODO: 3 use dht_test_setup class to simplify the node setup - dht::dht_settings sett = test_settings(); + dht::settings sett = test_settings(); mock_socket s; auto ls = dummy_listen_socket4(); obs observer; @@ -3460,6 +3474,8 @@ TORRENT_TEST(rpc_invalid_error_msg) // test bucket distribution TORRENT_TEST(node_id_bucket_distribution) { + init_rand_address(); + int nodes_per_bucket[160] = {0}; dht::node_id reference_id = generate_id(rand_v4()); int const num_samples = 100000; @@ -3519,7 +3535,7 @@ TORRENT_TEST(dht_verify_node_address) { obs observer; // initial setup taken from dht test above - dht::dht_settings s; + dht::settings s; s.extended_routing_table = false; node_id id = to_hash("3123456789abcdef01232456789abcdef0123456"); const int bucket_size = 10; @@ -3697,6 +3713,8 @@ TORRENT_TEST(dht_state) TORRENT_TEST(sample_infohashes) { + init_rand_address(); + dht_test_setup t(rand_udp_ep()); bdecode_node response; @@ -3763,6 +3781,59 @@ TORRENT_TEST(sample_infohashes) TEST_CHECK(g_sent_packets.empty()); } +namespace { +node_entry fake_node(bool verified, int rtt = 0) +{ + node_entry e(rand_udp_ep()); + e.verified = verified; + e.rtt = static_cast(rtt); + return e; +} +} + +TORRENT_TEST(node_entry_comparison) +{ + // being verified or not always trumps RTT in sort order + TEST_CHECK(fake_node(true, 10) < fake_node(false, 5)); + TEST_CHECK(fake_node(true, 5) < fake_node(false, 10)); + TEST_CHECK(!(fake_node(false, 10) < fake_node(true, 5))); + TEST_CHECK(!(fake_node(false, 5) < fake_node(true, 10))); + + // if both are verified, lower RTT is better + TEST_CHECK(fake_node(true, 5) < fake_node(true, 10)); + TEST_CHECK(!(fake_node(true, 10) < fake_node(true, 5))); + + // if neither are verified, lower RTT is better + TEST_CHECK(fake_node(false, 5) < fake_node(false, 10)); + TEST_CHECK(!(fake_node(false, 10) < fake_node(false, 5))); +} + +TORRENT_TEST(mostly_verified_nodes) +{ + // an empty bucket is OK + TEST_CHECK(mostly_verified_nodes({})); + TEST_CHECK(mostly_verified_nodes({fake_node(true)})); + TEST_CHECK(mostly_verified_nodes({fake_node(true), fake_node(false)})); + TEST_CHECK(mostly_verified_nodes({fake_node(true), fake_node(true), fake_node(false)})); + TEST_CHECK(mostly_verified_nodes({fake_node(true), fake_node(true), fake_node(true), fake_node(false)})); + + // a large bucket with only half of the nodes verified, does not count as + // "mostly" + TEST_CHECK(!mostly_verified_nodes({fake_node(true), fake_node(false) + , fake_node(true), fake_node(false) + , fake_node(true), fake_node(false) + , fake_node(true), fake_node(false) + , fake_node(true), fake_node(false) + , fake_node(true), fake_node(false)})); + + // 1 of 3 is not "mostly" + TEST_CHECK(!mostly_verified_nodes({fake_node(false), fake_node(true), fake_node(false)})); + + TEST_CHECK(!mostly_verified_nodes({fake_node(false)})); + TEST_CHECK(!mostly_verified_nodes({fake_node(false), fake_node(false)})); + TEST_CHECK(!mostly_verified_nodes({fake_node(false), fake_node(false), fake_node(false)})); +} + // TODO: test obfuscated_get_peers #else diff --git a/test/test_session_params.cpp b/test/test_session_params.cpp index 6c00d9f32..8981016f0 100644 --- a/test/test_session_params.cpp +++ b/test/test_session_params.cpp @@ -43,7 +43,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "setup_transfer.hpp" using namespace lt; -using namespace lt::dht; +using dht::dht_storage_interface; +using dht::dht_state; namespace {