40 Commits

Author SHA1 Message Date
adator
4d0087623c Merge pull request #49 from adator85/dev
unsubscribe before unload
2024-09-22 23:54:22 +02:00
adator
e25baea0ef unsubscribe before unload 2024-09-22 23:53:56 +02:00
adator
dc20f5ec3c Merge pull request #48 from adator85/dev
Dev
2024-09-22 23:20:37 +02:00
adator
ee02566343 V5.3.1 2024-09-22 23:20:12 +02:00
adator
11eedbb191 Include mod_jsonrpc 2024-09-22 22:23:13 +02:00
adator
110cae3b84 Merge pull request #47 from adator85/dev
Update the way to clean exceptions and bans
2024-09-22 16:45:54 +02:00
adator
7e5e2d4643 Update the way to clean exceptions and bans 2024-09-22 16:45:21 +02:00
adator
857cbfc85d Merge pull request #46 from adator85/dev
Send the ready msg to channel log
2024-09-22 16:33:49 +02:00
adator
7422bcad45 Send the ready msg to channel log 2024-09-22 16:33:21 +02:00
adator
3518589e9c Merge pull request #45 from adator85/dev
Fix ConfModel error
2024-09-22 16:31:35 +02:00
adator
0cf1262d31 Fix ConfModel error 2024-09-22 16:31:00 +02:00
adator
e14c97de03 Merge pull request #44 from adator85/dev
V5.3.0
2024-09-22 16:20:45 +02:00
adator
ff603ab2a4 V5.3.0 2024-09-22 16:20:02 +02:00
adator
69360be3ad Merge pull request #43 from adator85/dev
Update info command
2024-09-21 20:28:29 +02:00
adator
d7503768b6 Update info command 2024-09-21 20:28:02 +02:00
adator
bfa90c6bd5 Merge pull request #42 from adator85/dev
V5.2.9
2024-09-21 20:22:42 +02:00
adator
b5503d23d7 V5.2.9 2024-09-21 20:22:09 +02:00
adator
5c8378a0e7 Merge pull request #41 from adator85/dev
V5.2.9
2024-09-21 16:43:14 +02:00
adator
7be3f51bf4 V5.2.9 2024-09-21 16:28:50 +02:00
adator
e3b212ea88 Merge pull request #40 from adator85/dev
finetune clone connection
2024-09-20 23:13:33 +02:00
adator
2c0510b2a3 finetune clone connection 2024-09-20 23:12:57 +02:00
adator
0c2a350d38 Merge pull request #39 from adator85/dev
Dev
2024-09-20 21:12:59 +02:00
adator
ee039322d4 V5.2.7 2024-09-19 21:06:07 +02:00
adator
8f08a1e77f update readme.md 2024-09-18 20:29:33 +02:00
adator
c59dd16e87 V5.2.6 2024-09-18 20:21:44 +02:00
adator
0f31e67be6 check python version fix 2024-09-18 19:10:15 +02:00
adator
3cd2077f63 update installation module 2024-09-18 19:08:08 +02:00
adator
9c78ad0860 Same version, copyright changed 2024-09-17 01:46:09 +02:00
adator
487f9a2762 V5.2.5 2024-09-17 01:43:24 +02:00
adator
a7de16f7ad V5.2.4 multiple changes related to clones 2024-09-17 00:14:53 +02:00
adator
c1c0b480ce V5.2.3 If missing MOTD 2024-09-16 21:14:04 +02:00
adator
66ea492593 removing old code 2024-09-16 00:57:27 +02:00
adator
d459fd662f Changing hostname modification 2024-09-16 00:41:43 +02:00
adator
5d3a2b0e64 Changing Proxy_scan to PSUTIL_scan 2024-09-15 23:32:44 +02:00
adator
2f681db2d7 Adding Geoip to the UserModel 2024-09-15 23:29:32 +02:00
adator
7585db4f62 V5.2.2 2024-09-15 22:39:34 +02:00
adator
1cea8d0601 Merge pull request #38 from adator85/dev
V5.2.1
2024-09-15 03:09:22 +02:00
adator
1984511db8 V5.2.1 2024-09-15 03:08:49 +02:00
adator
652b400d5e Merge pull request #37 from adator85/dev
update mode clone
2024-09-15 02:50:27 +02:00
adator
ce47739a93 update mode clone 2024-09-15 02:49:42 +02:00
14 changed files with 1109 additions and 393 deletions

1
.gitignore vendored
View File

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

180
README.md
View File

@@ -57,66 +57,176 @@ Pour Les prochains lancement de defender vous devez utiliser la commande suivant
$ sudo systemctl --user start defender
# Configuration
```
SERVEUR (Serveur)
SERVEUR_IP: Adresse IP du serveur IRC à rejoindre.
SERVEUR_HOSTNAME: Nom d'hôte du serveur IRC à rejoindre (optionnel).
SERVEUR_LINK: Lien vers le serveur IRC (optionnel).
SERVEUR_PORT: Port de connexion au serveur IRC.
SERVEUR_PASSWORD: Mot de passe d'enregistrement du service sur le serveur IRC.
SERVEUR_ID: Identifiant unique du service.
SERVEUR_SSL: Active la connexion SSL sécurisée au serveur IRC (true/false).
* SERVEUR_IP: Adresse IP du serveur IRC à rejoindre. (default : 127.0.0.1)
* SERVEUR_HOSTNAME: Nom d'hôte du serveur IRC à rejoindre (optionnel).
* SERVEUR_LINK: Lien vers le serveur IRC (optionnel).
* SERVEUR_PORT: Port de connexion au serveur IRC.
* SERVEUR_PASSWORD: Mot de passe d'enregistrement du service sur le serveur IRC.
SERVEUR_ID: Identifiant unique du service. (default : 19Z)
SERVEUR_SSL: Active la connexion SSL sécurisée au serveur IRC (true/false) (default : false).
SERVICE (Service)
SERVICE_NAME: Nom du service IRC.
SERVICE_NICKNAME: Surnom utilisé par le service sur le serveur IRC.
SERVICE_REALNAME: Nom réel du service affiché sur le serveur IRC.
SERVICE_USERNAME: Nom d'utilisateur utilisé par le service pour se connecter au serveur IRC.
SERVICE_HOST: Nom d'hôte du service affiché sur le serveur IRC (optionnel).
SERVICE_INFO: Description du service.
SERVICE_CHANLOG: Canal utilisé pour la journalisation des actions du service.
SERVICE_SMODES: Modes serveur appliqués aux canaux rejoints par le service.
SERVICE_CMODES: Modes de canal appliqués aux canaux rejoints par le service.
SERVICE_UMODES: Modes utilisateur appliqués au service.
SERVICE_PREFIX: Caractère utilisé comme préfixe des commandes du service.
SERVICE_NAME: Nom du service IRC. (default : Defender)
SERVICE_NICKNAME: Surnom utilisé par le service sur le serveur IRC. (default : Defender)
SERVICE_REALNAME: Nom réel du service affiché sur le serveur IRC. (default : Defender Security)
SERVICE_USERNAME: Nom d'utilisateur utilisé par le service pour se connecter au serveur IRC. (default : IRCSecurity)
SERVICE_HOST: Nom d'hôte du service affiché sur le serveur IRC (optionnel). (default : defender.local.network)
SERVICE_INFO: Description du service. (default : Defender Network IRC Service)
SERVICE_CHANLOG: Canal utilisé pour la journalisation des actions du service. (default : #services)
SERVICE_SMODES: Modes serveur appliqués aux canaux rejoints par le service. (default : +ioqBS)
SERVICE_CMODES: Modes de canal appliqués aux canaux rejoints par le service. (default : ntsOP)
SERVICE_UMODES: Modes utilisateur appliqués au service. (default : o)
SERVICE_PREFIX: Caractère utilisé comme préfixe des commandes du service. (default : !)
COMPTE (Compte)
OWNER: Nom d'utilisateur possédant les droits d'administration du service.
PASSWORD: Mot de passe de l'administrateur du service.
OWNER: Nom d'utilisateur possédant les droits d'administration du service. (default : admin)
PASSWORD: Mot de passe de l'administrateur du service. (default : admin)
CANAUX (Canaux)
SALON_JAIL: Canal utilisé comme prison pour les utilisateurs sanctionnés.
SALON_JAIL_MODES: Modes appliqués au canal de prison.
SALON_LIBERER: Canal utilisé pour la libération des utilisateurs sanctionnés.
SALON_JAIL: Canal utilisé comme prison pour les utilisateurs sanctionnés. (default : #jail)
SALON_JAIL_MODES: Modes appliqués au canal de prison. (default : sS)
SALON_LIBERER: Canal utilisé pour la libération des utilisateurs sanctionnés. (default : #welcome)
API (API)
API_TIMEOUT: Durée maximale d'attente d'une réponse de l'API en secondes.
API_TIMEOUT: Durée maximale d'attente d'une réponse de l'API en secondes. (default : 2)
SCANNER (Scanner)
PORTS_TO_SCAN: Liste des ports à scanner pour détecter des serveurs potentiellement malveillants.
PORTS_TO_SCAN: Liste des ports à scanner pour détecter des serveurs potentiellement malveillants. (default : [])
SÉCURITÉ (Sécurité)
WHITELISTED_IP: Liste d'adresses IP autorisées à contourner certaines restrictions.
GLINE_DURATION: Durée de bannissement temporaire d'un utilisateur en minutes.
WHITELISTED_IP: Liste d'adresses IP autorisées à contourner certaines restrictions. (default : ['127.0.0.1'])
GLINE_DURATION: Durée de bannissement temporaire d'un utilisateur en minutes. (default : "30")
DEBUG (Debug)
DEBUG_LEVEL: Niveau de verbosité des messages de debug (plus grand est le nombre, plus il y a d'informations).
COULEURS (Couleurs)
CONFIG_COLOR: Dictionnaire contenant des codes de couleurs IRC pour un meilleur affichage des messages.
DEBUG_LEVEL: Niveau de verbosité des messages de debug (plus grand est le nombre, plus il y a d'informations). (default : 20) Pour une production
```
Modification de la configuration
Vous devez modifier le fichier configuration.json en remplaçant les valeurs par défaut avec vos propres informations. Assurez-vous de bien lire la description de chaque paramètre pour une configuration optimale du service.
## Exemple de configuration de base
```json
{
"SERVEUR_IP": "IP.DE.TON.SERVER",
"SERVEUR_HOSTNAME": "HOST.DE.TON.SERVER",
"SERVEUR_LINK": "LINK.DE.TON.SERVER",
"SERVEUR_PORT": 6901,
"SERVEUR_PASSWORD": "MOT_DE_PASS_DE_TON_LINK",
"SERVEUR_ID": "10Z",
"SERVEUR_SSL": true,
"SERVICE_NAME": "defender",
"SERVICE_NICKNAME": "PyDefender",
"SERVICE_REALNAME": "Python Defender Security",
"SERVICE_USERNAME": "PyDefender",
"SERVICE_HOST": "HOST.DE.TON.DEFENDER",
"OWNER": "TON_NICK_NAME",
"PASSWORD": "TON_PASSWORD"
}
```
## Exemple complet de configuration
```json
{
"SERVEUR_IP": "YOUR.SERVER.IP",
"SERVEUR_HOSTNAME": "YOUR.SERVER.HOST",
"SERVEUR_LINK": "LINK.DE.TON.SERVER",
"SERVEUR_PORT": 6901,
"SERVEUR_PASSWORD": "YOUR_LINK_PASSWORD",
"SERVEUR_ID": "10Z",
"SERVEUR_SSL": true,
"SERVICE_NAME": "defender",
"SERVICE_NICKNAME": "PyDefender",
"SERVICE_REALNAME": "Python Defender Security",
"SERVICE_USERNAME": "PyDefender",
"SERVICE_HOST": "HOST.DE.TON.DEFENDER",
"SERVICE_INFO": "Network IRC Service",
"SERVICE_CHANLOG": "#services",
"SERVICE_SMODES": "+ioqBS",
"SERVICE_CMODES": "ntsOP",
"SERVICE_UMODES": "o",
"SERVICE_PREFIX": "!",
"OWNER": "TON_NICK_NAME",
"PASSWORD": "TON_PASSWORD",
"JSONRPC_URL": "https://your.domaine.com:8600/api",
"JSONRPC_PATH_TO_SOCKET_FILE": "/PATH/TO/YOUR/IRCD/data/rpc.socket",
"JSONRPC_METHOD": "socket",
"JSONRPC_USER": "YOUR_RPC_USER",
"JSONRPC_PASSWORD": "YOUR_RPC_PASSWORD",
"SALON_JAIL": "#jail",
"SALON_JAIL_MODES": "sS",
"SALON_LIBERER": "#welcome",
"CLONE_CHANNEL": "#clones",
"CLONE_CMODES": "+nts",
"CLONE_LOG_HOST_EXEMPT": ["HOST.TO.SKIP"],
"CLONE_CHANNEL_PASSWORD": "YOUR_CHANNEL_PASSWORD",
"API_TIMEOUT": 2,
"PORTS_TO_SCAN": [3028, 8080, 1080, 1085, 4145, 9050],
"WHITELISTED_IP": ["127.0.0.1"],
"GLINE_DURATION": "30",
"DEBUG_LEVEL": 20
}
```
# \\!/ Attention \\!/
Le mot de passe de l'administrateur et le mot de passe du service doivent être modifiés pour des raisons de sécurité.
Ne partagez pas vos informations de connexion au serveur IRC avec des tiers.
a votre premiere connexion vous devez tapez
```
/msg [NomDuService] auth [nickname] [password]
-- Une fois identifié tapez la commande suivante
/msg [NomDuService] editaccess [nickname] [Nouveau-Password] 5
```
# Unrealircd configuration
```
listen {
ip *;
port 6901;
options { tls; serversonly; }
}
/msg [NomDuService] auth [nickname] [password]
-- Une fois identifié tapez la commande suivante
/msg [NomDuService] editaccess [nickname] [Nouveau-Password] 5
link LINK.DE.TON.SERVER
{
incoming {
mask *;
bind-ip *;
port 6901;
//options { tls; };
}
outgoing {
bind-ip *; /* ou une IP précise */
hostname LINK.DE.TON.SERVER;
port 6901;
//options { tls; }
}
password "YOUR_LINK_PASSWORD";
class servers;
}
ulines {
LINK.DE.TON.SERVER;
}
```
# Extension:
Le code est modulaire et conçu pour être facilement étendu. Vous pouvez ajouter de nouvelles commandes, de nouvelles fonctionnalités (mods/mod_test.py est un exemple pour bien demarrer la création de son module).

