From f5868df3ad4294404c937553fdf03b2d107abe71 Mon Sep 17 00:00:00 2001 From: Marcos Pinto Date: Sat, 22 Sep 2007 17:50:59 +0000 Subject: [PATCH] ipv6 fix, race condition fix, varient_stream fixes --- libtorrent/include/libtorrent/alert_types.hpp | 21 +- .../include/libtorrent/aux_/session_impl.hpp | 52 ++- .../include/libtorrent/broadcast_socket.hpp | 1 + libtorrent/include/libtorrent/enum_net.hpp | 2 +- .../libtorrent/http_tracker_connection.hpp | 4 + libtorrent/include/libtorrent/socket.hpp | 10 + .../include/libtorrent/tracker_manager.hpp | 1 + .../include/libtorrent/variant_stream.hpp | 38 +- libtorrent/src/alert.cpp | 2 +- libtorrent/src/broadcast_socket.cpp | 8 + libtorrent/src/bt_peer_connection.cpp | 13 +- libtorrent/src/enum_net.cpp | 5 +- libtorrent/src/http_tracker_connection.cpp | 22 +- libtorrent/src/policy.cpp | 27 +- libtorrent/src/session_impl.cpp | 351 +++++++++++------- libtorrent/src/torrent.cpp | 7 +- libtorrent/src/upnp.cpp | 25 +- 17 files changed, 394 insertions(+), 195 deletions(-) diff --git a/libtorrent/include/libtorrent/alert_types.hpp b/libtorrent/include/libtorrent/alert_types.hpp index 36c13c5ab..bfbfa8ae4 100755 --- a/libtorrent/include/libtorrent/alert_types.hpp +++ b/libtorrent/include/libtorrent/alert_types.hpp @@ -328,14 +328,33 @@ namespace libtorrent struct TORRENT_EXPORT listen_failed_alert: alert { listen_failed_alert( - const std::string& msg) + tcp::endpoint const& ep + , std::string const& msg) : alert(alert::fatal, msg) + , endpoint(ep) {} + tcp::endpoint endpoint; + virtual std::auto_ptr clone() const { return std::auto_ptr(new listen_failed_alert(*this)); } }; + struct TORRENT_EXPORT listen_succeeded_alert: alert + { + listen_succeeded_alert( + tcp::endpoint const& ep + , std::string const& msg) + : alert(alert::fatal, msg) + , endpoint(ep) + {} + + tcp::endpoint endpoint; + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new listen_succeeded_alert(*this)); } + }; + struct TORRENT_EXPORT portmap_error_alert: alert { portmap_error_alert(const std::string& msg) diff --git a/libtorrent/include/libtorrent/aux_/session_impl.hpp b/libtorrent/include/libtorrent/aux_/session_impl.hpp index 9300a1ce3..afb358afe 100644 --- a/libtorrent/include/libtorrent/aux_/session_impl.hpp +++ b/libtorrent/include/libtorrent/aux_/session_impl.hpp @@ -189,11 +189,16 @@ namespace libtorrent #endif void operator()(); - void open_listen_port(); + void open_listen_port() throw(); - void async_accept(); + // if we are listening on an IPv6 interface + // this will return one of the IPv6 addresses on this + // machine, otherwise just an empty endpoint + tcp::endpoint get_ipv6_interface() const; + + void async_accept(boost::shared_ptr const& listener); void on_incoming_connection(boost::shared_ptr const& s - , boost::weak_ptr const& as, asio::error_code const& e); + , boost::weak_ptr listener, asio::error_code const& e); // must be locked to access the data // in this struct @@ -393,8 +398,10 @@ namespace libtorrent // at startup int m_key; - // the range of ports we try to listen on - std::pair m_listen_port_range; + // the number of retries we make when binding the + // listen socket. For each retry the port number + // is incremented by one + int m_listen_port_retries; // the ip-address of the interface // we are supposed to listen on. @@ -402,17 +409,32 @@ namespace libtorrent // that we should let the os decide which // interface to listen on tcp::endpoint m_listen_interface; - - // this is typically set to the same as the local - // listen port. In case a NAT port forward was - // successfully opened, this will be set to the - // port that is open on the external (NAT) interface - // on the NAT box itself. This is the port that has - // to be published to peers, since this is the port - // the client is reachable through. - int m_external_listen_port; - boost::shared_ptr m_listen_socket; + // if we're listening on an IPv6 interface + // this is one of the non local IPv6 interfaces + // on this machine + tcp::endpoint m_ipv6_interface; + + struct listen_socket_t + { + listen_socket_t(): external_port(0) {} + // this is typically set to the same as the local + // listen port. In case a NAT port forward was + // successfully opened, this will be set to the + // port that is open on the external (NAT) interface + // on the NAT box itself. This is the port that has + // to be published to peers, since this is the port + // the client is reachable through. + int external_port; + + // the actual socket + boost::shared_ptr sock; + }; + // since we might be listening on multiple interfaces + // we might need more than one listen socket + std::list m_listen_sockets; + + listen_socket_t setup_listener(tcp::endpoint ep, int retries); // the settings for the client session_settings m_settings; diff --git a/libtorrent/include/libtorrent/broadcast_socket.hpp b/libtorrent/include/libtorrent/broadcast_socket.hpp index 23be67b0d..485d1bd1f 100644 --- a/libtorrent/include/libtorrent/broadcast_socket.hpp +++ b/libtorrent/include/libtorrent/broadcast_socket.hpp @@ -44,6 +44,7 @@ namespace libtorrent bool is_local(address const& a); bool is_loopback(address const& addr); bool is_multicast(address const& addr); + bool is_any(address const& addr); address_v4 guess_local_address(asio::io_service&); diff --git a/libtorrent/include/libtorrent/enum_net.hpp b/libtorrent/include/libtorrent/enum_net.hpp index 0c6063a2b..0da76ff36 100644 --- a/libtorrent/include/libtorrent/enum_net.hpp +++ b/libtorrent/include/libtorrent/enum_net.hpp @@ -37,7 +37,7 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { - std::vector
const& enum_net_interfaces(asio::io_service& ios, asio::error_code& ec); + std::vector
enum_net_interfaces(asio::io_service& ios, asio::error_code& ec); } #endif diff --git a/libtorrent/include/libtorrent/http_tracker_connection.hpp b/libtorrent/include/libtorrent/http_tracker_connection.hpp index 35d529504..76c3aac98 100755 --- a/libtorrent/include/libtorrent/http_tracker_connection.hpp +++ b/libtorrent/include/libtorrent/http_tracker_connection.hpp @@ -73,6 +73,8 @@ namespace libtorrent T header(char const* key) const; std::string const& protocol() const { return m_protocol; } int status_code() const { return m_status_code; } + std::string const& method() const { return m_method; } + std::string const& path() const { return m_path; } std::string message() const { return m_server_message; } buffer::const_interval get_body() const; bool header_finished() const { return m_state == read_body; } @@ -85,6 +87,8 @@ namespace libtorrent private: int m_recv_pos; int m_status_code; + std::string m_method; + std::string m_path; std::string m_protocol; std::string m_server_message; diff --git a/libtorrent/include/libtorrent/socket.hpp b/libtorrent/include/libtorrent/socket.hpp index c478a92ef..0b8d44968 100755 --- a/libtorrent/include/libtorrent/socket.hpp +++ b/libtorrent/include/libtorrent/socket.hpp @@ -98,6 +98,16 @@ namespace libtorrent typedef asio::basic_deadline_timer deadline_timer; + inline std::ostream& operator<<(std::ostream& os, tcp::endpoint const& ep) + { + address const& a = ep.address(); + if (a.is_v6()) + os << "[" << a.to_string() << "]:" << ep.port(); + else + os << a.to_string() << ":" << ep.port(); + return os; + } + namespace detail { template diff --git a/libtorrent/include/libtorrent/tracker_manager.hpp b/libtorrent/include/libtorrent/tracker_manager.hpp index 57f7bd851..b1b4d87ac 100755 --- a/libtorrent/include/libtorrent/tracker_manager.hpp +++ b/libtorrent/include/libtorrent/tracker_manager.hpp @@ -113,6 +113,7 @@ namespace libtorrent std::string url; int key; int num_want; + std::string ipv6; }; struct TORRENT_EXPORT request_callback diff --git a/libtorrent/include/libtorrent/variant_stream.hpp b/libtorrent/include/libtorrent/variant_stream.hpp index 8f9888519..4b32c9349 100644 --- a/libtorrent/include/libtorrent/variant_stream.hpp +++ b/libtorrent/include/libtorrent/variant_stream.hpp @@ -232,18 +232,18 @@ namespace aux // -------------- remote_endpoint ----------- - template - struct remote_endpoint_visitor + template + struct remote_endpoint_visitor_ec : boost::static_visitor { - remote_endpoint_visitor(Error_Handler const& error_handler) - : error_handler(error_handler) + remote_endpoint_visitor_ec(asio::error_code& ec) + : error_code(ec) {} template EndpointType operator()(T* p) const { - return p->remote_endpoint(error_handler); + return p->remote_endpoint(error_code); } EndpointType operator()(boost::blank) const @@ -251,11 +251,11 @@ namespace aux return EndpointType(); } - Error_Handler const& error_handler; + asio::error_code& error_code; }; template - struct remote_endpoint_visitor + struct remote_endpoint_visitor : boost::static_visitor { template @@ -272,18 +272,18 @@ namespace aux // -------------- local_endpoint ----------- - template - struct local_endpoint_visitor + template + struct local_endpoint_visitor_ec : boost::static_visitor { - local_endpoint_visitor(Error_Handler const& error_handler) - : error_handler(error_handler) + local_endpoint_visitor_ec(asio::error_code& ec) + : error_code(ec) {} template EndpointType operator()(T* p) const { - return p->local_endpoint(error_handler); + return p->local_endpoint(error_code); } EndpointType operator()(boost::blank) const @@ -291,11 +291,11 @@ namespace aux return EndpointType(); } - Error_Handler const& error_handler; + asio::error_code& error_code; }; template - struct local_endpoint_visitor + struct local_endpoint_visitor : boost::static_visitor { template @@ -665,12 +665,11 @@ public: return boost::apply_visitor(aux::remote_endpoint_visitor(), m_variant); } - template - endpoint_type remote_endpoint(Error_Handler const& error_handler) + endpoint_type remote_endpoint(asio::error_code& ec) { assert(instantiated()); return boost::apply_visitor( - aux::remote_endpoint_visitor(error_handler), m_variant + aux::remote_endpoint_visitor_ec(ec), m_variant ); } @@ -680,12 +679,11 @@ public: return boost::apply_visitor(aux::local_endpoint_visitor(), m_variant); } - template - endpoint_type local_endpoint(Error_Handler const& error_handler) + endpoint_type local_endpoint(asio::error_code& ec) { assert(instantiated()); return boost::apply_visitor( - aux::local_endpoint_visitor(error_handler), m_variant + aux::local_endpoint_visitor_ec(ec), m_variant ); } diff --git a/libtorrent/src/alert.cpp b/libtorrent/src/alert.cpp index b990db7e1..80ae6ead8 100755 --- a/libtorrent/src/alert.cpp +++ b/libtorrent/src/alert.cpp @@ -65,7 +65,7 @@ namespace libtorrent { alert_manager::alert_manager() - : m_severity(alert::none) + : m_severity(alert::fatal) {} alert_manager::~alert_manager() diff --git a/libtorrent/src/broadcast_socket.cpp b/libtorrent/src/broadcast_socket.cpp index 3aaadcc81..359c0e0d2 100644 --- a/libtorrent/src/broadcast_socket.cpp +++ b/libtorrent/src/broadcast_socket.cpp @@ -84,6 +84,14 @@ namespace libtorrent return addr.to_v6().is_multicast(); } + bool is_any(address const& addr) + { + if (addr.is_v4()) + return addr.to_v4() == address_v4::any(); + else + return addr.to_v6() == address_v6::any(); + } + broadcast_socket::broadcast_socket(asio::io_service& ios , udp::endpoint const& multicast_endpoint , receive_handler_t const& handler diff --git a/libtorrent/src/bt_peer_connection.cpp b/libtorrent/src/bt_peer_connection.cpp index ab61d98f9..e27a7f4c3 100755 --- a/libtorrent/src/bt_peer_connection.cpp +++ b/libtorrent/src/bt_peer_connection.cpp @@ -50,7 +50,6 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/version.hpp" #include "libtorrent/extensions.hpp" #include "libtorrent/aux_/session_impl.hpp" -#include "libtorrent/enum_net.hpp" #ifndef TORRENT_DISABLE_ENCRYPTION #include "libtorrent/pe_crypto.hpp" @@ -1475,18 +1474,14 @@ namespace libtorrent detail::write_address(remote().address(), out); handshake["yourip"] = remote_address; handshake["reqq"] = m_ses.settings().max_allowed_in_request_queue; - asio::error_code ec; - std::vector
const& interfaces = enum_net_interfaces(get_socket()->io_service(), ec); - for (std::vector
::const_iterator i = interfaces.begin() - , end(interfaces.end()); i != end; ++i) + + tcp::endpoint ep = m_ses.get_ipv6_interface(); + if (ep != tcp::endpoint()) { - // TODO: only use global IPv6 addresses - if (!i->is_v6() || i->to_v6().is_link_local()) continue; std::string ipv6_address; std::back_insert_iterator out(ipv6_address); - detail::write_address(*i, out); + detail::write_address(ep.address(), out); handshake["ipv6"] = ipv6_address; - break; } // loop backwards, to make the first extension be the last diff --git a/libtorrent/src/enum_net.cpp b/libtorrent/src/enum_net.cpp index 172719793..94c74e855 100644 --- a/libtorrent/src/enum_net.cpp +++ b/libtorrent/src/enum_net.cpp @@ -40,10 +40,9 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { - std::vector
const& enum_net_interfaces(asio::io_service& ios, asio::error_code& ec) + std::vector
enum_net_interfaces(asio::io_service& ios, asio::error_code& ec) { - static std::vector
ret; - if (!ret.empty()) return ret; + std::vector
ret; #if defined __linux__ || defined __MACH__ || defined(__FreeBSD__) int s = socket(AF_INET, SOCK_DGRAM, 0); diff --git a/libtorrent/src/http_tracker_connection.cpp b/libtorrent/src/http_tracker_connection.cpp index 3fcd97dbc..321a57df7 100755 --- a/libtorrent/src/http_tracker_connection.cpp +++ b/libtorrent/src/http_tracker_connection.cpp @@ -148,13 +148,19 @@ namespace libtorrent pos = newline; line >> m_protocol; - if (m_protocol.substr(0, 5) != "HTTP/") + if (m_protocol.substr(0, 5) == "HTTP/") { - throw std::runtime_error("unknown protocol in HTTP response: " - + m_protocol + " line: " + std::string(pos, newline)); + line >> m_status_code; + std::getline(line, m_server_message); + } + else + { + m_method = m_protocol; + std::transform(m_method.begin(), m_method.end(), m_method.begin(), &to_lower); + m_protocol.clear(); + line >> m_path >> m_protocol; + m_status_code = 0; } - line >> m_status_code; - std::getline(line, m_server_message); m_state = read_header; } @@ -422,6 +428,12 @@ namespace libtorrent m_send_buffer += "supportcrypto=1&"; #endif + if (!url_has_argument(request, "ipv6") && !req.ipv6.empty()) + { + m_send_buffer += "ipv6="; + m_send_buffer += req.ipv6; + } + // extension that tells the tracker that // we don't need any peer_id's in the response if (!url_has_argument(request, "no_peer_id")) diff --git a/libtorrent/src/policy.cpp b/libtorrent/src/policy.cpp index 4faed837e..49671c5db 100755 --- a/libtorrent/src/policy.cpp +++ b/libtorrent/src/policy.cpp @@ -162,6 +162,18 @@ namespace tcp::endpoint const& m_ep; }; + struct match_peer_id + { + match_peer_id(peer_id const& id_) + : m_id(id_) + {} + + bool operator()(policy::peer const& p) const + { return p.connection && p.connection->pid() == m_id; } + + peer_id const& m_id; + }; + struct match_peer_connection { match_peer_connection(peer_connection const& c) @@ -1030,6 +1042,12 @@ namespace libtorrent m_peers.begin() , m_peers.end() , match_peer_endpoint(remote)); + + if (i == m_peers.end()) + i = std::find_if( + m_peers.begin() + , m_peers.end() + , match_peer_id(pid)); } else { @@ -1156,7 +1174,7 @@ namespace libtorrent // data from now on void policy::unchoked(peer_connection& c) { - INVARIANT_CHECK; +// INVARIANT_CHECK; if (c.is_interesting()) { request_a_block(*m_torrent, c); @@ -1166,7 +1184,7 @@ namespace libtorrent // called when a peer is interested in us void policy::interested(peer_connection& c) { - INVARIANT_CHECK; +// INVARIANT_CHECK; assert(std::find_if(m_peers.begin(), m_peers.end() , boost::bind(std::equal_to(), bind(&peer::connection, _1) @@ -1278,7 +1296,7 @@ namespace libtorrent */ bool policy::connect_one_peer() { - INVARIANT_CHECK; +// INVARIANT_CHECK; assert(m_torrent->want_more_peers()); @@ -1291,7 +1309,6 @@ namespace libtorrent try { - INVARIANT_CHECK; p->connected = time_now(); p->connection = m_torrent->connect_to_peer(&*p); assert(p->connection == m_torrent->connection_for(p->ip)); @@ -1375,7 +1392,7 @@ namespace libtorrent void policy::peer_is_interesting(peer_connection& c) { - INVARIANT_CHECK; +// INVARIANT_CHECK; c.send_interested(); if (c.has_peer_choked() diff --git a/libtorrent/src/session_impl.cpp b/libtorrent/src/session_impl.cpp index 6e1129b99..52e04a6c4 100755 --- a/libtorrent/src/session_impl.cpp +++ b/libtorrent/src/session_impl.cpp @@ -73,6 +73,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/socket.hpp" #include "libtorrent/aux_/session_impl.hpp" #include "libtorrent/kademlia/dht_tracker.hpp" +#include "libtorrent/enum_net.hpp" #ifndef TORRENT_DISABLE_ENCRYPTION @@ -512,9 +513,8 @@ namespace detail , m_download_channel(m_io_service, peer_connection::download_channel) , m_upload_channel(m_io_service, peer_connection::upload_channel) , m_tracker_manager(m_settings, m_tracker_proxy) - , m_listen_port_range(listen_port_range) + , m_listen_port_retries(listen_port_range.second - listen_port_range.first) , m_listen_interface(address::from_string(listen_interface), listen_port_range.first) - , m_external_listen_port(0) , m_abort(false) , m_max_uploads(8) , m_max_connections(200) @@ -661,129 +661,223 @@ namespace detail *i = ' '; } - void session_impl::open_listen_port() + tcp::endpoint session_impl::get_ipv6_interface() const { - try - { - // create listener socket - m_listen_socket.reset(new socket_acceptor(m_io_service)); + return m_ipv6_interface; + } - for(;;) - { - try - { - m_listen_socket->open(m_listen_interface.protocol()); - m_listen_socket->set_option(socket_acceptor::reuse_address(true)); - m_listen_socket->bind(m_listen_interface); - m_listen_socket->listen(); - m_listen_interface = m_listen_socket->local_endpoint(); - m_external_listen_port = m_listen_interface.port(); - break; - } - catch (asio::system_error& e) - { - // TODO: make sure this is correct - if (e.code() == asio::error::host_not_found - || m_listen_interface.port() == 0) - { - if (m_alerts.should_post(alert::fatal)) - { - std::string msg = "cannot listen on the given interface '" - + m_listen_interface.address().to_string() + "'"; - m_alerts.post_alert(listen_failed_alert(msg)); - } -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - std::string msg = "cannot listen on the given interface '" - + m_listen_interface.address().to_string() + "'"; - (*m_logger) << msg << "\n"; -#endif - assert(m_listen_socket.unique()); - m_listen_socket.reset(); - break; - } - m_listen_socket->close(); - m_listen_interface.port(m_listen_interface.port() + 1); - if (m_listen_interface.port() > m_listen_port_range.second) - { - std::stringstream msg; - msg << "none of the ports in the range [" - << m_listen_port_range.first - << ", " << m_listen_port_range.second - << "] could be opened for listening"; - m_alerts.post_alert(listen_failed_alert(msg.str())); -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_logger) << msg.str() << "\n"; -#endif - m_listen_socket.reset(); - break; - } - } - } + session_impl::listen_socket_t session_impl::setup_listener(tcp::endpoint ep, int retries) + { + asio::error_code ec; + listen_socket_t s; + s.sock.reset(new socket_acceptor(m_io_service)); + s.sock->open(ep.protocol(), ec); + s.sock->set_option(socket_acceptor::reuse_address(true), ec); + s.sock->bind(ep, ec); + while (ec && retries > 0) + { + ec = asio::error_code(); + assert(!ec); + --retries; + ep.port(ep.port() + 1); + s.sock->bind(ep, ec); } - catch (asio::system_error& e) + if (ec) + { + // instead of giving up, try + // let the OS pick a port + ep.port(0); + ec = asio::error_code(); + s.sock->bind(ep, ec); + } + if (ec) + { + // not even that worked, give up + if (m_alerts.should_post(alert::fatal)) + { + std::string msg = "cannot bind to the given interface '" + + boost::lexical_cast(ep) + "' " + ec.message(); + m_alerts.post_alert(listen_failed_alert(ep, msg)); + } +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + std::string msg = "cannot bind to the given interface '" + + boost::lexical_cast(ep) + "' " + ec.message(); + (*m_logger) << msg << "\n"; +#endif + return listen_socket_t(); + } + s.external_port = s.sock->local_endpoint(ec).port(); + s.sock->listen(0, ec); + if (ec) { if (m_alerts.should_post(alert::fatal)) { - m_alerts.post_alert(listen_failed_alert( - std::string("failed to open listen port: ") + e.what())); + std::string msg = "cannot listen the given interface '" + + boost::lexical_cast(ep) + "' " + ec.message(); + m_alerts.post_alert(listen_failed_alert(ep, msg)); + } +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + std::string msg = "cannot listen the given interface '" + + boost::lexical_cast(ep) + "' " + ec.message(); + (*m_logger) << msg << "\n"; +#endif + return listen_socket_t(); + } + + if (m_alerts.should_post(alert::fatal)) + { + std::string msg = "listening on interface " + + boost::lexical_cast(ep); + m_alerts.post_alert(listen_succeeded_alert(ep, msg)); + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << "listening on: " << ep.address().to_string() << ":" << ep.port() + << " external port: " << s.external_port << "\n"; +#endif + return s; + } + + void session_impl::open_listen_port() throw() + { + // close the open listen sockets + m_listen_sockets.clear(); + m_incoming_connection = false; + + if (is_any(m_listen_interface.address())) + { + // this means we should open two listen sockets + // one for IPv4 and one for IPv6 + + listen_socket_t s = setup_listener( + tcp::endpoint(address_v4::any(), m_listen_interface.port()) + , m_listen_port_retries); + + if (s.sock) + { + m_listen_sockets.push_back(s); + async_accept(s.sock); + } + + s = setup_listener( + tcp::endpoint(address_v6::any(), m_listen_interface.port()) + , m_listen_port_retries); + + if (s.sock) + { + m_listen_sockets.push_back(s); + async_accept(s.sock); + } + } + else + { + // we should only open a single listen socket, that + // binds to the given interface + + listen_socket_t s = setup_listener( + m_listen_interface, m_listen_port_retries); + + if (s.sock) + { + m_listen_sockets.push_back(s); + async_accept(s.sock); } } - if (m_listen_socket) + m_ipv6_interface = tcp::endpoint(); + + for (std::list::const_iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) { -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_logger) << "listening on port: " << m_listen_interface.port() - << " external port: " << m_external_listen_port << "\n"; -#endif - async_accept(); - if (m_natpmp.get()) - m_natpmp->set_mappings(m_listen_interface.port(), 0); - if (m_upnp.get()) - m_upnp->set_mappings(m_listen_interface.port(), 0); + asio::error_code ec; + tcp::endpoint ep = i->sock->local_endpoint(ec); + if (ec || ep.address().is_v4()) continue; + + if (ep.address().to_v6() != address_v6::any()) + { + // if we're listening on a specific address + // pick it + m_ipv6_interface = ep; + } + else + { + // if we're listening on any IPv6 address, enumerate them and + // pick the first non-local address + std::vector
const& ifs = enum_net_interfaces(m_io_service, ec); + for (std::vector
::const_iterator i = ifs.begin() + , end(ifs.end()); i != end; ++i) + { + if (i->is_v4() || i->to_v6().is_link_local() || i->to_v6().is_loopback()) continue; + m_ipv6_interface = tcp::endpoint(*i, ep.port()); + break; + } + break; + } + } + + if (!m_listen_sockets.empty()) + { + asio::error_code ec; + tcp::endpoint local = m_listen_sockets.front().sock->local_endpoint(ec); + if (!ec) + { + if (m_natpmp.get()) m_natpmp->set_mappings(local.port(), 0); + if (m_upnp.get()) m_upnp->set_mappings(local.port(), 0); + } } } - void session_impl::async_accept() + void session_impl::async_accept(boost::shared_ptr const& listener) { shared_ptr c(new socket_type(m_io_service)); c->instantiate(); - m_listen_socket->async_accept(c->get() + listener->async_accept(c->get() , bind(&session_impl::on_incoming_connection, this, c - , weak_ptr(m_listen_socket), _1)); + , boost::weak_ptr(listener), _1)); } void session_impl::on_incoming_connection(shared_ptr const& s - , weak_ptr const& listen_socket, asio::error_code const& e) try + , weak_ptr listen_socket, asio::error_code const& e) try { - if (listen_socket.expired()) - return; + boost::shared_ptr listener = listen_socket.lock(); + if (!listener) return; - if (e == asio::error::operation_aborted) - return; + if (e == asio::error::operation_aborted) return; mutex_t::scoped_lock l(m_mutex); - assert(listen_socket.lock() == m_listen_socket); - if (m_abort) return; + asio::error_code ec; if (e) { + tcp::endpoint ep = listener->local_endpoint(ec); #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) std::string msg = "error accepting connection on '" - + m_listen_interface.address().to_string() + "'"; + + boost::lexical_cast(ep) + "' " + e.message(); (*m_logger) << msg << "\n"; #endif - assert(m_listen_socket.unique()); - // try any random port - m_listen_interface.port(0); - open_listen_port(); + if (m_alerts.should_post(alert::fatal)) + { + std::string msg = "error accepting connection on '" + + boost::lexical_cast(ep) + "' " + ec.message(); + m_alerts.post_alert(listen_failed_alert(ep, msg)); + } return; } - async_accept(); + async_accept(listener); // we got a connection request! m_incoming_connection = true; - tcp::endpoint endp = s->remote_endpoint(); + tcp::endpoint endp = s->remote_endpoint(ec); + + if (ec) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << endp << " <== INCOMING CONNECTION FAILED, could " + "not retrieve remote endpoint " << ec.message() << "\n"; +#endif + return; + } #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) (*m_logger) << endp << " <== INCOMING CONNECTION\n"; @@ -803,28 +897,22 @@ namespace detail // check if we have any active torrents // if we don't reject the connection - if (m_torrents.empty()) + if (m_torrents.empty()) return; + + bool has_active_torrent = false; + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) { - return; - } - else - { - bool has_active_torrent = false; - for (torrent_map::iterator i = m_torrents.begin() - , end(m_torrents.end()); i != end; ++i) + if (!i->second->is_paused()) { - if (!i->second->is_paused()) - { - has_active_torrent = true; - break; - } + has_active_torrent = true; + break; } - if (!has_active_torrent) - return; } + if (!has_active_torrent) return; boost::intrusive_ptr c( - new bt_peer_connection(*this, s, 0)); + new bt_peer_connection(*this, s, 0)); #ifndef NDEBUG c->m_in_constructor = false; #endif @@ -1064,7 +1152,9 @@ namespace detail if (t.should_request()) { tracker_request req = t.generate_tracker_request(); - req.listen_port = m_external_listen_port; + req.listen_port = 0; + if (!m_listen_sockets.empty()) + req.listen_port = m_listen_sockets.front().external_port; req.key = m_key; m_tracker_manager.queue_request(m_strand, m_half_open, req , t.tracker_login(), m_listen_interface.address(), i->second); @@ -1274,10 +1364,9 @@ namespace detail { eh_initializer(); - if (m_listen_port_range.first != 0 && m_listen_port_range.second != 0) { session_impl::mutex_t::scoped_lock l(m_mutex); - open_listen_port(); + if (m_listen_interface.port() != 0) open_listen_port(); } ptime timer = time_now(); @@ -1331,8 +1420,10 @@ namespace detail && !i->second->trackers().empty()) { tracker_request req = i->second->generate_tracker_request(); - assert(m_external_listen_port > 0); - req.listen_port = m_external_listen_port; + assert(!m_listen_sockets.empty()); + req.listen_port = 0; + if (!m_listen_sockets.empty()) + req.listen_port = m_listen_sockets.front().external_port; req.key = m_key; std::string login = i->second->tracker_login(); #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) @@ -1347,8 +1438,8 @@ namespace detail } } - // close listen socket - m_listen_socket.reset(); + // close the listen sockets + m_listen_sockets.clear(); ptime start(time_now()); l.unlock(); @@ -1477,10 +1568,12 @@ namespace detail , bool paused , void* userdata) { + assert(!save_path.empty()); + // if you get this assert, you haven't managed to // open a listen port. call listen_on() first. - assert(m_external_listen_port > 0); - assert(!save_path.empty()); + if (m_listen_sockets.empty()) + throw std::runtime_error("no listen socket opened"); if (ti->begin_files() == ti->end_files()) throw std::runtime_error("no files in torrent"); @@ -1489,7 +1582,7 @@ namespace detail mutex_t::scoped_lock l(m_mutex); mutex::scoped_lock l2(m_checker_impl.m_mutex); - INVARIANT_CHECK; +// INVARIANT_CHECK; if (is_aborted()) throw std::runtime_error("session is closing"); @@ -1573,7 +1666,7 @@ namespace detail // lock the session session_impl::mutex_t::scoped_lock l(m_mutex); - INVARIANT_CHECK; +// INVARIANT_CHECK; // is the torrent already active? if (!find_torrent(info_hash).expired()) @@ -1628,8 +1721,10 @@ namespace detail { tracker_request req = t.generate_tracker_request(); assert(req.event == tracker_request::stopped); - assert(m_external_listen_port > 0); - req.listen_port = m_external_listen_port; + assert(!m_listen_sockets.empty()); + req.listen_port = 0; + if (!m_listen_sockets.empty()) + req.listen_port = m_listen_sockets.front().external_port; req.key = m_key; #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) @@ -1684,19 +1779,15 @@ namespace detail if (net_interface && std::strlen(net_interface) > 0) new_interface = tcp::endpoint(address::from_string(net_interface), port_range.first); else - new_interface = tcp::endpoint(address(), port_range.first); + new_interface = tcp::endpoint(address_v4::any(), port_range.first); - m_listen_port_range = port_range; + m_listen_port_retries = port_range.second - port_range.first; // if the interface is the same and the socket is open // don't do anything if (new_interface == m_listen_interface - && m_listen_socket) return true; + && !m_listen_sockets.empty()) return true; - if (m_listen_socket) - m_listen_socket.reset(); - - m_incoming_connection = false; m_listen_interface = new_interface; open_listen_port(); @@ -1723,13 +1814,14 @@ namespace detail (*m_logger) << time_now_string() << "\n"; #endif - return m_listen_socket; + return !m_listen_sockets.empty(); } unsigned short session_impl::listen_port() const { mutex_t::scoped_lock l(m_mutex); - return m_external_listen_port; + if (m_listen_sockets.empty()) return 0; + return m_listen_sockets.front().external_port;; } void session_impl::announce_lsd(sha1_hash const& ih) @@ -1777,7 +1869,8 @@ namespace detail if (tcp_port != 0) { - m_external_listen_port = tcp_port; + if (!m_listen_sockets.empty()) + m_listen_sockets.front().external_port = tcp_port; if (m_alerts.should_post(alert::info)) { std::stringstream msg; @@ -1801,7 +1894,7 @@ namespace detail { mutex_t::scoped_lock l(m_mutex); - INVARIANT_CHECK; +// INVARIANT_CHECK; session_status s; s.has_incoming_connections = m_incoming_connection; @@ -1945,7 +2038,7 @@ namespace detail bool session_impl::is_listening() const { mutex_t::scoped_lock l(m_mutex); - return m_listen_socket; + return !m_listen_sockets.empty(); } session_impl::~session_impl() diff --git a/libtorrent/src/torrent.cpp b/libtorrent/src/torrent.cpp index 252461bc6..6e941e9ec 100755 --- a/libtorrent/src/torrent.cpp +++ b/libtorrent/src/torrent.cpp @@ -412,9 +412,9 @@ namespace libtorrent m_last_dht_announce = now; // TODO: There should be a way to abort an announce operation on the dht. // when the torrent is destructed - assert(m_ses.m_external_listen_port > 0); + if (m_ses.m_listen_sockets.empty()) return; m_ses.m_dht->announce(m_torrent_file->info_hash() - , m_ses.m_external_listen_port + , m_ses.m_listen_sockets.front().external_port , m_ses.m_strand.wrap(bind(&torrent::on_dht_announce_response_disp, self, _1))); } #endif @@ -1367,6 +1367,9 @@ namespace libtorrent req.left = bytes_left(); if (req.left == -1) req.left = 16*1024; req.event = m_event; + tcp::endpoint ep = m_ses.get_ipv6_interface(); + if (ep != tcp::endpoint()) + req.ipv6 = ep.address().to_string(); if (m_event != tracker_request::stopped) m_event = tracker_request::none; diff --git a/libtorrent/src/upnp.cpp b/libtorrent/src/upnp.cpp index ad50595a2..87f950b48 100644 --- a/libtorrent/src/upnp.cpp +++ b/libtorrent/src/upnp.cpp @@ -247,6 +247,18 @@ try EXT: Cache-Control:max-age=180 DATE: Fri, 02 Jan 1970 08:10:38 GMT + + a notification looks like this: + + NOTIFY * HTTP/1.1 + Host:239.255.255.250:1900 + NT:urn:schemas-upnp-org:device:MediaServer:1 + NTS:ssdp:alive + Location:http://10.0.3.169:2869/upnphost/udhisapi.dll?content=uuid:c17f0c32-d19b-4938-ae94-65f945c3a26e + USN:uuid:c17f0c32-d19b-4938-ae94-65f945c3a26e::urn:schemas-upnp-org:device:MediaServer:1 + Cache-Control:max-age=900 + Server:Microsoft-Windows-NT/5.1 UPnP/1.0 UPnP-Device-Host/1.0 + */ http_parser p; try @@ -263,12 +275,17 @@ try return; } - if (p.status_code() != 200) + if (p.status_code() != 200 && p.method() != "notify") { #ifdef TORRENT_UPNP_LOGGING - m_log << time_now_string() - << " <== Rootdevice responded with HTTP status: " << p.status_code() - << ". Ignoring device" << std::endl; + if (p.method().empty()) + m_log << time_now_string() + << " <== Device responded with HTTP status: " << p.status_code() + << ". Ignoring device" << std::endl; + else + m_log << time_now_string() + << " <== Device with HTTP method: " << p.method() + << ". Ignoring device" << std::endl; #endif return; }