The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
server_base.hpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2016 - 2025
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
16/**
17 * @file
18 * Base class for servers using Wesnoth's WML over TCP protocol.
19 */
20
21#pragma once
22
23#include "exceptions.hpp"
25
26#include "utils/variant.hpp"
27#include "utils/general.hpp"
29
30#ifdef _WIN32
32#endif
33
34#include <boost/asio/io_context.hpp>
35#include <boost/asio/ip/tcp.hpp>
36#ifndef _WIN32
37#include <boost/asio/posix/stream_descriptor.hpp>
38#endif
39#include <boost/asio/signal_set.hpp>
40#include <boost/asio/streambuf.hpp>
41#include <boost/asio/ssl.hpp>
42#include <boost/asio/spawn.hpp>
43#include <boost/shared_array.hpp>
44
45#include <map>
46
47extern bool dump_wml;
48
49class config;
50
51typedef std::shared_ptr<boost::asio::ip::tcp::socket> socket_ptr;
52typedef std::shared_ptr<boost::asio::ssl::stream<socket_ptr::element_type>> tls_socket_ptr;
53typedef utils::variant<socket_ptr, tls_socket_ptr> any_socket_ptr;
54
56{
57 boost::system::error_code ec;
58 server_shutdown(const std::string& msg, boost::system::error_code ec = {}) : game::error(msg), ec(ec) {}
59};
60
61/**
62 * Base class for implementing servers that use gzipped-WML network protocol
63 *
64 * The protocol is based on TCP connection between client and server.
65 * Before WML payloads can be sent a handshake is required. Handshake process is as follows:
66 * - client establishes a TCP connection to server.
67 * - client sends 32-bit integer(network byte order) representing protocol version requested.
68 * - server receives 32-bit integer. Depending on number received server does the following:
69 * 0: unencrypted protocol, send unspecified 32-bit integer for compatibility(current implementation sends 42)
70 * 1: depending on whether TLS is enabled on server
71 * if TLS enabled: send 32-bit integer 0 and immediately start TLS, client is expected to start TLS on receiving this 0
72 * if TLS disabled: send 32-bit integer 0xFFFFFFFF, on receiving this client should proceed as with unencrypted connection or immediately close
73 * any other number: server closes connection immediately
74 * - at this point handshake is completed and client and server can exchange WML messages
75 *
76 * Message format is as follows:
77 * - 32-bit unsigned integer(network byte order), this is size of the following payload
78 * - payload: gzipped WML data, which is WML text fed through gzip utility or the equivalent library function.
79 */
81{
82 template<class SocketPtr> void send_doc_queued(const SocketPtr& socket, std::unique_ptr<simple_wml::document>& doc_ptr, const boost::asio::yield_context& yield);
83
84public:
85 server_base(unsigned short port, bool keep_alive);
86 virtual ~server_base() {}
87 int run();
88
89 /**
90 * Send a WML document from within a coroutine
91 * @param socket
92 * @param doc
93 * @param yield The function will suspend on write operation using this yield context
94 */
95 template<class SocketPtr> void coro_send_doc(const SocketPtr& socket, simple_wml::document& doc, const boost::asio::yield_context& yield);
96 /**
97 * Send contents of entire file directly to socket from within a coroutine
98 * @param socket
99 * @param filename
100 * @param yield The function will suspend on write operations using this yield context
101 */
102 void coro_send_file(const socket_ptr& socket, const std::string& filename, const boost::asio::yield_context& yield);
103 void coro_send_file(const tls_socket_ptr& socket, const std::string& filename, const boost::asio::yield_context& yield);
104 /**
105 * Receive WML document from a coroutine
106 * @param socket
107 * @param yield The function will suspend on read operation using this yield context
108 * @return unique_ptr with doc deceived. In case of error empty unique_ptr
109 */
110 template<class SocketPtr> std::unique_ptr<simple_wml::document> coro_receive_doc(const SocketPtr& socket, const boost::asio::yield_context& yield);
111
112 /**
113 * High level wrapper for sending a WML document
114 *
115 * This function returns before send is finished. This function can be called again on same socket before previous send was finished.
116 * WML documents are kept in internal queue and sent in FIFO order.
117 * @param socket
118 * @param doc Document to send. A copy of it will be made so there is no need to keep the reference live after the function returns.
119 */
120 template<class SocketPtr> void async_send_doc_queued(const SocketPtr& socket, simple_wml::document& doc);
121
122 typedef std::map<std::string, std::string> info_table;
123 template<class SocketPtr> void async_send_error(SocketPtr socket, const std::string& msg, const char* error_code = "", const info_table& info = {});
124 template<class SocketPtr> void async_send_warning(const SocketPtr& socket, const std::string& msg, const char* warning_code = "", const info_table& info = {});
125
126 /**
127 * Handles hashing the password provided by the player before comparing it to the hashed password in the forum database.
128 *
129 * @param pw The plaintext password.
130 * @param salt The salt as retrieved from the forum database.
131 * @param username The player attempting to log in.
132 * @return The hashed password, or empty if the password couldn't be hashed.
133 */
134 std::string hash_password(const std::string& pw, const std::string& salt, const std::string& username);
135
136protected:
137 unsigned short port_;
139 boost::asio::io_context io_service_;
140 boost::asio::ssl::context tls_context_ { boost::asio::ssl::context::tlsv13_server };
141 bool tls_enabled_ { false };
142 boost::asio::ip::tcp::acceptor acceptor_v6_;
143 boost::asio::ip::tcp::acceptor acceptor_v4_;
144
145 void load_tls_config(const config& cfg);
146
147 void start_server();
148 void serve(const boost::asio::yield_context& yield, boost::asio::ip::tcp::acceptor& acceptor, const boost::asio::ip::tcp::endpoint& endpoint);
149
151
152 virtual void handle_new_client(socket_ptr socket) = 0;
153 virtual void handle_new_client(tls_socket_ptr socket) = 0;
154
155 virtual bool accepting_connections() const { return true; }
156 virtual bool ip_exceeds_connection_limit(const std::string&) const { return false; }
157
159 {
160 const char* error_code;
161 std::string reason;
162 utils::optional<std::chrono::seconds> time_remaining;
163 };
164
165 virtual utils::optional<login_ban_info> is_ip_banned(const std::string&) { return {}; }
166
167#ifndef _WIN32
168 boost::asio::posix::stream_descriptor input_;
169 std::string fifo_path_;
170 void read_from_fifo();
171 virtual void handle_read_from_fifo(const boost::system::error_code& error, std::size_t bytes_transferred) = 0;
172 boost::asio::streambuf admin_cmd_;
173
174 boost::asio::signal_set sighup_;
175 virtual void handle_sighup(const boost::system::error_code& error, int signal_number) = 0;
176#endif
177};
178
179template<class SocketPtr> std::string client_address(SocketPtr socket);
180template<class SocketPtr> std::string log_address(SocketPtr socket) { return (utils::decayed_is_same<tls_socket_ptr, decltype(socket)> ? "+" : "") + client_address(socket); }
181template<class SocketPtr> bool check_error(const boost::system::error_code& error, const SocketPtr& socket);
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
Base class for implementing servers that use gzipped-WML network protocol.
void async_send_warning(const SocketPtr &socket, const std::string &msg, const char *warning_code="", const info_table &info={})
virtual utils::optional< login_ban_info > is_ip_banned(const std::string &)
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...
virtual void handle_read_from_fifo(const boost::system::error_code &error, std::size_t bytes_transferred)=0
boost::asio::signal_set sighup_
uint32_t handshake_response_
boost::asio::streambuf admin_cmd_
void async_send_error(SocketPtr socket, const std::string &msg, const char *error_code="", const info_table &info={})
virtual void handle_new_client(socket_ptr socket)=0
virtual ~server_base()
boost::asio::ip::tcp::acceptor acceptor_v4_
std::unique_ptr< simple_wml::document > coro_receive_doc(const SocketPtr &socket, const boost::asio::yield_context &yield)
Receive WML document from a coroutine.
std::map< std::string, std::string > info_table
virtual void handle_new_client(tls_socket_ptr socket)=0
boost::asio::io_context io_service_
unsigned short port_
void async_send_doc_queued(const SocketPtr &socket, simple_wml::document &doc)
High level wrapper for sending a WML document.
virtual void handle_sighup(const boost::system::error_code &error, int signal_number)=0
void read_from_fifo()
void serve(const boost::asio::yield_context &yield, boost::asio::ip::tcp::acceptor &acceptor, const boost::asio::ip::tcp::endpoint &endpoint)
virtual bool ip_exceeds_connection_limit(const std::string &) const
virtual bool accepting_connections() const
void coro_send_file(const tls_socket_ptr &socket, const std::string &filename, const boost::asio::yield_context &yield)
void start_server()
void send_doc_queued(const SocketPtr &socket, std::unique_ptr< simple_wml::document > &doc_ptr, const boost::asio::yield_context &yield)
boost::asio::posix::stream_descriptor input_
void coro_send_file(const socket_ptr &socket, const std::string &filename, const boost::asio::yield_context &yield)
Send contents of entire file directly to socket from within a coroutine.
void load_tls_config(const config &cfg)
boost::asio::ssl::context tls_context_
void coro_send_doc(const SocketPtr &socket, simple_wml::document &doc, const boost::asio::yield_context &yield)
Send a WML document from within a coroutine.
boost::asio::ip::tcp::acceptor acceptor_v6_
std::string fifo_path_
const config * cfg
constexpr bool decayed_is_same
Equivalent to as std::is_same_v except both types are passed through std::decay first.
Definition general.hpp:32
std::shared_ptr< boost::asio::ssl::stream< socket_ptr::element_type > > tls_socket_ptr
std::string client_address(SocketPtr socket)
bool dump_wml
std::string log_address(SocketPtr socket)
utils::variant< socket_ptr, tls_socket_ptr > any_socket_ptr
std::shared_ptr< boost::asio::ip::tcp::socket > socket_ptr
bool check_error(const boost::system::error_code &error, const SocketPtr &socket)
std::string filename
Filename.
Base class for all the errors encountered by the engine.
utils::optional< std::chrono::seconds > time_remaining
boost::system::error_code ec
server_shutdown(const std::string &msg, boost::system::error_code ec={})
MacOS doesn't support std::visit when targing MacOS < 10.14 (currently we target 10....