diff --git a/bin/config_sample/config.ini b/bin/config_sample/config.ini index aa55025..8b7d490 100644 --- a/bin/config_sample/config.ini +++ b/bin/config_sample/config.ini @@ -2,7 +2,7 @@ ; The maximum number of players that can join the server at once. max_players=100 -; The TCP port to listen for incoming connections on. +; The port to listen for incoming connections on. port=27016 ; The server description that will appear on the master server. @@ -17,9 +17,6 @@ motd=MOTD is not set. ; Whether the server should accept WebAO connections or not. webao_enable=true -; The TCP port to listen for WebAO connections on. This must be different than the server port. -webao_port=27017 - ; The authorization level of the server. You shouldn't touch this, use /changeauth on the server instead. auth=simple diff --git a/bin_tests/config/config.ini b/bin_tests/config/config.ini index aa55025..8b7d490 100644 --- a/bin_tests/config/config.ini +++ b/bin_tests/config/config.ini @@ -2,7 +2,7 @@ ; The maximum number of players that can join the server at once. max_players=100 -; The TCP port to listen for incoming connections on. +; The port to listen for incoming connections on. port=27016 ; The server description that will appear on the master server. @@ -17,9 +17,6 @@ motd=MOTD is not set. ; Whether the server should accept WebAO connections or not. webao_enable=true -; The TCP port to listen for WebAO connections on. This must be different than the server port. -webao_port=27017 - ; The authorization level of the server. You shouldn't touch this, use /changeauth on the server instead. auth=simple diff --git a/src/advertiser.cpp b/src/advertiser.cpp index 5a6f285..0109e10 100644 --- a/src/advertiser.cpp +++ b/src/advertiser.cpp @@ -19,7 +19,7 @@ #include "config_manager.h" -Advertiser::Advertiser() +Advertiser::Advertiser(int port) { m_manager = new QNetworkAccessManager(); connect(m_manager, &QNetworkAccessManager::finished, @@ -28,15 +28,17 @@ Advertiser::Advertiser() m_name = ConfigManager::serverName(); m_hostname = ConfigManager::advertiserHostname(); m_description = ConfigManager::serverDescription(); - m_port = ConfigManager::serverPort(); // Cheap workaround to correctly advertise when Cloudflare tunnel is used. if (ConfigManager::advertiserCloudflareMode()) { + m_port = 80; m_ws_port = 80; } else { - m_ws_port = ConfigManager::webaoPort(); + m_port = port; + m_ws_port = port; } + m_masterserver = ConfigManager::advertiserIP(); m_debug = ConfigManager::advertiserDebug(); } diff --git a/src/advertiser.h b/src/advertiser.h index e95a37f..dc4c7e3 100644 --- a/src/advertiser.h +++ b/src/advertiser.h @@ -32,7 +32,7 @@ class Advertiser : public QObject /** * @brief Constructor for the HTTP_Advertiser class. */ - explicit Advertiser(); + explicit Advertiser(int port); /** * @brief Deconstructor for the HTTP_Advertiser class. Yes, that's it. Can't say more about it. diff --git a/src/aoclient.h b/src/aoclient.h index 258c26c..f733e66 100644 --- a/src/aoclient.h +++ b/src/aoclient.h @@ -328,7 +328,7 @@ class AOClient : public QObject QString m_hwid; /** - * @brief The network socket used by the client. Can either be a Websocket or TCP Socket. + * @brief The network socket used by the client. */ NetworkSocket *m_socket; diff --git a/src/config_manager.cpp b/src/config_manager.cpp index c65a46c..3817dc9 100644 --- a/src/config_manager.cpp +++ b/src/config_manager.cpp @@ -322,6 +322,11 @@ int ConfigManager::maxPlayers() int ConfigManager::serverPort() { + if (m_settings->contains("Options/webao_port")) { + qWarning("webao_port is deprecated, use port instead"); + return m_settings->value("Options/webao_port", 27016).toInt(); + } + return m_settings->value("Options/port", 27016).toInt(); } @@ -345,11 +350,6 @@ bool ConfigManager::webaoEnabled() return m_settings->value("Options/webao_enable", false).toBool(); } -int ConfigManager::webaoPort() -{ - return m_settings->value("Options/webao_port", 27017).toInt(); -} - DataTypes::AuthType ConfigManager::authType() { QString l_auth = m_settings->value("Options/auth", "simple").toString().toUpper(); diff --git a/src/config_manager.h b/src/config_manager.h index 9fd4e1f..d8897db 100644 --- a/src/config_manager.h +++ b/src/config_manager.h @@ -53,7 +53,7 @@ class ConfigManager static bool verifyServerConfig(); /** - * @brief Returns the IP the TCP Server binds to. + * @brief Returns the IP the server binds to. * * @return See short description */ @@ -171,13 +171,6 @@ class ConfigManager */ static bool webaoEnabled(); - /** - * @brief Returns the port to listen for webAO connections on. - * - * @return See short description. - */ - static int webaoPort(); - /** * @brief Returns the server's authorization type. * diff --git a/src/main.cpp b/src/main.cpp index 0b6db05..06f24cc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,7 +45,7 @@ int main(int argc, char *argv[]) QCoreApplication::quit(); } else { - server = new Server(ConfigManager::serverPort(), ConfigManager::webaoPort()); + server = new Server(ConfigManager::serverPort()); server->start(); } diff --git a/src/network/network_socket.cpp b/src/network/network_socket.cpp index 758ab6e..a858662 100644 --- a/src/network/network_socket.cpp +++ b/src/network/network_socket.cpp @@ -18,34 +18,19 @@ #include "network/network_socket.h" #include "packet/packet_factory.h" -NetworkSocket::NetworkSocket(QTcpSocket *f_socket, QObject *parent) : - QObject(parent) -{ - m_socket_type = TCP; - m_client_socket.tcp = f_socket; - connect(m_client_socket.tcp, &QTcpSocket::readyRead, - this, &NetworkSocket::readData); - connect(m_client_socket.tcp, &QTcpSocket::disconnected, - this, &NetworkSocket::clientDisconnected); - m_socket_ip = m_client_socket.tcp->peerAddress(); -} - NetworkSocket::NetworkSocket(QWebSocket *f_socket, QObject *parent) : QObject(parent) { - m_socket_type = WS; - m_client_socket.ws = f_socket; - connect(m_client_socket.ws, &QWebSocket::textMessageReceived, - this, &NetworkSocket::ws_readData); - connect(m_client_socket.ws, &QWebSocket::disconnected, - this, &NetworkSocket::clientDisconnected); + m_client_socket = f_socket; + connect(m_client_socket, &QWebSocket::textMessageReceived, this, &NetworkSocket::handleMessage); + connect(m_client_socket, &QWebSocket::disconnected, this, &NetworkSocket::clientDisconnected); - bool l_is_local = (m_client_socket.ws->peerAddress() == QHostAddress::LocalHost) || - (m_client_socket.ws->peerAddress() == QHostAddress::LocalHostIPv6) || - (m_client_socket.ws->peerAddress() == QHostAddress("::ffff:127.0.0.1")); + bool l_is_local = (m_client_socket->peerAddress() == QHostAddress::LocalHost) || + (m_client_socket->peerAddress() == QHostAddress::LocalHostIPv6) || + (m_client_socket->peerAddress() == QHostAddress("::ffff:127.0.0.1")); // TLDR : We check if the header comes trough a proxy/tunnel running locally. // This is to ensure nobody can send those headers from the web. - QNetworkRequest l_request = m_client_socket.ws->request(); + QNetworkRequest l_request = m_client_socket->request(); if (l_request.hasRawHeader("x-forwarded-for") && l_is_local) { m_socket_ip = QHostAddress(QString::fromUtf8(l_request.rawHeader("x-forwarded-for"))); } @@ -56,12 +41,7 @@ NetworkSocket::NetworkSocket(QWebSocket *f_socket, QObject *parent) : NetworkSocket::~NetworkSocket() { - if (m_socket_type == TCP) { - m_client_socket.tcp->deleteLater(); - } - else { - m_client_socket.ws->deleteLater(); - } + m_client_socket->deleteLater(); } QHostAddress NetworkSocket::peerAddress() @@ -69,56 +49,17 @@ QHostAddress NetworkSocket::peerAddress() return m_socket_ip; } -void NetworkSocket::close() -{ - if (m_socket_type == TCP) { - m_client_socket.tcp->deleteLater(); - } - else { - m_client_socket.ws->deleteLater(); - } -} - void NetworkSocket::close(QWebSocketProtocol::CloseCode f_code) { - m_client_socket.ws->close(f_code); + m_client_socket->close(f_code); } -void NetworkSocket::readData() -{ - if (m_client_socket.tcp->bytesAvailable() > 30720) { // Client can send a max of 30KB to the server. - m_client_socket.tcp->close(); - } - - QString l_data = QString::fromUtf8(m_client_socket.tcp->readAll()); - - if (m_is_partial) { - l_data = m_partial_packet + l_data; - } - if (!l_data.endsWith("%")) { - m_is_partial = true; - } - - QStringList l_all_packets = l_data.split("%"); - l_all_packets.removeLast(); // Remove the entry after the last delimiter - - for (const QString &l_single_packet : qAsConst(l_all_packets)) { - AOPacket *l_packet = PacketFactory::createPacket(l_single_packet); - if (!l_packet) { - qDebug() << "Unimplemented packet: " << l_single_packet; - continue; - } - - emit handlePacket(l_packet); - } -} - -void NetworkSocket::ws_readData(QString f_data) +void NetworkSocket::handleMessage(QString f_data) { QString l_data = f_data; if (l_data.toUtf8().size() > 30720) { - m_client_socket.ws->close(QWebSocketProtocol::CloseCodeTooMuchData); + m_client_socket->close(QWebSocketProtocol::CloseCodeTooMuchData); } QStringList l_all_packets = l_data.split("%"); @@ -142,12 +83,5 @@ void NetworkSocket::ws_readData(QString f_data) void NetworkSocket::write(AOPacket *f_packet) { - if (m_socket_type == TCP) { - m_client_socket.tcp->write(f_packet->toUtf8()); - m_client_socket.tcp->flush(); - } - else { - m_client_socket.ws->sendTextMessage(f_packet->toString()); - m_client_socket.ws->flush(); - } + m_client_socket->sendTextMessage(f_packet->toString()); } diff --git a/src/network/network_socket.h b/src/network/network_socket.h index 193fa11..ab8610d 100644 --- a/src/network/network_socket.h +++ b/src/network/network_socket.h @@ -20,7 +20,6 @@ #include #include -#include #include #include "network/aopacket.h" @@ -32,13 +31,6 @@ class NetworkSocket : public QObject Q_OBJECT public: - /** - * @brief Constructor for the network socket class. - * @param QTcpSocket for communication with external AO2-Client - * @param Pointer to the server object. - */ - NetworkSocket(QTcpSocket *f_socket, QObject *parent = nullptr); - /** * @brief Constructor for the network socket class. * @param QWebSocket for communication with external AO2-Client or WebAO clients. @@ -58,17 +50,12 @@ class NetworkSocket : public QObject */ QHostAddress peerAddress(); - /** - * @brief Closes the socket by request of the child AOClient object or the server. - */ - void close(); - /** * @brief Closes the socket by request of the child AOClient object or the server. * * @param The close code to the send to the client. */ - void close(QWebSocketProtocol::CloseCode f_code); + void close(QWebSocketProtocol::CloseCode f_code = QWebSocketProtocol::CloseCodeNormal); /** * @brief Writes data to the network socket. @@ -78,7 +65,6 @@ class NetworkSocket : public QObject void write(AOPacket *f_packet); signals: - /** * @brief handlePacket * @param f_packet @@ -91,34 +77,15 @@ class NetworkSocket : public QObject void clientDisconnected(); private slots: - /** - * @brief Handles the reading and processing of TCP stream data. - * - * @return Decoded AOPacket to be processed by the child AOClient object. - */ - void readData(); - /** * @brief Handles the processing of WebSocket data. * * @return Decoded AOPacket to be processed by the child AOClient object. */ - void ws_readData(QString f_data); + void handleMessage(QString f_data); private: - enum SocketType - { - TCP, - WS - }; - - /** - * @brief Union holding either a TCP- or Websocket. - */ - union { - QTcpSocket *tcp; - QWebSocket *ws; - } m_client_socket; + QWebSocket *m_client_socket; /** * @brief Remote IP of the client. @@ -126,27 +93,6 @@ class NetworkSocket : public QObject * @details In the case of the WebSocket we also check if this has been proxy forwarded. */ QHostAddress m_socket_ip; - - /** - * @brief Defines if the client is a Websocket or TCP client. - */ - SocketType m_socket_type; - - /** - * @brief Filled with part of a packet if said packet could not be read fully from the client's socket. - * - * @details Per AO2's network protocol, a packet is finished with the character `%`. - * - * @see #is_partial - */ - QString m_partial_packet; - - /** - * @brief True when the previous `readAll()` call from the client's socket returned an unfinished packet. - * - * @see #partial_packet - */ - bool m_is_partial; }; #endif diff --git a/src/packet/packet_id.cpp b/src/packet/packet_id.cpp index 30b1bbd..4f42d22 100644 --- a/src/packet/packet_id.cpp +++ b/src/packet/packet_id.cpp @@ -52,6 +52,12 @@ void PacketID::handlePacket(AreaData *area, AOClient &client) const client.m_version.release = 2; client.m_version.major = 10; client.m_version.minor = 0; + + if (!ConfigManager::webaoEnabled()) { + client.sendPacket("BD", {"WebAO is disabled on this server."}); + client.m_socket->close(); + return; + } } if (client.m_version.release != 2) { diff --git a/src/server.cpp b/src/server.cpp index 9eeead7..3227cdb 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -30,16 +30,11 @@ #include "network/network_socket.h" #include "packet/packet_factory.h" -Server::Server(int p_port, int p_ws_port, QObject *parent) : +Server::Server(int p_ws_port, QObject *parent) : QObject(parent), - port(p_port), - ws_port(p_ws_port), + m_port(p_ws_port), m_player_count(0) { - server = new QTcpServer(this); - - connect(server, &QTcpServer::newConnection, this, &Server::clientConnected); - timer = new QTimer(this); db_manager = new DBManager; @@ -71,24 +66,15 @@ void Server::start() if (bind_addr.protocol() != QAbstractSocket::IPv4Protocol && bind_addr.protocol() != QAbstractSocket::IPv6Protocol && bind_addr != QHostAddress::Any) { qDebug() << bind_ip << "is an invalid IP address to listen on! Server not starting, check your config."; } - if (!server->listen(bind_addr, port)) { + + server = new QWebSocketServer("Akashi", QWebSocketServer::NonSecureMode, this); + if (!server->listen(bind_addr, m_port)) { qDebug() << "Server error:" << server->errorString(); } else { - qDebug() << "Server listening on" << port; - } - - // Enable WebAO - if (ConfigManager::webaoEnabled()) { - ws_server = new QWebSocketServer("Akashi", QWebSocketServer::NonSecureMode, this); - if (!ws_server->listen(bind_addr, ConfigManager::webaoPort())) { - qDebug() << "Websocket Server error:" << ws_server->errorString(); - } - else { - connect(ws_server, &QWebSocketServer::newConnection, - this, &Server::ws_clientConnected); - qInfo() << "Websocket Server listening on" << ConfigManager::webaoPort(); - } + connect(server, &QWebSocketServer::newConnection, + this, &Server::clientConnected); + qInfo() << "Server listening on" << server->serverPort(); } // Checks if any Discord webhooks are enabled. @@ -97,7 +83,7 @@ void Server::start() // Construct modern advertiser if enabled in config if (ConfigManager::advertiseServer()) { AdvertiserTimer = new QTimer(this); - ms3_Advertiser = new Advertiser(); + ms3_Advertiser = new Advertiser(server->serverPort()); connect(AdvertiserTimer, &QTimer::timeout, ms3_Advertiser, &Advertiser::msAdvertiseServer); connect(this, &Server::playerCountUpdated, ms3_Advertiser, &Advertiser::updatePlayerCount); @@ -161,98 +147,7 @@ QVector Server::getClients() void Server::clientConnected() { - QTcpSocket *socket = server->nextPendingConnection(); - - // Too many players. Reject connection! - // This also enforces the maximum playercount. - if (m_available_ids.empty()) { - AOPacket *disconnect_reason = PacketFactory::createPacket("BD", {"Maximum playercount has been reached."}); - socket->write(disconnect_reason->toUtf8()); - socket->flush(); - socket->close(); - socket->deleteLater(); - return; - } - - int user_id = m_available_ids.pop(); - // The parent hierachry looks like this : - // QTcpSocket -> NetworkSocket -> AOClient - NetworkSocket *l_socket = new NetworkSocket(socket, socket); - AOClient *client = new AOClient(this, l_socket, l_socket, user_id, music_manager); - m_clients_ids.insert(user_id, client); - m_player_state_observer.registerClient(client); - - int multiclient_count = 1; - bool is_at_multiclient_limit = false; - client->calculateIpid(); - auto ban = db_manager->isIPBanned(client->getIpid()); - bool is_banned = ban.first; - for (AOClient *joined_client : qAsConst(m_clients)) { - if (client->m_remote_ip.isEqual(joined_client->m_remote_ip)) - multiclient_count++; - } - - if (multiclient_count > ConfigManager::multiClientLimit() && !client->m_remote_ip.isLoopback()) - is_at_multiclient_limit = true; - - if (is_banned) { - QString ban_duration; - if (!(ban.second.duration == -2)) { - ban_duration = QDateTime::fromSecsSinceEpoch(ban.second.time).addSecs(ban.second.duration).toString("MM/dd/yyyy, hh:mm"); - } - else { - ban_duration = "The heat death of the universe."; - } - AOPacket *ban_reason = PacketFactory::createPacket("BD", {"Reason: " + ban.second.reason + "\nBan ID: " + QString::number(ban.second.id) + "\nUntil: " + ban_duration}); - socket->write(ban_reason->toUtf8()); - } - if (is_banned || is_at_multiclient_limit) { - socket->flush(); - client->deleteLater(); - socket->close(); - markIDFree(user_id); - return; - } - - QHostAddress l_remote_ip = client->m_remote_ip; - if (l_remote_ip.protocol() == QAbstractSocket::IPv6Protocol) { - l_remote_ip = parseToIPv4(l_remote_ip); - } - - if (isIPBanned(l_remote_ip)) { - QString l_reason = "Your IP has been banned by a moderator."; - AOPacket *l_ban_reason = PacketFactory::createPacket("BD", {l_reason}); - socket->write(l_ban_reason->toUtf8()); - client->deleteLater(); - socket->close(); - markIDFree(user_id); - return; - } - - m_clients.append(client); - connect(l_socket, &NetworkSocket::clientDisconnected, this, [=] { - if (client->hasJoined()) { - decreasePlayerCount(); - } - m_clients.removeAll(client); - client->deleteLater(); - }); - connect(l_socket, &NetworkSocket::handlePacket, client, &AOClient::handlePacket); - - // This is the infamous workaround for - // tsuserver4. It should disable fantacrypt - // completely in any client 2.4.3 or newer - AOPacket *decryptor = PacketFactory::createPacket("decryptor", {"NOENCRYPT"}); - client->sendPacket(decryptor); - hookupAOClient(client); -#ifdef NET_DEBUG - qDebug() << client->m_remote_ip.toString() << "connected"; -#endif -} - -void Server::ws_clientConnected() -{ - QWebSocket *socket = ws_server->nextPendingConnection(); + QWebSocket *socket = server->nextPendingConnection(); NetworkSocket *l_socket = new NetworkSocket(socket, socket); // Too many players. Reject connection! diff --git a/src/server.h b/src/server.h index d9b4a30..443253c 100644 --- a/src/server.h +++ b/src/server.h @@ -25,8 +25,6 @@ #include #include #include -#include -#include #include #include #include @@ -44,7 +42,6 @@ class DBManager; class Discord; class MusicManager; class ULogger; -class WSProxy; /** * @brief The class that represents the actual server as it is. @@ -57,11 +54,10 @@ class Server : public QObject /** * @brief Creates a Server instance. * - * @param p_port The TCP port to listen for connections on. - * @param p_ws_port The WebSocket port to listen for connections on. + * @param p_ws_port The port to listen for connections on. * @param parent Qt-based parent, passed along to inherited constructor from QObject. */ - Server(int p_port, int p_ws_port, QObject *parent = nullptr); + Server(int p_ws_port, QObject *parent = nullptr); /** * @brief Destructor for the Server class. @@ -73,8 +69,7 @@ class Server : public QObject /** * @brief Starts the server. * - * @details Amongst other things, this function starts the listening on the given TCP port, sets up the server - * according to the configuration file, and starts listening on the WebSocket port if it is not `-1`. + * @details Starts listening for incoming connections on the given port. * * Advertising is not done here -- see Advertiser::contactMasterServer() for that. */ @@ -349,14 +344,6 @@ class Server : public QObject */ void clientConnected(); - /** - * @brief Handles a new connection. - * - * @details The function creates an AOClient to represent the user, assigns a user ID to them, and - * checks if the client is banned. - */ - void ws_clientConnected(); - /** * @brief Method to construct and reconstruct Discord Webhook Integration. * @@ -421,22 +408,10 @@ class Server : public QObject void logConnectionAttempt(const QString &f_ip_address, const QString &f_ipid, const QString &f_hwid); private: - /** - * @brief The proxy used for WebSocket connections. - * - * @see WSProxy and WSClient for an explanation as to why this is a thing. - */ - WSProxy *proxy; - - /** - * @brief Listens for incoming TCP connections. - */ - QTcpServer *server; - /** * @brief Listens for incoming websocket connections. */ - QWebSocketServer *ws_server; + QWebSocketServer *server; /** * @brief Handles Discord webhooks. @@ -463,15 +438,10 @@ class Server : public QObject */ MusicManager *music_manager; - /** - * @brief The port through which the server will accept TCP connections. - */ - int port; - /** * @brief The port through which the server will accept WebSocket connections. */ - int ws_port; + int m_port; /** * @brief The collection of all currently connected clients. diff --git a/tests/unittest_config_manager/tst_unittest_config_manager.cpp b/tests/unittest_config_manager/tst_unittest_config_manager.cpp index 93a4fa3..7737c3b 100644 --- a/tests/unittest_config_manager/tst_unittest_config_manager.cpp +++ b/tests/unittest_config_manager/tst_unittest_config_manager.cpp @@ -58,8 +58,6 @@ class tst_ConfigManager : public QObject void webaoEnabled(); - void webaoPort(); - void authType(); void modpass(); @@ -284,10 +282,6 @@ void tst_ConfigManager::webaoEnabled() { } -void tst_ConfigManager::webaoPort() -{ -} - void tst_ConfigManager::authType() { }