This commit is contained in:
adator
2024-11-15 22:14:11 +01:00
parent 44da01945c
commit b81f502b95
11 changed files with 310 additions and 210 deletions

View File

@@ -222,8 +222,8 @@ class Base:
filter: list[str] = ['PING', f":{self.Config.SERVICE_PREFIX}auth"]
# record.msg = record.getMessage().replace("PING", "[REDACTED]")
# if self.Settings.CONSOLE:
# print(record.getMessage())
if self.Settings.CONSOLE:
print(record.getMessage())
for f in filter:
if f in record.getMessage():

View File

@@ -1,9 +1,11 @@
from re import match, findall
from datetime import datetime
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Union
from ssl import SSLEOFError, SSLError
from dataclasses import dataclass
from websockets import serve
if TYPE_CHECKING:
from core.irc import Irc
@@ -17,6 +19,12 @@ class Unrealircd6:
self.__Base = ircInstance.Base
self.__Settings = ircInstance.Base.Settings
self.known_protocol = ['SJOIN', 'UID', 'MD', 'QUIT', 'SQUIT',
'EOS', 'PRIVMSG', 'MODE', 'UMODE2',
'VERSION', 'REPUTATION', 'SVS2MODE',
'SLOG', 'NICK', 'PART', 'PONG'
]
self.__Base.logs.info(f"** Loading protocol [{__name__}]")
def send2socket(self, message: str, print_log: bool = True) -> None:
@@ -104,6 +112,40 @@ class Unrealircd6:
except Exception as err:
self.__Base.logs.error(f"General Error: {err}")
def parse_server_msg(self, server_msg: list[str]) -> Union[str, None]:
"""Parse the server message and return the command
Args:
server_msg (list[str]): The Original server message >>
Returns:
Union[str, None]: Return the command protocol name
"""
protocol_exception = ['PING', 'SERVER', 'PROTOCTL']
increment = 0
server_msg_copy = server_msg.copy()
first_index = 0
second_index = 0
for index, element in enumerate(server_msg_copy):
# Handle the protocol exceptions ex. ping, server ....
if element in protocol_exception and index == 0:
return element
if element.startswith(':'):
increment += 1
first_index = index + 1 if increment == 1 else first_index
second_index = index if increment == 2 else second_index
second_index = len(server_msg_copy) if second_index == 0 else second_index
parsed_msg = server_msg_copy[first_index:second_index]
for cmd in parsed_msg:
if cmd in self.known_protocol:
return cmd
return None
def link(self):
"""Créer le link et envoyer les informations nécessaires pour la
connexion au serveur.
@@ -408,6 +450,32 @@ class Unrealircd6:
self.__Irc.Channel.delete_user_from_channel(channel, userObj.uid)
return None
def on_svs2mode(self, serverMsg: list[str]) -> None:
"""Handle svs2mode coming from a server
Args:
serverMsg (list[str]): Original server message
"""
try:
# >> [':00BAAAAAG', 'SVS2MODE', '001U01R03', '-r']
uid_user_to_edit = serverMsg[2]
umode = serverMsg[3]
userObj = self.__Irc.User.get_User(uid_user_to_edit)
if userObj is None:
return None
if self.__Irc.User.update_mode(userObj.uid, umode):
return None
return None
except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_umode2(self, serverMsg: list[str]) -> None:
"""Handle umode2 coming from a server
@@ -490,12 +558,32 @@ class Unrealircd6:
serverMsg (list[str]): Original server message
"""
# ['PROTOCTL', 'CHANMODES=beI,fkL,lFH,cdimnprstzCDGKMNOPQRSTVZ', 'USERMODES=diopqrstwxzBDGHIRSTWZ', 'BOOTED=1728815798', 'PREFIX=(qaohv)~&@%+', 'SID=001', 'MLOCK', 'TS=1730662755', 'EXTSWHOIS']
if len(serverMsg) > 5:
if '=' in serverMsg[5]:
serveur_hosting_id = str(serverMsg[5]).split('=')
self.__Config.HSID = serveur_hosting_id[1]
if 'USERMODES=' in serverMsg[2]:
self.__Settings.USER_MODES = list(serverMsg[2].split('=')[1])
user_modes: str = None
prefix: str = None
host_server_id: str = None
for msg in serverMsg:
pattern = None
if msg.startswith('PREFIX='):
pattern = r'^PREFIX=\((.*)\).*$'
find_match = match(pattern, msg)
prefix = find_match.group(1) if find_match else None
if find_match:
prefix = find_match.group(1)
elif msg.startswith('USERMODES='):
pattern = r'^USERMODES=(.*)$'
find_match = match(pattern, msg)
user_modes = find_match.group(1) if find_match else None
elif msg.startswith('SID='):
host_server_id = msg.split('=')[1]
if user_modes is None or prefix is None or host_server_id is None:
return None
self.__Config.HSID = host_server_id
self.__Settings.PROTOCTL_USER_MODES = list(user_modes)
self.__Settings.PROTOCTL_PREFIX = list(prefix)
return None
@@ -630,7 +718,7 @@ class Unrealircd6:
else:
geoip = None
score_connexion = 0
score_connexion = self.__Irc.first_score
self.__Irc.User.insert(
self.__Irc.Loader.Definition.MUser(
@@ -662,7 +750,7 @@ class Unrealircd6:
serverMsg (list[str]): List of str coming from the server
"""
try:
#
pong = str(serverMsg[1]).replace(':','')
self.send2socket(f"PONG :{pong}", print_log=False)
@@ -754,7 +842,7 @@ class Unrealircd6:
def on_version_msg(self, serverMsg: list[str]) -> None:
"""Handle version coming from the server
\n ex. /version Defender
Args:
serverMsg (list[str]): Original message from the server
"""
@@ -776,7 +864,7 @@ class Unrealircd6:
response_005 = ' | '.join(modules)
self.send2socket(f':{self.__Config.SERVICE_HOST} 005 {getUser.nickname} {response_005} are supported by this server')
response_005 = ''.join(self.__Settings.USER_MODES)
response_005 = ''.join(self.__Settings.PROTOCTL_USER_MODES)
self.send2socket(f":{self.__Config.SERVICE_HOST} 005 {getUser.nickname} {response_005} are supported by this server")
return None