View File

@@ -10,12 +10,15 @@ class User:
uid: str
nickname: str
username: str
realname: str
hostname: str
umodes: str
vhost: str
isWebirc: bool
isWebsocket: bool
remote_ip: str
score_connexion: int
geoip: str = None
connexion_datetime: datetime = field(default=datetime.now())
UID_DB: list[UserModel] = []
@@ -410,6 +413,11 @@ class Clones:
alive: bool
nickname: str
username: str
realname: str
channels: list
vhost: str = None
init: bool = True
connected: bool = False
UID_CLONE_DB: list[CloneModel] = []

View File

@@ -26,8 +26,6 @@ class Base:
self.engine, self.cursor = self.db_init() # Initialisation de la connexion a la base de données
self.__create_db() # Initialisation de la base de données
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
@@ -429,7 +427,7 @@ class Base:
except AssertionError as ae:
self.logs.error(f'Assertion Error -> {ae}')
def create_thread(self, func:object, func_args: tuple = (), run_once:bool = False) -> None:
def create_thread(self, func:object, func_args: tuple = (), run_once:bool = False, daemon: bool = True) -> None:
"""Create a new thread and store it into running_threads variable
Args:
@@ -445,7 +443,7 @@ class Base:
if thread.getName() == func_name:
return None
th = threading.Thread(target=func, args=func_args, name=str(func_name), daemon=True)
th = threading.Thread(target=func, args=func_args, name=str(func_name), daemon=daemon)
th.start()
self.running_threads.append(th)

View File

@@ -1,4 +1,6 @@
import socket, ssl
import socket
import ssl
import traceback
from ssl import SSLSocket
from core.loadConf import Config
from core.Model import Clones
@@ -7,13 +9,16 @@ from typing import Union
class Connection:
def __init__(self, server_port: int, nickname: str, username: str, channels:list[str], CloneObject: Clones, ssl:bool = False) -> None:
def __init__(self, server_port: int, nickname: str, username: str, realname: str, channels:list[str], CloneObject: Clones, ssl:bool = False) -> None:
self.Config = Config().ConfigObject
self.Base = Base(self.Config)
self.IrcSocket: Union[socket.socket, SSLSocket] = None
self.nickname = nickname
self.username = username
self.realname = realname
self.clone_chanlog = self.Config.CLONE_CHANNEL
self.clone_log_exempt = self.Config.CLONE_LOG_HOST_EXEMPT
self.channels:list[str] = channels
self.CHARSET = ['utf-8', 'iso-8859-1']
self.Clones = CloneObject
@@ -60,7 +65,7 @@ class Connection:
self.Base.logs.critical(f"AttributeError __create_socket: {ae} - {soc.fileno()}")
return False
def send2socket(self, send_message:str) -> None:
def send2socket(self, send_message:str, disconnect: bool = False) -> None:
"""Envoit les commandes à envoyer au serveur.
Args:
@@ -68,9 +73,8 @@ class Connection:
"""
try:
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}')
self.Base.logs.debug(f'<<{self.currentCloneObject.nickname}>>: {send_message}')
except UnicodeDecodeError:
self.Base.logs.error(f'Decode Error try iso-8859-1 - message: {send_message}')
@@ -97,10 +101,11 @@ class Connection:
try:
nickname = self.nickname
username = self.username
realname = self.realname
# Envoyer un message d'identification
writer.send(f"USER {nickname} {username} {username} {nickname} {username} :{username}\r\n".encode('utf-8'))
writer.send(f"USER {username} {username} {username} :{username}\r\n".encode('utf-8'))
writer.send(f"USER {username} {username} {username} :{realname}\r\n".encode('utf-8'))
writer.send(f"NICK {nickname}\r\n".encode('utf-8'))
self.Base.logs.debug('Link information sent to the server')
@@ -111,7 +116,6 @@ class Connection:
def connect(self):
try:
while self.signal:
try:
# 4072 max what the socket can grab
@@ -129,6 +133,7 @@ class Connection:
data = data_in_bytes.splitlines(True)
if not data:
# If no data then quit the loop
break
self.parser(data)
@@ -142,9 +147,10 @@ class Connection:
self.Base.logs.error(f"OSError __connect_to_irc: {oe} - {data}")
self.signal = False
self.IrcSocket.shutdown(socket.SHUT_RDWR)
self.IrcSocket.close()
self.Base.logs.info("--> Clone Disconnected ...")
self.IrcSocket.shutdown(socket.SHUT_WR)
self.IrcSocket.shutdown(socket.SHUT_RD)
self.currentCloneObject.init = False
self.Base.logs.info(f"<<{self.currentCloneObject.nickname}>> Clone Disconnected ...")
except AssertionError as ae:
self.Base.logs.error(f'Assertion error : {ae}')
@@ -154,14 +160,16 @@ class Connection:
self.Base.logs.error(f"OS Error __connect_to_irc: {soe}")
except AttributeError as atte:
self.Base.logs.critical(f"{atte}")
self.Base.logs.critical(f"{traceback.format_exc()}")
except Exception as e:
self.Base.logs.error(f"Exception: {e}")
def parser(self, cmd:list[bytes]):
try:
for data in cmd:
response = data.decode(self.CHARSET[0]).split()
self.signal = self.currentCloneObject.alive
current_clone_nickname = self.currentCloneObject.nickname
# print(response)
match response[0]:
@@ -172,24 +180,67 @@ class Connection:
case 'ERROR':
error_value = str(response[1]).replace(':','')
if error_value == 'Closing':
self.signal = False
self.Base.logs.info(f"<<{self.currentCloneObject.nickname}>> {response} ...")
self.currentCloneObject.connected = False
else:
self.Base.logs.info(f"<<{self.currentCloneObject.nickname}>> {response} ...")
# self.signal = False
match response[1]:
case '376':
# End of MOTD
self.currentCloneObject.connected = True
self.currentCloneObject.init = False
for channel in self.channels:
self.send2socket(f"JOIN {channel}")
self.send2socket(f"JOIN {self.clone_chanlog} {self.Config.CLONE_CHANNEL_PASSWORD}")
return None
case '422':
# Missing MOTD
self.currentCloneObject.connected = True
self.currentCloneObject.init = False
for channel in self.channels:
self.send2socket(f"JOIN {channel}")
self.send2socket(f"JOIN {self.clone_chanlog} {self.Config.CLONE_CHANNEL_PASSWORD}")
return None
case '433':
# Nickname already in use
self.currentCloneObject.connected = False
self.currentCloneObject.init = False
self.send2socket(f'QUIT :Thanks and goodbye')
self.Base.logs.warning(f"Nickname {self.currentCloneObject.nickname} already in use >> Clone should be disconnected")
return None
case 'PRIVMSG':
self.Base.logs.debug(response)
self.Base.logs.debug(f'{self.currentCloneObject.nickname} - {self.currentCloneObject.alive}')
self.Base.logs.debug(f'<<{self.currentCloneObject.nickname}>> Response: {response}')
self.Base.logs.debug(f'<<{self.currentCloneObject.nickname}>> Alive: {self.currentCloneObject.alive}')
fullname = str(response[0]).replace(':', '')
nickname = fullname.split('!')[0].replace(':','')
if response[2] == current_clone_nickname and nickname != self.Config.SERVICE_NICKNAME:
message = []
for i in range(3, len(response)):
message.append(response[i])
final_message = ' '.join(message)
exampt = False
for log_exception in self.clone_log_exempt:
if log_exception in fullname:
exampt = True
if not exampt:
self.send2socket(f"PRIVMSG {self.clone_chanlog} :{fullname} => {final_message[1:]}")
if nickname == self.Config.SERVICE_NICKNAME:
command = str(response[3]).replace(':','')
if command == 'KILL':
self.send2socket(f'QUIT :Thanks and goodbye')
self.signal = self.currentCloneObject.alive
if command == 'JOIN':
channel_to_join = str(response[4])

View File

@@ -1,48 +1,48 @@
{
"SERVEUR_IP": "0.0.0.0",
"SERVEUR_HOSTNAME": "your.host.name",
"SERVEUR_LINK": "your.link.to.server",
"SERVEUR_IP": "YOUR.SERVER.IP",
"SERVEUR_HOSTNAME": "YOUR.SERVER.HOST",
"SERVEUR_LINK": "LINK.DE.TON.SERVER",
"SERVEUR_PORT": 7002,
"SERVEUR_PASSWORD": "link_password",
"SERVEUR_PASSWORD": "YOUR_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_NICKNAME": "PyDefender",
"SERVICE_REALNAME": "Python Defender Security",
"SERVICE_USERNAME": "PyDefender",
"SERVICE_HOST": "HOST.DE.TON.DEFENDER",
"SERVICE_INFO": "Network IRC Service",
"SERVICE_CHANLOG": "#services",
"SERVICE_SMODES": "+ioqBS",
"SERVICE_CMODES": "ntsO",
"SERVICE_CMODES": "ntsOP",
"SERVICE_UMODES": "o",
"SERVICE_PREFIX": "!",
"OWNER": "admin",
"PASSWORD": "password",
"OWNER": "TON_NICK_NAME",
"PASSWORD": "TON_PASSWORD",
"JSONRPC_URL": "https://your.domaine.com:8600/api",
"JSONRPC_PATH_TO_SOCKET_FILE": "/PATH/TO/YOUR/IRCD/data/rpc.socket",
"JSONRPC_METHOD": "socket",
"JSONRPC_USER": "YOUR_RPC_USER",
"JSONRPC_PASSWORD": "YOUR_RPC_PASSWORD",
"SALON_JAIL": "#jail",
"SALON_JAIL_MODES": "sS",
"SALON_LIBERER": "#welcome",
"CLONE_CHANNEL": "#clones",
"CLONE_CMODES": "+nts",
"CLONE_LOG_HOST_EXEMPT": ["HOST.TO.SKIP"],
"CLONE_CHANNEL_PASSWORD": "YOUR_CHANNEL_PASSWORD",
"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"
}
"DEBUG_LEVEL": 20
}

View File

@@ -28,22 +28,17 @@ class Install:
self.set_configuration()
if not self.check_python_version():
# Tester si c'est la bonne version de python
exit("Python Version Error")
else:
if self.skip_install:
return None
if self.skip_install:
return None
# Sinon tester les dependances python et les installer avec pip
if self.do_install():
# Sinon tester les dependances python et les installer avec pip
if self.do_install():
self.install_dependencies()
self.install_dependencies()
self.create_service_file()
self.create_service_file()
self.print_final_message()
self.print_final_message()
return None
@@ -69,18 +64,29 @@ class Install:
defender_install_folder=defender_install_folder,
venv_folder=venv_folder,
venv_cmd_installation=['python3', '-m', 'venv', venv_folder],
venv_cmd_requirements=['sqlalchemy','psutil','requests','faker'],
venv_cmd_requirements=['sqlalchemy','psutil','requests','faker','unrealircd_rpc_py'],
venv_pip_executable=f'{os.path.join(defender_install_folder, venv_folder, "bin")}{os.sep}pip',
venv_python_executable=f'{os.path.join(defender_install_folder, venv_folder, "bin")}{os.sep}python'
)
# Exclude Windows OS
if not self.check_python_version():
# If the Python version is not good then Exit
exit("/!\\ Python version error /!\\")
if not os.path.exists(os.path.join(self.config.defender_install_folder, 'core', 'configuration.json')):
# If configuration file do not exist
exit("/!\\ Configuration file (configuration.json) doesn't exist /!\\")
# Exclude Windows OS from the installation
if os.name == 'nt':
#print('/!\\ Skip installation /!\\')
self.skip_install = True
else:
if self.is_root():
self.skip_install = True
return False
if self.is_root():
exit(f'/!\\ I highly not recommend running Defender as root /!\\')
self.skip_install = True
return False
def is_root(self) -> bool:

View File

@@ -29,7 +29,7 @@ class Irc:
# Liste des commandes internes du bot
self.commands_level = {
0: ['help', 'auth', 'copyright', 'uptime'],
0: ['help', 'auth', 'copyright', 'uptime', 'firstauth'],
1: ['load','reload','unload', 'deauth', 'checkversion'],
2: ['show_modules', 'show_timers', 'show_threads', 'show_channels', 'show_users', 'show_admins'],
3: ['quit', 'restart','addaccess','editaccess', 'delaccess']
@@ -175,8 +175,8 @@ class Irc:
except AttributeError as atte:
self.Base.logs.critical(f"AttributeError: {atte}")
except Exception as e:
self.Base.logs.critical(f"Exception: {e}")
self.Base.logs.critical(traceback.print_exc())
self.Base.logs.critical(f"General Error: {e}")
self.Base.logs.critical(traceback.format_exc())
def __link(self, writer:Union[socket.socket, SSLSocket]) -> None:
"""Créer le link et envoyer les informations nécessaires pour la
@@ -222,7 +222,7 @@ class Irc:
writer.send(f":{service_id} MODE {chan} +{cmodes}\r\n".encode(charset))
writer.send(f":{service_id} MODE {chan} +{umodes} {service_id}\r\n".encode(charset))
self.Base.logs.debug('Link information sent to the server')
self.Base.logs.debug('>> Link information sent to the server')
return None
except AttributeError as ae:
@@ -249,7 +249,6 @@ class Irc:
"""
try:
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}')
@@ -268,6 +267,41 @@ class Irc:
except OSError as oe:
self.Base.logs.error(f"OSError: {oe} - {send_message}")
def sendNotice(self, msg:str, nickname: str) -> None:
"""Sending NOTICE by batches
Args:
msg (str): The message to send to the server
nickname (str): The reciever Nickname
"""
batch_size = self.Config.BATCH_SIZE
service_nickname = self.Config.SERVICE_NICKNAME
for i in range(0, len(str(msg)), batch_size):
batch = str(msg)[i:i+batch_size]
self.send2socket(f":{service_nickname} NOTICE {nickname} :{batch}")
def sendPrivMsg(self, msg: str, channel: str = None, nickname: str = None):
"""Sending PRIVMSG to a channel or to a nickname by batches
could be either channel or nickname not both together
Args:
msg (str): The message to send
channel (str, optional): The receiver channel. Defaults to None.
nickname (str, optional): The reciever nickname. Defaults to None.
"""
batch_size = self.Config.BATCH_SIZE
service_nickname = self.Config.SERVICE_NICKNAME
if not channel is None:
for i in range(0, len(str(msg)), batch_size):
batch = str(msg)[i:i+batch_size]
self.send2socket(f":{service_nickname} PRIVMSG {channel} :{batch}")
if not nickname is None:
for i in range(0, len(str(msg)), batch_size):
batch = str(msg)[i:i+batch_size]
self.send2socket(f":{service_nickname} PRIVMSG {nickname} :{batch}")
def send_response(self, responses:list[bytes]) -> None:
try:
# print(data)
@@ -452,11 +486,11 @@ class Irc:
except ModuleNotFoundError as 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.COLORS.red}MODULE_NOT_FOUND{self.Config.COLORS.black} ]: {moduleNotFound}")
self.Base.db_delete_module(module_name)
except Exception as 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.COLORS.red}ERROR{self.Config.COLORS.black} ]: {e}")
def insert_db_admin(self, uid:str, level:int) -> None:
@@ -591,7 +625,7 @@ class Irc:
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')
return None
def cmd(self, data: list[str]) -> None:
@@ -655,6 +689,30 @@ class Irc:
# self.Base.create_thread(self.abuseipdb_scan, (cmd[7], ))
pass
case 'SQUIT':
# ['@msgid=QOEolbRxdhpVW5c8qLkbAU;time=2024-09-21T17:33:16.547Z', 'SQUIT', 'defender.deb.biz.st', ':Connection', 'closed']
server_hostname = interm_response[1]
uid_to_delete = ''
for s_user in self.User.UID_DB:
if s_user.hostname == server_hostname and 'S' in s_user.umodes:
uid_to_delete = s_user.uid
self.User.delete(uid_to_delete)
self.Channel.delete_user_from_all_channel(uid_to_delete)
case 'SJOIN':
# If Server Join channels
# [':11Z', 'SJOIN', '1726940687', '#welcome', '+', ':11ZAAAAAB']
channel_joined = original_response[3]
server_uid = self.Base.clean_uid(original_response[5])
self.Channel.insert(
self.Channel.ChannelModel(
name=channel_joined,
uids=[server_uid]
)
)
case 'REPUTATION':
# :001 REPUTATION 91.168.141.239 118
try:
@@ -728,6 +786,7 @@ class Irc:
self.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} : New Version available {version}")
# Initialisation terminé aprés le premier PING
self.sendPrivMsg(msg=f'[{self.Config.COLORS.green}INFORMATION{self.Config.COLORS.nogc}] >> Defender is ready', channel=self.Config.SERVICE_CHANLOG)
self.INIT = 0
case _:
@@ -813,44 +872,67 @@ class Irc:
self.Base.logs.error(f'Index Error: {ie}')
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',
# ':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==', ':...']
if 'webirc' in original_response[0]:
isWebirc = True
else:
isWebirc = False
try:
# ['@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',
# '001HB8G04', '0', '+iwxz', 'Clk-A62F1D18.vps.ovh.net', 'Clk-A62F1D18.vps.ovh.net', 'MyZBwg==', ':...']
uid = str(original_response[8])
nickname = str(original_response[3])
username = str(original_response[6])
hostname = str(original_response[7])
umodes = str(original_response[10])
vhost = str(original_response[11])
if not 'S' in umodes:
remote_ip = self.Base.decode_ip(str(original_response[13]))
else:
remote_ip = '127.0.0.1'
isWebirc = True if 'webirc' in original_response[0] else False
isWebsocket = True if 'websocket' in original_response[0] else False
score_connexion = self.first_score
uid = str(original_response[8])
nickname = str(original_response[3])
username = str(original_response[6])
hostname = str(original_response[7])
umodes = str(original_response[10])
vhost = str(original_response[11])
self.User.insert(
self.User.UserModel(
uid=uid,
nickname=nickname,
username=username,
hostname=hostname,
umodes=umodes,
vhost=vhost,
isWebirc=isWebirc,
remote_ip=remote_ip,
score_connexion=score_connexion,
connexion_datetime=datetime.now()
if not 'S' in umodes:
remote_ip = self.Base.decode_ip(str(original_response[13]))
else:
remote_ip = '127.0.0.1'
# extract realname
realname_list = []
for i in range(14, len(original_response)):
realname_list.append(original_response[i])
realname = ' '.join(realname_list)[1:]
# Extract Geoip information
pattern = r'^.*geoip=cc=(\S{2}).*$'
geoip_match = re.match(pattern, original_response[0])
if geoip_match:
geoip = geoip_match.group(1)
else:
geoip = None
score_connexion = self.first_score
self.User.insert(
self.User.UserModel(
uid=uid,
nickname=nickname,
username=username,
realname=realname,
hostname=hostname,
umodes=umodes,
vhost=vhost,
isWebirc=isWebirc,
isWebsocket=isWebsocket,
remote_ip=remote_ip,
geoip=geoip,
score_connexion=score_connexion,
connexion_datetime=datetime.now()
)
)
)
for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(original_response)
for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(original_response)
except Exception as err:
self.Base.logs.error(f'General Error: {err}')
case 'PRIVMSG':
try:
@@ -944,6 +1026,9 @@ class Irc:
except IndexError as ie:
self.Base.logs.error(f"{ie} / {original_response} / length {str(len(original_response))}")
except Exception as err:
self.Base.logs.error(f"General Error: {err}")
self.Base.logs.error(f"General Error: {traceback.format_exc()}")
def _hcmds(self, user: str, channel: Union[str, None], cmd: list, fullcmd: list = []) -> None:
"""_summary_
@@ -984,7 +1069,7 @@ class Irc:
case 'notallowed':
try:
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.User.get_nickname(fromuser)}')
self.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.red}{current_command}{self.Config.COLORS.black} ] - Accès Refusé à {self.User.get_nickname(fromuser)}')
self.send2socket(f':{dnickname} NOTICE {fromuser} : Accès Refusé')
except IndexError as ie:
self.Base.logs.error(f'{ie}')
@@ -994,7 +1079,57 @@ class Irc:
current_command = cmd[0]
uid_to_deauth = self.User.get_uid(fromuser)
self.delete_db_admin(uid_to_deauth)
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}{current_command}{self.Config.CONFIG_COLOR['noire']} ] - {self.User.get_nickname(fromuser)} est désormais déconnecter de {dnickname}")
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.red}{str(current_command).upper()} ]{self.Config.COLORS.black} - {self.User.get_nickname(fromuser)} est désormais déconnecter de {dnickname}")
case 'firstauth':
# firstauth OWNER_NICKNAME OWNER_PASSWORD
current_nickname = self.User.get_nickname(fromuser)
current_uid = self.User.get_uid(fromuser)
current_command = str(cmd[0])
query = f"SELECT count(id) as c FROM {self.Config.table_admin}"
result = self.Base.db_execute_query(query)
result_db = result.fetchone()
if result_db[0] > 0:
self.send2socket(f":{dnickname} NOTICE {fromuser} :You can't use this command anymore ! Please use [{self.Config.SERVICE_PREFIX}auth] instead")
return False
if current_nickname is None:
self.Base.logs.critical(f"This nickname [{fromuser}] don't exist")
return False
# Credentials sent from the user
cmd_owner = str(cmd[1])
cmd_password = str(cmd[2])
# Credentials coming from the Configuration
config_owner = self.Config.OWNER
config_password = self.Config.PASSWORD
if current_nickname != cmd_owner:
self.Base.logs.critical(f"The current nickname [{fromuser}] is different than the nickname sent [{cmd_owner}] !")
self.send2socket(f":{dnickname} NOTICE {fromuser} :The current nickname [{fromuser}] is different than the nickname sent [{cmd_owner}] !")
return False
if current_nickname != config_owner:
self.Base.logs.critical(f"The current nickname [{current_nickname}] is different than the configuration owner [{config_owner}] !")
self.send2socket(f":{dnickname} NOTICE {fromuser} :The current nickname [{current_nickname}] is different than the configuration owner [{config_owner}] !")
return False
if cmd_owner != config_owner:
self.Base.logs.critical(f"The nickname sent [{cmd_owner}] is different than the configuration owner [{config_owner}] !")
self.send2socket(f":{dnickname} NOTICE {fromuser} :The nickname sent [{cmd_owner}] is different than the configuration owner [{config_owner}] !")
return False
if cmd_owner == config_owner and cmd_password == config_password:
self.Base.db_create_first_admin()
self.insert_db_admin(current_uid, 5)
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}{str(current_command).upper()} ]{self.Config.COLORS.black} - {self.User.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.COLORS.red}{str(current_command).upper()} ]{self.Config.COLORS.black} - {self.User.get_nickname(fromuser)} a tapé un mauvais mot de pass")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Mot de passe incorrecte")
case 'auth':
# ['auth', 'adator', 'password']
@@ -1016,10 +1151,10 @@ class Irc:
if not user_from_db is None:
uid_user = self.User.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.User.get_nickname(fromuser)} est désormais connecté a {dnickname}")
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}{str(current_command).upper()} ]{self.Config.COLORS.black} - {self.User.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.User.get_nickname(fromuser)} a tapé un mauvais mot de pass")
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.red}{str(current_command).upper()} ]{self.Config.COLORS.black} - {self.User.get_nickname(fromuser)} a tapé un mauvais mot de pass")
self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Mot de passe incorrecte")
else:
@@ -1028,6 +1163,10 @@ class Irc:
case 'addaccess':
try:
# .addaccess adator 5 password
if len(cmd) < 4:
self.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} addaccess [nickname] [level] [password]')
self.send2socket(f':{dnickname} NOTICE {fromuser} : level: from 1 to 4')
newnickname = cmd[1]
newlevel = self.Base.int_if_possible(cmd[2])
password = cmd[3]
@@ -1154,13 +1293,13 @@ class Irc:
else:
user_level = 0
self.send2socket(f':{dnickname} NOTICE {fromuser} : **************** LIST DES COMMANDES *****************')
self.send2socket(f':{dnickname} NOTICE {fromuser} : ***************** LISTE DES COMMANDES *****************')
self.send2socket(f':{dnickname} NOTICE {fromuser} : ')
for levDef in self.commands_level:
if int(user_level) >= int(count_level_definition):
self.send2socket(f':{dnickname} NOTICE {fromuser} : **************** {self.Config.CONFIG_COLOR["noire"]}[ {self.Config.CONFIG_COLOR["verte"]}LEVEL {str(levDef)} {self.Config.CONFIG_COLOR["noire"]}] ****************')
self.send2socket(f':{dnickname} NOTICE {fromuser} : ***************** {self.Config.COLORS.nogc}[ {self.Config.COLORS.green}LEVEL {str(levDef)} {self.Config.COLORS.nogc}] *****************')
count_commands = 0
help = ''
for comm in self.commands_level[count_level_definition]:
@@ -1174,7 +1313,7 @@ class Irc:
count_level_definition += 1
self.send2socket(f':{dnickname} NOTICE {fromuser} : **************** FIN DES COMMANDES *****************')
self.send2socket(f':{dnickname} NOTICE {fromuser} : ***************** FIN DES COMMANDES *****************')
case 'load':
@@ -1310,9 +1449,9 @@ class Irc:
found = True
if found:
self.send2socket(f":{dnickname} NOTICE {fromuser} :{module} - {self.Config.CONFIG_COLOR['verte']}Loaded{self.Config.CONFIG_COLOR['nogc']}")
self.send2socket(f":{dnickname} NOTICE {fromuser} :{module} - {self.Config.COLORS.green}Loaded{self.Config.COLORS.nogc}")
else:
self.send2socket(f":{dnickname} NOTICE {fromuser} :{module} - {self.Config.CONFIG_COLOR['rouge']}Not Loaded{self.Config.CONFIG_COLOR['nogc']}")
self.send2socket(f":{dnickname} NOTICE {fromuser} :{module} - {self.Config.COLORS.red}Not Loaded{self.Config.COLORS.nogc}")
found = False
@@ -1342,7 +1481,7 @@ class Irc:
case 'show_users':
for db_user in self.User.UID_DB:
self.send2socket(f":{dnickname} NOTICE {fromuser} :UID : {db_user.uid} - isWebirc: {db_user.isWebirc} - Nickname: {db_user.nickname} - Connection: {db_user.connexion_datetime}")
self.send2socket(f":{dnickname} NOTICE {fromuser} :UID : {db_user.uid} - isWebirc: {db_user.isWebirc} - isWebSocket: {db_user.isWebsocket} - Nickname: {db_user.nickname} - Connection: {db_user.connexion_datetime}")
case 'show_admins':
for db_admin in self.Admin.UID_ADMIN_DB:
@@ -1353,7 +1492,7 @@ class Irc:
self.send2socket(f':{dnickname} NOTICE {fromuser} : {uptime}')
case 'copyright':
self.send2socket(f':{dnickname} NOTICE {fromuser} : # Defender V.{self.Config.current_version} Developped by adator® and dktmb® #')
self.send2socket(f':{dnickname} NOTICE {fromuser} : # Defender V.{self.Config.current_version} Developped by adator® #')
case 'checkversion':

