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