diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index de00f8ad0..7f400cc3c 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -194,10 +194,18 @@ namespace libtorrent #ifndef TORRENT_DISABLE_EXTENSIONS void add_extension(boost::function( torrent*, void*)> ext); +#endif +#ifndef NDEBUG + bool has_peer(peer_connection const* p) const + { + return std::find_if(m_connections.begin(), m_connections.end() + , boost::bind(&boost::intrusive_ptr::get, _1) == p) + != m_connections.end(); + } #endif void operator()(); - void open_listen_port() throw(); + void open_listen_port(); // if we are listening on an IPv6 interface // this will return one of the IPv6 addresses on this @@ -216,9 +224,8 @@ namespace libtorrent boost::weak_ptr find_torrent(const sha1_hash& info_hash); peer_id const& get_peer_id() const { return m_peer_id; } - void close_connection(boost::intrusive_ptr const& p); - void connection_failed(boost::intrusive_ptr const& p - , tcp::endpoint const& a, char const* message); + void close_connection(peer_connection const* p + , char const* message); void set_settings(session_settings const& s); session_settings const& settings() const { return m_settings; } diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index 927bdeb47..cced96c58 100755 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -224,7 +224,7 @@ namespace libtorrent void add_stat(size_type downloaded, size_type uploaded); // is called once every second by the main loop - void second_tick(float tick_interval) throw(); + void second_tick(float tick_interval); boost::shared_ptr get_socket() const { return m_socket; } tcp::endpoint const& remote() const { return m_remote; } @@ -235,7 +235,7 @@ namespace libtorrent void timed_out(); // this will cause this peer_connection to be disconnected. - void disconnect(); + void disconnect(char const* message); bool is_disconnecting() const { return m_disconnecting; } // this is called when the connection attempt has succeeded diff --git a/include/libtorrent/policy.hpp b/include/libtorrent/policy.hpp index 204149db7..27d790d13 100755 --- a/include/libtorrent/policy.hpp +++ b/include/libtorrent/policy.hpp @@ -85,10 +85,13 @@ namespace libtorrent // the tracker, pex, lsd or dht. policy::peer* peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid , int source, char flags); - void update_peer_port(int port, policy::peer* p, int src); + + // false means duplicate connection + bool update_peer_port(int port, policy::peer* p, int src); // called when an incoming connection is accepted - void new_connection(peer_connection& c); + // false means the connection was refused or failed + bool new_connection(peer_connection& c); // the given connection was just closed void connection_closed(const peer_connection& c) throw(); diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 033644987..ef0064802 100755 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -133,6 +133,11 @@ namespace libtorrent , void* userdata); #endif +#ifndef NDEBUG + bool has_peer(peer_connection* p) const + { return m_connections.find(p) != m_connections.end(); } +#endif + // this is called when the torrent has metadata. // it will initialize the storage and the piece-picker void init(); diff --git a/src/bt_peer_connection.cpp b/src/bt_peer_connection.cpp index f1bf8d706..5c1e3079e 100755 --- a/src/bt_peer_connection.cpp +++ b/src/bt_peer_connection.cpp @@ -790,7 +790,10 @@ namespace libtorrent TORRENT_ASSERT(received > 0); if (packet_size() != 1) - throw protocol_error("'choke' message size != 1"); + { + disconnect("'choke' message size != 1"); + return; + } m_statistics.received_bytes(0, received); if (!packet_finished()) return; @@ -821,7 +824,10 @@ namespace libtorrent TORRENT_ASSERT(received > 0); if (packet_size() != 1) - throw protocol_error("'unchoke' message size != 1"); + { + disconnect("'unchoke' message size != 1"); + return; + } m_statistics.received_bytes(0, received); if (!packet_finished()) return; @@ -838,7 +844,10 @@ namespace libtorrent TORRENT_ASSERT(received > 0); if (packet_size() != 1) - throw protocol_error("'interested' message size != 1"); + { + disconnect("'interested' message size != 1"); + return; + } m_statistics.received_bytes(0, received); if (!packet_finished()) return; @@ -855,7 +864,10 @@ namespace libtorrent TORRENT_ASSERT(received > 0); if (packet_size() != 1) - throw protocol_error("'not interested' message size != 1"); + { + disconnect("'not interested' message size != 1"); + return; + } m_statistics.received_bytes(0, received); if (!packet_finished()) return; @@ -872,7 +884,10 @@ namespace libtorrent TORRENT_ASSERT(received > 0); if (packet_size() != 5) - throw protocol_error("'have' message size != 5"); + { + disconnect("'have' message size != 5"); + return; + } m_statistics.received_bytes(0, received); if (!packet_finished()) return; @@ -901,7 +916,10 @@ namespace libtorrent // verify the bitfield size if (t->valid_metadata() && packet_size() - 1 != ((int)get_bitfield().size() + 7) / 8) - throw protocol_error("bitfield with invalid size"); + { + disconnect("bitfield with invalid size"); + return; + } m_statistics.received_bytes(0, received); if (!packet_finished()) return; @@ -935,7 +953,10 @@ namespace libtorrent TORRENT_ASSERT(received > 0); if (packet_size() != 13) - throw protocol_error("'request' message size != 13"); + { + disconnect("'request' message size != 13"); + return; + } m_statistics.received_bytes(0, received); if (!packet_finished()) return; @@ -1004,7 +1025,10 @@ namespace libtorrent TORRENT_ASSERT(received > 0); if (packet_size() != 13) - throw protocol_error("'cancel' message size != 13"); + { + disconnect("'cancel' message size != 13"); + return; + } m_statistics.received_bytes(0, received); if (!packet_finished()) return; @@ -1028,11 +1052,17 @@ namespace libtorrent INVARIANT_CHECK; if (!m_supports_dht_port) - throw protocol_error("got 'dht_port' message from peer that doesn't support it"); + { + disconnect("got 'dht_port' message from peer that doesn't support it"); + return; + } TORRENT_ASSERT(received > 0); if (packet_size() != 3) - throw protocol_error("'dht_port' message size != 3"); + { + disconnect("'dht_port' message size != 3"); + return; + } m_statistics.received_bytes(0, received); if (!packet_finished()) return; @@ -1049,7 +1079,10 @@ namespace libtorrent INVARIANT_CHECK; if (!m_supports_fast) - throw protocol_error("got 'suggest_piece' without FAST extension support"); + { + disconnect("got 'suggest_piece' without FAST excension support"); + return; + } m_statistics.received_bytes(0, received); if (!packet_finished()) return; @@ -1066,7 +1099,10 @@ namespace libtorrent INVARIANT_CHECK; if (!m_supports_fast) - throw protocol_error("got 'have_all' without FAST extension support"); + { + disconnect("got 'have_all' without FAST extension support"); + return; + } m_statistics.received_bytes(0, received); incoming_have_all(); } @@ -1076,7 +1112,10 @@ namespace libtorrent INVARIANT_CHECK; if (!m_supports_fast) - throw protocol_error("got 'have_none' without FAST extension support"); + { + disconnect("got 'have_none' without FAST extension support"); + return; + } m_statistics.received_bytes(0, received); incoming_have_none(); } @@ -1086,7 +1125,10 @@ namespace libtorrent INVARIANT_CHECK; if (!m_supports_fast) - throw protocol_error("got 'reject_request' without FAST extension support"); + { + disconnect("got 'reject_request' without FAST extension support"); + return; + } m_statistics.received_bytes(0, received); if (!packet_finished()) return; @@ -1107,7 +1149,10 @@ namespace libtorrent INVARIANT_CHECK; if (!m_supports_fast) - throw protocol_error("got 'allowed_fast' without FAST extension support"); + { + disconnect("got 'allowed_fast' without FAST extension support"); + return; + } m_statistics.received_bytes(0, received); if (!packet_finished()) return; @@ -1129,10 +1174,16 @@ namespace libtorrent TORRENT_ASSERT(received > 0); m_statistics.received_bytes(0, received); if (packet_size() < 2) - throw protocol_error("'extended' message smaller than 2 bytes"); + { + disconnect("'extended' message smaller than 2 bytes"); + return; + } if (associated_torrent().expired()) - throw protocol_error("'extended' message sent before proper handshake"); + { + disconnect("'extended' message sent before proper handshake"); + return; + } buffer::const_interval recv_buffer = receive_buffer(); if (recv_buffer.left() < 2) return; @@ -1158,8 +1209,10 @@ namespace libtorrent } #endif - throw protocol_error("unknown extended message id: " - + boost::lexical_cast(extended_id)); + std::stringstream msg; + msg << "unknown extended message id: " << extended_id; + disconnect(msg.str().c_str()); + return; } void bt_peer_connection::on_extended_handshake() @@ -1258,9 +1311,10 @@ namespace libtorrent } #endif - throw protocol_error("unknown message id: " - + boost::lexical_cast(packet_type) - + " size: " + boost::lexical_cast(packet_size())); + std::stringstream msg; + msg << "unkown message id: " << packet_type << " size: " << packet_size(); + disconnect(msg.str().c_str()); + return packet_finished(); } TORRENT_ASSERT(m_message_handler[packet_type] != 0); @@ -1712,10 +1766,7 @@ namespace libtorrent if (recv_buffer.left() < 20) { if (packet_finished()) - { - throw protocol_error ("sync hash not found"); - } - // else + disconnect("sync hash not found"); return; } @@ -1740,7 +1791,10 @@ namespace libtorrent std::size_t bytes_processed = recv_buffer.left() - 20; m_sync_bytes_read += bytes_processed; if (m_sync_bytes_read >= 512) - throw protocol_error("sync hash not found within 532 bytes"); + { + disconnect("sync hash not found within 532 bytes"); + return; + } cut_receive_buffer(bytes_processed, (std::min)(packet_size(), (512+20) - m_sync_bytes_read)); @@ -1807,6 +1861,8 @@ namespace libtorrent if (!t) { attach_to_torrent(info_hash); + if (is_disconnecting()) return; + t = associated_torrent().lock(); TORRENT_ASSERT(t); } @@ -1820,7 +1876,10 @@ namespace libtorrent } if (!m_RC4_handler.get()) - throw protocol_error("invalid streamkey identifier (info hash) in encrypted handshake"); + { + disconnect("invalid streamkey identifier (info hash) in encrypted handshake"); + return; + } // verify constant buffer::interval wr_recv_buf = wr_recv_buffer(); @@ -1830,7 +1889,8 @@ namespace libtorrent const char sh_vc[] = {0,0,0,0, 0,0,0,0}; if (!std::equal(sh_vc, sh_vc+8, recv_buffer.begin + 20)) { - throw protocol_error("unable to verify constant"); + disconnect("unable to verify constant"); + return; } #ifdef TORRENT_VERBOSE_LOGGING @@ -1851,10 +1911,7 @@ namespace libtorrent if (recv_buffer.left() < 8) { if (packet_finished()) - { - throw protocol_error ("sync verification constant not found"); - } - // else + disconnect("sync verification constant not found"); return; } @@ -1878,7 +1935,10 @@ namespace libtorrent std::size_t bytes_processed = recv_buffer.left() - 8; m_sync_bytes_read += bytes_processed; if (m_sync_bytes_read >= 512) - throw protocol_error("sync verification constant not found within 520 bytes"); + { + disconnect("sync verification constant not found within 520 bytes"); + return; + } cut_receive_buffer(bytes_processed, (std::min)(packet_size(), (512+8) - m_sync_bytes_read)); @@ -1939,14 +1999,20 @@ namespace libtorrent case (pe_settings::plaintext): { if (!(crypto_field & 0x01)) - throw protocol_error("plaintext not provided"); + { + disconnect("plaintext not provided"); + return; + } crypto_select = 0x01; } break; case (pe_settings::rc4): { if (!(crypto_field & 0x02)) - throw protocol_error("rc4 not provided"); + { + disconnect("rc4 not provided"); + return; + } crypto_select = 0x02; } break; @@ -1967,7 +2033,10 @@ namespace libtorrent crypto_select = 0x02; } if (!crypto_select) - throw protocol_error("rc4/plaintext not provided"); + { + disconnect("rc4/plaintext not provided"); + return; + } } } // switch @@ -1982,22 +2051,34 @@ namespace libtorrent if (crypto_field == 0x02) { if (allowed_enc_level == pe_settings::plaintext) - throw protocol_error("rc4 selected by peer when not provided"); + { + disconnect("rc4 selected by peer when not provided"); + return; + } m_rc4_encrypted = true; } else if (crypto_field == 0x01) { if (allowed_enc_level == pe_settings::rc4) - throw protocol_error("plaintext selected by peer when not provided"); + { + disconnect("plaintext selected by peer when not provided"); + return; + } m_rc4_encrypted = false; } else - throw protocol_error("unsupported crypto method selected by peer"); + { + disconnect("unsupported crypto method selected by peer"); + return; + } } int len_pad = detail::read_int16(recv_buffer.begin); if (len_pad < 0 || len_pad > 512) - throw protocol_error("invalid pad length"); + { + disconnect("invalid pad length"); + return; + } m_state = read_pe_pad; if (!is_local()) @@ -2031,7 +2112,11 @@ namespace libtorrent recv_buffer.begin += pad_size; int len_ia = detail::read_int16(recv_buffer.begin); - if (len_ia < 0) throw protocol_error("invalid len_ia in handshake"); + if (len_ia < 0) + { + disconnect("invalid len_ia in handshake"); + return; + } #ifdef TORRENT_VERBOSE_LOGGING (*m_logger) << " len(IA) : " << len_ia << "\n"; @@ -2141,7 +2226,10 @@ namespace libtorrent { #ifndef TORRENT_DISABLE_ENCRYPTION if (!is_local() && m_ses.get_pe_settings().in_enc_policy == pe_settings::disabled) - throw protocol_error("encrypted incoming connections disabled"); + { + disconnect("encrypted incoming connections disabled"); + return; + } // Don't attempt to perform an encrypted handshake // within an encrypted connection @@ -2158,7 +2246,8 @@ namespace libtorrent assert ((!is_local() && m_encrypted) || is_local()); #endif // #ifndef TORRENT_DISABLE_ENCRYPTION - throw protocol_error("incorrect protocol identifier"); + disconnect("incorrect protocol identifier"); + return; } #ifndef TORRENT_DISABLE_ENCRYPTION @@ -2167,7 +2256,10 @@ namespace libtorrent if (!is_local() && (m_ses.get_pe_settings().in_enc_policy == pe_settings::forced) && !m_encrypted) - throw protocol_error("non encrypted incoming connections disabled"); + { + disconnect("non encrypted incoming connections disabled"); + return; + } #endif #ifdef TORRENT_VERBOSE_LOGGING @@ -2226,6 +2318,7 @@ namespace libtorrent , (char*)info_hash.begin()); attach_to_torrent(info_hash); + if (is_disconnecting()) return; } else { @@ -2236,7 +2329,8 @@ namespace libtorrent #ifdef TORRENT_VERBOSE_LOGGING (*m_logger) << " received invalid info_hash\n"; #endif - throw protocol_error("invalid info-hash in handshake"); + disconnect("invalid info-hash in handshake"); + return; } #ifdef TORRENT_VERBOSE_LOGGING @@ -2253,6 +2347,8 @@ namespace libtorrent // if (t->valid_metadata()) // write_bitfield(t->pieces()); + if (is_disconnecting()) return; + TORRENT_ASSERT(t->get_policy().has_connection(this)); m_state = read_peer_id; @@ -2310,18 +2406,20 @@ namespace libtorrent // if not, we should close the outgoing one. if (pid < m_ses.get_peer_id() && is_local()) { - i->second.connection->disconnect(); + i->second.connection->disconnect("duplicate peer-id, connection closed"); } else { - throw protocol_error("duplicate peer-id, connection closed"); + disconnect("duplicate peer-id, connection closed"); + return; } } } if (pid == m_ses.get_peer_id()) { - throw protocol_error("closing connection to ourself"); + disconnect("closing connection to ourself"); + return; } m_client_version = identify_client(pid); @@ -2335,7 +2433,10 @@ namespace libtorrent // disconnect if the peer has the same peer-id as ourself // since it most likely is ourself then if (pid == m_ses.get_peer_id()) - throw std::runtime_error("closing connection to ourself"); + { + disconnect("closing connection to ourself"); + return; + } #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() @@ -2405,9 +2506,10 @@ namespace libtorrent if (packet_size > 1024*1024 || packet_size < 0) { // packet too large - throw std::runtime_error("packet > 1 MB (" - + boost::lexical_cast( - (unsigned int)packet_size) + " bytes)"); + std::stringstream msg; + msg << "packet > 1 MB (" << (unsigned int)packet_size << " bytes)"; + disconnect(msg.str().c_str()); + return; } if (packet_size == 0) diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index b33bf8452..6cb8a16e8 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -208,15 +208,26 @@ namespace libtorrent #endif { tcp::socket::non_blocking_io ioc(true); - m_socket->io_control(ioc); + asio::error_code ec; + m_socket->io_control(ioc, ec); + if (ec) + { + disconnect(ec.message().c_str()); + return; + } #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES std::fill(m_country, m_country + 2, 0); #endif - m_remote = m_socket->remote_endpoint(); + m_remote = m_socket->remote_endpoint(ec); + if (ec) + { + disconnect(ec.message().c_str()); + return; + } #ifdef TORRENT_VERBOSE_LOGGING - TORRENT_ASSERT(m_socket->remote_endpoint() == remote()); - m_logger = m_ses.create_log(remote().address().to_string() + "_" + TORRENT_ASSERT(m_socket->remote_endpoint() == remote() || ec); + m_logger = m_ses.create_log(remote().address().to_string(ec) + "_" + boost::lexical_cast(remote().port()), m_ses.listen_port()); (*m_logger) << "*** INCOMING CONNECTION\n"; #endif @@ -354,7 +365,8 @@ namespace libtorrent // if we're a seed too, disconnect if (t->is_finished()) { - throw std::runtime_error("seed to seed connection redundant, disconnecting"); + disconnect("seed to seed connection redundant"); + return; } m_num_pieces = num_pieces; t->peer_has_all(); @@ -396,7 +408,11 @@ namespace libtorrent << " *** CONNECTION CLOSED\n"; } #endif + TORRENT_ASSERT(!m_ses.has_peer(this)); #ifndef NDEBUG + for (aux::session_impl::torrent_map::const_iterator i = m_ses.m_torrents.begin() + , end(m_ses.m_torrents.end()); i != end; ++i) + TORRENT_ASSERT(!i->second->has_peer(this)); if (m_peer_info) TORRENT_ASSERT(m_peer_info->connection == 0); @@ -486,7 +502,11 @@ namespace libtorrent for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { +#ifdef BOOST_NO_EXCEPTIONS + (*i)->on_piece_pass(index); +#else try { (*i)->on_piece_pass(index); } catch (std::exception&) {} +#endif } #endif } @@ -499,7 +519,11 @@ namespace libtorrent for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { +#ifdef BOOST_NO_EXCEPTIONS + (*i)->on_piece_failed(index); +#else try { (*i)->on_piece_failed(index); } catch (std::exception&) {} +#endif } #endif @@ -587,7 +611,8 @@ namespace libtorrent (*m_logger) << " " << i->second->torrent_file().info_hash() << "\n"; } #endif - throw std::runtime_error("got info-hash that is not in our session"); + disconnect("got invalid info-hash"); + return; } if (t->is_paused()) @@ -597,7 +622,8 @@ namespace libtorrent #ifdef TORRENT_VERBOSE_LOGGING (*m_logger) << " rejected connection to paused torrent\n"; #endif - throw std::runtime_error("connection rejected by paused torrent"); + disconnect("connection rejected bacause torrent is paused"); + return; } TORRENT_ASSERT(m_torrent.expired()); @@ -929,8 +955,10 @@ namespace libtorrent // if we got an invalid message, abort if (index >= int(m_have_piece.size()) || index < 0) - throw protocol_error("got 'have'-message with higher index " - "than the number of pieces"); + { + disconnect("got 'have'-message with higher index than the number of pieces"); + return; + } if (m_have_piece[index]) { @@ -974,7 +1002,8 @@ namespace libtorrent m_peer_info->seed = true; if (t->is_finished()) { - throw protocol_error("seed to seed connection redundant, disconnecting"); + disconnect("seed to seed connection redundant"); + return; } } } @@ -1014,11 +1043,14 @@ namespace libtorrent // verify the bitfield size if (t->valid_metadata() && (bitfield.size() / 8) != (m_have_piece.size() / 8)) - throw protocol_error("got bitfield with invalid size: " - + boost::lexical_cast(bitfield.size() / 8) - + "bytes. expected: " - + boost::lexical_cast(m_have_piece.size() / 8) - + "bytes"); + { + std::stringstream msg; + msg << "got bitfield with invalid size: " << (bitfield.size() / 8) + << "bytes. expected: " << (m_have_piece.size() / 8) + << " bytes"; + disconnect(msg.str().c_str()); + return; + } // if we don't have metadata yet // just remember the bitmask @@ -1045,7 +1077,8 @@ namespace libtorrent // if we're a seed too, disconnect if (t->is_finished()) { - throw protocol_error("seed to seed connection redundant, disconnecting"); + disconnect("seed to seed connection redundant, disconnecting"); + return; } std::fill(m_have_piece.begin(), m_have_piece.end(), true); @@ -1325,7 +1358,8 @@ namespace libtorrent "start: " << p.start << " | " "length: " << p.length << " ]\n"; #endif - throw protocol_error("got invalid piece packet"); + disconnect("got invalid piece packet"); + return; } // if we're already seeding, don't bother, @@ -1433,8 +1467,8 @@ namespace libtorrent TORRENT_ASSERT(m_outstanding_writing_bytes >= 0); #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_ses.m_logger) << time_now_string() << " *** DISK_WRITE_COMPLETE [ p: " - << p.piece << " o: " << p.start << " ]\n"; +// (*m_ses.m_logger) << time_now_string() << " *** DISK_WRITE_COMPLETE [ p: " +// << p.piece << " o: " << p.start << " ]\n"; #endif // in case the outstanding bytes just dropped down // to allow to receive more data @@ -1448,7 +1482,7 @@ namespace libtorrent if (!t) { - m_ses.connection_failed(self(), remote(), j.str.c_str()); + disconnect(j.str.c_str()); return; } @@ -1474,11 +1508,6 @@ namespace libtorrent block_finished.block_index, block_finished.piece_index, "block finished")); } -#ifndef NDEBUG - try - { -#endif - // did we just finish the piece? if (picker.is_piece_finished(p.piece)) { @@ -1489,15 +1518,6 @@ namespace libtorrent , p.piece, _1)); } -#ifndef NDEBUG - } - catch (std::exception const& e) - { - std::cerr << e.what() << std::endl; - TORRENT_ASSERT(false); - } -#endif - if (!t->is_seed() && !m_torrent.expired()) { // this is a free function defined in policy.cpp @@ -1614,7 +1634,10 @@ namespace libtorrent // if we're a seed too, disconnect if (t->is_finished()) - throw protocol_error("seed to seed connection redundant, disconnecting"); + { + disconnect("seed to seed connection redundant, disconnecting"); + return; + } TORRENT_ASSERT(!m_have_piece.empty()); std::fill(m_have_piece.begin(), m_have_piece.end(), true); @@ -2004,7 +2027,8 @@ namespace libtorrent void close_socket_ignore_error(boost::shared_ptr s) { - try { s->close(); } catch (std::exception& e) {} + asio::error_code ec; + s->close(ec); } void peer_connection::timed_out() @@ -2013,14 +2037,17 @@ namespace libtorrent (*m_ses.m_logger) << time_now_string() << " CONNECTION TIMED OUT: " << m_remote.address().to_string() << "\n"; #endif - m_ses.connection_failed(self(), m_remote, "timed out"); + disconnect("timed out"); } - void peer_connection::disconnect() + void peer_connection::disconnect(char const* message) { session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - boost::intrusive_ptr me(this); +#if defined(TORRENT_VERBOSE_LOGGING) + (*m_logger) << "*** CONNECTION FAILED " << message << "\n"; +#endif +// boost::intrusive_ptr me(this); INVARIANT_CHECK; @@ -2033,6 +2060,15 @@ namespace libtorrent boost::shared_ptr t = m_torrent.lock(); + if (message && m_ses.m_alerts.should_post(alert::debug)) + { + m_ses.m_alerts.post_alert( + peer_error_alert( + remote() + , pid() + , message)); + } + if (t) { if (t->has_picker()) @@ -2055,7 +2091,7 @@ namespace libtorrent m_torrent.reset(); } - m_ses.close_connection(me); + m_ses.close_connection(this, message); } void peer_connection::set_upload_limit(int limit) @@ -2207,7 +2243,7 @@ namespace libtorrent if (m_packet_size >= m_recv_pos) m_recv_buffer.resize(m_packet_size); } - void peer_connection::second_tick(float tick_interval) throw() + void peer_connection::second_tick(float tick_interval) { INVARIANT_CHECK; @@ -2365,7 +2401,7 @@ namespace libtorrent #ifdef TORRENT_VERBOSE_LOGGING (*m_logger) << "**ERROR**: " << e.what() << "\n"; #endif - m_ses.connection_failed(self(), remote(), e.what()); + disconnect(e.what()); } } @@ -2415,7 +2451,7 @@ namespace libtorrent boost::shared_ptr t = m_torrent.lock(); if (!t) { - m_ses.connection_failed(self(), remote(), j.str.c_str()); + disconnect(j.str.c_str()); return; } @@ -2714,7 +2750,9 @@ namespace libtorrent #endif set_failed(); on_receive(error, bytes_transferred); - throw std::runtime_error(error.message()); + set_failed(); + disconnect(error.message().c_str()); + return; } do @@ -2757,7 +2795,10 @@ namespace libtorrent bytes_transferred = m_socket->read_some(asio::buffer(&m_recv_buffer[m_recv_pos] , max_receive), ec); if (ec && ec != asio::error::would_block) - throw asio::system_error(ec); + { + disconnect(ec.message().c_str()); + return; + } } while (bytes_transferred > 0); @@ -2770,7 +2811,7 @@ namespace libtorrent boost::shared_ptr t = m_torrent.lock(); if (!t) { - m_ses.connection_failed(self(), remote(), e.what()); + disconnect(e.what()); return; } @@ -2785,14 +2826,14 @@ namespace libtorrent catch (std::exception& e) { session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - m_ses.connection_failed(self(), remote(), e.what()); + disconnect(e.what()); } catch (...) { // all exceptions should derive from std::exception TORRENT_ASSERT(false); session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - m_ses.connection_failed(self(), remote(), "connection failed for unknown reason"); + disconnect("connection failed for unknown reason"); } bool peer_connection::can_write() const @@ -2828,8 +2869,9 @@ namespace libtorrent { INVARIANT_CHECK; + asio::error_code ec; #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_ses.m_logger) << time_now_string() << " CONNECTING: " << m_remote.address().to_string() + (*m_ses.m_logger) << time_now_string() << " CONNECTING: " << m_remote.address().to_string(ec) << ":" << m_remote.port() << "\n"; #endif @@ -2839,13 +2881,28 @@ namespace libtorrent m_queued = false; TORRENT_ASSERT(m_connecting); - m_socket->open(t->get_interface().protocol()); + m_socket->open(t->get_interface().protocol(), ec); + if (ec) + { + disconnect(ec.message().c_str()); + return; + } // set the socket to non-blocking, so that we can // read the entire buffer on each read event we get tcp::socket::non_blocking_io ioc(true); - m_socket->io_control(ioc); - m_socket->bind(t->get_interface()); + m_socket->io_control(ioc, ec); + if (ec) + { + disconnect(ec.message().c_str()); + return; + } + m_socket->bind(t->get_interface(), ec); + if (ec) + { + disconnect(ec.message().c_str()); + return; + } m_socket->async_connect(m_remote , bind(&peer_connection::on_connection_complete, self(), _1)); @@ -2874,7 +2931,7 @@ namespace libtorrent << ": " << e.message() << "\n"; #endif set_failed(); - m_ses.connection_failed(self(), m_remote, e.message().c_str()); + disconnect(e.message().c_str()); return; } @@ -2894,14 +2951,14 @@ namespace libtorrent catch (std::exception& ex) { session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - m_ses.connection_failed(self(), remote(), ex.what()); + disconnect(ex.what()); } catch (...) { // all exceptions should derive from std::exception TORRENT_ASSERT(false); session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - m_ses.connection_failed(self(), remote(), "connection failed for unkown reason"); + disconnect("connection failed for unkown reason"); } // -------------------------- @@ -2935,7 +2992,8 @@ namespace libtorrent (*m_logger) << "**ERROR**: " << error.message() << " [in peer_connection::on_send_data]\n"; #endif set_failed(); - throw std::runtime_error(error.message()); + disconnect(error.message().c_str()); + return; } if (m_disconnecting) return; @@ -2952,17 +3010,16 @@ namespace libtorrent catch (std::exception& e) { session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - m_ses.connection_failed(self(), remote(), e.what()); + disconnect(e.what()); } catch (...) { // all exceptions should derive from std::exception TORRENT_ASSERT(false); session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - m_ses.connection_failed(self(), remote(), "connection failed for unknown reason"); + disconnect("connection failed for unknown reason"); } - #ifndef NDEBUG void peer_connection::check_invariant() const { diff --git a/src/policy.cpp b/src/policy.cpp index 2aabffd0c..e452128bd 100755 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -361,7 +361,7 @@ namespace libtorrent if (i->second.connection) { - i->second.connection->disconnect(); + i->second.connection->disconnect("peer banned by IP filter"); if (ses.m_alerts.should_post(alert::info)) { ses.m_alerts.post_alert(peer_blocked_alert(i->second.ip.address() @@ -914,7 +914,7 @@ namespace libtorrent return ret; } - void policy::new_connection(peer_connection& c) + bool policy::new_connection(peer_connection& c) { TORRENT_ASSERT(!c.is_local()); @@ -926,13 +926,15 @@ namespace libtorrent // TODO: only allow _one_ connection to use this // override at a time - TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint()); + asio::error_code ec; + TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint(ec) || ec); if (m_torrent->num_peers() >= m_torrent->max_connections() && m_torrent->session().num_connections() >= m_torrent->session().max_connections() && c.remote().address() != m_torrent->current_tracker().address()) { - throw protocol_error("too many connections, refusing incoming connection"); // cause a disconnect + c.disconnect("too many connections, refusing incoming connection"); + return false; } #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) @@ -960,7 +962,10 @@ namespace libtorrent if (i != m_peers.end()) { if (i->second.banned) - throw protocol_error("ip address banned, closing"); + { + c.disconnect("ip address banned, closing"); + return false; + } if (i->second.connection != 0) { @@ -969,7 +974,8 @@ namespace libtorrent // or the current one is already connected if (!i->second.connection->is_connecting() || c.is_local()) { - throw protocol_error("duplicate connection, closing"); + c.disconnect("duplicate connection, closing"); + return false; } else { @@ -978,7 +984,8 @@ namespace libtorrent " is connecting and this connection is incoming. closing existing " "connection in favour of this one"); #endif - i->second.connection->disconnect(); + i->second.connection->disconnect("incoming duplicate connection " + "with higher priority, closing"); } } } @@ -986,7 +993,8 @@ namespace libtorrent { // we don't have any info about this peer. // add a new entry - TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint()); + asio::error_code ec; + TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint(ec) || ec); peer p(c.remote(), peer::not_connectable, 0); i = m_peers.insert(std::make_pair(c.remote().address(), p)); @@ -1002,12 +1010,15 @@ namespace libtorrent if (!c.fast_reconnect()) i->second.connected = time_now(); // m_last_optimistic_disconnect = time_now(); + return true; } - void policy::update_peer_port(int port, policy::peer* p, int src) + bool policy::update_peer_port(int port, policy::peer* p, int src) { TORRENT_ASSERT(p != 0); - if (p->ip.port() == port) return; + TORRENT_ASSERT(p->connection); + + if (p->ip.port() == port) return true; if (m_torrent->settings().allow_multiple_connections_per_ip) { @@ -1020,7 +1031,8 @@ namespace libtorrent policy::peer& pp = i->second; if (pp.connection) { - throw protocol_error("duplicate connection"); + p->connection->disconnect("duplicate connection"); + return false; } if (m_torrent->has_picker()) m_torrent->picker().clear_peer(&i->second); @@ -1033,6 +1045,7 @@ namespace libtorrent } p->ip.port(port); p->source |= src; + return true; } bool policy::has_peer(policy::peer const* p) const @@ -1362,7 +1375,7 @@ namespace libtorrent (*p->second.connection->m_logger) << "*** CLOSING CONNECTION 'too many connections'\n"; #endif - p->second.connection->disconnect(); + p->second.connection->disconnect("too many connections, closing"); return true; } diff --git a/src/session_impl.cpp b/src/session_impl.cpp index bb122e476..6f0013c63 100755 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -754,7 +754,7 @@ namespace detail #ifndef NDEBUG int conn = m_connections.size(); #endif - (*m_connections.begin())->disconnect(); + (*m_connections.begin())->disconnect("stopping torrent"); TORRENT_ASSERT(conn == int(m_connections.size()) + 1); } @@ -895,7 +895,7 @@ namespace detail return s; } - void session_impl::open_listen_port() throw() + void session_impl::open_listen_port() { // close the open listen sockets m_listen_sockets.clear(); @@ -1110,70 +1110,62 @@ namespace detail c->m_in_constructor = false; #endif - m_connections.insert(c); + if (!c->is_disconnecting()) m_connections.insert(c); } catch (std::exception& exc) { #ifndef NDEBUG std::string err = exc.what(); #endif - }; - - void session_impl::connection_failed(boost::intrusive_ptr const& peer - , tcp::endpoint const& a, char const* message) -#ifndef NDEBUG - try -#endif - { - mutex_t::scoped_lock l(m_mutex); - -// too expensive -// INVARIANT_CHECK; - - connection_map::iterator p = m_connections.find(peer); - - // the connection may have been disconnected in the receive or send phase - if (p == m_connections.end()) return; - if (m_alerts.should_post(alert::debug)) - { - m_alerts.post_alert( - peer_error_alert( - a - , (*p)->pid() - , message)); - } - -#if defined(TORRENT_VERBOSE_LOGGING) - (*(*p)->m_logger) << "*** CONNECTION FAILED " << message << "\n"; -#endif - (*p)->set_failed(); - (*p)->disconnect(); } -#ifndef NDEBUG - catch (...) - { - TORRENT_ASSERT(false); - }; -#endif - void session_impl::close_connection(boost::intrusive_ptr const& p) +/* + namespace + { + struct compare_peer_ptr + { + bool operator()(peer_connection const* lhs + , intrusive_ptr const& rhs) + { + return lhs < rhs.get(); + } + + bool operator()(intrusive_ptr const& lhs + , peer_connection const* rhs) + { + return lhs.get() < rhs; + } + }; + } +*/ + void session_impl::close_connection(peer_connection const* p + , char const* message) { mutex_t::scoped_lock l(m_mutex); // too expensive // INVARIANT_CHECK; +#ifndef NDEBUG +// for (aux::session_impl::torrent_map::const_iterator i = m_torrents.begin() +// , end(m_torrents.end()); i != end; ++i) +// TORRENT_ASSERT(!i->second->has_peer((peer_connection*)p)); +#endif + #if defined(TORRENT_LOGGING) - (*m_logger) << time_now_string() << " CLOSING CONNECTION " << p->remote() << "\n"; + (*m_logger) << time_now_string() << " CLOSING CONNECTION " + << p->remote() << " : " << message << "\n"; #endif TORRENT_ASSERT(p->is_disconnecting()); - connection_map::iterator i = m_connections.find(p); - if (i != m_connections.end()) - { - if (!(*i)->is_choked()) --m_num_unchoked; - m_connections.erase(i); - } + + if (!p->is_choked()) --m_num_unchoked; +// connection_map::iterator i = std::lower_bound(m_connections.begin(), m_connections.end() +// , p, bind(&boost::intrusive_ptr::get, _1) < p); +// if (i->get() != p) i == m_connections.end(); + connection_map::iterator i = std::find_if(m_connections.begin(), m_connections.end() + , bind(&boost::intrusive_ptr::get, _1) == p); + if (i != m_connections.end()) m_connections.erase(i); } void session_impl::set_peer_id(peer_id const& id) @@ -1334,7 +1326,7 @@ namespace detail #endif c.set_failed(); - c.disconnect(); + c.disconnect("timed out"); continue; } diff --git a/src/smart_ban.cpp b/src/smart_ban.cpp index 50f96f606..d0c753907 100644 --- a/src/smart_ban.cpp +++ b/src/smart_ban.cpp @@ -205,7 +205,7 @@ namespace libtorrent { namespace << " | ip: " << p->ip << " ]\n"; #endif p->banned = true; - if (p->connection) p->connection->disconnect(); + if (p->connection) p->connection->disconnect("banning peer for sending bad data"); } // we already have this exact entry in the map // we don't have to insert it @@ -267,7 +267,7 @@ namespace libtorrent { namespace << " | ip: " << p->ip << " ]\n"; #endif p->banned = true; - if (p->connection) p->connection->disconnect(); + if (p->connection) p->connection->disconnect("banning peer for sending bad data"); } torrent& m_torrent; diff --git a/src/torrent.cpp b/src/torrent.cpp index a8ca89cbf..dbc30e298 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -1086,7 +1086,7 @@ namespace libtorrent << " ] 'too many corrupt pieces'\n"; #endif #endif - p->connection->disconnect(); + p->connection->disconnect("too many corrupt pieces, banning peer"); } } } @@ -1828,10 +1828,7 @@ namespace libtorrent #endif // TODO: post an error alert! -// std::map::iterator i = m_connections.find(a); -// if (i != m_connections.end()) m_connections.erase(i); - m_ses.connection_failed(c, a, e.what()); - c->disconnect(); + c->disconnect(e.what()); } } catch (std::exception& exc) @@ -2130,8 +2127,7 @@ namespace libtorrent std::set::iterator i = m_connections.find(boost::get_pointer(c)); if (i != m_connections.end()) m_connections.erase(i); - m_ses.connection_failed(c, a, e.what()); - c->disconnect(); + c->disconnect(e.what()); return false; } peerinfo->connection = c.get(); @@ -2216,7 +2212,11 @@ namespace libtorrent #endif TORRENT_ASSERT(m_connections.find(p) == ci); TORRENT_ASSERT(*ci == p); - m_policy.new_connection(**ci); + if (!m_policy.new_connection(**ci)) + { + m_connections.erase(ci); + return; + } } catch (std::exception& e) { @@ -2261,7 +2261,7 @@ namespace libtorrent #ifndef NDEBUG std::size_t size = m_connections.size(); #endif - p->disconnect(); + p->disconnect(m_abort?"stopping torrent":"pausing torrent"); TORRENT_ASSERT(m_connections.size() <= size); } } @@ -2371,7 +2371,7 @@ namespace libtorrent } } std::for_each(seeds.begin(), seeds.end() - , bind(&peer_connection::disconnect, _1)); + , bind(&peer_connection::disconnect, _1, "torrent finished, disconnecting seed")); TORRENT_ASSERT(m_storage); // we need to keep the object alive during this operation @@ -2586,7 +2586,7 @@ namespace libtorrent // the connection failed, close it torrent::peer_iterator j = i; ++j; - m_ses.connection_failed(*i, (*i)->remote(), e.what()); + (*i)->disconnect(e.what()); i = j; } } @@ -2663,6 +2663,8 @@ namespace libtorrent std::map num_requests; for (const_peer_iterator i = begin(); i != end(); ++i) { + // make sure this peer is not a dangling pointer + TORRENT_ASSERT(m_ses.has_peer(*i)); peer_connection const& p = *(*i); for (std::deque::const_iterator i = p.request_queue().begin() , end(p.request_queue().end()); i != end; ++i) @@ -2997,7 +2999,7 @@ namespace libtorrent (*p->m_logger) << "**ERROR**: " << e.what() << "\n"; #endif p->set_failed(); - p->disconnect(); + p->disconnect(e.what()); } } accumulator += m_stat; diff --git a/src/web_peer_connection.cpp b/src/web_peer_connection.cpp index 04d8ac252..0851499f8 100755 --- a/src/web_peer_connection.cpp +++ b/src/web_peer_connection.cpp @@ -348,7 +348,10 @@ namespace libtorrent m_statistics.received_bytes(payload, protocol); if (error) - throw std::runtime_error("failed to parse HTTP response"); + { + disconnect("failed to parse HTTP response"); + return; + } TORRENT_ASSERT(recv_buffer.left() == 0 || *recv_buffer.begin == 'H'); @@ -377,7 +380,8 @@ namespace libtorrent m_ses.m_alerts.post_alert(url_seed_alert(t->get_handle(), url() , error_msg)); } - throw std::runtime_error(error_msg); + disconnect(error_msg.c_str()); + return; } if (!m_parser.header_finished()) break; @@ -402,7 +406,8 @@ namespace libtorrent { // we should not try this server again. t->remove_url_seed(m_url); - throw std::runtime_error("got HTTP redirection status without location header"); + disconnect("got HTTP redirection status without location header"); + return; } bool single_file_request = false; @@ -422,14 +427,20 @@ namespace libtorrent if (i == std::string::npos) { t->remove_url_seed(m_url); - throw std::runtime_error("got invalid HTTP redirection location (\"" + location + "\") " - "expected it to end with: " + path); + std::stringstream msg; + msg << "got invalid HTTP redirection location (\"" << location << "\") " + "expected it to end with: " << path; + disconnect(msg.str().c_str()); + return; } location.resize(i); } t->add_url_seed(location); t->remove_url_seed(m_url); - throw std::runtime_error("redirecting to " + location); + std::stringstream msg; + msg << "redirecting to \"" << location << "\""; + disconnect(msg.str().c_str()); + return; } std::string const& server_version = m_parser.header("server"); @@ -462,7 +473,10 @@ namespace libtorrent { // we should not try this server again. t->remove_url_seed(m_url); - throw std::runtime_error("invalid range in HTTP response: " + range_str.str()); + std::stringstream msg; + msg << "invalid range in HTTP response: " << range_str; + disconnect(msg.str().c_str()); + return; } // the http range is inclusive range_end++; @@ -475,7 +489,8 @@ namespace libtorrent { // we should not try this server again. t->remove_url_seed(m_url); - throw std::runtime_error("no content-length in HTTP response"); + disconnect("no content-length in HTTP response"); + return; } } @@ -485,7 +500,10 @@ namespace libtorrent torrent_info const& info = t->torrent_file(); if (m_requests.empty() || m_file_requests.empty()) - throw std::runtime_error("unexpected HTTP response"); + { + disconnect("unexpected HTTP response"); + return; + } int file_index = m_file_requests.front(); peer_request in_range = info.map_file(file_index, range_start @@ -516,7 +534,8 @@ namespace libtorrent { // this means the end of the incoming request ends _before_ the // first expected byte (fs + m_piece.size()) - throw std::runtime_error("invalid range in HTTP response"); + disconnect("invalid range in HTTP response"); + return; } // if the request is contained in the range (i.e. the entire request