16 #define BOOST_ASIO_NO_DEPRECATED
27 #include <boost/asio/connect.hpp>
28 #include <boost/asio/read.hpp>
29 #include <boost/asio/write.hpp>
36 #define DBG_NW LOG_STREAM(debug, log_network)
37 #define LOG_NW LOG_STREAM(info, log_network)
38 #define WRN_NW LOG_STREAM(warn, log_network)
39 #define ERR_NW LOG_STREAM(err, log_network)
43 #include <sys/types.h>
48 mptest_log(
const char* functionname)
50 WRN_NW <<
"Process:" << getpid() <<
" Thread:" << std::this_thread::get_id() <<
" Function: " << functionname <<
" Start";
54 #define MPTEST_LOG mptest_log mptest_log__(__func__)
56 #define MPTEST_LOG ((void)0)
59 using boost::system::error_code;
60 using boost::system::system_error;
62 using namespace std::chrono_literals;
68 , resolver_(io_context_)
69 , tls_context_(
boost::asio::ssl::context::sslv23)
76 , handshake_finished_()
78 , handshake_response_()
91 auto result =
resolver_.resolve(host, service, boost::asio::ip::resolver_query_base::numeric_host, ec);
104 }
catch(
const boost::system::system_error&) {
108 }
catch(
const std::future_error&) {
115 LOG_NW <<
"wesnothd_connection::io_service::run() returned";
118 LOG_NW <<
"Resolving hostname: " << host;
125 if(
auto socket = utils::get_if<tls_socket>(&
socket_)) {
128 (*socket)->async_shutdown([](
const error_code&) {} );
129 const char buffer[] =
"";
143 LOG_NW << __func__ <<
" Throwing: " << ec;
144 throw system_error(ec);
147 boost::asio::async_connect(*utils::get<raw_socket>(
socket_), results,
156 ERR_NW <<
"Tried all IPs. Giving up";
157 throw system_error(ec);
161 if(
endpoint.address().is_loopback()) {
177 static const uint32_t tls_handshake = htonl(uint32_t(1));
179 boost::asio::async_write(*utils::get<raw_socket>(
socket_), boost::asio::buffer(
use_tls_ ?
reinterpret_cast<const char*
>(&tls_handshake) :
reinterpret_cast<const char*
>(&
handshake), 4),
180 [](
const error_code& ec, std::size_t) {
if(ec) {
throw system_error(ec); } });
181 boost::asio::async_read(*utils::get<raw_socket>(
socket_), boost::asio::buffer(
reinterpret_cast<std::byte*
>(&
handshake_response_), 4),
187 return [verifier](
bool preverified, boost::asio::ssl::verify_context& ctx) {
188 char subject_name[256];
189 X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
190 X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
191 bool verified = verifier(preverified, ctx);
192 DBG_NW <<
"Verifying TLS certificate: " << subject_name <<
": " <<
193 (verified ?
"verified" :
"failed");
194 BIO* bio = BIO_new(BIO_s_mem());
196 X509_print(bio, cert);
197 while(BIO_read(bio, buffer, 1024) > 0)
211 if(ec == boost::asio::error::eof &&
use_tls_) {
216 LOG_NW << __func__ <<
" Throwing: " << ec;
217 throw system_error(ec);
233 auto& socket { *utils::get<tls_socket>(
socket_) };
235 socket.set_verify_mode(
236 boost::asio::ssl::verify_peer |
237 boost::asio::ssl::verify_fail_if_no_peer_cert
240 #if BOOST_VERSION >= 107300
241 socket.set_verify_callback(
verbose_verify(boost::asio::ssl::host_name_verification(
host_)));
246 socket.async_handshake(boost::asio::ssl::stream_base::client, [
this](
const error_code& ec) {
248 LOG_NW << __func__ <<
" Throwing: " << ec;
249 throw system_error(ec);
271 boost::asio::ip::tcp::endpoint
endpoint { utils::get<raw_socket>(
socket_)->remote_endpoint() };
272 utils::get<raw_socket>(
socket_)->close();
282 LOG_NW <<
"Waiting for handshake";
290 future.wait_for(10ms) == std::future_status::timeout
297 switch(future.wait_for(0ms)) {
298 case std::future_status::ready:
303 case std::future_status::timeout:
304 throw error(boost::asio::error::make_error_code(boost::asio::error::timed_out));
308 }
catch(
const boost::system::system_error&
err) {
309 if(
err.code() == boost::asio::error::operation_aborted ||
err.code() == boost::asio::error::eof) {
313 WRN_NW << __func__ <<
" Rethrowing: " <<
err.code();
315 }
catch(
const std::future_error&
e) {
316 if(
e.code() == std::future_errc::future_already_retrieved) {
327 auto buf_ptr = std::make_unique<boost::asio::streambuf>();
329 std::ostream os(buf_ptr.get());
332 boost::asio::post(
io_context_, [
this, buf_ptr = std::move(buf_ptr)]()
mutable {
334 DBG_NW <<
"In wesnothd_connection::send_data::lambda";
347 utils::visit([](
auto&& socket) {
348 if(socket->lowest_layer().is_open()) {
349 boost::system::error_code ec;
354 #pragma warning(push)
355 #pragma warning(disable:4996)
357 socket->lowest_layer().cancel(ec);
363 WRN_NW <<
"Failed to cancel network operations: " << ec.message();
387 LOG_NW << __func__ <<
" Error: " << ec;
401 DBG_NW <<
"Written " << bytes_transferred <<
" bytes.";
411 LOG_NW << __func__ <<
" Error: " << ec;
433 LOG_NW << __func__ <<
" Error: " << ec;
441 if(bytes_transferred < 4) {
449 is.read(
reinterpret_cast<char*
>(&data_size), 4);
465 DBG_NW <<
"Read " << bytes_transferred <<
" bytes.";
474 LOG_NW << __func__ <<
" Error: " << ec;
500 std::size_t buf_size = buf.size();
505 std::deque<boost::asio::const_buffer> bufs {
506 boost::asio::buffer(
reinterpret_cast<const char*
>(&
payload_size_), 4),
510 utils::visit([
this, &bufs](
auto&& socket) {
511 boost::asio::async_write(*socket, bufs,
522 utils::visit([
this](
auto&& socket) {
523 boost::asio::async_read(*socket,
read_buf_,
546 std::string user_msg;
549 user_msg =
_(
"Disconnected from server.");
564 lock, 10ms, [
this]() { return has_data_received(); }))
575 boost::asio::socket_base::keep_alive option(
true);
576 utils::get<raw_socket>(
socket_)->set_option(option);
580 int cnt = std::max((seconds - 10) / 10, 1);
582 setsockopt(utils::get<raw_socket>(
socket_)->native_handle(), SOL_TCP, TCP_KEEPIDLE, &timeout,
sizeof(timeout));
583 setsockopt(utils::get<raw_socket>(
socket_)->native_handle(), SOL_TCP, TCP_KEEPCNT, &cnt,
sizeof(cnt));
584 setsockopt(utils::get<raw_socket>(
socket_)->native_handle(), SOL_TCP, TCP_KEEPINTVL, &interval,
sizeof(interval));
585 #elif defined(__APPLE__) && defined(__MACH__)
586 setsockopt(utils::get<raw_socket>(
socket_)->native_handle(), IPPROTO_TCP, TCP_KEEPALIVE, &seconds,
sizeof(seconds));
587 #elif defined(_WIN32)
589 DWORD timeout_ms = seconds * 1000;
590 setsockopt(utils::get<raw_socket>(
socket_)->native_handle(), SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const char*
>(&timeout_ms),
sizeof(timeout_ms));
591 setsockopt(utils::get<raw_socket>(
socket_)->native_handle(), SOL_SOCKET, SO_SNDTIMEO,
reinterpret_cast<const char*
>(&timeout_ms),
sizeof(timeout_ms));
A config object defines a single node in a WML file, with access to child nodes.
static void spin()
Indicate to the player that loading is progressing.
boost::asio::ssl::context tls_context_
wesnothd_connection_error error
void handle_handshake(const boost::system::error_code &ec)
const boost::asio::ip::tcp::endpoint & endpoint
std::size_t bytes_to_read_
void handle_connect(const boost::system::error_code &ec, endpoint endpoint)
void handle_resolve(const boost::system::error_code &ec, results_type results)
data_queue< std::unique_ptr< boost::asio::streambuf > > send_queue_
std::condition_variable recv_queue_lock_
std::size_t is_write_complete(const boost::system::error_code &error, std::size_t bytes_transferred)
uint32_t handshake_response_
wesnothd_connection(const wesnothd_connection &)=delete
data_queue< config > recv_queue_
void wait_for_handshake()
Waits until the server handshake is complete.
boost::asio::io_context io_context_
std::mutex last_error_mutex_
bool receive_data(config &result)
Receives the next pending data pack from the server, if available.
void handle_write(const boost::system::error_code &ec, std::size_t bytes_transferred)
void send_data(const configr_of &request)
Queues the given data to be sent to the server.
void set_keepalive(int seconds)
std::mutex recv_queue_mutex_
resolver::results_type results_type
boost::asio::streambuf read_buf_
bool wait_and_receive_data(config &data)
Unlike receive_data, waits until data is available instead of returning immediately.
void handle_read(const boost::system::error_code &ec, std::size_t bytes_transferred)
void fallback_to_unencrypted()
std::unique_ptr< boost::asio::ip::tcp::socket > raw_socket
boost::system::error_code last_error_
std::unique_ptr< boost::asio::ssl::stream< raw_socket::element_type > > tls_socket
std::thread worker_thread_
std::promise< void > handshake_finished_
std::size_t bytes_to_write_
std::size_t bytes_written_
std::size_t is_read_complete(const boost::system::error_code &error, std::size_t bytes_transferred)
static std::string _(const char *str)
Standard logging facilities (interface).
void load_tls_root_certs(boost::asio::ssl::context &ctx)
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
void write_gz(std::ostream &out, const configr_of &cfg)
void write(std::ostream &out, const configr_of &cfg, unsigned int level)
void read_gz(config &cfg, std::istream &file, abstract_validator *validator)
Might throw a std::ios_base::failure especially a gzip_error.
static map_location::DIRECTION s
auto verbose_verify(Verifier &&verifier)
static lg::log_domain log_network("network")