diff --git a/ChangeLog b/ChangeLog index 45a59239e..fab8b9123 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * make tracker keys multi-homed. remove set_key() function on session. * add API to query whether alerts have been dropped or not * add flags()/set_flags()/unset_flags() to torrent_handle, deprecate individual functions * added alert for block being sent to the send buffer diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index 4dc99f65e..1c422efaa 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -198,6 +198,10 @@ namespace aux { // which alias the listen_socket_t shared_ptr std::shared_ptr sock; std::shared_ptr udp_sock; + + // the key is an id that is used to identify the + // client with the tracker only. + std::uint32_t tracker_key = 0; }; struct TORRENT_EXTRA_EXPORT listen_endpoint_t @@ -583,6 +587,8 @@ namespace aux { std::uint16_t ssl_listen_port() const override; std::uint16_t ssl_listen_port(listen_socket_t* sock) const; + std::uint32_t get_tracker_key(address const& iface) const; + void for_each_listen_socket(std::function f) override { for (auto& s : m_listen_sockets) @@ -906,11 +912,6 @@ namespace aux { // the peer id that is generated at the start of the session peer_id m_peer_id; - // the key is an id that is used to identify the - // client with the tracker only. It is randomized - // at startup - std::uint32_t m_key = 0; - // posts a notification when the set of local IPs changes std::unique_ptr m_ip_notifier; diff --git a/include/libtorrent/session_handle.hpp b/include/libtorrent/session_handle.hpp index 1438afbd1..231b66c3e 100644 --- a/include/libtorrent/session_handle.hpp +++ b/include/libtorrent/session_handle.hpp @@ -586,10 +586,13 @@ namespace libtorrent { // the peer ID is randomized per peer. peer_id id() const; +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 1.2 // sets the key sent to trackers. If it's not set, it is initialized // by libtorrent. The key may be used by the tracker to identify the // peer potentially across you changing your IP. void set_key(std::uint32_t key); +#endif // built-in peer classes static constexpr peer_class_t global_peer_class_id{0}; diff --git a/simulation/test_tracker.cpp b/simulation/test_tracker.cpp index 713584fcf..d9fb08610 100644 --- a/simulation/test_tracker.cpp +++ b/simulation/test_tracker.cpp @@ -43,6 +43,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/file_storage.hpp" #include "libtorrent/torrent_info.hpp" +#include + using namespace lt; using namespace sim; @@ -989,6 +991,30 @@ TORRENT_TEST(tracker_ipv6_argument) TEST_EQUAL(got_ipv6, true); } +TORRENT_TEST(tracker_key_argument) +{ + std::set keys; + tracker_test( + [](lt::add_torrent_params& p, lt::session& ses) + { + p.ti = make_torrent(true); + return 60; + }, + [&](std::string method, std::string req + , std::map& headers) + { + auto const pos = req.find("&key="); + TEST_CHECK(pos != std::string::npos); + keys.insert(req.substr(pos + 5, req.find_first_of('&', pos + 5) - pos - 5)); + return sim::send_response(200, "OK", 11) + "d5:peers0:e"; + } + , [](torrent_handle h) {} + , [](torrent_handle h) {}); + + // make sure we got two separate keys, one for each listen socket interface + TEST_EQUAL(keys.size(), 2); +} + // make sure we do _not_ send our IPv6 address to trackers for non-private // torrents TORRENT_TEST(tracker_ipv6_argument_non_private) diff --git a/src/session_handle.cpp b/src/session_handle.cpp index c15ce4e4d..ae63b6a07 100644 --- a/src/session_handle.cpp +++ b/src/session_handle.cpp @@ -802,6 +802,13 @@ namespace { p.set_str(settings_pack::peer_fingerprint, id.to_string()); apply_settings(std::move(p)); } + + void session_handle::set_key(std::uint32_t) + { + // this is just a dummy function now, as we generate the key automatically + // per listen interface + } + #endif peer_id session_handle::id() const @@ -809,11 +816,6 @@ namespace { return sync_call_ret(&session_impl::get_peer_id); } - void session_handle::set_key(std::uint32_t key) - { - async_call(&session_impl::set_key, key); - } - unsigned short session_handle::listen_port() const { return sync_call_ret diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 523cb9239..9adbcf611 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -1134,8 +1134,6 @@ namespace { } #endif - if (m_key) req.key = m_key; - #ifdef TORRENT_USE_OPENSSL bool use_ssl = req.ssl_ctx != nullptr; req.ssl_ctx = &m_ssl_ctx; @@ -1144,22 +1142,34 @@ namespace { if (req.outgoing_socket) { auto ls = req.outgoing_socket.get(); - req.listen_port = listen_port(ls); + + req.key ^= ls->tracker_key; + + req.listen_port = #ifdef TORRENT_USE_OPENSSL // SSL torrents use the SSL listen port - if (use_ssl) req.listen_port = ssl_listen_port(ls); + use_ssl ? ssl_listen_port(ls) : #endif + listen_port(ls); m_tracker_manager.queue_request(get_io_service(), req, c); } else { for (auto& ls : m_listen_sockets) { - req.listen_port = listen_port(ls.get()); +#ifdef TORRENT_USE_OPENSSL + if ((ls->ssl == transport::ssl) != use_ssl) continue; +#endif + req.listen_port = #ifdef TORRENT_USE_OPENSSL // SSL torrents use the SSL listen port - if (use_ssl) req.listen_port = ssl_listen_port(ls.get()); + use_ssl ? ssl_listen_port(ls.get()) : #endif + listen_port(ls.get()); + + // we combine the per-torrent key with the per-interface key to make + // them consistent and uniqiue per torrent and interface + req.key ^= ls->tracker_key; req.outgoing_socket = ls; m_tracker_manager.queue_request(get_io_service(), req, c); } @@ -1384,6 +1394,25 @@ namespace { reopen_outgoing_sockets(); } + std::uint32_t session_impl::get_tracker_key(address const& iface) const + { + uintptr_t const ses = reinterpret_cast(this); + hasher h(reinterpret_cast(&ses), sizeof(ses)); + if (iface.is_v4()) + { + auto const b = iface.to_v4().to_bytes(); + h.update({reinterpret_cast(b.data()), b.size()}); + } + else + { + auto const b = iface.to_v6().to_bytes(); + h.update({reinterpret_cast(b.data()), b.size()}); + } + sha1_hash const hash = h.final(); + unsigned char const* ptr = &hash[0]; + return detail::read_uint32(ptr); + } + std::shared_ptr session_impl::setup_listener( listen_endpoint_t const& lep, error_code& ec) { @@ -1591,6 +1620,8 @@ namespace { } return ret; } + ret->tracker_key = get_tracker_key(ret->local_endpoint.address()); + ret->tcp_external_port = ret->local_endpoint.port(); TORRENT_ASSERT(ret->tcp_external_port == bind_ep.port() || bind_ep.port() == 0); @@ -3006,11 +3037,6 @@ namespace { m_peer_id = id; } - void session_impl::set_key(std::uint32_t key) - { - m_key = key; - } - int session_impl::next_port() const { int start = m_settings.get_int(settings_pack::outgoing_port); @@ -5394,6 +5420,8 @@ namespace { } } + // TODO: 2 this function should be removed and users need to deal with the + // more generic case of having multiple listen ports std::uint16_t session_impl::listen_port() const { return listen_port(nullptr);