From cdfdbfd6aa71b9b0c6984459ff509991b7e58b30 Mon Sep 17 00:00:00 2001 From: arvidn Date: Sun, 21 Feb 2016 17:40:27 -0500 Subject: [PATCH 1/6] fix port mapping for SSL UDP socket --- include/libtorrent/aux_/session_impl.hpp | 2 +- src/session_impl.cpp | 44 +++++++++++++++++------- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index 391f1c24b..841edd7be 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -335,7 +335,7 @@ namespace libtorrent , std::vector
const& addresses, int port); #endif - void maybe_update_udp_mapping(int nat, int local_port, int external_port); + void maybe_update_udp_mapping(int nat, bool ssl, int local_port, int external_port); #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS) torrent const* find_encrypted_torrent( diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 214ecd054..a6cfa1e4b 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -2062,7 +2062,26 @@ retry: } ec.clear(); } - // TODO: 3 port map SSL udp socket here + else + { + maybe_update_udp_mapping(0, true, ssl_port, ssl_port); + maybe_update_udp_mapping(1, true, ssl_port, ssl_port); + } + } + else + { + // if there are mappings for the SSL socket, delete them now + if (m_ssl_udp_mapping[0] != -1 && m_natpmp) + { + m_natpmp->delete_mapping(m_ssl_udp_mapping[0]); + m_ssl_udp_mapping[0] = -1; + } + if (m_ssl_udp_mapping[1] != -1 && m_upnp) + { + m_upnp->delete_mapping(m_ssl_udp_mapping[1]); + m_ssl_udp_mapping[1] = -1; + } + } #endif // TORRENT_USE_OPENSSL @@ -2094,8 +2113,8 @@ retry: else { m_external_udp_port = m_udp_socket.local_port(); - maybe_update_udp_mapping(0, m_listen_interface.port(), m_listen_interface.port()); - maybe_update_udp_mapping(1, m_listen_interface.port(), m_listen_interface.port()); + maybe_update_udp_mapping(0, false, m_listen_interface.port(), m_listen_interface.port()); + maybe_update_udp_mapping(1, false, m_listen_interface.port(), m_listen_interface.port()); } // we made it! now post all the listen_succeeded_alerts @@ -5869,38 +5888,39 @@ retry: #endif - void session_impl::maybe_update_udp_mapping(int nat, int local_port, int external_port) + void session_impl::maybe_update_udp_mapping(int nat, bool ssl, int local_port, int external_port) { int local, external, protocol; + int* mapping = ssl ? m_ssl_udp_mapping : m_udp_mapping; if (nat == 0 && m_natpmp) { - if (m_udp_mapping[nat] != -1) + if (mapping[nat] != -1) { - if (m_natpmp->get_mapping(m_udp_mapping[nat], local, external, protocol)) + if (m_natpmp->get_mapping(mapping[nat], local, external, protocol)) { // we already have a mapping. If it's the same, don't do anything if (local == local_port && external == external_port && protocol == natpmp::udp) return; } - m_natpmp->delete_mapping(m_udp_mapping[nat]); + m_natpmp->delete_mapping(mapping[nat]); } - m_udp_mapping[nat] = m_natpmp->add_mapping(natpmp::udp + mapping[nat] = m_natpmp->add_mapping(natpmp::udp , local_port, external_port); return; } else if (nat == 1 && m_upnp) { - if (m_udp_mapping[nat] != -1) + if (mapping[nat] != -1) { - if (m_upnp->get_mapping(m_udp_mapping[nat], local, external, protocol)) + if (m_upnp->get_mapping(mapping[nat], local, external, protocol)) { // we already have a mapping. If it's the same, don't do anything if (local == local_port && external == external_port && protocol == natpmp::udp) return; } - m_upnp->delete_mapping(m_udp_mapping[nat]); + m_upnp->delete_mapping(mapping[nat]); } - m_udp_mapping[nat] = m_upnp->add_mapping(upnp::udp + mapping[nat] = m_upnp->add_mapping(upnp::udp , local_port, external_port); return; } From 61a67ece4aa6d2a4cd70947d14fbe9ad9aef520d Mon Sep 17 00:00:00 2001 From: arvidn Date: Sun, 21 Feb 2016 18:40:06 -0500 Subject: [PATCH 2/6] fix non-openssl build --- src/session_impl.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/session_impl.cpp b/src/session_impl.cpp index a6cfa1e4b..d2f57093c 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -5891,7 +5891,11 @@ retry: void session_impl::maybe_update_udp_mapping(int nat, bool ssl, int local_port, int external_port) { int local, external, protocol; +#ifdef TORRENT_USE_OPENSSL int* mapping = ssl ? m_ssl_udp_mapping : m_udp_mapping; +#else + int* mapping = m_udp_mapping; +#endif if (nat == 0 && m_natpmp) { if (mapping[nat] != -1) From d8b88ba381c9f71420658304cb0176794739f458 Mon Sep 17 00:00:00 2001 From: arvidn Date: Sun, 21 Feb 2016 19:16:00 -0500 Subject: [PATCH 3/6] include protocol in portmap_alert --- include/libtorrent/alert_types.hpp | 11 ++++++++++- include/libtorrent/aux_/session_impl.hpp | 2 +- include/libtorrent/natpmp.hpp | 4 ++-- include/libtorrent/upnp.hpp | 6 ++---- src/alert.cpp | 11 +++++++---- src/natpmp.cpp | 12 ++++++++---- src/session_impl.cpp | 19 ++++++++++++------- src/upnp.cpp | 12 ++++++++---- 8 files changed, 50 insertions(+), 27 deletions(-) diff --git a/include/libtorrent/alert_types.hpp b/include/libtorrent/alert_types.hpp index 6b995ddba..d87733f94 100644 --- a/include/libtorrent/alert_types.hpp +++ b/include/libtorrent/alert_types.hpp @@ -1366,7 +1366,7 @@ namespace libtorrent struct TORRENT_EXPORT portmap_alert TORRENT_FINAL : alert { // internal - portmap_alert(aux::stack_allocator& alloc, int i, int port, int t); + portmap_alert(aux::stack_allocator& alloc, int i, int port, int t, int protocol); TORRENT_DEFINE_ALERT(portmap_alert, 51) @@ -1382,6 +1382,15 @@ namespace libtorrent // 0 for NAT-PMP and 1 for UPnP. int map_type; + + enum protocol_t + { + tcp, + udp + }; + + // the protocol this mapping was for. one of protocol_t enums + int protocol; }; #ifndef TORRENT_DISABLE_LOGGING diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index 841edd7be..a5f066498 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -354,7 +354,7 @@ namespace libtorrent // called when a port mapping is successful, or a router returns // a failure to map a port void on_port_mapping(int mapping, address const& ip, int port - , error_code const& ec, int nat_transport); + , int protocol, error_code const& ec, int nat_transport); bool is_aborted() const TORRENT_OVERRIDE { return m_abort; } bool is_paused() const TORRENT_OVERRIDE { return m_paused; } diff --git a/include/libtorrent/natpmp.hpp b/include/libtorrent/natpmp.hpp index 6cb9927f9..f4b2be886 100644 --- a/include/libtorrent/natpmp.hpp +++ b/include/libtorrent/natpmp.hpp @@ -43,7 +43,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/aux_/disable_warnings_push.hpp" #include -#include +#include #include #include "libtorrent/aux_/disable_warnings_pop.hpp" @@ -54,7 +54,7 @@ namespace libtorrent // int: port mapping index // int: external port // std::string: error message -typedef boost::function portmap_callback_t; +typedef boost::function portmap_callback_t; typedef boost::function log_callback_t; class natpmp : public boost::enable_shared_from_this diff --git a/include/libtorrent/upnp.hpp b/include/libtorrent/upnp.hpp index 42ce868ec..2421b9cd1 100644 --- a/include/libtorrent/upnp.hpp +++ b/include/libtorrent/upnp.hpp @@ -43,7 +43,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/resolver.hpp" #include -#include +#include #include #include #include @@ -100,9 +100,7 @@ namespace libtorrent // int: external port // std::string: error message // an empty string as error means success -// a port-mapping index of -1 means it's -// an informational log message -typedef boost::function portmap_callback_t; +typedef boost::function portmap_callback_t; typedef boost::function log_callback_t; struct parse_state diff --git a/src/alert.cpp b/src/alert.cpp index 4bd7bc9ed..670454fe8 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -774,6 +774,8 @@ namespace libtorrent { static char const* const nat_type_str[] = {"NAT-PMP", "UPnP"}; + static char const* const protocol_str[] = {"TCP", "UDP"}; + static char const* const socket_type_str[] = { "null", "TCP", @@ -911,15 +913,16 @@ namespace libtorrent { + ": " + convert_from_native(error.message()); } - portmap_alert::portmap_alert(aux::stack_allocator&, int i, int port, int t) - : mapping(i), external_port(port), map_type(t) + portmap_alert::portmap_alert(aux::stack_allocator&, int i, int port, int t + , int proto) + : mapping(i), external_port(port), map_type(t), protocol(proto) {} std::string portmap_alert::message() const { char ret[200]; - snprintf(ret, sizeof(ret), "successfully mapped port using %s. external port: %u" - , nat_type_str[map_type], external_port); + snprintf(ret, sizeof(ret), "successfully mapped port using %s. external port: %s/%u" + , nat_type_str[map_type], protocol_str[protocol], external_port); return ret; } diff --git a/src/natpmp.cpp b/src/natpmp.cpp index f9338c12f..44d4e4e94 100644 --- a/src/natpmp.cpp +++ b/src/natpmp.cpp @@ -185,10 +185,11 @@ void natpmp::disable(error_code const& ec, mutex::scoped_lock& l) , end(m_mappings.end()); i != end; ++i) { if (i->protocol == none) continue; + int const proto = i->protocol; i->protocol = none; int index = i - m_mappings.begin(); l.unlock(); - m_callback(index, address(), 0, ec); + m_callback(index, address(), 0, proto, ec); l.lock(); } close_impl(l); @@ -563,15 +564,18 @@ void natpmp::on_reply(error_code const& e if (result >= 1 && result <= 5) ev = errors[result - 1]; m->expires = aux::time_now() + hours(2); + int const proto = m->protocol; l.unlock(); - m_callback(index, address(), 0, error_code(ev, get_libtorrent_category())); + m_callback(index, address(), 0, proto + , error_code(ev, get_libtorrent_category())); l.lock(); } else if (m->action == mapping_t::action_add) { + int const proto = m->protocol; l.unlock(); - m_callback(index, m_external_ip, m->external_port, - error_code(errors::no_error, get_libtorrent_category())); + m_callback(index, m_external_ip, m->external_port, proto + , error_code(errors::no_error, get_libtorrent_category())); l.lock(); } diff --git a/src/session_impl.cpp b/src/session_impl.cpp index d2f57093c..906a4d156 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -5446,7 +5446,7 @@ retry: } void session_impl::on_port_mapping(int mapping, address const& ip, int port - , error_code const& ec, int map_transport) + , int protocol, error_code const& ec, int map_transport) { TORRENT_ASSERT(is_single_thread()); @@ -5457,7 +5457,8 @@ retry: m_external_udp_port = port; if (m_alerts.should_post()) m_alerts.emplace_alert(mapping, port - , map_transport); + , map_transport, protocol == natpmp::udp + ? portmap_alert::udp : portmap_alert::tcp); return; } @@ -5476,7 +5477,8 @@ retry: } if (m_alerts.should_post()) m_alerts.emplace_alert(mapping, port - , map_transport); + , map_transport, protocol == natpmp::udp + ? portmap_alert::udp : portmap_alert::tcp); return; } @@ -5490,7 +5492,8 @@ retry: { if (m_alerts.should_post()) m_alerts.emplace_alert(mapping, port - , map_transport); + , map_transport, protocol == natpmp::udp + ? portmap_alert::udp : portmap_alert::tcp); } } @@ -5888,12 +5891,14 @@ retry: #endif - void session_impl::maybe_update_udp_mapping(int nat, bool ssl, int local_port, int external_port) + void session_impl::maybe_update_udp_mapping(int const nat, bool const ssl + , int const local_port, int const external_port) { int local, external, protocol; #ifdef TORRENT_USE_OPENSSL int* mapping = ssl ? m_ssl_udp_mapping : m_udp_mapping; #else + TORRENT_UNUSED(ssl); int* mapping = m_udp_mapping; #endif if (nat == 0 && m_natpmp) @@ -6626,7 +6631,7 @@ retry: // into the session_impl. m_natpmp = boost::make_shared(boost::ref(m_io_service) , boost::bind(&session_impl::on_port_mapping - , this, _1, _2, _3, _4, 0) + , this, _1, _2, _3, _4, _5, 0) , boost::bind(&session_impl::on_port_map_log , this, _1, 0)); m_natpmp->start(); @@ -6663,7 +6668,7 @@ retry: , m_listen_interface.address() , m_settings.get_str(settings_pack::user_agent) , boost::bind(&session_impl::on_port_mapping - , this, _1, _2, _3, _4, 1) + , this, _1, _2, _3, _4, _5, 1) , boost::bind(&session_impl::on_port_map_log , this, _1, 1) , m_settings.get_bool(settings_pack::upnp_ignore_nonrouters)); diff --git a/src/upnp.cpp b/src/upnp.cpp index c769476f6..c2a80e759 100644 --- a/src/upnp.cpp +++ b/src/upnp.cpp @@ -1044,9 +1044,10 @@ void upnp::disable(error_code const& ec, mutex::scoped_lock& l) , end(m_mappings.end()); i != end; ++i) { if (i->protocol == none) continue; + int const proto = i->protocol; i->protocol = none; l.unlock(); - m_callback(i - m_mappings.begin(), address(), 0, ec); + m_callback(i - m_mappings.begin(), address(), 0, proto, ec); l.lock(); } @@ -1375,7 +1376,7 @@ void upnp::on_upnp_map_response(error_code const& e if (s.error_code == -1) { l.unlock(); - m_callback(mapping, d.external_ip, m.external_port, error_code()); + m_callback(mapping, d.external_ip, m.external_port, m.protocol, error_code()); l.lock(); if (d.lease_duration > 0) { @@ -1417,8 +1418,9 @@ void upnp::return_error(int mapping, int code, mutex::scoped_lock& l) error_string += ": "; error_string += e->msg; } + const int proto = m_mappings[mapping].protocol; l.unlock(); - m_callback(mapping, address(), 0, error_code(code, get_upnp_category())); + m_callback(mapping, address(), 0, proto, error_code(code, get_upnp_category())); l.lock(); } @@ -1471,8 +1473,10 @@ void upnp::on_upnp_unmap_response(error_code const& e , boost::bind(&find_error_code, _1, _2, boost::ref(s))); } + int const proto = m_mappings[mapping].protocol; + l.unlock(); - m_callback(mapping, address(), 0, p.status_code() != 200 + m_callback(mapping, address(), 0, proto, p.status_code() != 200 ? error_code(p.status_code(), get_http_category()) : error_code(s.error_code, get_upnp_category())); l.lock(); From 72a3771b87a2b8711031ab23f7d2a130fa2fe547 Mon Sep 17 00:00:00 2001 From: arvidn Date: Sun, 21 Feb 2016 20:00:55 -0500 Subject: [PATCH 4/6] attempt to fix logic around around actually closing udp sockets (specifically the SSL UDP socket) --- include/libtorrent/udp_socket.hpp | 15 ++--- src/session_impl.cpp | 24 +++++-- src/udp_socket.cpp | 102 ++++++++++++++++++++++++------ 3 files changed, 105 insertions(+), 36 deletions(-) diff --git a/include/libtorrent/udp_socket.hpp b/include/libtorrent/udp_socket.hpp index 675e95748..81f79f6b2 100644 --- a/include/libtorrent/udp_socket.hpp +++ b/include/libtorrent/udp_socket.hpp @@ -79,14 +79,7 @@ namespace libtorrent , dont_queue = 8 }; - bool is_open() const - { - return m_ipv4_sock.is_open() -#if TORRENT_USE_IPV6 - || m_ipv6_sock.is_open() -#endif - ; - } + bool is_open() const { return m_abort == false; } io_service& get_io_service() { return m_ipv4_sock.get_io_service(); } void subscribe(udp_socket_observer* o); @@ -144,7 +137,7 @@ namespace libtorrent udp::endpoint proxy_addr() const { return m_proxy_addr; } - protected: + private: struct queued_packet { @@ -170,12 +163,12 @@ namespace libtorrent ; } - private: - // non-copyable udp_socket(udp_socket const&); udp_socket& operator=(udp_socket const&); + void close_impl(); + // observers on this udp socket std::vector m_observers; std::vector m_added_observers; diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 906a4d156..86d961247 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -2040,7 +2040,7 @@ retry: } #ifdef TORRENT_USE_OPENSSL - int ssl_port = m_settings.get_int(settings_pack::ssl_listen); + int const ssl_port = m_settings.get_int(settings_pack::ssl_listen); udp::endpoint ssl_bind_if(m_listen_interface.address(), ssl_port); // if ssl port is 0, we don't want to listen on an SSL port @@ -2070,6 +2070,8 @@ retry: } else { + m_ssl_udp_socket.close(); + // if there are mappings for the SSL socket, delete them now if (m_ssl_udp_mapping[0] != -1 && m_natpmp) { @@ -2081,7 +2083,6 @@ retry: m_upnp->delete_mapping(m_ssl_udp_mapping[1]); m_ssl_udp_mapping[1] = -1; } - } #endif // TORRENT_USE_OPENSSL @@ -2193,7 +2194,11 @@ retry: if (m_tcp_mapping[0] != -1) m_natpmp->delete_mapping(m_tcp_mapping[0]); m_tcp_mapping[0] = m_natpmp->add_mapping(natpmp::tcp, tcp_port, tcp_port); #ifdef TORRENT_USE_OPENSSL - if (m_ssl_tcp_mapping[0] != -1) m_natpmp->delete_mapping(m_ssl_tcp_mapping[0]); + if (m_ssl_tcp_mapping[0] != -1) + { + m_natpmp->delete_mapping(m_ssl_tcp_mapping[0]); + m_ssl_tcp_mapping[0] = -1; + } if (ssl_port > 0) m_ssl_tcp_mapping[0] = m_natpmp->add_mapping(natpmp::tcp , ssl_port, ssl_port); #endif @@ -2203,7 +2208,11 @@ retry: if (m_tcp_mapping[1] != -1) m_upnp->delete_mapping(m_tcp_mapping[1]); m_tcp_mapping[1] = m_upnp->add_mapping(upnp::tcp, tcp_port, tcp_port); #ifdef TORRENT_USE_OPENSSL - if (m_ssl_tcp_mapping[1] != -1) m_upnp->delete_mapping(m_ssl_tcp_mapping[1]); + if (m_ssl_tcp_mapping[1] != -1) + { + m_upnp->delete_mapping(m_ssl_tcp_mapping[1]); + m_ssl_tcp_mapping[1] = -1; + } if (ssl_port > 0) m_ssl_tcp_mapping[1] = m_upnp->add_mapping(upnp::tcp , ssl_port, ssl_port); #endif @@ -5378,6 +5387,11 @@ retry: boost::uint16_t session_impl::ssl_listen_port() const { #ifdef TORRENT_USE_OPENSSL + + // honor the SSL listen port being disabled + if (m_settings.get_int(settings_pack::ssl_listen) == 0) + return 0; + // if peer connections are set up to be received over a socks // proxy, and it's the same one as we're using for the tracker // just tell the tracker the socks5 port we're listening on @@ -6636,7 +6650,7 @@ retry: , this, _1, 0)); m_natpmp->start(); - int ssl_port = ssl_listen_port(); + int const ssl_port = ssl_listen_port(); if (m_listen_interface.port() > 0) { diff --git a/src/udp_socket.cpp b/src/udp_socket.cpp index f7e26a0ea..a923a8af6 100644 --- a/src/udp_socket.cpp +++ b/src/udp_socket.cpp @@ -78,7 +78,7 @@ udp_socket::udp_socket(io_service& ios) , m_queue_packets(false) , m_tunnel_packets(false) , m_force_proxy(false) - , m_abort(false) + , m_abort(true) , m_outstanding_ops(0) #if TORRENT_USE_IPV6 , m_v6_write_subscribed(false) @@ -307,7 +307,11 @@ void udp_socket::on_read(error_code const& ec, udp::socket* s) } return; } - if (m_abort) return; + if (m_abort) + { + close_impl(); + return; + } CHECK_MAGIC; @@ -493,7 +497,11 @@ void udp_socket::on_read_impl(udp::endpoint const& ep return; } - if (m_abort) return; + if (m_abort) + { + close_impl(); + return; + } return; } @@ -516,7 +524,11 @@ void udp_socket::on_read_impl(udp::endpoint const& ep void udp_socket::setup_read(udp::socket* s) { - if (m_abort) return; + if (m_abort) + { + close_impl(); + return; + } #if TORRENT_USE_IPV6 if (s == &m_ipv6_sock) @@ -774,17 +786,13 @@ void udp_socket::bind(udp::endpoint const& ep, error_code& ec) CHECK_MAGIC; TORRENT_ASSERT(is_single_thread()); - TORRENT_ASSERT(m_abort == false); - if (m_abort) - { - ec = boost::asio::error::operation_aborted; - return; - } + m_abort = false; if (m_ipv4_sock.is_open()) m_ipv4_sock.close(ec); #if TORRENT_USE_IPV6 if (m_ipv6_sock.is_open()) m_ipv6_sock.close(ec); #endif + ec.clear(); if (ep.address().is_v4()) { @@ -858,7 +866,11 @@ void udp_socket::set_proxy_settings(aux::proxy_settings const& ps) m_proxy_settings = ps; - if (m_abort) return; + if (m_abort) + { + close_impl(); + return; + } if (ps.type == settings_pack::socks5 || ps.type == settings_pack::socks5_pw) @@ -880,6 +892,19 @@ void udp_socket::set_proxy_settings(aux::proxy_settings const& ps) } } +void udp_socket::close_impl() +{ + if (m_outstanding_ops == 0) + { + error_code ec; + m_ipv4_sock.close(ec); +#if TORRENT_USE_IPV6 + m_ipv6_sock.close(ec); +#endif + m_socks5_sock.close(ec); + } +} + void udp_socket::on_name_lookup(error_code const& e, tcp::resolver::iterator i) { #if defined TORRENT_ASIO_DEBUGGING @@ -897,7 +922,12 @@ void udp_socket::on_name_lookup(error_code const& e, tcp::resolver::iterator i) + m_outstanding_resolve + m_outstanding_socks); - if (m_abort) return; + if (m_abort) + { + close_impl(); + return; + } + CHECK_MAGIC; if (e == boost::asio::error::operation_aborted) return; @@ -976,7 +1006,11 @@ void udp_socket::on_connect_timeout(error_code const& ec) m_queue_packets = false; - if (m_abort) return; + if (m_abort) + { + close_impl(); + return; + } CHECK_MAGIC; TORRENT_ASSERT(is_single_thread()); @@ -1009,7 +1043,11 @@ void udp_socket::on_connected(error_code const& e) if (e == boost::asio::error::operation_aborted) return; - if (m_abort) return; + if (m_abort) + { + close_impl(); + return; + } if (e) { @@ -1068,7 +1106,11 @@ void udp_socket::handshake1(error_code const& e) + m_outstanding_timeout + m_outstanding_resolve + m_outstanding_socks); - if (m_abort) return; + if (m_abort) + { + close_impl(); + return; + } CHECK_MAGIC; if (e) { @@ -1104,7 +1146,11 @@ void udp_socket::handshake2(error_code const& e) + m_outstanding_timeout + m_outstanding_resolve + m_outstanding_socks); - if (m_abort) return; + if (m_abort) + { + close_impl(); + return; + } CHECK_MAGIC; if (e) @@ -1185,7 +1231,11 @@ void udp_socket::handshake3(error_code const& e) + m_outstanding_timeout + m_outstanding_resolve + m_outstanding_socks); - if (m_abort) return; + if (m_abort) + { + close_impl(); + return; + } CHECK_MAGIC; if (e) { @@ -1221,7 +1271,11 @@ void udp_socket::handshake4(error_code const& e) + m_outstanding_timeout + m_outstanding_resolve + m_outstanding_socks); - if (m_abort) return; + if (m_abort) + { + close_impl(); + return; + } CHECK_MAGIC; if (e) { @@ -1287,7 +1341,11 @@ void udp_socket::connect1(error_code const& e) + m_outstanding_timeout + m_outstanding_resolve + m_outstanding_socks); - if (m_abort) return; + if (m_abort) + { + close_impl(); + return; + } CHECK_MAGIC; if (e) { @@ -1394,7 +1452,11 @@ void udp_socket::hung_up(error_code const& e) + m_outstanding_timeout + m_outstanding_resolve + m_outstanding_socks); - if (m_abort) return; + if (m_abort) + { + close_impl(); + return; + } CHECK_MAGIC; TORRENT_ASSERT(is_single_thread()); From c919c63ca3ac9974ab71959d0c192405de2979a8 Mon Sep 17 00:00:00 2001 From: arvidn Date: Sun, 21 Feb 2016 17:09:41 -0500 Subject: [PATCH 5/6] extend the tutorial --- .travis.yml | 3 + appveyor.yml | 3 + docs/tutorial.rst | 206 +++++++++++++++++++++----- examples/Jamfile | 4 + examples/Makefile.am | 17 +-- examples/bt-get.cpp | 75 ++++++++++ examples/bt-get2.cpp | 142 ++++++++++++++++++ include/libtorrent/alert_types.hpp | 3 +- include/libtorrent/torrent_handle.hpp | 9 +- 9 files changed, 406 insertions(+), 56 deletions(-) create mode 100644 examples/bt-get.cpp create mode 100644 examples/bt-get2.cpp diff --git a/.travis.yml b/.travis.yml index 501a85814..65fba8eb1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -91,6 +91,9 @@ script: - cd ../examples - bjam --hash -j3 warnings-as-errors=on variant=$variant $toolset link=shared + - if [[ lang == "cpp11" ]]; then + bjam --hash -j3 warnings-as-errors=on variant=$variant $toolset link=shared bt-get bt-get2; + fi - cd .. # build libtorrent separately and install it in a temporary (well known) dir diff --git a/appveyor.yml b/appveyor.yml index a080522b2..760ed7577 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -117,6 +117,9 @@ build_script: - if not defined x64 ( cd %ROOT_DIRECTORY%\examples & b2.exe --hash -j2 %compiler% variant=%variant% linkflags=%linkflags% include=%include% link=shared + & if %compiler% == msvc-14.0 ( b2.exe --hash -j2 %compiler% variant=%variant% linkflags=%linkflags% include=%include% link=shared bt-get bt-get2 ) + & cd %ROOT_DIRECTORY%\examples + & b2.exe --hash -j2 %compiler% variant=%variant% linkflags=%linkflags% include=%include% link=shared & cd %ROOT_DIRECTORY%\bindings\python & b2.exe --hash -j2 %compiler% stage_module install-dependencies=on variant=%variant% libtorrent-link=shared linkflags=%linkflags% include=%include% & python test.py diff --git a/docs/tutorial.rst b/docs/tutorial.rst index cd165f650..511e52281 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -29,7 +29,7 @@ For example: #include #include #include - + namespace lt = libtorrent; int main(int argc, char const* argv[]) { @@ -70,46 +70,165 @@ completes downloading, we can poll the session for alerts periodically and print them out, as well as listening for the torrent_finished_alert_, which is posted when a torrent completes. +.. include:: ../examples/bt-get.cpp + :code: c++ + :tab-width: 2 + :start-after: */ + +alert masks +----------- + +The output from this program will be quite verbose, which is probably a good +starting point to get some understanding of what's going on. Alerts are +categorized into alert categories. Each category can be enabled and disabled +independently via the *alert mask*. + +The alert mask is a configuration option offered by libtorrent. There are many +configuration options, see settings_pack_. The alert_mask_ setting is an integer +of the `category flags`_ ORed together. + +For instance, to only see the most pertinent alerts, the session can be +constructed like this: + .. code:: c++ - #include - - #include - #include - #include - #include - - namespace lt = libtorrent; - int main(int argc, char const* argv[]) + lt::settings_pack pack; + pack.set_int(lt::settings_pack::alert_mask + , lt::alert::error_notification + | lt::alert::storage_notification + | lt::alert::status_notification); + + lt::session ses(pack); + +Configuration options can be updated after the session is started by calling +`apply_settings()`_. Some settings are best set before starting the session +though, like listen_interfaces_, to avoid race conditions. If you start the +session with the default settings and then immediately change them, there will +still be a window where the default settings apply. + +Changing the settings may trigger listen sockets to close and re-open and +NAT-PMP, UPnP updates to be sent. For this reason, it's typically a good idea +to batch settings updates into a single call. + +session destruction +------------------- + +The session destructor is blocking by default. When shutting down, trackers +will need to be contacted to stop torrents and other outstanding operations +need to be cancelled. Shutting down can sometimes take several seconds, +primarily because of trackers that are unresponsive (and time out) and also +DNS servers that are unresponsive. DNS lookups are especially difficult to +abort when stalled. + +In order to be able to start destruction an wait for it asynchronously, one +can call `session::abort()`_. + +This call returns a session_proxy_ object, which is a handle keeping the session +state alive while destructing it. It deliberately does not provide any of the +session operations, since it's shutting down. + +After having a session_proxy_ object, the session destructor does not block. +However, the session_proxy_ destructor *will*. + +This can be used to shut down multiple sessions or other parts of the +application in parallel. + +asynchronous operations +----------------------- + +Essentially any call to a member function of session_ or torrent_handle_ that +returns a value is a blocking synchronous call. Meaning it will post a message +to the main libtorrent thread and wait for a response. Such calls may be +expensive, and in applications where stalls should be avoided (such as user +interface threads), blocking calls should be avoided. + +In the example above, session::add_torrent() returns a torrent_handle_ and is +thus blocking. For higher efficiency, `async_add_torrent()`_ will post a message +to the main thread to add a torrent, and post the resulting torrent_handle_ back +in an alert (add_torrent_alert_). This is especially useful when adding a lot +of torrents in quick succession, as there's no stall in between calls. + +In the example above, we don't actually use the torrent_handle_ for anything, so +converting it to use `async_add_torrent()`_ is just a matter of replacing the +`add_torrent()`_ call with `async_add_torrent()`_. + +torrent_status_updates +---------------------- + +To get updates to the status of torrents, call `post_torrent_updates()`_ on the +session object. This will cause libtorrent to post a state_update_alert_ +containing torrent_status_ objects for all torrents whose status has *changed* +since the last call to `post_torrent_updates()`_. + +The state_update_alert_ looks something like this: + +.. code:: c++ + + struct state_update_alert : alert { - if (argc != 2) { - std::cerr << "usage: " << argv[0] << " " << std::endl; - return 1; - } - lt::session ses; + virtual std::string message() const; + std::vector status; + }; - lt::add_torrent_params atp; - atp.url = argv[1]; - atp.save_path = "."; // save in current dir - lt::torrent_handle h = ses.add_torrent(atp); +The ``status`` field only contains the torrent_status_ for torrents with +updates since the last call. It may be empty if no torrent has updated its +state. This feature is critical for scalability_. - bool done = false; - while (!done) { - std::vector alerts; - ses.pop_alerts(&alerts); +See the torrent_status_ object for more information on what is in there. +Perhaps the most interesting fields are ``total_payload_download``, +``total_payload_upload``, ``num_peers`` and ``state``. - for (lt::alert const* a : alerts) { - std::cout << a->message() << std::endl; - if (lt::alert_cast(a)) { - done = true; - } - } - } - } +resuming torrents +----------------- -*TODO* cover async_add_torrent() -*TODO* cover post_torrent_updates() -*TODO* cover save_resume_data() +Since bittorrent downloads pieces of files in random order, it's not trivial to +resume a partial download. When resuming a download, the bittorrent engine must +restore the state of the downloading torrent, specifically, which parts of the +file(s) are downloaded. There are two approaches to doing this: + +1. read every piece of the downloaded files from disk and compare it against the + expected hash. +2. save to disk the state of which pieces (and partial pieces) are downloaded, + and load it back in again when resuming. + +If no resume data is provided with a torrent that's added, libtorrent will +employ (1) by default. + +To save resume data, call `save_resume_data()`_ on the torrent_handle_ object. +This will ask libtorrent to generate the resume data and post it back in +a save_resume_data_alert_. If generating the resume data fails for any reason, +a save_resume_data_failed_alert_ is posted instead. Exactly one of those alerts +will be posted for every call to `save_resume_data()`_. + +The save_resume_data_alert_ looks something like this: + +.. code:: c++ + + struct save_resume_data_alert : torrent_alert + { + virtual std::string message() const; + + // points to the resume data. + boost::shared_ptr resume_data; + }; + +``resume_data`` points to an entry_ object. This represents a node or a tree +of nodes in a bencoded_ structure, which is the native encoding scheme in +bittorrent. It can be encoded into a byte buffer or file using `bencode()`_. + +example +------- + +Here's an updated version of the above example with the following updates: + +1. not using blocking calls +2. printing torrent status updates rather than the raw log +3. saving and loading resume files + +.. include:: ../examples/bt-get2.cpp + :code: c++ + :tab-width: 2 + :start-after: */ .. _session: reference-Core.html#session .. _session_handle: reference-Core.html#session_handle @@ -121,5 +240,22 @@ when a torrent completes. .. _`alert`: reference-Alerts.html#alert .. _`alert_cast<>`: reference-Alerts.html#alert_cast() .. _torrent_finished_alert: reference-Alerts.html#torrent-finished-alert - +.. _listen_interfaces: reference-Settings.html#listen_interfaces +.. _`add_torrent_alert`: reference-Alerts.html#add-torrent-alert +.. _settings_pack: reference-Settings.html#settings_pack +.. _alert_mask: reference-Settings.html#alert_mask +.. _`category flags`: reference-Alerts.html#category_t +.. _`apply_settings()`: reference-Core.html#apply_settings() +.. _`session::abort()`: reference-Core.html#abort() +.. _session_proxy: reference-Core.html#session_proxy +.. _`post_torrent_updates()`: reference-Core.html#post_torrent_updates() +.. _torrent_status: reference-Core.html#torrent_status +.. _state_update_alert: reference-Alerts.html#state_update_alert +.. _scalability: http://blog.libtorrent.org/2011/11/scalable-interfaces/ +.. _`save_resume_data()`: reference-Core.html#save_resume_data() +.. _save_resume_data_alert: reference-Alerts.html#save_resume_data_alert +.. _save_resume_data_failed_alert: reference-Alerts.html#save_resume_data_failed_alert +.. _bencoded: https://en.wikipedia.org/wiki/Bencode +.. _entry: reference-Bencoding.html#entry +.. _`bencode()`: reference-Bencoding.html#bencode() diff --git a/examples/Jamfile b/examples/Jamfile index 0b6057d47..6c885ac1a 100644 --- a/examples/Jamfile +++ b/examples/Jamfile @@ -20,6 +20,8 @@ project client_test exe client_test : client_test.cpp print.cpp torrent_view.cpp session_view.cpp ; exe simple_client : simple_client.cpp ; +exe bt-get : bt-get.cpp ; +exe bt-get2 : bt-get2.cpp ; exe stats_counters : stats_counters.cpp ; exe dump_torrent : dump_torrent.cpp ; exe make_torrent : make_torrent.cpp ; @@ -28,6 +30,8 @@ exe upnp_test : upnp_test.cpp ; explicit stage_client_test ; explicit stage_connection_tester ; +explicit bt-get ; +explicit bt-get2 ; install stage_client_test : client_test : . ; install stage_connection_tester : connection_tester : . ; diff --git a/examples/Makefile.am b/examples/Makefile.am index ccedaa87b..4ced3561c 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -5,6 +5,8 @@ example_programs = \ make_torrent \ simple_client \ upnp_test \ + bt_get \ + bt_get2 \ connection_tester if ENABLE_EXAMPLES @@ -15,25 +17,14 @@ EXTRA_PROGRAMS = $(example_programs) EXTRA_DIST = Jamfile CMakeLists.txt run_cmake.sh.in cmake/FindLibtorrentRasterbar.cmake client_test_SOURCES = client_test.cpp print.cpp session_view.cpp torrent_view.cpp -#client_test_LDADD = $(top_builddir)/src/libtorrent-rasterbar.la - stats_counters_SOURCES = stats_counters.cpp -#stats_counters_LDADD = $(top_builddir)/src/libtorrent-rasterbar.la - +bt_get_SOURCES = bt-get.cpp +bt_get2_SOURCES = bt-get2.cpp dump_torrent_SOURCES = dump_torrent.cpp -#dump_torrent_LDADD = $(top_builddir)/src/libtorrent-rasterbar.la - make_torrent_SOURCES = make_torrent.cpp -#make_torrent_LDADD = $(top_builddir)/src/libtorrent-rasterbar.la - simple_client_SOURCES = simple_client.cpp -#simple_client_LDADD = $(top_builddir)/src/libtorrent-rasterbar.la - connection_tester_SOURCES = connection_tester.cpp -#connection_tester_LDADD = $(top_builddir)/src/libtorrent-rasterbar.la - upnp_test_SOURCES = upnp_test.cpp -#upnp_test_LDADD = $(top_builddir)/src/libtorrent-rasterbar.la LDADD = $(top_builddir)/src/libtorrent-rasterbar.la diff --git a/examples/bt-get.cpp b/examples/bt-get.cpp new file mode 100644 index 000000000..0029a81bf --- /dev/null +++ b/examples/bt-get.cpp @@ -0,0 +1,75 @@ +/* + +Copyright (c) 2016, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include + +#include +#include +#include +#include + +namespace lt = libtorrent; +int main(int argc, char const* argv[]) +{ + if (argc != 2) { + std::cerr << "usage: " << argv[0] << " " << std::endl; + return 1; + } + lt::session ses; + + lt::add_torrent_params atp; + atp.url = argv[1]; + atp.save_path = "."; // save in current dir + lt::torrent_handle h = ses.add_torrent(atp); + + for (;;) { + std::vector alerts; + ses.pop_alerts(&alerts); + + for (lt::alert const* a : alerts) { + std::cout << a->message() << std::endl; + // if we receive the finished alert or an error, we're done + if (lt::alert_cast(a)) { + goto done; + } + if (lt::alert_cast(a)) { + goto done; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + done: + std::cout << "done, shutting down" << std::endl; +} + diff --git a/examples/bt-get2.cpp b/examples/bt-get2.cpp new file mode 100644 index 000000000..ab0873747 --- /dev/null +++ b/examples/bt-get2.cpp @@ -0,0 +1,142 @@ +/* + +Copyright (c) 2016, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace lt = libtorrent; + +// return the name of a torrent status enum +char const* state(lt::torrent_status::state_t s) +{ + switch(s) + { + case lt::torrent_status::checking_files: return "checking"; + case lt::torrent_status::downloading_metadata: return "dl metadata"; + case lt::torrent_status::downloading: return "downloading"; + case lt::torrent_status::finished: return "finished"; + case lt::torrent_status::seeding: return "seeding"; + case lt::torrent_status::allocating: return "allocating"; + case lt::torrent_status::checking_resume_data: return "checking resume"; + default: return "<>"; + } +} + +int main(int argc, char const* argv[]) +{ + if (argc != 2) { + std::cerr << "usage: " << argv[0] << " " << std::endl; + return 1; + } + + lt::settings_pack pack; + pack.set_int(lt::settings_pack::alert_mask + , lt::alert::error_notification + | lt::alert::storage_notification + | lt::alert::status_notification); + + lt::session ses(pack); + + lt::add_torrent_params atp; + + // load resume data from disk and pass it in as we add the magnet link + std::ifstream ifs(".resume_file", std::ios_base::binary); + ifs.unsetf(std::ios_base::skipws); + atp.resume_data.assign(std::istream_iterator(ifs) + , std::istream_iterator()); + atp.url = argv[1]; + atp.save_path = "."; // save in current dir + ses.async_add_torrent(atp); + + // this is the handle we'll set once we get the notification of it being + // added + lt::torrent_handle h; + for (;;) { + std::vector alerts; + ses.pop_alerts(&alerts); + + for (lt::alert const* a : alerts) { + if (auto at = lt::alert_cast(a)) { + h = at->handle; + } + // if we receive the finished alert or an error, we're done + if (lt::alert_cast(a)) { + h.save_resume_data(); + goto done; + } + if (lt::alert_cast(a)) { + std::cout << a->message() << std::endl; + goto done; + } + + // when resume data is ready, save it + if (auto rd = lt::alert_cast(a)) { + std::ofstream of(".resume_file", std::ios_base::binary); + of.unsetf(std::ios_base::skipws); + lt::bencode(std::ostream_iterator(of) + , *rd->resume_data); + } + + if (auto st = lt::alert_cast(a)) { + if (st->status.empty()) continue; + + // we only have a single torrent, so we know which one + // the status is for + lt::torrent_status const& s = st->status[0]; + std::cout << "\r" << state(s.state) << " " + << (s.download_payload_rate / 1000) << " kB/s " + << (s.total_done / 1000) << " kB (" + << (s.progress_ppm / 10000) << "%) downloaded\x1b[K"; + std::cout.flush(); + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + + // ask the session to post a state_update_alert, to update our + // state output for the torrent + ses.post_torrent_updates(); + + // TODO: 3 call save_resume_data() once every 30 seconds or so + } + done: + std::cout << "\ndone, shutting down" << std::endl; +} + diff --git a/include/libtorrent/alert_types.hpp b/include/libtorrent/alert_types.hpp index 6b995ddba..da9ce25bf 100644 --- a/include/libtorrent/alert_types.hpp +++ b/include/libtorrent/alert_types.hpp @@ -1942,8 +1942,7 @@ namespace libtorrent TORRENT_DEFINE_ALERT(dht_error_alert, 73) - static const int static_category = alert::error_notification - | alert::dht_notification; + static const int static_category = alert::error_notification | alert::dht_notification; virtual std::string message() const TORRENT_OVERRIDE; // the error code diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp index c04dfaadb..dcb528467 100644 --- a/include/libtorrent/torrent_handle.hpp +++ b/include/libtorrent/torrent_handle.hpp @@ -662,9 +662,8 @@ namespace libtorrent only_if_modified = 4 }; - // ``save_resume_data()`` generates fast-resume data and returns it as an - // entry. This entry is suitable for being bencoded. For more information - // about how fast-resume works, see fast-resume_. + // ``save_resume_data()`` asks libtorrent to generate fast-resume data for + // this torrent. // // The ``flags`` argument is a bitmask of flags ORed together. see // save_resume_flags_t @@ -676,9 +675,7 @@ namespace libtorrent // The fast resume data will be empty in the following cases: // // 1. The torrent handle is invalid. - // 2. The torrent is checking (or is queued for checking) its storage, it - // will obviously not be ready to write resume data. - // 3. The torrent hasn't received valid metadata and was started without + // 2. The torrent hasn't received valid metadata and was started without // metadata (see libtorrent's metadata-from-peers_ extension) // // Note that by the time you receive the fast resume data, it may already From 5d7fbd42c786889bb403b82c952de1f949fe7203 Mon Sep 17 00:00:00 2001 From: arvidn Date: Sun, 21 Feb 2016 23:17:47 -0500 Subject: [PATCH 6/6] clean up TORRENT_FINAL and TORRENT_OVERRIDE in documentation --- docs/gen_reference_doc.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/gen_reference_doc.py b/docs/gen_reference_doc.py index 10c0645f4..029e95801 100644 --- a/docs/gen_reference_doc.py +++ b/docs/gen_reference_doc.py @@ -149,6 +149,7 @@ def is_visible(desc): return True def highlight_signature(s): + s = s.replace('TORRENT_OVERRIDE', 'override').replace('TORRENT_FINAL', 'final') name = s.split('(', 1) name2 = name[0].split(' ') if len(name2[-1]) == 0: return s @@ -273,7 +274,8 @@ def parse_class(lno, lines, filename): state = 'private' class_type = 'class' - name = decl.split(':')[0].replace('class ', '').replace('struct ', '').replace('TORRENT_FINAL', '').strip() + decl = decl.replace('TORRENT_FINAL', 'final') + name = decl.split(':')[0].replace('class ', '').replace('struct ', '').replace('final', '').strip() while lno < len(lines):