2017-08-05 21:47:23 +00:00
|
|
|
#include <iostream>
|
|
|
|
#include <boost/asio.hpp>
|
|
|
|
#include <boost/lexical_cast.hpp>
|
|
|
|
#include <thread>
|
|
|
|
#include <array>
|
|
|
|
#include <iomanip>
|
|
|
|
|
2017-08-06 19:55:08 +00:00
|
|
|
|
|
|
|
#include <boost/bind.hpp>
|
|
|
|
|
|
|
|
#define SSL_R_SHORT_READ 219
|
|
|
|
#include "ssl/ssl_locl.h"
|
|
|
|
#include <boost/asio/ssl.hpp>
|
|
|
|
|
|
|
|
#if defined(close)
|
|
|
|
#undef close
|
|
|
|
#endif
|
|
|
|
|
2017-08-05 21:47:23 +00:00
|
|
|
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:
|
2017-08-06 19:55:08 +00:00
|
|
|
proxyClient(boost::asio::io_service& ioServiceIn, boost::asio::ssl::context& context, boost::asio::ip::tcp::resolver::iterator endpointIn)
|
2017-08-05 21:47:23 +00:00
|
|
|
: ioService(ioServiceIn)
|
2017-08-06 19:55:08 +00:00
|
|
|
, socket(ioServiceIn, context)
|
2017-08-05 21:47:23 +00:00
|
|
|
{
|
2017-08-06 19:55:08 +00:00
|
|
|
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));
|
|
|
|
|
|
|
|
|
|
|
|
doConnect(endpointIn);
|
2017-08-05 21:47:23 +00:00
|
|
|
}
|
2017-08-06 19:55:08 +00:00
|
|
|
|
2017-08-05 21:47:23 +00:00
|
|
|
void close()
|
|
|
|
{
|
2017-08-06 19:55:08 +00:00
|
|
|
ioService.post([this]() { lowerSocket().close(); });
|
2017-08-05 21:47:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2017-08-06 19:55:08 +00:00
|
|
|
|
|
|
|
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>::lowest_layer_type& lowerSocket()
|
|
|
|
{
|
|
|
|
return socket.lowest_layer();
|
|
|
|
}
|
|
|
|
|
|
|
|
void doConnect(boost::asio::ip::tcp::resolver::iterator endpointIterator)
|
2017-08-05 21:47:23 +00:00
|
|
|
{
|
2017-08-06 19:55:08 +00:00
|
|
|
boost::asio::async_connect(lowerSocket(), endpointIterator,
|
2017-08-05 21:47:23 +00:00
|
|
|
[this](boost::system::error_code ec, boost::asio::ip::tcp::resolver::iterator)
|
|
|
|
{
|
|
|
|
if (!ec)
|
2017-08-06 19:55:08 +00:00
|
|
|
{
|
|
|
|
//sendVersion();
|
|
|
|
|
|
|
|
doHandshake();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void doHandshake()
|
|
|
|
{
|
|
|
|
socket.async_handshake(boost::asio::ssl::stream_base::client,
|
|
|
|
[this](const boost::system::error_code& error) {
|
|
|
|
|
|
|
|
|
|
|
|
if (!error)
|
2017-08-05 21:47:23 +00:00
|
|
|
{
|
|
|
|
sendVersion();
|
|
|
|
}
|
2017-08-06 19:55:08 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
std::cout << "Handshake failed: " << error.message() << "\n";
|
|
|
|
lowerSocket().close();
|
|
|
|
}
|
|
|
|
|
2017-08-05 21:47:23 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-08-06 19:55:08 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-08-05 21:47:23 +00:00
|
|
|
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
|
|
|
|
{
|
2017-08-06 19:55:08 +00:00
|
|
|
lowerSocket().close();
|
2017-08-05 21:47:23 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2017-08-06 19:55:08 +00:00
|
|
|
lowerSocket().close();
|
2017-08-05 21:47:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-08-06 19:55:08 +00:00
|
|
|
lowerSocket().close();
|
2017-08-05 21:47:23 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
{
|
2017-08-06 19:55:08 +00:00
|
|
|
lowerSocket().close();
|
2017-08-05 21:47:23 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
{
|
2017-08-06 19:55:08 +00:00
|
|
|
lowerSocket().close();
|
2017-08-05 21:47:23 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
{
|
2017-08-06 19:55:08 +00:00
|
|
|
lowerSocket().close();
|
2017-08-05 21:47:23 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
connectToWebAddress("www.google.com");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Authorization is not succeed
|
2017-08-06 19:55:08 +00:00
|
|
|
lowerSocket().close();
|
2017-08-05 21:47:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-08-06 19:55:08 +00:00
|
|
|
lowerSocket().close();
|
2017-08-05 21:47:23 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
{
|
2017-08-06 19:55:08 +00:00
|
|
|
lowerSocket().close();
|
2017-08-05 21:47:23 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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]);
|
2017-08-06 19:55:08 +00:00
|
|
|
connectResponseRecord.addressType = static_cast<AddressType>(connectResponseRecord.rawData[3]);
|
2017-08-05 21:47:23 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
{
|
2017-08-06 19:55:08 +00:00
|
|
|
lowerSocket().close();
|
2017-08-05 21:47:23 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
{
|
2017-08-06 19:55:08 +00:00
|
|
|
lowerSocket().close();
|
2017-08-05 21:47:23 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
{
|
2017-08-06 19:55:08 +00:00
|
|
|
lowerSocket().close();
|
2017-08-05 21:47:23 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
boost::asio::io_service& ioService;
|
2017-08-06 19:55:08 +00:00
|
|
|
|
|
|
|
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket;
|
2017-08-05 21:47:23 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-08-06 19:55:08 +00:00
|
|
|
|
2017-08-05 21:47:23 +00:00
|
|
|
int main()
|
|
|
|
{
|
|
|
|
std::cout << "Hello" << std::endl;
|
2017-08-06 19:55:08 +00:00
|
|
|
|
2017-08-05 21:47:23 +00:00
|
|
|
boost::asio::io_service ioService;
|
2017-08-06 19:55:08 +00:00
|
|
|
//boost::asio::io_service::work work(ioService);
|
2017-08-05 21:47:23 +00:00
|
|
|
|
|
|
|
boost::asio::ip::tcp::resolver resolver(ioService);
|
2017-08-06 19:55:08 +00:00
|
|
|
//auto endpointIterator = resolver.resolve({ "telegram-proxy.fishrungames.com", "8043" });
|
|
|
|
auto endpointIterator = resolver.resolve({ "127.0.0.1", "8043" });
|
|
|
|
|
|
|
|
|
|
|
|
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
|
|
|
|
ctx.load_verify_file("rootca.crt");
|
2017-08-05 21:47:23 +00:00
|
|
|
|
|
|
|
|
2017-08-06 19:55:08 +00:00
|
|
|
proxyClient c(ioService, ctx, endpointIterator);
|
2017-08-05 21:47:23 +00:00
|
|
|
|
|
|
|
std::thread t([&ioService]() { ioService.run(); });
|
|
|
|
|
|
|
|
|
|
|
|
t.join();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|