1/**
2 * The Forgotten Server - a free and open-source MMORPG server emulator
3 * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
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 as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "otpch.h"
21
22#include "outputmessage.h"
23#include "server.h"
24#include "scheduler.h"
25#include "configmanager.h"
26#include "ban.h"
27
28extern ConfigManager g_config;
29Ban g_bans;
30
31ServiceManager::~ServiceManager()
32{
33 stop();
34}
35
36void ServiceManager::die()
37{
38 io_service.stop();
39}
40
41void ServiceManager::run()
42{
43 assert(!running);
44 running = true;
45 io_service.run();
46}
47
48void ServiceManager::stop()
49{
50 if (!running) {
51 return;
52 }
53
54 running = false;
55
56 for (auto& servicePortIt : acceptors) {
57 try {
58 io_service.post(std::bind(&ServicePort::onStopServer, servicePortIt.second));
59 } catch (boost::system::system_error& e) {
60 std::cout << "[ServiceManager::stop] Network Error: " << e.what() << std::endl;
61 }
62 }
63
64 acceptors.clear();
65
66 death_timer.expires_from_now(boost::posix_time::seconds(3));
67 death_timer.async_wait(std::bind(&ServiceManager::die, this));
68}
69
70ServicePort::~ServicePort()
71{
72 close();
73}
74
75bool ServicePort::is_single_socket() const
76{
77 return !services.empty() && services.front()->is_single_socket();
78}
79
80std::string ServicePort::get_protocol_names() const
81{
82 if (services.empty()) {
83 return std::string();
84 }
85
86 std::string str = services.front()->get_protocol_name();
87 for (size_t i = 1; i < services.size(); ++i) {
88 str.push_back(',');
89 str.push_back(' ');
90 str.append(services[i]->get_protocol_name());
91 }
92 return str;
93}
94
95void ServicePort::accept()
96{
97 if (!acceptor) {
98 return;
99 }
100
101 auto connection = ConnectionManager::getInstance().createConnection(io_service, shared_from_this());
102 acceptor->async_accept(connection->getSocket(), std::bind(&ServicePort::onAccept, shared_from_this(), connection, std::placeholders::_1));
103}
104
105void ServicePort::onAccept(Connection_ptr connection, const boost::system::error_code& error)
106{
107 if (!error) {
108 if (services.empty()) {
109 return;
110 }
111
112 auto remote_ip = connection->getIP();
113 if (remote_ip != 0 && g_bans.acceptConnection(remote_ip)) {
114 Service_ptr service = services.front();
115 if (service->is_single_socket()) {
116 connection->accept(service->make_protocol(connection));
117 } else {
118 connection->accept();
119 }
120 } else {
121 connection->close(Connection::FORCE_CLOSE);
122 }
123
124 accept();
125 } else if (error != boost::asio::error::operation_aborted) {
126 if (!pendingStart) {
127 close();
128 pendingStart = true;
129 g_scheduler.addEvent(createSchedulerTask(15000,
130 std::bind(&ServicePort::openAcceptor, std::weak_ptr<ServicePort>(shared_from_this()), serverPort)));
131 }
132 }
133}
134
135Protocol_ptr ServicePort::make_protocol(bool checksummed, NetworkMessage& msg, const Connection_ptr& connection) const
136{
137 uint8_t protocolID = msg.getByte();
138 for (auto& service : services) {
139 if (protocolID != service->get_protocol_identifier()) {
140 continue;
141 }
142
143 if ((checksummed && service->is_checksummed()) || !service->is_checksummed()) {
144 return service->make_protocol(connection);
145 }
146 }
147 return nullptr;
148}
149
150void ServicePort::onStopServer()
151{
152 close();
153}
154
155void ServicePort::openAcceptor(std::weak_ptr<ServicePort> weak_service, uint16_t port)
156{
157 if (auto service = weak_service.lock()) {
158 service->open(port);
159 }
160}
161
162void ServicePort::open(uint16_t port)
163{
164 close();
165
166 serverPort = port;
167 pendingStart = false;
168
169 try {
170 if (g_config.getBoolean(ConfigManager::BIND_ONLY_GLOBAL_ADDRESS)) {
171 acceptor.reset(new boost::asio::ip::tcp::acceptor(io_service, boost::asio::ip::tcp::endpoint(
172 boost::asio::ip::address(boost::asio::ip::address_v4::from_string(g_config.getString(ConfigManager::IP))), serverPort)));
173 } else {
174 acceptor.reset(new boost::asio::ip::tcp::acceptor(io_service, boost::asio::ip::tcp::endpoint(
175 boost::asio::ip::address(boost::asio::ip::address_v4(INADDR_ANY)), serverPort)));
176 }
177
178 acceptor->set_option(boost::asio::ip::tcp::no_delay(true));
179
180 accept();
181 } catch (boost::system::system_error& e) {
182 std::cout << "[ServicePort::open] Error: " << e.what() << std::endl;
183
184 pendingStart = true;
185 g_scheduler.addEvent(createSchedulerTask(15000,
186 std::bind(&ServicePort::openAcceptor, std::weak_ptr<ServicePort>(shared_from_this()), port)));
187 }
188}
189
190void ServicePort::close()
191{
192 if (acceptor && acceptor->is_open()) {
193 boost::system::error_code error;
194 acceptor->close(error);
195 }
196}
197
198bool ServicePort::add_service(const Service_ptr& new_svc)
199{
200 if (std::any_of(services.begin(), services.end(), [](const Service_ptr& svc) {return svc->is_single_socket();})) {
201 return false;
202 }
203
204 services.push_back(new_svc);
205 return true;
206}
207