View File

@@ -7,6 +7,17 @@ from dataclasses import dataclass, field
# CONFIGURATION FILE #
##########################################
@dataclass
class ColorModel:
white: str = "\x0300"
black: str = "\x0301"
blue: str = "\x0302"
green: str = "\x0303"
red: str = "\x0304"
yellow: str = "\x0306"
bold: str = "\x02"
nogc: str = "\x03"
@dataclass
class ConfigDataModel:
@@ -73,6 +84,12 @@ class ConfigDataModel:
PASSWORD: str
"""The password of the admin of the service"""
JSONRPC_URL: str
JSONRPC_PATH_TO_SOCKET_FILE: str
JSONRPC_METHOD: str
JSONRPC_USER: str
JSONRPC_PASSWORD: str
SALON_JAIL: str
"""The JAIL channel (ex. #jail)"""
@@ -82,6 +99,18 @@ class ConfigDataModel:
SALON_LIBERER: str
"""Channel where the nickname will be released"""
CLONE_CHANNEL: str
"""Channel where clones are hosted and will log PRIVMSG"""
CLONE_CMODES: str
"""Clone channel modes"""
CLONE_LOG_HOST_EXEMPT: list[str]
"""Hosts that clones will not log"""
CLONE_CHANNEL_PASSWORD: str
"""Clone password channel"""
API_TIMEOUT: int
"""Default api timeout in second"""
@@ -97,8 +126,6 @@ class ConfigDataModel:
DEBUG_LEVEL:Literal[10, 20, 30, 40, 50]
"""Logs level: DEBUG 10 | INFO 20 | WARNING 30 | ERROR 40 | CRITICAL 50"""
CONFIG_COLOR: dict[str, str]
table_admin: str
"""Admin table"""
@@ -129,6 +156,12 @@ class ConfigDataModel:
db_path: str
"""The database path"""
COLORS: ColorModel = field(default_factory=ColorModel)
"""Available colors in Defender"""
BATCH_SIZE: int = 400
"""The batch size used for privmsg and notice"""
def __post_init__(self):
# Initialiser SERVICE_ID après la création de l'objet
self.SERVICE_ID:str = f"{self.SERVEUR_ID}AAAAAB"
@@ -147,49 +180,61 @@ class Config:
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] = str(value).encode('utf-8').decode('unicode_escape')
return configuration
except FileNotFoundError as fe:
print(f'FileNotFound: {fe}')
print('Configuration file not found please create core/configuration.json')
sys.exit(0)
except KeyError as ke:
print(f'Key Error: {ke}')
print('The key must be defined in core/configuration.json')
def __load_service_configuration(self) -> ConfigDataModel:
import_config = self.__load_json_service_configuration()
ConfigObject: ConfigDataModel = 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"],
SERVEUR_IP=import_config["SERVEUR_IP"] if "SERVEUR_IP" in import_config else '127.0.0.1',
SERVEUR_HOSTNAME=import_config["SERVEUR_HOSTNAME"] if "SERVEUR_HOSTNAME" in import_config else None,
SERVEUR_LINK=import_config["SERVEUR_LINK"] if "SERVEUR_LINK" in import_config else None,
SERVEUR_PORT=import_config["SERVEUR_PORT"] if "SERVEUR_PORT" in import_config else 6667,
SERVEUR_PASSWORD=import_config["SERVEUR_PASSWORD"] if "SERVEUR_PASSWORD" in import_config else None,
SERVEUR_ID=import_config["SERVEUR_ID"] if "SERVEUR_ID" in import_config else '19Z',
SERVEUR_SSL=import_config["SERVEUR_SSL"] if "SERVEUR_SSL" in import_config else False,
SERVICE_NAME=import_config["SERVICE_NAME"] if "SERVICE_NAME" in import_config else 'Defender',
SERVICE_NICKNAME=import_config["SERVICE_NICKNAME"] if "SERVICE_NICKNAME" in import_config else 'Defender',
SERVICE_REALNAME=import_config["SERVICE_REALNAME"] if "SERVICE_REALNAME" in import_config else 'Defender Security',
SERVICE_USERNAME=import_config["SERVICE_USERNAME"] if "SERVICE_USERNAME" in import_config else 'IRCSecurity',
SERVICE_HOST=import_config["SERVICE_HOST"] if "SERVICE_HOST" in import_config else 'defender.local.network',
SERVICE_INFO=import_config["SERVICE_INFO"] if "SERVICE_INFO" in import_config else 'Defender Network IRC Service',
SERVICE_CHANLOG=import_config["SERVICE_CHANLOG"] if "SERVICE_CHANLOG" in import_config else '#services',
SERVICE_SMODES=import_config["SERVICE_SMODES"] if "SERVICE_SMODES" in import_config else '+ioqBS',
SERVICE_CMODES=import_config["SERVICE_CMODES"] if "SERVICE_CMODES" in import_config else 'ntsOP',
SERVICE_UMODES=import_config["SERVICE_UMODES"] if "SERVICE_UMODES" in import_config else 'o',
SERVICE_PREFIX=import_config["SERVICE_PREFIX"] if "SERVICE_PREFIX" in import_config else '!',
OWNER=import_config["OWNER"] if "OWNER" in import_config else 'admin',
PASSWORD=import_config["PASSWORD"] if "PASSWORD" in import_config else 'admin',
JSONRPC_METHOD=import_config["JSONRPC_METHOD"] if "JSONRPC_METHOD" in import_config else 'socket',
JSONRPC_URL=import_config["JSONRPC_URL"] if "JSONRPC_URL" in import_config else None,
JSONRPC_PATH_TO_SOCKET_FILE=import_config["JSONRPC_PATH_TO_SOCKET_FILE"] if "JSONRPC_PATH_TO_SOCKET_FILE" in import_config else None,
JSONRPC_USER=import_config["JSONRPC_USER"] if "JSONRPC_USER" in import_config else None,
JSONRPC_PASSWORD=import_config["JSONRPC_PASSWORD"] if "JSONRPC_PASSWORD" in import_config else None,
SALON_JAIL=import_config["SALON_JAIL"] if "SALON_JAIL" in import_config else '#jail',
SALON_JAIL_MODES=import_config["SALON_JAIL_MODES"] if "SALON_JAIL_MODES" in import_config else 'sS',
SALON_LIBERER=import_config["SALON_LIBERER"] if "SALON_LIBERER" in import_config else '#welcome',
CLONE_CHANNEL=import_config["CLONE_CHANNEL"] if "CLONE_CHANNEL" in import_config else '#clones',
CLONE_CMODES=import_config["CLONE_CMODES"] if "CLONE_CMODES" in import_config else '+nts',
CLONE_LOG_HOST_EXEMPT=import_config["CLONE_LOG_HOST_EXEMPT"] if "CLONE_LOG_HOST_EXEMPT" in import_config else [],
CLONE_CHANNEL_PASSWORD=import_config["CLONE_CHANNEL_PASSWORD"] if "CLONE_CHANNEL_PASSWORD" in import_config else "clone_Password_1234",
API_TIMEOUT=import_config["API_TIMEOUT"] if "API_TIMEOUT" in import_config else 2,
PORTS_TO_SCAN=import_config["PORTS_TO_SCAN"] if "PORTS_TO_SCAN" in import_config else [],
WHITELISTED_IP=import_config["WHITELISTED_IP"] if "WHITELISTED_IP" in import_config else ['127.0.0.1'],
GLINE_DURATION=import_config["GLINE_DURATION"] if "GLINE_DURATION" in import_config else '30',
DEBUG_LEVEL=import_config["DEBUG_LEVEL"] if "DEBUG_LEVEL" in import_config else 20,
table_admin='core_admin',
table_commande='core_command',
table_log='core_log',

