From b7398b90594f95e22d02120acf1c64bac60e466c Mon Sep 17 00:00:00 2001 From: Maxime THIEBAUT <46688461+0xThiebaut@users.noreply.github.com> Date: Fri, 25 Oct 2024 23:06:18 +0200 Subject: [PATCH] Add announce_port support --- ChangeLog | 1 + include/libtorrent/settings_pack.hpp | 14 +++++ simulation/test_tracker.cpp | 84 ++++++++++++++++++++++++++++ src/session_impl.cpp | 8 ++- src/settings_pack.cpp | 3 +- 5 files changed, 106 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index de183faa2ae..cc29ef96be5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -13,6 +13,7 @@ * fix integer overflow in piece picker * torrent_status::num_pieces counts pieces passed hash check, as documented * check settings_pack::max_out_request_queue before performance alert + * add announce_port setting to overwrite the port reported to trackers 2.0.10 released diff --git a/include/libtorrent/settings_pack.hpp b/include/libtorrent/settings_pack.hpp index 0446f2b60d6..5b5d5f51002 100644 --- a/include/libtorrent/settings_pack.hpp +++ b/include/libtorrent/settings_pack.hpp @@ -2068,6 +2068,20 @@ namespace aux { i2p_inbound_length, i2p_outbound_length, + // ``announce_port`` is the port passed along to trackers as the + // ``&port=`` parameter. This setting does not affect the effective + // listening port. If left as zero (default), the listening port + // value is used. + // + // .. note:: + // This setting is only meant for very special cases where a + // seed's listening port differs from the external port. As an + // example, if a local proxy is used and that the proxy supports + // reverse tunnels through NAT-PMP, the tracker must connect to + // the external NAT-PMP port (configured using ``announce_port``) + // instead of the actual local listening port. + announce_port, + max_int_setting_internal }; diff --git a/simulation/test_tracker.cpp b/simulation/test_tracker.cpp index 6dabfbc326a..ac776c9aed7 100644 --- a/simulation/test_tracker.cpp +++ b/simulation/test_tracker.cpp @@ -361,6 +361,90 @@ void on_alert_notify(lt::session* ses) }); } +void test_announce() +{ + using sim::asio::ip::address_v4; + sim::default_config network_cfg; + sim::simulation sim{network_cfg}; + + sim::asio::io_context web_server(sim, make_address_v4("2.2.2.2")); + + // listen on port 8080 + sim::http_server http(web_server, 8080); + + int announces = 0; + + // expect announced IP & port + std::string const expect_port = "&port=1234"; + std::string const expect_ip = "&ip=1.2.3.4"; + + http.register_handler("/announce" + , [&announces, expect_port, expect_ip](std::string method, std::string req + , std::map&) + { + ++announces; + TEST_EQUAL(method, "GET"); + TEST_CHECK(req.find(expect_port) != std::string::npos); + TEST_CHECK(req.find(expect_ip) != std::string::npos); + char response[500]; + int const size = std::snprintf(response, sizeof(response), "d8:intervali1800e5:peers0:e"); + return sim::send_response(200, "OK", size) + response; + }); + + { + lt::session_proxy zombie; + + std::vector ips; + ips.push_back(make_address("123.0.0.3")); + + asio::io_context ios(sim, ips); + lt::settings_pack sett = settings(); + sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:6881"); + sett.set_str(settings_pack::announce_ip, "1.2.3.4"); + sett.set_int(settings_pack::announce_port, 1234); + + auto ses = std::make_unique(sett, ios); + + ses->set_alert_notify(std::bind(&on_alert_notify, ses.get())); + + lt::add_torrent_params p; + p.name = "test-torrent"; + p.save_path = "."; + p.info_hashes.v1.assign("abababababababababab"); + + p.trackers.push_back("http://2.2.2.2:8080/announce"); + ses->async_add_torrent(p); + + // stop the torrent 5 seconds in + sim::timer t1(sim, lt::seconds(5) + , [&ses](boost::system::error_code const&) + { + std::vector torrents = ses->get_torrents(); + for (auto const& t : torrents) + { + t.pause(); + } + }); + + // then shut down 10 seconds in + sim::timer t2(sim, lt::seconds(10) + , [&ses,&zombie](boost::system::error_code const&) + { + zombie = ses->abort(); + ses.reset(); + }); + + sim.run(); + } + + TEST_EQUAL(announces, 2); +} + +// this test makes sure that a seed can overwrite its announced IP & port +TORRENT_TEST(announce_ip_port) { + test_announce(); +} + static const int num_interfaces = 3; void test_ipv6_support(char const* listen_interfaces diff --git a/src/session_impl.cpp b/src/session_impl.cpp index b1efa6d1e3e..fe8598524ff 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -1301,9 +1301,11 @@ namespace { #endif req.ssl_ctx = &m_ssl_ctx; #endif - - auto ls = req.outgoing_socket.get(); - if (ls) + if (const auto announce_port = std::uint16_t(m_settings.get_int(settings_pack::announce_port))) + { + req.listen_port = announce_port; + } + else if (auto ls = req.outgoing_socket.get()) { req.listen_port = #ifdef TORRENT_SSL_PEERS diff --git a/src/settings_pack.cpp b/src/settings_pack.cpp index 5a2c20f7405..72d0a886fd9 100644 --- a/src/settings_pack.cpp +++ b/src/settings_pack.cpp @@ -401,7 +401,8 @@ constexpr int DISK_WRITE_MODE = settings_pack::enable_os_cache; SET(i2p_inbound_quantity, 3, nullptr), SET(i2p_outbound_quantity, 3, nullptr), SET(i2p_inbound_length, 3, nullptr), - SET(i2p_outbound_length, 3, nullptr) + SET(i2p_outbound_length, 3, nullptr), + SET(announce_port, 0, nullptr) }}); #undef SET