18 Commits

Author SHA1 Message Date
adator
b0325d4db5 Merge pull request #14 from adator85/dev
V4.1.0 change configuration systeme
2024-08-04 00:37:43 +02:00
adator
b9e4878764 V4.1.0 change configuration systeme 2024-08-04 00:37:03 +02:00
adator
22cec8a0ef Merge pull request #13 from adator85/dev
patch V4.0.4
2024-08-03 21:14:53 +02:00
adator
1837edf1c2 patch V4.0.4 2024-08-03 21:14:28 +02:00
adator
62b10313a4 Merge pull request #12 from adator85/dev
Patch V4.0.3
2024-08-03 21:05:56 +02:00
adator
c369d86a22 Patch V4.0.3 2024-08-03 21:04:38 +02:00
adator
61813e38ae Merge pull request #11 from adator85/dev
convert version string to int
2024-08-03 20:25:37 +02:00
adator
743069f8e0 convert version string to int 2024-08-03 20:24:58 +02:00
adator
de69a1af63 Merge pull request #10 from adator85/dev
Move checkversion command to admin commands
2024-08-03 20:13:03 +02:00
adator
c8c5f782d7 Move checkversion command to admin commands 2024-08-03 20:12:19 +02:00
adator
a639964701 Merge pull request #9 from adator85/dev
Dev
2024-08-03 19:22:30 +02:00
adator
ff0f880fcd V4.0.0 2024-08-03 19:21:30 +02:00
adator
41b9582e43 Merge pull request #8 from adator85/main
align main to dev
2024-08-03 17:04:26 +02:00
adator
dba0301190 Delete version.json 2024-08-03 17:03:42 +02:00
adator
6e6d001605 New Version 2024-08-03 17:01:19 +02:00
adator
e27a027ae9 Create version.json 2024-08-03 15:29:19 +02:00
adator85
19b7f85ec7 . 2024-03-24 01:01:18 +01:00
adator85
7068d88168 . 2024-03-23 03:40:50 +01:00
7 changed files with 599 additions and 310 deletions

View File

