From 0675bd263feb29c241ce2a1bfc36ab509cee2dfc Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Sun, 22 Dec 2019 00:48:05 +0100 Subject: [PATCH] add new socks5_alert to trouble shoot SOCKS5 proxies --- ChangeLog | 2 + bindings/python/src/alert.cpp | 8 ++ include/libtorrent/alert_types.hpp | 24 ++++- .../libtorrent/aux_/session_udp_sockets.hpp | 8 +- include/libtorrent/fwd.hpp | 4 + include/libtorrent/operations.hpp | 5 +- include/libtorrent/udp_socket.hpp | 3 +- src/alert.cpp | 22 ++++- src/session_impl.cpp | 8 +- src/session_udp_sockets.cpp | 4 +- src/udp_socket.cpp | 91 +++++++++++++++---- test/test_alert_types.cpp | 3 +- test/test_priority.cpp | 1 - 13 files changed, 152 insertions(+), 31 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1f5527901..78f2f1552 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,5 @@ + * add new socks5_alert to trouble shoot SOCKS5 proxies + 1.2.3 release * fix erroneous event=completed tracker announce when checking files diff --git a/bindings/python/src/alert.cpp b/bindings/python/src/alert.cpp index e1c5b1c3e..2809229de 100644 --- a/bindings/python/src/alert.cpp +++ b/bindings/python/src/alert.cpp @@ -236,6 +236,7 @@ namespace boost POLY(block_uploaded_alert) POLY(alerts_dropped_alert) POLY(session_stats_alert) + POLY(socks5_alert) #if TORRENT_ABI_VERSION == 1 POLY(anonymous_mode_alert) @@ -1029,6 +1030,13 @@ void bind_alert() .add_property("dropped_alerts", &get_dropped_alerts) ; + class_, noncopyable>( + "socks5_alert", no_init) + .def_readonly("error", &socks5_alert::error) + .def_readonly("op", &socks5_alert::op) + .add_property("ip", make_getter(&socks5_alert::ip, by_value())) + ; + } #ifdef _MSC_VER diff --git a/include/libtorrent/alert_types.hpp b/include/libtorrent/alert_types.hpp index 4a84638fb..17d8a2a91 100644 --- a/include/libtorrent/alert_types.hpp +++ b/include/libtorrent/alert_types.hpp @@ -83,7 +83,7 @@ namespace libtorrent { constexpr int user_alert_id = 10000; // this constant represents "max_alert_index" + 1 - constexpr int num_alert_types = 96; + constexpr int num_alert_types = 97; // internal enum alert_priority @@ -2947,6 +2947,28 @@ TORRENT_VERSION_NAMESPACE_2 std::bitset dropped_alerts; }; + // this alert is posted with SOCKS5 related errors, when a SOCKS5 proxy is + // configured. It's enabled with the error_notification alert category. + struct TORRENT_EXPORT socks5_alert final : alert + { + // internal + explicit socks5_alert(aux::stack_allocator& alloc + , tcp::endpoint const& ep, operation_t operation, error_code const& ec); + TORRENT_DEFINE_ALERT(socks5_alert, 96) + + static constexpr alert_category_t static_category = alert::error_notification; + std::string message() const override; + + // the error + error_code error; + + // the operation that failed + operation_t op; + + // the endpoint configured as the proxy + aux::noexcept_movable ip; + }; + TORRENT_VERSION_NAMESPACE_2_END #undef TORRENT_DEFINE_ALERT_IMPL diff --git a/include/libtorrent/aux_/session_udp_sockets.hpp b/include/libtorrent/aux_/session_udp_sockets.hpp index 5bc5959d2..1687665f7 100644 --- a/include/libtorrent/aux_/session_udp_sockets.hpp +++ b/include/libtorrent/aux_/session_udp_sockets.hpp @@ -39,7 +39,11 @@ POSSIBILITY OF SUCH DAMAGE. #include #include -namespace libtorrent { namespace aux { +namespace libtorrent { + + class alert_manager; + +namespace aux { struct listen_endpoint_t; struct proxy_settings; @@ -92,7 +96,7 @@ namespace libtorrent { namespace aux { tcp::endpoint bind(socket_type& s, address const& remote_address , error_code& ec) const; - void update_proxy(proxy_settings const& settings); + void update_proxy(proxy_settings const& settings, alert_manager& alerts); // close all sockets void close(); diff --git a/include/libtorrent/fwd.hpp b/include/libtorrent/fwd.hpp index 2cd8805f9..d51806325 100644 --- a/include/libtorrent/fwd.hpp +++ b/include/libtorrent/fwd.hpp @@ -139,6 +139,7 @@ struct session_stats_header_alert; struct dht_sample_infohashes_alert; struct block_uploaded_alert; struct alerts_dropped_alert; +struct socks5_alert; TORRENT_VERSION_NAMESPACE_2_END // include/libtorrent/announce_entry.hpp @@ -280,6 +281,9 @@ TORRENT_VERSION_NAMESPACE_2_END // include/libtorrent/file_storage.hpp struct file_entry; +// include/libtorrent/fingerprint.hpp +struct fingerprint; + // include/libtorrent/lazy_entry.hpp struct pascal_string; struct lazy_entry; diff --git a/include/libtorrent/operations.hpp b/include/libtorrent/operations.hpp index 42f291f82..220c9957e 100644 --- a/include/libtorrent/operations.hpp +++ b/include/libtorrent/operations.hpp @@ -166,10 +166,13 @@ namespace libtorrent { // create or read a symlink symlink, + + // handshake with a peer or server + handshake, }; // maps an operation id (from peer_error_alert and peer_disconnected_alert) - // to its name. See peer_connection for the constants + // to its name. See operation_t for the constants TORRENT_EXPORT char const* operation_name(operation_t op); #if TORRENT_ABI_VERSION == 1 diff --git a/include/libtorrent/udp_socket.hpp b/include/libtorrent/udp_socket.hpp index c5c9aeba6..0b18b85c6 100644 --- a/include/libtorrent/udp_socket.hpp +++ b/include/libtorrent/udp_socket.hpp @@ -46,6 +46,7 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { + class alert_manager; struct socks5; using udp_send_flags_t = flags::bitfield_flag; @@ -95,7 +96,7 @@ namespace libtorrent { void close(); int local_port() const { return m_bind_port; } - void set_proxy_settings(aux::proxy_settings const& ps); + void set_proxy_settings(aux::proxy_settings const& ps, alert_manager& alerts); aux::proxy_settings const& get_proxy_settings() { return m_proxy_settings; } bool is_closed() const { return m_abort; } diff --git a/src/alert.cpp b/src/alert.cpp index e167297a2..a90e9165b 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -895,6 +895,7 @@ namespace { case o::partfile_write: return -1; case o::hostname_lookup: return -1; case o::symlink: return -1; + case o::handshake: return -1; } return -1; } @@ -1560,7 +1561,8 @@ namespace { "partfile_read", "partfile_write", "hostname_lookup", - "symlink" + "symlink", + "handshake" }; int const idx = static_cast(op); @@ -2582,7 +2584,7 @@ namespace { "dht_pkt", "dht_get_peers_reply", "dht_direct_response", "picker_log", "session_error", "dht_live_nodes", "session_stats_header", "dht_sample_infohashes", - "block_uploaded", "alerts_dropped" + "block_uploaded", "alerts_dropped", "socks5" }}; TORRENT_ASSERT(alert_type >= 0); @@ -2605,6 +2607,21 @@ namespace { return ret; } + socks5_alert::socks5_alert(aux::stack_allocator& + , tcp::endpoint const& ep, operation_t operation, error_code const& ec) + : error(ec) + , op(operation) + , ip(ep) + {} + + std::string socks5_alert::message() const + { + char buf[512]; + std::snprintf(buf, sizeof(buf), "SOCKS5 error. op: %s ec: %s ep: %s" + , operation_name(op), error.message().c_str(), print_endpoint(ip).c_str()); + return buf; + } + // this will no longer be necessary in C++17 constexpr alert_category_t torrent_removed_alert::static_category; constexpr alert_category_t read_piece_alert::static_category; @@ -2693,6 +2710,7 @@ namespace { constexpr alert_category_t dht_sample_infohashes_alert::static_category; constexpr alert_category_t block_uploaded_alert::static_category; constexpr alert_category_t alerts_dropped_alert::static_category; + constexpr alert_category_t socks5_alert::static_category; #if TORRENT_ABI_VERSION == 1 constexpr alert_category_t anonymous_mode_alert::static_category; constexpr alert_category_t mmap_cache_alert::static_category; diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 1abcdbdb2..bb6fa5a17 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -1665,7 +1665,7 @@ namespace aux { // change after the session is up and listening, at no other point // set_proxy_settings is called with the correct proxy configuration, // internally, this method handle the SOCKS5's connection logic - ret->udp_sock->sock.set_proxy_settings(proxy()); + ret->udp_sock->sock.set_proxy_settings(proxy(), m_alerts); ADD_OUTSTANDING_ASYNC("session_impl::on_udp_packet"); ret->udp_sock->sock.async_read(aux::make_handler(std::bind(&session_impl::on_udp_packet @@ -2100,7 +2100,7 @@ namespace aux { // change after the session is up and listening, at no other point // set_proxy_settings is called with the correct proxy configuration, // internally, this method handle the SOCKS5's connection logic - udp_sock->sock.set_proxy_settings(proxy()); + udp_sock->sock.set_proxy_settings(proxy(), m_alerts); ADD_OUTSTANDING_ASYNC("session_impl::on_udp_packet"); udp_sock->sock.async_read(aux::make_handler(std::bind(&session_impl::on_udp_packet @@ -5286,8 +5286,8 @@ namespace aux { void session_impl::update_proxy() { for (auto& i : m_listen_sockets) - i->udp_sock->sock.set_proxy_settings(proxy()); - m_outgoing_sockets.update_proxy(proxy()); + i->udp_sock->sock.set_proxy_settings(proxy(), m_alerts); + m_outgoing_sockets.update_proxy(proxy(), m_alerts); } void session_impl::update_ip_notifier() diff --git a/src/session_udp_sockets.cpp b/src/session_udp_sockets.cpp index 2a63f1e43..9b314c064 100644 --- a/src/session_udp_sockets.cpp +++ b/src/session_udp_sockets.cpp @@ -108,10 +108,10 @@ namespace libtorrent { namespace aux { return tcp::endpoint(); } - void outgoing_sockets::update_proxy(proxy_settings const& settings) + void outgoing_sockets::update_proxy(proxy_settings const& settings, alert_manager& alerts) { for (auto const& i : sockets) - i->sock.set_proxy_settings(settings); + i->sock.set_proxy_settings(settings, alerts); } void outgoing_sockets::close() diff --git a/src/udp_socket.cpp b/src/udp_socket.cpp index 3242ec6a1..d5405562f 100644 --- a/src/udp_socket.cpp +++ b/src/udp_socket.cpp @@ -40,6 +40,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/deadline_timer.hpp" #include "libtorrent/aux_/numeric_cast.hpp" #include "libtorrent/broadcast_socket.hpp" // for is_v4 +#include "libtorrent/alert_manager.hpp" +#include "libtorrent/socks5_stream.hpp" // for socks_error #include #include @@ -67,11 +69,12 @@ std::size_t const max_header_size = 255; // the common case cheaper by not allocating this space unconditionally struct socks5 : std::enable_shared_from_this { - explicit socks5(io_service& ios) + explicit socks5(io_service& ios, alert_manager& alerts) : m_socks5_sock(ios) , m_resolver(ios) , m_timer(ios) , m_retry_timer(ios) + , m_alerts(alerts) , m_abort(false) , m_active(false) {} @@ -103,6 +106,7 @@ private: tcp::resolver m_resolver; deadline_timer m_timer; deadline_timer m_retry_timer; + alert_manager& m_alerts; std::array m_tmp_buf; aux::proxy_settings m_proxy_settings; @@ -111,7 +115,7 @@ private: // when performing a UDP associate, we get another // endpoint (presumably on the same IP) where we're // supposed to send UDP packets. - udp::endpoint m_proxy_addr; + tcp::endpoint m_proxy_addr; // this is where UDP packets that are to be forwarded // are sent. The result from UDP ASSOCIATE is stored @@ -472,7 +476,8 @@ void udp_socket::bind(udp::endpoint const& ep, error_code& ec) if (err) m_bind_port = ep.port(); } -void udp_socket::set_proxy_settings(aux::proxy_settings const& ps) +void udp_socket::set_proxy_settings(aux::proxy_settings const& ps + , alert_manager& alerts) { TORRENT_ASSERT(is_single_thread()); @@ -490,8 +495,7 @@ void udp_socket::set_proxy_settings(aux::proxy_settings const& ps) || ps.type == settings_pack::socks5_pw) { // connect to socks5 server and open up the UDP tunnel - - m_socks5_connection = std::make_shared(lt::get_io_service(m_socket)); + m_socks5_connection = std::make_shared(lt::get_io_service(m_socket), alerts); m_socks5_connection->start(ps); } } @@ -517,10 +521,14 @@ void socks5::on_name_lookup(error_code const& e, tcp::resolver::iterator i) if (e == boost::asio::error::operation_aborted) return; - if (e) return; + if (e) + { + if (m_alerts.should_post()) + m_alerts.emplace_alert(m_proxy_addr, operation_t::hostname_lookup, e); + return; + } - m_proxy_addr.address(i->endpoint().address()); - m_proxy_addr.port(i->endpoint().port()); + m_proxy_addr = i->endpoint(); error_code ec; m_socks5_sock.open(is_v4(m_proxy_addr) ? tcp::v4() : tcp::v6(), ec); @@ -529,7 +537,7 @@ void socks5::on_name_lookup(error_code const& e, tcp::resolver::iterator i) m_socks5_sock.set_option(boost::asio::socket_base::keep_alive(true), ec); ADD_OUTSTANDING_ASYNC("socks5::on_connected"); - m_socks5_sock.async_connect(tcp::endpoint(m_proxy_addr.address(), m_proxy_addr.port()) + m_socks5_sock.async_connect(m_proxy_addr , std::bind(&socks5::on_connected, self(), _1)); ADD_OUTSTANDING_ASYNC("socks5::on_connect_timeout"); @@ -546,6 +554,9 @@ void socks5::on_connect_timeout(error_code const& e) if (m_abort) return; + if (m_alerts.should_post()) + m_alerts.emplace_alert(m_proxy_addr, operation_t::connect, errors::timed_out); + error_code ignore; m_socks5_sock.close(ignore); } @@ -561,7 +572,12 @@ void socks5::on_connected(error_code const& e) if (m_abort) return; // we failed to connect to the proxy - if (e) return; + if (e) + { + if (m_alerts.should_post()) + m_alerts.emplace_alert(m_proxy_addr, operation_t::connect, e); + return; + } using namespace libtorrent::detail; @@ -591,7 +607,12 @@ void socks5::handshake1(error_code const& e) { COMPLETE_ASYNC("socks5::on_handshake1"); if (m_abort) return; - if (e) return; + if (e) + { + if (m_alerts.should_post()) + m_alerts.emplace_alert(m_proxy_addr, operation_t::handshake, e); + return; + } ADD_OUTSTANDING_ASYNC("socks5::on_handshake2"); boost::asio::async_read(m_socks5_sock, boost::asio::buffer(m_tmp_buf.data(), 2) @@ -603,7 +624,12 @@ void socks5::handshake2(error_code const& e) COMPLETE_ASYNC("socks5::on_handshake2"); if (m_abort) return; - if (e) return; + if (e) + { + if (m_alerts.should_post()) + m_alerts.emplace_alert(m_proxy_addr, operation_t::handshake, e); + return; + } using namespace libtorrent::detail; @@ -613,6 +639,9 @@ void socks5::handshake2(error_code const& e) if (version < 5) { + if (m_alerts.should_post()) + m_alerts.emplace_alert(m_proxy_addr, operation_t::handshake + , socks_error::unsupported_version); error_code ec; m_socks5_sock.close(ec); return; @@ -626,6 +655,9 @@ void socks5::handshake2(error_code const& e) { if (m_proxy_settings.username.empty()) { + if (m_alerts.should_post()) + m_alerts.emplace_alert(m_proxy_addr, operation_t::handshake + , socks_error::username_required); error_code ec; m_socks5_sock.close(ec); return; @@ -648,6 +680,10 @@ void socks5::handshake2(error_code const& e) } else { + if (m_alerts.should_post()) + m_alerts.emplace_alert(m_proxy_addr, operation_t::handshake + , socks_error::unsupported_authentication_method); + error_code ec; m_socks5_sock.close(ec); return; @@ -658,7 +694,12 @@ void socks5::handshake3(error_code const& e) { COMPLETE_ASYNC("socks5::on_handshake3"); if (m_abort) return; - if (e) return; + if (e) + { + if (m_alerts.should_post()) + m_alerts.emplace_alert(m_proxy_addr, operation_t::handshake, e); + return; + } ADD_OUTSTANDING_ASYNC("socks5::on_handshake4"); boost::asio::async_read(m_socks5_sock, boost::asio::buffer(m_tmp_buf.data(), 2) @@ -669,7 +710,12 @@ void socks5::handshake4(error_code const& e) { COMPLETE_ASYNC("socks5::on_handshake4"); if (m_abort) return; - if (e) return; + if (e) + { + if (m_alerts.should_post()) + m_alerts.emplace_alert(m_proxy_addr, operation_t::handshake, e); + return; + } using namespace libtorrent::detail; @@ -705,7 +751,12 @@ void socks5::connect1(error_code const& e) { COMPLETE_ASYNC("socks5::connect1"); if (m_abort) return; - if (e) return; + if (e) + { + if (m_alerts.should_post()) + m_alerts.emplace_alert(m_proxy_addr, operation_t::connect, e); + return; + } ADD_OUTSTANDING_ASYNC("socks5::connect2"); boost::asio::async_read(m_socks5_sock, boost::asio::buffer(m_tmp_buf.data(), 10) @@ -717,7 +768,12 @@ void socks5::connect2(error_code const& e) COMPLETE_ASYNC("socks5::connect2"); if (m_abort) return; - if (e) return; + if (e) + { + if (m_alerts.should_post()) + m_alerts.emplace_alert(m_proxy_addr, operation_t::handshake, e); + return; + } using namespace libtorrent::detail; @@ -757,6 +813,9 @@ void socks5::hung_up(error_code const& e) if (e == boost::asio::error::operation_aborted || m_abort) return; + if (e && m_alerts.should_post()) + m_alerts.emplace_alert(m_proxy_addr, operation_t::sock_read, e); + // 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 diff --git a/test/test_alert_types.cpp b/test/test_alert_types.cpp index 22eecd872..ecffb14a1 100644 --- a/test/test_alert_types.cpp +++ b/test/test_alert_types.cpp @@ -178,10 +178,11 @@ TORRENT_TEST(alerts_types) TEST_ALERT_TYPE(dht_sample_infohashes_alert, 93, 0, alert::dht_operation_notification); TEST_ALERT_TYPE(block_uploaded_alert, 94, 0, PROGRESS_NOTIFICATION alert::upload_notification); TEST_ALERT_TYPE(alerts_dropped_alert, 95, 3, alert::error_notification); + TEST_ALERT_TYPE(socks5_alert, 96, 0, alert::error_notification); #undef TEST_ALERT_TYPE - TEST_EQUAL(num_alert_types, 96); + TEST_EQUAL(num_alert_types, 97); TEST_EQUAL(num_alert_types, count_alert_types); } diff --git a/test/test_priority.cpp b/test/test_priority.cpp index 4d5098578..6355aa8cd 100644 --- a/test/test_priority.cpp +++ b/test/test_priority.cpp @@ -489,7 +489,6 @@ TORRENT_TEST(file_priority_multiple_calls) settings_pack pack = settings(); lt::session ses(pack); - error_code ec; auto t = ::generate_torrent(true); add_torrent_params addp;