From 03034e4aa0c6b010a55ade72e67c30cdd76fda6c Mon Sep 17 00:00:00 2001 From: Ayush Saini <36878972+imayushsaini@users.noreply.github.com> Date: Fri, 17 Jun 2022 01:58:59 +0530 Subject: [PATCH] saving IP and device id in user profile , request-flood mitigation --- dist/ba_data/python/ba/_hooks.py | 17 +- dist/ba_root/mods/custom_hooks.py | 4 +- dist/ba_root/mods/tools/servercheck.py | 515 ++++++++++++------------- 3 files changed, 261 insertions(+), 275 deletions(-) diff --git a/dist/ba_data/python/ba/_hooks.py b/dist/ba_data/python/ba/_hooks.py index f649f0a..a3534cb 100644 --- a/dist/ba_data/python/ba/_hooks.py +++ b/dist/ba_data/python/ba/_hooks.py @@ -337,30 +337,29 @@ def filter_chat_message(msg: str, client_id: int) -> Optional[str]: return chooks.filter_chat_message(msg,client_id) def on_client_request(ip): - print("player requesting to join from ip = " + ip) + chooks.on_join_request(ip) + def kick_vote_started(by:str,to:str) -> None: """ get account ids of who started kick vote for whom , do what ever u want logging to files , whatever. """ - print(by+">"+to) + chooks.kick_vote_started(by,to) def on_kicked(account_id:str) -> None: - pass - # print(account_id+" kicked ...sad") + chooks.on_kicked(account_id) def on_kick_vote_end() -> None: - pass - # print("kick vote end") + chooks.on_kick_vote_end() def on_player_join(pb_id:str)-> None: + # not integrated yet pass - # print(pb_id+" joined python layer") def on_player_leave(pb_id:str)-> None: + # not integrated yet pass - # - print(pb_id+" left python layer") + def local_chat_message(msg: str) -> None: if (_ba.app.ui.party_window is not None diff --git a/dist/ba_root/mods/custom_hooks.py b/dist/ba_root/mods/custom_hooks.py index 745253e..7a8f973 100644 --- a/dist/ba_root/mods/custom_hooks.py +++ b/dist/ba_root/mods/custom_hooks.py @@ -213,5 +213,5 @@ def on_kick_vote_end(): logger.log("Kick vote End") -_hooks.kick_vote_started = kick_vote_started -_hooks.on_kicked = on_kicked +def on_join_request(ip): + servercheck.on_join_request(ip) diff --git a/dist/ba_root/mods/tools/servercheck.py b/dist/ba_root/mods/tools/servercheck.py index 49df4e6..d49849e 100644 --- a/dist/ba_root/mods/tools/servercheck.py +++ b/dist/ba_root/mods/tools/servercheck.py @@ -18,204 +18,182 @@ import _thread from tools import logger from features import profanity -# class ServerChecker: -# def __init__(): -# run() - -# def run(self): -# clients=roset.players -# # check if some one joined the party -# for client in clients: -# if cleint.account_id not in serverdata.currentclients: -# self.playerjoined(client) -# # check if some one left the party -# clients_id=[client.account_id for client in clients] -# for player in serverdata.currentclients: -# if player not in clients_id: -# self.playerleft(player) - - -# def playerjoined(self,client): -# if client.account_id in serverdata.cachedclients: -# serevrdata.currentclients[client_account_id]=serverdata.cachedclients[id] - -# playerData=pdata.get_info(client.account_id) -# playerData["lastjoin"]=time.time() -# if playerData ==None: -# self.registernewplayer(cleint) -# else if playerData['isBan']: -# _ba.disconnect_client(client.client_id,9999) -# else: -# serverData.currentclients[client_account_id]=playerData - - -# def playerleft(self,player): -# serverdata.cachedclients[player]=serverdata.currentclients[player] - -# serverdata.currentclients.remove(player) - -# timeplayed=time.time()-serverdata.currentclients[player]['lastjoin'] -# serverdata.cachedclients[player]["totaltimeplayed"]+=timeplayed - -# pdata.update_profile(serverdata.cachedclients[player]) settings = setting.get_settings_data() + class checkserver(object): - def start(self): - self.players=[] - - self.t1=ba.timer(1, ba.Call(self.check),repeat=True) + def start(self): + self.players = [] - def check(self): - newPlayers=[] - for ros in _ba.get_game_roster(): - - newPlayers.append(ros['account_id']) - if ros['account_id'] not in self.players and ros['client_id'] !=-1: - d_str=ros['display_string'] - d_str2=profanity.censor(d_str) - try: - logger.log(d_str+"||"+ros["account_id"]+"|| joined server","playerjoin") - except: - pass - if d_str2!=d_str: - _ba.screenmessage("Profanity in Id , change your ID and join back",color=(1,0,0),transient=True,clients=[ros['client_id']]) - try: - logger.log(d_str+"||"+ros["account_id"]+"|| kicked by profanity check","sys") - except: - pass - _ba.disconnect_client(ros['client_id'],1) - - return - if settings["whitelist"] and ros["account_id"]!=None: - if ros["account_id"] not in pdata.CacheData.whitelist: - _ba.screenmessage("Not in whitelist,contact admin",color=(1,0,0),transient=True,clients=[ros['client_id']]) - logger.log(d_str+"||"+ros["account_id"]+" | kicked > not in whitelist") - _ba.disconnect_client(ros['client_id']) - - - return + self.t1 = ba.timer(1, ba.Call(self.check), repeat=True) - if ros['account_id'] != None: - if ros['account_id'] in serverdata.clients: - on_player_join_server(ros['account_id'],serverdata.clients[ros['account_id']]) - else: - LoadProfile(ros['account_id']).start() - - self.players=newPlayers + def check(self): + newPlayers = [] + for ros in _ba.get_game_roster(): + + newPlayers.append(ros['account_id']) + if ros['account_id'] not in self.players and ros[ + 'client_id'] != -1: + d_str = ros['display_string'] + d_str2 = profanity.censor(d_str) + try: + logger.log( + d_str + "||" + ros["account_id"] + "|| joined server", + "playerjoin") + except: + pass + if d_str2 != d_str: + _ba.screenmessage( + "Profanity in Id , change your ID and join back", + color=(1, 0, 0), transient=True, + clients=[ros['client_id']]) + try: + logger.log(d_str + "||" + ros[ + "account_id"] + "|| kicked by profanity check", + "sys") + except: + pass + _ba.disconnect_client(ros['client_id'], 1) + + return + if settings["whitelist"] and ros["account_id"] != None: + if ros["account_id"] not in pdata.CacheData.whitelist: + _ba.screenmessage("Not in whitelist,contact admin", + color=(1, 0, 0), transient=True, + clients=[ros['client_id']]) + logger.log(d_str + "||" + ros[ + "account_id"] + " | kicked > not in whitelist") + _ba.disconnect_client(ros['client_id']) + + return + + if ros['account_id'] != None: + if ros['account_id'] in serverdata.clients: + on_player_join_server(ros['account_id'], + serverdata.clients[ + ros['account_id']]) + else: + LoadProfile(ros['account_id']).start() + + self.players = newPlayers +def on_player_join_server(pbid, player_data): + now = time.time() + # player_data=pdata.get_info(pbid) + clid = 113 + for ros in _ba.get_game_roster(): + if ros["account_id"] == pbid: + clid = ros["client_id"] + if pbid in serverdata.clients: + rejoinCount = serverdata.clients[pbid]["rejoincount"] + spamCount = serverdata.clients[pbid]["spamCount"] + if now - serverdata.clients[pbid]["lastJoin"] < 15: + rejoinCount += 1 + if rejoinCount > 2: + _ba.screenmessage("Joining too fast , slow down dude", + color=(1, 0, 1), transient=True, + clients=[clid]) + logger.log(pbid + "|| kicked for joining too fast") + _ba.disconnect_client(clid) + + _thread.start_new_thread(reportSpam, (pbid,)) + + return + else: + rejoinCount = 0 + + serverdata.clients[pbid]["rejoincount"] = rejoinCount + serverdata.clients[pbid]["lastJoin"] = now + + if player_data != None: + device_strin = "" + if player_data["isBan"] or get_account_age(player_data["accountAge"]) < \ + settings["minAgeToJoinInHours"]: + for ros in _ba.get_game_roster(): + if ros['account_id'] == pbid: + if not player_data["isBan"]: + _ba.screenmessage( + "New Accounts not allowed here , come back later", + color=(1, 0, 0), transient=True, + clients=[ros['client_id']]) + logger.log(pbid + " | kicked > reason:Banned account") + _ba.disconnect_client(ros['client_id']) + + return + else: + if pbid not in serverdata.clients: + serverdata.clients[pbid] = player_data + serverdata.clients[pbid]["warnCount"] = 0 + serverdata.clients[pbid]["lastWarned"] = time.time() + serverdata.clients[pbid]["verified"] = False + serverdata.clients[pbid]["rejoincount"] = 1 + serverdata.clients[pbid]["lastJoin"] = time.time() + if not player_data["canStartKickVote"]: + _ba.disable_kickvote(pbid) + + verify_account(pbid, player_data) + cid = 113 + d_st = "xx" + for ros in _ba.get_game_roster(): + if ros['account_id'] == pbid: + cid = ros['client_id'] + d_st = ros['display_string'] + serverdata.clients[pbid]["lastIP"] = _ba.get_client_ip(cid) + + device_id = _ba.get_client_public_device_uuid(cid) + if(device_id==None): + device_id = _ba.get_client_device_uuid(cid) + serverdata.clients[pbid]["deviceUUID"] = device_id + _ba.screenmessage(settings["regularWelcomeMsg"] + " " + d_st, + color=(0.60, 0.8, 0.6), transient=True, + clients=[cid]) + + else: + + d_string = "" + cid = 113 + for ros in _ba.get_game_roster(): + if ros['account_id'] == pbid: + d_string = ros['display_string'] + cid = ros['client_id'] + + thread = FetchThread( + target=my_acc_age, + callback=save_age, + pb_id=pbid, + display_string=d_string + ) + + thread.start() + _ba.screenmessage(settings["firstTimeJoinMsg"], color=(0.6, 0.8, 0.6), + transient=True, clients=[cid]) + + # pdata.add_profile(pbid,d_string,d_string) +def verify_account(pb_id, p_data): + d_string = "" + for ros in _ba.get_game_roster(): + if ros['account_id'] == pb_id: + d_string = ros['display_string'] -def on_player_join_server(pbid,player_data): - now=time.time() - #player_data=pdata.get_info(pbid) - clid=113 - for ros in _ba.get_game_roster(): - if ros["account_id"]==pbid: - clid=ros["client_id"] - if pbid in serverdata.clients: - rejoinCount=serverdata.clients[pbid]["rejoincount"] - spamCount=serverdata.clients[pbid]["spamCount"] - if now-serverdata.clients[pbid]["lastJoin"] < 15: - rejoinCount+=1 - if rejoinCount >2: - _ba.screenmessage("Joining too fast , slow down dude",color=(1,0,1),transient=True,clients=[clid]) - logger.log(pbid+"|| kicked for joining too fast") - _ba.disconnect_client(clid) - - _thread.start_new_thread(reportSpam,(pbid,)) - - return - else: - rejoinCount=0 + if d_string not in p_data['display_string']: - serverdata.clients[pbid]["rejoincount"]=rejoinCount - serverdata.clients[pbid]["lastJoin"]=now + thread2 = FetchThread( + target=get_device_accounts, + callback=save_ids, + pb_id=pb_id, + display_string=d_string + ) + thread2.start() + else: + serverdata.clients[pb_id]["verified"] = True - - - - - - if player_data!=None: - device_strin="" - if player_data["isBan"] or get_account_age(player_data["accountAge"]) < settings["minAgeToJoinInHours"]: - for ros in _ba.get_game_roster(): - if ros['account_id']==pbid: - if not player_data["isBan"]: - _ba.screenmessage("New Accounts not allowed here , come back later",color=(1,0,0), transient=True,clients=[ros['client_id']]) - logger.log(pbid+" | kicked > reason:Banned account") - _ba.disconnect_client(ros['client_id']) - - return - else: - if pbid not in serverdata.clients: - serverdata.clients[pbid]=player_data - serverdata.clients[pbid]["warnCount"]=0 - serverdata.clients[pbid]["lastWarned"]=time.time() - serverdata.clients[pbid]["verified"]=False - serverdata.clients[pbid]["rejoincount"]=1 - serverdata.clients[pbid]["lastJoin"]=time.time() - if not player_data["canStartKickVote"]: - _ba.disable_kickvote(pbid) - - verify_account(pbid,player_data) - cid=113 - d_st="xx" - for ros in _ba.get_game_roster(): - if ros['account_id']==pbid: - cid=ros['client_id'] - d_st=ros['display_string'] - _ba.screenmessage(settings["regularWelcomeMsg"]+" "+d_st,color=(0.60,0.8,0.6),transient=True,clients=[cid]) - - else: - - d_string="" - cid=113 - for ros in _ba.get_game_roster(): - if ros['account_id']==pbid: - d_string=ros['display_string'] - cid=ros['client_id'] - - thread = FetchThread( - target=my_acc_age, - callback=save_age, - pb_id=pbid, - display_string=d_string - ) - - thread.start() - _ba.screenmessage(settings["firstTimeJoinMsg"],color=(0.6,0.8,0.6),transient=True,clients=[cid]) - - - - #pdata.add_profile(pbid,d_string,d_string) - -def verify_account(pb_id,p_data): - d_string="" - for ros in _ba.get_game_roster(): - if ros['account_id']==pb_id: - d_string=ros['display_string'] - - if d_string not in p_data['display_string']: - - thread2 = FetchThread( - target=get_device_accounts, - callback=save_ids, - pb_id=pb_id, - display_string=d_string - ) - thread2.start() - else: - serverdata.clients[pb_id]["verified"]=True - - -#============== IGNORE BLOW CODE , ELSE DIE ======================= +# ============== IGNORE BLOW CODE , ELSE DIE ======================= def _make_request_safe(request, retries=2, raise_err=True): try: @@ -223,14 +201,17 @@ def _make_request_safe(request, retries=2, raise_err=True): except: if retries > 0: time.sleep(1) - return _make_request_safe(request, retries=retries-1, raise_err=raise_err) + return _make_request_safe(request, retries=retries - 1, + raise_err=raise_err) if raise_err: raise + def get_account_creation_date(pb_id): - # thanks rikko + # thanks rikko account_creation_url = "http://bombsquadgame.com/accountquery?id=" + pb_id - account_creation = _make_request_safe(lambda: urllib.request.urlopen(account_creation_url)) + account_creation = _make_request_safe( + lambda: urllib.request.urlopen(account_creation_url)) if account_creation is not None: try: account_creation = json.loads(account_creation.read()) @@ -239,7 +220,8 @@ def get_account_creation_date(pb_id): else: creation_time = account_creation["created"] creation_time = map(str, creation_time) - creation_time = datetime.datetime.strptime("/".join(creation_time), "%Y/%m/%d/%H/%M/%S") + creation_time = datetime.datetime.strptime("/".join(creation_time), + "%Y/%m/%d/%H/%M/%S") # Convert to IST creation_time += datetime.timedelta(hours=5, minutes=30) return str(creation_time) @@ -248,16 +230,18 @@ def get_account_creation_date(pb_id): # delta_hours = delta.total_seconds() / (60 * 60) # return delta_hours + def get_device_accounts(pb_id): - url="http://bombsquadgame.com/bsAccountInfo?buildNumber=20258&accountID="+pb_id - data=_make_request_safe(lambda:urllib.request.urlopen(url)) - if data is not None: - try: - accounts=json.loads(data.read())["accountDisplayStrings"] - except ValueError: - return ['???'] - else: - return accounts + url = "http://bombsquadgame.com/bsAccountInfo?buildNumber=20258&accountID=" + pb_id + data = _make_request_safe(lambda: urllib.request.urlopen(url)) + if data is not None: + try: + accounts = json.loads(data.read())["accountDisplayStrings"] + except ValueError: + return ['???'] + else: + return accounts + # ======= yes fucking threading code , dont touch ============== @@ -265,85 +249,73 @@ def get_device_accounts(pb_id): # ============ file I/O ============= class LoadProfile(threading.Thread): - def __init__(self,pb_id): - threading.Thread.__init__(self) - self.pbid=pb_id - - - def run(self): - player_data=pdata.get_info(self.pbid) - _ba.pushcall(Call(on_player_join_server,self.pbid,player_data),from_other_thread=True) - - - - + def __init__(self, pb_id): + threading.Thread.__init__(self) + self.pbid = pb_id + def run(self): + player_data = pdata.get_info(self.pbid) + _ba.pushcall(Call(on_player_join_server, self.pbid, player_data), + from_other_thread=True) # ================ http ================ class FetchThread(threading.Thread): - def __init__(self,target, callback=None,pb_id="ji",display_string="XXX"): - - super(FetchThread, self).__init__(target=self.target_with_callback, args=(pb_id,display_string,)) + def __init__(self, target, callback=None, pb_id="ji", + display_string="XXX"): + super(FetchThread, self).__init__(target=self.target_with_callback, + args=(pb_id, display_string,)) self.callback = callback self.method = target - - - def target_with_callback(self,pb_id,display_string): - - data=self.method(pb_id) + + def target_with_callback(self, pb_id, display_string): + data = self.method(pb_id) if self.callback is not None: - self.callback(data,pb_id,display_string) + self.callback(data, pb_id, display_string) def my_acc_age(pb_id): - return get_account_creation_date(pb_id) -def save_age(age, pb_id,display_string): - - - pdata.add_profile(pb_id,display_string,display_string,age) +def save_age(age, pb_id, display_string): + pdata.add_profile(pb_id, display_string, display_string, age) time.sleep(2) thread2 = FetchThread( - target=get_device_accounts, - callback=save_ids, - pb_id=pb_id, - display_string=display_string - ) + target=get_device_accounts, + callback=save_ids, + pb_id=pb_id, + display_string=display_string + ) thread2.start() if get_account_age(age) < settings["minAgeToJoinInHours"]: - msg="New Accounts not allowed to play here , come back tmrw." - logger.log(pb_id+"|| kicked > new account") - _ba.pushcall(Call(kick_by_pb_id,pb_id,msg),from_other_thread=True) - -def save_ids(ids,pb_id,display_string): - - - pdata.update_display_string(pb_id,ids) + msg = "New Accounts not allowed to play here , come back tmrw." + logger.log(pb_id + "|| kicked > new account") + _ba.pushcall(Call(kick_by_pb_id, pb_id, msg), from_other_thread=True) - if display_string not in ids: - msg="Spoofed Id detected , Goodbye" - _ba.pushcall(Call(kick_by_pb_id,pb_id,msg),from_other_thread=True) - serverdata.clients[pb_id]["verified"]=False - logger.log(pb_id+"|| kicked , for using spoofed id "+display_string) - else: - serverdata.clients[pb_id]["verified"]=True - +def save_ids(ids, pb_id, display_string): + pdata.update_display_string(pb_id, ids) + + if display_string not in ids: + msg = "Spoofed Id detected , Goodbye" + _ba.pushcall(Call(kick_by_pb_id, pb_id, msg), from_other_thread=True) + serverdata.clients[pb_id]["verified"] = False + logger.log( + pb_id + "|| kicked , for using spoofed id " + display_string) + else: + serverdata.clients[pb_id]["verified"] = True -def kick_by_pb_id(pb_id,msg): - for ros in _ba.get_game_roster(): - if ros['account_id']==pb_id: - _ba.screenmessage(msg, transient=True, clients=[ros['client_id']]) - _ba.disconnect_client(ros['client_id']) +def kick_by_pb_id(pb_id, msg): + for ros in _ba.get_game_roster(): + if ros['account_id'] == pb_id: + _ba.screenmessage(msg, transient=True, clients=[ros['client_id']]) + _ba.disconnect_client(ros['client_id']) - def get_account_age(ct): - creation_time=datetime.datetime.strptime(ct,"%Y-%m-%d %H:%M:%S") + creation_time = datetime.datetime.strptime(ct, "%Y-%m-%d %H:%M:%S") now = datetime.datetime.now() delta = now - creation_time delta_hours = delta.total_seconds() / (60 * 60) @@ -351,18 +323,33 @@ def get_account_age(ct): def reportSpam(id): - now=time.time() - profiles=pdata.get_profiles() - if id in profiles: - count=profiles[id]["spamCount"] - - if now-profiles[id]["lastSpam"] < 2*24*60*60: - count+=1 - if count > 3: - profiles[id]["isBan"]=True - else: - count =0 + now = time.time() + profiles = pdata.get_profiles() + if id in profiles: + count = profiles[id]["spamCount"] - profiles[id]["spamCount"]=count - profiles[id]["lastSpam"]=now - pdata.commit_profiles(profiles) \ No newline at end of file + if now - profiles[id]["lastSpam"] < 2 * 24 * 60 * 60: + count += 1 + if count > 3: + profiles[id]["isBan"] = True + else: + count = 0 + + profiles[id]["spamCount"] = count + profiles[id]["lastSpam"] = now + pdata.commit_profiles(profiles) + +def on_join_request(ip): + now = time.time() + if ip in serverdata.ips: + lastRequest = serverdata.ips[ip]["lastRequest"] + count = serverdata.ips[ip]["count"] + if now - lastRequest < 5: + count +=1 + if count > 40: + _ba.ban_ip(ip) + else: + count = 0 + serverdata.ips[ip] = {"lastRequest":time.time(),"count":count} + else: + serverdata.ips[ip]={"lastRequest":time.time(),"count":0}