@@ -1,8 +1,8 @@
import time, threading, os, random, socket, hashlib
import time, threading, os, random, socket, hashlib, ipaddress, logging, requests, json, sys
from datetime import datetime
from sqlalchemy import create_engine, Engine, Connection, CursorResult
from sqlalchemy.sql import text
from core.configuration import Config
from core.loadConf import Config
class Base:
@@ -16,9 +16,16 @@ class Base:
'modules': 'sys_modules'
}
DEFENDER_VERSION = '' # MAJOR.MINOR.BATCH
LATEST_DEFENDER_VERSION = '' # Latest Version of Defender in git
DEFENDER_DB_PATH = 'db' + os.sep # Séparateur en fonction de l'OS
DEFENDER_DB_NAME = 'defender' # Le nom de la base de données principale
def __init__(self, Config: Config) -> None:
self.Config = Config # Assigner l'objet de configuration
self.init_log_system() # Demarrer le systeme de log
self.check_for_new_version() # Verifier si une nouvelle version est disponible
self.running_timers:list[threading.Timer] = [] # Liste des timers en cours
self.running_threads:list[threading.Thread] = [] # Liste des threads en cours
@@ -32,6 +39,73 @@ class Base:
self.db_create_first_admin() # Créer un nouvel admin si la base de données est vide
def __set_current_defender_version(self) -> None:
"""This will put the current version of Defender
located in version.json
"""
version_filename = f'.{os.sep}version.json'
with open(version_filename, 'r') as version_data:
current_version:dict[str, str] = json.load(version_data)
self.DEFENDER_VERSION = current_version["version"]
return None
def __get_latest_defender_version(self) -> None:
try:
token = ''
json_url = f'https://raw.githubusercontent.com/adator85/IRC_DEFENDER_MODULES/main/version.json'
headers = {
'Authorization': f'token {token}',
'Accept': 'application/vnd.github.v3.raw' # Indique à GitHub que nous voulons le contenu brut du fichier
}
if token == '':
response = requests.get(json_url)
else:
response = requests.get(json_url, headers=headers)
response.raise_for_status() # Vérifie si la requête a réussi
json_response:dict = response.json()
self.LATEST_DEFENDER_VERSION = json_response["version"]
return None
except requests.HTTPError as err:
self.logs.error(f'Github not available to fetch latest version: {err}')
except:
self.logs.warning(f'Github not available to fetch latest version')
def check_for_new_version(self) -> bool:
try:
# Assigner la version actuelle de Defender
self.__set_current_defender_version()
# Récuperer la dernier version disponible dans github
self.__get_latest_defender_version()
isNewVersion = False
latest_version = self.LATEST_DEFENDER_VERSION
current_version = self.DEFENDER_VERSION
curr_major , curr_minor, curr_patch = current_version.split('.')
last_major, last_minor, last_patch = latest_version.split('.')
if int(last_major) > int(curr_major):
self.logs.info(f'New version available: {current_version} >>> {latest_version}')
isNewVersion = True
elif int(last_major) == int(curr_major) and int(last_minor) > int(curr_minor):
self.logs.info(f'New version available: {current_version} >>> {latest_version}')
isNewVersion = True
elif int(last_major) == int(curr_major) and int(last_minor) == int(curr_minor) and int(last_patch) > int(curr_patch):
self.logs.info(f'New version available: {current_version} >>> {latest_version}')
isNewVersion = True
else:
isNewVersion = False
return isNewVersion
except ValueError as ve:
self.logs.error(f'Impossible to convert in version number : {ve}')
def get_unixtime(self) -> int:
"""
Cette fonction retourne un UNIXTIME de type 12365456
@@ -62,12 +136,36 @@ class Base:
return None
def init_log_system(self) -> None:
# Create folder if not available
logs_directory = f'logs{os.sep}'
if not os.path.exists(f'{logs_directory}'):
os.makedirs(logs_directory)
# Init logs object
self.logs = logging
self.logs.basicConfig(level=self.Config.DEBUG_LEVEL,
filename='logs/defender.log',
encoding='UTF-8',
format='%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(funcName)s - %(message)s')
self.logs.info('#################### STARTING INTERCEPTOR HQ ####################')
return None
def log_cmd(self, user_cmd:str, cmd:str) -> None:
"""Enregistre les commandes envoyées par les utilisateurs
Args:
cmd (str): la commande a enregistrer
"""
cmd_list = cmd.split()
if len(cmd_list) == 3:
if cmd_list[0].replace('.', '') == 'auth':
cmd_list[1] = '*******'
cmd_list[2] = '*******'
cmd = ' '.join(cmd_list)
insert_cmd_query = f"INSERT INTO {self.DB_SCHEMA['commandes']} (datetime, user, commande) VALUES (:datetime, :user, :commande)"
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'commande': cmd}
self.db_execute_query(insert_cmd_query, mes_donnees)
@@ -100,13 +198,13 @@ class Base:
"""
if not self.db_isModuleExist(module_name):
self.__debug(f"Le module {module_name} n'existe pas alors ont le créer")
self.logs.debug(f"Le module {module_name} n'existe pas alors ont le créer")
insert_cmd_query = f"INSERT INTO {self.DB_SCHEMA['modules']} (datetime, user, module) VALUES (:datetime, :user, :module)"
mes_donnees = {'datetime': self.get_datetime(), 'user': user_cmd, 'module': module_name}
self.db_execute_query(insert_cmd_query, mes_donnees)
# self.db_close_session(self.session)
else:
self.__debug(f"Le module {module_name} existe déja dans la base de données")
self.logs.debug(f"Le module {module_name} existe déja dans la base de données")
return False
@@ -148,10 +246,10 @@ class Base:
self.running_timers.append(t)
self.__debug(f"Timer ID : {str(t.ident)} | Running Threads : {len(threading.enumerate())}")
self.logs.debug(f"Timer ID : {str(t.ident)} | Running Threads : {len(threading.enumerate())}")
except AssertionError as ae:
self.__debug(f'Assertion Error -> {ae}')
self.logs.error(f'Assertion Error -> {ae}')
def create_thread(self, func:object, func_args: tuple = (), run_once:bool = False) -> None:
try:
@@ -170,98 +268,95 @@ class Base:
th.start()
self.running_threads.append(th)
self.__debug(f"Thread ID : {str(th.ident)} | Thread name : {th.getName()} | Running Threads : {len(threading.enumerate())}")
self.logs.debug(f"Thread ID : {str(th.ident)} | Thread name : {th.getName()} | Running Threads : {len(threading.enumerate())}")
except AssertionError as ae:
self.__debug(f'Assertion Error -> {ae}')
self.logs.error(f'{ae}')
def garbage_collector_timer(self) -> None:
"""Methode qui supprime les timers qui ont finis leurs job
"""
try:
self.__debug(f"=======> Checking for Timers to stop")
# print(f"{self.running_timers}")
for timer in self.running_timers:
if not timer.is_alive():
timer.cancel()
self.running_timers.remove(timer)
self.__debug(f"Timer {str(timer)} removed")
self.logs.info(f"Timer {str(timer)} removed")
else:
self.__debug(f"===> Timer {str(timer)} Still running ...")
self.logs.debug(f"===> Timer {str(timer)} Still running ...")
except AssertionError as ae:
print(f'Assertion Error -> {ae}')
self.logs.error(f'Assertion Error -> {ae}')
def garbage_collector_thread(self) -> None:
"""Methode qui supprime les threads qui ont finis leurs job
"""
try:
self.__debug(f"=======> Checking for Threads to stop")
for thread in self.running_threads:
if thread.getName() != 'heartbeat':
if not thread.is_alive():
self.running_threads.remove(thread)
self.__debug(f"Thread {str(thread.getName())} {str(thread.native_id)} removed")
self.logs.debug(f"Thread {str(thread.getName())} {str(thread.native_id)} removed")
# print(threading.enumerate())
except AssertionError as ae:
self.__debug(f'Assertion Error -> {ae}')
self.logs.error(f'Assertion Error -> {ae}')
def garbage_collector_sockets(self) -> None:
self.__debug(f"=======> Checking for Sockets to stop")
for soc in self.running_sockets:
while soc.fileno() != -1:
self.__debug(soc.fileno())
self.logs.debug(soc.fileno())
soc.close()
soc.close()
self.running_sockets.remove(soc)
self.__debug(f"Socket ==> closed {str(soc.fileno())}")
self.logs.debug(f"Socket ==> closed {str(soc.fileno())}")
def shutdown(self) -> None:
"""Methode qui va préparer l'arrêt complêt du service
"""
# Nettoyage des timers
print(f"=======> Checking for Timers to stop")
self.logs.debug(f"=======> Checking for Timers to stop")
for timer in self.running_timers:
while timer.is_alive():
print(f"> waiting for {timer.getName()} to close")
self.logs.debug(f"> waiting for {timer.getName()} to close")
timer.cancel()
time.sleep(0.2)
self.running_timers.remove(timer)
print(f"> Cancelling {timer.getName()} {timer.native_id}")
self.logs.debug(f"> Cancelling {timer.getName()} {timer.native_id}")
print(f"=======> Checking for Threads to stop")
self.logs.debug(f"=======> Checking for Threads to stop")
for thread in self.running_threads:
if thread.getName() == 'heartbeat' and thread.is_alive():
self.execute_periodic_action()
print(f"> Running the last periodic action")
self.logs.debug(f"> Running the last periodic action")
self.running_threads.remove(thread)
print(f"> Cancelling {thread.getName()} {thread.native_id}")
self.logs.debug(f"> Cancelling {thread.getName()} {thread.native_id}")
print(f"=======> Checking for Sockets to stop")
self.logs.debug(f"=======> Checking for Sockets to stop")
for soc in self.running_sockets:
soc.close()
while soc.fileno() != -1:
soc.close()
self.running_sockets.remove(soc)
print(f"> Socket ==> closed {str(soc.fileno())}")
self.logs.debug(f"> Socket ==> closed {str(soc.fileno())}")
return None
def db_init(self) -> tuple[Engine, Connection]:
db_directory = self.Config.DEFENDER_DB_PATH
full_path_db = self.Config.DEFENDER_DB_PATH + self.Config.DEFENDER_DB_NAME
db_directory = self.DEFENDER_DB_PATH
full_path_db = self.DEFENDER_DB_PATH + self.DEFENDER_DB_NAME
if not os.path.exists(db_directory):
os.makedirs(db_directory)
engine = create_engine(f'sqlite:///{full_path_db}.db', echo=False)
cursor = engine.connect()
self.logs.info("database connexion has been initiated")
return engine, cursor
def __create_db(self) -> None:
@@ -325,7 +420,7 @@ class Base:
try:
self.cursor.close()
except AttributeError as ae:
self.__debug(f"Attribute Error : {ae}")
self.logs.error(f"Attribute Error : {ae}")
def crypt_password(self, password:str) -> str:
"""Retourne un mot de passe chiffré en MD5
@@ -358,6 +453,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.
@@ -385,10 +491,3 @@ class Base:
# Vider le dictionnaire de fonction
self.periodic_func.clear()
def __debug(self, debug_msg:str) -> None:
if self.Config.DEBUG == 1:
print(f"[{self.get_datetime()}] - {debug_msg}")
return None

View File

@@ -0,0 +1,48 @@
{
"SERVEUR_IP": "0.0.0.0",
"SERVEUR_HOSTNAME": "your.host.name",
"SERVEUR_LINK": "your.link.to.server",
"SERVEUR_PORT": 7002,
"SERVEUR_PASSWORD": "link_password",
"SERVEUR_ID": "006",
"SERVEUR_SSL": true,
"SERVICE_NAME": "defender",
"SERVICE_NICKNAME": "BotNickname",
"SERVICE_REALNAME": "BotRealname",
"SERVICE_USERNAME": "BotUsername",
"SERVICE_HOST": "your.service.hostname",
"SERVICE_INFO": "Network IRC Service",
"SERVICE_CHANLOG": "#services",
"SERVICE_SMODES": "+ioqBS",
"SERVICE_CMODES": "ntsO",
"SERVICE_UMODES": "o",
"SERVICE_PREFIX": "!",
"OWNER": "admin",
"PASSWORD": "password",
"SALON_JAIL": "#jail",
"SALON_JAIL_MODES": "sS",
"SALON_LIBERER": "#welcome",
"API_TIMEOUT": 2,
"PORTS_TO_SCAN": [3028, 8080, 1080, 1085, 4145, 9050],
"WHITELISTED_IP": ["127.0.0.1"],
"GLINE_DURATION": "30",
"DEBUG_LEVEL": 20,
"CONFIG_COLOR": {
"blanche": "\\u0003\\u0030",
"noire": "\\u0003\\u0031",
"bleue": "\\u0003\\u0020",
"verte": "\\u0003\\u0033",
"rouge": "\\u0003\\u0034",
"jaune": "\\u0003\\u0036",
"gras": "\\u0002",
"nogc": "\\u0002\\u0003"
}
}

View File

@@ -1,58 +0,0 @@
import os
from typing import Literal, Dict, List
##########################################
# CONFIGURATION FILE : #
# Rename file to : configuration.py #
##########################################
class Config:
DEFENDER_VERSION = '3.3.2' # MAJOR.MINOR.BATCH
DEFENDER_DB_PATH = 'db' + os.sep # Séparateur en fonction de l'OS
DEFENDER_DB_NAME = 'defender' # Le nom de la base de données principale
SERVICE_NAME = 'defender' # Le nom du service
SERVEUR_IP = '8.8.8.8' # IP ou host du serveur à rejoindre
SERVEUR_HOSTNAME = 'your hostname' # Le hostname du serveur IRC
SERVEUR_LINK = 'your link' # Host attendu par votre IRCd (ex. dans votre link block pour Unrealircd)
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
SERVICE_NICKNAME = 'BotName' # Nick du bot sur IRC
SERVICE_REALNAME = 'BotRealname' # Realname du bot
SERVICE_USERNAME = 'BotIdent' # Ident du bot
SERVICE_HOST = 'your service host' # Host du bot
SERVICE_INFO = 'Network IRC Service' # swhois du bot
SERVICE_CHANLOG = '#services' # Salon des logs et autres messages issus du bot
SERVICE_SMODES = '+ioqBS' # Mode du service
SERVICE_CMODES = 'ntsO' # Mode du salon (#ChanLog) que le bot appliquera à son entrée
SERVICE_UMODES = 'o' # Mode que le bot pourra se donner à sa connexion au salon chanlog
SERVICE_PREFIX = '.' # Prefix pour envoyer les commandes au bot
SERVICE_ID = SERVEUR_ID + 'AAAAAB' # L'identifiant du service
OWNER = 'admin' # Identifiant du compte admin
PASSWORD = 'password' # Mot de passe du compte admin
SALON_JAIL = '#JAIL' # Salon pot de miel
SALON_JAIL_MODES = 'sS' # Mode du salon pot de miel
SALON_LIBERER = '#welcome' # Le salon ou sera envoyé l'utilisateur clean
API_TIMEOUT = 2 # Timeout des api's
PORTS_TO_SCAN = [3028, 8080, 1080, 1085, 4145, 9050] # Liste des ports a scanné pour une detection de proxy
WHITELISTED_IP = ['127.0.0.1'] # IP a ne pas scanner
GLINE_DURATION = '1d' # La durée du gline
DEBUG = 0 # Afficher l'ensemble des messages du serveurs dans la console
CONFIG_COLOR = {
'blanche': '\x0300', # Couleur blanche
'noire': '\x0301', # Couleur noire
'bleue': '\x0302', # Couleur Bleue
'verte': '\x0303', # Couleur Verte
'rouge': '\x0304', # Couleur rouge
'jaune': '\x0306', # Couleur jaune
'gras': '\x02', # Gras
'nogc': '\x02\x03' # Retirer gras et couleur
}

View File

@@ -1,7 +1,8 @@
import ssl, re, importlib, sys, time, threading, socket
from ssl import SSLSocket
from datetime import datetime, timedelta
from typing import Union
from core.configuration import Config
from core.loadConf import Config
from core.base import Base
class Irc:
@@ -16,17 +17,19 @@ class Irc:
self.beat = 30 # Lancer toutes les 30 secondes des actions de nettoyages
self.hb_active = True # Heartbeat active
self.HSID = '' # ID du serveur qui accueil le service ( Host Serveur Id )
self.IrcSocket:Union[socket.socket, SSLSocket] = None
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()
self.Config = Config().ConfigModel
# Liste des commandes internes du bot
self.commands_level = {
0: ['help', 'auth', 'copyright'],
1: ['load','reload','unload', 'deauth', 'uptime'],
1: ['load','reload','unload', 'deauth', 'uptime', 'checkversion'],
2: ['show_modules', 'show_timers', 'show_threads', 'sentinel'],
3: ['quit', 'restart','addaccess','editaccess', 'delaccess']
}
@@ -48,26 +51,46 @@ class Irc:
self.__create_socket()
self.__connect_to_irc(ircInstance)
except AssertionError as ae:
self.debug(f'Assertion error : {ae}')
self.Base.logs.critical(f'Assertion error: {ae}')
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(self.IrcSocket, server_hostname=self.Config.SERVEUR_HOSTNAME)
# self.IrcSocket = ssl_connexion
if self.Config.SERVEUR_SSL:
# Créer un object ssl
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:SSLSocket = ssl_connexion
return None
self.Base.logs.info(f"Connexion en mode SSL : Version = {self.IrcSocket.version()}")
self.SSL_VERSION = self.IrcSocket.version()
else:
soc.connect(connexion_information)
self.IrcSocket:socket.socket = soc
self.Base.logs.info("Connexion en mode normal")
return None
except ssl.SSLEOFError as soe:
self.Base.logs.critical(f"SSLEOFError __create_socket: {soe} - {soc.fileno()}")
except ssl.SSLError as se:
self.Base.logs.critical(f"SSLError __create_socket: {se} - {soc.fileno()}")
except OSError as oe:
self.Base.logs.critical(f"OSError __create_socket: {oe} - {soc.fileno()}")
except AttributeError as ae:
self.Base.logs.critical(f"OSError __create_socket: {oe} - {soc.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
self.Base.logs.debug(f'SSLContext initiated with verified mode {ctx.verify_mode}')
return ctx
def __connect_to_irc(self, ircInstance: 'Irc') -> None:
@@ -80,23 +103,28 @@ class Irc:
while self.signal:
try:
if self.RESTART == 1:
self.Base.logs.debug('Restarting Defender ...')
self.IrcSocket.shutdown(socket.SHUT_RDWR)
self.IrcSocket.close()
while self.IrcSocket.fileno() != -1:
time.sleep(0.5)
self.debug("--> En attente de la fermeture du socket ...")
self.Base.logs.warning('--> Waiting for socket to close ...')
# Reload configuration
self.Base.logs.debug('Reloading configuration')
self.Config = Config().ConfigModel
self.Base = Base(self.Config)
self.__create_socket()
self.__link(self.IrcSocket)
self.load_existing_modules()
self.RESTART = 0
# 4072 max what the socket can grab
buffer_size = self.IrcSocket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)
# data = self.IrcSocket.recv(buffer_size).splitlines(True)
data_in_bytes = self.IrcSocket.recv(buffer_size)
data = data_in_bytes.splitlines(True)
count_bytes = len(data_in_bytes)
while count_bytes > 4070:
@@ -104,40 +132,35 @@ class Irc:
new_data = self.IrcSocket.recv(buffer_size)
data_in_bytes += new_data
count_bytes = len(new_data)
# print("========================================================")
data = data_in_bytes.splitlines()
# print(f"{str(buffer_size)} - {str(len(data_in_bytes))}")
data = data_in_bytes.splitlines(True)
if not data:
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}")
self.Base.logs.error(f"SSLEOFError __connect_to_irc: {soe} - {data}")
except ssl.SSLError as se:
self.debug(f"SSLError __connect_to_irc: {se} - {data}")
self.Base.logs.error(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}")
self.Base.logs.error(f"SSLError __connect_to_irc: {oe} - {data}")
self.IrcSocket.shutdown(socket.SHUT_RDWR)
self.IrcSocket.close()
self.debug("--> Fermeture de Defender ...")
self.Base.logs.info("--> Fermeture de Defender ...")
except AssertionError as ae:
self.debug(f'Assertion error : {ae}')
self.Base.logs.error(f'Assertion error : {ae}')
except ValueError as ve:
self.debug(f'Value Error : {ve}')
self.Base.logs.error(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}")
self.Base.logs.error(f"OS Error __connect_to_irc: {soe}")
except AttributeError as atte:
self.Base.logs.critical(f"{atte}")
# 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
@@ -146,38 +169,43 @@ class Irc:
Args:
writer (StreamWriter): permet l'envoi des informations au serveur.
"""
nickname = self.Config.SERVICE_NICKNAME
username = self.Config.SERVICE_USERNAME
realname = self.Config.SERVICE_REALNAME
chan = self.Config.SERVICE_CHANLOG
info = self.Config.SERVICE_INFO
smodes = self.Config.SERVICE_SMODES
cmodes = self.Config.SERVICE_CMODES
umodes = self.Config.SERVICE_UMODES
host = self.Config.SERVICE_HOST
service_name = self.Config.SERVICE_NAME
try:
nickname = self.Config.SERVICE_NICKNAME
username = self.Config.SERVICE_USERNAME
realname = self.Config.SERVICE_REALNAME
chan = self.Config.SERVICE_CHANLOG
info = self.Config.SERVICE_INFO
smodes = self.Config.SERVICE_SMODES
cmodes = self.Config.SERVICE_CMODES
umodes = self.Config.SERVICE_UMODES
host = self.Config.SERVICE_HOST
service_name = self.Config.SERVICE_NAME
password = self.Config.SERVEUR_PASSWORD
link = self.Config.SERVEUR_LINK
sid = self.Config.SERVEUR_ID
service_id = self.Config.SERVICE_ID
password = self.Config.SERVEUR_PASSWORD
link = self.Config.SERVEUR_LINK
sid = self.Config.SERVEUR_ID
service_id = self.Config.SERVICE_ID
version = self.Config.DEFENDER_VERSION
unixtime = self.Base.get_unixtime()
version = self.Base.DEFENDER_VERSION
unixtime = self.Base.get_unixtime()
# Envoyer un message d'identification
writer.send(f":{sid} PASS :{password}\r\n".encode('utf-8'))
writer.send(f":{sid} PROTOCTL NICKv2 VHP UMODE2 NICKIP SJOIN SJOIN2 SJ3 NOQUIT TKLEXT MLOCK SID MTAGS\r\n".encode('utf-8'))
writer.send(f":{sid} PROTOCTL EAUTH={link},,,{service_name}-v{version}\r\n".encode('utf-8'))
writer.send(f":{sid} PROTOCTL SID={sid}\r\n".encode('utf-8'))
writer.send(f":{sid} SERVER {link} 1 :{info}\r\n".encode('utf-8'))
writer.send(f":{sid} {nickname} :Reserved for services\r\n".encode('utf-8'))
writer.send(f":{sid} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * * :{realname}\r\n".encode('utf-8'))
writer.send(f":{sid} SJOIN {unixtime} {chan} + :{service_id}\r\n".encode('utf-8'))
writer.send(f":{sid} MODE {chan} +{cmodes}\r\n".encode('utf-8'))
writer.send(f":{service_id} SAMODE {chan} +{umodes} {nickname}\r\n".encode('utf-8'))
# Envoyer un message d'identification
writer.send(f":{sid} PASS :{password}\r\n".encode('utf-8'))
writer.send(f":{sid} PROTOCTL NICKv2 VHP UMODE2 NICKIP SJOIN SJOIN2 SJ3 NOQUIT TKLEXT MLOCK SID MTAGS\r\n".encode('utf-8'))
writer.send(f":{sid} PROTOCTL EAUTH={link},,,{service_name}-v{version}\r\n".encode('utf-8'))
writer.send(f":{sid} PROTOCTL SID={sid}\r\n".encode('utf-8'))
writer.send(f":{sid} SERVER {link} 1 :{info}\r\n".encode('utf-8'))
writer.send(f":{sid} {nickname} :Reserved for services\r\n".encode('utf-8'))
writer.send(f":{sid} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * * :{realname}\r\n".encode('utf-8'))
writer.send(f":{sid} SJOIN {unixtime} {chan} + :{service_id}\r\n".encode('utf-8'))
writer.send(f":{sid} MODE {chan} +{cmodes}\r\n".encode('utf-8'))
writer.send(f":{service_id} SAMODE {chan} +{umodes} {nickname}\r\n".encode('utf-8'))
return None
self.Base.logs.debug('Link information sent to the server')
return None
except AttributeError as ae:
self.Base.logs.critical(f'{ae}')
def send2socket(self, send_message:str) -> None:
"""Envoit les commandes à envoyer au serveur.
@@ -186,22 +214,25 @@ 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]))
self.Base.logs.debug(f'{send_message}')
except UnicodeDecodeError:
self.debug('Write Decode impossible try iso-8859-1')
self.Base.logs.error(f'Decode Error try iso-8859-1 - message: {send_message}')
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0],'replace'))
except UnicodeEncodeError:
self.debug('Write Encode impossible ... try iso-8859-1')
self.Base.logs.error(f'Encode Error try iso-8859-1 - message: {send_message}')
self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0],'replace'))
except AssertionError as ae:
self.debug(f"Assertion error : {ae}")
self.Base.logs.warning(f'Assertion Error {ae} - message: {send_message}')
except ssl.SSLEOFError as soe:
self.debug(f"SSLEOFError send2socket: {soe} - {send_message}")
self.Base.logs.error(f"SSLEOFError: {soe} - {send_message}")
except ssl.SSLError as se:
self.debug(f"SSLError send2socket: {se} - {send_message}")
self.Base.logs.error(f"SSLError: {se} - {send_message}")
except OSError as oe:
self.debug(f"OSError send2socket: {oe} - {send_message}")
self.Base.logs.error(f"OSError: {oe} - {send_message}")
def send_response(self, responses:list[bytes]) -> None:
try:
@@ -209,6 +240,7 @@ class Irc:
for data in responses:
response = data.decode(self.CHARSET[0]).split()
self.cmd(response)
except UnicodeEncodeError:
for data in responses:
response = data.decode(self.CHARSET[1],'replace').split()
@@ -218,7 +250,8 @@ class Irc:
response = data.decode(self.CHARSET[1],'replace').split()
self.cmd(response)
except AssertionError as ae:
self.debug(f"Assertion error : {ae}")
self.Base.logs.error(f"Assertion error : {ae}")
##############################################
# FIN CONNEXION IRC #
##############################################
@@ -268,7 +301,7 @@ class Irc:
# 2. Executer la fonction
try:
if not class_name in self.loaded_classes:
self.debug(f"La class [{class_name} n'existe pas !!]")
self.Base.logs.error(f"La class [{class_name} n'existe pas !!]")
return False
class_instance = self.loaded_classes[class_name]
@@ -278,12 +311,12 @@ class Irc:
self.Base.running_timers.append(t)
self.debug(f"Timer ID : {str(t.ident)} | Running Threads : {len(threading.enumerate())}")
self.Base.logs.debug(f"Timer ID : {str(t.ident)} | Running Threads : {len(threading.enumerate())}")
except AssertionError as ae:
self.debug(f'Assertion Error -> {ae}')
self.Base.logs.error(f'Assertion Error -> {ae}')
except TypeError as te:
self.debug(f"Type error -> {te}")
self.Base.logs.error(f"Type error -> {te}")
def __create_tasks(self, obj: object, method_name: str, param:list) -> None:
"""#### Ajouter les méthodes a éxecuter dans un dictionnaire
@@ -302,7 +335,7 @@ class Irc:
'param': param
}
self.debug(f'Function to execute : {str(self.Base.periodic_func)}')
self.Base.logs.debug(f'Function to execute : {str(self.Base.periodic_func)}')
self.send_ping_to_sereur()
return None
@@ -316,7 +349,6 @@ class Irc:
return None
def load_module(self, fromuser:str, module_name:str, init:bool = False) -> bool:
try:
# module_name : mod_voice
module_name = module_name.lower()
@@ -326,8 +358,8 @@ class Irc:
# Si le module est déja chargé
if 'mods.' + module_name in sys.modules:
self.debug("Module déja chargé ...")
self.debug('module name = ' + module_name)
self.Base.logs.info("Module déja chargé ...")
self.Base.logs.info('module name = ' + module_name)
if class_name in self.loaded_classes:
# Si le module existe dans la variable globale retourne False
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Le module {module_name} est déja chargé ! si vous souhaiter le recharge tapez {self.Config.SERVICE_PREFIX}reload {module_name}")
@@ -358,14 +390,14 @@ class Irc:
self.Base.db_record_module(fromuser, module_name)
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} chargé")
self.debug(self.loaded_classes)
self.Base.logs.info(self.loaded_classes)
return True
except ModuleNotFoundError as moduleNotFound:
self.debug(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}")
except Exception as e:
self.debug(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}")
def insert_db_uid(self, uid:str, nickname:str, username:str, hostname:str, umodes:str, vhost:str, isWebirc: bool) -> None:
@@ -415,10 +447,10 @@ class Irc:
if oldnickname in self.db_uid:
del self.db_uid[oldnickname]
else:
self.debug(f"L'ancien nickname {oldnickname} n'existe pas dans UID_DB")
self.Base.logs.debug(f"L'ancien nickname {oldnickname} n'existe pas dans UID_DB")
response = False
self.debug(f"{oldnickname} changed to {newnickname}")
self.Base.logs.debug(f"{oldnickname} changed to {newnickname}")
return None
@@ -503,7 +535,7 @@ class Irc:
# Supprimer les doublons de la liste
self.db_chan = list(set(self.db_chan))
self.debug(f"Le salon {channel} a été ajouté à la liste CHAN_DB")
self.Base.logs.debug(f"Le salon {channel} a été ajouté à la liste CHAN_DB")
return response
@@ -514,13 +546,13 @@ class Irc:
if level > 4:
response = "Impossible d'ajouter un niveau > 4"
self.debug(response)
self.Base.logs.warning(response)
return response
# Verification si le user existe dans notre UID_DB
if not nickname in self.db_uid:
response = f"{nickname} n'est pas connecté, impossible de l'enregistrer pour le moment"
self.debug(response)
self.Base.logs.warning(response)
return response
hostname = self.db_uid[nickname]['hostname']
@@ -541,12 +573,12 @@ class Irc:
''', mes_donnees)
response = f"{nickname} ajouté en tant qu'administrateur de niveau {level}"
self.send2socket(f':{self.Config.SERVICE_NICKNAME} NOTICE {nickname} : {response}')
self.debug(response)
self.Base.logs.info(response)
return response
else:
response = f'{nickname} Existe déjà dans les users enregistrés'
self.send2socket(f':{self.Config.SERVICE_NICKNAME} NOTICE {nickname} : {response}')
self.debug(response)
self.Base.logs.info(response)
return response
def get_uid(self, uidornickname:str) -> Union[str, None]:
@@ -602,12 +634,13 @@ class Irc:
def debug(self, debug_msg:str) -> None:
if self.Config.DEBUG == 1:
if type(debug_msg) == list:
if debug_msg[0] != 'PING':
print(f"[{self.Base.get_datetime()}] - {debug_msg}")
else:
print(f"[{self.Base.get_datetime()}] - {debug_msg}")
# if self.Config.DEBUG == 1:
# if type(debug_msg) == list:
# if debug_msg[0] != 'PING':
# print(f"[{self.Base.get_datetime()}] - {debug_msg}")
# else:
#
print(f"[{self.Base.get_datetime()}] - {debug_msg}")
return None
@@ -620,6 +653,7 @@ class Irc:
def cmd(self, data:list) -> None:
try:
cmd_to_send:list[str] = data.copy()
cmd = data.copy()
@@ -627,9 +661,19 @@ class Irc:
cmd_to_debug.pop(0)
if len(cmd) == 0 or len(cmd) == 1:
self.Base.logs.warning(f'Size ({str(len(cmd))}) - {cmd}')
return False
self.debug(cmd_to_debug)
# self.debug(cmd_to_debug)
if len(data) == 7:
if data[2] == 'PRIVMSG' and data[4] == ':auth':
data_copy = data.copy()
data_copy[6] = '**********'
self.Base.logs.debug(data_copy)
else:
self.Base.logs.debug(data)
else:
self.Base.logs.debug(data)
match cmd[0]:
@@ -671,8 +715,8 @@ class Irc:
# self.Base.create_thread(self.abuseipdb_scan, (cmd[2], ))
pass
# Possibilité de déclancher les bans a ce niveau.
except IndexError:
self.debug(f'cmd reputation: index error')
except IndexError as ie:
self.Base.logs.error(f'{ie}')
case '320':
#:irc.deb.biz.st 320 PyDefender IRCParis07 :is in security-groups: known-users,webirc-users,tls-and-known-users,tls-users
@@ -691,17 +735,38 @@ class Irc:
hsid = str(cmd[0]).replace(':','')
if hsid == self.HSID:
if self.INIT == 1:
if self.Base.check_for_new_version():
version = f'{self.Base.DEFENDER_VERSION} >>> {self.Base.LATEST_DEFENDER_VERSION}'
else:
version = f'{self.Base.DEFENDER_VERSION}'
self.send2socket(f"MODE {self.Config.SERVICE_NICKNAME} +B")
self.send2socket(f"JOIN {self.Config.SERVICE_CHANLOG}")
print(f"################### DEFENDER ###################")
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} ")
print(f"# VERSION : {version} ")
print(f"################################################")
self.Base.logs.info(f"################### DEFENDER ###################")
self.Base.logs.info(f"# SERVICE CONNECTE ")
self.Base.logs.info(f"# SERVEUR : {self.Config.SERVEUR_IP} ")
self.Base.logs.info(f"# PORT : {self.Config.SERVEUR_PORT} ")
self.Base.logs.info(f"# SSL : {self.Config.SERVEUR_SSL} ")
self.Base.logs.info(f"# SSL VER : {self.SSL_VERSION} ")
self.Base.logs.info(f"# NICKNAME : {self.Config.SERVICE_NICKNAME} ")
self.Base.logs.info(f"# CHANNEL : {self.Config.SERVICE_CHANLOG} ")
self.Base.logs.info(f"# VERSION : {version} ")
self.Base.logs.info(f"################################################")
if self.Base.check_for_new_version():
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} : New Version available {version}")
# Initialisation terminé aprés le premier PING
self.INIT = 0
# self.send2socket(f':{self.Config.SERVICE_ID} PING :{hsid}')
@@ -767,6 +832,15 @@ class Irc:
cmd.pop(0)
get_uid_or_nickname = str(cmd[0].replace(':',''))
if len(cmd) == 6:
if cmd[1] == 'PRIVMSG' and cmd[3] == ':auth':
cmd_copy = cmd.copy()
cmd_copy[5] = '**********'
self.Base.logs.debug(cmd_copy)
else:
self.Base.logs.info(cmd)
else:
self.Base.logs.info(f'{cmd}')
# user_trigger = get_user.split('!')[0]
user_trigger = self.get_nickname(get_uid_or_nickname)
dnickname = self.Config.SERVICE_NICKNAME
@@ -780,7 +854,7 @@ class Irc:
arg = convert_to_string.split()
arg.remove(f':{self.Config.SERVICE_PREFIX}')
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]} is not available")
return False
cmd_to_send = convert_to_string.replace(':','')
@@ -800,7 +874,7 @@ class Irc:
# Réponse a un CTCP VERSION
if arg[0] == '\x01VERSION\x01':
self.send2socket(f':{dnickname} NOTICE {user_trigger} :\x01VERSION Service {self.Config.SERVICE_NICKNAME} V{self.Config.DEFENDER_VERSION}\x01')
self.send2socket(f':{dnickname} NOTICE {user_trigger} :\x01VERSION Service {self.Config.SERVICE_NICKNAME} V{self.Base.DEFENDER_VERSION}\x01')
return False
# Réponse a un TIME
@@ -826,8 +900,8 @@ class Irc:
self._hcmds(user_trigger, arg)
except IndexError:
self.debug(f'cmd --> PRIVMSG --> List index out of range')
except IndexError as io:
self.Base.logs.error(f'{io}')
case _:
pass
@@ -838,7 +912,7 @@ class Irc:
classe_object.cmd(cmd_to_send)
except IndexError as ie:
self.debug(f"IRC CMD -> IndexError : {ie} - {cmd} - length {str(len(cmd))}")
self.Base.logs.error(f"{ie} / {cmd} / length {str(len(cmd))}")
def _hcmds(self, user: str, cmd:list) -> None:
@@ -870,8 +944,8 @@ class Irc:
current_command = cmd[0]
self.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["rouge"]}{current_command}{self.Config.CONFIG_COLOR["noire"]} ] - Accès Refusé à {self.get_nickname(fromuser)}')
self.send2socket(f':{dnickname} NOTICE {fromuser} : Accès Refusé')
except IndexError:
self.debug(f'_hcmd notallowed : Index Error')
except IndexError as ie:
self.Base.logs.error(f'{ie}')
case 'deauth':
@@ -896,6 +970,7 @@ class Irc:
uid_user = self.get_uid(user_to_log)
self.insert_db_admin(uid_user, user_from_db[1])
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}{current_command}{self.Config.CONFIG_COLOR['noire']} ] - {self.get_nickname(fromuser)} est désormais connecté a {dnickname}")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Connexion a {dnickname} réussie!")
else:
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}{current_command}{self.Config.CONFIG_COLOR['noire']} ] - {self.get_nickname(fromuser)} a tapé un mauvais mot de pass")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Mot de passe incorrecte")
@@ -912,13 +987,13 @@ class Irc:
response = self.create_defender_user(newnickname, newlevel, password)
self.send2socket(f':{dnickname} NOTICE {fromuser} : {response}')
self.debug(response)
self.Base.logs.info(response)
except IndexError as ie:
self.debug(f'_hcmd addaccess: {ie}')
self.Base.logs.error(f'_hcmd addaccess: {ie}')
self.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} addaccess [nickname] [level] [password]')
except TypeError as te:
self.debug(f'_hcmd addaccess: out of index : {te}')
self.Base.logs.error(f'_hcmd addaccess: out of index : {te}')
self.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} addaccess [nickname] [level] [password]')
case 'editaccess':
@@ -966,9 +1041,9 @@ class Irc:
self.send2socket(f":{dnickname} NOTICE {fromuser} : Impossible de modifier l'utilisateur {str(user_new_level)}")
except TypeError as te:
self.debug(f"Type error : {te}")
self.Base.logs.error(f"Type error : {te}")
except ValueError as ve:
self.debug(f"Value Error : {ve}")
self.Base.logs.error(f"Value Error : {ve}")
self.send2socket(f':{dnickname} NOTICE {fromuser} : .editaccess [USER] [NEWPASSWORD] [NEWLEVEL]')
case 'delaccess':
@@ -978,9 +1053,9 @@ class Irc:
if user_to_del != user_confirmation:
self.send2socket(f':{dnickname} NOTICE {fromuser} : Les user ne sont pas les mêmes, tu dois confirmer le user que tu veux supprimer')
self.Base.logs.warning(f':{dnickname} NOTICE {fromuser} : Les user ne sont pas les mêmes, tu dois confirmer le user que tu veux supprimer')
return None
print(len(cmd))
if len(cmd) < 3:
self.send2socket(f':{dnickname} NOTICE {fromuser} : .delaccess [USER] [CONFIRMUSER]')
return None
@@ -999,6 +1074,7 @@ class Irc:
level_user_to_del = info_user[1]
if current_user_level <= level_user_to_del:
self.send2socket(f':{dnickname} NOTICE {fromuser} : You are not allowed to delete this access')
self.Base.logs.warning(f':{dnickname} NOTICE {fromuser} : You are not allowed to delete this access')
return None
data_to_delete = {'user': user_to_del}
@@ -1008,6 +1084,7 @@ class Irc:
self.send2socket(f':{dnickname} NOTICE {fromuser} : User {user_to_del} has been deleted !')
else:
self.send2socket(f":{dnickname} NOTICE {fromuser} : Impossible de supprimer l'utilisateur.")
self.Base.logs.warning(f":{dnickname} NOTICE {fromuser} : Impossible de supprimer l'utilisateur.")
case 'help':
@@ -1065,7 +1142,7 @@ class Irc:
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} supprimé")
except:
self.debug(f"Something went wrong with a module you want to load")
self.Base.logs.error(f"Something went wrong with a module you want to load")
case 'reload':
# reload mod_dktmb
@@ -1075,7 +1152,7 @@ class Irc:
if 'mods.' + module_name in sys.modules:
self.loaded_classes[class_name].unload()
self.debug('Module Already Loaded ... reload the module ...')
self.Base.logs.info('Module Already Loaded ... reload the module ...')
the_module = sys.modules['mods.' + module_name]
importlib.reload(the_module)
@@ -1099,7 +1176,7 @@ class Irc:
else:
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Module {module_name} n'est pas chargé !")
except:
self.debug(f"Something went wrong with a module you want to reload")
self.Base.logs.error(f"Something went wrong with a module you want to reload")
case 'quit':
try:
@@ -1114,12 +1191,12 @@ class Irc:
self.send2socket(f':{dnickname} NOTICE {fromuser} : Arrêt du service {dnickname}')
self.send2socket(f':{self.Config.SERVEUR_LINK} SQUIT {self.Config.SERVEUR_LINK} :{final_reason}')
self.debug(f'Arrêt du server {dnickname}')
self.Base.logs.info(f'Arrêt du server {dnickname}')
self.RESTART = 0
self.signal = False
except IndexError:
self.debug('_hcmd die: out of index')
except IndexError as ie:
self.Base.logs.error(f'{ie}')
self.send2socket(f"QUIT Good bye")
@@ -1137,14 +1214,14 @@ class Irc:
self.send2socket(f':{dnickname} NOTICE {fromuser} : Redémarrage du service {dnickname}')
self.send2socket(f':{self.Config.SERVEUR_LINK} SQUIT {self.Config.SERVEUR_LINK} :{final_reason}')
self.debug(f'Redémarrage du server {dnickname}')
self.Base.logs.info(f'Redémarrage du server {dnickname}')
self.loaded_classes.clear()
self.RESTART = 1 # Set restart status to 1 saying that the service will restart
self.INIT = 1 # set init to 1 saying that the service will be re initiated
case 'show_modules':
self.debug(self.loaded_classes)
self.Base.logs.debug(self.loaded_classes)
results = self.Base.db_execute_query(f'SELECT module FROM {self.Base.DB_SCHEMA["modules"]}')
results = results.fetchall()
@@ -1155,13 +1232,11 @@ class Irc:
for r in results:
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :Le module {r[0]} chargé")
self.debug(r[0])
case 'show_timers':
if self.Base.running_timers:
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :{self.Base.running_timers}")
self.debug(self.Base.running_timers)
else:
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :Aucun timers en cours d'execution")
@@ -1178,7 +1253,7 @@ class Irc:
self.send2socket(f':{dnickname} NOTICE {fromuser} : {uptime}')
case 'copyright':
self.send2socket(f':{dnickname} NOTICE {fromuser} : # Defender V.{self.Config.DEFENDER_VERSION} Developped by adator® and dktmb® #')
self.send2socket(f':{dnickname} NOTICE {fromuser} : # Defender V.{self.Base.DEFENDER_VERSION} Developped by adator® and dktmb® #')
case 'sentinel':
# .sentinel on
@@ -1196,5 +1271,14 @@ class Irc:
if not chan in channel_to_dont_quit:
self.send2socket(f":{service_id} PART {chan}")
case 'checkversion':
if self.Base.check_for_new_version():
self.send2socket(f':{dnickname} NOTICE {fromuser} : New Version available : {self.Base.DEFENDER_VERSION} >>> {self.Base.LATEST_DEFENDER_VERSION}')
self.send2socket(f':{dnickname} NOTICE {fromuser} : Please run (git pull origin main) in the current folder')
else:
self.send2socket(f':{dnickname} NOTICE {fromuser} : You have the latest version of defender')
pass
case _:
pass

105
core/loadConf.py Normal file
View File

@@ -0,0 +1,105 @@
import json, os
from typing import Union
from dataclasses import dataclass, field
##########################################
# CONFIGURATION FILE #
##########################################
@dataclass
class ConfigDataModel:
SERVEUR_IP: str
SERVEUR_HOSTNAME: str # Le hostname du serveur IRC
SERVEUR_LINK: str # Host attendu par votre IRCd (ex. dans votre link block pour Unrealircd)
SERVEUR_PORT: int # Port du link
SERVEUR_PASSWORD: str # Mot de passe du link (Privilégiez argon2 sur Unrealircd)
SERVEUR_ID: str # SID (identification) du bot en tant que Services
SERVEUR_SSL: bool # Activer la connexion SSL
SERVICE_NAME: str # Le nom du service
SERVICE_NICKNAME: str # Nick du bot sur IRC
SERVICE_REALNAME: str # Realname du bot
SERVICE_USERNAME: str # Ident du bot
SERVICE_HOST: str # Host du bot
SERVICE_INFO: str # swhois du bot
SERVICE_CHANLOG: str # Salon des logs et autres messages issus du bot
SERVICE_SMODES: str # Mode du service
SERVICE_CMODES: str # Mode du salon (#ChanLog) que le bot appliquera à son entrée
SERVICE_UMODES: str # Mode que le bot pourra se donner à sa connexion au salon chanlog
SERVICE_PREFIX: str # Prefix pour envoyer les commandes au bot
SERVICE_ID: str = field(init=False) # L'identifiant du service
OWNER: str # Identifiant du compte admin
PASSWORD: str # Mot de passe du compte admin
SALON_JAIL: str # Salon pot de miel
SALON_JAIL_MODES: str # Mode du salon pot de miel
SALON_LIBERER: str # Le salon ou sera envoyé l'utilisateur clean
API_TIMEOUT: int # Timeout des api's
PORTS_TO_SCAN: list # Liste des ports a scanné pour une detection de proxy
WHITELISTED_IP: list # IP a ne pas scanner
GLINE_DURATION: str # La durée du gline
DEBUG_LEVEL: int # Le niveau des logs DEBUG 10 | INFO 20 | WARNING 30 | ERROR 40 | CRITICAL 50
CONFIG_COLOR: dict
def __post_init__(self):
# Initialiser SERVICE_ID après la création de l'objet
self.SERVICE_ID:str = f"{self.SERVEUR_ID}AAAAAB"
class Config:
def __init__(self):
import_config = self.__load_json_configuration()
ConfigModel = ConfigDataModel(
SERVEUR_IP=import_config["SERVEUR_IP"],
SERVEUR_HOSTNAME=import_config["SERVEUR_HOSTNAME"],
SERVEUR_LINK=import_config["SERVEUR_LINK"],
SERVEUR_PORT=import_config["SERVEUR_PORT"],
SERVEUR_PASSWORD=import_config["SERVEUR_PASSWORD"],
SERVEUR_ID=import_config["SERVEUR_ID"],
SERVEUR_SSL=import_config["SERVEUR_SSL"],
SERVICE_NAME=import_config["SERVICE_NAME"],
SERVICE_NICKNAME=import_config["SERVICE_NICKNAME"],
SERVICE_REALNAME=import_config["SERVICE_REALNAME"],
SERVICE_USERNAME=import_config["SERVICE_USERNAME"],
SERVICE_HOST=import_config["SERVICE_HOST"],
SERVICE_INFO=import_config["SERVICE_INFO"],
SERVICE_CHANLOG=import_config["SERVICE_CHANLOG"],
SERVICE_SMODES=import_config["SERVICE_SMODES"],
SERVICE_CMODES=import_config["SERVICE_CMODES"],
SERVICE_UMODES=import_config["SERVICE_UMODES"],
SERVICE_PREFIX=import_config["SERVICE_PREFIX"],
OWNER=import_config["OWNER"],
PASSWORD=import_config["PASSWORD"],
SALON_JAIL=import_config["SALON_JAIL"],
SALON_JAIL_MODES=import_config["SALON_JAIL_MODES"],
SALON_LIBERER=import_config["SALON_LIBERER"],
API_TIMEOUT=import_config["API_TIMEOUT"],
PORTS_TO_SCAN=import_config["PORTS_TO_SCAN"],
WHITELISTED_IP=import_config["WHITELISTED_IP"],
GLINE_DURATION=import_config["GLINE_DURATION"],
DEBUG_LEVEL=import_config["DEBUG_LEVEL"],
CONFIG_COLOR=import_config["CONFIG_COLOR"]
)
self.ConfigModel = ConfigModel
return None
def __load_json_configuration(self):
conf_filename = f'core{os.sep}configuration.json'
with open(conf_filename, 'r') as configuration_data:
configuration:dict[str, Union[str, int, list, dict]] = json.load(configuration_data)
for key, value in configuration['CONFIG_COLOR'].items():
configuration['CONFIG_COLOR'][key] = value.encode('utf-8').decode('unicode_escape')
return configuration

View File

@@ -37,7 +37,7 @@ class Defender():
self.localscan_isRunning:bool = True
self.reputationTimer_isRunning:bool = True
self.Irc.debug(f'Module {self.__class__.__name__} loaded ...')
self.Irc.Base.logs.info(f'Module {self.__class__.__name__} loaded ...')
# Créer les nouvelles commandes du module
self.commands_level = {
@@ -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
@@ -129,7 +128,9 @@ class Defender():
self.db_reputation = {} # Definir la variable qui contiendra la liste des user concerné par la réputation
self.flood_system = {} # Variable qui va contenir les users
self.reputation_first_connexion = {'ip': '', 'score': -1} # Contient les premieres informations de connexion
# 13c34603fee4d2941a2c443cc5c77fd750757ca2a2c1b304bd0f418aff80c24be12651d1a3cfe674
self.abuseipdb_key = '13c34603fee4d2941a2c443cc5c77fd750757ca2a2c1b304bd0f418aff80c24be12651d1a3cfe674' # Laisser vide si aucune clé
# r1gEtjtfgRQjtNBDMxsg
self.cloudfilt_key = 'r1gEtjtfgRQjtNBDMxsg' # Laisser vide si aucune clé
# Rejoindre les salons
@@ -180,7 +181,7 @@ class Defender():
insert = self.Base.db_execute_query('INSERT INTO def_config (datetime, parameter, value) VALUES (:datetime, :parameter, :value)', mes_donnees)
insert_rows = insert.rowcount
if insert_rows > 0:
self.Irc.debug(f'Row affected into def_config : {insert_rows}')
self.Irc.Base.logs.debug(f'Row affected into def_config : {insert_rows}')
# Inserer une nouvelle configuration
for param, value in self.defConfig.items():
@@ -194,7 +195,7 @@ class Defender():
insert = self.Base.db_execute_query('INSERT INTO def_config (datetime, parameter, value) VALUES (:datetime, :parameter, :value)', mes_donnees)
insert_rows = insert.rowcount
if insert_rows > 0:
self.Irc.debug(f'DB_Def_config - new param included : {insert_rows}')
self.Irc.Base.logs.debug(f'DB_Def_config - new param included : {insert_rows}')
# Supprimer un parameter si il n'existe plus dans la variable global
query = "SELECT parameter FROM def_config"
@@ -207,7 +208,7 @@ class Defender():
delete = self.Base.db_execute_query('DELETE FROM def_config WHERE parameter = :parameter', mes_donnees)
row_affected = delete.rowcount
if row_affected > 0:
self.Irc.debug(f'DB_Def_config - param [{dbparam[0]}] has been deleted')
self.Irc.Base.logs.debug(f'DB_Def_config - param [{dbparam[0]}] has been deleted')
# Synchroniser la base de données avec la variable global
query = "SELECT parameter, value FROM def_config"
@@ -217,13 +218,13 @@ class Defender():
for param, value in result:
self.defConfig[param] = self.Base.int_if_possible(value)
self.Irc.debug(self.defConfig)
self.Irc.Base.logs.debug(self.defConfig)
return None
def update_db_configuration(self, param:str, value:str) -> None:
if not param in self.defConfig:
self.Irc.debug(f"Le parametre {param} n'existe pas dans la variable global")
self.Irc.Base.logs.error(f"Le parametre {param} n'existe pas dans la variable global")
return None
mes_donnees = {'parameter': param}
@@ -237,9 +238,9 @@ class Defender():
updated_rows = update.rowcount
if updated_rows > 0:
self.defConfig[param] = self.Base.int_if_possible(value)
self.Irc.debug(f'DB_Def_config - new param updated : {param} {value}')
self.Irc.Base.logs.debug(f'DB_Def_config - new param updated : {param} {value}')
self.Irc.debug(self.defConfig)
self.Irc.Base.logs.debug(self.defConfig)
def add_defender_channel(self, channel:str) -> bool:
"""Cette fonction ajoute les salons de join de Defender
@@ -306,7 +307,7 @@ class Defender():
secret_code = self.Base.get_random(8)
if not uid in self.Irc.db_uid:
self.Irc.debug(f'Etrange UID {uid}')
self.Irc.Base.logs.error(f'Etrange UID {uid}')
return None
if uid in self.db_reputation:
@@ -314,7 +315,7 @@ class Defender():
self.db_reputation[uid]['updated_datetime'] = currentDateTime
self.db_reputation[uid]['secret_code'] = secret_code
else:
self.Irc.debug(f"L'UID {uid} n'existe pas dans REPUTATION_DB")
self.Irc.Base.logs.error(f"L'UID {uid} n'existe pas dans REPUTATION_DB")
return None
@@ -329,7 +330,7 @@ class Defender():
if uid in self.db_reputation:
# Si le nickname existe dans le dictionnaire alors le supprimer
del self.db_reputation[uid]
self.Irc.debug(f"Le UID {uid} a été supprimé du REPUTATION_DB")
self.Irc.Base.logs.debug(f"Le UID {uid} a été supprimé du REPUTATION_DB")
def insert_db_trusted(self, uid: str, nickname:str) -> None:
@@ -394,7 +395,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
@@ -437,22 +438,23 @@ class Defender():
if chan != salon_jail:
self.Irc.send2socket(f":{service_id} MODE {chan} +b {jailed_nickname}!*@*")
self.Irc.send2socket(f":{service_id} KICK {chan} {jailed_nickname}")
self.Irc.debug(f"system_reputation : {jailed_nickname} à été capturé par le système de réputation")
self.Irc.Base.logs.info(f"system_reputation : {jailed_nickname} à été capturé par le système de réputation")
# self.Irc.create_ping_timer(int(self.defConfig['reputation_timer']) * 60, 'Defender', 'system_reputation_timer')
# self.Base.create_timer(int(self.defConfig['reputation_timer']) * 60, self.system_reputation_timer)
else:
self.Irc.debug(f"system_reputation : {jailed_nickname} à été supprimé du système de réputation car connecté via WebIrc ou il est dans la 'Trusted list'")
self.Irc.Base.logs.info(f"system_reputation : {jailed_nickname} à été supprimé du système de réputation car connecté via WebIrc ou il est dans la 'Trusted list'")
self.delete_db_reputation(uid)
except IndexError as e:
self.Irc.debug(f"system_reputation : {str(e)}")
self.Irc.Base.logs.error(f"system_reputation : {str(e)}")
def system_reputation_timer(self) -> None:
try:
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']
@@ -464,26 +466,25 @@ class Defender():
elif reputation_timer == 0:
return None
# self.Irc.debug(self.db_reputation)
uid_to_clean = []
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.Base.logs.info(f"Nickname: {self.db_reputation[uid]['nickname']} KILLED after {str(reputation_timer)} minutes of inactivity")
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}.
@@ -491,7 +492,7 @@ class Defender():
self.delete_db_reputation(uid)
except AssertionError as ae:
self.Irc.debug(f'Assertion Error -> {ae}')
self.Irc.Base.logs.error(f'Assertion Error -> {ae}')
def thread_reputation_timer(self) -> None:
try:
@@ -501,7 +502,7 @@ class Defender():
return None
except ValueError as ve:
self.Irc.debug(f"thread_reputation_timer Error : {ve}")
self.Irc.Base.logs.error(f"thread_reputation_timer Error : {ve}")
def _execute_flood_action(self, action:str, channel:str) -> None:
"""DO NOT EXECUTE THIS FUNCTION WITHOUT THREADING
@@ -561,10 +562,10 @@ class Defender():
get_diff_secondes = unixtime - self.flood_system[get_detected_uid]['first_msg_time']
elif self.flood_system[get_detected_uid]['nbr_msg'] > flood_message:
self.Irc.debug('system de flood detecté')
self.Irc.Base.logs.info('system de flood detecté')
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} : {color_red} {color_bold} Flood detected. Apply the +m mode (Ô_o)')
self.Irc.send2socket(f":{service_id} MODE {channel} +m")
self.Irc.debug(f'FLOOD Détecté sur {get_detected_nickname} mode +m appliqué sur le salon {channel}')
self.Irc.Base.logs.info(f'FLOOD Détecté sur {get_detected_nickname} mode +m appliqué sur le salon {channel}')
self.flood_system[get_detected_uid]['nbr_msg'] = 0
self.flood_system[get_detected_uid]['first_msg_time'] = unixtime
@@ -599,7 +600,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,15 +612,15 @@ class Defender():
newSocket.shutdown(socket.SHUT_RDWR)
newSocket.close()
except (socket.timeout, ConnectionRefusedError):
self.Irc.debug(f"Le port {str(port)} est fermé")
self.Base.logs.info(f"Le port {remote_ip}:{str(port)} est fermé")
except AttributeError as ae:
self.Irc.debug(f"AttributeError : {ae}")
self.Base.logs.warning(f"AttributeError ({remote_ip}): {ae}")
except socket.gaierror as err:
self.Irc.debug(f"Address Info Error: {err}")
self.Base.logs.warning(f"Address Info Error ({remote_ip}): {err}")
finally:
# newSocket.shutdown(socket.SHUT_RDWR)
newSocket.close()
self.Irc.debug('=======> Fermeture de la socket')
self.Base.logs.info('=======> Fermeture de la socket')
pass
@@ -640,7 +641,7 @@ class Defender():
return None
except ValueError as ve:
self.Irc.debug(f"thread_local_scan Error : {ve}")
self.Base.logs.warning(f"thread_local_scan Error : {ve}")
def get_ports_connexion(self, remote_ip: str) -> list[int]:
"""psutil_scan
@@ -657,7 +658,7 @@ class Defender():
connections = psutil.net_connections(kind='inet')
matching_ports = [conn.raddr.port for conn in connections if conn.raddr and conn.raddr.ip == remote_ip]
self.Irc.debug(f"Connexion of {remote_ip} using ports : {str(matching_ports)}")
self.Base.logs.info(f"Connexion of {remote_ip} using ports : {str(matching_ports)}")
return matching_ports
@@ -678,7 +679,7 @@ class Defender():
return None
except ValueError as ve:
self.Irc.debug(f"thread_psutil_scan Error : {ve}")
self.Base.logs.warning(f"thread_psutil_scan Error : {ve}")
def abuseipdb_scan(self, remote_ip:str) -> Union[dict[str, any], None]:
"""Analyse l'ip avec AbuseIpDB
@@ -740,11 +741,11 @@ class Defender():
return result
except KeyError as ke:
self.Irc.debug(f"AbuseIpDb KeyError : {ke}")
self.Base.logs.error(f"AbuseIpDb KeyError : {ke}")
except requests.ReadTimeout as rt:
self.Irc.debug(f"AbuseIpDb Timeout : {rt}")
self.Base.logs.error(f"AbuseIpDb Timeout : {rt}")
except requests.ConnectionError as ce:
self.Irc.debug(f"AbuseIpDb Connection Error : {ce}")
self.Base.logs.error(f"AbuseIpDb Connection Error : {ce}")
def thread_abuseipdb_scan(self) -> None:
try:
@@ -763,7 +764,7 @@ class Defender():
return None
except ValueError as ve:
self.Irc.debug(f"thread_abuseipdb_scan Error : {ve}")
self.Base.logs.error(f"thread_abuseipdb_scan Error : {ve}")
def freeipapi_scan(self, remote_ip:str) -> Union[dict[str, any], None]:
"""Analyse l'ip avec Freeipapi
@@ -798,10 +799,10 @@ class Defender():
try:
status_code = response.status_code
if status_code == 429:
self.Irc.debug(f'Too Many Requests - The rate limit for the API has been exceeded.')
self.Base.logs.warning(f'Too Many Requests - The rate limit for the API has been exceeded.')
return None
elif status_code != 200:
print("salut salut")
self.Base.logs.warning(f'status code = {str(status_code)}')
return None
result = {
@@ -817,7 +818,7 @@ class Defender():
return result
except KeyError as ke:
self.Irc.debug(f"FREEIPAPI_SCAN KeyError : {ke}")
self.Base.logs.error(f"FREEIPAPI_SCAN KeyError : {ke}")
def thread_freeipapi_scan(self) -> None:
try:
@@ -836,7 +837,7 @@ class Defender():
return None
except ValueError as ve:
self.Irc.debug(f"thread_freeipapi_scan Error : {ve}")
self.Base.logs.error(f"thread_freeipapi_scan Error : {ve}")
def cloudfilt_scan(self, remote_ip:str) -> Union[dict[str, any], None]:
"""Analyse l'ip avec cloudfilt
@@ -874,7 +875,7 @@ class Defender():
try:
status_code = response.status_code
if status_code != 200:
self.Irc.debug(f'Error connecting to cloudfilt API | Code: {str(status_code)}')
self.Base.logs.warning(f'Error connecting to cloudfilt API | Code: {str(status_code)}')
return None
result = {
@@ -893,7 +894,7 @@ class Defender():
return result
except KeyError as ke:
self.Irc.debug(f"CLOUDFILT_SCAN KeyError : {ke}")
self.Base.logs.error(f"CLOUDFILT_SCAN KeyError : {ke}")
return None
def thread_cloudfilt_scan(self) -> None:
@@ -913,7 +914,7 @@ class Defender():
return None
except ValueError as ve:
self.Irc.debug(f"Thread_cloudfilt_scan Error : {ve}")
self.Base.logs.error(f"Thread_cloudfilt_scan Error : {ve}")
def cmd(self, data:list) -> None:
@@ -931,6 +932,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])
@@ -949,7 +953,7 @@ class Defender():
# Possibilité de déclancher les bans a ce niveau.
except IndexError:
self.Irc.debug(f'cmd reputation: index error')
self.Irc.Base.logs.error(f'cmd reputation: index error')
match cmd[2]:
@@ -1002,7 +1006,7 @@ class Defender():
if uid in self.db_reputation:
if reputation_flag == 1 and int(client_score) <= int(reputation_seuil):
self.system_reputation(uid)
self.Irc.debug('Démarrer le systeme de reputation')
self.Base.logs.info('Démarrer le systeme de reputation')
case 'SJOIN':
# ['@msgid=F9B7JeHL5pj9nN57cJ5pEr;time=2023-12-28T20:47:24.305Z', ':001', 'SJOIN', '1702138958', '#welcome', ':0015L1AHL']
@@ -1023,13 +1027,17 @@ class Defender():
self.Irc.send2socket(f":{service_id} MODE {parsed_chan} +b {self.db_reputation[parsed_UID]['nickname']}!*@*")
self.Irc.send2socket(f":{service_id} KICK {parsed_chan} {self.db_reputation[parsed_UID]['nickname']}")
self.Irc.debug(f'SJOIN parsed_uid : {parsed_UID}')
self.Base.logs.debug(f'SJOIN parsed_uid : {parsed_UID}')
except KeyError as ke:
self.Irc.debug(f"key error SJOIN : {ke}")
self.Base.logs.error(f"key error SJOIN : {ke}")
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 +1048,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])
@@ -1065,11 +1073,12 @@ class Defender():
self.Irc.send2socket(f":{service_id} MODE {chan} -b {oldnick}!*@*")
self.Irc.send2socket(f":{service_id} MODE {chan} +b {newnickname}!*@*")
except KeyError as ke:
self.Irc.debug(f'cmd - NICK - KeyError: {ke}')
self.Base.logs.error(f'cmd - NICK - KeyError: {ke}')
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 +1088,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)
@@ -1106,9 +1115,9 @@ class Defender():
# self.Base.create_timer(timer_sent, self.Base.garbage_collector_sockets)
except TypeError as te:
self.Irc.debug(f"Type Error -> {te}")
self.Base.logs.error(f"Type Error -> {te}")
except ValueError as ve:
self.Irc.debug(f"Value Error -> {ve}")
self.Base.logs.error(f"Value Error -> {ve}")
case 'show_reputation':
@@ -1131,12 +1140,11 @@ class Defender():
jailed_salon = self.Config.SALON_JAIL
reputation_seuil = self.defConfig['reputation_seuil']
welcome_salon = self.Config.SALON_LIBERER
self.Irc.debug(f"IP de {jailed_nickname} : {jailed_IP}")
self.Base.logs.debug(f"IP de {jailed_nickname} : {jailed_IP}")
link = self.Config.SERVEUR_LINK
color_green = self.Config.CONFIG_COLOR['verte']
color_black = self.Config.CONFIG_COLOR['noire']
if jailed_UID in self.db_reputation:
if release_code == self.db_reputation[jailed_UID]['secret_code']:
@@ -1148,7 +1156,7 @@ class Defender():
self.Irc.send2socket(f":{service_id} MODE {chan} -b {jailed_nickname}!*@*")
del self.db_reputation[jailed_UID]
self.Irc.debug(f'{jailed_UID} - {jailed_nickname} removed from REPUTATION_DB')
self.Base.logs.debug(f'{jailed_UID} - {jailed_nickname} removed from REPUTATION_DB')
self.Irc.send2socket(f":{service_id} SAPART {jailed_nickname} {jailed_salon}")
self.Irc.send2socket(f":{service_id} SAJOIN {jailed_nickname} {welcome_salon}")
self.Irc.send2socket(f":{link} REPUTATION {jailed_IP} {int(reputation_seuil) + 1}")
@@ -1161,10 +1169,10 @@ class Defender():
self.Irc.send2socket(f":{dnickname} PRIVMSG {jailed_salon} : Ce n'est pas à toi de taper le mot de passe !")
except IndexError:
self.Irc.debug('_hcmd code: out of index')
self.Base.logs.error('_hcmd code: out of index')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} code [code]')
except KeyError as ke:
self.Irc.debug(f'_hcmd code: KeyError {ke}')
self.Base.logs.error(f'_hcmd code: KeyError {ke}')
# self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} code [code]')
pass
@@ -1228,7 +1236,6 @@ class Defender():
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}BAN ON ALL CHANS{self.Config.CONFIG_COLOR["noire"]} ] : Activated by {fromuser}')
elif get_value == 'off':
print(get_value)
if self.defConfig[key] == 0:
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}BAN ON ALL CHANS{self.Config.CONFIG_COLOR['noire']} ] : Already deactivated")
return False
@@ -1253,15 +1260,16 @@ class Defender():
case _:
pass
except IndexError:
self.Irc.debug('_hcmd reputation: out of index')
except IndexError as ie:
self.Base.logs.warning(f'{ie}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation [ON/OFF]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation set banallchan [ON/OFF]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation set limit [1234]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation set timer [1234]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} reputation set action [kill|None]')
except ValueError:
except ValueError as ve:
self.Base.logs.warning(f'{ie}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : La valeur devrait etre un entier >= 0')
case 'proxy_scan':
@@ -1429,7 +1437,7 @@ class Defender():
pass
except ValueError as ve:
self.Irc.debug(f"{self.__class__.__name__} Value Error : {ve}")
self.Base.logs.error(f"{self.__class__.__name__} Value Error : {ve}")
case 'status':
color_green = self.Config.CONFIG_COLOR['verte']
@@ -1452,7 +1460,7 @@ class Defender():
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : flood_time ==> {self.defConfig["flood_time"]}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : flood_timer ==> {self.defConfig["flood_timer"]}')
except KeyError as ke:
self.Irc.debug(f"Key Error : {ke}")
self.Base.logs.error(f"Key Error : {ke}")
case 'join':
@@ -1461,8 +1469,8 @@ class Defender():
self.Irc.send2socket(f':{service_id} JOIN {channel}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : {dnickname} JOINED {channel}')
self.add_defender_channel(channel)
except IndexError:
self.Irc.debug('_hcmd join: out of index')
except IndexError as ie:
self.Base.logs.error(f'{ie}')
case 'part':
@@ -1475,8 +1483,8 @@ class Defender():
self.Irc.send2socket(f':{service_id} PART {channel}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : {dnickname} LEFT {channel}')
self.delete_defender_channel(channel)
except IndexError:
self.Irc.debug('_hcmd part: out of index')
except IndexError as ie:
self.Base.logs.error(f'{ie}')
case 'op' | 'o':
# /mode #channel +o user
@@ -1488,7 +1496,7 @@ class Defender():
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} +o {nickname}")
except IndexError as e:
self.Irc.debug(f'_hcmd OP: {str(e)}')
self.Base.logs.warning(f'_hcmd OP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} op [#SALON] [NICKNAME]')
case 'deop' | 'do':
@@ -1499,7 +1507,7 @@ class Defender():
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} -o {nickname}")
except IndexError as e:
self.Irc.debug(f'_hcmd DEOP: {str(e)}')
self.Base.logs.warning(f'_hcmd DEOP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deop [#SALON] [NICKNAME]')
case 'owner' | 'q':
@@ -1510,7 +1518,7 @@ class Defender():
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} +q {nickname}")
except IndexError as e:
self.Irc.debug(f'_hcmd OWNER: {str(e)}')
self.Base.logs.warning(f'_hcmd OWNER: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} owner [#SALON] [NICKNAME]')
case 'deowner' | 'dq':
@@ -1521,7 +1529,7 @@ class Defender():
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} -q {nickname}")
except IndexError as e:
self.Irc.debug(f'_hcmd DEOWNER: {str(e)}')
self.Base.logs.warning(f'_hcmd DEOWNER: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deowner [#SALON] [NICKNAME]')
case 'halfop' | 'h':
@@ -1532,7 +1540,7 @@ class Defender():
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} +h {nickname}")
except IndexError as e:
self.Irc.debug(f'_hcmd halfop: {str(e)}')
self.Base.logs.warning(f'_hcmd halfop: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} halfop [#SALON] [NICKNAME]')
case 'dehalfop' | 'dh':
@@ -1543,7 +1551,7 @@ class Defender():
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} -h {nickname}")
except IndexError as e:
self.Irc.debug(f'_hcmd DEHALFOP: {str(e)}')
self.Base.logs.warning(f'_hcmd DEHALFOP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} dehalfop [#SALON] [NICKNAME]')
case 'voice' | 'v':
@@ -1554,7 +1562,7 @@ class Defender():
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} +v {nickname}")
except IndexError as e:
self.Irc.debug(f'_hcmd VOICE: {str(e)}')
self.Base.logs.warning(f'_hcmd VOICE: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} voice [#SALON] [NICKNAME]')
case 'devoice' | 'dv':
@@ -1565,7 +1573,7 @@ class Defender():
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} -v {nickname}")
except IndexError as e:
self.Irc.debug(f'_hcmd DEVOICE: {str(e)}')
self.Base.logs.warning(f'_hcmd DEVOICE: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} devoice [#SALON] [NICKNAME]')
case 'ban' | 'b':
@@ -1575,9 +1583,9 @@ class Defender():
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} +b {nickname}!*@*")
self.Irc.debug(f'{fromuser} has banned {nickname} from {channel}')
self.Base.logs.debug(f'{fromuser} has banned {nickname} from {channel}')
except IndexError as e:
self.Irc.debug(f'_hcmd BAN: {str(e)}')
self.Base.logs.warning(f'_hcmd BAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} ban [#SALON] [NICKNAME]')
case 'unban' | 'ub':
@@ -1587,9 +1595,9 @@ class Defender():
nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} -b {nickname}!*@*")
self.Irc.debug(f'{fromuser} has unbanned {nickname} from {channel}')
self.Base.logs.debug(f'{fromuser} has unbanned {nickname} from {channel}')
except IndexError as e:
self.Irc.debug(f'_hcmd UNBAN: {str(e)}')
self.Base.logs.warning(f'_hcmd UNBAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} unban [#SALON] [NICKNAME]')
case 'kick' | 'k':
@@ -1605,9 +1613,9 @@ class Defender():
final_reason = ' '.join(reason)
self.Irc.send2socket(f":{service_id} KICK {channel} {nickname} {final_reason}")
self.Irc.debug(f'{fromuser} has kicked {nickname} from {channel} : {final_reason}')
self.Base.logs.debug(f'{fromuser} has kicked {nickname} from {channel} : {final_reason}')
except IndexError as e:
self.Irc.debug(f'_hcmd KICK: {str(e)}')
self.Base.logs.warning(f'_hcmd KICK: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} kick [#SALON] [NICKNAME] [REASON]')
case 'kickban' | 'kb':
@@ -1624,9 +1632,9 @@ class Defender():
self.Irc.send2socket(f":{service_id} KICK {channel} {nickname} {final_reason}")
self.Irc.send2socket(f":{service_id} MODE {channel} +b {nickname}!*@*")
self.Irc.debug(f'{fromuser} has kicked and banned {nickname} from {channel} : {final_reason}')
self.Base.logs.debug(f'{fromuser} has kicked and banned {nickname} from {channel} : {final_reason}')
except IndexError as e:
self.Irc.debug(f'_hcmd KICKBAN: {str(e)}')
self.Base.logs.warning(f'_hcmd KICKBAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} kickban [#SALON] [NICKNAME] [REASON]')
case 'info':
@@ -1652,7 +1660,7 @@ class Defender():
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : MODES : {self.Irc.db_uid[uid_query]["umodes"]}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : CONNECTION TIME : {self.Irc.db_uid[uid_query]["datetime"]}')
except KeyError as ke:
self.Irc.debug(f"Key error info user : {ke}")
self.Base.logs.warning(f"Key error info user : {ke}")
case 'show_users':
for uid, infousers in self.Irc.db_uid.items():

3
version.json Normal file
View File

@@ -0,0 +1,3 @@
{
"version": "4.1.0"
}