18 Commits

Author SHA1 Message Date
adator
71053437a7 Merge pull request #33 from adator85/dev
V5.1.6
2024-09-01 22:15:54 +02:00
adator
7796d05206 Merge pull request #32 from adator85/dev
Update vote kick commands
2024-09-01 18:55:57 +02:00
adator
5f2567f9e5 Merge pull request #31 from adator85/dev
mod_command update
2024-09-01 17:30:05 +02:00
adator
aaa1dd9a1a Merge pull request #30 from adator85/dev
adding Say command for clones
2024-09-01 16:40:25 +02:00
adator
a02f2f9a26 Merge pull request #29 from adator85/dev
update mod_clone module
2024-09-01 15:54:50 +02:00
adator
d73adb6f0b Merge pull request #28 from adator85/dev
update readme
2024-09-01 15:35:59 +02:00
adator
b812e64992 Merge pull request #27 from adator85/dev
Dev
2024-09-01 15:24:18 +02:00
adator
9bd1f68df2 Merge pull request #26 from adator85/dev
Dev
2024-09-01 14:59:38 +02:00
adator
f44b08bf36 Merge pull request #25 from adator85/dev
fix Installation
2024-08-29 01:36:38 +02:00
adator
1a19e1613a Merge pull request #24 from adator85/dev
Fix Bug installation
2024-08-29 01:31:19 +02:00
adator
cdc15b7b47 Merge pull request #23 from adator85/dev
Dev
2024-08-29 01:16:55 +02:00
adator
31fe9f62ec Merge pull request #22 from adator85/dev
Dev
2024-08-24 01:39:11 +02:00
adator
f0853e3afb Merge pull request #21 from adator85/dev
New Installation file created for unix system
2024-08-22 01:02:00 +02:00
adator
6dade09257 Merge pull request #20 from adator85/dev
README Update
2024-08-21 00:50:31 +02:00
adator
9533b010b2 Merge pull request #19 from adator85/dev
V5.0.4 - Delete a user when a user has been kicked
2024-08-20 02:24:37 +02:00
adator
824db73590 Merge pull request #18 from adator85/dev
Delete channel mode information
2024-08-20 02:14:31 +02:00
adator
96bf4b6f80 Merge pull request #17 from adator85/dev
Fix channel update
2024-08-20 02:08:09 +02:00
adator
922336363e Merge pull request #16 from adator85/dev
Dev
2024-08-20 01:56:04 +02:00
11 changed files with 92 additions and 369 deletions

1
.gitignore vendored
View File

@@ -5,4 +5,3 @@ __pycache__/
configuration.json configuration.json
install.log install.log
test.py test.py
# mods/mod_ia.py

View File

@@ -1,9 +1,4 @@
# IRC-DEFENDER # IRC-DEFENDER
![Static Badge](https://img.shields.io/badge/UnrealIRCd-6.2.2%20or%20later-green)
![Static Badge](https://img.shields.io/badge/Python3-3.10%20or%20later-green)
![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fadator85%2FIRC_DEFENDER_MODULES%2Fmain%2Fversion.json&query=version&label=Current%20Version)
![Static Badge](https://img.shields.io/badge/Maintained-Yes-green)
Defender est un service IRC basé sur la sécurité des réseaux IRC ( UnrealIRCD ) Defender est un service IRC basé sur la sécurité des réseaux IRC ( UnrealIRCD )
Il permet d'ajouter une sécurité supplémentaire pour vérifier les users connectés au réseau Il permet d'ajouter une sécurité supplémentaire pour vérifier les users connectés au réseau
en demandant aux user un code de validation. en demandant aux user un code de validation.
@@ -14,9 +9,9 @@ Il permet aux opérateurs de gérer efficacement un canal, tout en offrant aux u
Kick: Expulser un utilisateur du canal. Kick: Expulser un utilisateur du canal.
Ban: Interdire définitivement l'accès au canal. Ban: Interdire définitivement l'accès au canal.
Unban: Lever une interdiction. Unban: Lever une interdiction.
Op/Deop/Opall/Deopall: Attribuer ou retirer les droits d'opérateur. Op/Deop: Attribuer ou retirer les droits d'opérateur.
Halfop/Dehalfop: Attribuer ou retirer les droits Halfop/Dehalfop: Attribuer ou retirer les droits
Voice/Devoice/VoiceAll/DevoiceAll: Attribuer ou retirer les droits de voix. Voice/Devoice: Attribuer ou retirer les droits de voix.
Système de quarantaine: Système de quarantaine:
Mise en quarantaine: Isoler temporairement un utilisateur dans un canal privé. Mise en quarantaine: Isoler temporairement un utilisateur dans un canal privé.
@@ -30,7 +25,6 @@ Il permet aux opérateurs de gérer efficacement un canal, tout en offrant aux u
Prérequis: Prérequis:
- Système d'exploitation Linux (Windows non supporté) - Système d'exploitation Linux (Windows non supporté)
- Un server UnrealIRCD corréctement configuré
- Python version 3.10 ou supérieure - Python version 3.10 ou supérieure
Bash: Bash:

View File

@@ -1,7 +1,7 @@
import time, threading, os, random, socket, hashlib, ipaddress, logging, requests, json, re, ast import time, threading, os, random, socket, hashlib, ipaddress, logging, requests, json, re, ast
from dataclasses import fields from dataclasses import fields
from typing import Union, Literal from typing import Union, Literal
from base64 import b64decode, b64encode from base64 import b64decode
from datetime import datetime from datetime import datetime
from sqlalchemy import create_engine, Engine, Connection, CursorResult from sqlalchemy import create_engine, Engine, Connection, CursorResult
from sqlalchemy.sql import text from sqlalchemy.sql import text
@@ -307,7 +307,7 @@ class Base:
def db_update_core_config(self, module_name:str, dataclassObj: object, param_key:str, param_value: str) -> bool: def db_update_core_config(self, module_name:str, dataclassObj: object, param_key:str, param_value: str) -> bool:
core_table = self.Config.table_config core_table = 'core_config'
# Check if the param exist # Check if the param exist
if not hasattr(dataclassObj, param_key): if not hasattr(dataclassObj, param_key):
self.logs.error(f"Le parametre {param_key} n'existe pas dans la variable global") self.logs.error(f"Le parametre {param_key} n'existe pas dans la variable global")
@@ -330,10 +330,6 @@ class Base:
if updated_rows > 0: if updated_rows > 0:
setattr(dataclassObj, param_key, self.int_if_possible(param_value)) setattr(dataclassObj, param_key, self.int_if_possible(param_value))
self.logs.debug(f'Parameter updated : {param_key} - {param_value} | Module: {module_name}') self.logs.debug(f'Parameter updated : {param_key} - {param_value} | Module: {module_name}')
else:
self.logs.error(f'Parameter NOT updated : {param_key} - {param_value} | Module: {module_name}')
else:
self.logs.error(f'Parameter and Module do not exist: Param ({param_key}) - Value ({param_value}) | Module ({module_name})')
self.logs.debug(dataclassObj) self.logs.debug(dataclassObj)
@@ -682,17 +678,6 @@ class Base:
self.logs.critical(f'This remote ip is not valid : {ve}') self.logs.critical(f'This remote ip is not valid : {ve}')
return None return None
# def encode_ip(self, remote_ip_address: str) -> Union[str, None]:
# binary_ip = b64encode()
# try:
# decoded_ip = ipaddress.ip_address(binary_ip)
# return decoded_ip.exploded
# except ValueError as ve:
# self.logs.critical(f'This remote ip is not valid : {ve}')
# return None
def get_random(self, lenght:int) -> str: def get_random(self, lenght:int) -> str:
""" """
Retourn une chaîne aléatoire en fonction de la longueur spécifiée. Retourn une chaîne aléatoire en fonction de la longueur spécifiée.

View File

@@ -1,4 +1,4 @@
import socket, ssl import socket, ssl, time
from ssl import SSLSocket from ssl import SSLSocket
from core.loadConf import Config from core.loadConf import Config
from core.Model import Clones from core.Model import Clones
@@ -186,17 +186,15 @@ class Connection:
nickname = fullname.split('!')[0].replace(':','') nickname = fullname.split('!')[0].replace(':','')
if nickname == self.Config.SERVICE_NICKNAME: if nickname == self.Config.SERVICE_NICKNAME:
command = str(response[3]).replace(':','') command = str(response[3]).replace(':','')
if command == 'KILL': if command == 'KILL':
self.send2socket(f'QUIT :Thanks and goodbye') self.send2socket(f'QUIT :Thanks and goodbye')
self.signal = self.currentCloneObject.alive self.signal = self.currentCloneObject.alive
if command == 'JOIN': if command == 'JOIN':
channel_to_join = str(response[4]) channel_to_join = str(response[4])
self.send2socket(f"JOIN {channel_to_join}") self.send2socket(f"JOIN {channel_to_join}")
if command == 'SAY': if command == 'SAY':
clone_channel = str(response[4]) clone_channel = str(response[4])
message = [] message = []
for i in range(5, len(response)): for i in range(5, len(response)):
message.append(response[i]) message.append(response[i])
@@ -204,6 +202,7 @@ class Connection:
self.send2socket(f"PRIVMSG {clone_channel} :{final_message}") self.send2socket(f"PRIVMSG {clone_channel} :{final_message}")
except UnicodeEncodeError: except UnicodeEncodeError:
for data in cmd: for data in cmd:
response = data.decode(self.CHARSET[1],'replace').split() response = data.decode(self.CHARSET[1],'replace').split()

View File

@@ -1,4 +1,4 @@
import ssl, re, importlib, sys, time, threading, socket, traceback import ssl, re, importlib, sys, time, threading, socket
from ssl import SSLSocket from ssl import SSLSocket
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Union, Literal from typing import Union, Literal
@@ -110,8 +110,8 @@ class Irc:
self.ircObject = ircInstance # créer une copie de l'instance Irc self.ircObject = ircInstance # créer une copie de l'instance Irc
self.__link(self.IrcSocket) # établir la connexion au serveur IRC self.__link(self.IrcSocket) # établir la connexion au serveur IRC
self.signal = True # Une variable pour initier la boucle infinie self.signal = True # Une variable pour initier la boucle infinie
self.__join_saved_channels() # Join existing channels
self.load_existing_modules() # Charger les modules existant dans la base de données self.load_existing_modules() # Charger les modules existant dans la base de données
self.__join_saved_channels() # Join existing channels
while self.signal: while self.signal:
try: try:
@@ -131,7 +131,6 @@ class Irc:
self.__create_socket() self.__create_socket()
self.__link(self.IrcSocket) self.__link(self.IrcSocket)
self.__join_saved_channels()
self.load_existing_modules() self.load_existing_modules()
self.RESTART = 0 self.RESTART = 0
@@ -176,7 +175,6 @@ class Irc:
self.Base.logs.critical(f"AttributeError: {atte}") self.Base.logs.critical(f"AttributeError: {atte}")
except Exception as e: except Exception as e:
self.Base.logs.critical(f"Exception: {e}") self.Base.logs.critical(f"Exception: {e}")
self.Base.logs.critical(traceback.print_exc())
def __link(self, writer:Union[socket.socket, SSLSocket]) -> None: def __link(self, writer:Union[socket.socket, SSLSocket]) -> None:
"""Créer le link et envoyer les informations nécessaires pour la """Créer le link et envoyer les informations nécessaires pour la
@@ -214,8 +212,7 @@ class Irc:
writer.send(f":{sid} PROTOCTL SID={sid}\r\n".encode(charset)) writer.send(f":{sid} PROTOCTL SID={sid}\r\n".encode(charset))
writer.send(f":{sid} SERVER {link} 1 :{info}\r\n".encode(charset)) writer.send(f":{sid} SERVER {link} 1 :{info}\r\n".encode(charset))
writer.send(f":{sid} {nickname} :Reserved for services\r\n".encode(charset)) writer.send(f":{sid} {nickname} :Reserved for services\r\n".encode(charset))
#writer.send(f":{sid} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * * :{realname}\r\n".encode(charset)) writer.send(f":{sid} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * * :{realname}\r\n".encode(charset))
writer.send(f":{sid} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * fwAAAQ== :{realname}\r\n".encode(charset))
writer.send(f":{sid} SJOIN {unixtime} {chan} + :{service_id}\r\n".encode(charset)) writer.send(f":{sid} SJOIN {unixtime} {chan} + :{service_id}\r\n".encode(charset))
writer.send(f":{sid} TKL + Q * {nickname} {host} 0 {unixtime} :Reserved for services\r\n".encode(charset)) writer.send(f":{sid} TKL + Q * {nickname} {host} 0 {unixtime} :Reserved for services\r\n".encode(charset))
@@ -275,20 +272,14 @@ class Irc:
response = data.decode(self.CHARSET[0]).split() response = data.decode(self.CHARSET[0]).split()
self.cmd(response) self.cmd(response)
except UnicodeEncodeError as ue: except UnicodeEncodeError:
for data in responses: for data in responses:
response = data.decode(self.CHARSET[1],'replace').split() response = data.decode(self.CHARSET[1],'replace').split()
self.cmd(response) self.cmd(response)
self.Base.logs.error(f'UnicodeEncodeError: {ue}') except UnicodeDecodeError:
self.Base.logs.error(response)
except UnicodeDecodeError as ud:
for data in responses: for data in responses:
response = data.decode(self.CHARSET[1],'replace').split() response = data.decode(self.CHARSET[1],'replace').split()
self.cmd(response) self.cmd(response)
self.Base.logs.error(f'UnicodeDecodeError: {ud}')
self.Base.logs.error(response)
except AssertionError as ae: except AssertionError as ae:
self.Base.logs.error(f"Assertion error : {ae}") self.Base.logs.error(f"Assertion error : {ae}")
@@ -583,6 +574,7 @@ class Irc:
return None return None
def thread_check_for_new_version(self, fromuser: str) -> None: def thread_check_for_new_version(self, fromuser: str) -> None:
dnickname = self.Config.SERVICE_NICKNAME dnickname = self.Config.SERVICE_NICKNAME
if self.Base.check_for_new_version(True): if self.Base.check_for_new_version(True):
@@ -593,39 +585,35 @@ class Irc:
return None return None
def cmd(self, data: list[str]) -> None: def cmd(self, data:list) -> None:
"""Parse server response
Args:
data (list[str]): Server response splitted in a list
"""
try: try:
original_response: list[str] = data.copy()
interm_response: list[str] = data.copy() cmd_to_send:list[str] = data.copy()
"""This the original without first value""" cmd = data.copy()
interm_response.pop(0) cmd_to_debug = data.copy()
cmd_to_debug.pop(0)
if len(original_response) == 0 or len(original_response) == 1: if len(cmd) == 0 or len(cmd) == 1:
self.Base.logs.warning(f'Size ({str(len(original_response))}) - {original_response}') self.Base.logs.warning(f'Size ({str(len(cmd))}) - {cmd}')
return False return False
if len(original_response) == 7: # self.debug(cmd_to_debug)
if original_response[2] == 'PRIVMSG' and original_response[4] == ':auth': if len(data) == 7:
data_copy = original_response.copy() if data[2] == 'PRIVMSG' and data[4] == ':auth':
data_copy = data.copy()
data_copy[6] = '**********' data_copy[6] = '**********'
self.Base.logs.debug(data_copy) self.Base.logs.debug(data_copy)
else: else:
self.Base.logs.debug(original_response) self.Base.logs.debug(data)
else: else:
self.Base.logs.debug(original_response) self.Base.logs.debug(data)
match original_response[0]: match cmd[0]:
case 'PING': case 'PING':
# Sending PONG response to the serveur # Sending PONG response to the serveur
pong = str(original_response[1]).replace(':','') pong = str(cmd[1]).replace(':','')
self.send2socket(f"PONG :{pong}") self.send2socket(f"PONG :{pong}")
return None return None
@@ -634,19 +622,19 @@ class Irc:
# 'PREFIX=(qaohv)~&@%+', 'SID=001', 'MLOCK', 'TS=1703793941', 'EXTSWHOIS'] # 'PREFIX=(qaohv)~&@%+', 'SID=001', 'MLOCK', 'TS=1703793941', 'EXTSWHOIS']
# GET SERVER ID HOST # GET SERVER ID HOST
if len(original_response) > 5: if len(cmd) > 5:
if '=' in original_response[5]: if '=' in cmd[5]:
serveur_hosting_id = str(original_response[5]).split('=') serveur_hosting_id = str(cmd[5]).split('=')
self.HSID = serveur_hosting_id[1] self.HSID = serveur_hosting_id[1]
return False return False
case _: case _:
pass pass
if len(original_response) < 2: if len(cmd) < 2:
return False return False
match original_response[1]: match cmd[1]:
case 'SLOG': case 'SLOG':
# self.Base.scan_ports(cmd[7]) # self.Base.scan_ports(cmd[7])
@@ -657,18 +645,20 @@ class Irc:
case 'REPUTATION': case 'REPUTATION':
# :001 REPUTATION 91.168.141.239 118 # :001 REPUTATION 91.168.141.239 118
try: try:
self.first_connexion_ip = original_response[2] # if self.Config.ABUSEIPDB == 1:
# self.Base.create_thread(self.abuseipdb_scan, (cmd[2], ))
self.first_connexion_ip = cmd[2]
self.first_score = 0 self.first_score = 0
if str(original_response[3]).find('*') != -1: if str(cmd[3]).find('*') != -1:
# If * available, it means that an ircop changed the repurtation score # If * available, it means that an ircop changed the repurtation score
# means also that the user exist will try to update all users with same IP # means also that the user exist will try to update all users with same IP
self.first_score = int(str(original_response[3]).replace('*','')) self.first_score = int(str(cmd[3]).replace('*',''))
for user in self.User.UID_DB: for user in self.User.UID_DB:
if user.remote_ip == self.first_connexion_ip: if user.remote_ip == self.first_connexion_ip:
user.score_connexion = self.first_score user.score_connexion = self.first_score
else: else:
self.first_score = int(original_response[3]) self.first_score = int(cmd[3])
# Possibilité de déclancher les bans a ce niveau. # Possibilité de déclancher les bans a ce niveau.
except IndexError as ie: except IndexError as ie:
@@ -691,7 +681,7 @@ class Irc:
case 'EOS': case 'EOS':
hsid = str(original_response[0]).replace(':','') hsid = str(cmd[0]).replace(':','')
if hsid == self.HSID: if hsid == self.HSID:
if self.INIT == 1: if self.INIT == 1:
current_version = self.Config.current_version current_version = self.Config.current_version
@@ -701,6 +691,10 @@ class Irc:
else: else:
version = f'{current_version}' version = f'{current_version}'
# self.send2socket(f":{self.Config.SERVICE_NICKNAME} SVSJOIN {self.Config.SERVICE_NICKNAME} {self.Config.SERVICE_CHANLOG}")
# self.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.SERVICE_CHANLOG} +o {self.Config.SERVICE_NICKNAME}")
# self.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.SERVICE_CHANLOG} +{self.Config.SERVICE_CMODES}")
print(f"################### DEFENDER ###################") print(f"################### DEFENDER ###################")
print(f"# SERVICE CONNECTE ") print(f"# SERVICE CONNECTE ")
print(f"# SERVEUR : {self.Config.SERVEUR_IP} ") print(f"# SERVEUR : {self.Config.SERVEUR_IP} ")
@@ -732,15 +726,15 @@ class Irc:
case _: case _:
pass pass
if len(original_response) < 3: if len(cmd) < 3:
return False return False
match original_response[2]: match cmd[2]:
case 'QUIT': case 'QUIT':
# :001N1WD7L QUIT :Quit: free_znc_1 # :001N1WD7L QUIT :Quit: free_znc_1
cmd.pop(0)
uid_who_quit = str(interm_response[0]).replace(':', '') uid_who_quit = str(cmd[0]).replace(':', '')
self.User.delete(uid_who_quit) self.User.delete(uid_who_quit)
self.Channel.delete_user_from_all_channel(uid_who_quit) self.Channel.delete_user_from_all_channel(uid_who_quit)
@@ -752,8 +746,10 @@ class Irc:
# ['@unrealircd.org/geoip=FR;unrealircd.org/', ':001OOU2H3', 'NICK', 'WebIrc', '1703795844'] # ['@unrealircd.org/geoip=FR;unrealircd.org/', ':001OOU2H3', 'NICK', 'WebIrc', '1703795844']
# Changement de nickname # Changement de nickname
uid = str(interm_response[0]).replace(':','') # Supprimer la premiere valeur de la liste
newnickname = interm_response[2] cmd.pop(0)
uid = str(cmd[0]).replace(':','')
newnickname = cmd[2]
self.User.update(uid, newnickname) self.User.update(uid, newnickname)
case 'MODE': case 'MODE':
@@ -768,24 +764,24 @@ class Irc:
# ':001T6VU3F', '001JGWB2K', '@11ZAAAAAB', # ':001T6VU3F', '001JGWB2K', '@11ZAAAAAB',
# '001F16WGR', '001X9YMGQ', '*+001DYPFGP', '@00BAAAAAJ', '001AAGOG9', '001FMFVG8', '001DAEEG7', # '001F16WGR', '001X9YMGQ', '*+001DYPFGP', '@00BAAAAAJ', '001AAGOG9', '001FMFVG8', '001DAEEG7',
# '&~G:unknown-users', '"~G:websocket-users', '"~G:known-users', '"~G:webirc-users'] # '&~G:unknown-users', '"~G:websocket-users', '"~G:known-users', '"~G:webirc-users']
cmd.pop(0)
channel = str(interm_response[3]).lower() channel = str(cmd[3]).lower()
len_cmd = len(interm_response) len_cmd = len(cmd)
list_users:list = [] list_users:list = []
occurence = 0 occurence = 0
start_boucle = 0 start_boucle = 0
# Trouver le premier user # Trouver le premier user
for i in range(len_cmd): for i in range(len_cmd):
s: list = re.findall(fr':', interm_response[i]) s: list = re.findall(fr':', cmd[i])
if s: if s:
occurence += 1 occurence += 1
if occurence == 2: if occurence == 2:
start_boucle = i start_boucle = i
# Boucle qui va ajouter l'ensemble des users (UID) # Boucle qui va ajouter l'ensemble des users (UID)
for i in range(start_boucle, len(interm_response)): for i in range(start_boucle, len(cmd)):
parsed_UID = str(interm_response[i]) parsed_UID = str(cmd[i])
# pattern = fr'[:|@|%|\+|~|\*]*' # pattern = fr'[:|@|%|\+|~|\*]*'
# pattern = fr':' # pattern = fr':'
# parsed_UID = re.sub(pattern, '', parsed_UID) # parsed_UID = re.sub(pattern, '', parsed_UID)
@@ -803,31 +799,29 @@ class Irc:
case 'PART': case 'PART':
# ['@unrealircd.org/geoip=FR;unrealircd.org/userhost=50d6492c@80.214.73.44;unrealircd.org/userip=50d6492c@80.214.73.44;msgid=YSIPB9q4PcRu0EVfC9ci7y-/mZT0+Gj5FLiDSZshH5NCw;time=2024-08-15T15:35:53.772Z', # ['@unrealircd.org/geoip=FR;unrealircd.org/userhost=50d6492c@80.214.73.44;unrealircd.org/userip=50d6492c@80.214.73.44;msgid=YSIPB9q4PcRu0EVfC9ci7y-/mZT0+Gj5FLiDSZshH5NCw;time=2024-08-15T15:35:53.772Z',
# ':001EPFBRD', 'PART', '#welcome', ':WEB', 'IRC', 'Paris'] # ':001EPFBRD', 'PART', '#welcome', ':WEB', 'IRC', 'Paris']
try: uid = str(cmd[1]).replace(':','')
uid = str(interm_response[0]).replace(':','') channel = str(cmd[3]).lower()
channel = str(interm_response[2]).lower()
self.Channel.delete_user_from_channel(channel, uid) self.Channel.delete_user_from_channel(channel, uid)
except IndexError as ie: pass
self.Base.logs.error(f'Index Error: {ie}')
case 'UID': case 'UID':
# ['@s2s-md/geoip=cc=GB|cd=United\\sKingdom|asn=16276|asname=OVH\\sSAS;s2s-md/tls_cipher=TLSv1.3-TLS_CHACHA20_POLY1305_SHA256;s2s-md/creationtime=1721564601', # ['@s2s-md/geoip=cc=GB|cd=United\\sKingdom|asn=16276|asname=OVH\\sSAS;s2s-md/tls_cipher=TLSv1.3-TLS_CHACHA20_POLY1305_SHA256;s2s-md/creationtime=1721564601',
# ':001', 'UID', 'albatros', '0', '1721564597', 'albatros', 'vps-91b2f28b.vps.ovh.net', # ':001', 'UID', 'albatros', '0', '1721564597', 'albatros', 'vps-91b2f28b.vps.ovh.net',
# '001HB8G04', '0', '+iwxz', 'Clk-A62F1D18.vps.ovh.net', 'Clk-A62F1D18.vps.ovh.net', 'MyZBwg==', ':...'] # '001HB8G04', '0', '+iwxz', 'Clk-A62F1D18.vps.ovh.net', 'Clk-A62F1D18.vps.ovh.net', 'MyZBwg==', ':...']
if 'webirc' in original_response[0]: if 'webirc' in cmd[0]:
isWebirc = True isWebirc = True
else: else:
isWebirc = False isWebirc = False
uid = str(original_response[8]) uid = str(cmd[8])
nickname = str(original_response[3]) nickname = str(cmd[3])
username = str(original_response[6]) username = str(cmd[6])
hostname = str(original_response[7]) hostname = str(cmd[7])
umodes = str(original_response[10]) umodes = str(cmd[10])
vhost = str(original_response[11]) vhost = str(cmd[11])
if not 'S' in umodes: if not 'S' in umodes:
remote_ip = self.Base.decode_ip(str(original_response[13])) remote_ip = self.Base.decode_ip(str(cmd[13]))
else: else:
remote_ip = '127.0.0.1' remote_ip = '127.0.0.1'
@@ -849,19 +843,18 @@ class Irc:
) )
for classe_name, classe_object in self.loaded_classes.items(): for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(original_response) classe_object.cmd(cmd_to_send)
case 'PRIVMSG': case 'PRIVMSG':
try: try:
# Supprimer la premiere valeur # Supprimer la premiere valeur
cmd = interm_response.copy() cmd.pop(0)
get_uid_or_nickname = str(cmd[0].replace(':','')) get_uid_or_nickname = str(cmd[0].replace(':',''))
user_trigger = self.User.get_nickname(get_uid_or_nickname) user_trigger = self.User.get_nickname(get_uid_or_nickname)
dnickname = self.Config.SERVICE_NICKNAME dnickname = self.Config.SERVICE_NICKNAME
if len(cmd) == 6: if len(cmd) == 6:
if cmd[1] == 'PRIVMSG' and str(cmd[3]).replace(self.Config.SERVICE_PREFIX,'') == ':auth': if cmd[1] == 'PRIVMSG' and str(cmd[3]).replace('.','') == ':auth':
cmd_copy = cmd.copy() cmd_copy = cmd.copy()
cmd_copy[5] = '**********' cmd_copy[5] = '**********'
self.Base.logs.info(cmd_copy) self.Base.logs.info(cmd_copy)
@@ -918,11 +911,11 @@ class Irc:
return False return False
if not arg[0].lower() in self.commands: if not arg[0].lower() in self.commands:
self.Base.logs.debug(f"This command {arg[0]} sent by {user_trigger} is not available") self.debug(f"This command {arg[0]} is not available")
return False return False
cmd_to_send = convert_to_string.replace(':','') cmd_to_send = convert_to_string.replace(':','')
self.Base.log_cmd(user_trigger, cmd_to_send) self.Base.log_cmd(self.User.get_nickname(user_trigger), cmd_to_send)
fromchannel = None fromchannel = None
if len(arg) >= 2: if len(arg) >= 2:
@@ -936,26 +929,15 @@ class Irc:
case _: case _:
pass pass
if original_response[2] != 'UID': if cmd[2] != 'UID':
# Envoyer la commande aux classes dynamiquement chargées # Envoyer la commande aux classes dynamiquement chargées
for classe_name, classe_object in self.loaded_classes.items(): for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(original_response) classe_object.cmd(cmd_to_send)
except IndexError as ie: except IndexError as ie:
self.Base.logs.error(f"{ie} / {original_response} / length {str(len(original_response))}") self.Base.logs.error(f"{ie} / {cmd} / length {str(len(cmd))}")
def _hcmds(self, user: str, channel: Union[str, None], cmd: list, fullcmd: list = []) -> None: def _hcmds(self, user: str, channel: Union[str, None], cmd:list, fullcmd: list = []) -> None:
"""_summary_
Args:
user (str): The user who sent the query
channel (Union[str, None]): If the command contain the channel
cmd (list): The defender cmd
fullcmd (list, optional): The full list of the cmd coming from PRIVMS. Defaults to [].
Returns:
None: Nothing to return
"""
fromuser = self.User.get_nickname(user) # Nickname qui a lancé la commande fromuser = self.User.get_nickname(user) # Nickname qui a lancé la commande
uid = self.User.get_uid(fromuser) # Récuperer le uid de l'utilisateur uid = self.User.get_uid(fromuser) # Récuperer le uid de l'utilisateur
@@ -1301,6 +1283,10 @@ class Irc:
results = self.Base.db_execute_query(f'SELECT module_name FROM {self.Config.table_module}') results = self.Base.db_execute_query(f'SELECT module_name FROM {self.Config.table_module}')
results = results.fetchall() results = results.fetchall()
# if len(results) == 0:
# self.send2socket(f":{dnickname} NOTICE {fromuser} :There is no module loaded")
# return False
found = False found = False
for module in all_modules: for module in all_modules:
@@ -1315,6 +1301,9 @@ class Irc:
found = False found = False
# for r in results:
# self.send2socket(f":{dnickname} NOTICE {fromuser} :{r[0]} - {self.Config.CONFIG_COLOR['verte']}Loaded{self.Config.CONFIG_COLOR['nogc']}")
case 'show_timers': case 'show_timers':
if self.Base.running_timers: if self.Base.running_timers:

View File

@@ -35,9 +35,7 @@ class Command():
# Create module commands (Mandatory) # Create module commands (Mandatory)
self.commands_level = { self.commands_level = {
1: ['join', 'part'], 1: ['join', 'part'],
2: ['owner', 'deowner', 'op', 'deop', 'halfop', 'dehalfop', 'voice', 2: ['owner', 'deowner', 'op', 'deop', 'halfop', 'dehalfop', 'voice', 'devoice', 'opall', 'deopall', 'devoiceall', 'voiceall', 'ban', 'unban','kick', 'kickban', 'umode']
'devoice', 'opall', 'deopall', 'devoiceall', 'voiceall', 'ban',
'unban','kick', 'kickban', 'umode', 'svsjoin', 'svspart', 'svsnick']
} }
# Init the module # Init the module
@@ -583,62 +581,8 @@ class Command():
nickname = str(cmd[1]) nickname = str(cmd[1])
umode = str(cmd[2]) umode = str(cmd[2])
self.Irc.send2socket(f':{dnickname} SVSMODE {nickname} {umode}') self.send2socket(f':{dnickname} SVSMODE {nickname} {umode}')
except KeyError as ke: except KeyError as ke:
self.Base.logs.error(ke) self.Base.logs.error(ke)
except Exception as err: except Exception as err:
self.Logs.warning(f'Unknown Error: {str(err)}') self.Logs.warning(f'Unknown Error: {str(err)}')
case 'svsjoin':
try:
# .svsjoin nickname #channel
nickname = str(cmd[1])
channel = str(cmd[2])
if len(cmd) != 3:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSJOIN nickname #channel')
return None
self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SVSJOIN {nickname} {channel}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSJOIN nickname #channel')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'svspart':
try:
# .svspart nickname #channel
nickname = str(cmd[1])
channel = str(cmd[2])
if len(cmd) != 3:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSPART nickname #channel')
return None
self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SVSPART {nickname} {channel}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSPART nickname #channel')
self.Logs.warning(f'Unknown Error: {str(err)}')
case 'svsnick':
try:
# .svsnick nickname newnickname
nickname = str(cmd[1])
newnickname = str(cmd[2])
unixtime = self.Base.get_unixtime()
if self.User.get_nickname(nickname) is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : This nickname do not exist')
return None
if len(cmd) != 3:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSNICK nickname newnickname')
return None
self.Irc.send2socket(f':{self.Config.SERVEUR_ID} SVSNICK {nickname} {newnickname} {unixtime}')
except KeyError as ke:
self.Base.logs.error(ke)
except Exception as err:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : /msg {dnickname} SVSNICK nickname newnickname')
self.Logs.warning(f'Unknown Error: {str(err)}')

View File

@@ -140,9 +140,6 @@ class Defender():
self.Base.create_thread(func=self.thread_psutil_scan) self.Base.create_thread(func=self.thread_psutil_scan)
self.Base.create_thread(func=self.thread_reputation_timer) self.Base.create_thread(func=self.thread_reputation_timer)
if self.ModConfig.reputation == 1:
self.Irc.send2socket(f":{self.Config.SERVICE_ID} SAMODE {self.Config.SALON_JAIL} +{self.Config.SERVICE_UMODES} {self.Config.SERVICE_NICKNAME}")
return None return None
def __set_commands(self, commands:dict[int, list[str]]) -> None: def __set_commands(self, commands:dict[int, list[str]]) -> None:
@@ -490,7 +487,6 @@ class Defender():
if self.get_user_uptime_in_minutes(user.uid) >= reputation_timer and int(user.score) <= int(reputation_seuil): if self.get_user_uptime_in_minutes(user.uid) >= reputation_timer and int(user.score) <= int(reputation_seuil):
self.Irc.send2socket(f":{service_id} PRIVMSG {dchanlog} :[{color_red} REPUTATION {color_black}] : Action sur {user.nickname} aprés {str(reputation_timer)} minutes d'inactivité") self.Irc.send2socket(f":{service_id} PRIVMSG {dchanlog} :[{color_red} REPUTATION {color_black}] : Action sur {user.nickname} aprés {str(reputation_timer)} minutes d'inactivité")
self.Irc.send2socket(f":{service_id} KILL {user.nickname} After {str(reputation_timer)} minutes of inactivity you should reconnect and type the password code ") self.Irc.send2socket(f":{service_id} KILL {user.nickname} After {str(reputation_timer)} minutes of inactivity you should reconnect and type the password code ")
self.Irc.send2socket(f":{self.Config.SERVEUR_LINK} REPUTATION {user.ip} 0")
self.Logs.info(f"Nickname: {user.nickname} KILLED after {str(reputation_timer)} minutes of inactivity") self.Logs.info(f"Nickname: {user.nickname} KILLED after {str(reputation_timer)} minutes of inactivity")
uid_to_clean.append(user.uid) uid_to_clean.append(user.uid)
@@ -940,7 +936,7 @@ class Defender():
except ValueError as ve: except ValueError as ve:
self.Logs.error(f"Thread_cloudfilt_scan Error : {ve}") self.Logs.error(f"Thread_cloudfilt_scan Error : {ve}")
def cmd(self, data: list) -> None: def cmd(self, data:list) -> None:
service_id = self.Config.SERVICE_ID # Defender serveur id service_id = self.Config.SERVICE_ID # Defender serveur id
cmd = list(data).copy() cmd = list(data).copy()
@@ -950,10 +946,6 @@ class Defender():
match cmd[1]: match cmd[1]:
case 'EOS':
if self.Irc.INIT == 0:
self.Irc.send2socket(f":{service_id} SAMODE {self.Config.SALON_JAIL} +{self.Config.SERVICE_UMODES} {self.Config.SERVICE_NICKNAME}")
case 'REPUTATION': case 'REPUTATION':
# :001 REPUTATION 91.168.141.239 118 # :001 REPUTATION 91.168.141.239 118
try: try:

View File

@@ -1,179 +0,0 @@
from dataclasses import dataclass
from core.irc import Irc
from unrealircd_rpc_py.Live import Live
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']
}
# 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 #
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.send2socket(f":{dnickname} PRIVMSG {dchanlog} :{json_response}")
pass
def thread_start_jsonrpc(self):
liveRpc = Live(path_to_socket_file='/home/adator/IRC/unrealircd6.1.2.1/data/rpc.socket',
callback_object_instance=self,
callback_method_name='callback_sent_to_irc'
)
liveRpc.execute_async_method()
pass
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:
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':
self.Base.create_thread(self.thread_start_jsonrpc, run_once=True)
pass
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}")

Binary file not shown.

View File

@@ -1,3 +1,3 @@
{ {
"version": "5.1.9" "version": "5.1.6"
} }