The Battle for Wesnoth  1.15.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 http://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 
15 #include "server/server_base.hpp"
16 
17 #include "lexical_cast.hpp"
18 #include "log.hpp"
19 #include "utils/functional.hpp"
20 
21 static lg::log_domain log_server("server");
22 #define ERR_SERVER LOG_STREAM(err, log_server)
23 #define WRN_SERVER LOG_STREAM(warn, log_server)
24 #define LOG_SERVER LOG_STREAM(info, log_server)
25 #define DBG_SERVER LOG_STREAM(debug, log_server)
26 
27 static lg::log_domain log_config("config");
28 #define ERR_CONFIG LOG_STREAM(err, log_config)
29 #define WRN_CONFIG LOG_STREAM(warn, log_config)
30 
32 
33 server_base::server_base(unsigned short port, bool keep_alive) :
34  port_(port),
35  keep_alive_(keep_alive),
36  io_service_(),
37  acceptor_(io_service_),
38  #ifndef _WIN32
39  input_(io_service_),
40  sighup_(io_service_, SIGHUP),
41  #endif
42  sigs_(io_service_, SIGINT, SIGTERM)
43 {
44 }
45 
47 {
48  boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), port_);
49  acceptor_.open(endpoint.protocol());
50  acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
51  acceptor_.set_option(boost::asio::ip::tcp::acceptor::keep_alive(keep_alive_));
52  acceptor_.bind(endpoint);
53  acceptor_.listen();
54  serve();
55 
56  handshake_response_.connection_num = htonl(42);
57 
58 #ifndef _WIN32
59  sighup_.async_wait(
60  [=](const boost::system::error_code& error, int sig)
61  { this->handle_sighup(error, sig); });
62 #endif
63  sigs_.async_wait(std::bind(&server_base::handle_termination, this, _1, _2));
64 }
65 
67 {
68  socket_ptr socket = std::make_shared<boost::asio::ip::tcp::socket>(std::ref(io_service_));
69  acceptor_.async_accept(*socket, std::bind(&server_base::accept_connection, this, _1, socket));
70 }
71 
72 void server_base::accept_connection(const boost::system::error_code& error, socket_ptr socket)
73 {
75  serve();
76  if(error) {
77  ERR_SERVER << "Accept failed: " << error.message() << "\n";
78  return;
79  }
80 
81  DBG_SERVER << client_address(socket) << "\tnew connection tentatively accepted\n";
82  serverside_handshake(socket);
83 }
84 
86 {
87  boost::shared_array<char> handshake(new char[4]);
88  async_read(
89  *socket, boost::asio::buffer(handshake.get(), 4),
90  std::bind(&server_base::handle_handshake, this, _1, socket, handshake)
91  );
92 }
93 
94 void server_base::handle_handshake(const boost::system::error_code& error, socket_ptr socket, boost::shared_array<char> handshake)
95 {
96  if(check_error(error, socket))
97  return;
98 
99  if(memcmp(handshake.get(), "\0\0\0\0", 4) != 0) {
100  ERR_SERVER << client_address(socket) << "\tincorrect handshake\n";
101  return;
102  }
103  async_write(
104  *socket, boost::asio::buffer(handshake_response_.buf, 4),
105  [=](const boost::system::error_code& error, std::size_t)
106  {
107  if(!check_error(error, socket)) {
108  const std::string ip = client_address(socket);
109 
110  const std::string reason = is_ip_banned(ip);
111  if (!reason.empty()) {
112  LOG_SERVER << ip << "\trejected banned user. Reason: " << reason << "\n";
113  async_send_error(socket, "You are banned. Reason: " + reason);
114  return;
115  } else if (ip_exceeds_connection_limit(ip)) {
116  LOG_SERVER << ip << "\trejected ip due to excessive connections\n";
117  async_send_error(socket, "Too many connections from your IP.");
118  return;
119  } else {
120  DBG_SERVER << ip << "\tnew connection fully accepted\n";
121  this->handle_new_client(socket);
122  }
123  }
124  }
125  );
126 }
127 
128 #ifndef _WIN32
130  async_read_until(input_,
131  admin_cmd_, '\n',
132  [=](const boost::system::error_code& error, std::size_t bytes_transferred)
133  { this->handle_read_from_fifo(error, bytes_transferred); }
134  );
135 }
136 #endif
137 
138 void server_base::handle_termination(const boost::system::error_code& error, int signal_number)
139 {
140  assert(!error);
141 
142  std::string signame;
143  if(signal_number == SIGINT) signame = "SIGINT";
144  else if(signal_number == SIGTERM) signame = "SIGTERM";
145  else signame = lexical_cast<std::string>(signal_number);
146  LOG_SERVER << signame << " caught, exiting without cleanup immediately.\n";
147  exit(128 + signal_number);
148 }
149 
151  try {
152  io_service_.run();
153  LOG_SERVER << "Server has shut down because event loop is out of work\n";
154  } catch(const server_shutdown& e) {
155  LOG_SERVER << "Server has been shut down: " << e.what() << "\n";
156  }
157 }
158 
159 std::string client_address(const socket_ptr socket)
160 {
161  boost::system::error_code error;
162  std::string result = socket->remote_endpoint(error).address().to_string();
163  if(error)
164  return "<unknown address>";
165  else
166  return result;
167 }
168 
169 bool check_error(const boost::system::error_code& error, socket_ptr socket)
170 {
171  if(error) {
172  if(error == boost::asio::error::eof)
173  LOG_SERVER << client_address(socket) << "\tconnection closed\n";
174  else
175  ERR_SERVER << client_address(socket) << "\t" << error.message() << "\n";
176  return true;
177  }
178  return false;
179 }
180 
181 void async_send_error(socket_ptr socket, const std::string& msg, const char* error_code)
182 {
184  doc.root().add_child("error").set_attr_dup("message", msg.c_str());
185  if(*error_code != '\0') {
186  doc.child("error")->set_attr("error_code", error_code);
187  }
188 
189  async_send_doc(socket, doc);
190 }
191 
192 void async_send_warning(socket_ptr socket, const std::string& msg, const char* warning_code)
193 {
195  doc.root().add_child("warning").set_attr_dup("message", msg.c_str());
196  if(*warning_code != '\0') {
197  doc.child("warning")->set_attr("warning_code", warning_code);
198  }
199 
200  async_send_doc(socket, doc);
201 }
202 
203 void async_send_message(socket_ptr socket, const std::string& msg)
204 {
206  doc.root().add_child("message").set_attr_dup("message", msg.c_str());
207 
208  async_send_doc(socket, doc);
209 }
210 
211 // This is just here to get it to build without the deprecation_message function
212 #include "version.hpp"
213 #include "deprecation.hpp"
214 
215 std::string deprecated_message(const std::string&, DEP_LEVEL, const version_info&, const std::string&) {return "";}
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
void handle_handshake(const boost::system::error_code &error, socket_ptr socket, boost::shared_array< char > buf)
Definition: server_base.cpp:94
#define LOG_SERVER
Definition: server_base.cpp:24
bool check_error(const boost::system::error_code &error, socket_ptr socket)
boost::asio::signal_set sighup_
Definition: server_base.hpp:71
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:39
New lexcical_cast header.
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 void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
To lexical_cast(From value)
Lexical cast converts one type to another.
void serverside_handshake(socket_ptr socket)
Definition: server_base.cpp:85
unsigned short port_
Definition: server_base.hpp:43
void async_send_warning(socket_ptr socket, const std::string &msg, const char *warning_code)
#define ERR_SERVER
Definition: server_base.cpp:22
void start_server()
Definition: server_base.cpp:46
node * child(const char *name)
Definition: simple_wml.hpp:256
bool keep_alive_
Definition: server_base.hpp:44
boost::asio::streambuf admin_cmd_
Definition: server_base.hpp:69
boost::asio::io_service io_service_
Definition: server_base.hpp:45
#define DBG_SERVER
Definition: server_base.cpp:25
void accept_connection(const boost::system::error_code &error, socket_ptr socket)
Definition: server_base.cpp:72
virtual bool accepting_connections() const
Definition: server_base.hpp:60
boost::asio::ip::tcp::acceptor acceptor_
Definition: server_base.hpp:46
const char * what() const noexcept
Definition: exceptions.hpp:37
virtual void handle_read_from_fifo(const boost::system::error_code &error, std::size_t bytes_transferred)=0
static lg::log_domain log_server("server")
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:33
static lg::log_domain log_config("config")
std::string client_address(const socket_ptr socket)
void read_from_fifo()
boost::asio::posix::stream_descriptor input_
Definition: server_base.hpp:65
Represents version numbers.
Definition: version.hpp:43
std::string deprecated_message(const std::string &, DEP_LEVEL, const version_info &, const std::string &)
void async_send_doc(socket_ptr socket, simple_wml::document &doc, Handler handler, ErrorHandler error_handler)
DEP_LEVEL
See https://wiki.wesnoth.org/CompatibilityStandards for more info.
Definition: deprecation.hpp:19
void async_send_error(socket_ptr socket, const std::string &msg, const char *error_code)
Standard logging facilities (interface).
std::shared_ptr< boost::asio::ip::tcp::socket > socket_ptr
Definition: server_base.hpp:28
union server_base::@19 handshake_response_
void async_send_message(socket_ptr socket, const std::string &msg)
#define e
void serve()
Definition: server_base.cpp:66
Interfaces for manipulating version numbers of engine, add-ons, etc.
Base class for servers using Wesnoth&#39;s WML over TCP protocol.
boost::asio::signal_set sigs_
Definition: server_base.hpp:74