diff --git a/CMakeLists.txt b/CMakeLists.txt index 37b2ca2ec..40dd2c495 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -348,6 +348,7 @@ set(TDLIB_SOURCE td/mtproto/RawConnection.cpp td/mtproto/SessionConnection.cpp td/mtproto/TcpTransport.cpp + td/mtproto/TlsReaderByteFlow.cpp td/mtproto/Transport.cpp td/mtproto/utils.cpp @@ -478,6 +479,7 @@ set(TDLIB_SOURCE td/mtproto/RawConnection.h td/mtproto/SessionConnection.h td/mtproto/TcpTransport.h + td/mtproto/TlsReaderByteFlow.h td/mtproto/Transport.h td/mtproto/TransportType.h td/mtproto/utils.h diff --git a/td/mtproto/IStreamTransport.cpp b/td/mtproto/IStreamTransport.cpp index 6b807e529..92e050354 100644 --- a/td/mtproto/IStreamTransport.cpp +++ b/td/mtproto/IStreamTransport.cpp @@ -15,7 +15,7 @@ namespace mtproto { unique_ptr create_transport(TransportType type) { switch (type.type) { case TransportType::ObfuscatedTcp: - return td::make_unique(type.dc_id, std::move(type.secret)); + return td::make_unique(type.dc_id, std::move(type.secret), type.emulate_tls); case TransportType::Tcp: return td::make_unique(); case TransportType::Http: diff --git a/td/mtproto/TcpTransport.cpp b/td/mtproto/TcpTransport.cpp index 10c2b2814..709fc9bc9 100644 --- a/td/mtproto/TcpTransport.cpp +++ b/td/mtproto/TcpTransport.cpp @@ -184,7 +184,12 @@ void ObfuscatedTransport::init(ChainBufferReader *input, ChainBufferWriter *outp }; fix_key(key); aes_ctr_byte_flow_.init(key, as(rheader.data() + 8 + 32)); - aes_ctr_byte_flow_.set_input(input_); + if (emulate_tls_) { + tls_reader_byte_flow_.set_input(input_); + tls_reader_byte_flow_ >> aes_ctr_byte_flow_; + } else { + aes_ctr_byte_flow_.set_input(input_); + } aes_ctr_byte_flow_ >> byte_flow_sink_; output_key_ = as(header.data() + 8); @@ -195,6 +200,67 @@ void ObfuscatedTransport::init(ChainBufferReader *input, ChainBufferWriter *outp output_->append(header_slice.substr(56, 8)); } +Result ObfuscatedTransport::read_next(BufferSlice *message, uint32 *quick_ack) { + if (emulate_tls_) { + tls_reader_byte_flow_.wakeup(); + } else { + aes_ctr_byte_flow_.wakeup(); + } + return impl_.read_from_stream(byte_flow_sink_.get_output(), message, quick_ack); +} + +void ObfuscatedTransport::write(BufferWriter &&message, bool quick_ack) { + impl_.write_prepare_inplace(&message, quick_ack); + output_state_.encrypt(message.as_slice(), message.as_slice()); + if (emulate_tls_) { + do_write_tls(std::move(message)); + } else { + do_write(message.as_buffer_slice()); + } +} + +void ObfuscatedTransport::do_write_tls(BufferWriter &&message) { + size_t size = message.size(); + + if (size > (1 << 14)) { + auto buffer_slice = message.as_buffer_slice(); + auto slice = buffer_slice.as_slice(); + while (!slice.empty()) { + auto buf = buffer_slice.from_slice(slice.substr(0, 1 << 14)); + slice.remove_prefix(buf.size()); + BufferBuilder builder; + builder.append(std::move(buf)); + do_write_tls(std::move(builder)); + } + return; + } + + BufferBuilder builder(std::move(message)); + do_write_tls(std::move(builder)); +} + +void ObfuscatedTransport::do_write_tls(BufferBuilder &&builder) { + size_t size = builder.size(); + CHECK(size <= (1 << 14)); + + char buf[] = "\x17\x03\x03\x00\x00"; + buf[3] = (size >> 8) & 0xff; + buf[4] = size & 0xff; + builder.prepend(Slice(buf, 5)); + + if (is_first_tls_packet_) { + is_first_tls_packet_ = false; + Slice first_prefix("\x14\x03\x03\x00\x01\x01"); + builder.prepend(first_prefix); + } + + do_write(builder.extract()); +} + +void ObfuscatedTransport::do_write(BufferSlice &&slice) { + output_->append(std::move(slice)); +} + } // namespace tcp } // namespace mtproto } // namespace td diff --git a/td/mtproto/TcpTransport.h b/td/mtproto/TcpTransport.h index b1e0b6bfc..0a950ec64 100644 --- a/td/mtproto/TcpTransport.h +++ b/td/mtproto/TcpTransport.h @@ -8,6 +8,7 @@ #include "td/mtproto/IStreamTransport.h" #include "td/mtproto/TransportType.h" +#include "td/mtproto/TlsReaderByteFlow.h" #include "td/utils/AesCtrByteFlow.h" #include "td/utils/buffer.h" @@ -122,24 +123,17 @@ class OldTransport : public IStreamTransport { class ObfuscatedTransport : public IStreamTransport { public: - ObfuscatedTransport(int16 dc_id, std::string secret) - : dc_id_(dc_id), secret_(std::move(secret)), impl_(secret_.size() >= 17) { - } - Result read_next(BufferSlice *message, uint32 *quick_ack) override TD_WARN_UNUSED_RESULT { - aes_ctr_byte_flow_.wakeup(); - return impl_.read_from_stream(byte_flow_sink_.get_output(), message, quick_ack); + ObfuscatedTransport(int16 dc_id, std::string secret, bool emulate_tls) + : dc_id_(dc_id), secret_(std::move(secret)), emulate_tls_(emulate_tls), impl_(secret_.size() >= 17) { } + Result read_next(BufferSlice *message, uint32 *quick_ack) override TD_WARN_UNUSED_RESULT; + bool support_quick_ack() const override { return impl_.support_quick_ack(); } - void write(BufferWriter &&message, bool quick_ack) override { - impl_.write_prepare_inplace(&message, quick_ack); - auto slice = message.as_buffer_slice(); - output_state_.encrypt(slice.as_slice(), slice.as_slice()); - output_->append(std::move(slice)); - } + void write(BufferWriter &&message, bool quick_ack) override; void init(ChainBufferReader *input, ChainBufferWriter *output) override; @@ -152,7 +146,14 @@ class ObfuscatedTransport : public IStreamTransport { } size_t max_prepend_size() const override { - return 4; + size_t res = 4; + if (emulate_tls_) { + res += 5; + if (is_first_tls_packet_) { + res += 6; + } + } + return res; } size_t max_append_size() const override { @@ -166,7 +167,10 @@ class ObfuscatedTransport : public IStreamTransport { private: int16 dc_id_; std::string secret_; + bool emulate_tls_; + bool is_first_tls_packet_{true}; TransportImpl impl_; + TlsReaderByteFlow tls_reader_byte_flow_; AesCtrByteFlow aes_ctr_byte_flow_; ByteFlowSink byte_flow_sink_; ChainBufferReader *input_; @@ -177,6 +181,10 @@ class ObfuscatedTransport : public IStreamTransport { UInt256 output_key_; AesCtrState output_state_; ChainBufferWriter *output_; + + void do_write_tls(BufferWriter &&message); + void do_write_tls(BufferBuilder &&builder); + void do_write(BufferSlice &&message); }; using Transport = ObfuscatedTransport; diff --git a/td/mtproto/TlsReaderByteFlow.cpp b/td/mtproto/TlsReaderByteFlow.cpp new file mode 100644 index 000000000..26ea2cbb6 --- /dev/null +++ b/td/mtproto/TlsReaderByteFlow.cpp @@ -0,0 +1,43 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/mtproto/TlsReaderByteFlow.h" + +#include "td/utils/Status.h" + +namespace td { + +void TlsReaderByteFlow::loop() { + while (true) { + if (input_->size() < 5) { + set_need_size(5); + return; + } + + auto it = input_->clone(); + uint8 buf[5]; + it.advance(5, MutableSlice(buf, 5)); + if (Slice(buf, 3) != Slice("\x17\x03\x03")) { + close_input(td::Status::Error("Invalid bytes at the beginning of a packet (emulated tls)")); + return; + } + size_t len = (buf[3] << 8) | buf[4]; + if (len > (1 << 14)) { + close_input(td::Status::Error("Packet lenght is too big (emulated tls)")); + return; + } + + if (it.size() < len) { + set_need_size(5 + len); + return; + } + + output_.append(it.cut_head(len)); + *input_ = std::move(it); + } +} + +} // namespace td diff --git a/td/mtproto/TlsReaderByteFlow.h b/td/mtproto/TlsReaderByteFlow.h new file mode 100644 index 000000000..bb6eca45e --- /dev/null +++ b/td/mtproto/TlsReaderByteFlow.h @@ -0,0 +1,19 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/utils/ByteFlow.h" + +namespace td { + +class TlsReaderByteFlow final : public ByteFlowBase { + public: + TlsReaderByteFlow() = default; + void loop() override; +}; + +} // namespace td diff --git a/td/mtproto/TransportType.h b/td/mtproto/TransportType.h index e5323da49..1a1bb4279 100644 --- a/td/mtproto/TransportType.h +++ b/td/mtproto/TransportType.h @@ -12,13 +12,14 @@ namespace td { namespace mtproto { struct TransportType { - enum Type { Tcp, ObfuscatedTcp, Http } type; - int16 dc_id; + enum Type { Tcp, ObfuscatedTcp, Http } type = Tcp; + int16 dc_id{0}; string secret; + bool emulate_tls{false}; - TransportType() : type(Tcp), dc_id(0), secret() { - } - TransportType(Type type, int16 dc_id, string secret) : type(type), dc_id(dc_id), secret(std::move(secret)) { + TransportType() = default; + TransportType(Type type, int16 dc_id, string secret, bool emulate_tls = false) + : type(type), dc_id(dc_id), secret(std::move(secret)), emulate_tls(emulate_tls) { } }; diff --git a/tdutils/td/utils/buffer.cpp b/tdutils/td/utils/buffer.cpp index 83a9e3509..516d0c9c0 100644 --- a/tdutils/td/utils/buffer.cpp +++ b/tdutils/td/utils/buffer.cpp @@ -133,10 +133,9 @@ BufferSlice BufferBuilder::extract() { if (to_append_.empty() && to_prepend_.empty()) { return buffer_writer_.as_buffer_slice(); } - size_t total_size = 0; - for_each([&](auto &&slice) { total_size += slice.size(); }); + size_t total_size = size(); BufferWriter writer(0, 0, total_size); - for_each([&](auto &&slice) { + std::move(*this).for_each([&](auto &&slice) { writer.prepare_append().truncate(slice.size()).copy_from(slice.as_slice()); writer.confirm_append(slice.size()); }); @@ -144,6 +143,12 @@ BufferSlice BufferBuilder::extract() { return writer.as_buffer_slice(); } +size_t BufferBuilder::size() const { + size_t total_size = 0; + for_each([&](auto &&slice) { total_size += slice.size(); }); + return total_size; +} + bool BufferBuilder::append_inplace(Slice slice) { if (!to_append_.empty()) { return false; diff --git a/tdutils/td/utils/buffer.h b/tdutils/td/utils/buffer.h index 87899389a..27add9219 100644 --- a/tdutils/td/utils/buffer.h +++ b/tdutils/td/utils/buffer.h @@ -731,6 +731,8 @@ class BufferBuilder { BufferBuilder(Slice slice, size_t prepend_size, size_t append_size) : buffer_writer_(slice, prepend_size, append_size) { } + explicit BufferBuilder(BufferWriter &&buffer_writer) : buffer_writer_(std::move(buffer_writer)) { + } void append(BufferSlice slice); void append(Slice slice); @@ -739,7 +741,7 @@ class BufferBuilder { void prepend(Slice slice); template - void for_each(F &&f) { + void for_each(F &&f) const & { for (auto &slice : reversed(to_prepend_)) { f(slice); } @@ -750,6 +752,19 @@ class BufferBuilder { f(slice); } } + template + void for_each(F &&f) && { + for (auto &slice : reversed(to_prepend_)) { + f(std::move(slice)); + } + if (!buffer_writer_.empty()) { + f(buffer_writer_.as_buffer_slice()); + } + for (auto &slice : to_append_) { + f(std::move(slice)); + } + } + size_t size() const; BufferSlice extract();