diff --git a/td/telegram/net/ConnectionCreator.cpp b/td/telegram/net/ConnectionCreator.cpp index f6d464b90..70f75bb87 100644 --- a/td/telegram/net/ConnectionCreator.cpp +++ b/td/telegram/net/ConnectionCreator.cpp @@ -24,6 +24,7 @@ #include "td/net/GetHostByNameActor.h" #include "td/net/Socks5.h" +#include "td/net/TransparentProxy.h" #include "td/utils/format.h" #include "td/utils/logging.h" @@ -493,7 +494,7 @@ void ConnectionCreator::ping_proxy_resolved(int32 proxy_id, IPAddress ip_address }); CHECK(proxy.use_proxy()); if (proxy.use_socks5_proxy()) { - class Callback : public Socks5::Callback { + class Callback : public TransparentProxy::Callback { public: explicit Callback(Promise promise) : promise_(std::move(promise)) { } @@ -905,7 +906,7 @@ void ConnectionCreator::client_loop(ClientInfo &client) { extra.stat); if (proxy.use_socks5_proxy()) { - class Callback : public Socks5::Callback { + class Callback : public TransparentProxy::Callback { public: explicit Callback(Promise promise, std::unique_ptr stats_callback) : promise_(std::move(promise)), stats_callback_(std::move(stats_callback)) { diff --git a/tdnet/CMakeLists.txt b/tdnet/CMakeLists.txt index 823ed027d..66763aa6d 100644 --- a/tdnet/CMakeLists.txt +++ b/tdnet/CMakeLists.txt @@ -19,6 +19,7 @@ set(TDNET_SOURCE td/net/Socks5.cpp td/net/SslFd.cpp td/net/TcpListener.cpp + td/net/TransparentProxy.cpp td/net/Wget.cpp td/net/GetHostByNameActor.h @@ -35,6 +36,7 @@ set(TDNET_SOURCE td/net/Socks5.h td/net/SslFd.h td/net/TcpListener.h + td/net/TransparentProxy.h td/net/Wget.h ) diff --git a/tdnet/td/net/Socks5.cpp b/tdnet/td/net/Socks5.cpp index 4170f573e..494a6d7be 100644 --- a/tdnet/td/net/Socks5.cpp +++ b/tdnet/td/net/Socks5.cpp @@ -13,54 +13,8 @@ namespace td { -static int VERBOSITY_NAME(socks5) = VERBOSITY_NAME(DEBUG); - -Socks5::Socks5(SocketFd socket_fd, IPAddress ip_address, string username, string password, - std::unique_ptr callback, ActorShared<> parent) - : fd_(std::move(socket_fd)) - , ip_address_(std::move(ip_address)) - , username_(std::move(username)) - , password_(std::move(password)) - , callback_(std::move(callback)) - , parent_(std::move(parent)) { -} - -void Socks5::on_error(Status status) { - CHECK(status.is_error()); - VLOG(socks5) << "Receive " << status; - if (callback_) { - callback_->set_result(std::move(status)); - callback_.reset(); - } - stop(); -} - -void Socks5::tear_down() { - VLOG(socks5) << "Finish to connect to proxy"; - unsubscribe(fd_.get_fd()); - fd_.get_fd().set_observer(nullptr); - if (callback_) { - callback_->set_result(std::move(fd_)); - callback_.reset(); - } -} - -void Socks5::hangup() { - on_error(Status::Error("Cancelled")); -} - -void Socks5::start_up() { - VLOG(socks5) << "Begin to connect to proxy"; - fd_.get_fd().set_observer(this); - subscribe(fd_.get_fd()); - set_timeout_in(10); - if (can_write(fd_)) { - loop(); - } -} - void Socks5::send_greeting() { - VLOG(socks5) << "Send greeting to proxy"; + VLOG(proxy) << "Send greeting to proxy"; CHECK(state_ == State::SendGreeting); state_ = State::WaitGreetingResponse; @@ -79,7 +33,7 @@ void Socks5::send_greeting() { Status Socks5::wait_greeting_response() { auto &buf = fd_.input_buffer(); - VLOG(socks5) << "Receive greeting response of size " << buf.size(); + VLOG(proxy) << "Receive greeting response of size " << buf.size(); if (buf.size() < 2) { return Status::OK(); } @@ -101,7 +55,7 @@ Status Socks5::wait_greeting_response() { } Status Socks5::send_username_password() { - VLOG(socks5) << "Send username and password"; + VLOG(proxy) << "Send username and password"; if (username_.size() >= 128) { return Status::Error("Username is too long"); } @@ -123,7 +77,7 @@ Status Socks5::send_username_password() { Status Socks5::wait_password_response() { auto &buf = fd_.input_buffer(); - VLOG(socks5) << "Receive password response of size " << buf.size(); + VLOG(proxy) << "Receive password response of size " << buf.size(); if (buf.size() < 2) { return Status::OK(); } @@ -142,7 +96,7 @@ Status Socks5::wait_password_response() { } void Socks5::send_ip_address() { - VLOG(socks5) << "Send IP address"; + VLOG(proxy) << "Send IP address"; CHECK(state_ == State::SendIpAddress); callback_->on_connected(); string request; @@ -170,7 +124,7 @@ void Socks5::send_ip_address() { Status Socks5::wait_ip_address_response() { CHECK(state_ == State::WaitIpAddressResponse); auto it = fd_.input_buffer().clone(); - VLOG(socks5) << "Receive IP address response of size " << it.size(); + VLOG(proxy) << "Receive IP address response of size " << it.size(); if (it.size() < 4) { return Status::OK(); } @@ -189,16 +143,19 @@ Status Socks5::wait_ip_address_response() { return Status::Error("byte must be zero"); } it.advance(1, c_slice); + size_t total_size = 6; if (c == '\x01') { if (it.size() < 4) { return Status::OK(); } it.advance(4); + total_size += 4; } else if (c == '\x04') { if (it.size() < 16) { return Status::OK(); } it.advance(16); + total_size += 16; } else { return Status::Error("Invalid response"); } @@ -206,43 +163,30 @@ Status Socks5::wait_ip_address_response() { return Status::OK(); } it.advance(2); + fd_.input_buffer().advance(total_size); stop(); return Status::OK(); } -void Socks5::loop() { - auto status = [&] { - TRY_STATUS(fd_.flush_read()); - switch (state_) { - case State::SendGreeting: - send_greeting(); - break; - case State::WaitGreetingResponse: - TRY_STATUS(wait_greeting_response()); - break; - case State::WaitPasswordResponse: - TRY_STATUS(wait_password_response()); - break; - case State::WaitIpAddressResponse: - TRY_STATUS(wait_ip_address_response()); - break; - case State::SendIpAddress: - case State::Stop: - UNREACHABLE(); - } - TRY_STATUS(fd_.flush_write()); - return Status::OK(); - }(); - if (status.is_error()) { - on_error(std::move(status)); +Status Socks5::loop_impl() { + switch (state_) { + case State::SendGreeting: + send_greeting(); + break; + case State::WaitGreetingResponse: + TRY_STATUS(wait_greeting_response()); + break; + case State::WaitPasswordResponse: + TRY_STATUS(wait_password_response()); + break; + case State::WaitIpAddressResponse: + TRY_STATUS(wait_ip_address_response()); + break; + case State::SendIpAddress: + case State::Stop: + UNREACHABLE(); } - if (can_close(fd_)) { - on_error(Status::Error("Connection closed")); - } -} - -void Socks5::timeout_expired() { - on_error(Status::Error("Timeout expired")); + return Status::OK(); } } // namespace td diff --git a/tdnet/td/net/Socks5.h b/tdnet/td/net/Socks5.h index 9d20dd05e..76f2a768f 100644 --- a/tdnet/td/net/Socks5.h +++ b/tdnet/td/net/Socks5.h @@ -6,45 +6,17 @@ // #pragma once -#include "td/actor/actor.h" +#include "td/net/TransparentProxy.h" -#include "td/utils/BufferedFd.h" -#include "td/utils/common.h" -#include "td/utils/port/IPAddress.h" -#include "td/utils/port/SocketFd.h" #include "td/utils/Status.h" namespace td { -class Socks5 : public Actor { +class Socks5 : public TransparentProxy { public: - class Callback { - public: - Callback() = default; - Callback(const Callback &) = delete; - Callback &operator=(const Callback &) = delete; - virtual ~Callback() = default; - - virtual void set_result(Result) = 0; - virtual void on_connected() = 0; - }; - - Socks5(SocketFd socket_fd, IPAddress ip_address, string username, string password, std::unique_ptr callback, - ActorShared<> parent); + using TransparentProxy::TransparentProxy; private: - BufferedFd fd_; - IPAddress ip_address_; - string username_; - string password_; - std::unique_ptr callback_; - ActorShared<> parent_; - - void on_error(Status status); - void tear_down() override; - void start_up() override; - void hangup() override; - enum class State { SendGreeting, WaitGreetingResponse, @@ -63,8 +35,7 @@ class Socks5 : public Actor { void send_ip_address(); Status wait_ip_address_response(); - void loop() override; - void timeout_expired() override; + Status loop_impl() override; }; } // namespace td diff --git a/tdnet/td/net/TransparentProxy.cpp b/tdnet/td/net/TransparentProxy.cpp new file mode 100644 index 000000000..9b997f0b5 --- /dev/null +++ b/tdnet/td/net/TransparentProxy.cpp @@ -0,0 +1,85 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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/net/TransparentProxy.h" + +#include "td/utils/logging.h" +#include "td/utils/port/Fd.h" +#include "td/utils/Slice.h" + +namespace td { + +int VERBOSITY_NAME(proxy) = VERBOSITY_NAME(DEBUG); + +TransparentProxy::TransparentProxy(SocketFd socket_fd, IPAddress ip_address, string username, string password, + std::unique_ptr callback, ActorShared<> parent) + : fd_(std::move(socket_fd)) + , ip_address_(std::move(ip_address)) + , username_(std::move(username)) + , password_(std::move(password)) + , callback_(std::move(callback)) + , parent_(std::move(parent)) { +} + +void TransparentProxy::on_error(Status status) { + CHECK(status.is_error()); + VLOG(proxy) << "Receive " << status; + if (callback_) { + callback_->set_result(std::move(status)); + callback_.reset(); + } + stop(); +} + +void TransparentProxy::tear_down() { + VLOG(proxy) << "Finish to connect to proxy"; + unsubscribe(fd_.get_fd()); + fd_.get_fd().set_observer(nullptr); + if (callback_) { + if (!fd_.input_buffer().empty()) { + LOG(ERROR) << "Have " << fd_.input_buffer().size() << " unread bytes"; + callback_->set_result(Status::Error("Proxy has sent to much data")); + } else { + callback_->set_result(std::move(fd_)); + } + callback_.reset(); + } +} + +void TransparentProxy::hangup() { + on_error(Status::Error("Cancelled")); +} + +void TransparentProxy::start_up() { + VLOG(proxy) << "Begin to connect to proxy"; + fd_.get_fd().set_observer(this); + subscribe(fd_.get_fd()); + set_timeout_in(10); + if (can_write(fd_)) { + loop(); + } +} + +void TransparentProxy::loop() { + auto status = [&] { + TRY_STATUS(fd_.flush_read()); + TRY_STATUS(loop_impl()); + TRY_STATUS(fd_.flush_write()); + return Status::OK(); + }(); + if (status.is_error()) { + on_error(std::move(status)); + } + if (can_close(fd_)) { + on_error(Status::Error("Connection closed")); + } +} + +void TransparentProxy::timeout_expired() { + on_error(Status::Error("Timeout expired")); +} + +} // namespace td diff --git a/tdnet/td/net/TransparentProxy.h b/tdnet/td/net/TransparentProxy.h new file mode 100644 index 000000000..ef3bbd548 --- /dev/null +++ b/tdnet/td/net/TransparentProxy.h @@ -0,0 +1,56 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// 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/actor/actor.h" + +#include "td/utils/BufferedFd.h" +#include "td/utils/common.h" +#include "td/utils/port/IPAddress.h" +#include "td/utils/port/SocketFd.h" +#include "td/utils/Status.h" + +namespace td { + +extern int VERBOSITY_NAME(proxy); + +class TransparentProxy : public Actor { + public: + class Callback { + public: + Callback() = default; + Callback(const Callback &) = delete; + Callback &operator=(const Callback &) = delete; + virtual ~Callback() = default; + + virtual void set_result(Result) = 0; + virtual void on_connected() = 0; + }; + + TransparentProxy(SocketFd socket_fd, IPAddress ip_address, string username, string password, std::unique_ptr callback, + ActorShared<> parent); + + protected: + BufferedFd fd_; + IPAddress ip_address_; + string username_; + string password_; + std::unique_ptr callback_; + ActorShared<> parent_; + + void on_error(Status status); + void tear_down() override; + void start_up() override; + void hangup() override; + + void loop() override; + void timeout_expired() override; + + virtual Status loop_impl() = 0; +}; + +} // namespace td diff --git a/test/mtproto.cpp b/test/mtproto.cpp index a755a117e..87a770fdb 100644 --- a/test/mtproto.cpp +++ b/test/mtproto.cpp @@ -17,6 +17,7 @@ #include "td/mtproto/RawConnection.h" #include "td/net/Socks5.h" +#include "td/net/TransparentProxy.h" #include "td/telegram/ConfigManager.h" #include "td/telegram/net/DcId.h" @@ -302,7 +303,7 @@ class Socks5TestActor : public Actor { send_closure(actor_id, &Socks5TestActor::on_result, std::move(res), false); }); - class Callback : public Socks5::Callback { + class Callback : public TransparentProxy::Callback { public: explicit Callback(Promise promise) : promise_(std::move(promise)) { }