diff --git a/CMakeLists.txt b/CMakeLists.txt index 45e80e97ddc..cd72988100c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -660,18 +660,21 @@ if (NOT Windows) return 0; } ]=]) + string(REPLACE "std::atomic" "std::atomic" ATOMICS8_TEST_SOURCE "${ATOMICS_TEST_SOURCE}") string(REPLACE "std::atomic" "std::atomic" ATOMICS64_TEST_SOURCE "${ATOMICS_TEST_SOURCE}") if(APPLE) set(CMAKE_REQUIRED_FLAGS "-std=c++11") endif() check_cxx_source_compiles("${ATOMICS_TEST_SOURCE}" HAVE_CXX_ATOMICS_WITHOUT_LIB) + check_cxx_source_compiles("${ATOMICS8_TEST_SOURCE}" HAVE_CXX_ATOMICS8_WITHOUT_LIB) check_cxx_source_compiles("${ATOMICS64_TEST_SOURCE}" HAVE_CXX_ATOMICS64_WITHOUT_LIB) - if((NOT HAVE_CXX_ATOMICS_WITHOUT_LIB) OR (NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)) + if((NOT HAVE_CXX_ATOMICS_WITHOUT_LIB) OR (NOT HAVE_CXX_ATOMICS8_WITHOUT_LIB) OR (NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)) set(CMAKE_REQUIRED_LIBRARIES "atomic") check_cxx_source_compiles("${ATOMICS_TEST_SOURCE}" HAVE_CXX_ATOMICS_WITH_LIB) + check_cxx_source_compiles("${ATOMICS8_TEST_SOURCE}" HAVE_CXX_ATOMICS8_WITH_LIB) check_cxx_source_compiles("${ATOMICS64_TEST_SOURCE}" HAVE_CXX_ATOMICS64_WITH_LIB) - if ((NOT HAVE_CXX_ATOMICS_WITH_LIB) OR (NOT HAVE_CXX_ATOMICS64_WITH_LIB)) + if ((NOT HAVE_CXX_ATOMICS_WITH_LIB) OR (NOT HAVE_CXX_ATOMICS8_WITH_LIB) OR (NOT HAVE_CXX_ATOMICS64_WITH_LIB)) message(STATUS, "No native support for std::atomic, or libatomic not found! Build link step may fail") else() message(STATUS "Linking with libatomic for atomics support") diff --git a/Jamfile b/Jamfile index 66fd52a5b6f..757e603284d 100644 --- a/Jamfile +++ b/Jamfile @@ -156,8 +156,7 @@ rule linking ( properties * ) result += CoreFoundation SystemConfiguration ; } - if ( gcc in $(properties) - || clang in $(properties) ) + if gcc in $(properties) && linux in $(properties) && ( on in $(properties) || production in $(properties) diff --git a/include/libtorrent/aux_/bt_peer_connection.hpp b/include/libtorrent/aux_/bt_peer_connection.hpp index 8274b11c30a..bd2aaa1b1f0 100644 --- a/include/libtorrent/aux_/bt_peer_connection.hpp +++ b/include/libtorrent/aux_/bt_peer_connection.hpp @@ -341,6 +341,10 @@ namespace libtorrent::aux { private: +#if !defined TORRENT_DISABLE_ENCRYPTION + void init_bt_handshake(); +#endif + enum class state_t : std::uint8_t { #if !defined TORRENT_DISABLE_ENCRYPTION @@ -351,7 +355,6 @@ namespace libtorrent::aux { read_pe_cryptofield, read_pe_pad, read_pe_ia, - init_bt_handshake, #endif read_protocol_identifier, read_info_hash, diff --git a/include/libtorrent/aux_/pe_crypto.hpp b/include/libtorrent/aux_/pe_crypto.hpp index fa8d8e3ac8b..0aeb1ea73b4 100644 --- a/include/libtorrent/aux_/pe_crypto.hpp +++ b/include/libtorrent/aux_/pe_crypto.hpp @@ -51,7 +51,6 @@ namespace libtorrent::aux { { public: dh_key_exchange(); - bool good() const { return true; } // Get local public key key_t const& get_local_key() const { return m_dh_local_key; } diff --git a/include/libtorrent/aux_/torrent_list.hpp b/include/libtorrent/aux_/torrent_list.hpp index cead6d71fed..03aba00f525 100644 --- a/include/libtorrent/aux_/torrent_list.hpp +++ b/include/libtorrent/aux_/torrent_list.hpp @@ -211,7 +211,9 @@ struct torrent_list #endif TORRENT_ASSERT(all_torrents == all_indexed_torrents); +#if !defined TORRENT_DISABLE_ENCRYPTION TORRENT_ASSERT(all_torrents == all_obf_indexed_torrents); +#endif } #endif diff --git a/src/bt_peer_connection.cpp b/src/bt_peer_connection.cpp index 5f20806f27d..7cc5814409b 100644 --- a/src/bt_peer_connection.cpp +++ b/src/bt_peer_connection.cpp @@ -512,7 +512,7 @@ namespace { #endif m_dh_key_exchange.reset(new (std::nothrow) dh_key_exchange); - if (!m_dh_key_exchange || !m_dh_key_exchange->good()) + if (!m_dh_key_exchange) { disconnect(errors::no_memory, operation_t::encryption); return; @@ -2714,6 +2714,44 @@ namespace { on_receive_impl(bytes_transferred); } +#if !defined TORRENT_DISABLE_ENCRYPTION + void bt_peer_connection::init_bt_handshake() + { + m_encrypted = true; + if (m_rc4_encrypted) + { + switch_send_crypto(m_rc4); + switch_recv_crypto(m_rc4); + } + + // decrypt remaining received bytes + if (m_rc4_encrypted) + { + span const remaining = m_recv_buffer.mutable_buffer() + .subspan(m_recv_buffer.packet_size()); + rc4_decrypt(remaining); + +#ifndef TORRENT_DISABLE_LOGGING + peer_log(peer_log_alert::info, "ENCRYPTION" + , "decrypted remaining %d bytes", int(remaining.size())); +#endif + } + m_rc4.reset(); + + // encrypted portion of handshake completed, toggle + // peer_info pe_support flag back to true + if (is_outgoing() && + m_settings.get_int(settings_pack::out_enc_policy) + == settings_pack::pe_enabled) + { + torrent_peer* pi = peer_info_struct(); + TORRENT_ASSERT(pi); + + pi->pe_support = true; + } + } +#endif + void bt_peer_connection::on_receive_impl(std::size_t bytes_transferred) { auto t = associated_torrent().lock(); @@ -3104,30 +3142,28 @@ namespace { m_rc4_encrypted = true; } - int const len_pad = aux::read_int16(recv_buffer); + int len_pad = aux::read_int16(recv_buffer); if (len_pad < 0 || len_pad > 512) { disconnect(errors::invalid_pad_size, operation_t::encryption, peer_error); return; } - m_state = state_t::read_pe_pad; + // len(IA) at the end of pad if (!is_outgoing()) - m_recv_buffer.reset(len_pad + 2); // len(IA) at the end of pad + len_pad += 2; + + if (len_pad > 0) + { + m_state = state_t::read_pe_pad; + m_recv_buffer.reset(len_pad); + } else { - if (len_pad == 0) - { - m_encrypted = true; - if (m_rc4_encrypted) - { - switch_send_crypto(m_rc4); - switch_recv_crypto(m_rc4); - } - m_state = state_t::init_bt_handshake; - } - else - m_recv_buffer.reset(len_pad); + TORRENT_ASSERT(len_pad == 0); + init_bt_handshake(); + m_state = state_t::read_protocol_identifier; + m_recv_buffer.reset(20); } } @@ -3149,55 +3185,51 @@ namespace { recv_buffer = recv_buffer.subspan(pad_size); int const len_ia = aux::read_int16(recv_buffer); - if (len_ia < 0) +#ifndef TORRENT_DISABLE_LOGGING + peer_log(peer_log_alert::info, "ENCRYPTION", "len(IA) : %d", len_ia); +#endif + if (len_ia < 0 || len_ia > 68) { disconnect(errors::invalid_encrypt_handshake, operation_t::encryption, peer_error); return; } -#ifndef TORRENT_DISABLE_LOGGING - peer_log(peer_log_alert::info, "ENCRYPTION", "len(IA) : %d", len_ia); -#endif if (len_ia == 0) { - // everything after this is Encrypt2 - m_encrypted = true; - if (m_rc4_encrypted) - { - switch_send_crypto(m_rc4); - switch_recv_crypto(m_rc4); - } - m_state = state_t::init_bt_handshake; + // everything after this is encrypted + init_bt_handshake(); + m_state = state_t::read_protocol_identifier; + m_recv_buffer.reset(20); } else { + // The other peer indicated that a non-zero bytes will be + // encrypted at the start of the underlying bittorrent + // protocol. This number of bytes, len_ia, is not + // necessarily aligned to message boundaries. We first read + // that many bytes, decrypt it, and then pass it back into + // the regular protocol parser m_state = state_t::read_pe_ia; m_recv_buffer.reset(len_ia); } } else // is_outgoing() { - // everything that arrives after this is Encrypt2 - m_encrypted = true; - if (m_rc4_encrypted) - { - switch_send_crypto(m_rc4); - switch_recv_crypto(m_rc4); - } - m_state = state_t::init_bt_handshake; + // everything that arrives after this is encrypted + init_bt_handshake(); + m_state = state_t::read_protocol_identifier; + m_recv_buffer.reset(20); } } if (m_state == state_t::read_pe_ia) { - received_bytes(0, int(bytes_transferred)); - bytes_transferred = 0; TORRENT_ASSERT(!is_outgoing()); TORRENT_ASSERT(!m_encrypted); if (!m_recv_buffer.packet_finished()) return; - // ia is always rc4, so decrypt it + // the IA bytes are always rc4, so decrypt it rc4_decrypt(m_recv_buffer.mutable_buffer().first(m_recv_buffer.packet_size())); #ifndef TORRENT_DISABLE_LOGGING @@ -3214,47 +3246,14 @@ namespace { } m_rc4.reset(); + // now that we have decrypted IA length of bytes, we + // reinterpret the receive buffer as the very start of a normal + // connection. First we expect to find the protocol identifier + // (i.e. "BitTorrent Protocol") m_state = state_t::read_protocol_identifier; m_recv_buffer.cut(0, 20); } - if (m_state == state_t::init_bt_handshake) - { - received_bytes(0, int(bytes_transferred)); - bytes_transferred = 0; - TORRENT_ASSERT(m_encrypted); - - // decrypt remaining received bytes - if (m_rc4_encrypted) - { - span const remaining = m_recv_buffer.mutable_buffer() - .subspan(m_recv_buffer.packet_size()); - rc4_decrypt(remaining); - -#ifndef TORRENT_DISABLE_LOGGING - peer_log(peer_log_alert::info, "ENCRYPTION" - , "decrypted remaining %d bytes", int(remaining.size())); -#endif - } - m_rc4.reset(); - - // payload stream, start with 20 handshake bytes - m_state = state_t::read_protocol_identifier; - m_recv_buffer.reset(20); - - // encrypted portion of handshake completed, toggle - // peer_info pe_support flag back to true - if (is_outgoing() && - m_settings.get_int(settings_pack::out_enc_policy) - == settings_pack::pe_enabled) - { - torrent_peer* pi = peer_info_struct(); - TORRENT_ASSERT(pi); - - pi->pe_support = true; - } - } - #endif // #if !defined TORRENT_DISABLE_ENCRYPTION if (m_state == state_t::read_protocol_identifier) @@ -3312,6 +3311,10 @@ namespace { peer_log(peer_log_alert::info, "ENCRYPTION", "attempting encrypted connection"); #endif m_state = state_t::read_pe_dhkey; + // we're "cutting" off 0 bytes from the receive buffer here + // because we want to interpret it as something else. It didn't + // contain the expected bittorrent handshake string, so let's + // try again to interpret it as an encrypted handshake m_recv_buffer.cut(0, dh_key_len); TORRENT_ASSERT(!m_recv_buffer.packet_finished()); return;