View File

@@ -11,4 +11,5 @@ class Settings:
CONSOLE: bool = False
USER_MODES: list[str] = []
PROTOCTL_USER_MODES: list[str] = []
PROTOCTL_PREFIX: list[str] = []

View File

@@ -92,7 +92,7 @@ class User:
return False
liste_umodes = list(umodes)
final_umodes_liste = [x for x in self.Base.Settings.USER_MODES if x in liste_umodes]
final_umodes_liste = [x for x in self.Base.Settings.PROTOCTL_USER_MODES if x in liste_umodes]
final_umodes = ''.join(final_umodes_liste)
userObj.umodes = f"+{final_umodes}"

View File

@@ -1,3 +1,5 @@
from ast import parse
from http import server
import sys
import socket
import threading
@@ -73,10 +75,13 @@ class Irc:
self.autolimit_started: bool = False
"""This variable is to make sure the thread is not running"""
self.first_score: int = 100
# define first reputation score to 0
self.first_score: int = 0
# Define the dict that will contain all loaded modules
self.loaded_classes:dict[str, 'Irc'] = {} # Definir la variable qui contiendra la liste modules chargés
# Define the IrcSocket object
self.IrcSocket:Union[socket.socket, SSLSocket] = None
# Liste des commandes internes du bot
@@ -731,109 +736,28 @@ class Irc:
"""
try:
original_response: list[str] = data.copy()
interm_response: list[str] = data.copy()
"""This the original without first value"""
interm_response.pop(0)
if len(original_response) == 0 or len(original_response) == 1:
self.Logs.warning(f'Size ({str(len(original_response))}) - {original_response}')
return False
if len(original_response) == 7:
if original_response[2] == 'PRIVMSG' and original_response[4] == ':auth':
data_copy = original_response.copy()
data_copy[6] = '**********'
self.Logs.debug(f">> {data_copy}")
else:
self.Logs.debug(f">> {original_response}")
else:
self.Logs.debug(f">> {original_response}")
parsed_protocol = self.Protocol.parse_server_msg(original_response.copy())
match original_response[0]:
match parsed_protocol:
case 'PING':
# Sending PONG response to the serveur
self.Protocol.on_server_ping(original_response)
self.Protocol.on_server_ping(serverMsg=original_response)
# print(f"** handle {parsed_protocol}")
return None
case 'PROTOCTL':
#['PROTOCTL', 'CHANMODES=beI,fkL,lFH,cdimnprstzCDGKMNOPQRSTVZ', 'USERMODES=diopqrstwxzBDGHIRSTWZ', 'BOOTED=1702138935',
# 'PREFIX=(qaohv)~&@%+', 'SID=001', 'MLOCK', 'TS=1703793941', 'EXTSWHOIS']
# GET SERVER ID HOST
self.Protocol.on_protoctl(serverMsg=original_response)
return None
case _:
pass
if len(original_response) < 2:
return False
match original_response[1]:
case 'PING':
# Sending PONG response to the serveur
self.Protocol.on_server_ping(original_response)
return None
case 'SLOG':
# self.Base.scan_ports(cmd[7])
# if self.Config.ABUSEIPDB == 1:
# self.Base.create_thread(self.abuseipdb_scan, (cmd[7], ))
pass
case 'VERSION':
self.Protocol.on_version_msg(original_response)
case 'UMODE2':
# [':adator_', 'UMODE2', '-i']
self.Protocol.on_umode2(serverMsg=original_response)
case 'SQUIT':
self.Protocol.on_squit(serverMsg=original_response)
case 'REPUTATION':
# :001 REPUTATION 127.0.0.1 118
try:
self.first_connexion_ip = original_response[2]
self.first_score = 0
if str(original_response[3]).find('*') != -1:
# 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
self.first_score = int(str(original_response[3]).replace('*',''))
for user in self.User.UID_DB:
if user.remote_ip == self.first_connexion_ip:
user.score_connexion = self.first_score
else:
self.first_score = int(original_response[3])
# Possibilité de déclancher les bans a ce niveau.
except IndexError as ie:
self.Logs.error(f'{ie}')
except ValueError as ve:
self.first_score = 0
self.Logs.error(f'Impossible to convert first_score: {ve}')
case '320':
#:irc.deb.biz.st 320 PyDefender IRCParis07 :is in security-groups: known-users,webirc-users,tls-and-known-users,tls-users
pass
case '318':
#:irc.deb.biz.st 318 PyDefender IRCParis93 :End of /WHOIS list.
pass
case 'MD':
# [':001', 'MD', 'client', '001CG0TG7', 'webirc', ':2']
pass
case 'EOS':
case 'SJOIN':
self.Protocol.on_sjoin(serverMsg=original_response)
# print(f"** handle {parsed_protocol}")
case 'EOS': # TODO
hsid = str(original_response[0]).replace(':','')
if hsid == self.Config.HSID:
if self.Config.DEFENDER_INIT == 1:
@@ -886,45 +810,9 @@ class Irc:
classe_object.cmd(original_response)
# Stop here When EOS
# print(f"** handle {parsed_protocol}")
return None
case _:
pass
if len(original_response) < 3:
return False
match original_response[2]:
case 'VERSION':
self.Protocol.on_version_msg(original_response)
case 'QUIT':
self.Protocol.on_quit(serverMsg=original_response)
case 'PONG':
# ['@msgid=aTNJhp17kcPboF5diQqkUL;time=2023-12-28T20:35:58.411Z', ':irc.deb.biz.st', 'PONG', 'irc.deb.biz.st', ':Dev-PyDefender']
self.Base.execute_periodic_action()
case 'NICK':
self.Protocol.on_nick(original_response)
case 'MODE':
#['@msgid=d0ySx56Yd0nc35oHts2SkC-/J9mVUA1hfM6+Z4494xWUg;time=2024-08-09T12:45:36.651Z',
# ':001', 'MODE', '#a', '+nt', '1723207536']
# [':adator_', 'UMODE2', '-i']
pass
case 'SJOIN':
self.Protocol.on_sjoin(serverMsg=original_response)
case 'PART':
self.Protocol.on_part(serverMsg=original_response)
case 'UID':
try:
self.Protocol.on_uid(serverMsg=original_response)
@@ -932,10 +820,76 @@ class Irc:
for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(original_response)
# print(f"** handle {parsed_protocol}")
except Exception as err:
self.Logs.error(f'General Error: {err}')
case 'PRIVMSG':
case 'QUIT':
self.Protocol.on_quit(serverMsg=original_response)
# print(f"** handle {parsed_protocol}")
case 'PROTOCTL':
self.Protocol.on_protoctl(serverMsg=original_response)
# print(f"** handle {parsed_protocol}")
case 'SVS2MODE':
# >> [':00BAAAAAG', 'SVS2MODE', '001U01R03', '-r']
self.Protocol.on_svs2mode(serverMsg=original_response)
# print(f"** handle {parsed_protocol}")
case 'SQUIT':
self.Protocol.on_squit(serverMsg=original_response)
# print(f"** handle {parsed_protocol}")
case 'PART':
self.Protocol.on_part(serverMsg=parsed_protocol)
# print(f"** handle {parsed_protocol}")
case 'VERSION':
self.Protocol.on_version_msg(serverMsg=original_response)
# print(f"** handle {parsed_protocol}")
case 'UMODE2':
# [':adator_', 'UMODE2', '-i']
self.Protocol.on_umode2(serverMsg=original_response)
# print(f"** handle {parsed_protocol}")
case 'NICK':
self.Protocol.on_nick(serverMsg=original_response)
# print(f"** handle {parsed_protocol}")
case 'REPUTATION': # TODO
# :001 REPUTATION 127.0.0.1 118
try:
self.first_connexion_ip = original_response[2]
self.first_score = 0
if str(original_response[3]).find('*') != -1:
# 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
self.first_score = int(str(original_response[3]).replace('*',''))
for user in self.User.UID_DB:
if user.remote_ip == self.first_connexion_ip:
user.score_connexion = self.first_score
else:
self.first_score = int(original_response[3])
# print(f"** handle {parsed_protocol}")
# Possibilité de déclancher les bans a ce niveau.
except IndexError as ie:
self.Logs.error(f'{ie}')
except ValueError as ve:
self.first_score = 0
self.Logs.error(f'Impossible to convert first_score: {ve}')
case 'SLOG': # TODO
print(f"** handle {parsed_protocol}")
case 'MD': # TODO
print(f"** handle {parsed_protocol}")
case 'PRIVMSG': # TODO
try:
# Supprimer la premiere valeur
cmd = interm_response.copy()
@@ -975,7 +929,7 @@ class Irc:
self.Base.log_cmd(user_trigger, cmd_to_send)
fromchannel = str(cmd[2]).lower() if self.Channel.Is_Channel(cmd[2]) else None
self._hcmds(user_trigger, fromchannel, arg, cmd)
self.hcmds(user_trigger, fromchannel, arg, cmd)
if cmd[2] == self.Config.SERVICE_ID:
pattern = fr'^:.*?:(.*)$'
@@ -1013,13 +967,41 @@ class Irc:
if len(arg) >= 2:
fromchannel = str(arg[1]).lower() if self.Channel.Is_Channel(arg[1]) else None
self._hcmds(user_trigger, fromchannel, arg, cmd)
self.hcmds(user_trigger, fromchannel, arg, cmd)
# print(f"** handle {parsed_protocol}")
except IndexError as io:
self.Logs.error(f'{io}')
case _:
pass
case 'PONG': # TODO
print(f"** handle {parsed_protocol}")
case 'MODE': # TODO
#['@msgid=d0ySx56Yd0nc35oHts2SkC-/J9mVUA1hfM6+Z4494xWUg;time=2024-08-09T12:45:36.651Z',
# ':001', 'MODE', '#a', '+nt', '1723207536']
# [':adator_', 'UMODE2', '-i']
print(f"** handle {parsed_protocol}")
case '320': # TODO
#:irc.deb.biz.st 320 PyDefender IRCParis07 :is in security-groups: known-users,webirc-users,tls-and-known-users,tls-users
print(f"** handle {parsed_protocol}")
case '318': # TODO
#:irc.deb.biz.st 318 PyDefender IRCParis93 :End of /WHOIS list.
print(f"** handle {parsed_protocol}")
case None:
print(f"** TO BE HANDLE {original_response}")
if len(original_response) == 7:
if original_response[2] == 'PRIVMSG' and original_response[4] == ':auth':
data_copy = original_response.copy()
data_copy[6] = '**********'
self.Logs.debug(f">> {data_copy}")
else:
self.Logs.debug(f">> {original_response}")
else:
self.Logs.debug(f">> {original_response}")
if original_response[2] != 'UID':
# Envoyer la commande aux classes dynamiquement chargées
@@ -1032,8 +1014,8 @@ class Irc:
self.Logs.error(f"General Error: {err}")
self.Logs.error(f"General Error: {traceback.format_exc()}")
def _hcmds(self, user: str, channel: Union[str, None], cmd: list, fullcmd: list = []) -> None:
"""_summary_
def hcmds(self, user: str, channel: Union[str, None], cmd: list, fullcmd: list = []) -> None:
"""Create
Args:
user (str): The user who sent the query
@@ -1064,7 +1046,7 @@ class Irc:
# Envoyer la commande aux classes dynamiquement chargées
if command != 'notallowed':
for classe_name, classe_object in self.loaded_classes.items():
classe_object._hcmds(user, channel, cmd, fullcmd)
classe_object.hcmds(user, channel, cmd, fullcmd)
match command: