proxyTest/main.cpp

780 lines
18 KiB
C++
Executable File

#include <iostream>
#include <boost/asio.hpp>
#include <boost/lexical_cast.hpp>
#include <thread>
#include <array>
#include <iomanip>
#include <boost/bind.hpp>
#include <boost/thread/thread.hpp>
#define SSL_R_SHORT_READ 219
#include "ssl/ssl_locl.h"
#include <boost/asio/ssl.hpp>
#if defined(close)
#undef close
#endif
enum AddressType
{
AT_IPV4 = 0,
AT_HOST = 3,
AT_IPV6 = 4
};
class ConnectResponseRecord
{
public:
std::vector<unsigned char> 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<unsigned char>(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<void()> onReady;
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>& getSocket()
{
return socket;
}
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>::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::ip::tcp::resolver::iterator endpointIterator(resolver.resolve({ "https-proxy.fishrungames.com", "8043" }));
boost::asio::ip::tcp::resolver::iterator endpointIterator(resolver.resolve({ "52.89.37.158", "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<char, 3> 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<unsigned char, 2> serverVersionMethod;
std::array<unsigned char, 2> serverAuthStatus;
std::vector<ConnectRequestRecord> 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<unsigned char>(login.size());
data += login;
data += static_cast<unsigned char>(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<char, 100> 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<std::array<unsigned char, 5>> firstPartPtr = std::make_shared<std::array<unsigned char, 5>>();
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<std::array<unsigned char, 5>> 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<std::vector<char>> secondPartPtr = std::make_shared<std::vector<char>>();
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::ConnectResponseType>(connectResponseRecord.rawData[1]);
connectResponseRecord.addressType = static_cast<AddressType>(connectResponseRecord.rawData[3]);
size_t portOffset = 8;
if (connectResponseRecord.addressType == AddressType::AT_IPV4)
{
connectResponseRecord.address = boost::lexical_cast<std::string>(static_cast<unsigned int>(connectResponseRecord.rawData[4]))
+ "." + boost::lexical_cast<std::string>(static_cast<unsigned int>(connectResponseRecord.rawData[5]))
+ "." + boost::lexical_cast<std::string>(static_cast<unsigned int>(connectResponseRecord.rawData[6]))
+ "." + boost::lexical_cast<std::string>(static_cast<unsigned int>(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<std::string>(static_cast<unsigned int>(item)) + " ";
std::cout << boost::lexical_cast<std::string>(static_cast<unsigned int>(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<boost::asio::ip::tcp::socket> socket;
boost::asio::ip::tcp::resolver& resolver;
};
class ProxyClientSession : public std::enable_shared_from_this<ProxyClientSession> {
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;
std::array<unsigned char, 8192> forwardBuffer;
std::array<unsigned char, 8192> backwardBuffer;
void onProxyReady()
{
transferDataForward();
transferDataBackward();
proxyClient.onReady = nullptr;
}
void transferDataForward()
{
auto self(shared_from_this());
socket.async_read_some(boost::asio::buffer(forwardBuffer),
[this, self](boost::system::error_code ec, std::size_t length)
{
if (!ec)
{
std::shared_ptr<std::vector<unsigned char>> data = std::make_shared<std::vector<unsigned char>>();
data->resize(length);
std::copy(&forwardBuffer[0], &forwardBuffer[0] + length, &(*data)[0]);
boost::asio::async_write(proxyClient.getSocket(),
boost::asio::buffer(*data),
[this, self, data](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 if (ec == boost::asio::error::eof)
{
std::cout << "transferDataForward read end of file" << std::endl;
if (length > 0)
{
std::cout << "but length is positive" << std::endl;
}
return;
}
else
{
std::cout << "transferDataForward read error" << std::endl;
proxyClient.lowerSocket().close();
socket.close();
}
});
}
void transferDataBackward()
{
auto self(shared_from_this());
proxyClient.getSocket().async_read_some(boost::asio::buffer(backwardBuffer),
[this, self](boost::system::error_code ec, std::size_t length)
{
if (!ec)
{
std::shared_ptr<std::vector<unsigned char>> data = std::make_shared<std::vector<unsigned char>>();
data->resize(length);
std::copy(&backwardBuffer[0], &backwardBuffer[0] + length, &(*data)[0]);
boost::asio::async_write(socket,
boost::asio::buffer(*data),
[this, self, data](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 if (ec == boost::asio::error::eof)
{
std::cout << "transferDataBackward read end of file" << std::endl;
if (length > 0)
{
std::cout << "but length is positive" << std::endl;
}
return;
}
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)
, 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<ProxyClientSession>(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();
boost::thread_group threadpool;
/*
threadpool.create_thread(
boost::bind(&boost::asio::io_service::run, &ioService)
);
threadpool.create_thread(
boost::bind(&boost::asio::io_service::run, &ioService)
);
threadpool.create_thread(
boost::bind(&boost::asio::io_service::run, &ioService)
);*/
threadpool.create_thread(
boost::bind(&boost::asio::io_service::run, &ioService)
);
threadpool.join_all();
return 0;
}