diff --git a/CMakeLists.txt b/CMakeLists.txt index 210d6c42d..0f1e53efe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,7 +186,7 @@ if (NOT CMAKE_BUILD_TYPE) endif() # add_definitions() doesn't seem to let you say wich build type to apply it to -set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DTORRENT_DEBUG") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DTORRENT_DEBUG") if(UNIX) set(CMAKE_C_FLAGS_RELWITHDEBINFO "-Os -g") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") diff --git a/ChangeLog b/ChangeLog index d7fe901bc..9c1393027 100644 --- a/ChangeLog +++ b/ChangeLog @@ -73,6 +73,8 @@ * require C++11 to build libtorrent + * fix python3 portability issue in python binding + * delay 5 seconds before reconnecting socks5 proxy for UDP ASSOCIATE * fix NAT-PMP crash when removing a mapping at the wrong time * improve path sanitization (filter unicode text direction characters) * deprecate partial_piece_info::piece_state diff --git a/bindings/python/src/boost_python.hpp b/bindings/python/src/boost_python.hpp index 90353a5b8..acd2fc4f5 100644 --- a/bindings/python/src/boost_python.hpp +++ b/bindings/python/src/boost_python.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include // something in here creates a define for this, presumably to make older diff --git a/bindings/python/src/converters.cpp b/bindings/python/src/converters.cpp index cf6f04b60..ac37d09f4 100644 --- a/bindings/python/src/converters.cpp +++ b/bindings/python/src/converters.cpp @@ -131,12 +131,11 @@ struct dict_to_map dict o(borrowed(x)); std::map m; - list iterkeys = (list)o.keys(); - int const len = int(boost::python::len(iterkeys)); - for (int i = 0; i < len; i++) + stl_input_iterator i(o.keys()), end; + for (; i != end; ++i) { - object key = iterkeys[i]; - m[extract(key)] = extract(o[key]); + T1 const& key = *i; + m[key] = extract(o[key]); } new (storage) std::map(m); data->convertible = storage; diff --git a/bindings/python/src/session.cpp b/bindings/python/src/session.cpp index 69f29eff8..6994a3d77 100644 --- a/bindings/python/src/session.cpp +++ b/bindings/python/src/session.cpp @@ -108,11 +108,10 @@ namespace void make_settings_pack(lt::settings_pack& p, dict const& sett_dict) { - list iterkeys = (list)sett_dict.keys(); - int const len = int(boost::python::len(iterkeys)); - for (int i = 0; i < len; i++) + stl_input_iterator i(sett_dict.keys()), end; + for (; i != end; ++i) { - std::string const key = extract(iterkeys[i]); + std::string const key = *i; int sett = setting_by_name(key); if (sett < 0) diff --git a/include/libtorrent/debug.hpp b/include/libtorrent/debug.hpp index 46b2b85b3..8547bc86f 100644 --- a/include/libtorrent/debug.hpp +++ b/include/libtorrent/debug.hpp @@ -208,6 +208,21 @@ namespace libtorrent { bool is_not_thread() const {return true; } }; #endif + +#if TORRENT_USE_ASSERTS + struct increment_guard + { + int& m_cnt; + increment_guard(int& c) : m_cnt(c) { TORRENT_ASSERT(m_cnt >= 0); ++m_cnt; } + ~increment_guard() { --m_cnt; TORRENT_ASSERT(m_cnt >= 0); } + private: + increment_guard(increment_guard const&); + increment_guard operator=(increment_guard const&); + }; +#define TORRENT_INCREMENT(x) increment_guard inc_(x) +#else +#define TORRENT_INCREMENT(x) do {} while (false) +#endif } #endif // TORRENT_DEBUG_HPP_INCLUDED diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 743df46ff..847576a27 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -1660,6 +1660,10 @@ namespace libtorrent { // set to true when torrent is start()ed. It may only be started once bool m_was_started = false; bool m_outstanding_check_files = false; + + // this is set to true while we're looping over m_connections. We may not + // mutate the list while doing this + mutable int m_iterating_connections = 0; #endif }; } diff --git a/simulation/libsimulator b/simulation/libsimulator index 70feadef8..96e8e2414 160000 --- a/simulation/libsimulator +++ b/simulation/libsimulator @@ -1 +1 @@ -Subproject commit 70feadef80dc76ae6d5e7c2f92334377d698dd7d +Subproject commit 96e8e2414df955c04b37b6bccd2b7360a54ff2f1 diff --git a/simulation/test_socks5.cpp b/simulation/test_socks5.cpp index a80c332a6..d2d6c59db 100644 --- a/simulation/test_socks5.cpp +++ b/simulation/test_socks5.cpp @@ -246,3 +246,40 @@ TORRENT_TEST(udp_tracker) TEST_CHECK(announced); } +TORRENT_TEST(socks5_udp_retry) +{ + // this test is asserting that when a UDP associate command fails, we have a + // 5 second delay before we try again. There is no need to actually add a + // torrent for this test, just to open the udp socket with a socks5 proxy + using namespace libtorrent; + + // setup the simulation + sim::default_config network_cfg; + sim::simulation sim{network_cfg}; + std::unique_ptr ios = make_io_service(sim, 0); + lt::session_proxy zombie; + + sim::asio::io_service proxy_ios{sim, addr("50.50.50.50") }; + // close UDP associate connectons prematurely + sim::socks_server socks5(proxy_ios, 5555, 5, socks_flag::disconnect_udp_associate); + + lt::settings_pack pack = settings(); + pack.set_str(settings_pack::listen_interfaces, "50.50.50.50:6881"); + // create session + std::shared_ptr ses = std::make_shared(pack, *ios); + set_proxy(*ses, settings_pack::socks5); + + // run for 60 seconds.The sokcks5 retry interval is expected to be 5 seconds, + // meaning there should have been 12 connection attempts + sim::timer t(sim, lt::seconds(60), [&](boost::system::error_code const& ec) + { + fprintf(stderr, "shutting down\n"); + // shut down + zombie = ses->abort(); + ses.reset(); + }); + sim.run(); + + // number of UDP ASSOCIATE commands invoked on the socks proxy + TEST_EQUAL(socks5.cmd_counts()[2], 12); +} diff --git a/src/torrent.cpp b/src/torrent.cpp index be4ca17e5..1d8190788 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -867,6 +867,7 @@ namespace libtorrent { #ifndef TORRENT_DISABLE_EXTENSIONS for (auto const pc : m_connections) { + TORRENT_INCREMENT(m_iterating_connections); if (pc->type() != connection_type::bittorrent) continue; bt_peer_connection* p = static_cast(pc); p->write_share_mode(); @@ -882,6 +883,7 @@ namespace libtorrent { for (auto p : m_connections) { + TORRENT_INCREMENT(m_iterating_connections); if (p->type() != connection_type::bittorrent) continue; @@ -937,6 +939,7 @@ namespace libtorrent { // clear request queues of all peers for (auto p : m_connections) { + TORRENT_INCREMENT(m_iterating_connections); // we may want to disconnect other upload-only peers if (p->upload_only()) p->update_interest(); @@ -956,6 +959,7 @@ namespace libtorrent { // send_block_requests on all peers for (auto p : m_connections) { + TORRENT_INCREMENT(m_iterating_connections); // we may be interested now, or no longer interested p->update_interest(); p->send_block_requests(); @@ -1185,6 +1189,7 @@ namespace libtorrent { for (auto p : m_connections) { + TORRENT_INCREMENT(m_iterating_connections); peer_has(p->get_bitfield(), p); } } @@ -1357,6 +1362,7 @@ namespace libtorrent { for (auto p : m_connections) { + TORRENT_INCREMENT(m_iterating_connections); std::shared_ptr pp(tp->new_connection(peer_connection_handle(p->self()))); if (pp) p->add_extension(std::move(pp)); } @@ -1871,6 +1877,7 @@ namespace libtorrent { #ifndef TORRENT_DISABLE_EXTENSIONS for (auto pe : m_connections) { + TORRENT_INCREMENT(m_iterating_connections); if (pe->type() != connection_type::bittorrent) continue; bt_peer_connection* p = static_cast(pe); if (!p->supports_holepunch()) continue; @@ -1886,6 +1893,7 @@ namespace libtorrent { { for (auto p : m_connections) { + TORRENT_INCREMENT(m_iterating_connections); if (p->type() != connection_type::bittorrent) continue; if (p->remote() == ep) return static_cast(p); } @@ -3820,6 +3828,7 @@ namespace libtorrent { // invalidate the iterator for (auto p : m_connections) { + TORRENT_INCREMENT(m_iterating_connections); // if we're not interested already, no need to check if (!p->is_interesting()) continue; // if the peer doesn't have the piece we just got, it @@ -3956,6 +3965,7 @@ namespace libtorrent { for (auto p : m_connections) { + TORRENT_INCREMENT(m_iterating_connections); #ifndef TORRENT_DISABLE_LOGGING p->peer_log(peer_log_alert::outgoing, "PREDICTIVE_HAVE", "piece: %d expected in %d ms" , static_cast(index), milliseconds); @@ -3993,6 +4003,7 @@ namespace libtorrent { { for (auto p : m_connections) { + TORRENT_INCREMENT(m_iterating_connections); // send reject messages for // potential outstanding requests to this piece p->reject_piece(index); @@ -4186,6 +4197,7 @@ namespace libtorrent { // blocks to this piece for (auto p : m_connections) { + TORRENT_INCREMENT(m_iterating_connections); for (auto const& b : p->download_queue()) { if (b.timed_out || b.not_wanted) continue; @@ -4518,6 +4530,7 @@ namespace libtorrent { for (auto p : m_connections) { + TORRENT_INCREMENT(m_iterating_connections); // for each peer, go through its download and request queue // and cancel everything, except pieces that are time critical @@ -5210,6 +5223,7 @@ namespace libtorrent { for (auto p : m_connections) { + TORRENT_INCREMENT(m_iterating_connections); p->cancel_request(block); } } @@ -5922,6 +5936,7 @@ namespace libtorrent { TORRENT_ASSERT(!c->m_in_constructor); // add the newly connected peer to this torrent's peer list + TORRENT_ASSERT(m_iterating_connections == 0); sorted_insert(m_connections, c.get()); update_want_peers(); update_want_tick(); @@ -6515,6 +6530,7 @@ namespace libtorrent { #endif // add the newly connected peer to this torrent's peer list + TORRENT_ASSERT(m_iterating_connections == 0); sorted_insert(m_connections, c.get()); m_ses.insert_peer(c); need_peer_list(); @@ -6532,6 +6548,7 @@ namespace libtorrent { } TORRENT_CATCH (std::exception const&) { + TORRENT_ASSERT(m_iterating_connections == 0); c->disconnect(errors::no_error, op_bittorrent, 1); return false; } @@ -6802,6 +6819,7 @@ namespace libtorrent { peers_erased(st.erased); TORRENT_ASSERT(sorted_find(m_connections, p) == m_connections.end()); + TORRENT_ASSERT(m_iterating_connections == 0); sorted_insert(m_connections, p); update_want_peers(); update_want_tick(); @@ -7056,8 +7074,10 @@ namespace libtorrent { void torrent::disconnect_all(error_code const& ec, operation_t op) { + TORRENT_ASSERT(m_iterating_connections == 0); for (auto const& p : m_connections) { + TORRENT_INCREMENT(m_iterating_connections); TORRENT_ASSERT(p->associated_torrent().lock().get() == this); p->disconnect(ec, op); } @@ -7116,7 +7136,10 @@ namespace libtorrent { #if TORRENT_USE_ASSERTS // make sure we don't have any dangling pointers for (auto p : m_connections) + { + TORRENT_INCREMENT(m_iterating_connections); TORRENT_ASSERT(m_ses.has_peer(p)); + } #endif aux::vector to_disconnect; to_disconnect.resize(num); @@ -7165,6 +7188,7 @@ namespace libtorrent { std::vector seeds; for (auto const p : m_connections) { + TORRENT_INCREMENT(m_iterating_connections); TORRENT_ASSERT(p->associated_torrent().lock().get() == this); if (p->upload_only()) { @@ -7400,6 +7424,7 @@ namespace libtorrent { for (auto pc : m_connections) { + TORRENT_INCREMENT(m_iterating_connections); // all peer connections have to initialize themselves now that the metadata // is available if (notify_initialized) @@ -8414,6 +8439,7 @@ namespace libtorrent { // requests for (peer_connection* p : m_connections) { + TORRENT_INCREMENT(m_iterating_connections); TORRENT_ASSERT(p->associated_torrent().lock().get() == this); if (p->is_disconnecting()) continue; @@ -9075,6 +9101,7 @@ namespace libtorrent { int num_interested = 0; for (auto const p : m_connections) { + TORRENT_INCREMENT(m_iterating_connections); if (p->is_connecting()) continue; if (p->is_disconnecting()) continue; ++num_peers; diff --git a/src/udp_socket.cpp b/src/udp_socket.cpp index 0e8cac571..2b26e2fd7 100644 --- a/src/udp_socket.cpp +++ b/src/udp_socket.cpp @@ -64,6 +64,7 @@ struct socks5 : std::enable_shared_from_this : m_socks5_sock(ios) , m_resolver(ios) , m_timer(ios) + , m_retry_timer(ios) , m_abort(false) , m_active(false) { @@ -91,10 +92,12 @@ private: void connect1(error_code const& e); void connect2(error_code const& e); void hung_up(error_code const& e); + void retry_socks_connect(error_code const& e); tcp::socket m_socks5_sock; tcp::resolver m_resolver; deadline_timer m_timer; + deadline_timer m_retry_timer; char m_tmp_buf[270]; aux::proxy_settings m_proxy_settings; @@ -738,7 +741,15 @@ void socks5::hung_up(error_code const& e) if (e == boost::asio::error::operation_aborted || m_abort) return; - // the socks connection was closed, re-open it + // the socks connection was closed, re-open it in a bit + m_retry_timer.expires_from_now(seconds(5)); + m_retry_timer.async_wait(std::bind(&socks5::retry_socks_connect + , self(), _1)); +} + +void socks5::retry_socks_connect(error_code const& e) +{ + if (e) return; start(m_proxy_settings); }