Merge pull request #36 from adator85/dev

Dev
This commit is contained in:
adator
2024-09-15 02:04:32 +02:00
committed by GitHub
7 changed files with 224 additions and 163 deletions

3
.gitignore vendored
View File

@@ -2,6 +2,7 @@
db/ db/
logs/ logs/
__pycache__/ __pycache__/
mods/mod_jsonrpc.py
configuration.json configuration.json
install.log *.log
test.py test.py

View File

@@ -218,7 +218,7 @@ class Base:
"""Supprime les modules de la base de données """Supprime les modules de la base de données
Args: Args:
cmd (str): le module a enregistrer cmd (str): le module a supprimer
""" """
insert_cmd_query = f"DELETE FROM {self.Config.table_module} WHERE module_name = :module_name" insert_cmd_query = f"DELETE FROM {self.Config.table_module} WHERE module_name = :module_name"
mes_donnees = {'module_name': module_name} mes_donnees = {'module_name': module_name}
@@ -307,7 +307,7 @@ class Base:
def db_update_core_config(self, module_name:str, dataclassObj: object, param_key:str, param_value: str) -> bool: def db_update_core_config(self, module_name:str, dataclassObj: object, param_key:str, param_value: str) -> bool:
core_table = 'core_config' core_table = self.Config.table_config
# Check if the param exist # Check if the param exist
if not hasattr(dataclassObj, param_key): if not hasattr(dataclassObj, param_key):
self.logs.error(f"Le parametre {param_key} n'existe pas dans la variable global") self.logs.error(f"Le parametre {param_key} n'existe pas dans la variable global")
@@ -330,6 +330,10 @@ class Base:
if updated_rows > 0: if updated_rows > 0:
setattr(dataclassObj, param_key, self.int_if_possible(param_value)) setattr(dataclassObj, param_key, self.int_if_possible(param_value))
self.logs.debug(f'Parameter updated : {param_key} - {param_value} | Module: {module_name}') self.logs.debug(f'Parameter updated : {param_key} - {param_value} | Module: {module_name}')
else:
self.logs.error(f'Parameter NOT updated : {param_key} - {param_value} | Module: {module_name}')
else:
self.logs.error(f'Parameter and Module do not exist: Param ({param_key}) - Value ({param_value}) | Module ({module_name})')
self.logs.debug(dataclassObj) self.logs.debug(dataclassObj)

View File

@@ -204,7 +204,6 @@ class Connection:
self.send2socket(f"PRIVMSG {clone_channel} :{final_message}") self.send2socket(f"PRIVMSG {clone_channel} :{final_message}")
except UnicodeEncodeError: except UnicodeEncodeError:
for data in cmd: for data in cmd:
response = data.decode(self.CHARSET[1],'replace').split() response = data.decode(self.CHARSET[1],'replace').split()

View File

@@ -36,8 +36,6 @@ class Install:
if self.skip_install: if self.skip_install:
return None return None
print(f'Configuration loaded : {self.config}')
# Sinon tester les dependances python et les installer avec pip # Sinon tester les dependances python et les installer avec pip
if self.do_install(): if self.do_install():

View File