View File

@@ -1,5 +1,5 @@
from dataclasses import dataclass, fields, field
import random, faker, time
import random, faker, time, logging
from datetime import datetime
from typing import Union
from core.irc import Irc
@@ -58,6 +58,11 @@ class Clone():
# Load module configuration (Mandatory)
self.__load_module_configuration()
self.Base.db_query_channel(action='add', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} JOIN {self.Config.CLONE_CHANNEL}")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} +nts")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} +k {self.Config.CLONE_CHANNEL_PASSWORD}")
def __set_commands(self, commands:dict[int, list[str]]) -> None:
"""### Rajoute les commandes du module au programme principal
@@ -120,53 +125,139 @@ class Clone():
for clone in self.ModConfig.clone_nicknames:
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone} :KILL')
self.Base.db_query_channel(action='del', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} PART {self.Config.CLONE_CHANNEL}")
return None
def thread_create_clones(self, nickname: str, username: str, channels: list, server_port: int, ssl: bool) -> None:
def thread_clone_clean_up(self, wait: float):
Connection(server_port=server_port, nickname=nickname, username=username, channels=channels, CloneObject=self.Clone, ssl=ssl)
activated = True
while activated:
clone_to_kill: list[str] = []
for clone in self.Clone.UID_CLONE_DB:
if not clone.connected and clone.alive and not clone.init:
clone_to_kill.append(clone.nickname)
clone.alive = False
for clone_nickname in clone_to_kill:
if self.Clone.delete(clone_nickname):
self.Logs.debug(f'<<{clone_nickname}>> object has been deleted')
del clone_to_kill
# If LIST empty then stop this thread
if not self.Clone.UID_CLONE_DB:
break
time.sleep(wait)
def thread_change_hostname(self):
fake = faker.Faker('en_GB')
for clone in self.Clone.UID_CLONE_DB:
if not clone.vhost is None:
continue
rand_1 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8)
rand_2 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8)
rand_3 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8)
rand_ip = ''.join(rand_1) + '.' + ''.join(rand_2) + '.' + ''.join(rand_3) + '.IP'
found = False
while not found:
if clone.connected:
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} CHGHOST {clone.nickname} {rand_ip}')
found = True
clone.vhost = rand_ip
break
if not clone in self.Clone.UID_CLONE_DB:
found = True
break
def thread_create_clones_with_interval(self, number_of_clones:int, channels: list, connection_interval: float):
for i in range(number_of_clones):
nickname, username, realname = self.generate_names()
self.Base.create_thread(
self.thread_create_clones,
(nickname, username, realname, channels, 6697, True)
)
time.sleep(connection_interval)
self.Base.create_thread(
self.thread_change_hostname
)
# self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{str(number_of_clones)} clones joined the network')
self.Base.create_thread(self.thread_clone_clean_up, (5, ), run_once=True)
def thread_create_clones(self, nickname: str, username: str, realname: str, channels: list, server_port: int, ssl: bool) -> None:
Connection(server_port=server_port, nickname=nickname, username=username, realname=realname, channels=channels, CloneObject=self.Clone, ssl=ssl)
return None
def thread_join_channels(self, channel_name: str, wait: float, clone_name:str = None):
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Clones start to join {channel_name} with {wait} secondes frequency')
if clone_name is None:
for clone in self.Clone.UID_CLONE_DB:
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}')
time.sleep(wait)
if not channel_name in clone.channels:
time.sleep(wait)
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}')
clone.channels.append(channel_name)
else:
for clone in self.Clone.UID_CLONE_DB:
if clone_name == clone.nickname:
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}')
time.sleep(wait)
if not channel_name in clone.channels:
time.sleep(wait)
self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}')
clone.channels.append(channel_name)
def generate_names(self) -> tuple[str, str]:
def generate_names(self) -> tuple[str, str, str]:
try:
logging.getLogger('faker').setLevel(logging.CRITICAL)
fake = faker.Faker('en_GB')
nickname = fake.first_name()
username = fake.last_name()
# nickname = fake.first_name()
# username = fake.last_name()
# Generate Username
chaine = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
new_username = fake.random_sample(chaine, 9)
username = ''.join(new_username)
# Create realname XX F|M Department
gender = fake.random_choices(['F','M'], 1)
gender = ''.join(gender)
if gender == 'F':
nickname = fake.first_name_female()
elif gender == 'M':
nickname = fake.first_name_male()
else:
nickname = fake.first_name()
age = random.randint(20, 60)
fake_fr = faker.Faker(['fr_FR', 'en_GB'])
department = fake_fr.department_name()
realname = f'{age} {gender} {department}'
if self.Clone.exists(nickname=nickname):
caracteres = '0123456789'
randomize = ''.join(random.choice(caracteres) for _ in range(2))
nickname = nickname + str(randomize)
self.Clone.insert(
self.Clone.CloneModel(alive=True, nickname=nickname, username=username)
self.Clone.CloneModel(alive=True, nickname=nickname, username=username, realname=realname, channels=[])
)
else:
self.Clone.insert(
self.Clone.CloneModel(alive=True, nickname=nickname, username=username)
self.Clone.CloneModel(alive=True, nickname=nickname, username=username, realname=realname, channels=[])
)
# if not nickname in self.ModConfig.clone_nicknames:
# self.ModConfig.clone_nicknames.append(nickname)
# else:
# caracteres = '0123456789'
# randomize = ''.join(random.choice(caracteres) for _ in range(2))
# nickname = nickname + str(randomize)
# self.ModConfig.clone_nicknames.append(nickname)
return (nickname, username)
return (nickname, username, realname)
except AttributeError as ae:
self.Logs.error(f'Attribute Error : {ae}')
@@ -188,117 +279,123 @@ class Clone():
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower()
fromuser = user
try:
command = str(cmd[0]).lower()
fromuser = user
dnickname = self.Config.SERVICE_NICKNAME # Defender nickname
dnickname = self.Config.SERVICE_NICKNAME # Defender nickname
match command:
match command:
case 'clone':
option = str(cmd[1]).lower()
case 'clone':
if len(command) == 1:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect 6')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill [all | nickname]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join [all | nickname] #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone list')
match option:
case 'connect':
try:
number_of_clones = int(cmd[2])
for i in range(number_of_clones):
nickname, username = self.generate_names()
self.Base.create_thread(
self.thread_create_clones,
(nickname, username, [], 6697, True)
)
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{str(number_of_clones)} clones joined the network')
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect [number of clone you want to connect]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} clone connect 6')
case 'kill':
try:
# clone kill [all | nickname]
clone_name = str(cmd[2])
clone_to_kill: list[str] = []
if clone_name.lower() == 'all':
for clone in self.Clone.UID_CLONE_DB:
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone.nickname} :KILL')
clone_to_kill.append(clone.nickname)
clone.alive = False
for clone_nickname in clone_to_kill:
self.Clone.delete(clone_nickname)
del clone_to_kill
else:
if self.Clone.exists(clone_name):
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone_name} :KILL')
self.Clone.kill(clone_name)
self.Clone.delete(clone_name)
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill all')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill clone_nickname')
case 'join':
try:
# clone join [all | nickname] #channel
clone_name = str(cmd[2])
clone_channel_to_join = str(cmd[3])
if clone_name.lower() == 'all':
self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 2))
else:
self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 2, clone_name))
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join all #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join clone_nickname #channel')
case 'list':
try:
for clone_name in self.Clone.UID_CLONE_DB:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> {clone_name.nickname} | {clone_name.username}')
pass
except Exception as err:
self.Logs.error(f'{err}')
case 'say':
try:
# clone say clone_nickname #channel message
clone_name = str(cmd[2])
clone_channel = str(cmd[3]) if self.Base.Is_Channel(str(cmd[3])) else None
message = []
for i in range(4, len(cmd)):
message.append(cmd[i])
final_message = ' '.join(message)
if clone_channel is None or not self.Clone.exists(clone_name):
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel message')
return None
if self.Clone.exists(clone_name):
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone_name} :SAY {clone_channel} {final_message}')
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel message')
case _:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect 6')
if len(cmd) == 1:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect 6 2.5')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill [all | nickname]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join [all | nickname] #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel [message]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone list')
option = str(cmd[1]).lower()
match option:
case 'connect':
try:
# clone connect 5
number_of_clones = int(cmd[2])
connection_interval = int(cmd[3]) if len(cmd) == 4 else 0.5
self.Base.create_thread(
self.thread_create_clones_with_interval,
(number_of_clones, [], connection_interval)
)
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect [number of clone you want to connect] [Connection Interval]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} clone connect 6 2.5')
case 'kill':
try:
# clone kill [all | nickname]
clone_name = str(cmd[2])
clone_to_kill: list[str] = []
if clone_name.lower() == 'all':
for clone in self.Clone.UID_CLONE_DB:
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone.nickname} :KILL')
clone_to_kill.append(clone.nickname)
clone.alive = False
for clone_nickname in clone_to_kill:
self.Clone.delete(clone_nickname)
del clone_to_kill
else:
if self.Clone.exists(clone_name):
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone_name} :KILL')
self.Clone.kill(clone_name)
self.Clone.delete(clone_name)
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill all')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill clone_nickname')
case 'join':
try:
# clone join [all | nickname] #channel
clone_name = str(cmd[2])
clone_channel_to_join = str(cmd[3])
if clone_name.lower() == 'all':
self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 2))
else:
self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 2, clone_name))
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join all #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join clone_nickname #channel')
case 'list':
try:
clone_count = len(self.Clone.UID_CLONE_DB)
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> Number of connected clones: {clone_count}')
for clone_name in self.Clone.UID_CLONE_DB:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> Nickname: {clone_name.nickname} | Username: {clone_name.username} | Realname: {clone_name.realname} | Vhost: {clone_name.vhost} | Init: {clone_name.init} | Live: {clone_name.alive} | Connected: {clone_name.connected}')
except Exception as err:
self.Logs.error(f'{err}')
case 'say':
try:
# clone say clone_nickname #channel message
clone_name = str(cmd[2])
clone_channel = str(cmd[3]) if self.Base.Is_Channel(str(cmd[3])) else None
message = []
for i in range(4, len(cmd)):
message.append(cmd[i])
final_message = ' '.join(message)
if clone_channel is None or not self.Clone.exists(clone_name):
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel message')
return None
if self.Clone.exists(clone_name):
self.Irc.send2socket(f':{dnickname} PRIVMSG {clone_name} :SAY {clone_channel} {final_message}')
except Exception as err:
self.Logs.error(f'{err}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel message')
case _:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect 6')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill [all | nickname]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join [all | nickname] #channel')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel [message]')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone list')
except IndexError as ie:
self.Logs.error(f'Index Error: {ie}')
except Exception as err:
self.Logs.error(f'Index Error: {err}')

View File

@@ -44,11 +44,13 @@ class Defender():
nickname: str
username: str
hostname: str
realname: str
umodes: str
vhost: str
ip: str
score: int
isWebirc: bool
isWebsocket: bool
secret_code: str
connected_datetime: str
updated_datetime: str
@@ -109,19 +111,13 @@ class Defender():
self.timeout = self.Config.API_TIMEOUT
# Listes qui vont contenir les ip a scanner avec les différentes API
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
# self.freeipapi_remote_ip:list = []
# self.cloudfilt_remote_ip:list = []
# self.abuseipdb_remote_ip:list = []
# self.psutil_remote_ip:list = []
# self.localscan_remote_ip:list = []
# Variables qui indique que les threads sont en cours d'éxecutions
self.abuseipdb_isRunning:bool = True
self.freeipapi_isRunning:bool = True
@@ -234,11 +230,11 @@ class Defender():
"""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.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
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] = []
self.abuseipdb_isRunning:bool = False
self.freeipapi_isRunning:bool = False
@@ -445,9 +441,9 @@ class Defender():
jailed_nickname = get_reputation.nickname
jailed_score = get_reputation.score
color_red = self.Config.CONFIG_COLOR['rouge']
color_black = self.Config.CONFIG_COLOR['noire']
color_bold = self.Config.CONFIG_COLOR['gras']
color_red = self.Config.COLORS.red
color_black = self.Config.COLORS.black
color_bold = self.Config.COLORS.bold
service_id = self.Config.SERVICE_ID
service_prefix = self.Config.SERVICE_PREFIX
reputation_ban_all_chan = self.ModConfig.reputation_ban_all_chan
@@ -482,8 +478,8 @@ class Defender():
ban_all_chan = self.ModConfig.reputation_ban_all_chan
service_id = self.Config.SERVICE_ID
dchanlog = self.Config.SERVICE_CHANLOG
color_red = self.Config.CONFIG_COLOR['rouge']
color_black = self.Config.CONFIG_COLOR['noire']
color_red = self.Config.COLORS.red
color_black = self.Config.COLORS.black
salon_jail = self.Config.SALON_JAIL
if reputation_flag == 0:
@@ -563,8 +559,8 @@ class Defender():
flood_timer = self.ModConfig.flood_timer
service_id = self.Config.SERVICE_ID
dnickname = self.Config.SERVICE_NICKNAME
color_red = self.Config.CONFIG_COLOR['rouge']
color_bold = self.Config.CONFIG_COLOR['gras']
color_red = self.Config.COLORS.red
color_bold = self.Config.COLORS.bold
get_detected_uid = self.User.get_uid(detected_user)
get_detected_nickname = self.User.get_nickname(detected_user)
@@ -617,13 +613,14 @@ class Defender():
"""local_scan
Args:
remote_ip (str): _description_
userModel (UserModel): _description_
"""
User = userModel
remote_ip = User.remote_ip
username = User.username
hostname = User.hostname
nickname = User.nickname
fullname = f'{nickname}!{username}@{hostname}'
if remote_ip in self.Config.WHITELISTED_IP:
return None
@@ -637,14 +634,13 @@ class Defender():
connection = (remote_ip, self.Base.int_if_possible(port))
newSocket.connect(connection)
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}]")
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.COLORS.red}PROXY_SCAN{self.Config.COLORS.black} ] {fullname} ({remote_ip}) : Port [{str(port)}] ouvert sur l'adresse ip [{remote_ip}]")
# print(f"=======> Le port {str(port)} est ouvert !!")
self.Base.running_sockets.append(newSocket)
# print(newSocket)
newSocket.shutdown(socket.SHUT_RDWR)
newSocket.close()
except (socket.timeout, ConnectionRefusedError):
self.Logs.info(f"Le port {remote_ip}:{str(port)} est fermé")
except AttributeError as ae:
@@ -676,10 +672,10 @@ class Defender():
self.Logs.warning(f"thread_local_scan Error : {ve}")
def get_ports_connexion(self, userModel: User.UserModel) -> list[int]:
"""psutil_scan for Linux
"""psutil_scan for Linux (should be run on the same location as the unrealircd server)
Args:
remote_ip (str): The remote ip address
userModel (UserModel): The User Model Object
Returns:
list[int]: list of ports
@@ -700,6 +696,9 @@ class Defender():
matching_ports = [conn.raddr.port for conn in connections if conn.raddr and conn.raddr.ip == remote_ip]
self.Logs.info(f"Connexion of {fullname} ({remote_ip}) using ports : {str(matching_ports)}")
if matching_ports:
self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.COLORS.red}PSUTIL_SCAN{self.Config.COLORS.black} ] {fullname} ({remote_ip}) : is using ports {matching_ports}")
return matching_ports
except psutil.AccessDenied as ad:
@@ -778,8 +777,8 @@ class Defender():
service_id = self.Config.SERVICE_ID
service_chanlog = self.Config.SERVICE_CHANLOG
color_red = self.Config.CONFIG_COLOR['rouge']
color_black = self.Config.CONFIG_COLOR['noire']
color_red = self.Config.COLORS.red
color_black = self.Config.COLORS.black
# pseudo!ident@host
fullname = f'{nickname}!{username}@{hostname}'
@@ -846,8 +845,8 @@ class Defender():
service_id = self.Config.SERVICE_ID
service_chanlog = self.Config.SERVICE_CHANLOG
color_red = self.Config.CONFIG_COLOR['rouge']
color_black = self.Config.CONFIG_COLOR['noire']
color_red = self.Config.COLORS.red
color_black = self.Config.COLORS.black
url = f'https://freeipapi.com/api/json/{remote_ip}'
@@ -934,8 +933,8 @@ class Defender():
service_id = self.Config.SERVICE_ID
service_chanlog = self.Config.SERVICE_CHANLOG
color_red = self.Config.CONFIG_COLOR['rouge']
color_black = self.Config.CONFIG_COLOR['noire']
color_red = self.Config.COLORS.red
color_black = self.Config.COLORS.black
url = f"https://developers18334.cloudfilt.com/"
@@ -1067,9 +1066,9 @@ class Defender():
currentDateTime = self.Base.get_datetime()
self.reputation_insert(
self.ReputationModel(
uid=_User.uid, nickname=_User.nickname, username=_User.username, hostname=_User.hostname,
umodes=_User.umodes, vhost=_User.vhost, ip=_User.remote_ip, score=_User.score_connexion,
secret_code=self.Base.get_random(8), isWebirc=_User.isWebirc, connected_datetime=currentDateTime,
uid=_User.uid, nickname=_User.nickname, username=_User.username, realname=_User.realname,
hostname=_User.hostname, umodes=_User.umodes, vhost=_User.vhost, ip=_User.remote_ip, score=_User.score_connexion,
secret_code=self.Base.get_random(8), isWebirc=_User.isWebirc, isWebsocket=_User.isWebsocket, connected_datetime=currentDateTime,
updated_datetime=currentDateTime
)
)
@@ -1092,6 +1091,9 @@ class Defender():
get_reputation = self.reputation_get_Reputation(parsed_UID)
self.Irc.send2socket(f":{service_id} MODE {parsed_chan} +b ~security-group:unknown-users")
self.Irc.send2socket(f":{service_id} MODE {parsed_chan} +eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users")
if not get_reputation is None:
isWebirc = get_reputation.isWebirc
@@ -1115,20 +1117,20 @@ class Defender():
if not self.Base.is_valid_ip(cmd[7]):
return None
if self.ModConfig.local_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
self.localscan_remote_ip.append(cmd[7])
# if self.ModConfig.local_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
# self.localscan_remote_ip.append(cmd[7])
if self.ModConfig.psutil_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
self.psutil_remote_ip.append(cmd[7])
# if self.ModConfig.psutil_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
# self.psutil_remote_ip.append(cmd[7])
if self.ModConfig.abuseipdb_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
self.abuseipdb_remote_ip.append(cmd[7])
# if self.ModConfig.abuseipdb_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
# self.abuseipdb_remote_ip.append(cmd[7])
if self.ModConfig.freeipapi_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
self.freeipapi_remote_ip.append(cmd[7])
# if self.ModConfig.freeipapi_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
# self.freeipapi_remote_ip.append(cmd[7])
if self.ModConfig.cloudfilt_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
self.cloudfilt_remote_ip.append(cmd[7])
# if self.ModConfig.cloudfilt_scan == 1 and not cmd[7] in self.Config.WHITELISTED_IP:
# self.cloudfilt_remote_ip.append(cmd[7])
case 'NICK':
# :0010BS24L NICK [NEWNICK] 1697917711
@@ -1231,8 +1233,8 @@ class Defender():
self.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']
color_green = self.Config.COLORS.green
color_black = self.Config.COLORS.black
if release_code == get_reputation.secret_code:
self.Irc.send2socket(f':{dnickname} PRIVMSG {jailed_salon} : Bon mot de passe. Allez du vent !')
@@ -1276,13 +1278,13 @@ class Defender():
if activation == 'on':
if self.ModConfig.reputation == 1:
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}REPUTATION{self.Config.CONFIG_COLOR['noire']} ] : Already activated")
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}REPUTATION{self.Config.COLORS.black} ] : Already activated")
return False
# self.update_db_configuration('reputation', 1)
self.__update_configuration(key, 1)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}REPUTATION{self.Config.CONFIG_COLOR['noire']} ] : Activated by {fromuser}")
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}REPUTATION{self.Config.COLORS.black} ] : Activated by {fromuser}")
self.Irc.send2socket(f":{service_id} JOIN {jail_chan}")
self.Irc.send2socket(f":{service_id} SAMODE {jail_chan} +{dumodes} {dnickname}")
self.Irc.send2socket(f":{service_id} MODE {jail_chan} +{jail_chan_mode}")
@@ -1291,21 +1293,19 @@ class Defender():
for chan in self.Channel.UID_CHANNEL_DB:
if chan.name != jail_chan:
self.Irc.send2socket(f":{service_id} MODE {chan.name} +b ~security-group:unknown-users")
self.Irc.send2socket(f":{service_id} MODE {chan.name} +e ~security-group:webirc-users")
self.Irc.send2socket(f":{service_id} MODE {chan.name} +e ~security-group:known-users")
self.Irc.send2socket(f":{service_id} MODE {chan.name} +e ~security-group:websocket-users")
self.Irc.send2socket(f":{service_id} MODE {chan.name} +eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users")
self.Base.db_query_channel('add', self.module_name, jail_chan)
if activation == 'off':
if self.ModConfig.reputation == 0:
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}REPUTATION{self.Config.CONFIG_COLOR['noire']} ] : Already deactivated")
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}REPUTATION{self.Config.COLORS.black} ] : Already deactivated")
return False
self.__update_configuration(key, 0)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}REPUTATION{self.Config.CONFIG_COLOR['noire']} ] : Deactivated by {fromuser}")
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.red}REPUTATION{self.Config.COLORS.black} ] : Deactivated by {fromuser}")
self.Irc.send2socket(f":{service_id} SAMODE {jail_chan} -{dumodes} {dnickname}")
self.Irc.send2socket(f":{service_id} MODE {jail_chan} -sS")
self.Irc.send2socket(f":{service_id} PART {jail_chan}")
@@ -1313,9 +1313,7 @@ class Defender():
for chan in self.Channel.UID_CHANNEL_DB:
if chan.name != jail_chan:
self.Irc.send2socket(f":{service_id} MODE {chan.name} -b ~security-group:unknown-users")
self.Irc.send2socket(f":{service_id} MODE {chan.name} -e ~security-group:webirc-users")
self.Irc.send2socket(f":{service_id} MODE {chan.name} -e ~security-group:known-users")
self.Irc.send2socket(f":{service_id} MODE {chan.name} -e ~security-group:websocket-users")
self.Irc.send2socket(f":{service_id} MODE {chan.name} -eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users")
self.Base.db_query_channel('del', self.module_name, jail_chan)
@@ -1335,23 +1333,23 @@ class Defender():
if get_value == 'on':
if self.ModConfig.reputation_ban_all_chan == 1:
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}BAN ON ALL CHANS{self.Config.CONFIG_COLOR['noire']} ] : Already activated")
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.red}BAN ON ALL CHANS{self.Config.COLORS.black} ] : Already activated")
return False
# self.update_db_configuration(key, 1)
self.__update_configuration(key, 1)
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}BAN ON ALL CHANS{self.Config.CONFIG_COLOR["noire"]} ] : Activated by {fromuser}')
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}BAN ON ALL CHANS{self.Config.COLORS.black} ] : Activated by {fromuser}')
elif get_value == 'off':
if self.ModConfig.reputation_ban_all_chan == 0:
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}BAN ON ALL CHANS{self.Config.CONFIG_COLOR['noire']} ] : Already deactivated")
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.red}BAN ON ALL CHANS{self.Config.COLORS.black} ] : Already deactivated")
return False
# self.update_db_configuration(key, 0)
self.__update_configuration(key, 0)
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}BAN ON ALL CHANS{self.Config.CONFIG_COLOR["noire"]} ] : Deactivated by {fromuser}')
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}BAN ON ALL CHANS{self.Config.COLORS.black} ] : Deactivated by {fromuser}')
case 'limit':
reputation_seuil = int(cmd[3])
@@ -1360,7 +1358,7 @@ class Defender():
# self.update_db_configuration(key, reputation_seuil)
self.__update_configuration(key, reputation_seuil)
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}REPUTATION SEUIL{self.Config.CONFIG_COLOR["noire"]} ] : Limit set to {str(reputation_seuil)} by {fromuser}')
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}REPUTATION SEUIL{self.Config.COLORS.black} ] : Limit set to {str(reputation_seuil)} by {fromuser}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Reputation set to {reputation_seuil}')
case 'timer':
@@ -1368,7 +1366,7 @@ class Defender():
key = 'reputation_timer'
self.__update_configuration(key, reputation_timer)
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}REPUTATION TIMER{self.Config.CONFIG_COLOR["noire"]} ] : Timer set to {str(reputation_timer)} minute(s) by {fromuser}')
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}REPUTATION TIMER{self.Config.COLORS.black} ] : Timer set to {str(reputation_timer)} minute(s) by {fromuser}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Reputation set to {reputation_timer}')
case 'score_after_release':
@@ -1376,7 +1374,7 @@ class Defender():
key = 'reputation_score_after_release'
self.__update_configuration(key, reputation_score_after_release)
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}REPUTATION SCORE AFTER RELEASE{self.Config.CONFIG_COLOR["noire"]} ] : Reputation score after release set to {str(reputation_score_after_release)} by {fromuser}')
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}REPUTATION SCORE AFTER RELEASE{self.Config.COLORS.black} ] : Reputation score after release set to {str(reputation_score_after_release)} by {fromuser}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Reputation score after release set to {reputation_score_after_release}')
case 'security_group':
@@ -1384,7 +1382,7 @@ class Defender():
key = 'reputation_sg'
self.__update_configuration(key, reputation_sg)
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR["verte"]}REPUTATION SECURITY-GROUP{self.Config.CONFIG_COLOR["noire"]} ] : Reputation Security-group set to {str(reputation_sg)} by {fromuser}')
self.Irc.send2socket(f':{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}REPUTATION SECURITY-GROUP{self.Config.COLORS.black} ] : Reputation Security-group set to {str(reputation_sg)} by {fromuser}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Reputation score after release set to {reputation_sg}')
case _:
@@ -1414,9 +1412,9 @@ class Defender():
# .proxy_scan set psutil_scan on/off --> Active les informations de connexion a la machine locale
# .proxy_scan set abuseipdb_scan on/off --> Active le scan via l'api abuseipdb
len_cmd = len(cmd)
color_green = self.Config.CONFIG_COLOR['verte']
color_red = self.Config.CONFIG_COLOR['rouge']
color_black = self.Config.CONFIG_COLOR['noire']
color_green = self.Config.COLORS.green
color_red = self.Config.COLORS.red
color_black = self.Config.COLORS.black
if len_cmd == 4:
set_key = str(cmd[1]).lower()
@@ -1548,21 +1546,21 @@ class Defender():
key = 'flood'
if activation == 'on':
if self.ModConfig.flood == 1:
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Already activated")
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Already activated")
return False
self.__update_configuration(key, 1)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Activated by {fromuser}")
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Activated by {fromuser}")
if activation == 'off':
if self.ModConfig.flood == 0:
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Already Deactivated")
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.red}FLOOD{self.Config.COLORS.black} ] : Already Deactivated")
return False
self.__update_configuration(key, 0)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Deactivated by {fromuser}")
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Deactivated by {fromuser}")
if len_cmd == 4:
set_key = str(cmd[2]).lower()
@@ -1574,21 +1572,21 @@ class Defender():
set_value = int(cmd[3])
self.__update_configuration(key, set_value)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Flood message set to {set_value} by {fromuser}")
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Flood message set to {set_value} by {fromuser}")
case 'flood_time':
key = 'flood_time'
set_value = int(cmd[3])
self.__update_configuration(key, set_value)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Flood time set to {set_value} by {fromuser}")
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Flood time set to {set_value} by {fromuser}")
case 'flood_timer':
key = 'flood_timer'
set_value = int(cmd[3])
self.__update_configuration(key, set_value)
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}FLOOD{self.Config.CONFIG_COLOR['noire']} ] : Flood timer set to {set_value} by {fromuser}")
self.Irc.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.COLORS.green}FLOOD{self.Config.COLORS.black} ] : Flood timer set to {set_value} by {fromuser}")
case _:
pass
@@ -1597,9 +1595,9 @@ class Defender():
self.Logs.error(f"{self.__class__.__name__} Value Error : {ve}")
case 'status':
color_green = self.Config.CONFIG_COLOR['verte']
color_red = self.Config.CONFIG_COLOR['rouge']
color_black = self.Config.CONFIG_COLOR['noire']
color_green = self.Config.COLORS.green
color_red = self.Config.COLORS.red
color_black = self.Config.COLORS.black
try:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : [{color_green if self.ModConfig.reputation == 1 else color_red}Reputation{color_black}] ==> {self.ModConfig.reputation}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : reputation_seuil ==> {self.ModConfig.reputation_seuil}')
@@ -1626,15 +1624,25 @@ class Defender():
UserObject = self.User.get_User(nickoruid)
if not UserObject is None:
channels: list = []
for chan in self.Channel.UID_CHANNEL_DB:
for uid_in_chan in chan.uids:
if self.Base.clean_uid(uid_in_chan) == UserObject.uid:
channels.append(chan.name)
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : UID : {UserObject.uid}')
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} : REALNAME : {UserObject.realname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : HOSTNAME : {UserObject.hostname}')
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} : Country : {UserObject.geoip}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : WebIrc : {UserObject.isWebirc}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : WebWebsocket : {UserObject.isWebsocket}')
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} : CHANNELS : {channels}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : CONNECTION TIME : {UserObject.connexion_datetime}')
else:
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} : This user {nickoruid} doesn't exist")

