diff --git a/bin/config_sample/iprange_bans.txt b/bin/config_sample/iprange_bans.txt new file mode 100644 index 0000000..c04f87f --- /dev/null +++ b/bin/config_sample/iprange_bans.txt @@ -0,0 +1,10 @@ +# Test nets +192.0.2.0/24 +198.51.100.0/24 +192.88.99.0/24 +203.0.113.0/24 + +# IPv6 +2001:0000:/32 +2001:db8::/32 +2002::/16 \ No newline at end of file diff --git a/core/include/config_manager.h b/core/include/config_manager.h index 451d9f1..b228a68 100644 --- a/core/include/config_manager.h +++ b/core/include/config_manager.h @@ -30,6 +30,7 @@ #include #include #include +#include //JSON loading requirements #include @@ -112,6 +113,13 @@ class ConfigManager { */ static QStringList rawAreaNames(); + /** + * @brief Returns a list of the IPrange bans. + * + * @return See short description. + */ + static QStringList iprangeBans(); + /** * @brief Returns true if the server should advertise to the master server. * diff --git a/core/include/server.h b/core/include/server.h index a6c7e96..26bccbf 100644 --- a/core/include/server.h +++ b/core/include/server.h @@ -179,6 +179,11 @@ class Server : public QObject { */ void updateHTTPAdvertiserConfig(); + /** + * @brief Checks if an IP is in a subnet of the IPBanlist. + **/ + bool isIPBanned(QHostAddress f_remote_IP); + /** * @brief Getter for an area specific buffer from the logger. */ @@ -225,6 +230,11 @@ class Server : public QObject { */ QStringList m_backgrounds; + /** + * @brief Collection of all IPs that are banned. + */ + QStringList m_ipban_list; + /** * @brief The database manager on the server, used to store users' bans and authorisation details. */ @@ -243,6 +253,11 @@ class Server : public QObject { */ QTimer next_message_timer; + /** + * @brief Attempts to parse a IPv6 mapped IPv4 to an IPv4. + */ + QHostAddress parseToIPv4(QHostAddress f_remote_ip); + /** * @brief If false, IC messages will be rejected. */ diff --git a/core/src/commands/moderation.cpp b/core/src/commands/moderation.cpp index bbd4bb4..45c41d3 100644 --- a/core/src/commands/moderation.cpp +++ b/core/src/commands/moderation.cpp @@ -474,6 +474,7 @@ void AOClient::cmdReload(int argc, QStringList argv) server->updateHTTPAdvertiserConfig(); server->handleDiscordIntegration(); server->m_music_list = ConfigManager::musiclist(); + server->m_ipban_list = ConfigManager::iprangeBans(); sendServerMessage("Reloaded configurations"); } diff --git a/core/src/config_manager.cpp b/core/src/config_manager.cpp index 4945dfd..601506e 100644 --- a/core/src/config_manager.cpp +++ b/core/src/config_manager.cpp @@ -236,6 +236,18 @@ QStringList ConfigManager::rawAreaNames() return m_areas->childGroups(); } +QStringList ConfigManager::iprangeBans() +{ + QStringList l_iprange_bans; + QFile l_file("config/iprange_bans.txt"); + l_file.open(QIODevice::ReadOnly | QIODevice::Text); + while (!(l_file.atEnd())) { + l_iprange_bans.append(l_file.readLine().trimmed()); + } + l_file.close(); + return l_iprange_bans; +} + void ConfigManager::reloadSettings() { m_settings->sync(); diff --git a/core/src/packets.cpp b/core/src/packets.cpp index 52c37c3..b668ff5 100644 --- a/core/src/packets.cpp +++ b/core/src/packets.cpp @@ -398,6 +398,20 @@ void AOClient::pktWebSocketIp(AreaData* area, int argc, QStringList argv, AOPack qDebug() << "ws ip set to" << argv[0]; #endif m_remote_ip = QHostAddress(argv[0]); + + QHostAddress l_remote_ip = m_remote_ip; + if (l_remote_ip.protocol() == QAbstractSocket::IPv6Protocol) { + l_remote_ip = server->parseToIPv4(l_remote_ip); + } + + if (server->isIPBanned(l_remote_ip)){ + QString l_reason = "Your IP has been banned by a moderator."; + AOPacket l_ban_reason("BD", {l_reason}); + m_socket->write(l_ban_reason.toUtf8()); + m_socket->close(); + return; + } + calculateIpid(); auto l_ban = server->db_manager->isIPBanned(m_ipid); if (l_ban.first) { diff --git a/core/src/server.cpp b/core/src/server.cpp index da0c521..1f1c53f 100644 --- a/core/src/server.cpp +++ b/core/src/server.cpp @@ -99,6 +99,9 @@ void Server::start() //Loads the command help information. This is not stored inside the server. ConfigManager::loadCommandHelp(); + + //Get IP bans + m_ipban_list = ConfigManager::iprangeBans(); //Rate-Limiter for IC-Chat connect(&next_message_timer, SIGNAL(timeout()), this, SLOT(allowMessage())); @@ -145,6 +148,20 @@ void Server::clientConnected() 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("BD", {l_reason}); + socket->write(l_ban_reason.toUtf8()); + client->deleteLater(); + socket->close(); + return; + } + m_clients.append(client); connect(socket, &QTcpSocket::disconnected, client, &AOClient::clientDisconnected); @@ -200,6 +217,17 @@ QStringList Server::getCursedCharsTaken(AOClient* client, QStringList chars_take return chars_taken_cursed; } +QHostAddress Server::parseToIPv4(QHostAddress f_remote_ip) +{ + bool l_ok; + QHostAddress l_remote_ip = f_remote_ip; + QHostAddress l_temp_remote_ip = QHostAddress(f_remote_ip.toIPv4Address(&l_ok)); + if (l_ok) { + l_remote_ip = l_temp_remote_ip; + } + return l_remote_ip; +} + void Server::broadcast(AOPacket packet, int area_index) { for (AOClient* client : qAsConst(m_clients)) { @@ -357,6 +385,18 @@ void Server::hookupLogger(AOClient* client) logger, &ULogger::logModcall); } +bool Server::isIPBanned(QHostAddress f_remote_IP) +{ + bool l_match_found = false; + for(const QString &l_ipban : qAsConst(m_ipban_list)) { + if (f_remote_IP.isInSubnet(QHostAddress::parseSubnet(l_ipban))) { + l_match_found = true; + break; + } + } + return l_match_found; +} + Server::~Server() { for (AOClient* client : qAsConst(m_clients)) {