#include #include #include #include #include #include #include #define SSL_R_SHORT_READ 219 #include "ssl/ssl_locl.h" #include #if defined(close) #undef close #endif enum AddressType { AT_IPV4 = 0, AT_HOST = 3, AT_IPV6 = 4 }; class ConnectResponseRecord { public: std::vector rawData; enum ConnectResponseType { CRT_SUCCESS = 0, CRT_GENERAL_SOCKS_SERVER_FAILURE = 1, CRT_CONNECTION_NOT_ALLOWED_BY_RULESET = 2, CRT_NETWORK_UNREACHABLE = 3, CRT_HOST_UNREACHABLE = 4, CRT_CONNECTION_REFUSED = 5, CRT_TTL_EXPIRED = 6, CRT_COMMAND_NOT_SUPPORTED = 7, CRT_ADDRESS_TYPE_NOT_SUPPORTED = 8, CRT_TO_FF_UNASSIGNED = 9 }; ConnectResponseType connectResponseType; AddressType addressType; std::string address; uint16_t port; }; class ConnectRequestRecord { public: std::string address; uint16_t port; std::string getRequestData(); }; std::string ConnectRequestRecord::getRequestData() { std::string data = ""; data += 0x05; data += 0x01; data += char(0x00); data += 0x03; data += static_cast(address.size()); data += address; data += port / 256; data += port % 256; return data; } class ProxyClient { public: ProxyClient(boost::asio::io_service& ioServiceIn, boost::asio::ssl::context& context, boost::asio::ip::tcp::resolver& resolverIn) : ioService(ioServiceIn) , socket(ioServiceIn, context) , resolver(resolverIn) { socket.set_verify_mode(boost::asio::ssl::verify_peer); socket.set_verify_callback( std::bind(&ProxyClient::verify_certificate, this, std::placeholders::_1, std::placeholders::_2)); } void start() { doConnect(); } void close() { ioService.post([this]() { lowerSocket().close(); }); } std::function onReady; boost::asio::ssl::stream& getSocket() { return socket; } boost::asio::ssl::stream::lowest_layer_type& lowerSocket() { return socket.lowest_layer(); } private: void doConnect() { boost::asio::ip::tcp::resolver::iterator endpointIterator(resolver.resolve({ "127.0.0.1", "8043" })); boost::asio::async_connect(lowerSocket(), endpointIterator, [this](boost::system::error_code ec, boost::asio::ip::tcp::resolver::iterator) { if (!ec) { doHandshake(); } }); } void doHandshake() { socket.async_handshake(boost::asio::ssl::stream_base::client, [this](const boost::system::error_code& error) { if (!error) { onReady(); //sendVersion(); } else { std::cout << "Handshake failed: " << error.message() << "\n"; lowerSocket().close(); } }); } bool verify_certificate(bool preverified, boost::asio::ssl::verify_context& ctx) { // The verify callback can be used to check whether the certificate that is // being presented is valid for the peer. For example, RFC 2818 describes // the steps involved in doing this for HTTPS. Consult the OpenSSL // documentation for more details. Note that the callback is called once // for each certificate in the certificate chain, starting from the root // certificate authority. // In this example we will simply print the certificate's subject name. char subject_name[256]; X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256); std::cout << "Verifying " << subject_name << "\n"; return preverified; } void sendVersion() { std::array version = {0x05, 0x01, 0x02}; boost::asio::async_write(socket, boost::asio::buffer(version.data(), version.size()), [this](boost::system::error_code ec, std::size_t length) { if (!ec) { doReadServerVersion(); } else { lowerSocket().close(); } }); } std::array serverVersionMethod; std::array serverAuthStatus; std::vector connectRequestArr; void doReadServerVersion() { boost::asio::async_read(socket, boost::asio::buffer(serverVersionMethod.data(), serverVersionMethod.size()), [this](boost::system::error_code ec, std::size_t length) { if (!ec) { if (serverVersionMethod[0] == 5 && serverVersionMethod[1] == 2) { doWriteLoginPassword(); } else { //Others not supported lowerSocket().close(); } } else { lowerSocket().close(); } }); } void doWriteLoginPassword() { std::string login = "telegram-proxy-user"; std::string password = "telegram-telegram-999112"; std::string data = ""; data += 0x01; data += static_cast(login.size()); data += login; data += static_cast(password.size()); data += password; boost::asio::async_write(socket, boost::asio::buffer(data.data(), data.size()), [this](boost::system::error_code ec, std::size_t length) { if (!ec) { doReadAuthStatus(); } else { lowerSocket().close(); } }); } void connectToWebAddress(std::string address) { ConnectRequestRecord record = { address, 80 }; connectRequestArr.push_back(record); auto data = record.getRequestData(); boost::asio::async_write(socket, boost::asio::buffer(data.data(), data.size()), [this](boost::system::error_code ec, std::size_t length) { if (!ec) { doReadConnectToWebAddressResponse(); } else { lowerSocket().close(); } }); } void doWriteSomething() { //std::array something = { 0x05, 0x01, 0x00, 0x03 }; std::string data = "GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n"; boost::asio::async_write(socket, boost::asio::buffer(data.data(), data.size()), [this](boost::system::error_code ec, std::size_t length) { if (!ec) { doReadSomethingText(); } else { lowerSocket().close(); } }); } void doReadAuthStatus() { boost::asio::async_read(socket, boost::asio::buffer(serverAuthStatus.data(), serverAuthStatus.size()), [this](boost::system::error_code ec, std::size_t length) { if (!ec) { if (serverAuthStatus[0] == 1 && serverAuthStatus[1] == 0) { //doWriteSomething(); /* if (onReady) { onReady(); }*/ //connectToWebAddress("www.google.com"); } else { //Authorization is not succeed lowerSocket().close(); } } else { lowerSocket().close(); } }); } void doReadConnectToWebAddressResponse() { std::shared_ptr> firstPartPtr = std::make_shared>(); boost::asio::async_read(socket, boost::asio::buffer(firstPartPtr->data(), firstPartPtr->size()), [this, firstPartPtr](boost::system::error_code ec, std::size_t length) { if (!ec) { doReadConnectToWebAddressResponsePart2(firstPartPtr); } else { lowerSocket().close(); } }); } void doReadConnectToWebAddressResponsePart2(std::shared_ptr> firstPartPtr) { unsigned int size = 3; if ((*firstPartPtr)[3] == AddressType::AT_IPV4) { size = 3; } if ((*firstPartPtr)[3] == AddressType::AT_HOST) { size = (*firstPartPtr)[4]; } if ((*firstPartPtr)[3] == AddressType::AT_IPV6) { size = 15; } size += 2; //add port std::shared_ptr> secondPartPtr = std::make_shared>(); secondPartPtr->resize(size); boost::asio::async_read(socket, boost::asio::buffer(secondPartPtr->data(), secondPartPtr->size()), [this, firstPartPtr, secondPartPtr](boost::system::error_code ec, std::size_t length) { if (!ec) { ConnectResponseRecord connectResponseRecord; connectResponseRecord.rawData.clear(); for (unsigned char c : *firstPartPtr) { connectResponseRecord.rawData.push_back(c); } for (unsigned char c : *secondPartPtr) { connectResponseRecord.rawData.push_back(c); } connectResponseRecord.connectResponseType = static_cast(connectResponseRecord.rawData[1]); connectResponseRecord.addressType = static_cast(connectResponseRecord.rawData[3]); size_t portOffset = 8; if (connectResponseRecord.addressType == AddressType::AT_IPV4) { connectResponseRecord.address = boost::lexical_cast(static_cast(connectResponseRecord.rawData[4])) + "." + boost::lexical_cast(static_cast(connectResponseRecord.rawData[5])) + "." + boost::lexical_cast(static_cast(connectResponseRecord.rawData[6])) + "." + boost::lexical_cast(static_cast(connectResponseRecord.rawData[7])); portOffset = 8; } if (connectResponseRecord.addressType == AddressType::AT_HOST) { size_t count = connectResponseRecord.rawData[4]; for (size_t i = 0; i < count; i++) { connectResponseRecord.address.push_back(connectResponseRecord.rawData[5 + i]); } portOffset = 5 + count; } if (connectResponseRecord.addressType == AddressType::AT_IPV6) { std::stringstream stream; stream << std::hex << connectResponseRecord.rawData[4]; stream << std::hex << connectResponseRecord.rawData[5]; stream << ":"; stream << std::hex << connectResponseRecord.rawData[6]; stream << std::hex << connectResponseRecord.rawData[7]; stream << ":"; stream << std::hex << connectResponseRecord.rawData[8]; stream << std::hex << connectResponseRecord.rawData[9]; stream << ":"; stream << std::hex << connectResponseRecord.rawData[10]; stream << std::hex << connectResponseRecord.rawData[11]; stream << ":"; stream << std::hex << connectResponseRecord.rawData[12]; stream << std::hex << connectResponseRecord.rawData[13]; stream << ":"; stream << std::hex << connectResponseRecord.rawData[14]; stream << std::hex << connectResponseRecord.rawData[15]; stream << ":"; stream << std::hex << connectResponseRecord.rawData[16]; stream << std::hex << connectResponseRecord.rawData[17]; stream << ":"; stream << std::hex << connectResponseRecord.rawData[18]; stream << std::hex << connectResponseRecord.rawData[19]; connectResponseRecord.address = stream.str(); portOffset = 20; } connectResponseRecord.port = connectResponseRecord.rawData[portOffset] * 256 + connectResponseRecord.rawData[portOffset + 1]; doWriteSomething(); } else { lowerSocket().close(); } }); } unsigned char item; std::string result; void doReadSomething() { boost::asio::async_read(socket, boost::asio::buffer(&item, 1), [this](boost::system::error_code ec, std::size_t length) { if (!ec) { result += boost::lexical_cast(static_cast(item)) + " "; std::cout << boost::lexical_cast(static_cast(item)) + " "; doReadSomething(); } else { lowerSocket().close(); } }); } void doReadSomethingText() { boost::asio::async_read(socket, boost::asio::buffer(&item, 1), [this](boost::system::error_code ec, std::size_t length) { if (!ec) { result += (item); std::cout << item; doReadSomethingText(); } else { lowerSocket().close(); } }); } private: boost::asio::io_service& ioService; boost::asio::ssl::stream socket; boost::asio::ip::tcp::resolver& resolver; }; class ProxyClientSession : public std::enable_shared_from_this { public: ProxyClientSession(boost::asio::ip::tcp::socket socket, boost::asio::io_service& ioServiceIn, boost::asio::ssl::context& context, boost::asio::ip::tcp::resolver& resolverIn) : socket(std::move(socket)) , resolver(resolverIn) , proxyClient(ioServiceIn, context, resolverIn) { std::cout << "ProxyClientSession create" << std::endl; } ~ProxyClientSession() { std::cout << "ProxyClientSession destroy" << std::endl; } void start() { auto self(shared_from_this()); proxyClient.onReady = [self]() {self->onProxyReady(); }; proxyClient.start(); } protected: unsigned char forwardChar; unsigned char backwardChar; void onProxyReady() { transferDataForward(); transferDataBackward(); } void transferDataForward() { auto self(shared_from_this()); boost::asio::async_read(socket, boost::asio::buffer(&forwardChar, 1), [this, self](boost::system::error_code ec, std::size_t /*length*/) { if (!ec) { std::cout << static_cast(forwardChar) << " "; boost::asio::async_write(proxyClient.getSocket(), boost::asio::buffer(&forwardChar, 1), [this, self](boost::system::error_code ec, std::size_t length) { if (!ec) { transferDataForward(); } else { std::cout << "transferDataForward write error" << std::endl; proxyClient.lowerSocket().close(); socket.close(); } }); } else { std::cout << "transferDataForward read error" << std::endl; proxyClient.lowerSocket().close(); socket.close(); } }); } void transferDataBackward() { auto self(shared_from_this()); boost::asio::async_read(proxyClient.getSocket(), boost::asio::buffer(&backwardChar, 1), [this, self](boost::system::error_code ec, std::size_t /*length*/) { if (!ec) { boost::asio::async_write(socket, boost::asio::buffer(&backwardChar, 1), [this, self](boost::system::error_code ec, std::size_t length) { if (!ec) { transferDataBackward(); } else { std::cout << "transferDataBackward write error" << std::endl; proxyClient.lowerSocket().close(); socket.close(); } }); } else { std::cout << "transferDataBackward read error" << std::endl; proxyClient.lowerSocket().close(); socket.close(); } }); } boost::asio::ip::tcp::socket socket; boost::asio::ip::tcp::resolver& resolver; ProxyClient proxyClient; }; class IntermediateServer { public: IntermediateServer(boost::asio::io_service& io_service, const boost::asio::ip::tcp::endpoint& endpoint) : ioService(io_service) , acceptor(io_service, endpoint) , socket(io_service) , resolver(io_service) //, endpointIterator(resolver.resolve({ "127.0.0.1", "8043" })) //resolver.resolve({ "telegram-proxy.fishrungames.com", "8043" }); , ctx(boost::asio::ssl::context::sslv23) { ctx.load_verify_file("rootca.crt"); doAccept(); } protected: void doAccept() { acceptor.async_accept(socket, [this](boost::system::error_code ec) { if (!ec) { std::make_shared(std::move(socket), ioService, ctx, resolver)->start(); } doAccept(); }); } boost::asio::io_service& ioService; boost::asio::ip::tcp::acceptor acceptor; boost::asio::ip::tcp::socket socket; boost::asio::ip::tcp::resolver resolver; //boost::asio::ip::tcp::resolver::iterator endpointIterator; boost::asio::ssl::context ctx; }; int main() { std::cout << "Hello" << std::endl; boost::asio::io_service ioService; //boost::asio::io_service::work work(ioService); //Create intermediate: boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 8042); IntermediateServer intermediateServer(ioService, endpoint); //ProxyClient c(ioService, ctx, endpointIterator); std::thread t([&ioService]() { ioService.run(); }); t.join(); return 0; }