255
mods/mod_jsonrpc.py Normal file
View File

@@ -0,0 +1,255 @@
from dataclasses import dataclass
from core.irc import Irc
from unrealircd_rpc_py.Live import Live
from unrealircd_rpc_py.Loader import Loader
class Jsonrpc():
@dataclass
class ModConfModel:
"""The Model containing the module parameters
"""
param_exemple1: str
param_exemple2: int
def __init__(self, ircInstance:Irc) -> None:
# Module name (Mandatory)
self.module_name = 'mod_' + str(self.__class__.__name__).lower()
# Add Irc Object to the module (Mandatory)
self.Irc = ircInstance
# Add Global Configuration to the module (Mandatory)
self.Config = ircInstance.Config
# Add Base object to the module (Mandatory)
self.Base = ircInstance.Base
# Add logs object to the module (Mandatory)
self.Logs = ircInstance.Base.logs
# Add User object to the module (Mandatory)
self.User = ircInstance.User
# Add Channel object to the module (Mandatory)
self.Channel = ircInstance.Channel
# Create module commands (Mandatory)
self.commands_level = {
1: ['jsonrpc', 'jruser']
}
# Init the module
self.__init_module()
# Log the module
self.Logs.debug(f'Module {self.module_name} loaded ...')
def __init_module(self) -> None:
# Insert module commands into the core one (Mandatory)
self.__set_commands(self.commands_level)
# Create you own tables (Mandatory)
# self.__create_tables()
# Load module configuration and sync with core one (Mandatory)
self.__load_module_configuration()
# End of mandatory methods you can start your customization #
self.UnrealIrcdRpcLive: Live = None
self.Rpc: Loader = Loader(
req_method=self.Config.JSONRPC_METHOD,
url=self.Config.JSONRPC_URL,
username=self.Config.JSONRPC_USER,
password=self.Config.JSONRPC_PASSWORD
)
if self.Rpc.Error.code != 0:
self.Irc.sendPrivMsg(f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.Rpc.Error.message}", self.Config.SERVICE_CHANLOG)
return None
def __set_commands(self, commands:dict[int, list[str]]) -> None:
"""### Rajoute les commandes du module au programme principal
Args:
commands (list): Liste des commandes du module
"""
for level, com in commands.items():
for c in commands[level]:
if not c in self.Irc.commands:
self.Irc.commands_level[level].append(c)
self.Irc.commands.append(c)
return None
def __create_tables(self) -> None:
"""Methode qui va créer la base de donnée si elle n'existe pas.
Une Session unique pour cette classe sera crée, qui sera utilisé dans cette classe / module
Args:
database_name (str): Nom de la base de données ( pas d'espace dans le nom )
Returns:
None: Aucun retour n'es attendu
"""
table_logs = '''CREATE TABLE IF NOT EXISTS test_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
datetime TEXT,
server_msg TEXT
)
'''
self.Base.db_execute_query(table_logs)
return None
def callback_sent_to_irc(self, json_response: str):
dnickname = self.Config.SERVICE_NICKNAME
dchanlog = self.Config.SERVICE_CHANLOG
self.Irc.sendPrivMsg(msg=json_response, channel=dchanlog)
def thread_start_jsonrpc(self):
self.UnrealIrcdRpcLive = Live(path_to_socket_file=self.Config.JSONRPC_PATH_TO_SOCKET_FILE,
callback_object_instance=self,
callback_method_name='callback_sent_to_irc'
)
if self.UnrealIrcdRpcLive.Error.code == 0:
self.UnrealIrcdRpcLive.subscribe()
else:
self.Irc.sendPrivMsg(f"[{self.Config.COLORS.red}ERROR{self.Config.COLORS.nogc}] {self.UnrealIrcdRpcLive.Error.message}", self.Config.SERVICE_CHANLOG)
def __load_module_configuration(self) -> None:
"""### Load Module Configuration
"""
try:
# Build the default configuration model (Mandatory)
self.ModConfig = self.ModConfModel(param_exemple1='param value 1', param_exemple2=1)
# Sync the configuration with core configuration (Mandatory)
#self.Base.db_sync_core_config(self.module_name, self.ModConfig)
return None
except TypeError as te:
self.Logs.critical(te)
def __update_configuration(self, param_key: str, param_value: str):
"""Update the local and core configuration
Args:
param_key (str): The parameter key
param_value (str): The parameter value
"""
self.Base.db_update_core_config(self.module_name, self.ModConfig, param_key, param_value)
def unload(self) -> None:
self.UnrealIrcdRpcLive.unsubscribe()
return None
def cmd(self, data:list) -> None:
return None
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower()
dnickname = self.Config.SERVICE_NICKNAME
fromuser = user
fromchannel = str(channel) if not channel is None else None
match command:
case 'jsonrpc':
try:
option = str(cmd[1]).lower()
if len(command) == 1:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} jsonrpc on')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} jsonrpc off')
match option:
case 'on':
self.Base.create_thread(self.thread_start_jsonrpc, run_once=True)
case 'off':
self.UnrealIrcdRpcLive.unsubscribe()
except IndexError as ie:
self.Logs.error(ie)
case 'jruser':
try:
option = str(cmd[1]).lower()
if len(command) == 1:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} jruser get nickname')
match option:
case 'get':
nickname = str(cmd[2])
uid_to_get = self.User.get_uid(nickname)
if uid_to_get is None:
return None
rpc = Loader(
req_method=self.Config.JSONRPC_METHOD,
url=self.Config.JSONRPC_URL,
username=self.Config.JSONRPC_USER,
password=self.Config.JSONRPC_PASSWORD
)
UserInfo = rpc.User.get(uid_to_get)
if rpc.Error.code != 0:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{rpc.Error.message}')
return None
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :UID : {UserInfo.id}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :NICKNAME : {UserInfo.name}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :USERNAME : {UserInfo.username}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :REALNAME : {UserInfo.realname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CHANNELS : {UserInfo.channels}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :SECURITY GROUP : {UserInfo.security_groups}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :REPUTATION : {UserInfo.reputation}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :IP : {UserInfo.ip}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :COUNTRY CODE : {UserInfo.country_code}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :ASN : {UserInfo.asn}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :ASNAME : {UserInfo.asname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CLOAKED HOST : {UserInfo.cloakedhost}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :HOSTNAME : {UserInfo.hostname}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :VHOST : {UserInfo.vhost}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CLIENT PORT : {UserInfo.client_port}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :SERVER PORT : {UserInfo.server_port}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :IDLE SINCE : {UserInfo.idle_since}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :CONNECTED SINCE : {UserInfo.connected_since}')
except IndexError as ie:
self.Logs.error(ie)
case 'ia':
try:
self.Base.create_thread(self.thread_ask_ia, ('',))
self.Irc.send2socket(f":{dnickname} NOTICE {fromuser} : This is a notice to the sender ...")
self.Irc.send2socket(f":{dnickname} PRIVMSG {fromuser} : This is private message to the sender ...")
if not fromchannel is None:
self.Irc.send2socket(f":{dnickname} PRIVMSG {fromchannel} : This is channel message to the sender ...")
# How to update your module configuration
self.__update_configuration('param_exemple2', 7)
# Log if you want the result
self.Logs.debug(f"Test logs ready")
except Exception as err:
self.Logs.error(f"Unknown Error: {err}")

View File

@@ -229,11 +229,11 @@ class Votekick():
if chan.channel_name == channel:
target_user = self.User.get_nickname(chan.target_user)
if chan.vote_for > chan.vote_against:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.CONFIG_COLOR["gras"]}{target_user}{self.Config.CONFIG_COLOR["nogc"]} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll be kicked from the channel')
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll be kicked from the channel')
self.Irc.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}")
self.Channel.delete_user_from_channel(channel, self.User.get_uid(target_user))
elif chan.vote_for <= chan.vote_against:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.CONFIG_COLOR["gras"]}{target_user}{self.Config.CONFIG_COLOR["nogc"]} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll remain in the channel')
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll remain in the channel')
# Init the system
if self.init_vote_system(channel):
@@ -471,10 +471,10 @@ class Votekick():
if chan.channel_name == channel:
target_user = self.User.get_nickname(chan.target_user)
if chan.vote_for > chan.vote_against:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.CONFIG_COLOR["gras"]}{target_user}{self.Config.CONFIG_COLOR["nogc"]} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll be kicked from the channel')
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll be kicked from the channel')
self.Irc.send2socket(f":{dnickname} KICK {channel} {target_user} Following the vote, you are not welcome in {channel}")
elif chan.vote_for <= chan.vote_against:
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.CONFIG_COLOR["gras"]}{target_user}{self.Config.CONFIG_COLOR["nogc"]} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll remain in the channel')
self.Irc.send2socket(f':{dnickname} PRIVMSG {channel} :User {self.Config.COLORS.bold}{target_user}{self.Config.COLORS.nogc} has {chan.vote_against} votes against and {chan.vote_for} votes for. For this reason, it\'ll remain in the channel')
# Init the system
if self.init_vote_system(channel):

View File

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