The Battle for Wesnoth  1.17.0-dev
server_base.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2016 - 2021
3  by Sergey Popov <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
17 
18 #include "config.hpp"
19 #include "hash.hpp"
20 #include "log.hpp"
21 #include "serialization/parser.hpp"
22 #include "serialization/base64.hpp"
23 #include "filesystem.hpp"
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 
29 #ifdef HAVE_SENDFILE
30 #include <sys/sendfile.h>
31 #endif
32 
33 #ifdef _WIN32
34 #include <windows.h>
35 #include <boost/scope_exit.hpp>
36 #endif
37 
38 #include <boost/asio/ip/v6_only.hpp>
39 #include <boost/asio/read.hpp>
40 #ifndef _WIN32
41 #include <boost/asio/read_until.hpp>
42 #endif
43 #include <boost/asio/write.hpp>
44 
45 #include <array>
46 #include <ctime>
47 #include <functional>
48 #include <queue>
49 #include <sstream>
50 #include <string>
51 
52 
53 static lg::log_domain log_server("server");
54 #define ERR_SERVER LOG_STREAM(err, log_server)
55 #define WRN_SERVER LOG_STREAM(warn, log_server)
56 #define LOG_SERVER LOG_STREAM(info, log_server)
57 #define DBG_SERVER LOG_STREAM(debug, log_server)
58 
59 static lg::log_domain log_config("config");
60 #define ERR_CONFIG LOG_STREAM(err, log_config)
61 #define WRN_CONFIG LOG_STREAM(warn, log_config)
62 
63 bool dump_wml = false;
64 
65 server_base::server_base(unsigned short port, bool keep_alive)
66  : port_(port)
67  , keep_alive_(keep_alive)
68  , io_service_()
69  , acceptor_v6_(io_service_)
70  , acceptor_v4_(io_service_)
71  , handshake_response_()
72 #ifndef _WIN32
73  , input_(io_service_)
74  , sighup_(io_service_, SIGHUP)
75 #endif
76  , sigs_(io_service_, SIGINT, SIGTERM)
77 {
78 }
79 
81 {
82  boost::asio::ip::tcp::endpoint endpoint_v6(boost::asio::ip::tcp::v6(), port_);
83  boost::asio::spawn(io_service_, [this, endpoint_v6](boost::asio::yield_context yield) { serve(yield, acceptor_v6_, endpoint_v6); });
84 
85  boost::asio::ip::tcp::endpoint endpoint_v4(boost::asio::ip::tcp::v4(), port_);
86  boost::asio::spawn(io_service_, [this, endpoint_v4](boost::asio::yield_context yield) { serve(yield, acceptor_v4_, endpoint_v4); });
87 
88  handshake_response_ = htonl(42);
89 
90 #ifndef _WIN32
91  sighup_.async_wait(
92  [=](const boost::system::error_code& error, int sig)
93  { this->handle_sighup(error, sig); });
94 #endif
95  sigs_.async_wait(std::bind(&server_base::handle_termination, this, std::placeholders::_1, std::placeholders::_2));
96 }
97 
98 void server_base::serve(boost::asio::yield_context yield, boost::asio::ip::tcp::acceptor& acceptor, boost::asio::ip::tcp::endpoint endpoint)
99 {
100  if(!acceptor.is_open()) {
101  acceptor.open(endpoint.protocol());
102  acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
103  acceptor.set_option(boost::asio::ip::tcp::acceptor::keep_alive(keep_alive_));
104  if(endpoint.protocol() == boost::asio::ip::tcp::v6())
105  acceptor.set_option(boost::asio::ip::v6_only(true));
106  acceptor.bind(endpoint);
107  acceptor.listen();
108  }
109 
110  socket_ptr socket = std::make_shared<socket_ptr::element_type>(io_service_);
111 
112  boost::system::error_code error;
113  acceptor.async_accept(socket->lowest_layer(), yield[error]);
114  if(error) {
115  ERR_SERVER << "Accept failed: " << error.message() << "\n";
116  return;
117  }
118 
119  if(accepting_connections()) {
120  boost::asio::spawn(io_service_, [this, &acceptor, endpoint](boost::asio::yield_context yield) { serve(yield, acceptor, endpoint); });
121  }
122 
123 #ifndef _WIN32
124  if(keep_alive_) {
125  int timeout = 30;
126 #ifdef __linux__
127  int cnt = 10;
128  int interval = 30;
129  setsockopt(socket->native_handle(), SOL_TCP, TCP_KEEPIDLE, &timeout, sizeof(timeout));
130  setsockopt(socket->native_handle(), SOL_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt));
131  setsockopt(socket->native_handle(), SOL_TCP, TCP_KEEPINTVL, &interval, sizeof(interval));
132 #endif
133 #if defined(__APPLE__) && defined(__MACH__)
134  setsockopt(socket->native_handle(), IPPROTO_TCP, TCP_KEEPALIVE, &timeout, sizeof(timeout));
135 #endif
136  }
137 #endif
138 
139  DBG_SERVER << client_address(socket) << "\tnew connection tentatively accepted\n";
140 
141  uint32_t protocol_version;
142  uint32_t handshake_response;
143 
144  any_socket_ptr final_socket;
145 
146  async_read(*socket, boost::asio::buffer(reinterpret_cast<std::byte*>(&protocol_version), 4), yield[error]);
147  if(check_error(error, socket))
148  return;
149 
150  switch(ntohl(protocol_version)) {
151  case 0:
152  async_write(*socket, boost::asio::buffer(reinterpret_cast<std::byte*>(&handshake_response_), 4), yield[error]);
153  if(check_error(error, socket)) return;
154  final_socket = socket;
155  break;
156  case 1:
157  if(!tls_enabled_) {
158  ERR_SERVER << client_address(socket) << "\tTLS requested by client but not enabled on server\n";
159  handshake_response = 0xFFFFFFFFU;
160  } else {
161  handshake_response = 0x00000000;
162  }
163 
164  async_write(*socket, boost::asio::buffer(reinterpret_cast<const std::byte*>(&handshake_response), 4), yield[error]);
165  if(check_error(error, socket)) return;
166  if(!tls_enabled_) { // continue with unencrypted connection if TLS disabled
167  final_socket = socket;
168  break;
169  }
170 
171  final_socket = tls_socket_ptr { new tls_socket_ptr::element_type(std::move(*socket), tls_context_) };
172  utils::get<tls_socket_ptr>(final_socket)->async_handshake(boost::asio::ssl::stream_base::server, yield[error]);
173  if(error) {
174  ERR_SERVER << "TLS handshake failed: " << error.message() << "\n";
175  return;
176  }
177 
178  break;
179  default:
180  ERR_SERVER << client_address(socket) << "\tincorrect handshake\n";
181  return;
182  }
183 
184  utils::visit([this](auto&& socket) {
185  const std::string ip = client_address(socket);
186 
187  const std::string reason = is_ip_banned(ip);
188  if (!reason.empty()) {
189  LOG_SERVER << ip << "\trejected banned user. Reason: " << reason << "\n";
190  async_send_error(socket, "You are banned. Reason: " + reason);
191  return;
192  } else if (ip_exceeds_connection_limit(ip)) {
193  LOG_SERVER << ip << "\trejected ip due to excessive connections\n";
194  async_send_error(socket, "Too many connections from your IP.");
195  return;
196  } else {
197  if constexpr (utils::decayed_is_same<tls_socket_ptr, decltype(socket)>) {
198  DBG_SERVER << ip << "\tnew encrypted connection fully accepted\n";
199  } else {
200  DBG_SERVER << ip << "\tnew connection fully accepted\n";
201  }
202  this->handle_new_client(socket);
203  }
204  }, final_socket);
205 }
206 
207 #ifndef _WIN32
209  async_read_until(input_,
210  admin_cmd_, '\n',
211  [=](const boost::system::error_code& error, std::size_t bytes_transferred)
212  { this->handle_read_from_fifo(error, bytes_transferred); }
213  );
214 }
215 #endif
216 
217 void server_base::handle_termination(const boost::system::error_code& error, int signal_number)
218 {
219  assert(!error);
220 
221  std::string signame;
222  if(signal_number == SIGINT) signame = "SIGINT";
223  else if(signal_number == SIGTERM) signame = "SIGTERM";
224  else signame = std::to_string(signal_number);
225  LOG_SERVER << signame << " caught, exiting without cleanup immediately.\n";
226  exit(128 + signal_number);
227 }
228 
230  try {
231  io_service_.run();
232  LOG_SERVER << "Server has shut down because event loop is out of work\n";
233  } catch(const server_shutdown& e) {
234  LOG_SERVER << "Server has been shut down: " << e.what() << "\n";
235  }
236 }
237 
238 template<class SocketPtr> std::string client_address(SocketPtr socket)
239 {
240  boost::system::error_code error;
241  std::string result = socket->lowest_layer().remote_endpoint(error).address().to_string();
242  if(error)
243  return "<unknown address>";
244  else
245  return result;
246 }
247 
248 template<class SocketPtr> bool check_error(const boost::system::error_code& error, SocketPtr socket)
249 {
250  if(error) {
251  if(error == boost::asio::error::eof)
252  LOG_SERVER << log_address(socket) << "\tconnection closed\n";
253  else
254  ERR_SERVER << log_address(socket) << "\t" << error.message() << "\n";
255  return true;
256  }
257  return false;
258 }
259 template bool check_error<tls_socket_ptr>(const boost::system::error_code& error, tls_socket_ptr socket);
260 
261 namespace {
262 
263 void info_table_into_simple_wml(simple_wml::document& doc, const std::string& parent_name, const server_base::info_table& info)
264 {
265  if(info.empty()) {
266  return;
267  }
268 
269  auto& node = doc.child(parent_name.c_str())->add_child("data");
270  for(const auto& kv : info) {
271  node.set_attr_dup(kv.first.c_str(), kv.second.c_str());
272  }
273 }
274 
275 }
276 
277 /**
278  * Send a WML document from within a coroutine
279  * @param socket
280  * @param doc
281  * @param yield The function will suspend on write operation using this yield context
282  */
283 template<class SocketPtr> void server_base::coro_send_doc(SocketPtr socket, simple_wml::document& doc, boost::asio::yield_context yield)
284 {
285  if(dump_wml) {
286  std::cout << "Sending WML to " << log_address(socket) << ": \n" << doc.output() << std::endl;
287  }
288 
289  try {
291 
292  union DataSize
293  {
294  uint32_t size;
295  char buf[4];
296  } data_size {};
297  data_size.size = htonl(s.size());
298 
299  std::vector<boost::asio::const_buffer> buffers {
300  { data_size.buf, 4 },
301  { s.begin(), std::size_t(s.size()) }
302  };
303 
304  async_write(*socket, buffers, yield);
305  } catch (simple_wml::error& e) {
306  WRN_CONFIG << __func__ << ": simple_wml error: " << e.message << std::endl;
307  throw;
308  }
309 }
310 template void server_base::coro_send_doc<socket_ptr>(socket_ptr socket, simple_wml::document& doc, boost::asio::yield_context yield);
311 template void server_base::coro_send_doc<tls_socket_ptr>(tls_socket_ptr socket, simple_wml::document& doc, boost::asio::yield_context yield);
312 
313 template<class SocketPtr> void coro_send_file_userspace(SocketPtr socket, const std::string& filename, boost::asio::yield_context yield)
314 {
315  std::size_t filesize { std::size_t(filesystem::file_size(filename)) };
316  union DataSize
317  {
318  uint32_t size;
319  char buf[4];
320  } data_size {};
321  data_size.size = htonl(filesize);
322 
323  async_write(*socket, boost::asio::buffer(data_size.buf), yield);
324 
325  auto ifs { filesystem::istream_file(filename) };
326  ifs->seekg(0);
327  while(ifs->good()) {
328  char buf[16384];
329  ifs->read(buf, sizeof(buf));
330  async_write(*socket, boost::asio::buffer(buf, ifs->gcount()), yield);
331  }
332 }
333 
334 #ifdef HAVE_SENDFILE
335 
336 void server_base::coro_send_file(tls_socket_ptr socket, const std::string& filename, boost::asio::yield_context yield)
337 {
338  // We fallback to userspace if using TLS socket because sendfile is not aware of TLS state
339  // TODO: keep in mind possibility of using KTLS instead. This seem to be available only in openssl3 branch for now
340  coro_send_file_userspace(socket, filename, yield);
341 }
342 
343 void server_base::coro_send_file(socket_ptr socket, const std::string& filename, boost::asio::yield_context yield)
344 {
345  std::size_t filesize { std::size_t(filesystem::file_size(filename)) };
346  int in_file { open(filename.c_str(), O_RDONLY) };
347  off_t offset { 0 };
348  std::size_t total_bytes_transferred { 0 };
349 
350  union DataSize
351  {
352  uint32_t size;
353  char buf[4];
354  } data_size {};
355  data_size.size = htonl(filesize);
356 
357  async_write(*socket, boost::asio::buffer(data_size.buf), yield);
358  if(*(yield.ec_)) return;
359 
360  // Put the underlying socket into non-blocking mode.
361  if(!socket->native_non_blocking())
362  socket->native_non_blocking(true, *yield.ec_);
363  if(*(yield.ec_)) return;
364 
365  for (;;)
366  {
367  // Try the system call.
368  errno = 0;
369  int n = ::sendfile(socket->native_handle(), in_file, &offset, 65536);
370  *(yield.ec_) = boost::system::error_code(n < 0 ? errno : 0,
371  boost::asio::error::get_system_category());
372  total_bytes_transferred += *(yield.ec_) ? 0 : n;
373 
374  // Retry operation immediately if interrupted by signal.
375  if (*(yield.ec_) == boost::asio::error::interrupted)
376  continue;
377 
378  // Check if we need to run the operation again.
379  if (*(yield.ec_) == boost::asio::error::would_block
380  || *(yield.ec_) == boost::asio::error::try_again)
381  {
382  // We have to wait for the socket to become ready again.
383  socket->async_write_some(boost::asio::null_buffers(), yield);
384  continue;
385  }
386 
387  if (*(yield.ec_) || n == 0)
388  {
389  // An error occurred, or we have reached the end of the file.
390  // Either way we must exit the loop.
391  break;
392  }
393 
394  // Loop around to try calling sendfile again.
395  }
396 }
397 
398 #elif defined(_WIN32)
399 
400 void server_base::coro_send_file(tls_socket_ptr socket, const std::string& filename, boost::asio::yield_context yield)
401 {
402  coro_send_file_userspace(socket, filename, yield);
403 }
404 
405 void server_base::coro_send_file(socket_ptr socket, const std::string& filename, boost::asio::yield_context yield)
406 {
407  OVERLAPPED overlap;
408  std::vector<boost::asio::const_buffer> buffers;
409 
410  SetLastError(ERROR_SUCCESS);
411 
412  std::size_t filesize = filesystem::file_size(filename);
413  std::wstring filename_ucs2 = unicode_cast<std::wstring>(filename);
414  HANDLE in_file = CreateFileW(filename_ucs2.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
415  FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
416  if (GetLastError() != ERROR_SUCCESS)
417  {
418  throw std::runtime_error("Failed to open the file");
419  }
420  BOOST_SCOPE_EXIT_ALL(in_file) {
421  CloseHandle(&in_file);
422  };
423 
424  HANDLE event = CreateEvent(nullptr, TRUE, TRUE, nullptr);
425  if (GetLastError() != ERROR_SUCCESS)
426  {
427  throw std::runtime_error("Failed to create an event");
428  }
429  BOOST_SCOPE_EXIT_ALL(&overlap) {
430  CloseHandle(overlap.hEvent);
431  };
432 
433  overlap.hEvent = event;
434 
435  union DataSize
436  {
437  uint32_t size;
438  char buf[4];
439  } data_size {};
440  data_size.size = htonl(filesize);
441 
442  async_write(*socket, boost::asio::buffer(data_size.buf, 4), yield);
443 
444  BOOL success = TransmitFile(socket->native_handle(), in_file, 0, 0, &overlap, nullptr, 0);
445  if(!success) {
446  if(WSAGetLastError() == WSA_IO_PENDING) {
447  while(true) {
448  // The request is pending. Wait until it completes.
449  socket->async_write_some(boost::asio::null_buffers(), yield);
450 
451  DWORD win_ec = GetLastError();
452  if (win_ec != ERROR_IO_PENDING && win_ec != ERROR_SUCCESS)
453  throw std::runtime_error("TransmitFile failed");
454 
455  if(HasOverlappedIoCompleted(&overlap)) break;
456  }
457  } else {
458  throw std::runtime_error("TransmitFile failed");
459  }
460  }
461 }
462 
463 #else
464 
465 void server_base::coro_send_file(tls_socket_ptr socket, const std::string& filename, boost::asio::yield_context yield)
466 {
467  coro_send_file_userspace(socket, filename, yield);
468 }
469 
470 void server_base::coro_send_file(socket_ptr socket, const std::string& filename, boost::asio::yield_context yield)
471 {
472  coro_send_file_userspace(socket, filename, yield);
473 }
474 
475 #endif
476 
477 template<class SocketPtr> std::unique_ptr<simple_wml::document> server_base::coro_receive_doc(SocketPtr socket, boost::asio::yield_context yield)
478 {
479  union DataSize
480  {
481  uint32_t size;
482  char buf[4];
483  } data_size {};
484  async_read(*socket, boost::asio::buffer(data_size.buf, 4), yield);
485  if(*yield.ec_) return {};
486  uint32_t size = ntohl(data_size.size);
487 
488  if(size == 0) {
489  ERR_SERVER <<
490  log_address(socket) <<
491  "\treceived invalid packet with payload size 0" << std::endl;
492  return {};
493  }
495  ERR_SERVER <<
496  log_address(socket) <<
497  "\treceived packet with payload size over size limit" << std::endl;
498  return {};
499  }
500 
501  boost::shared_array<char> buffer{ new char[size] };
502  async_read(*socket, boost::asio::buffer(buffer.get(), size), yield);
503 
504  try {
505  simple_wml::string_span compressed_buf(buffer.get(), size);
506  return std::make_unique<simple_wml::document>(compressed_buf);
507  } catch (simple_wml::error& e) {
508  ERR_SERVER <<
509  log_address(socket) <<
510  "\tsimple_wml error in received data: " << e.message << std::endl;
511  async_send_error(socket, "Invalid WML received: " + e.message);
512  return {};
513  }
514 }
515 template std::unique_ptr<simple_wml::document> server_base::coro_receive_doc<socket_ptr>(socket_ptr socket, boost::asio::yield_context yield);
516 template std::unique_ptr<simple_wml::document> server_base::coro_receive_doc<tls_socket_ptr>(tls_socket_ptr socket, boost::asio::yield_context yield);
517 
518 template<class SocketPtr> void server_base::async_send_doc_queued(SocketPtr socket, simple_wml::document& doc)
519 {
520  boost::asio::spawn(
521  io_service_, [this, doc_ptr = doc.clone(), socket](boost::asio::yield_context yield) mutable {
522  static std::map<SocketPtr, std::queue<std::unique_ptr<simple_wml::document>>> queues;
523 
524  queues[socket].push(std::move(doc_ptr));
525  if(queues[socket].size() > 1) {
526  return;
527  }
528 
529  while(queues[socket].size() > 0) {
530  coro_send_doc(socket, *(queues[socket].front()), yield);
531  queues[socket].pop();
532  }
533  queues.erase(socket);
534  }
535  );
536 }
537 
538 template<class SocketPtr> void server_base::async_send_error(SocketPtr socket, const std::string& msg, const char* error_code, const info_table& info)
539 {
541  doc.root().add_child("error").set_attr_dup("message", msg.c_str());
542  if(*error_code != '\0') {
543  doc.child("error")->set_attr("error_code", error_code);
544  }
545  info_table_into_simple_wml(doc, "error", info);
546 
547  async_send_doc_queued(socket, doc);
548 }
549 template void server_base::async_send_error<socket_ptr>(socket_ptr socket, const std::string& msg, const char* error_code, const info_table& info);
550 template void server_base::async_send_error<tls_socket_ptr>(tls_socket_ptr socket, const std::string& msg, const char* error_code, const info_table& info);
551 
552 template<class SocketPtr> void server_base::async_send_warning(SocketPtr socket, const std::string& msg, const char* warning_code, const info_table& info)
553 {
555  doc.root().add_child("warning").set_attr_dup("message", msg.c_str());
556  if(*warning_code != '\0') {
557  doc.child("warning")->set_attr("warning_code", warning_code);
558  }
559  info_table_into_simple_wml(doc, "warning", info);
560 
561  async_send_doc_queued(socket, doc);
562 }
563 template void server_base::async_send_warning<socket_ptr>(socket_ptr socket, const std::string& msg, const char* warning_code, const info_table& info);
564 template void server_base::async_send_warning<tls_socket_ptr>(tls_socket_ptr socket, const std::string& msg, const char* warning_code, const info_table& info);
565 
567 {
568  tls_enabled_ = cfg["tls_enabled"].to_bool(false);
569  if(!tls_enabled_) return;
570 
571  tls_context_.set_options(
572  boost::asio::ssl::context::default_workarounds
573  | boost::asio::ssl::context::no_sslv2
574  | boost::asio::ssl::context::no_sslv3
575  | boost::asio::ssl::context::single_dh_use
576  );
577 
578  tls_context_.use_certificate_chain_file(cfg["tls_fullchain"].str());
579  tls_context_.use_private_key_file(cfg["tls_private_key"].str(), boost::asio::ssl::context::pem);
580  if(!cfg["tls_dh"].str().empty()) tls_context_.use_tmp_dh_file(cfg["tls_dh"].str());
581 }
582 
583 std::string server_base::hash_password(const std::string& pw, const std::string& salt, const std::string& username)
584 {
585  if(salt.length() < 12) {
586  ERR_SERVER << "Bad salt found for user: " << username << std::endl;
587  return "";
588  }
589 
590  if(utils::md5::is_valid_prefix(salt)) {
591  std::string hash = utils::md5(pw, utils::md5::get_salt(salt), utils::md5::get_iteration_count(salt)).base64_digest();
592  return salt+hash;
593  } else if(utils::bcrypt::is_valid_prefix(salt)) {
594  try {
595  auto bcrypt_salt = utils::bcrypt::from_salted_salt(salt);
596  auto hash = utils::bcrypt::hash_pw(pw, bcrypt_salt);
597  return hash.base64_digest();
598  } catch(const utils::hash_error& err) {
599  ERR_SERVER << "bcrypt hash failed for user " << username << ": " << err.what() << std::endl;
600  return "";
601  }
602  } else {
603  ERR_SERVER << "Unable to determine how to hash the password for user: " << username << std::endl;
604  return "";
605  }
606 }
607 
608 // This is just here to get it to build without the deprecation_message function
609 #include "game_version.hpp"
610 #include "deprecation.hpp"
611 
612 std::string deprecated_message(const std::string&, DEP_LEVEL, const version_info&, const std::string&) {return "";}
boost::asio::ip::tcp::acceptor acceptor_v4_
node & add_child(const char *name)
Definition: simple_wml.cpp:465
virtual void handle_sighup(const boost::system::error_code &error, int signal_number)=0
template bool check_error< tls_socket_ptr >(const boost::system::error_code &error, tls_socket_ptr socket)
std::unique_ptr< simple_wml::document > coro_receive_doc(SocketPtr socket, boost::asio::yield_context yield)
Receive WML document from a coroutine.
string_span output_compressed(bool bzip2=false)
#define LOG_SERVER
Definition: server_base.cpp:56
Interfaces for manipulating version numbers of engine, add-ons, etc.
static bool is_valid_prefix(const std::string &hash)
Definition: hash.cpp:92
void coro_send_file(socket_ptr socket, const std::string &filename, boost::asio::yield_context yield)
Send contents of entire file directly to socket from within a coroutine.
virtual std::string base64_digest() const override
Definition: hash.cpp:122
virtual void handle_new_client(socket_ptr socket)=0
boost::asio::signal_set sighup_
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:40
logger & info()
Definition: log.cpp:89
ucs4_convert_impl::enableif< TD, typename TS::value_type >::type unicode_cast(const TS &source)
void async_send_error(SocketPtr socket, const std::string &msg, const char *error_code="", const info_table &info={})
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
node & set_attr(const char *key, const char *value)
Definition: simple_wml.cpp:412
void handle_termination(const boost::system::error_code &error, int signal_number)
static bool is_valid_prefix(const std::string &hash)
Definition: hash.cpp:188
static int get_iteration_count(const std::string &hash)
Definition: hash.cpp:84
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:110
DEP_LEVEL
See https://wiki.wesnoth.org/CompatibilityStandards for more info.
Definition: deprecation.hpp:20
void load_tls_config(const config &cfg)
bool dump_wml
Definition: server_base.cpp:63
Definitions for the interface to Wesnoth Markup Language (WML).
unsigned short port_
utils::variant< socket_ptr, tls_socket_ptr > any_socket_ptr
Definition: server_base.hpp:52
#define ERR_SERVER
Definition: server_base.cpp:54
void start_server()
Definition: server_base.cpp:80
std::unique_ptr< document > clone()
node * child(const char *name)
Definition: simple_wml.hpp:262
const char * output()
const char * begin() const
Definition: simple_wml.hpp:91
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
boost::asio::streambuf admin_cmd_
void coro_send_file_userspace(SocketPtr socket, const std::string &filename, boost::asio::yield_context yield)
std::string log_address(SocketPtr socket)
static bcrypt from_salted_salt(const std::string &input)
Definition: hash.cpp:158
boost::asio::io_service io_service_
#define DBG_SERVER
Definition: server_base.cpp:57
void async_send_doc_queued(SocketPtr socket, simple_wml::document &doc)
High level wrapper for sending a WML document.
std::string client_address(SocketPtr socket)
boost::asio::ip::tcp::acceptor acceptor_v6_
virtual bool accepting_connections() const
const char * what() const noexcept
Definition: exceptions.hpp:36
constexpr bool decayed_is_same
Equivalent to as std::is_same_v except both types are passed throgh std::decay first.
Definition: general.hpp:33
static std::size_t document_size_limit
Definition: simple_wml.hpp:291
virtual bool ip_exceeds_connection_limit(const std::string &) const
virtual void handle_read_from_fifo(const boost::system::error_code &error, std::size_t bytes_transferred)=0
std::string hash_password(const std::string &pw, const std::string &salt, const std::string &username)
Handles hashing the password provided by the player before comparing it to the hashed password in the...
uint32_t handshake_response_
static lg::log_domain log_server("server")
logger & err()
Definition: log.cpp:77
#define WRN_CONFIG
Definition: server_base.cpp:61
node & set_attr_dup(const char *key, const char *value)
Definition: simple_wml.cpp:428
server_base(unsigned short port, bool keep_alive)
Definition: server_base.cpp:65
std::map< std::string, std::string > info_table
static map_location::DIRECTION s
void coro_send_doc(SocketPtr socket, simple_wml::document &doc, boost::asio::yield_context yield)
Send a WML document from within a coroutine.
static lg::log_domain log_config("config")
Declarations for File-IO.
void read_from_fifo()
virtual std::string is_ip_banned(const std::string &)
boost::asio::posix::stream_descriptor input_
Represents version numbers.
std::string deprecated_message(const std::string &, DEP_LEVEL, const version_info &, const std::string &)
int file_size(const std::string &fname)
Returns the size of a file, or -1 if the file doesn&#39;t exist.
std::shared_ptr< boost::asio::ssl::stream< socket_ptr::element_type > > tls_socket_ptr
Definition: server_base.hpp:51
bool check_error(const boost::system::error_code &error, SocketPtr socket)
void async_send_warning(SocketPtr socket, const std::string &msg, const char *warning_code="", const info_table &info={})
Standard logging facilities (interface).
std::string message
Definition: exceptions.hpp:30
std::shared_ptr< boost::asio::ip::tcp::socket > socket_ptr
Definition: server_base.hpp:48
static std::string get_salt(const std::string &hash)
Definition: hash.cpp:88
#define e
static bcrypt hash_pw(const std::string &password, bcrypt &salt)
Definition: hash.cpp:179
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
static map_location::DIRECTION n
boost::asio::ssl::context tls_context_
void serve(boost::asio::yield_context yield, boost::asio::ip::tcp::acceptor &acceptor, boost::asio::ip::tcp::endpoint endpoint)
Definition: server_base.cpp:98
Base class for servers using Wesnoth&#39;s WML over TCP protocol.
boost::asio::signal_set sigs_