#pragma semicolon 1 #define PLUGIN_AUTHOR "jenz" #define g_dLength 400 #define PLUGIN_VERSION "1.0" #pragma newdecls required #include #include #include #include int validate_state [MAXPLAYERS + 1]; Database g_dDatabase; Database g_hDatabase_sourceban; Handle g_hOnReportBanPostForward; bool g_bReportedClientBanAvoiding[MAXPLAYERS + 1]; public Plugin myinfo = { name = "jenz ban detector", author = PLUGIN_AUTHOR, description = "my ban detector maybe catches you", version = PLUGIN_VERSION, url = "www.unloze.com" }; public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { RegPluginLibrary("jenz_ban_detector"); return APLRes_Success; } public void OnPluginStart() { g_hOnReportBanPostForward = CreateGlobalForward("BanDetectorPost", ET_Ignore, Param_Cell, Param_String); if (!g_dDatabase) { Database.Connect(SQL_OnDatabaseConnect, "jenz_ban_detector"); } for (int i = 0; i < MaxClients; i++) { if (IsValidClient(i)) { validate_state[i] = 0; g_bReportedClientBanAvoiding[i] = false; } } CreateTimer(10.0, start_checks, _, TIMER_REPEAT); } public void SQL_addEntry(int client) { char sQuery[g_dLength]; char sSID[MAX_NAME_LENGTH]; char sIP[MAX_NAME_LENGTH]; char sName[MAX_NAME_LENGTH]; GetClientName(client, sName, sizeof(sName)); int size2 = 2 * strlen(sName) + 1; char[] sEscapedName = new char[size2 + 1]; GetClientAuthId(client, AuthId_Steam2, sSID, sizeof(sSID)); g_dDatabase.Escape(sName, sEscapedName, size2 + 1); GetClientIP(client, sIP, sizeof(sIP)); Format(sQuery, sizeof(sQuery), "insert into `ban_detector_steamids` (`steamid`, `name`, `ID`) SELECT '%s', '%s', bd.ID from `ban_detector` bd where bd.ip = '%s' and not exists (select bds.ID from `ban_detector_steamids` bds where bds.ID = bd.ID and bds.steamid = '%s')", sSID, sEscapedName, sIP, sSID); g_dDatabase.Query(SQL_UpdateEntry, sQuery, client, DBPrio_High); } public void SQL_OnDatabaseConnect(Database db, const char[] error, any data) { if(!db || strlen(error)) { LogError("Database error: %s", error); return; } g_dDatabase = db; } public void SQL_OnDatabaseConnect_sb(Database db, const char[] error, any data) { if(!db || strlen(error)) { LogError("Database error: %s", error); return; } g_hDatabase_sourceban = db; } public void SQL_UpdateEntry(Database db, DBResultSet results, const char[] error, int client) { if(!db || strlen(error)) { LogError("Database error: %s", error); return; } if (IsValidClient(client)) { char sQuery[g_dLength]; char sSID[MAX_NAME_LENGTH]; char sIP[MAX_NAME_LENGTH]; GetClientIP(client, sIP, sizeof(sIP)); GetClientAuthId(client, AuthId_Steam2, sSID, sizeof(sSID)); Format(sQuery, sizeof(sQuery), "SELECT bd.fingerprint FROM `ban_detector` bd inner join `ban_detector_steamids` bds on bd.ID = bds.ID where bds.steamid = '%s' or bd.ip = '%s'", sSID, sIP); //PrintToChatAll("sQuery: %s", sQuery); g_dDatabase.Query(SQL_FindFingerPrints, sQuery, client, DBPrio_High); } delete results; } public void SQL_FindFingerPrints(Database db, DBResultSet results, const char[] error, int client) { if (!db || strlen(error)) { LogError("Database error: %s", error); return; } char fingerprint[1024]; while (results.RowCount > 0 && results.FetchRow()) { results.FetchString(0, fingerprint, sizeof(fingerprint)); char sQuery[1024]; char[] sEscapedFingerPrint = new char[1024]; g_dDatabase.Escape(fingerprint, sEscapedFingerPrint, 1024); Format(sQuery, sizeof(sQuery), "select steamid, ip from ban_detector bd inner join ban_detector_steamids bds on bd.ID = bds.ID where fingerprint = '%s'", sEscapedFingerPrint); //PrintToChatAll("sQuery: %s", sQuery); g_dDatabase.Query(SQL_checkSourcebans, sQuery, client, DBPrio_High); if (IsValidClient(client) && g_bReportedClientBanAvoiding[client]) { break; } } delete results; } public void SQL_checkSourcebans(Database db, DBResultSet results, const char[] error, int client) { if (!db || strlen(error)) { LogError("Database error: %s", error); return; } while (results.RowCount > 0 && results.FetchRow()) { char sSID[MAX_NAME_LENGTH]; char sIP[MAX_NAME_LENGTH]; results.FetchString(0, sSID, sizeof(sSID)); results.FetchString(1, sIP, sizeof(sIP)); char sql_statement[512]; //PrintToChatAll(sSID); //PrintToChatAll(sIP); // + 3600 for one hour to accomdate timezone difference Format(sql_statement, sizeof(sql_statement), "select authid, ip from sb_bans where ((ip = '%s' and ip is not null and ip != '') or (authid = '%s' and authid is not null and authid =! '')) and (RemoveType != 'U' or RemoveType is NULL) and (ends > UNIX_TIMESTAMP() + 3600 or ends = created) order by created desc limit 1", sIP, sSID); g_hDatabase_sourceban.Query(sql_select_sb_bans, sql_statement, client, DBPrio_High); if (IsValidClient(client) && g_bReportedClientBanAvoiding[client]) { break; } } delete results; } public void sql_select_sb_bans(Database db, DBResultSet results, const char[] error, int client) { if (!db || strlen(error)) { LogError("Database error: %s", error); return; } if (!IsValidClient(client)) { delete results; return; } if (results.RowCount && results.FetchRow() && IsValidClient(client) && !g_bReportedClientBanAvoiding[client]) { g_bReportedClientBanAvoiding[client] = true; char sSID[MAX_NAME_LENGTH]; char sIP[MAX_NAME_LENGTH]; results.FetchString(0, sSID, sizeof(sSID)); results.FetchString(1, sIP, sizeof(sIP)); Call_StartForward(g_hOnReportBanPostForward); Call_PushCell(client); if (strlen(sSID) == 0) { // use IP instead if no steamID Call_PushString(sIP); } else { //found steamID Call_PushString(sSID); } Call_Finish(); // TODO in the future: just add a sourceban ban here on the client in the future for auto bans. should just be 30 minute bans } delete results; } public Action start_checks(Handle hTimer) { if (!g_dDatabase) { Database.Connect(SQL_OnDatabaseConnect, "jenz_ban_detector"); } else { if (!g_hDatabase_sourceban) { Database.Connect(SQL_OnDatabaseConnect_sb, "sourcebans"); } else { for (int i = 0; i < MaxClients; i++) { if (IsValidClient(i) && validate_state[i] == 0) { validate_state[i] = -1; SQL_addEntry(i); } } } } } public void OnClientDisconnect(int client) { validate_state[client] = -1; g_bReportedClientBanAvoiding[client] = false; } public void OnClientPostAdminCheck(int client) { validate_state[client] = -1; CreateTimer(10.0, make_db_entry, client); g_bReportedClientBanAvoiding[client] = false; } public Action make_db_entry(Handle hTimer, int client) { if (IsValidClient(client)) { validate_state[client] = 0; } } stock bool IsValidClient(int client) { if (client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client) && !IsFakeClient(client)) return true; return false; }