Add IPv6 ban table

This commit is contained in:
Benau 2019-10-25 12:44:50 +08:00
parent 0217eaf173
commit 04dd5a8bfb
4 changed files with 148 additions and 4 deletions

View File

@ -159,9 +159,12 @@ The current server configuration xml looks like this:
<!-- Specified in millisecond for maximum time waiting in sqlite3_busy_handler. You may need a higher value if your database is shared by many servers or having a slow hard disk. --> <!-- Specified in millisecond for maximum time waiting in sqlite3_busy_handler. You may need a higher value if your database is shared by many servers or having a slow hard disk. -->
<database-timeout value="1000" /> <database-timeout value="1000" />
<!-- Ip ban list table name, you need to create the table first, see NETWORKING.md for details, empty to disable. This table can be shared for all servers if you use the same name. STK can auto kick active peer from ban list (update per minute) which allows live kicking peer by inserting record to database. --> <!-- IPv4 ban list table name, you need to create the table first, see NETWORKING.md for details, empty to disable. This table can be shared for all servers if you use the same name. STK can auto kick active peer from ban list (update per minute) whichallows live kicking peer by inserting record to database. -->
<ip-ban-table value="ip_ban" /> <ip-ban-table value="ip_ban" />
<!-- IPv6 ban list table name, you need to create the table first, see NETWORKING.md for details, empty to disable. This table can be shared for all servers if you use the same name. STK can auto kick active peer from ban list (update per minute) whichallows live kicking peer by inserting record to database. -->
<ipv6-ban-table value="ipv6_ban" />
<!-- Online ID ban list table name, you need to create the table first, see NETWORKING.md for details, empty to disable. This table can be shared for all servers if you use the same name. STK can auto kick active peer from ban list (update per minute) which allows live kicking peer by inserting record to database. --> <!-- Online ID ban list table name, you need to create the table first, see NETWORKING.md for details, empty to disable. This table can be shared for all servers if you use the same name. STK can auto kick active peer from ban list (update per minute) which allows live kicking peer by inserting record to database. -->
<online-id-ban-table value="online_id_ban" /> <online-id-ban-table value="online_id_ban" />
@ -223,7 +226,7 @@ You have the best gaming experience when choosing server having all players less
Currently STK uses sqlite (if building with sqlite3 on) for server management with the following functions at the moment: Currently STK uses sqlite (if building with sqlite3 on) for server management with the following functions at the moment:
1. Server statistics 1. Server statistics
2. IPv4 / online ID ban list 2. IP / online ID ban list
3. Player reports 3. Player reports
4. IPv4 geolocation 4. IPv4 geolocation
@ -286,6 +289,17 @@ CREATE TABLE ip_ban
last_trigger TIMESTAMP NULL DEFAULT NULL -- Latest time this banning entry was triggered last_trigger TIMESTAMP NULL DEFAULT NULL -- Latest time this banning entry was triggered
); );
CREATE TABLE ipv6_ban
(
ipv6_cidr TEXT NOT NULL UNIQUE, -- IPv6 CIDR range for banning (for example 2001::/64), use /128 for a specific ip
starting_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- Starting time of this banning entry to be effective
expired_days REAL NULL DEFAULT NULL, -- Days for this banning to be expired, use NULL for a permanent ban
reason TEXT NOT NULL DEFAULT '', -- Banned reason shown in user stk menu, can be empty
description TEXT NOT NULL DEFAULT '', -- Private description for server admin
trigger_count INTEGER UNSIGNED NOT NULL DEFAULT 0, -- Number of banning triggered by this ban entry
last_trigger TIMESTAMP NULL DEFAULT NULL -- Latest time this banning entry was triggered
);
CREATE TABLE online_id_ban CREATE TABLE online_id_ban
( (
online_id INTEGER UNSIGNED NOT NULL UNIQUE, -- Online id from STK addons database for banning online_id INTEGER UNSIGNED NOT NULL UNIQUE, -- Online id from STK addons database for banning

View File

@ -218,6 +218,7 @@ void ServerLobby::initDatabase()
m_last_poll_db_time = StkTime::getMonoTimeMs(); m_last_poll_db_time = StkTime::getMonoTimeMs();
m_db = NULL; m_db = NULL;
m_ip_ban_table_exists = false; m_ip_ban_table_exists = false;
m_ipv6_ban_table_exists = false;
m_online_id_ban_table_exists = false; m_online_id_ban_table_exists = false;
m_ip_geolocation_table_exists = false; m_ip_geolocation_table_exists = false;
if (!ServerConfig::m_sql_management) if (!ServerConfig::m_sql_management)
@ -248,6 +249,7 @@ void ServerLobby::initDatabase()
sqlite3_create_function(m_db, "insideIPv6CIDR", 2, SQLITE_UTF8, NULL, sqlite3_create_function(m_db, "insideIPv6CIDR", 2, SQLITE_UTF8, NULL,
&insideIPv6CIDRSQL, NULL, NULL); &insideIPv6CIDRSQL, NULL, NULL);
checkTableExists(ServerConfig::m_ip_ban_table, m_ip_ban_table_exists); checkTableExists(ServerConfig::m_ip_ban_table, m_ip_ban_table_exists);
checkTableExists(ServerConfig::m_ipv6_ban_table, m_ipv6_ban_table_exists);
checkTableExists(ServerConfig::m_online_id_ban_table, checkTableExists(ServerConfig::m_online_id_ban_table,
m_online_id_ban_table_exists); m_online_id_ban_table_exists);
checkTableExists(ServerConfig::m_player_reports_table, checkTableExists(ServerConfig::m_player_reports_table,
@ -830,6 +832,40 @@ void ServerLobby::pollDatabase()
}, &peers, NULL); }, &peers, NULL);
} }
if (m_ipv6_ban_table_exists)
{
std::string query =
"SELECT ipv6_cidr, reason, description FROM ";
query += ServerConfig::m_ipv6_ban_table;
query += " WHERE datetime('now') > datetime(starting_time) AND "
"(expired_days is NULL OR datetime"
"(starting_time, '+'||expired_days||' days') > datetime('now'));";
auto peers = STKHost::get()->getPeers();
sqlite3_exec(m_db, query.c_str(),
[](void* ptr, int count, char** data, char** columns)
{
std::vector<std::shared_ptr<STKPeer> >* peers_ptr =
(std::vector<std::shared_ptr<STKPeer> >*)ptr;
for (std::shared_ptr<STKPeer>& p : *peers_ptr)
{
// IPv6 ban list atm
if (p->isAIPeer() || p->getIPV6Address().empty())
continue;
char* ipv6_cidr = data[0];
if (insideIPv6CIDR(ipv6_cidr,
p->getIPV6Address().c_str()) == 1)
{
Log::info("ServerLobby",
"Kick %s, reason: %s, description: %s",
p->getIPV6Address().c_str(), data[1], data[2]);
p->kick();
}
}
return 0;
}, &peers, NULL);
}
if (m_online_id_ban_table_exists) if (m_online_id_ban_table_exists)
{ {
std::string query = "SELECT online_id, reason, description FROM "; std::string query = "SELECT online_id, reason, description FROM ";
@ -3008,6 +3044,10 @@ void ServerLobby::connectionRequested(Event* event)
if (peer->isDisconnected()) if (peer->isDisconnected())
return; return;
testBannedForIPv6(peer.get());
if (peer->isDisconnected())
return;
if (online_id != 0) if (online_id != 0)
testBannedForOnlineId(peer.get(), online_id); testBannedForOnlineId(peer.get(), online_id);
// Will be disconnected if banned by online id // Will be disconnected if banned by online id
@ -4117,7 +4157,7 @@ void ServerLobby::testBannedForIP(STKPeer* peer) const
if (!m_db || !m_ip_ban_table_exists) if (!m_db || !m_ip_ban_table_exists)
return; return;
// We only test for IPv4 atm // Test for IPv4
if (!peer->getIPV6Address().empty()) if (!peer->getIPV6Address().empty())
return; return;
@ -4177,6 +4217,84 @@ void ServerLobby::testBannedForIP(STKPeer* peer) const
#endif #endif
} // testBannedForIP } // testBannedForIP
//-----------------------------------------------------------------------------
void ServerLobby::testBannedForIPv6(STKPeer* peer) const
{
#ifdef ENABLE_SQLITE3
if (!m_db || !m_ipv6_ban_table_exists)
return;
// Test for IPv6
if (peer->getIPV6Address().empty())
return;
int row_id = -1;
std::string ipv6_cidr;
std::string query = StringUtils::insertValues(
"SELECT rowid, ipv6_cidr, reason, description FROM %s "
"WHERE insideIPv6CIDR(ipv6_cidr, ?) = 1 "
"AND datetime('now') > datetime(starting_time) AND "
"(expired_days is NULL OR datetime"
"(starting_time, '+'||expired_days||' days') > datetime('now')) "
"LIMIT 1;",
ServerConfig::m_ipv6_ban_table.c_str());
sqlite3_stmt* stmt = NULL;
int ret = sqlite3_prepare_v2(m_db, query.c_str(), -1, &stmt, 0);
if (ret == SQLITE_OK)
{
if (sqlite3_bind_text(stmt, 1, peer->getIPV6Address().c_str(),
-1, SQLITE_TRANSIENT) != SQLITE_OK)
{
Log::error("ServerLobby", "Error binding ipv6 addr for query: %s",
sqlite3_errmsg(m_db));
}
ret = sqlite3_step(stmt);
if (ret == SQLITE_ROW)
{
row_id = sqlite3_column_int(stmt, 0);
ipv6_cidr = (char*)sqlite3_column_text(stmt, 1);
const char* reason = (char*)sqlite3_column_text(stmt, 2);
const char* desc = (char*)sqlite3_column_text(stmt, 3);
Log::info("ServerLobby", "%s banned by IP: %s "
"(rowid: %d, description: %s).",
peer->getRealAddress().c_str(), reason, row_id, desc);
kickPlayerWithReason(peer, reason);
}
ret = sqlite3_finalize(stmt);
if (ret != SQLITE_OK)
{
Log::error("ServerLobby",
"Error finalize database for query %s: %s",
query.c_str(), sqlite3_errmsg(m_db));
}
}
else
{
Log::error("ServerLobby", "Error preparing database for query %s: %s",
query.c_str(), sqlite3_errmsg(m_db));
return;
}
if (row_id != -1)
{
query = StringUtils::insertValues(
"UPDATE %s SET trigger_count = trigger_count + 1, "
"last_trigger = datetime('now') "
"WHERE ipv6_cidr = ?;", ServerConfig::m_ipv6_ban_table.c_str());
easySQLQuery(query, [ipv6_cidr](sqlite3_stmt* stmt)
{
if (sqlite3_bind_text(stmt, 1, ipv6_cidr.c_str(),
-1, SQLITE_TRANSIENT) != SQLITE_OK)
{
Log::error("easySQLQuery", "Failed to bind %s.",
ipv6_cidr.c_str());
}
});
}
#endif
} // testBannedForIPv6
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void ServerLobby::testBannedForOnlineId(STKPeer* peer, void ServerLobby::testBannedForOnlineId(STKPeer* peer,
uint32_t online_id) const uint32_t online_id) const

View File

@ -80,6 +80,8 @@ private:
bool m_ip_ban_table_exists; bool m_ip_ban_table_exists;
bool m_ipv6_ban_table_exists;
bool m_online_id_ban_table_exists; bool m_online_id_ban_table_exists;
bool m_ip_geolocation_table_exists; bool m_ip_geolocation_table_exists;
@ -325,6 +327,7 @@ private:
void clientSelectingAssetsWantsToBackLobby(Event* event); void clientSelectingAssetsWantsToBackLobby(Event* event);
void kickPlayerWithReason(STKPeer* peer, const char* reason) const; void kickPlayerWithReason(STKPeer* peer, const char* reason) const;
void testBannedForIP(STKPeer* peer) const; void testBannedForIP(STKPeer* peer) const;
void testBannedForIPv6(STKPeer* peer) const;
void testBannedForOnlineId(STKPeer* peer, uint32_t online_id) const; void testBannedForOnlineId(STKPeer* peer, uint32_t online_id) const;
void writeDisconnectInfoTable(STKPeer* peer); void writeDisconnectInfoTable(STKPeer* peer);
void writePlayerReport(Event* event); void writePlayerReport(Event* event);

View File

@ -351,7 +351,16 @@ namespace ServerConfig
SERVER_CFG_PREFIX StringServerConfigParam m_ip_ban_table SERVER_CFG_PREFIX StringServerConfigParam m_ip_ban_table
SERVER_CFG_DEFAULT(StringServerConfigParam("ip_ban", SERVER_CFG_DEFAULT(StringServerConfigParam("ip_ban",
"ip-ban-table", "ip-ban-table",
"Ip ban list table name, you need to create the table first, see " "IPv4 ban list table name, you need to create the table first, see "
"NETWORKING.md for details, empty to disable. "
"This table can be shared for all servers if you use the same name. "
"STK can auto kick active peer from ban list (update per minute) which"
"allows live kicking peer by inserting record to database."));
SERVER_CFG_PREFIX StringServerConfigParam m_ipv6_ban_table
SERVER_CFG_DEFAULT(StringServerConfigParam("ipv6_ban",
"ipv6-ban-table",
"IPv6 ban list table name, you need to create the table first, see "
"NETWORKING.md for details, empty to disable. " "NETWORKING.md for details, empty to disable. "
"This table can be shared for all servers if you use the same name. " "This table can be shared for all servers if you use the same name. "
"STK can auto kick active peer from ban list (update per minute) which" "STK can auto kick active peer from ban list (update per minute) which"