diff --git a/ChangeLog b/ChangeLog index 32a3db83227..e10734b65b5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -130,6 +130,9 @@ * libtorrent now requires C++14 to build * added support for GnuTLS for HTTPS and torrents over SSL + + * made UPnP and LSD code avoid using select_reactor (to work around an issue on windows in boost.asio < 1.80) + 1.2.17 released * fixed tracker connections spinning when hostname lookups stall diff --git a/include/libtorrent/aux_/lsd.hpp b/include/libtorrent/aux_/lsd.hpp index 6f049cba039..21bdb3a636f 100644 --- a/include/libtorrent/aux_/lsd.hpp +++ b/include/libtorrent/aux_/lsd.hpp @@ -51,7 +51,7 @@ struct lsd : std::enable_shared_from_this void announce_impl(sha1_hash const& ih, int listen_port, int retry_count); void resend_announce(error_code const& e, sha1_hash const& info_hash , int listen_port, int retry_count); - void on_announce(error_code const& ec); + void on_announce(error_code const& ec, std::size_t len); lsd_callback& m_callback; @@ -59,6 +59,8 @@ struct lsd : std::enable_shared_from_this address m_netmask; udp::socket m_socket; + std::array m_buffer; + udp::endpoint m_remote; #ifndef TORRENT_DISABLE_LOGGING bool should_log() const; diff --git a/include/libtorrent/upnp.hpp b/include/libtorrent/upnp.hpp index 1de64de93dd..ce66fd4afe0 100644 --- a/include/libtorrent/upnp.hpp +++ b/include/libtorrent/upnp.hpp @@ -39,6 +39,17 @@ namespace libtorrent { class http_parser; } +namespace aux { + + struct socket_package + { + socket_package(io_context& ios) : socket(ios) {} + udp::socket socket; + std::array buffer; + udp::endpoint remote; + }; +} + namespace upnp_errors { // error codes for the upnp_error_category. They hold error codes // returned by UPnP routers when mapping ports @@ -185,15 +196,15 @@ struct TORRENT_EXTRA_EXPORT upnp final std::shared_ptr self() { return shared_from_this(); } - void open_multicast_socket(udp::socket& s, error_code& ec); - void open_unicast_socket(udp::socket& s, error_code& ec); + void open_multicast_socket(aux::socket_package& s, error_code& ec); + void open_unicast_socket(aux::socket_package& s, error_code& ec); void map_timer(error_code const& ec); void try_map_upnp(); void discover_device_impl(); void resend_request(error_code const& e); - void on_reply(udp::socket& s, error_code const& ec); + void on_reply(aux::socket_package& s, error_code const& ec, std::size_t len); struct rootdevice; void next(rootdevice& d, port_mapping_t i); @@ -326,8 +337,8 @@ struct TORRENT_EXTRA_EXPORT upnp final // the udp socket used to send and receive // multicast messages on the network - udp::socket m_multicast_socket; - udp::socket m_unicast_socket; + aux::socket_package m_multicast; + aux::socket_package m_unicast; // used to resend udp packets in case // they time out diff --git a/src/lsd.cpp b/src/lsd.cpp index 1c518d8a3d5..035b65c6225 100644 --- a/src/lsd.cpp +++ b/src/lsd.cpp @@ -23,6 +23,7 @@ see LICENSE file. #include "libtorrent/hex.hpp" // to_hex, from_hex #include "libtorrent/aux_/numeric_cast.hpp" #include "libtorrent/aux_/enum_net.hpp" +#include "libtorrent/aux_/scope_end.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" #include @@ -113,8 +114,8 @@ void lsd::start(error_code& ec) } ADD_OUTSTANDING_ASYNC("lsd::on_announce"); - m_socket.async_wait(udp::socket::wait_read - , std::bind(&lsd::on_announce, self(), _1)); + m_socket.async_receive_from(boost::asio::buffer(m_buffer), m_remote + , std::bind(&lsd::on_announce, self(), _1, _2)); } lsd::~lsd() = default; @@ -184,20 +185,26 @@ void lsd::resend_announce(error_code const& e, sha1_hash const& info_hash announce_impl(info_hash, listen_port, retry_count); } -void lsd::on_announce(error_code const& ec) +void lsd::on_announce(error_code const& ec, std::size_t len) { COMPLETE_ASYNC("lsd::on_announce"); - if (ec) return; + if (ec) + { +#ifndef TORRENT_DISABLE_LOGGING + debug_log("<== LSD: receive error: %s", ec.message().c_str()); +#endif + return; + } - std::array buffer; - udp::endpoint from; - error_code err; - int const len = static_cast(m_socket.receive_from( - boost::asio::buffer(buffer), from, {}, err)); + udp::endpoint const from = m_remote; - ADD_OUTSTANDING_ASYNC("lsd::on_announce"); - m_socket.async_wait(udp::socket::wait_read - , std::bind(&lsd::on_announce, self(), _1)); + // reissue the async receive as we exit the function. We can't do this + // earlier because we still need to use m_buffer + auto reissue_receive = aux::scope_end([&] { + ADD_OUTSTANDING_ASYNC("lsd::on_announce"); + m_socket.async_receive_from(boost::asio::buffer(m_buffer), m_remote + , std::bind(&lsd::on_announce, self(), _1, _2)); + }); if (!aux::match_addr_mask(from.address(), m_listen_address, m_netmask)) { @@ -209,18 +216,10 @@ void lsd::on_announce(error_code const& ec) return; } - if (err) - { -#ifndef TORRENT_DISABLE_LOGGING - debug_log("<== LSD: receive error: %s", err.message().c_str()); -#endif - return; - } - aux::http_parser p; bool error = false; - p.incoming(span{buffer.data(), len}, error); + p.incoming(span{m_buffer.data(), std::ptrdiff_t(len)}, error); if (!p.header_finished() || error) { diff --git a/src/upnp.cpp b/src/upnp.cpp index f99a9af7f7a..8fad532a1bf 100644 --- a/src/upnp.cpp +++ b/src/upnp.cpp @@ -27,6 +27,7 @@ see LICENSE file. #include "libtorrent/aux_/http_connection.hpp" #include "libtorrent/aux_/numeric_cast.hpp" #include "libtorrent/aux_/ssl.hpp" +#include "libtorrent/aux_/scope_end.hpp" #if defined TORRENT_ASIO_DEBUGGING #include "libtorrent/aux_/debug.hpp" @@ -91,8 +92,8 @@ upnp::upnp(io_context& ios , m_callback(cb) , m_io_service(ios) , m_resolver(ios) - , m_multicast_socket(ios) - , m_unicast_socket(ios) + , m_multicast(ios) + , m_unicast(ios) , m_broadcast_timer(ios) , m_refresh_timer(ios) , m_map_timer(ios) @@ -114,7 +115,7 @@ void upnp::start() TORRENT_ASSERT(is_single_thread()); error_code ec; - open_multicast_socket(m_multicast_socket, ec); + open_multicast_socket(m_multicast, ec); #ifndef TORRENT_DISABLE_LOGGING if (ec && should_log()) { @@ -125,7 +126,7 @@ void upnp::start() } #endif - open_unicast_socket(m_unicast_socket, ec); + open_unicast_socket(m_unicast, ec); #ifndef TORRENT_DISABLE_LOGGING if (ec && should_log()) { @@ -147,39 +148,39 @@ namespace { } -void upnp::open_multicast_socket(udp::socket& s, error_code& ec) +void upnp::open_multicast_socket(aux::socket_package& s, error_code& ec) { using namespace boost::asio::ip::multicast; - s.open(udp::v4(), ec); + s.socket.open(udp::v4(), ec); if (ec) return; - s.set_option(udp::socket::reuse_address(true), ec); + s.socket.set_option(udp::socket::reuse_address(true), ec); if (ec) return; - s.bind(udp::endpoint(m_listen_address, ssdp_port), ec); + s.socket.bind(udp::endpoint(m_listen_address, ssdp_port), ec); if (ec) return; - s.set_option(join_group(ssdp_multicast_addr), ec); + s.socket.set_option(join_group(ssdp_multicast_addr), ec); if (ec) return; - s.set_option(hops(255), ec); + s.socket.set_option(hops(255), ec); if (ec) return; - s.set_option(enable_loopback(true), ec); + s.socket.set_option(enable_loopback(true), ec); if (ec) return; - s.set_option(outbound_interface(m_listen_address), ec); + s.socket.set_option(outbound_interface(m_listen_address), ec); if (ec) return; ADD_OUTSTANDING_ASYNC("upnp::on_reply"); - s.async_receive(boost::asio::null_buffers{} - , std::bind(&upnp::on_reply, self(), std::ref(s), _1)); + s.socket.async_receive_from(boost::asio::buffer(s.buffer), s.remote + , std::bind(&upnp::on_reply, self(), std::ref(s), _1, _2)); } -void upnp::open_unicast_socket(udp::socket& s, error_code& ec) +void upnp::open_unicast_socket(aux::socket_package& s, error_code& ec) { - s.open(udp::v4(), ec); + s.socket.open(udp::v4(), ec); if (ec) return; - s.bind(udp::endpoint(m_listen_address, 0), ec); + s.socket.bind(udp::endpoint(m_listen_address, 0), ec); if (ec) return; ADD_OUTSTANDING_ASYNC("upnp::on_reply"); - s.async_receive(boost::asio::null_buffers{} - , std::bind(&upnp::on_reply, self(), std::ref(s), _1)); + s.socket.async_receive_from(boost::asio::buffer(s.buffer), s.remote + , std::bind(&upnp::on_reply, self(), std::ref(s), _1, _2)); } upnp::~upnp() = default; @@ -222,9 +223,9 @@ void upnp::discover_device_impl() error_code mcast_ec; error_code ucast_ec; - m_multicast_socket.send_to(boost::asio::buffer(msearch, sizeof(msearch) - 1) + m_multicast.socket.send_to(boost::asio::buffer(msearch, sizeof(msearch) - 1) , udp::endpoint(ssdp_multicast_addr, ssdp_port), 0, mcast_ec); - m_unicast_socket.send_to(boost::asio::buffer(msearch, sizeof(msearch) - 1) + m_unicast.socket.send_to(boost::asio::buffer(msearch, sizeof(msearch) - 1) , udp::endpoint(ssdp_multicast_addr, ssdp_port), 0, ucast_ec); if (mcast_ec && ucast_ec) @@ -439,7 +440,7 @@ void upnp::connect(rootdevice& d) } } -void upnp::on_reply(udp::socket& s, error_code const& ec) +void upnp::on_reply(aux::socket_package& s, error_code const& ec, std::size_t const len) { TORRENT_ASSERT(is_single_thread()); COMPLETE_ASYNC("upnp::on_reply"); @@ -448,13 +449,7 @@ void upnp::on_reply(udp::socket& s, error_code const& ec) if (m_closing) return; std::shared_ptr me(self()); - - std::array buffer{}; - udp::endpoint from; - error_code err; - int const len = static_cast(s.receive_from(boost::asio::buffer(buffer) - , from, 0, err)); - + udp::endpoint const from = s.remote; // parse out the url for the device /* @@ -482,11 +477,15 @@ void upnp::on_reply(udp::socket& s, error_code const& ec) */ - ADD_OUTSTANDING_ASYNC("upnp::on_reply"); - s.async_receive(boost::asio::null_buffers{} - , std::bind(&upnp::on_reply, self(), std::ref(s), _1)); + // reissue the async receive as we exit the function. We can't do this + // earlier because we still need to use s.buffer + auto reissue_receive = scope_end([&] { + ADD_OUTSTANDING_ASYNC("upnp::on_reply"); + s.socket.async_receive_from(boost::asio::buffer(s.buffer), s.remote + , std::bind(&upnp::on_reply, self(), std::ref(s), _1, _2)); + }); - if (err) return; + if (ec) return; if (m_settings.get_bool(settings_pack::upnp_ignore_nonrouters) && !match_addr_mask(m_listen_address, from.address(), m_netmask)) @@ -505,7 +504,7 @@ void upnp::on_reply(udp::socket& s, error_code const& ec) aux::http_parser p; bool error = false; - p.incoming({buffer.data(), len}, error); + p.incoming({s.buffer.data(), std::ptrdiff_t(len)}, error); if (error) { #ifndef TORRENT_DISABLE_LOGGING @@ -572,6 +571,7 @@ void upnp::on_reply(udp::socket& s, error_code const& ec) { std::string protocol; std::string auth; + error_code err; // we don't have this device in our list. Add it std::tie(protocol, auth, d.hostname, d.port, d.path) = parse_url_components(d.url, err); @@ -1137,8 +1137,8 @@ void upnp::disable(error_code const& ec) m_refresh_timer.cancel(); m_map_timer.cancel(); error_code e; - m_unicast_socket.close(e); - m_multicast_socket.close(e); + m_unicast.socket.close(e); + m_multicast.socket.close(e); } void find_error_code(int const type, string_view string, error_code_parse_state& state) @@ -1633,8 +1633,8 @@ void upnp::close() m_map_timer.cancel(); m_closing = true; error_code ec; - m_unicast_socket.close(ec); - m_multicast_socket.close(ec); + m_unicast.socket.close(ec); + m_multicast.socket.close(ec); for (auto const& dev : m_devices) { diff --git a/test/test_storage.cpp b/test/test_storage.cpp index f12aaa2748c..7a4fe22d519 100644 --- a/test/test_storage.cpp +++ b/test/test_storage.cpp @@ -28,7 +28,6 @@ see LICENSE file. #include "libtorrent/torrent_info.hpp" #include "libtorrent/read_resume_data.hpp" #include "libtorrent/write_resume_data.hpp" -#include "libtorrent/aux_/mmap_disk_job.hpp" #include "libtorrent/aux_/path.hpp" #include "libtorrent/aux_/storage_utils.hpp" #include "libtorrent/aux_/session_settings.hpp" @@ -55,9 +54,10 @@ namespace aux { namespace { +#if TORRENT_HAVE_MMAP || TORRENT_HAVE_MAP_VIEW_OF_FILE using lt::aux::mmap_storage; +#endif using lt::aux::posix_storage; -using lt::aux::mmap_disk_job; constexpr int piece_size = 16 * 1024 * 16; constexpr int half = piece_size / 2; @@ -165,33 +165,50 @@ std::shared_ptr setup_torrent_info(file_storage& fs return info; } +// file_pool_type is a meta function returning the file pool type for a specific +// StorageType +// maybe this would be easier to follow if it was all bundled up in a +// traits class +template +struct file_pool_type {}; + +#if TORRENT_HAVE_MMAP || TORRENT_HAVE_MAP_VIEW_OF_FILE +template <> +struct file_pool_type +{ + using type = aux::file_view_pool; +}; +#endif + +template <> +struct file_pool_type +{ + using type = int; +}; + template std::shared_ptr make_storage(storage_params const& p - , aux::file_view_pool& fp); + , typename file_pool_type::type& fp); -#if TORRENT_HAVE_MMAP +#if TORRENT_HAVE_MMAP || TORRENT_HAVE_MAP_VIEW_OF_FILE template <> std::shared_ptr make_storage(storage_params const& p , aux::file_view_pool& fp) { return std::make_shared(p, fp); } -#else -template <> -std::shared_ptr make_storage(storage_params const& p - , aux::file_view_pool& fp) = delete; #endif template <> std::shared_ptr make_storage(storage_params const& p - , aux::file_view_pool&) + , int&) { return std::make_shared(p); } -template +template std::shared_ptr setup_torrent(file_storage& fs - , aux::file_view_pool& fp + , FilePool& fp , std::vector& buf , std::string const& test_path , aux::session_settings& set) @@ -224,7 +241,7 @@ std::shared_ptr setup_torrent(file_storage& fs return s; } -#if TORRENT_HAVE_MMAP +#if TORRENT_HAVE_MMAP || TORRENT_HAVE_MAP_VIEW_OF_FILE int write(std::shared_ptr s , aux::session_settings const& sett , span buf @@ -308,7 +325,7 @@ void run_storage_tests(std::shared_ptr info { // avoid having two storages use the same files - aux::file_view_pool fp; + typename file_pool_type::type fp; boost::asio::io_context ios; aux::vector priorities; sha1_hash info_hash; @@ -398,7 +415,7 @@ void test_remove(std::string const& test_path) file_storage fs; std::vector buf; - aux::file_view_pool fp; + typename file_pool_type::type fp; io_context ios; aux::session_settings set; @@ -468,7 +485,7 @@ void test_rename(std::string const& test_path) file_storage fs; std::vector buf; - aux::file_view_pool fp; + typename file_pool_type::type fp; io_context ios; aux::session_settings set; @@ -649,7 +666,7 @@ void run_test() delete_dirs("temp_storage"); } -#if TORRENT_HAVE_MMAP +#if TORRENT_HAVE_MMAP || TORRENT_HAVE_MAP_VIEW_OF_FILE TORRENT_TEST(check_files_sparse_mmap) { test_check_files(sparse | zero_prio, lt::mmap_disk_io_constructor); @@ -692,7 +709,7 @@ TORRENT_TEST(check_files_allocate_posix) test_check_files(zero_prio, lt::posix_disk_io_constructor); } -#if TORRENT_HAVE_MMAP +#if TORRENT_HAVE_MMAP || TORRENT_HAVE_MAP_VIEW_OF_FILE TORRENT_TEST(rename_mmap_disk_io) { test_rename(current_working_directory()); @@ -1058,7 +1075,7 @@ bool check_pattern(std::vector const& buf, int counter) } // anonymous namespace -#if TORRENT_HAVE_MMAP +#if TORRENT_HAVE_MMAP || TORRENT_HAVE_MAP_VIEW_OF_FILE TORRENT_TEST(mmap_disk_io) { run_test(); } #endif TORRENT_TEST(posix_disk_io) { run_test(); } @@ -1287,7 +1304,7 @@ void test_move_storage_to_self() aux::session_settings set; file_storage fs; std::vector buf; - aux::file_view_pool fp; + typename file_pool_type::type fp; io_context ios; auto s = setup_torrent(fs, fp, buf, save_path, set); @@ -1321,7 +1338,7 @@ void test_move_storage_into_self() aux::session_settings set; file_storage fs; std::vector buf; - aux::file_view_pool fp; + typename file_pool_type::type fp; io_context ios; auto s = setup_torrent(fs, fp, buf, save_path, set); @@ -1343,7 +1360,7 @@ void test_move_storage_into_self() , combine_path("_folder3", "test4.tmp"))))); } -#if TORRENT_HAVE_MMAP +#if TORRENT_HAVE_MMAP || TORRENT_HAVE_MAP_VIEW_OF_FILE TORRENT_TEST(move_default_storage_to_self) { test_move_storage_to_self(); @@ -1378,7 +1395,7 @@ TORRENT_TEST(storage_paths_string_pooling) TEST_CHECK(file_storage.paths().size() <= 2); } -#if TORRENT_HAVE_MMAP +#if TORRENT_HAVE_MMAP || TORRENT_HAVE_MAP_VIEW_OF_FILE TORRENT_TEST(dont_move_intermingled_files) { std::string const save_path = complete("save_path_1"); @@ -1390,7 +1407,7 @@ TORRENT_TEST(dont_move_intermingled_files) aux::session_settings set; file_storage fs; std::vector buf; - aux::file_view_pool fp; + typename file_pool_type::type fp; io_context ios; auto s = setup_torrent(fs, fp, buf, save_path, set); @@ -1622,7 +1639,7 @@ void none_from_store_buffer(lt::disk_interface* disk_io, lt::storage_holder cons } -#if TORRENT_HAVE_MMAP +#if TORRENT_HAVE_MMAP || TORRENT_HAVE_MAP_VIEW_OF_FILE TORRENT_TEST(mmap_unaligned_read_both_store_buffer) { test_unaligned_read(lt::mmap_disk_io_constructor, both_sides_from_store_buffer);