diff --git a/core/base.py b/core/base.py index 99884b0..07c8666 100644 --- a/core/base.py +++ b/core/base.py @@ -1,4 +1,4 @@ -import time, threading, os, random, socket, hashlib +import time, threading, os, random, socket, hashlib, ipaddress from datetime import datetime from sqlalchemy import create_engine, Engine, Connection, CursorResult from sqlalchemy.sql import text @@ -179,7 +179,7 @@ class Base: """Methode qui supprime les timers qui ont finis leurs job """ try: - self.__debug(f"=======> Checking for Timers to stop") + # self.__debug(f"=======> Checking for Timers to stop") # print(f"{self.running_timers}") for timer in self.running_timers: if not timer.is_alive(): @@ -196,7 +196,7 @@ class Base: """Methode qui supprime les threads qui ont finis leurs job """ try: - self.__debug(f"=======> Checking for Threads to stop") + # self.__debug(f"=======> Checking for Threads to stop") for thread in self.running_threads: if thread.getName() != 'heartbeat': if not thread.is_alive(): @@ -209,7 +209,7 @@ class Base: def garbage_collector_sockets(self) -> None: - self.__debug(f"=======> Checking for Sockets to stop") + # self.__debug(f"=======> Checking for Sockets to stop") for soc in self.running_sockets: while soc.fileno() != -1: self.__debug(soc.fileno()) @@ -358,6 +358,17 @@ class Base: except TypeError: return value + def is_valid_ip(self, ip_to_control:str) -> bool: + + try: + if ip_to_control in self.Config.WHITELISTED_IP: + return False + + ipaddress.ip_address(ip_to_control) + return True + except ValueError: + return False + def get_random(self, lenght:int) -> str: """ Retourn une chaîne aléatoire en fonction de la longueur spécifiée. diff --git a/core/exemple_configuration.py b/core/exemple_configuration.py index 4a6da1b..4a4ebcf 100644 --- a/core/exemple_configuration.py +++ b/core/exemple_configuration.py @@ -18,6 +18,7 @@ class Config: SERVEUR_PORT = 6666 # Port du link SERVEUR_PASSWORD = 'your link password' # Mot de passe du link (Privilégiez argon2 sur Unrealircd) SERVEUR_ID = '002' # SID (identification) du bot en tant que Services + SERVEUR_SSL = False # Activer / Desactiver la connexion SSL SERVICE_NICKNAME = 'BotName' # Nick du bot sur IRC SERVICE_REALNAME = 'BotRealname' # Realname du bot diff --git a/core/irc.py b/core/irc.py index 6ee4eb0..46a1245 100644 --- a/core/irc.py +++ b/core/irc.py @@ -19,7 +19,8 @@ class Irc: self.INIT = 1 # Variable d'intialisation | 1 -> indique si le programme est en cours d'initialisation self.RESTART = 0 # Variable pour le redemarrage du bot | 0 -> indique que le programme n'es pas en cours de redemarrage - self.CHARSET = ['utf-8', 'iso-8859-1'] # Charset utiliser pour décoder/encoder les messages + self.CHARSET = ['utf-8', 'iso-8859-1'] # Charset utiliser pour décoder/encoder les messages + self.SSL_VERSION = None # Version SSL self.Config = Config() @@ -52,20 +53,35 @@ class Irc: def __create_socket(self) -> None: - self.IrcSocket:socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - connexion_information = (self.Config.SERVEUR_IP, self.Config.SERVEUR_PORT) - self.IrcSocket.connect(connexion_information) + try: + soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM or socket.SOCK_NONBLOCK) + connexion_information = (self.Config.SERVEUR_IP, self.Config.SERVEUR_PORT) - # Créer un object ssl - # ssl_context = self.__ssl_context() - # ssl_connexion = ssl_context.wrap_socket(s, server_hostname=self.Config.SERVEUR_HOSTNAME) - # ssl_connexion.connect(connexion_information) - # self.IrcSocket = ssl_connexion + if self.Config.SERVEUR_SSL: + # Créer un object ssl - return None + ssl_context = self.__ssl_context() + ssl_connexion = ssl_context.wrap_socket(soc, server_hostname=self.Config.SERVEUR_HOSTNAME) + ssl_connexion.connect(connexion_information) + self.IrcSocket:ssl.SSLSocket = ssl_connexion + self.debug(f"Connexion en mode SSL : Version = {self.IrcSocket.version()}") + self.SSL_VERSION = self.IrcSocket.version() + else: + soc.connect(connexion_information) + self.IrcSocket:socket = soc + self.debug("Connexion en mode normal") + + return None + + except ssl.SSLEOFError as soe: + self.debug(f"SSLEOFError __create_socket: {soe} - {self.IrcSocket.fileno()}") + except ssl.SSLError as se: + self.debug(f"SSLError __create_socket: {se} - {self.IrcSocket.fileno()}") + except OSError as oe: + self.debug(f"OSError __create_socket: {oe} - {self.IrcSocket.fileno()}") def __ssl_context(self) -> ssl.SSLContext: - ctx = ssl.create_default_context() + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE @@ -114,9 +130,6 @@ class Irc: break self.send_response(data) - if self.IrcSocket.fileno() == -1: - print('restarting the socket') - self.RESTART = 1 except ssl.SSLEOFError as soe: self.debug(f"SSLEOFError __connect_to_irc: {soe} - {data}") @@ -124,8 +137,8 @@ class Irc: self.debug(f"SSLError __connect_to_irc: {se} - {data}") except OSError as oe: self.debug(f"SSLError __connect_to_irc: {oe} - {data}") - except Exception as e: - self.debug(f"Exception __connect_to_irc: {e} - {data}") + # except Exception as e: + # self.debug(f"Exception __connect_to_irc: {e} - {data}") self.IrcSocket.shutdown(socket.SHUT_RDWR) self.IrcSocket.close() @@ -137,8 +150,8 @@ class Irc: self.debug(f'Value Error : {ve}') except ssl.SSLEOFError as soe: self.debug(f"OS Error __connect_to_irc: {soe}") - except Exception as e: - self.debug(f"Exception: {e}") + # except Exception as e: + # self.debug(f"Exception: {e}") def __link(self, writer:socket.socket) -> None: """Créer le link et envoyer les informations nécessaires pour la @@ -187,7 +200,9 @@ class Irc: string (Str): contient la commande à envoyer au serveur. """ try: - self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0])) + with self.Base.lock: + # print(f">{str(send_message)}") + self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0])) except UnicodeDecodeError: self.debug('Write Decode impossible try iso-8859-1') @@ -698,6 +713,8 @@ class Irc: print(f"# SERVICE CONNECTE ") print(f"# SERVEUR : {self.Config.SERVEUR_IP} ") print(f"# PORT : {self.Config.SERVEUR_PORT} ") + print(f"# SSL : {self.Config.SERVEUR_SSL} ") + print(f"# SSL VER : {self.SSL_VERSION} ") print(f"# NICKNAME : {self.Config.SERVICE_NICKNAME} ") print(f"# CHANNEL : {self.Config.SERVICE_CHANLOG} ") print(f"# VERSION : {self.Config.DEFENDER_VERSION} ") diff --git a/mods/mod_defender.py b/mods/mod_defender.py index 298ec9e..c489d87 100644 --- a/mods/mod_defender.py +++ b/mods/mod_defender.py @@ -57,9 +57,6 @@ class Defender(): Args: commands (list): Liste des commandes du module - - Returns: - None: Aucun retour attendu """ for level, com in commands.items(): for c in commands[level]: @@ -70,10 +67,12 @@ class Defender(): return None def unload(self) -> None: - + """Cette methode sera executée a chaque désactivation ou + rechargement de module + """ + self.abuseipdb_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec abuseipdb self.freeipapi_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec freeipapi self.cloudfilt_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec cloudfilt - self.abuseipdb_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec abuseipdb self.psutil_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec psutil_scan self.localscan_remote_ip:list = [] # Liste qui va contenir les adresses ip a scanner avec local_scan @@ -394,7 +393,7 @@ class Defender(): uptime = current_datetime - connected_time convert_to_minutes = uptime.seconds / 60 - uptime_minutes = round(number=convert_to_minutes, ndigits=0) + uptime_minutes = round(number=convert_to_minutes, ndigits=2) return uptime_minutes @@ -453,6 +452,7 @@ class Defender(): reputation_flag = int(self.defConfig['reputation']) reputation_timer = int(self.defConfig['reputation_timer']) reputation_seuil = self.defConfig['reputation_seuil'] + ban_all_chan = self.Base.int_if_possible(self.defConfig['reputation_ban_all_chan']) service_id = self.Config.SERVICE_ID dchanlog = self.Config.SERVICE_CHANLOG color_red = self.Config.CONFIG_COLOR['rouge'] @@ -469,21 +469,23 @@ class Defender(): for uid in self.db_reputation: if not self.db_reputation[uid]['isWebirc']: # Si il ne vient pas de WebIRC - self.Irc.debug(f"Nickname: {self.db_reputation[uid]['nickname']} | uptime: {self.get_user_uptime_in_minutes(uid)} | reputation time: {reputation_timer}") + # self.Irc.debug(f"Nickname: {self.db_reputation[uid]['nickname']} | uptime: {self.get_user_uptime_in_minutes(uid)} | reputation time: {reputation_timer}") if self.get_user_uptime_in_minutes(uid) >= reputation_timer and int(self.db_reputation[uid]['score']) <= int(reputation_seuil): - self.Irc.debug('-----'*20) self.Irc.send2socket(f":{service_id} PRIVMSG {dchanlog} :[{color_red} REPUTATION {color_black}] : Action sur {self.db_reputation[uid]['nickname']} aprés {str(reputation_timer)} minutes d'inactivité") # if not system_reputation_timer_action(cglobal['reputation_timer_action'], uid, self.db_reputation[uid]['nickname']): # return False self.Irc.send2socket(f":{service_id} KILL {self.db_reputation[uid]['nickname']} After {str(reputation_timer)} minutes of inactivity you should reconnect and type the password code ") - self.Irc.debug(f"Action sur {self.db_reputation[uid]['nickname']} aprés {str(reputation_timer)} minutes d'inactivité") + self.Irc.debug('-----'*20) + self.Irc.debug(f"Nickname: {self.db_reputation[uid]['nickname']} KILLED after {str(reputation_timer)} minutes of inactivity") + self.Irc.debug('-----'*20) + uid_to_clean.append(uid) for uid in uid_to_clean: # Suppression des éléments dans {UID_DB} et {REPUTATION_DB} for chan in self.Irc.db_chan: - if chan != salon_jail: + if chan != salon_jail and ban_all_chan == 1: self.Irc.send2socket(f":{service_id} MODE {chan} -b {self.db_reputation[uid]['nickname']}!*@*") # Lorsqu'un utilisateur quitte, il doit être supprimé de {UID_DB}. @@ -599,7 +601,7 @@ class Defender(): for port in self.Config.PORTS_TO_SCAN: newSocket = '' - newSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + newSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM or socket.SOCK_NONBLOCK) newSocket.settimeout(0.5) try: connection = (remote_ip, self.Base.int_if_possible(port)) @@ -611,11 +613,11 @@ class Defender(): newSocket.shutdown(socket.SHUT_RDWR) newSocket.close() except (socket.timeout, ConnectionRefusedError): - self.Irc.debug(f"Le port {str(port)} est fermé") + self.Irc.debug(f"Le port {remote_ip}:{str(port)} est fermé") except AttributeError as ae: - self.Irc.debug(f"AttributeError : {ae}") + self.Irc.debug(f"AttributeError ({remote_ip}): {ae}") except socket.gaierror as err: - self.Irc.debug(f"Address Info Error: {err}") + self.Irc.debug(f"Address Info Error ({remote_ip}): {err}") finally: # newSocket.shutdown(socket.SHUT_RDWR) newSocket.close() @@ -931,6 +933,9 @@ class Defender(): self.reputation_first_connexion['ip'] = cmd[2] self.reputation_first_connexion['score'] = cmd[3] + if not self.Base.is_valid_ip(cmd[2]): + return None + # self.Base.scan_ports(cmd[2]) if self.defConfig['local_scan'] == 1 and not cmd[2] in self.Config.WHITELISTED_IP: self.localscan_remote_ip.append(cmd[2]) @@ -1030,6 +1035,10 @@ class Defender(): case 'SLOG': # self.Base.scan_ports(cmd[7]) cmd.pop(0) + + if not self.Base.is_valid_ip(cmd[7]): + return None + if self.defConfig['local_scan'] == 1 and not cmd[7] in self.Config.WHITELISTED_IP: self.localscan_remote_ip.append(cmd[7]) @@ -1040,7 +1049,7 @@ class Defender(): self.abuseipdb_remote_ip.append(cmd[7]) if self.defConfig['freeipapi_scan'] == 1 and not cmd[7] in self.Config.WHITELISTED_IP: - self.freeipapi_remote_ip.append[cmd[7]] + self.freeipapi_remote_ip.append(cmd[7]) if self.defConfig['cloudfilt_scan'] == 1 and not cmd[7] in self.Config.WHITELISTED_IP: self.cloudfilt_remote_ip.append(cmd[7]) @@ -1070,6 +1079,7 @@ class Defender(): case 'QUIT': # :001N1WD7L QUIT :Quit: free_znc_1 cmd.pop(0) + ban_all_chan = self.Base.int_if_possible(self.defConfig['reputation_ban_all_chan']) user_id = str(cmd[0]).replace(':','') final_UID = user_id @@ -1079,7 +1089,7 @@ class Defender(): if final_UID in self.db_reputation: final_nickname = self.db_reputation[user_id]['nickname'] for chan in self.Irc.db_chan: - if chan != jail_salon: + if chan != jail_salon and ban_all_chan == 1: self.Irc.send2socket(f":{service_id} MODE {chan} -b {final_nickname}!*@*") self.delete_db_reputation(final_UID)