@@ -1,4 +1,4 @@
import ssl, re, importlib, sys, time, threading, socket import ssl, re, importlib, sys, time, threading, socket, traceback
from ssl import SSLSocket from ssl import SSLSocket
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Union, Literal from typing import Union, Literal
@@ -176,6 +176,7 @@ class Irc:
self.Base.logs.critical(f"AttributeError: {atte}") self.Base.logs.critical(f"AttributeError: {atte}")
except Exception as e: except Exception as e:
self.Base.logs.critical(f"Exception: {e}") self.Base.logs.critical(f"Exception: {e}")
self.Base.logs.critical(traceback.print_exc())
def __link(self, writer:Union[socket.socket, SSLSocket]) -> None: def __link(self, writer:Union[socket.socket, SSLSocket]) -> None:
"""Créer le link et envoyer les informations nécessaires pour la """Créer le link et envoyer les informations nécessaires pour la
@@ -274,14 +275,20 @@ class Irc:
response = data.decode(self.CHARSET[0]).split() response = data.decode(self.CHARSET[0]).split()
self.cmd(response) self.cmd(response)
except UnicodeEncodeError: except UnicodeEncodeError as ue:
for data in responses: for data in responses:
response = data.decode(self.CHARSET[1],'replace').split() response = data.decode(self.CHARSET[1],'replace').split()
self.cmd(response) self.cmd(response)
except UnicodeDecodeError: self.Base.logs.error(f'UnicodeEncodeError: {ue}')
self.Base.logs.error(response)
except UnicodeDecodeError as ud:
for data in responses: for data in responses:
response = data.decode(self.CHARSET[1],'replace').split() response = data.decode(self.CHARSET[1],'replace').split()
self.cmd(response) self.cmd(response)
self.Base.logs.error(f'UnicodeDecodeError: {ud}')
self.Base.logs.error(response)
except AssertionError as ae: except AssertionError as ae:
self.Base.logs.error(f"Assertion error : {ae}") self.Base.logs.error(f"Assertion error : {ae}")
@@ -446,6 +453,7 @@ class Irc:
except ModuleNotFoundError as moduleNotFound: except ModuleNotFoundError as moduleNotFound:
self.Base.logs.error(f"MODULE_NOT_FOUND: {moduleNotFound}") self.Base.logs.error(f"MODULE_NOT_FOUND: {moduleNotFound}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.CONFIG_COLOR['rouge']}MODULE_NOT_FOUND{self.Config.CONFIG_COLOR['noire']} ]: {moduleNotFound}") self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.CONFIG_COLOR['rouge']}MODULE_NOT_FOUND{self.Config.CONFIG_COLOR['noire']} ]: {moduleNotFound}")
self.Base.db_delete_module(module_name)
except Exception as e: except Exception as e:
self.Base.logs.error(f"Something went wrong with a module you want to load : {e}") self.Base.logs.error(f"Something went wrong with a module you want to load : {e}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.CONFIG_COLOR['rouge']}ERROR{self.Config.CONFIG_COLOR['noire']} ]: {e}") self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.CONFIG_COLOR['rouge']}ERROR{self.Config.CONFIG_COLOR['noire']} ]: {e}")
@@ -576,7 +584,6 @@ class Irc:
return None return None
def thread_check_for_new_version(self, fromuser: str) -> None: def thread_check_for_new_version(self, fromuser: str) -> None:
dnickname = self.Config.SERVICE_NICKNAME dnickname = self.Config.SERVICE_NICKNAME
if self.Base.check_for_new_version(True): if self.Base.check_for_new_version(True):
@@ -587,35 +594,39 @@ class Irc:
return None return None
def cmd(self, data:list) -> None: def cmd(self, data: list[str]) -> None:
"""Parse server response
Args:
data (list[str]): Server response splitted in a list
"""
try: try:
original_response: list[str] = data.copy()
cmd_to_send:list[str] = data.copy() interm_response: list[str] = data.copy()
cmd = data.copy() """This the original without first value"""
cmd_to_debug = data.copy() interm_response.pop(0)
cmd_to_debug.pop(0)
if len(cmd) == 0 or len(cmd) == 1: if len(original_response) == 0 or len(original_response) == 1:
self.Base.logs.warning(f'Size ({str(len(cmd))}) - {cmd}') self.Base.logs.warning(f'Size ({str(len(original_response))}) - {original_response}')
return False return False
# self.debug(cmd_to_debug) if len(original_response) == 7:
if len(data) == 7: if original_response[2] == 'PRIVMSG' and original_response[4] == ':auth':
if data[2] == 'PRIVMSG' and data[4] == ':auth': data_copy = original_response.copy()
data_copy = data.copy()
data_copy[6] = '**********' data_copy[6] = '**********'
self.Base.logs.debug(data_copy) self.Base.logs.debug(data_copy)
else: else:
self.Base.logs.debug(data) self.Base.logs.debug(original_response)
else: else:
self.Base.logs.debug(data) self.Base.logs.debug(original_response)
match cmd[0]: match original_response[0]:
case 'PING': case 'PING':
# Sending PONG response to the serveur # Sending PONG response to the serveur
pong = str(cmd[1]).replace(':','') pong = str(original_response[1]).replace(':','')
self.send2socket(f"PONG :{pong}") self.send2socket(f"PONG :{pong}")
return None return None
@@ -624,19 +635,19 @@ class Irc:
# 'PREFIX=(qaohv)~&@%+', 'SID=001', 'MLOCK', 'TS=1703793941', 'EXTSWHOIS'] # 'PREFIX=(qaohv)~&@%+', 'SID=001', 'MLOCK', 'TS=1703793941', 'EXTSWHOIS']
# GET SERVER ID HOST # GET SERVER ID HOST
if len(cmd) > 5: if len(original_response) > 5:
if '=' in cmd[5]: if '=' in original_response[5]:
serveur_hosting_id = str(cmd[5]).split('=') serveur_hosting_id = str(original_response[5]).split('=')
self.HSID = serveur_hosting_id[1] self.HSID = serveur_hosting_id[1]
return False return False
case _: case _:
pass pass
if len(cmd) < 2: if len(original_response) < 2:
return False return False
match cmd[1]: match original_response[1]:
case 'SLOG': case 'SLOG':
# self.Base.scan_ports(cmd[7]) # self.Base.scan_ports(cmd[7])
@@ -647,20 +658,18 @@ class Irc:
case 'REPUTATION': case 'REPUTATION':
# :001 REPUTATION 91.168.141.239 118 # :001 REPUTATION 91.168.141.239 118
try: try:
# if self.Config.ABUSEIPDB == 1: self.first_connexion_ip = original_response[2]
# self.Base.create_thread(self.abuseipdb_scan, (cmd[2], ))
self.first_connexion_ip = cmd[2]
self.first_score = 0 self.first_score = 0
if str(cmd[3]).find('*') != -1: if str(original_response[3]).find('*') != -1:
# If * available, it means that an ircop changed the repurtation score # If * available, it means that an ircop changed the repurtation score
# means also that the user exist will try to update all users with same IP # means also that the user exist will try to update all users with same IP
self.first_score = int(str(cmd[3]).replace('*','')) self.first_score = int(str(original_response[3]).replace('*',''))
for user in self.User.UID_DB: for user in self.User.UID_DB:
if user.remote_ip == self.first_connexion_ip: if user.remote_ip == self.first_connexion_ip:
user.score_connexion = self.first_score user.score_connexion = self.first_score
else: else:
self.first_score = int(cmd[3]) self.first_score = int(original_response[3])
# Possibilité de déclancher les bans a ce niveau. # Possibilité de déclancher les bans a ce niveau.
except IndexError as ie: except IndexError as ie:
@@ -683,7 +692,7 @@ class Irc:
case 'EOS': case 'EOS':
hsid = str(cmd[0]).replace(':','') hsid = str(original_response[0]).replace(':','')
if hsid == self.HSID: if hsid == self.HSID:
if self.INIT == 1: if self.INIT == 1:
current_version = self.Config.current_version current_version = self.Config.current_version
@@ -693,10 +702,6 @@ class Irc:
else: else:
version = f'{current_version}' version = f'{current_version}'
# self.send2socket(f":{self.Config.SERVICE_NICKNAME} SVSJOIN {self.Config.SERVICE_NICKNAME} {self.Config.SERVICE_CHANLOG}")
# self.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.SERVICE_CHANLOG} +o {self.Config.SERVICE_NICKNAME}")
# self.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.SERVICE_CHANLOG} +{self.Config.SERVICE_CMODES}")
print(f"################### DEFENDER ###################") print(f"################### DEFENDER ###################")
print(f"# SERVICE CONNECTE ") print(f"# SERVICE CONNECTE ")
print(f"# SERVEUR : {self.Config.SERVEUR_IP} ") print(f"# SERVEUR : {self.Config.SERVEUR_IP} ")
@@ -728,15 +733,15 @@ class Irc:
case _: case _:
pass pass
if len(cmd) < 3: if len(original_response) < 3:
return False return False
match cmd[2]: match original_response[2]:
case 'QUIT': case 'QUIT':
# :001N1WD7L QUIT :Quit: free_znc_1 # :001N1WD7L QUIT :Quit: free_znc_1
cmd.pop(0)
uid_who_quit = str(cmd[0]).replace(':', '') uid_who_quit = str(interm_response[0]).replace(':', '')
self.User.delete(uid_who_quit) self.User.delete(uid_who_quit)
self.Channel.delete_user_from_all_channel(uid_who_quit) self.Channel.delete_user_from_all_channel(uid_who_quit)
@@ -748,10 +753,8 @@ class Irc:
# ['@unrealircd.org/geoip=FR;unrealircd.org/', ':001OOU2H3', 'NICK', 'WebIrc', '1703795844'] # ['@unrealircd.org/geoip=FR;unrealircd.org/', ':001OOU2H3', 'NICK', 'WebIrc', '1703795844']
# Changement de nickname # Changement de nickname
# Supprimer la premiere valeur de la liste uid = str(interm_response[0]).replace(':','')
cmd.pop(0) newnickname = interm_response[2]
uid = str(cmd[0]).replace(':','')
newnickname = cmd[2]
self.User.update(uid, newnickname) self.User.update(uid, newnickname)
case 'MODE': case 'MODE':
@@ -766,24 +769,24 @@ class Irc:
# ':001T6VU3F', '001JGWB2K', '@11ZAAAAAB', # ':001T6VU3F', '001JGWB2K', '@11ZAAAAAB',
# '001F16WGR', '001X9YMGQ', '*+001DYPFGP', '@00BAAAAAJ', '001AAGOG9', '001FMFVG8', '001DAEEG7', # '001F16WGR', '001X9YMGQ', '*+001DYPFGP', '@00BAAAAAJ', '001AAGOG9', '001FMFVG8', '001DAEEG7',
# '&~G:unknown-users', '"~G:websocket-users', '"~G:known-users', '"~G:webirc-users'] # '&~G:unknown-users', '"~G:websocket-users', '"~G:known-users', '"~G:webirc-users']
cmd.pop(0)
channel = str(cmd[3]).lower() channel = str(interm_response[3]).lower()
len_cmd = len(cmd) len_cmd = len(interm_response)
list_users:list = [] list_users:list = []
occurence = 0 occurence = 0
start_boucle = 0 start_boucle = 0
# Trouver le premier user # Trouver le premier user
for i in range(len_cmd): for i in range(len_cmd):
s: list = re.findall(fr':', cmd[i]) s: list = re.findall(fr':', interm_response[i])
if s: if s:
occurence += 1 occurence += 1
if occurence == 2: if occurence == 2:
start_boucle = i start_boucle = i
# Boucle qui va ajouter l'ensemble des users (UID) # Boucle qui va ajouter l'ensemble des users (UID)
for i in range(start_boucle, len(cmd)): for i in range(start_boucle, len(interm_response)):
parsed_UID = str(cmd[i]) parsed_UID = str(interm_response[i])
# pattern = fr'[:|@|%|\+|~|\*]*' # pattern = fr'[:|@|%|\+|~|\*]*'
# pattern = fr':' # pattern = fr':'
# parsed_UID = re.sub(pattern, '', parsed_UID) # parsed_UID = re.sub(pattern, '', parsed_UID)
@@ -801,29 +804,31 @@ class Irc:
case 'PART': case 'PART':
# ['@unrealircd.org/geoip=FR;unrealircd.org/userhost=50d6492c@80.214.73.44;unrealircd.org/userip=50d6492c@80.214.73.44;msgid=YSIPB9q4PcRu0EVfC9ci7y-/mZT0+Gj5FLiDSZshH5NCw;time=2024-08-15T15:35:53.772Z', # ['@unrealircd.org/geoip=FR;unrealircd.org/userhost=50d6492c@80.214.73.44;unrealircd.org/userip=50d6492c@80.214.73.44;msgid=YSIPB9q4PcRu0EVfC9ci7y-/mZT0+Gj5FLiDSZshH5NCw;time=2024-08-15T15:35:53.772Z',
# ':001EPFBRD', 'PART', '#welcome', ':WEB', 'IRC', 'Paris'] # ':001EPFBRD', 'PART', '#welcome', ':WEB', 'IRC', 'Paris']
uid = str(cmd[1]).replace(':','') try:
channel = str(cmd[3]).lower() uid = str(interm_response[0]).replace(':','')
channel = str(interm_response[2]).lower()
self.Channel.delete_user_from_channel(channel, uid) self.Channel.delete_user_from_channel(channel, uid)
pass except IndexError as ie:
self.Base.logs.error(f'Index Error: {ie}')
case 'UID': case 'UID':
# ['@s2s-md/geoip=cc=GB|cd=United\\sKingdom|asn=16276|asname=OVH\\sSAS;s2s-md/tls_cipher=TLSv1.3-TLS_CHACHA20_POLY1305_SHA256;s2s-md/creationtime=1721564601', # ['@s2s-md/geoip=cc=GB|cd=United\\sKingdom|asn=16276|asname=OVH\\sSAS;s2s-md/tls_cipher=TLSv1.3-TLS_CHACHA20_POLY1305_SHA256;s2s-md/creationtime=1721564601',
# ':001', 'UID', 'albatros', '0', '1721564597', 'albatros', 'vps-91b2f28b.vps.ovh.net', # ':001', 'UID', 'albatros', '0', '1721564597', 'albatros', 'vps-91b2f28b.vps.ovh.net',
# '001HB8G04', '0', '+iwxz', 'Clk-A62F1D18.vps.ovh.net', 'Clk-A62F1D18.vps.ovh.net', 'MyZBwg==', ':...'] # '001HB8G04', '0', '+iwxz', 'Clk-A62F1D18.vps.ovh.net', 'Clk-A62F1D18.vps.ovh.net', 'MyZBwg==', ':...']
if 'webirc' in cmd[0]: if 'webirc' in original_response[0]:
isWebirc = True isWebirc = True
else: else:
isWebirc = False isWebirc = False
uid = str(cmd[8]) uid = str(original_response[8])
nickname = str(cmd[3]) nickname = str(original_response[3])
username = str(cmd[6]) username = str(original_response[6])
hostname = str(cmd[7]) hostname = str(original_response[7])
umodes = str(cmd[10]) umodes = str(original_response[10])
vhost = str(cmd[11]) vhost = str(original_response[11])
if not 'S' in umodes: if not 'S' in umodes:
remote_ip = self.Base.decode_ip(str(cmd[13])) remote_ip = self.Base.decode_ip(str(original_response[13]))
else: else:
remote_ip = '127.0.0.1' remote_ip = '127.0.0.1'
@@ -845,18 +850,19 @@ class Irc:
) )
for classe_name, classe_object in self.loaded_classes.items(): for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(cmd_to_send) classe_object.cmd(original_response)
case 'PRIVMSG': case 'PRIVMSG':
try: try:
# Supprimer la premiere valeur # Supprimer la premiere valeur
cmd.pop(0) cmd = interm_response.copy()
get_uid_or_nickname = str(cmd[0].replace(':','')) get_uid_or_nickname = str(cmd[0].replace(':',''))
user_trigger = self.User.get_nickname(get_uid_or_nickname) user_trigger = self.User.get_nickname(get_uid_or_nickname)
dnickname = self.Config.SERVICE_NICKNAME dnickname = self.Config.SERVICE_NICKNAME
if len(cmd) == 6: if len(cmd) == 6:
if cmd[1] == 'PRIVMSG' and str(cmd[3]).replace('.','') == ':auth': if cmd[1] == 'PRIVMSG' and str(cmd[3]).replace(self.Config.SERVICE_PREFIX,'') == ':auth':
cmd_copy = cmd.copy() cmd_copy = cmd.copy()
cmd_copy[5] = '**********' cmd_copy[5] = '**********'
self.Base.logs.info(cmd_copy) self.Base.logs.info(cmd_copy)
@@ -913,11 +919,11 @@ class Irc:
return False return False
if not arg[0].lower() in self.commands: if not arg[0].lower() in self.commands:
self.debug(f"This command {arg[0]} is not available") self.Base.logs.debug(f"This command {arg[0]} sent by {user_trigger} is not available")
return False return False
cmd_to_send = convert_to_string.replace(':','') cmd_to_send = convert_to_string.replace(':','')
self.Base.log_cmd(self.User.get_nickname(user_trigger), cmd_to_send) self.Base.log_cmd(user_trigger, cmd_to_send)
fromchannel = None fromchannel = None
if len(arg) >= 2: if len(arg) >= 2:
@@ -931,15 +937,26 @@ class Irc:
case _: case _:
pass pass
if cmd[2] != 'UID': if original_response[2] != 'UID':
# Envoyer la commande aux classes dynamiquement chargées # Envoyer la commande aux classes dynamiquement chargées
for classe_name, classe_object in self.loaded_classes.items(): for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(cmd_to_send) classe_object.cmd(original_response)
except IndexError as ie: except IndexError as ie:
self.Base.logs.error(f"{ie} / {cmd} / length {str(len(cmd))}") self.Base.logs.error(f"{ie} / {original_response} / length {str(len(original_response))}")
def _hcmds(self, user: str, channel: Union[str, None], cmd: list, fullcmd: list = []) -> None: def _hcmds(self, user: str, channel: Union[str, None], cmd: list, fullcmd: list = []) -> None:
"""_summary_
Args:
user (str): The user who sent the query
channel (Union[str, None]): If the command contain the channel
cmd (list): The defender cmd
fullcmd (list, optional): The full list of the cmd coming from PRIVMS. Defaults to [].
Returns:
None: Nothing to return
"""
fromuser = self.User.get_nickname(user) # Nickname qui a lancé la commande fromuser = self.User.get_nickname(user) # Nickname qui a lancé la commande
uid = self.User.get_uid(fromuser) # Récuperer le uid de l'utilisateur uid = self.User.get_uid(fromuser) # Récuperer le uid de l'utilisateur
@@ -1285,10 +1302,6 @@ class Irc:
results = self.Base.db_execute_query(f'SELECT module_name FROM {self.Config.table_module}') results = self.Base.db_execute_query(f'SELECT module_name FROM {self.Config.table_module}')
results = results.fetchall() results = results.fetchall()
# if len(results) == 0:
# self.send2socket(f":{dnickname} NOTICE {fromuser} :There is no module loaded")
# return False
found = False found = False
for module in all_modules: for module in all_modules:
@@ -1303,9 +1316,6 @@ class Irc:
found = False found = False
# for r in results:
# self.send2socket(f":{dnickname} NOTICE {fromuser} :{r[0]} - {self.Config.CONFIG_COLOR['verte']}Loaded{self.Config.CONFIG_COLOR['nogc']}")
case 'show_timers': case 'show_timers':
if self.Base.running_timers: if self.Base.running_timers:

