16 #define BOOST_ASIO_NO_DEPRECATED
27 #include <boost/asio/connect.hpp>
28 #include <boost/asio/read.hpp>
35 #define DBG_NW LOG_STREAM(debug, log_network)
36 #define LOG_NW LOG_STREAM(info, log_network)
37 #define WRN_NW LOG_STREAM(warn, log_network)
38 #define ERR_NW LOG_STREAM(err, log_network)
42 #include <sys/types.h>
47 mptest_log(
const char* functionname)
49 WRN_NW <<
"Process:" << getpid() <<
" Thread:" << std::this_thread::get_id() <<
" Function: " << functionname <<
" Start";
53 #define MPTEST_LOG mptest_log mptest_log__(__func__)
55 #define MPTEST_LOG ((void)0)
58 using boost::system::error_code;
59 using boost::system::system_error;
61 using namespace std::chrono_literals;
67 , resolver_(io_context_)
68 , tls_context_(boost::asio::ssl::context::sslv23)
75 , handshake_finished_()
77 , handshake_response_()
90 auto result =
resolver_.resolve(host, service, boost::asio::ip::resolver_query_base::numeric_host, ec);
103 }
catch(
const boost::system::system_error&) {
107 }
catch(
const std::future_error&) {
114 LOG_NW <<
"wesnothd_connection::io_service::run() returned";
117 LOG_NW <<
"Resolving hostname: " << host;
124 if(
auto socket = utils::get_if<tls_socket>(&
socket_)) {
127 (*socket)->async_shutdown([](
const error_code&) {} );
128 const char buffer[] =
"";
142 LOG_NW << __func__ <<
" Throwing: " << ec;
143 throw system_error(ec);
146 boost::asio::async_connect(*utils::get<raw_socket>(
socket_), results,
155 ERR_NW <<
"Tried all IPs. Giving up";
156 throw system_error(ec);
160 if(
endpoint.address().is_loopback()) {
176 static const uint32_t tls_handshake = htonl(uint32_t(1));
178 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),
179 [](
const error_code& ec, std::size_t) {
if(ec) {
throw system_error(ec); } });
180 boost::asio::async_read(*utils::get<raw_socket>(
socket_), boost::asio::buffer(
reinterpret_cast<std::byte*
>(&
handshake_response_), 4),
186 return [verifier](
bool preverified, boost::asio::ssl::verify_context& ctx) {
187 char subject_name[256];
188 X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
189 X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
190 bool verified = verifier(preverified, ctx);
191 DBG_NW <<
"Verifying TLS certificate: " << subject_name <<
": " <<
192 (verified ?
"verified" :
"failed");
193 BIO* bio = BIO_new(BIO_s_mem());
195 X509_print(bio, cert);
196 while(BIO_read(bio, buffer, 1024) > 0)
210 if(ec == boost::asio::error::eof &&
use_tls_) {
215 LOG_NW << __func__ <<
" Throwing: " << ec;
216 throw system_error(ec);
232 auto& socket { *utils::get<tls_socket>(
socket_) };
234 socket.set_verify_mode(
235 boost::asio::ssl::verify_peer |
236 boost::asio::ssl::verify_fail_if_no_peer_cert
239 #if BOOST_VERSION >= 107300
240 socket.set_verify_callback(
verbose_verify(boost::asio::ssl::host_name_verification(
host_)));
245 socket.async_handshake(boost::asio::ssl::stream_base::client, [
this](
const error_code& ec) {
247 LOG_NW << __func__ <<
" Throwing: " << ec;
248 throw system_error(ec);
270 boost::asio::ip::tcp::endpoint
endpoint { utils::get<raw_socket>(
socket_)->remote_endpoint() };
271 utils::get<raw_socket>(
socket_)->close();
281 LOG_NW <<
"Waiting for handshake";
289 future.wait_for(10ms) == std::future_status::timeout
296 switch(future.wait_for(0ms)) {
297 case std::future_status::ready:
302 case std::future_status::timeout:
303 throw error(boost::asio::error::make_error_code(boost::asio::error::timed_out));
307 }
catch(
const boost::system::system_error&
err) {
308 if(
err.code() == boost::asio::error::operation_aborted ||
err.code() == boost::asio::error::eof) {
312 WRN_NW << __func__ <<
" Rethrowing: " <<
err.code();
314 }
catch(
const std::future_error&
e) {
315 if(
e.code() == std::future_errc::future_already_retrieved) {
326 auto buf_ptr = std::make_unique<boost::asio::streambuf>();
328 std::ostream os(buf_ptr.get());
331 boost::asio::post(
io_context_, [
this, buf_ptr = std::move(buf_ptr)]()
mutable {
333 DBG_NW <<
"In wesnothd_connection::send_data::lambda";
346 utils::visit([](
auto&& socket) {
347 if(socket->lowest_layer().is_open()) {
348 boost::system::error_code ec;
353 #pragma warning(push)
354 #pragma warning(disable:4996)
356 socket->lowest_layer().cancel(ec);
362 WRN_NW <<
"Failed to cancel network operations: " << ec.message();
386 LOG_NW << __func__ <<
" Error: " << ec;
400 DBG_NW <<
"Written " << bytes_transferred <<
" bytes.";
410 LOG_NW << __func__ <<
" Error: " << ec;
432 LOG_NW << __func__ <<
" Error: " << ec;
440 if(bytes_transferred < 4) {
448 is.read(
reinterpret_cast<char*
>(&data_size), 4);
464 DBG_NW <<
"Read " << bytes_transferred <<
" bytes.";
473 LOG_NW << __func__ <<
" Error: " << ec;
499 std::size_t buf_size = buf.size();
504 std::deque<boost::asio::const_buffer> bufs {
505 boost::asio::buffer(
reinterpret_cast<const char*
>(&
payload_size_), 4),
509 utils::visit([
this, &bufs](
auto&& socket) {
510 boost::asio::async_write(*socket, bufs,
521 utils::visit([
this](
auto&& socket) {
522 boost::asio::async_read(*socket,
read_buf_,
545 std::string user_msg;
548 user_msg =
_(
"Disconnected from server.");
563 lock, 10ms, [
this]() { return has_data_received(); }))
574 boost::asio::socket_base::keep_alive option(
true);
575 utils::get<raw_socket>(
socket_)->set_option(option);
579 int cnt = std::max((seconds - 10) / 10, 1);
581 setsockopt(utils::get<raw_socket>(
socket_)->native_handle(), SOL_TCP, TCP_KEEPIDLE, &timeout,
sizeof(timeout));
582 setsockopt(utils::get<raw_socket>(
socket_)->native_handle(), SOL_TCP, TCP_KEEPCNT, &cnt,
sizeof(cnt));
583 setsockopt(utils::get<raw_socket>(
socket_)->native_handle(), SOL_TCP, TCP_KEEPINTVL, &interval,
sizeof(interval));
584 #elif defined(__APPLE__) && defined(__MACH__)
585 setsockopt(utils::get<raw_socket>(
socket_)->native_handle(), IPPROTO_TCP, TCP_KEEPALIVE, &seconds,
sizeof(seconds));
586 #elif defined(_WIN32)
588 DWORD timeout_ms = seconds * 1000;
589 setsockopt(utils::get<raw_socket>(
socket_)->native_handle(), SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const char*
>(&timeout_ms),
sizeof(timeout_ms));
590 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")