diff --git a/Jamfile b/Jamfile index 856543183bd..30efdc2f055 100644 --- a/Jamfile +++ b/Jamfile @@ -153,8 +153,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/bt_peer_connection.hpp b/include/libtorrent/bt_peer_connection.hpp index 06a61fb6fac..776ebedea81 100644 --- a/include/libtorrent/bt_peer_connection.hpp +++ b/include/libtorrent/bt_peer_connection.hpp @@ -364,6 +364,10 @@ namespace libtorrent { private: +#if !defined TORRENT_DISABLE_ENCRYPTION + void init_bt_handshake(); +#endif + enum class state_t : std::uint8_t { #if !defined TORRENT_DISABLE_ENCRYPTION @@ -374,7 +378,6 @@ namespace libtorrent { read_pe_cryptofield, read_pe_pad, read_pe_ia, - init_bt_handshake, #endif read_protocol_identifier, read_info_hash, diff --git a/include/libtorrent/pe_crypto.hpp b/include/libtorrent/pe_crypto.hpp index 037b0b7abfe..3c2280ea229 100644 --- a/include/libtorrent/pe_crypto.hpp +++ b/include/libtorrent/pe_crypto.hpp @@ -74,7 +74,6 @@ namespace libtorrent { { 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/src/bt_peer_connection.cpp b/src/bt_peer_connection.cpp index f1632cbcefa..99a2dfea0dd 100644 --- a/src/bt_peer_connection.cpp +++ b/src/bt_peer_connection.cpp @@ -527,7 +527,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; @@ -2729,6 +2729,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) { std::shared_ptr t = associated_torrent().lock(); @@ -3119,30 +3157,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); } } @@ -3164,55 +3200,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 @@ -3229,47 +3261,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) @@ -3327,6 +3326,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;