View File

@@ -4,6 +4,7 @@ from typing import Union
import re, socket, psutil, requests, json, time import re, socket, psutil, requests, json, time
from sys import exit from sys import exit
from core.irc import Irc from core.irc import Irc
from core.Model import User
# Le module crée devra réspecter quelques conditions # Le module crée devra réspecter quelques conditions
# 1. Le nom de la classe devra toujours s'appeler comme le module. Exemple => nom de class Defender | nom du module mod_defender # 1. Le nom de la classe devra toujours s'appeler comme le module. Exemple => nom de class Defender | nom du module mod_defender
@@ -107,12 +108,19 @@ class Defender():
# self.join_saved_channels() # self.join_saved_channels()
self.timeout = self.Config.API_TIMEOUT self.timeout = self.Config.API_TIMEOUT
self.abuseipdb_UserModel: list[User.UserModel] = []
self.freeipapi_UserModel: list[User.UserModel] = []
self.cloudfilt_UserModel: list[User.UserModel] = []
self.psutil_UserModel: list[User.UserModel] = []
self.localscan_UserModel: list[User.UserModel] = []
# Listes qui vont contenir les ip a scanner avec les différentes API # Listes qui vont contenir les ip a scanner avec les différentes API
self.freeipapi_remote_ip:list = [] # self.freeipapi_remote_ip:list = []
self.cloudfilt_remote_ip:list = [] # self.cloudfilt_remote_ip:list = []
self.abuseipdb_remote_ip:list = [] # self.abuseipdb_remote_ip:list = []
self.psutil_remote_ip:list = [] # self.psutil_remote_ip:list = []
self.localscan_remote_ip:list = [] # self.localscan_remote_ip:list = []
# Variables qui indique que les threads sont en cours d'éxecutions # Variables qui indique que les threads sont en cours d'éxecutions
self.abuseipdb_isRunning:bool = True self.abuseipdb_isRunning:bool = True
@@ -605,23 +613,33 @@ class Defender():
return None return None
def scan_ports(self, remote_ip: str) -> None: def scan_ports(self, userModel: User.UserModel) -> None:
"""local_scan """local_scan
Args: Args:
remote_ip (str): _description_ remote_ip (str): _description_
""" """
User = userModel
remote_ip = User.remote_ip
username = User.username
hostname = User.hostname
nickname = User.nickname
if remote_ip in self.Config.WHITELISTED_IP: if remote_ip in self.Config.WHITELISTED_IP:
return None return None
for port in self.Config.PORTS_TO_SCAN: for port in self.Config.PORTS_TO_SCAN:
try:
newSocket = '' newSocket = ''
newSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM or socket.SOCK_NONBLOCK) newSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM or socket.SOCK_NONBLOCK)
newSocket.settimeout(0.5) newSocket.settimeout(0.5)
try:
connection = (remote_ip, self.Base.int_if_possible(port)) connection = (remote_ip, self.Base.int_if_possible(port))
newSocket.connect(connection) newSocket.connect(connection)
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.CONFIG_COLOR['rouge']}PROXY_SCAN{self.Config.CONFIG_COLOR['noire']} ] : Port [{str(port)}] ouvert sur l'adresse ip [{remote_ip}]")
fullname = f'{nickname}!{username}@{hostname}'
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.CONFIG_COLOR['rouge']}PROXY_SCAN{self.Config.CONFIG_COLOR['noire']} ] {fullname} ({remote_ip}) : Port [{str(port)}] ouvert sur l'adresse ip [{remote_ip}]")
# print(f"=======> Le port {str(port)} est ouvert !!") # print(f"=======> Le port {str(port)} est ouvert !!")
self.Base.running_sockets.append(newSocket) self.Base.running_sockets.append(newSocket)
# print(newSocket) # print(newSocket)
@@ -638,20 +656,18 @@ class Defender():
newSocket.close() newSocket.close()
self.Logs.info('=======> Fermeture de la socket') self.Logs.info('=======> Fermeture de la socket')
pass
def thread_local_scan(self) -> None: def thread_local_scan(self) -> None:
try: try:
while self.localscan_isRunning: while self.localscan_isRunning:
list_to_remove:list = [] list_to_remove:list = []
for ip in self.localscan_remote_ip: for user in self.localscan_UserModel:
self.scan_ports(ip) self.scan_ports(user)
list_to_remove.append(ip) list_to_remove.append(user)
time.sleep(1) time.sleep(1)
for ip_to_remove in list_to_remove: for user_model in list_to_remove:
self.localscan_remote_ip.remove(ip_to_remove) self.localscan_UserModel.remove(user_model)
time.sleep(1) time.sleep(1)
@@ -659,7 +675,7 @@ class Defender():
except ValueError as ve: except ValueError as ve:
self.Logs.warning(f"thread_local_scan Error : {ve}") self.Logs.warning(f"thread_local_scan Error : {ve}")
def get_ports_connexion(self, remote_ip: str) -> list[int]: def get_ports_connexion(self, userModel: User.UserModel) -> list[int]:
"""psutil_scan for Linux """psutil_scan for Linux
Args: Args:
@@ -669,13 +685,20 @@ class Defender():
list[int]: list of ports list[int]: list of ports
""" """
try: try:
User = userModel
remote_ip = User.remote_ip
username = User.username
hostname = User.hostname
nickname = User.nickname
if remote_ip in self.Config.WHITELISTED_IP: if remote_ip in self.Config.WHITELISTED_IP:
return None return None
connections = psutil.net_connections(kind='inet') connections = psutil.net_connections(kind='inet')
fullname = f'{nickname}!{username}@{hostname}'
matching_ports = [conn.raddr.port for conn in connections if conn.raddr and conn.raddr.ip == remote_ip] matching_ports = [conn.raddr.port for conn in connections if conn.raddr and conn.raddr.ip == remote_ip]
self.Logs.info(f"Connexion of {remote_ip} using ports : {str(matching_ports)}") self.Logs.info(f"Connexion of {fullname} ({remote_ip}) using ports : {str(matching_ports)}")
return matching_ports return matching_ports
@@ -688,13 +711,13 @@ class Defender():
while self.psutil_isRunning: while self.psutil_isRunning:
list_to_remove:list = [] list_to_remove:list = []
for ip in self.psutil_remote_ip: for user in self.psutil_UserModel:
self.get_ports_connexion(ip) self.get_ports_connexion(user)
list_to_remove.append(ip) list_to_remove.append(user)
time.sleep(1) time.sleep(1)
for ip_to_remove in list_to_remove: for user_model in list_to_remove:
self.psutil_remote_ip.remove(ip_to_remove) self.psutil_UserModel.remove(user_model)
time.sleep(1) time.sleep(1)
@@ -702,16 +725,22 @@ class Defender():
except ValueError as ve: except ValueError as ve:
self.Logs.warning(f"thread_psutil_scan Error : {ve}") self.Logs.warning(f"thread_psutil_scan Error : {ve}")
def abuseipdb_scan(self, remote_ip:str) -> Union[dict[str, any], None]: def abuseipdb_scan(self, userModel: User.UserModel) -> Union[dict[str, any], None]:
"""Analyse l'ip avec AbuseIpDB """Analyse l'ip avec AbuseIpDB
Cette methode devra etre lancer toujours via un thread ou un timer. Cette methode devra etre lancer toujours via un thread ou un timer.
Args: Args:
remote_ip (_type_): l'ip a analyser userModel (UserModel): l'objet User qui contient l'ip
Returns: Returns:
dict[str, any] | None: les informations du provider dict[str, any] | None: les informations du provider
keys : 'score', 'country', 'isTor', 'totalReports' keys : 'score', 'country', 'isTor', 'totalReports'
""" """
User = userModel
remote_ip = User.remote_ip
username = User.username
hostname = User.hostname
nickname = User.nickname
if remote_ip in self.Config.WHITELISTED_IP: if remote_ip in self.Config.WHITELISTED_IP:
return None return None
if self.ModConfig.abuseipdb_scan == 0: if self.ModConfig.abuseipdb_scan == 0:
@@ -731,11 +760,12 @@ class Defender():
'Key': self.abuseipdb_key 'Key': self.abuseipdb_key
} }
try:
response = requests.request(method='GET', url=url, headers=headers, params=querystring, timeout=self.timeout) response = requests.request(method='GET', url=url, headers=headers, params=querystring, timeout=self.timeout)
# Formatted output # Formatted output
decodedResponse = json.loads(response.text) decodedResponse = json.loads(response.text)
try:
if not 'data' in decodedResponse: if not 'data' in decodedResponse:
return None return None
@@ -751,7 +781,10 @@ class Defender():
color_red = self.Config.CONFIG_COLOR['rouge'] color_red = self.Config.CONFIG_COLOR['rouge']
color_black = self.Config.CONFIG_COLOR['noire'] color_black = self.Config.CONFIG_COLOR['noire']
self.Irc.send2socket(f":{service_id} PRIVMSG {service_chanlog} :[ {color_red}ABUSEIPDB_SCAN{color_black} ] : Connexion de {remote_ip} ==> Score: {str(result['score'])} | Country : {result['country']} | Tor : {str(result['isTor'])} | Total Reports : {str(result['totalReports'])}") # pseudo!ident@host
fullname = f'{nickname}!{username}@{hostname}'
self.Irc.send2socket(f":{service_id} PRIVMSG {service_chanlog} :[ {color_red}ABUSEIPDB_SCAN{color_black} ] : Connexion de {fullname} ({remote_ip}) ==> Score: {str(result['score'])} | Country : {result['country']} | Tor : {str(result['isTor'])} | Total Reports : {str(result['totalReports'])}")
if result['isTor']: if result['isTor']:
self.Irc.send2socket(f":{service_id} GLINE +*@{remote_ip} {self.Config.GLINE_DURATION} This server do not allow Tor connexions {str(result['isTor'])} - Detected by Abuseipdb") self.Irc.send2socket(f":{service_id} GLINE +*@{remote_ip} {self.Config.GLINE_DURATION} This server do not allow Tor connexions {str(result['isTor'])} - Detected by Abuseipdb")
@@ -767,6 +800,8 @@ class Defender():
self.Logs.error(f"AbuseIpDb Timeout : {rt}") self.Logs.error(f"AbuseIpDb Timeout : {rt}")
except requests.ConnectionError as ce: except requests.ConnectionError as ce:
self.Logs.error(f"AbuseIpDb Connection Error : {ce}") self.Logs.error(f"AbuseIpDb Connection Error : {ce}")
except Exception as err:
self.Logs.error(f"General Error Abuseipdb : {err}")
def thread_abuseipdb_scan(self) -> None: def thread_abuseipdb_scan(self) -> None:
try: try:
@@ -774,13 +809,13 @@ class Defender():
while self.abuseipdb_isRunning: while self.abuseipdb_isRunning:
list_to_remove: list = [] list_to_remove: list = []
for ip in self.abuseipdb_remote_ip: for user in self.abuseipdb_UserModel:
self.abuseipdb_scan(ip) self.abuseipdb_scan(user)
list_to_remove.append(ip) list_to_remove.append(user)
time.sleep(1) time.sleep(1)
for ip_to_remove in list_to_remove: for user_model in list_to_remove:
self.abuseipdb_remote_ip.remove(ip_to_remove) self.abuseipdb_UserModel.remove(user_model)
time.sleep(1) time.sleep(1)
@@ -788,7 +823,7 @@ class Defender():
except ValueError as ve: except ValueError as ve:
self.Logs.error(f"thread_abuseipdb_scan Error : {ve}") self.Logs.error(f"thread_abuseipdb_scan Error : {ve}")
def freeipapi_scan(self, remote_ip:str) -> Union[dict[str, any], None]: def freeipapi_scan(self, userModel: User.UserModel) -> Union[dict[str, any], None]:
"""Analyse l'ip avec Freeipapi """Analyse l'ip avec Freeipapi
Cette methode devra etre lancer toujours via un thread ou un timer. Cette methode devra etre lancer toujours via un thread ou un timer.
Args: Args:
@@ -798,6 +833,12 @@ class Defender():
dict[str, any] | None: les informations du provider dict[str, any] | None: les informations du provider
keys : 'countryCode', 'isProxy' keys : 'countryCode', 'isProxy'
""" """
User = userModel
remote_ip = User.remote_ip
username = User.username
hostname = User.hostname
nickname = User.nickname
if remote_ip in self.Config.WHITELISTED_IP: if remote_ip in self.Config.WHITELISTED_IP:
return None return None
if self.ModConfig.freeipapi_scan == 0: if self.ModConfig.freeipapi_scan == 0:
@@ -814,11 +855,12 @@ class Defender():
'Accept': 'application/json', 'Accept': 'application/json',
} }
try:
response = requests.request(method='GET', url=url, headers=headers, timeout=self.timeout) response = requests.request(method='GET', url=url, headers=headers, timeout=self.timeout)
# Formatted output # Formatted output
decodedResponse = json.loads(response.text) decodedResponse = json.loads(response.text)
try:
status_code = response.status_code status_code = response.status_code
if status_code == 429: if status_code == 429:
self.Logs.warning(f'Too Many Requests - The rate limit for the API has been exceeded.') self.Logs.warning(f'Too Many Requests - The rate limit for the API has been exceeded.')
@@ -832,7 +874,10 @@ class Defender():
'isProxy': decodedResponse['isProxy'] if 'isProxy' in decodedResponse else None 'isProxy': decodedResponse['isProxy'] if 'isProxy' in decodedResponse else None
} }
self.Irc.send2socket(f":{service_id} PRIVMSG {service_chanlog} :[ {color_red}FREEIPAPI_SCAN{color_black} ] : Connexion de {remote_ip} ==> Proxy: {str(result['isProxy'])} | Country : {str(result['countryCode'])}") # pseudo!ident@host
fullname = f'{nickname}!{username}@{hostname}'
self.Irc.send2socket(f":{service_id} PRIVMSG {service_chanlog} :[ {color_red}FREEIPAPI_SCAN{color_black} ] : Connexion de {fullname} ({remote_ip}) ==> Proxy: {str(result['isProxy'])} | Country : {str(result['countryCode'])}")
if result['isProxy']: if result['isProxy']:
self.Irc.send2socket(f":{service_id} GLINE +*@{remote_ip} {self.Config.GLINE_DURATION} This server do not allow proxy connexions {str(result['isProxy'])} - detected by freeipapi") self.Irc.send2socket(f":{service_id} GLINE +*@{remote_ip} {self.Config.GLINE_DURATION} This server do not allow proxy connexions {str(result['isProxy'])} - detected by freeipapi")
@@ -841,20 +886,22 @@ class Defender():
return result return result
except KeyError as ke: except KeyError as ke:
self.Logs.error(f"FREEIPAPI_SCAN KeyError : {ke}") self.Logs.error(f"FREEIPAPI_SCAN KeyError : {ke}")
except Exception as err:
self.Logs.error(f"General Error Freeipapi : {err}")
def thread_freeipapi_scan(self) -> None: def thread_freeipapi_scan(self) -> None:
try: try:
while self.freeipapi_isRunning: while self.freeipapi_isRunning:
list_to_remove:list = [] list_to_remove: list[User.UserModel] = []
for ip in self.freeipapi_remote_ip: for user in self.freeipapi_UserModel:
self.freeipapi_scan(ip) self.freeipapi_scan(user)
list_to_remove.append(ip) list_to_remove.append(user)
time.sleep(1) time.sleep(1)
for ip_to_remove in list_to_remove: for user_model in list_to_remove:
self.freeipapi_remote_ip.remove(ip_to_remove) self.freeipapi_UserModel.remove(user_model)
time.sleep(1) time.sleep(1)
@@ -862,7 +909,7 @@ class Defender():
except ValueError as ve: except ValueError as ve:
self.Logs.error(f"thread_freeipapi_scan Error : {ve}") self.Logs.error(f"thread_freeipapi_scan Error : {ve}")
def cloudfilt_scan(self, remote_ip:str) -> Union[dict[str, any], None]: def cloudfilt_scan(self, userModel: User.UserModel) -> Union[dict[str, any], None]:
"""Analyse l'ip avec cloudfilt """Analyse l'ip avec cloudfilt
Cette methode devra etre lancer toujours via un thread ou un timer. Cette methode devra etre lancer toujours via un thread ou un timer.
Args: Args:
@@ -872,6 +919,12 @@ class Defender():
dict[str, any] | None: les informations du provider dict[str, any] | None: les informations du provider
keys : 'countryCode', 'isProxy' keys : 'countryCode', 'isProxy'
""" """
User = userModel
remote_ip = User.remote_ip
username = User.username
hostname = User.hostname
nickname = User.nickname
if remote_ip in self.Config.WHITELISTED_IP: if remote_ip in self.Config.WHITELISTED_IP:
return None return None
if self.ModConfig.cloudfilt_scan == 0: if self.ModConfig.cloudfilt_scan == 0:
@@ -891,11 +944,10 @@ class Defender():
'key': self.cloudfilt_key 'key': self.cloudfilt_key
} }
try:
response = requests.post(url=url, data=data) response = requests.post(url=url, data=data)
# Formatted output # Formatted output
decodedResponse = json.loads(response.text) decodedResponse = json.loads(response.text)
try:
status_code = response.status_code status_code = response.status_code
if status_code != 200: if status_code != 200:
self.Logs.warning(f'Error connecting to cloudfilt API | Code: {str(status_code)}') self.Logs.warning(f'Error connecting to cloudfilt API | Code: {str(status_code)}')
@@ -908,7 +960,10 @@ class Defender():
'host': decodedResponse['host'] if 'host' in decodedResponse else None 'host': decodedResponse['host'] if 'host' in decodedResponse else None
} }
self.Irc.send2socket(f":{service_id} PRIVMSG {service_chanlog} :[ {color_red}CLOUDFILT_SCAN{color_black} ] : Connexion de {str(remote_ip)} ==> Host: {str(result['host'])} | country: {str(result['countryiso'])} | listed: {str(result['listed'])} | listed by : {str(result['listed_by'])}") # pseudo!ident@host
fullname = f'{nickname}!{username}@{hostname}'
self.Irc.send2socket(f":{service_id} PRIVMSG {service_chanlog} :[ {color_red}CLOUDFILT_SCAN{color_black} ] : Connexion de {fullname} ({remote_ip}) ==> Host: {str(result['host'])} | country: {str(result['countryiso'])} | listed: {str(result['listed'])} | listed by : {str(result['listed_by'])}")
if result['listed']: if result['listed']:
self.Irc.send2socket(f":{service_id} GLINE +*@{remote_ip} {self.Config.GLINE_DURATION} You connexion is listed as dangerous {str(result['listed'])} {str(result['listed_by'])} - detected by cloudfilt") self.Irc.send2socket(f":{service_id} GLINE +*@{remote_ip} {self.Config.GLINE_DURATION} You connexion is listed as dangerous {str(result['listed'])} {str(result['listed_by'])} - detected by cloudfilt")
@@ -926,13 +981,13 @@ class Defender():
while self.cloudfilt_isRunning: while self.cloudfilt_isRunning:
list_to_remove:list = [] list_to_remove:list = []
for ip in self.cloudfilt_remote_ip: for user in self.cloudfilt_UserModel:
self.cloudfilt_scan(ip) self.cloudfilt_scan(user)
list_to_remove.append(ip) list_to_remove.append(user)
time.sleep(1) time.sleep(1)
for ip_to_remove in list_to_remove: for user_model in list_to_remove:
self.cloudfilt_remote_ip.remove(ip_to_remove) self.cloudfilt_UserModel.remove(user_model)
time.sleep(1) time.sleep(1)
@@ -966,22 +1021,6 @@ class Defender():
if not self.Base.is_valid_ip(cmd[2]): if not self.Base.is_valid_ip(cmd[2]):
return None return None
# self.Base.scan_ports(cmd[2])
if self.ModConfig.local_scan == 1 and not cmd[2] in self.Config.WHITELISTED_IP:
self.localscan_remote_ip.append(cmd[2])
if self.ModConfig.psutil_scan == 1 and not cmd[2] in self.Config.WHITELISTED_IP:
self.psutil_remote_ip.append(cmd[2])
if self.ModConfig.abuseipdb_scan == 1 and not cmd[2] in self.Config.WHITELISTED_IP:
self.abuseipdb_remote_ip.append(cmd[2])
if self.ModConfig.freeipapi_scan == 1 and not cmd[2] in self.Config.WHITELISTED_IP:
self.freeipapi_remote_ip.append(cmd[2])
if self.ModConfig.cloudfilt_scan == 1 and not cmd[2] in self.Config.WHITELISTED_IP:
self.cloudfilt_remote_ip.append(cmd[2])
# Possibilité de déclancher les bans a ce niveau. # Possibilité de déclancher les bans a ce niveau.
except IndexError as ie: except IndexError as ie:
self.Logs.error(f'cmd reputation: index error: {ie}') self.Logs.error(f'cmd reputation: index error: {ie}')
@@ -1005,6 +1044,15 @@ class Defender():
# Get User information # Get User information
_User = self.User.get_User(str(cmd[7])) _User = self.User.get_User(str(cmd[7]))
# If user is not service or IrcOp then scan them
if not re.match(fr'^.*[S|o?].*$', _User.umodes):
self.abuseipdb_UserModel.append(_User) if self.ModConfig.abuseipdb_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.freeipapi_UserModel.append(_User) if self.ModConfig.freeipapi_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.cloudfilt_UserModel.append(_User) if self.ModConfig.cloudfilt_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.psutil_UserModel.append(_User) if self.ModConfig.psutil_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
self.localscan_UserModel.append(_User) if self.ModConfig.local_scan == 1 and not _User.remote_ip in self.Config.WHITELISTED_IP else None
if _User is None: if _User is None:
self.Logs.critical(f'This UID: [{cmd[7]}] is not available please check why') self.Logs.critical(f'This UID: [{cmd[7]}] is not available please check why')
return None return None
@@ -1582,9 +1630,10 @@ class Defender():
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : NICKNAME : {UserObject.nickname}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : NICKNAME : {UserObject.nickname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : USERNAME : {UserObject.username}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : USERNAME : {UserObject.username}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : HOSTNAME : {UserObject.hostname}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : HOSTNAME : {UserObject.hostname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : IP : {UserObject.remote_ip}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : REPUTATION : {UserObject.score_connexion}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : VHOST : {UserObject.vhost}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : VHOST : {UserObject.vhost}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : IP : {UserObject.remote_ip}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : WebIrc : {UserObject.isWebirc}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : REPUTATION : {UserObject.score_connexion}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : MODES : {UserObject.umodes}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : MODES : {UserObject.umodes}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : CONNECTION TIME : {UserObject.connexion_datetime}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : CONNECTION TIME : {UserObject.connexion_datetime}')
else: else:

View File

@@ -1,3 +1,3 @@
{ {
"version": "5.1.8" "version": "5.2.0"
} }