#pragma semicolon 1 #define PLUGIN_AUTHOR "jenz" #define g_dLength 400 #define PLUGIN_VERSION "1.1" #pragma newdecls required #include #include #include #include #include Database g_dDatabase; int g_disable_html_motd[MAXPLAYERS + 1]; Handle g_hOnReportBanPostForward; 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"); } } public void OnMapStart() { if (!g_dDatabase) { Database.Connect(SQL_OnDatabaseConnect, "jenz_ban_detector"); } } 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 OnClientDisconnect(int client) { g_disable_html_motd[client] = 0; } public void OnClientPostAdminCheck(int client) { g_disable_html_motd[client] = 0; if (!IsFakeClient(client) && !IsClientSourceTV(client)) { SQL_addEntry(client); } } 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 ignore into `ban_detector` (`steamid`, `ip`, `name`) SELECT '%s', '%s','%s'", sSID, sIP,sEscapedName); Format(sQuery, sizeof(sQuery), "insert ignore into `ban_detector` (`steamid`, `ip`, `name`) SELECT '%s', '%s','%s'", sSID, sIP,sEscapedName); g_dDatabase.Query(SQL_callback_insert_ignore, sQuery, GetClientSerial(client), DBPrio_Low); } public void SQL_callback_insert_ignore(Database db, DBResultSet results, const char[] error, int Serial) { if(!db || strlen(error)) { LogError("Database error: %s", error); delete results; return; } delete results; int client; if ((client = GetClientFromSerial(Serial)) == 0) { return; } if (IsValidClient(client)) { QueryClientConVar(client, "cl_disablehtmlmotd", CvarQueryFinished); } } public void CvarQueryFinished(QueryCookie cookie, int client, ConVarQueryResult res, const char[] sCvarName, const char[] sCvarVal) { if (res != ConVarQuery_Okay) { return; } int disable_html_motd = StringToInt(sCvarVal); if (IsValidClient(client)) { g_disable_html_motd[client] = disable_html_motd; char sQuery[g_dLength]; char sSID[MAX_NAME_LENGTH]; GetClientAuthId(client, AuthId_Steam2, sSID, sizeof(sSID)); Format(sQuery, sizeof(sQuery), "UPDATE ban_detector SET last_connect = now(), disable_html_motd = '%i' where steamid = '%s'", disable_html_motd, sSID); g_dDatabase.Query(SQL_callback_update, sQuery, GetClientSerial(client), DBPrio_Low); } } public void SQL_callback_update(Database db, DBResultSet results, const char[] error, int Serial) { if(!db || strlen(error)) { LogError("Database error: %s", error); delete results; return; } delete results; int client; if ((client = GetClientFromSerial(Serial)) == 0) { return; } if (IsValidClient(client) && !g_disable_html_motd[client]) { CreateTimer(7.0, re_open_motd, Serial); } } public Action re_open_motd(Handle hTimer, int Serial) { int client; if ((client = GetClientFromSerial(Serial)) == 0) { return Plugin_Handled; } if (IsValidClient(client)) { //reopening the menu again after info got stored. it still generates the same fingerprint despite not showing the client any menu. Handle panel = CreateKeyValues("data"); KvSetString(panel, "title", ""); KvSetString(panel, "type", "2"); KvSetString(panel, "msg", "https://unloze.com/motd/CSS_ZE_MOTD.html"); ShowVGUIPanel(client, "info", panel, false); CloseHandle(panel); CreateTimer(5.0, SQL_Select_fingerprints, Serial); } return Plugin_Handled; } public Action SQL_Select_fingerprints(Handle hTimer, int Serial) { int client; if ((client = GetClientFromSerial(Serial)) == 0) { return Plugin_Handled; } 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)); //steamID is unique but fingerprint might occur multiple times. Format(sQuery, sizeof(sQuery), "select steamid from ban_detector.ban_detector where fingerprint = (select fingerprint from ban_detector.ban_detector where steamid = '%s' and fingerprint is not null and length(fingerprint) > 5)", sSID); //PrintToChatAll("sQuery: %s", sQuery); g_dDatabase.Query(SQL_FindFingerPrints, sQuery, Serial, DBPrio_Low); } return Plugin_Handled; } public void SQL_FindFingerPrints(Database db, DBResultSet results, const char[] error, int Serial) { if (!db || strlen(error)) { LogError("Database error: %s", error); delete results; return; } int client; if ((client = GetClientFromSerial(Serial)) == 0) { delete results; return; } if (!IsValidClient(client)) { delete results; return; } //god knows how big this might need to be char sQuery[4344]; Format(sQuery, sizeof(sQuery), "select authid from unloze_sourceban.sb_bans where authid in ("); //cant rely on IP cause several chinese players share VPN and end up with same IP despite clearly being different people. //now instead of fingerprint returns all steam IDs related to the specific fingerprint. bool first = true; while (results.RowCount > 0 && results.FetchRow()) { char steamid[64]; results.FetchString(0, steamid, sizeof(steamid)); if (first) { Format(steamid, sizeof(steamid), "'%s'", steamid); StrCat(sQuery, sizeof(sQuery), steamid); first = false; } else { Format(steamid, sizeof(steamid), ",'%s'", steamid); StrCat(sQuery, sizeof(sQuery), steamid); } } StrCat(sQuery, sizeof(sQuery), ") and (RemoveType != 'U' or RemoveType is NULL) and (ends > UNIX_TIMESTAMP() + 3600 or ends = created) order by created desc limit 1"); delete results; if (!first) { g_dDatabase.Query(sql_select_sb_bans, sQuery, Serial, DBPrio_Low); } } public void sql_select_sb_bans(Database db, DBResultSet results, const char[] error, int Serial) { if (!db || strlen(error)) { delete results; LogError("Database error 2: %s", error); return; } int client; if ((client = GetClientFromSerial(Serial)) == 0) { delete results; return; } if (!IsValidClient(client)) { delete results; return; } //if we get a result its a steam ID that still has an active ban. the steam ID that is banned is confirmed to have the same fingerprint as the connecting client. if (results.RowCount && results.FetchRow()) { char sSID[MAX_NAME_LENGTH]; results.FetchString(0, sSID, sizeof(sSID)); Call_StartForward(g_hOnReportBanPostForward); Call_PushCell(client); Call_PushString(sSID); Call_Finish(); //bans need to be over 1 hour long for getting detected char message[1024]; Format(message, sizeof(message), "Ban avoiding (Jenz ban detector). SteamID avoiding ban: %s", sSID); //instead of permaban now just banning the alt account for circa 3 months. SBPP_BanPlayer(0, client, 131487, message); //first parameter is client doing the ban, should be 0 so its console. //second parameter is the target getting banned //third parameter was the actual timevalue. } delete results; } stock bool IsValidClient(int client) { if (client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client) && !IsFakeClient(client)) return true; return false; }