Code Refactoring

This commit is contained in:
adator
2025-11-23 18:22:54 +01:00
parent 5938a1511b
commit cbe527d7d9
10 changed files with 558 additions and 398 deletions

View File

@@ -2,17 +2,18 @@ import asyncio
import os
import re
import json
import time
import socket
import threading
import ipaddress
import ast
import requests
import concurrent.futures
from dataclasses import fields
from typing import Any, Callable, Iterable, Optional, TYPE_CHECKING
from typing import Any, Awaitable, Callable, Optional, TYPE_CHECKING, Union
from base64 import b64decode, b64encode
from sqlalchemy import create_engine, Engine, Connection, CursorResult
from sqlalchemy.sql import text
import core.definition as dfn
if TYPE_CHECKING:
from core.loader import Loader
@@ -36,12 +37,15 @@ class Base:
# Liste des threads en cours
self.running_threads: list[threading.Thread] = self.Settings.RUNNING_THREADS
# List of all async tasks
self.running_asynctasks: list[asyncio.Task] = self.Settings.RUNNING_ASYNCTASKS
# Les sockets ouvert
self.running_sockets: list[socket.socket] = self.Settings.RUNNING_SOCKETS
# List of all asyncio tasks
self.running_iotasks: list[asyncio.Task] = self.Settings.RUNNING_ASYNC_TASKS
# List of all asyncio threads pool executors
self.running_iothreads: list[dfn.MThread] = self.Settings.RUNNING_ASYNC_THREADS
# Liste des fonctions en attentes
self.periodic_func: dict[object] = self.Settings.PERIODIC_FUNC
@@ -71,7 +75,6 @@ class Base:
return None
def __get_latest_defender_version(self) -> None:
try:
self.logs.debug(f'-- Looking for a new version available on Github')
token = ''
json_url = f'https://raw.githubusercontent.com/adator85/DEFENDER/main/version.json'
@@ -80,23 +83,24 @@ class Base:
'Accept': 'application/vnd.github.v3.raw' # Indique à GitHub que nous voulons le contenu brut du fichier
}
with requests.Session() as sess:
try:
if token == '':
response = requests.get(json_url, timeout=self.Config.API_TIMEOUT)
response = sess.get(json_url, timeout=self.Config.API_TIMEOUT)
else:
response = requests.get(json_url, headers=headers, timeout=self.Config.API_TIMEOUT)
response = sess.get(json_url, headers=headers, timeout=self.Config.API_TIMEOUT)
response.raise_for_status() # Vérifie si la requête a réussi
json_response:dict = response.json()
# self.LATEST_DEFENDER_VERSION = json_response["version"]
self.Config.LATEST_VERSION = json_response['version']
self.Config.LATEST_VERSION = json_response.get('version', '')
return None
except requests.HTTPError as err:
self.logs.error(f'Github not available to fetch latest version: {err}')
except:
self.logs.warning(f'Github not available to fetch latest version')
def check_for_new_version(self, online:bool) -> bool:
def check_for_new_version(self, online: bool) -> bool:
"""Check if there is a new version available
Args:
@@ -359,8 +363,8 @@ class Base:
except Exception as err:
self.logs.error(err, exc_info=True)
def create_asynctask(self, func: Any, *, async_name: str = None, run_once: bool = False) -> asyncio.Task:
"""Create a new asynchrone and store it into running_asynctasks variable
def create_asynctask(self, func: Callable[..., Awaitable[Any]], *, async_name: str = None, run_once: bool = False) -> Optional[asyncio.Task]:
"""Create a new asynchrone and store it into running_iotasks variable
Args:
func (Callable): The function you want to call in asynchrone way
@@ -379,26 +383,71 @@ class Base:
task = asyncio.create_task(func, name=name)
task.add_done_callback(self.asynctask_done)
self.running_asynctasks.append(task)
self.running_iotasks.append(task)
self.logs.debug(f"++ New asynchrone task created as: {task.get_name()}")
self.logs.debug(f"=== New IO task created as: {task.get_name()}")
return task
def asynctask_done(self, task: asyncio.Task):
async def create_thread_io(self, func: Callable[..., Any], *args, run_once: bool = False, thread_flag: bool = False) -> Optional[Any]:
"""Run threads via asyncio.
Args:
func (Callable[..., Any]): The blocking IO function
run_once (bool, optional): If it should be run once.. Defaults to False.
thread_flag (bool, optional): If you are using a endless loop, use the threading Event object. Defaults to False.
Returns:
Any: The final result of the blocking IO function
"""
if run_once:
for iothread in self.running_iothreads:
if func.__name__.lower() == iothread.name.lower():
return None
with concurrent.futures.ThreadPoolExecutor(max_workers=1, thread_name_prefix=func.__name__) as executor:
loop = asyncio.get_event_loop()
largs = list(args)
thread_event: Optional[threading.Event] = None
if thread_flag:
thread_event = threading.Event()
thread_event.set()
largs.insert(0, thread_event)
future = loop.run_in_executor(executor, func, *tuple(largs))
future.add_done_callback(self.asynctask_done)
id_obj = self.Loader.Definition.MThread(
name=func.__name__,
thread_id=list(executor._threads)[0].native_id,
thread_event=thread_event,
thread_obj=list(executor._threads)[0],
executor=executor,
future=future)
self.running_iothreads.append(id_obj)
self.logs.debug(f"=== New thread started {func.__name__} with max workers set to: {executor._max_workers}")
result = await future
self.running_iothreads.remove(id_obj)
return result
def asynctask_done(self, task: Union[asyncio.Task, asyncio.Future]):
"""Log task when done
Args:
task (asyncio.Task): The Asyncio Task callback
"""
name = task.get_name() if isinstance(task, asyncio.Task) else "Thread"
task_or_future = "Task" if isinstance(task, asyncio.Task) else "Future"
try:
if task.exception():
self.logs.error(f"[ASYNCIO] Task {task.get_name()} failed with exception: {task.exception()}")
self.logs.error(f"[ASYNCIO] {task_or_future} {name} failed with exception: {task.exception()}")
else:
self.logs.debug(f"[ASYNCIO] Task {task.get_name()} completed successfully.")
self.logs.debug(f"[ASYNCIO] {task_or_future} {name} completed successfully.")
except asyncio.CancelledError as ce:
self.logs.debug(f"[ASYNCIO] Task {task.get_name()} terminated with cancelled error.")
self.logs.debug(f"[ASYNCIO] {task_or_future} {name} terminated with cancelled error. {ce}")
except asyncio.InvalidStateError as ie:
self.logs.debug(f"[ASYNCIO] Task {task.get_name()} terminated with invalid state error.")
self.logs.debug(f"[ASYNCIO] {task_or_future} {name} terminated with invalid state error. {ie}")
def is_thread_alive(self, thread_name: str) -> bool:
"""Check if the thread is still running! using the is_alive method of Threads.
@@ -492,59 +541,6 @@ class Base:
self.running_sockets.remove(soc)
self.logs.debug(f"-- Socket ==> closed {str(soc.fileno())}")
async def shutdown(self) -> None:
"""Methode qui va préparer l'arrêt complêt du service
"""
# Stop RpcServer if running
await self.Loader.RpcServer.stop_server()
# unload modules.
self.logs.debug(f"=======> Unloading all modules!")
for module in self.Loader.ModuleUtils.model_get_loaded_modules().copy():
await self.Loader.ModuleUtils.unload_one_module(module.module_name)
self.logs.debug(f"=======> Closing all Coroutines!")
try:
await asyncio.wait_for(asyncio.gather(*self.running_asynctasks), timeout=5)
except asyncio.exceptions.TimeoutError as te:
self.logs.debug(f"Asyncio Timeout reached! {te}")
for task in self.running_asynctasks:
task.cancel()
except asyncio.exceptions.CancelledError as cerr:
self.logs.debug(f"Asyncio CancelledError reached! {cerr}")
# Nettoyage des timers
self.logs.debug(f"=======> Checking for Timers to stop")
for timer in self.running_timers:
while timer.is_alive():
self.logs.debug(f"> waiting for {timer.name} to close")
timer.cancel()
await asyncio.sleep(0.2)
self.running_timers.remove(timer)
self.logs.debug(f"> Cancelling {timer.name} {timer.native_id}")
self.logs.debug(f"=======> Checking for Threads to stop")
for thread in self.running_threads:
if thread.name == 'heartbeat' and thread.is_alive():
self.execute_periodic_action()
self.logs.debug(f"> Running the last periodic action")
self.running_threads.remove(thread)
self.logs.debug(f"> Cancelling {thread.name} {thread.native_id}")
self.logs.debug(f"=======> Checking for Sockets to stop")
for soc in self.running_sockets:
soc.close()
while soc.fileno() != -1:
soc.close()
self.running_sockets.remove(soc)
self.logs.debug(f"> Socket ==> closed {str(soc.fileno())}")
self.db_close()
return None
def db_init(self) -> tuple[Engine, Connection]:
db_directory = self.Config.DB_PATH

View File

@@ -1,3 +1,4 @@
import asyncio
import importlib
import sys
import time
@@ -26,7 +27,6 @@ REHASH_MODULES = [
'core.classes.protocols.inspircd'
]
async def restart_service(uplink: 'Loader', reason: str = "Restarting with no reason!") -> None:
"""
@@ -69,7 +69,7 @@ async def rehash_service(uplink: 'Loader', nickname: str) -> None:
need_a_restart = ["SERVEUR_ID"]
uplink.Settings.set_cache('db_commands', uplink.Commands.DB_COMMANDS)
await uplink.RpcServer.stop_server()
await uplink.RpcServer.stop_rpc_server()
restart_flag = False
config_model_bakcup = uplink.Config
@@ -122,10 +122,68 @@ async def rehash_service(uplink: 'Loader', nickname: str) -> None:
uplink.Irc.Protocol.register_command()
uplink.RpcServer = uplink.RpcServerModule.JSonRpcServer(uplink)
uplink.Base.create_asynctask(uplink.RpcServer.start_server())
uplink.Base.create_asynctask(uplink.RpcServer.start_rpc_server())
# Reload Service modules
for module in uplink.ModuleUtils.model_get_loaded_modules().copy():
await uplink.ModuleUtils.reload_one_module(module.module_name, nickname)
return None
async def shutdown(uplink: 'Loader') -> None:
"""Methode qui va préparer l'arrêt complêt du service
"""
# Stop RpcServer if running
await uplink.RpcServer.stop_rpc_server()
# unload modules.
uplink.Logs.debug(f"=======> Unloading all modules!")
for module in uplink.ModuleUtils.model_get_loaded_modules().copy():
await uplink.ModuleUtils.unload_one_module(module.module_name)
# Nettoyage des timers
uplink.Logs.debug(f"=======> Closing all timers!")
for timer in uplink.Base.running_timers:
while timer.is_alive():
uplink.Logs.debug(f"> waiting for {timer.name} to close")
timer.cancel()
await asyncio.sleep(0.2)
uplink.Logs.debug(f"> Cancelling {timer.name} {timer.native_id}")
uplink.Logs.debug(f"=======> Closing all Threads!")
for thread in uplink.Base.running_threads:
if thread.name == 'heartbeat' and thread.is_alive():
uplink.Base.execute_periodic_action()
uplink.Logs.debug(f"> Running the last periodic action")
uplink.Logs.debug(f"> Cancelling {thread.name} {thread.native_id}")
uplink.Logs.debug(f"=======> Closing all IO Threads!")
[th.thread_event.clear() for th in uplink.Base.running_iothreads]
uplink.Logs.debug(f"=======> Closing all IO TASKS!")
try:
await asyncio.wait_for(asyncio.gather(*uplink.Base.running_iotasks), timeout=5)
except asyncio.exceptions.TimeoutError as te:
uplink.Logs.debug(f"Asyncio Timeout reached! {te}")
for task in uplink.Base.running_iotasks:
task.cancel()
except asyncio.exceptions.CancelledError as cerr:
uplink.Logs.debug(f"Asyncio CancelledError reached! {cerr}")
uplink.Logs.debug(f"=======> Closing all Sockets!")
for soc in uplink.Base.running_sockets:
soc.close()
while soc.fileno() != -1:
soc.close()
uplink.Base.running_sockets.remove(soc)
uplink.Logs.debug(f"> Socket ==> closed {str(soc.fileno())}")
uplink.Base.running_timers.clear()
uplink.Base.running_threads.clear()
uplink.Base.running_iotasks.clear()
uplink.Base.running_iothreads.clear()
uplink.Base.running_sockets.clear()
uplink.Base.db_close()
return None

View File

@@ -17,11 +17,11 @@ if TYPE_CHECKING:
class JSonRpcServer:
def __init__(self, context: 'Loader', *, hostname: str = '0.0.0.0', port: int = 5000):
def __init__(self, context: 'Loader'):
self._ctx = context
self.live: bool = False
self.host = hostname
self.port = port
self.host = context.Config.RPC_HOST
self.port = context.Config.RPC_PORT
self.routes: list[Route] = []
self.server: Optional[uvicorn.Server] = None
@@ -34,12 +34,12 @@ class JSonRpcServer:
'command.get.by.module': RPCCommand(context).command_get_by_module
}
async def start_server(self):
async def start_rpc_server(self):
if not self.live:
self.routes = [Route('/api', self.request_handler, methods=['POST'])]
self.app_jsonrpc = Starlette(debug=False, routes=self.routes)
config = uvicorn.Config(self.app_jsonrpc, host=self.host, port=self.port, log_level=self._ctx.Config.DEBUG_LEVEL)
config = uvicorn.Config(self.app_jsonrpc, host=self.host, port=self.port, log_level=self._ctx.Config.DEBUG_LEVEL+10)
self.server = uvicorn.Server(config)
self.live = True
await self._ctx.Irc.Protocol.send_priv_msg(
@@ -52,7 +52,7 @@ class JSonRpcServer:
else:
self._ctx.Logs.debug("Server already running")
async def stop_server(self):
async def stop_rpc_server(self):
if self.server:
self.server.should_exit = True

View File

@@ -126,7 +126,11 @@ class Irc:
async def listen(self):
self.ctx.Base.create_asynctask(
self.ctx.Base.create_thread_io(self.ctx.Utils.heartbeat, True, self.ctx, self.beat)
self.ctx.Base.create_thread_io(
self.ctx.Utils.heartbeat,
self.ctx, self.beat,
run_once=True, thread_flag=True
)
)
while self.signal:
@@ -343,8 +347,13 @@ class Irc:
async def thread_check_for_new_version(self, fromuser: str) -> None:
dnickname = self.ctx.Config.SERVICE_NICKNAME
response = self.ctx.Base.create_asynctask(
self.ctx.Base.create_thread_io(
self.ctx.Base.check_for_new_version, True
)
)
if self.ctx.Base.check_for_new_version(True):
if response:
await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" New Version available : {self.ctx.Config.CURRENT_VERSION} >>> {self.ctx.Config.LATEST_VERSION}")
await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=" Please run (git pull origin main) in the current folder")
else:

View File

@@ -81,7 +81,7 @@ class Loader:
self.PFactory: factory.ProtocolFactorty = factory.ProtocolFactorty(self)
self.RpcServer: rpc_mod.JSonRpcServer = rpc_mod.JSonRpcServer(self, hostname='0.0.0.0')
self.RpcServer: rpc_mod.JSonRpcServer = rpc_mod.JSonRpcServer(self)
self.Logs.debug(self.Utils.tr("Loader %s success", __name__))

View File

@@ -1,4 +1,5 @@
from dataclasses import dataclass
import logging
from typing import Any, TYPE_CHECKING, Optional
from core.classes.interfaces.imodule import IModule
import mods.defender.schemas as schemas
@@ -26,6 +27,8 @@ class Defender(IModule):
def __init__(self, context: 'Loader') -> None:
super().__init__(context)
self._mod_config: Optional[schemas.ModConfModel] = None
self.Schemas = schemas.RepDB()
self.Threads = thds
@property
def mod_config(self) -> ModConfModel:
@@ -55,40 +58,43 @@ class Defender(IModule):
return None
async def load(self):
# Variable qui va contenir les options de configuration du module Defender
self._mod_config: schemas.ModConfModel = self.ModConfModel()
# sync the database with local variable (Mandatory)
await self.sync_db()
# Add module schemas
self.Schemas = schemas
# Add module utils functions
self.mod_utils = utils
# Create module commands (Mandatory)
self.ctx.Irc.build_command(0, self.module_name, 'code', 'Display the code or key for access')
self.ctx.Irc.build_command(1, self.module_name, 'info', 'Provide information about the channel or server')
self.ctx.Irc.build_command(1, self.module_name, 'autolimit', 'Automatically set channel user limits')
self.ctx.Irc.build_command(3, self.module_name, 'reputation', 'Check or manage user reputation')
self.ctx.Irc.build_command(3, self.module_name, 'proxy_scan', 'Scan users for proxy connections')
self.ctx.Irc.build_command(3, self.module_name, 'flood', 'Handle flood detection and mitigation')
self.ctx.Irc.build_command(3, self.module_name, 'status', 'Check the status of the server or bot')
self.ctx.Irc.build_command(3, self.module_name, 'show_reputation', 'Display reputation information')
self.ctx.Irc.build_command(3, self.module_name, 'sentinel', 'Monitor and guard the channel or server')
self.ctx.Commands.build_command(0, self.module_name, 'code', 'Display the code or key for access')
self.ctx.Commands.build_command(1, self.module_name, 'info', 'Provide information about the channel or server')
self.ctx.Commands.build_command(1, self.module_name, 'autolimit', 'Automatically set channel user limits')
self.ctx.Commands.build_command(3, self.module_name, 'reputation', 'Check or manage user reputation')
self.ctx.Commands.build_command(3, self.module_name, 'proxy_scan', 'Scan users for proxy connections')
self.ctx.Commands.build_command(3, self.module_name, 'flood', 'Handle flood detection and mitigation')
self.ctx.Commands.build_command(3, self.module_name, 'status', 'Check the status of the server or bot')
self.ctx.Commands.build_command(3, self.module_name, 'show_reputation', 'Display reputation information')
self.ctx.Commands.build_command(3, self.module_name, 'sentinel', 'Monitor and guard the channel or server')
self.timeout = self.ctx.Config.API_TIMEOUT
# Listes qui vont contenir les ip a scanner avec les différentes API
self.Schemas.DB_ABUSEIPDB_USERS = self.Schemas.DB_FREEIPAPI_USERS = self.Schemas.DB_CLOUDFILT_USERS = []
self.Schemas.DB_PSUTIL_USERS = self.Schemas.DB_LOCALSCAN_USERS = []
self.Schemas.DB_ABUSEIPDB_USERS = []
self.Schemas.DB_FREEIPAPI_USERS = []
self.Schemas.DB_CLOUDFILT_USERS = []
self.Schemas.DB_PSUTIL_USERS = []
self.Schemas.DB_LOCALSCAN_USERS = []
# Variables qui indique que les threads sont en cours d'éxecutions
self.abuseipdb_isRunning = self.freeipapi_isRunning = self.cloudfilt_isRunning = True
self.psutil_isRunning = self.localscan_isRunning = self.reputationTimer_isRunning = True
self.autolimit_isRunning = True
self.abuseipdb_isRunning = True if self.mod_config.abuseipdb_scan == 1 else False
self.freeipapi_isRunning = True if self.mod_config.freeipapi_scan == 1 else False
self.cloudfilt_isRunning = True if self.mod_config.cloudfilt_scan == 1 else False
self.psutil_isRunning = True if self.mod_config.psutil_scan == 1 else False
self.localscan_isRunning = True if self.mod_config.local_scan == 1 else False
self.reputationTimer_isRunning = True if self.mod_config.reputation == 1 else False
self.autolimit_isRunning = True if self.mod_config.autolimit == 1 else False
# Variable qui va contenir les users
self.flood_system = {}
@@ -101,19 +107,21 @@ class Defender(IModule):
self.cloudfilt_key = 'r1gEtjtfgRQjtNBDMxsg'
# Démarrer les threads pour démarrer les api
self.ctx.Base.create_asynctask(thds.coro_freeipapi_scan(self))
self.ctx.Base.create_asynctask(thds.coro_cloudfilt_scan(self))
self.ctx.Base.create_asynctask(thds.coro_abuseipdb_scan(self))
self.ctx.Base.create_asynctask(thds.coro_local_scan(self))
self.ctx.Base.create_asynctask(thds.coro_psutil_scan(self))
self.ctx.Base.create_asynctask(thds.coro_apply_reputation_sanctions(self))
if self.mod_config.autolimit == 1:
self.ctx.Base.create_asynctask(thds.coro_autolimit(self))
self.ctx.Base.create_asynctask(self.Threads.coro_freeipapi_scan(self)) if self.mod_config.freeipapi_scan == 1 else None
self.ctx.Base.create_asynctask(self.Threads.coro_cloudfilt_scan(self)) if self.mod_config.cloudfilt_scan == 1 else None
self.ctx.Base.create_asynctask(self.Threads.coro_abuseipdb_scan(self)) if self.mod_config.abuseipdb_scan == 1 else None
self.ctx.Base.create_asynctask(self.Threads.coro_local_scan(self)) if self.mod_config.local_scan == 1 else None
self.ctx.Base.create_asynctask(self.Threads.coro_psutil_scan(self)) if self.mod_config.psutil_scan == 1 else None
self.ctx.Base.create_asynctask(self.Threads.coro_apply_reputation_sanctions(self)) if self.mod_config.reputation == 1 else None
self.ctx.Base.create_asynctask(self.Threads.coro_autolimit(self)) if self.mod_config.autolimit == 1 else None
if self.mod_config.reputation == 1:
await self.ctx.Irc.Protocol.send_sjoin(self.ctx.Config.SALON_JAIL)
await self.ctx.Irc.Protocol.send2socket(f":{self.ctx.Config.SERVICE_NICKNAME} SAMODE {self.ctx.Config.SALON_JAIL} +o {self.ctx.Config.SERVICE_NICKNAME}")
for chan in self.ctx.Channel.UID_CHANNEL_DB:
if chan.name != self.ctx.Config.SALON_JAIL:
await self.ctx.Irc.Protocol.send_set_mode('+b', channel_name=chan.name, params='~security-group:unknown-users')
await self.ctx.Irc.Protocol.send_set_mode('+eee', channel_name=chan.name, params='~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users')
def __onload(self):
@@ -138,7 +146,7 @@ class Defender(IModule):
if localscan:
self.Schemas.DB_LOCALSCAN_USERS = localscan
def unload(self) -> None:
async def unload(self) -> None:
"""Cette methode sera executée a chaque désactivation ou
rechargement de module
"""
@@ -158,6 +166,13 @@ class Defender(IModule):
self.ctx.Commands.drop_command_by_module(self.module_name)
if self.mod_config.reputation == 1:
await self.ctx.Irc.Protocol.send_part_chan(self.ctx.Config.SERVICE_ID, self.ctx.Config.SALON_JAIL)
for chan in self.ctx.Channel.UID_CHANNEL_DB:
if chan.name != self.ctx.Config.SALON_JAIL:
await self.ctx.Irc.Protocol.send_set_mode('-b', channel_name=chan.name, params='~security-group:unknown-users')
await self.ctx.Irc.Protocol.send_set_mode('-eee', channel_name=chan.name, params='~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users')
return None
async def insert_db_trusted(self, uid: str, nickname:str) -> None:
@@ -411,22 +426,26 @@ class Defender(IModule):
if self.mod_config.reputation == 1:
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.ctx.Config.COLORS.green}REPUTATION{self.ctx.Config.COLORS.black} ] : Already activated", channel=dchanlog)
return False
return None
# self.update_db_configuration('reputation', 1)
await self.update_configuration(key, 1)
self.ctx.Base.create_asynctask(self.Threads.coro_apply_reputation_sanctions(self))
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {self.ctx.Config.COLORS.green}REPUTATION{self.ctx.Config.COLORS.black} ] : Activated by {fromuser}", channel=dchanlog)
await self.ctx.Irc.Protocol.send_join_chan(uidornickname=dnickname, channel=jail_chan)
await self.ctx.Irc.Protocol.send2socket(f":{service_id} SAMODE {jail_chan} +{dumodes} {dnickname}")
await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {jail_chan} +{jail_chan_mode}")
await self.ctx.Irc.Protocol.send_set_mode(f'+{jail_chan_mode}', channel_name=jail_chan)
if self.mod_config.reputation_sg == 1:
for chan in self.ctx.Channel.UID_CHANNEL_DB:
if chan.name != jail_chan:
await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {chan.name} +b ~security-group:unknown-users")
await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {chan.name} +eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users")
await self.ctx.Irc.Protocol.send_set_mode('+b', channel_name=chan.name, params='~security-group:unknown-users')
await self.ctx.Irc.Protocol.send_set_mode(
'+eee',
channel_name=chan.name,
params='~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users'
)
await self.ctx.Channel.db_query_channel('add', self.module_name, jail_chan)
@@ -441,20 +460,26 @@ class Defender(IModule):
return False
await self.update_configuration(key, 0)
self.reputationTimer_isRunning = False
await self.ctx.Irc.Protocol.send_priv_msg(
nick_from=dnickname,
msg=f"[ {self.ctx.Config.COLORS.red}REPUTATION{self.ctx.Config.COLORS.black} ] : Deactivated by {fromuser}",
channel=dchanlog
)
await self.ctx.Irc.Protocol.send2socket(f":{service_id} SAMODE {jail_chan} -{dumodes} {dnickname}")
await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {jail_chan} -sS")
await self.ctx.Irc.Protocol.send2socket(f":{service_id} PART {jail_chan}")
await self.ctx.Irc.Protocol.send_set_mode('-sS', channel_name=jail_chan)
await self.ctx.Irc.Protocol.send_part_chan(service_id, jail_chan)
for chan in self.ctx.Channel.UID_CHANNEL_DB:
if chan.name != jail_chan:
await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {chan.name} -b ~security-group:unknown-users")
await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {chan.name} -eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users")
await self.ctx.Irc.Protocol.send_set_mode('-b', channel_name=chan.name, params='~security-group:unknown-users')
await self.ctx.Irc.Protocol.send_set_mode(
'-eee',
channel_name=chan.name,
params='~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users'
)
await self.ctx.Channel.db_query_channel('del', self.module_name, jail_chan)
@@ -464,12 +489,17 @@ class Defender(IModule):
match get_options:
case 'release':
# .reputation release [nick]
p = await self.ctx.Irc.Protocol
link = self.ctx.Config.SERVEUR_LINK
jailed_salon = self.ctx.Config.SALON_JAIL
welcome_salon = self.ctx.Config.SALON_LIBERER
client_obj = self.ctx.User.get_user(str(cmd[2]))
if self.mod_config.reputation != 1:
await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname,
nick_to=fromuser,
msg="The reputation system is not activated!")
return None
if client_obj is None:
await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname,
nick_to=fromuser,
@@ -658,6 +688,7 @@ class Defender(IModule):
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog)
return None
self.ctx.Base.create_asynctask(self.Threads.coro_local_scan(self))
await self.update_configuration(option, 1)
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog)
@@ -667,6 +698,7 @@ class Defender(IModule):
return None
await self.update_configuration(option, 0)
self.localscan_isRunning = False
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog)
@@ -676,6 +708,7 @@ class Defender(IModule):
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog)
return None
self.ctx.Base.create_asynctask(self.Threads.coro_psutil_scan(self))
await self.update_configuration(option, 1)
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog)
@@ -685,6 +718,7 @@ class Defender(IModule):
return None
await self.update_configuration(option, 0)
self.psutil_isRunning = False
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog)
@@ -694,6 +728,7 @@ class Defender(IModule):
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog)
return None
self.ctx.Base.create_asynctask(self.Threads.coro_abuseipdb_scan(self))
await self.update_configuration(option, 1)
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog)
@@ -703,6 +738,7 @@ class Defender(IModule):
return None
await self.update_configuration(option, 0)
self.abuseipdb_isRunning = False
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog)
@@ -712,6 +748,7 @@ class Defender(IModule):
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog)
return None
self.ctx.Base.create_asynctask(self.Threads.coro_freeipapi_scan(self))
await self.update_configuration(option, 1)
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog)
@@ -721,6 +758,7 @@ class Defender(IModule):
return None
await self.update_configuration(option, 0)
self.freeipapi_isRunning = False
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog)
@@ -730,6 +768,7 @@ class Defender(IModule):
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Already activated", channel=dchanlog)
return None
self.ctx.Base.create_asynctask(self.Threads.coro_cloudfilt_scan(self))
await self.update_configuration(option, 1)
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_green}PROXY_SCAN {option.upper()}{color_black} ] : Activated by {fromuser}", channel=dchanlog)
@@ -739,6 +778,7 @@ class Defender(IModule):
return None
await self.update_configuration(option, 0)
self.cloudfilt_isRunning = False
await self.ctx.Irc.Protocol.send_priv_msg(nick_from=dnickname, msg=f"[ {color_red}PROXY_SCAN {option.upper()}{color_black} ] : Deactivated by {fromuser}", channel=dchanlog)
@@ -915,3 +955,6 @@ class Defender(IModule):
await self.join_saved_channels()
return None
case _:
pass

View File

@@ -28,9 +28,11 @@ class FloodUser(MainModel):
nbr_msg: int = 0
first_msg_time: int = 0
DB_FLOOD_USERS: list[FloodUser] = []
DB_ABUSEIPDB_USERS: list[MUser] = []
DB_FREEIPAPI_USERS: list[MUser] = []
DB_CLOUDFILT_USERS: list[MUser] = []
DB_PSUTIL_USERS: list[MUser] = []
DB_LOCALSCAN_USERS: list[MUser] = []
class RepDB:
DB_FLOOD_USERS: list[FloodUser] = []
DB_ABUSEIPDB_USERS: list[MUser] = []
DB_FREEIPAPI_USERS: list[MUser] = []
DB_CLOUDFILT_USERS: list[MUser] = []
DB_PSUTIL_USERS: list[MUser] = []
DB_LOCALSCAN_USERS: list[MUser] = []

View File

@@ -1,89 +1,251 @@
import asyncio
from typing import TYPE_CHECKING
from time import sleep
from typing import TYPE_CHECKING, Optional
if TYPE_CHECKING:
from mods.defender.mod_defender import Defender
async def coro_apply_reputation_sanctions(uplink: 'Defender'):
uplink.reputationTimer_isRunning = True
while uplink.reputationTimer_isRunning:
await uplink.mod_utils.action_apply_reputation_santions(uplink)
await asyncio.sleep(5)
async def coro_cloudfilt_scan(uplink: 'Defender'):
uplink.cloudfilt_isRunning = True
service_id = uplink.ctx.Config.SERVICE_ID
service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG
color_red = uplink.ctx.Config.COLORS.red
nogc = uplink.ctx.Config.COLORS.nogc
nogc = uplink.ctx.Config.COLORS.nogc
p = uplink.ctx.Irc.Protocol
while uplink.cloudfilt_isRunning:
try:
list_to_remove:list = []
for user in uplink.Schemas.DB_CLOUDFILT_USERS:
await uplink.mod_utils.action_scan_client_with_cloudfilt(uplink, user)
if user.remote_ip not in uplink.ctx.Config.WHITELISTED_IP:
result: Optional[dict] = await uplink.ctx.Base.create_thread_io(
uplink.mod_utils.action_scan_client_with_cloudfilt,
uplink, user
)
list_to_remove.append(user)
if not result:
continue
remote_ip = user.remote_ip
fullname = f'{user.nickname}!{user.username}@{user.hostname}'
r_host = result.get('host', None)
r_countryiso = result.get('countryiso', None)
r_listed = result.get('listed', False)
r_listedby = result.get('listed_by', None)
await p.send_priv_msg(
nick_from=service_id,
msg=f"[ {color_red}CLOUDFILT_SCAN{nogc} ] : Connexion de {fullname} ({remote_ip}) ==> Host: {r_host} | country: {r_countryiso} | listed: {r_listed} | listed by : {r_listedby}",
channel=service_chanlog)
uplink.ctx.Logs.debug(f"[CLOUDFILT SCAN] ({fullname}) connected from ({r_countryiso}), Listed: {r_listed}, by: {r_listedby}")
if r_listed:
await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} Your connexion is listed as dangerous {r_listed} {r_listedby} - detected by cloudfilt")
uplink.ctx.Logs.debug(f"[CLOUDFILT SCAN GLINE] Dangerous connection ({fullname}) from ({r_countryiso}) Listed: {r_listed}, by: {r_listedby}")
await asyncio.sleep(1)
for user_model in list_to_remove:
uplink.Schemas.DB_CLOUDFILT_USERS.remove(user_model)
await asyncio.sleep(1)
await asyncio.sleep(1.5)
except ValueError as ve:
uplink.ctx.Logs.debug(f"The value to remove is not in the list. {ve}")
except TimeoutError as te:
uplink.ctx.Logs.debug(f"Timeout Error {te}")
async def coro_freeipapi_scan(uplink: 'Defender'):
uplink.freeipapi_isRunning = True
service_id = uplink.ctx.Config.SERVICE_ID
service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG
color_red = uplink.ctx.Config.COLORS.red
nogc = uplink.ctx.Config.COLORS.nogc
p = uplink.ctx.Irc.Protocol
while uplink.freeipapi_isRunning:
try:
list_to_remove: list = []
for user in uplink.Schemas.DB_FREEIPAPI_USERS:
await uplink.mod_utils.action_scan_client_with_freeipapi(uplink, user)
if user.remote_ip not in uplink.ctx.Config.WHITELISTED_IP:
result: Optional[dict] = await uplink.ctx.Base.create_thread_io(
uplink.mod_utils.action_scan_client_with_freeipapi,
uplink, user
)
if not result:
continue
# pseudo!ident@host
remote_ip = user.remote_ip
fullname = f'{user.nickname}!{user.username}@{user.hostname}'
await p.send_priv_msg(
nick_from=service_id,
msg=f"[ {color_red}FREEIPAPI_SCAN{nogc} ] : Connexion de {fullname} ({remote_ip}) ==> Proxy: {str(result['isProxy'])} | Country : {str(result['countryCode'])}",
channel=service_chanlog)
uplink.ctx.Logs.debug(f"[FREEIPAPI SCAN] ({fullname}) connected from ({result['countryCode']}), Proxy: {result['isProxy']}")
if result['isProxy']:
await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} This server do not allow proxy connexions {str(result['isProxy'])} - detected by freeipapi")
uplink.ctx.Logs.debug(f"[FREEIPAPI SCAN GLINE] Server do not allow proxy connexions {result['isProxy']}")
list_to_remove.append(user)
await asyncio.sleep(1)
# remove users from the list
for user_model in list_to_remove:
uplink.Schemas.DB_FREEIPAPI_USERS.remove(user_model)
await asyncio.sleep(1)
await asyncio.sleep(1.5)
except ValueError as ve:
uplink.ctx.Logs.debug(f"The value to remove is not in the list. {ve}")
except TimeoutError as te:
uplink.ctx.Logs.debug(f"Timeout Error {te}")
async def coro_abuseipdb_scan(uplink: 'Defender'):
while uplink.abuseipdb_isRunning:
uplink.abuseipdb_isRunning = True
service_id = uplink.ctx.Config.SERVICE_ID
service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG
color_red = uplink.ctx.Config.COLORS.red
nogc = uplink.ctx.Config.COLORS.nogc
p = uplink.ctx.Irc.Protocol
while uplink.abuseipdb_isRunning:
try:
list_to_remove: list = []
print(uplink.Schemas.DB_ABUSEIPDB_USERS)
for user in uplink.Schemas.DB_ABUSEIPDB_USERS:
await uplink.mod_utils.action_scan_client_with_abuseipdb(uplink, user)
if user.remote_ip not in uplink.ctx.Config.WHITELISTED_IP:
result: Optional[dict] = await uplink.ctx.Base.create_thread_io(
uplink.mod_utils.action_scan_client_with_abuseipdb,
uplink, user
)
list_to_remove.append(user)
if not result:
continue
remote_ip = user.remote_ip
fullname = f'{user.nickname}!{user.username}@{user.hostname}'
await p.send_priv_msg(
nick_from=service_id,
msg=f"[ {color_red}ABUSEIPDB_SCAN{nogc} ] : Connexion de {fullname} ({remote_ip}) ==> Score: {str(result['score'])} | Country : {result['country']} | Tor : {str(result['isTor'])} | Total Reports : {str(result['totalReports'])}",
channel=service_chanlog
)
uplink.ctx.Logs.debug(f"[ABUSEIPDB SCAN] ({fullname}) connected from ({result['country']}), Score: {result['score']}, Tor: {result['isTor']}")
if result['isTor']:
await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} This server do not allow Tor connexions {str(result['isTor'])} - Detected by Abuseipdb")
uplink.ctx.Logs.debug(f"[ABUSEIPDB SCAN GLINE] Server do not allow Tor connections Tor: {result['isTor']}, Score: {result['score']}")
elif result['score'] >= 95:
await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} You were banned from this server because your abuse score is = {str(result['score'])} - Detected by Abuseipdb")
uplink.ctx.Logs.debug(f"[ABUSEIPDB SCAN GLINE] Server do not high risk connections Country: {result['country']}, Score: {result['score']}")
await asyncio.sleep(1)
print(list_to_remove)
for user_model in list_to_remove:
uplink.Schemas.DB_ABUSEIPDB_USERS.remove(user_model)
await asyncio.sleep(1)
await asyncio.sleep(1.5)
except ValueError as ve:
uplink.ctx.Logs.debug(f"The value to remove is not in the list. {ve}", exc_info=True)
except TimeoutError as te:
uplink.ctx.Logs.debug(f"Timeout Error {te}", exc_info=True)
async def coro_local_scan(uplink: 'Defender'):
uplink.localscan_isRunning = True
service_id = uplink.ctx.Config.SERVICE_ID
service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG
color_red = uplink.ctx.Config.COLORS.red
nogc = uplink.ctx.Config.COLORS.nogc
p = uplink.ctx.Irc.Protocol
while uplink.localscan_isRunning:
try:
list_to_remove:list = []
for user in uplink.Schemas.DB_LOCALSCAN_USERS:
await uplink.mod_utils.action_scan_client_with_local_socket(uplink, user)
if user.remote_ip not in uplink.ctx.Config.WHITELISTED_IP:
list_to_remove.append(user)
result = await uplink.ctx.Base.create_thread_io(
uplink.mod_utils.action_scan_client_with_local_socket,
uplink, user
)
if not result:
continue
fullname = f'{user.nickname}!{user.username}@{user.hostname}'
opened_ports = result['opened_ports']
closed_ports = result['closed_ports']
if opened_ports:
await p.send_priv_msg(
nick_from=service_id,
msg=f"[ {color_red}LOCAL_SCAN{nogc} ] {fullname} ({user.remote_ip}) : The Port(s) {opened_ports} are opened on this remote ip [{user.remote_ip}]",
channel=service_chanlog
)
if closed_ports:
await p.send_priv_msg(
nick_from=service_id,
msg=f"[ {color_red}LOCAL_SCAN{nogc} ] {fullname} ({user.remote_ip}) : The Port(s) {closed_ports} are closed on this remote ip [{user.remote_ip}]",
channel=service_chanlog
)
await asyncio.sleep(1)
for user_model in list_to_remove:
uplink.Schemas.DB_LOCALSCAN_USERS.remove(user_model)
await asyncio.sleep(1)
await asyncio.sleep(1.5)
except ValueError as ve:
uplink.ctx.Logs.debug(f"The value to remove is not in the list. {ve}")
except TimeoutError as te:
uplink.ctx.Logs.debug(f"Timeout Error {te}")
async def coro_psutil_scan(uplink: 'Defender'):
uplink.psutil_isRunning = True
service_id = uplink.ctx.Config.SERVICE_ID
service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG
color_red = uplink.ctx.Config.COLORS.red
nogc = uplink.ctx.Config.COLORS.nogc
p = uplink.ctx.Irc.Protocol
while uplink.psutil_isRunning:
try:
list_to_remove:list = []
for user in uplink.Schemas.DB_PSUTIL_USERS:
await uplink.mod_utils.action_scan_client_with_psutil(uplink, user)
result = await uplink.ctx.Base.create_thread_io(uplink.mod_utils.action_scan_client_with_psutil, uplink, user)
list_to_remove.append(user)
if not result:
continue
fullname = f'{user.nickname}!{user.username}@{user.hostname}'
await p.send_priv_msg(
nick_from=service_id,
msg=f"[ {color_red}PSUTIL_SCAN{nogc} ] {fullname} ({user.remote_ip}) is using ports {result}",
channel=service_chanlog
)
await asyncio.sleep(1)
for user_model in list_to_remove:
uplink.Schemas.DB_PSUTIL_USERS.remove(user_model)
await asyncio.sleep(1)
await asyncio.sleep(1.5)
except ValueError as ve:
uplink.ctx.Logs.debug(f"The value to remove is not in the list. {ve}")
except TimeoutError as te:
uplink.ctx.Logs.debug(f"Timeout Error {te}")
async def coro_autolimit(uplink: 'Defender'):
@@ -104,7 +266,6 @@ async def coro_autolimit(uplink: 'Defender'):
chan_list: list[str] = [c.name for c in uplink.ctx.Channel.UID_CHANNEL_DB]
while uplink.autolimit_isRunning:
if uplink.mod_config.autolimit == 0:
uplink.ctx.Logs.debug("autolimit deactivated ... stopping the current thread")
break
@@ -112,7 +273,7 @@ async def coro_autolimit(uplink: 'Defender'):
for chan in uplink.ctx.Channel.UID_CHANNEL_DB:
for chan_copy in chanObj_copy:
if chan_copy["name"] == chan.name and len(chan.uids) != chan_copy["uids_count"]:
await p.send2socket(f":{uplink.ctx.Config.SERVICE_ID} MODE {chan.name} +l {len(chan.uids) + uplink.mod_config.autolimit_amount}")
await p.send_set_mode('+l', channel_name=chan.name, params=len(chan.uids) + uplink.mod_config.autolimit_amount)
chan_copy["uids_count"] = len(chan.uids)
if chan.name not in chan_list:
@@ -128,13 +289,13 @@ async def coro_autolimit(uplink: 'Defender'):
# Si c'est la premiere execution
if INIT == 1:
for chan in uplink.ctx.Channel.UID_CHANNEL_DB:
await p.send2socket(f":{uplink.ctx.Config.SERVICE_ID} MODE {chan.name} +l {len(chan.uids) + uplink.mod_config.autolimit_amount}")
await p.send_set_mode('+l', channel_name=chan.name, params=len(chan.uids) + uplink.mod_config.autolimit_amount)
# Si le nouveau amount est différent de l'initial
if init_amount != uplink.mod_config.autolimit_amount:
init_amount = uplink.mod_config.autolimit_amount
for chan in uplink.ctx.Channel.UID_CHANNEL_DB:
await p.send2socket(f":{uplink.ctx.Config.SERVICE_ID} MODE {chan.name} +l {len(chan.uids) + uplink.mod_config.autolimit_amount}")
await p.send_set_mode('+l', channel_name=chan.name, params=len(chan.uids) + uplink.mod_config.autolimit_amount)
INIT = 0
@@ -142,7 +303,6 @@ async def coro_autolimit(uplink: 'Defender'):
await asyncio.sleep(uplink.mod_config.autolimit_interval)
for chan in uplink.ctx.Channel.UID_CHANNEL_DB:
# await p.send2socket(f":{uplink.ctx.Config.SERVICE_ID} MODE {chan.name} -l")
await p.send_set_mode('-l', channel_name=chan.name)
uplink.ctx.Irc.autolimit_started = False
@@ -158,7 +318,6 @@ async def coro_release_mode_mute(uplink: 'Defender', action: str, channel: str):
channel (str): The related channel
"""
service_id = uplink.ctx.Config.SERVICE_ID
timeout = uplink.mod_config.flood_timer
await asyncio.sleep(timeout)
@@ -169,6 +328,6 @@ async def coro_release_mode_mute(uplink: 'Defender', action: str, channel: str):
match action:
case 'mode-m':
# Action -m sur le salon
await uplink.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {channel} -m")
await uplink.ctx.Irc.Protocol.send_set_mode('-m', channel_name=channel)
case _:
pass

View File

@@ -267,6 +267,7 @@ async def handle_on_uid(uplink: 'Defender', srvmsg: list[str]):
####################
# [:<sid>] UID <uid> <ts> <nick> <real-host> <displayed-host> <real-user> <ip> <signon> <modes> [<mode-parameters>]+ :<real>
# [:<sid>] UID nickname hopcount timestamp username hostname uid servicestamp umodes virthost cloakedhost ip :gecos
async def action_on_flood(uplink: 'Defender', srvmsg: list[str]):
confmodel = uplink.mod_config
@@ -316,17 +317,16 @@ async def action_on_flood(uplink: 'Defender', srvmsg: list[str]):
fu.nbr_msg = 0
get_diff_secondes = unixtime - fu.first_msg_time
elif fu.nbr_msg > flood_message:
uplink.ctx.Logs.info('system de flood detecté')
await p.send_set_mode('+m', channel_name=channel)
await p.send_priv_msg(
nick_from=dnickname,
msg=f"{color_red} {color_bold} Flood detected. Apply the +m mode (Ô_o)",
channel=channel
)
await p.send2socket(f":{service_id} MODE {channel} +m")
uplink.ctx.Logs.info(f'FLOOD Détecté sur {get_detected_nickname} mode +m appliqué sur le salon {channel}')
uplink.ctx.Logs.debug(f'[FLOOD] {get_detected_nickname} triggered +m mode on the channel {channel}')
fu.nbr_msg = 0
fu.first_msg_time = unixtime
uplink.ctx.Base.create_asynctask(dthreads.coro_release_mode_mute(uplink, 'mode-m', channel))
uplink.ctx.Base.create_asynctask(uplink.Threads.coro_release_mode_mute(uplink, 'mode-m', channel))
async def action_add_reputation_sanctions(uplink: 'Defender', jailed_uid: str ):
@@ -336,10 +336,14 @@ async def action_add_reputation_sanctions(uplink: 'Defender', jailed_uid: str ):
confmodel = uplink.mod_config
get_reputation = uplink.ctx.Reputation.get_reputation(jailed_uid)
if get_reputation is None:
uplink.ctx.Logs.warning(f'UID {jailed_uid} has not been found')
return
return None
if get_reputation.isWebirc or get_reputation.isWebsocket:
uplink.ctx.Logs.debug(f'This nickname is exampted from the reputation system (Webirc or Websocket). {get_reputation.nickname} ({get_reputation.uid})')
uplink.ctx.Reputation.delete(get_reputation.uid)
return None
salon_logs = gconfig.SERVICE_CHANLOG
salon_jail = gconfig.SALON_JAIL
@@ -360,7 +364,7 @@ async def action_add_reputation_sanctions(uplink: 'Defender', jailed_uid: str ):
# Si le user ne vient pas de webIrc
await p.send_sajoin(nick_to_sajoin=jailed_nickname, channel_name=salon_jail)
await p.send_priv_msg(nick_from=gconfig.SERVICE_NICKNAME,
msg=f" [{color_red} REPUTATION {nogc}] : Connexion de {jailed_nickname} ({jailed_score}) ==> {salon_jail}",
msg=f" [ {color_red}REPUTATION{nogc} ]: The nickname {jailed_nickname} has been sent to {salon_jail} because his reputation score is ({jailed_score})",
channel=salon_logs
)
await p.send_notice(
@@ -371,7 +375,7 @@ async def action_add_reputation_sanctions(uplink: 'Defender', jailed_uid: str ):
if reputation_ban_all_chan == 1:
for chan in uplink.ctx.Channel.UID_CHANNEL_DB:
if chan.name != salon_jail:
await p.send2socket(f":{service_id} MODE {chan.name} +b {jailed_nickname}!*@*")
await p.send_set_mode('+b', channel_name=chan.name, params=f'{jailed_nickname}!*@*')
await p.send2socket(f":{service_id} KICK {chan.name} {jailed_nickname}")
uplink.ctx.Logs.info(f"[REPUTATION] {jailed_nickname} jailed (UID: {jailed_uid}, score: {jailed_score})")
@@ -419,14 +423,14 @@ async def action_apply_reputation_santions(uplink: 'Defender') -> None:
for chan in uplink.ctx.Channel.UID_CHANNEL_DB:
if chan.name != salon_jail and ban_all_chan == 1:
get_user_reputation = uplink.ctx.Reputation.get_reputation(uid)
await p.send2socket(f":{service_id} MODE {chan.name} -b {get_user_reputation.nickname}!*@*")
await p.send_set_mode('-b', channel_name=chan.name, params=f"{get_user_reputation.nickname}!*@*")
# Lorsqu'un utilisateur quitte, il doit être supprimé de {UID_DB}.
uplink.ctx.Channel.delete_user_from_all_channel(uid)
uplink.ctx.Reputation.delete(uid)
uplink.ctx.User.delete(uid)
async def action_scan_client_with_cloudfilt(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]:
def action_scan_client_with_cloudfilt(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]:
"""Analyse l'ip avec cloudfilt
Cette methode devra etre lancer toujours via un thread ou un timer.
Args:
@@ -438,11 +442,6 @@ async def action_scan_client_with_cloudfilt(uplink: 'Defender', user_model: 'MUs
"""
remote_ip = user_model.remote_ip
username = user_model.username
hostname = user_model.hostname
nickname = user_model.nickname
p = uplink.ctx.Irc.Protocol
if remote_ip in uplink.ctx.Config.WHITELISTED_IP:
return None
if uplink.mod_config.cloudfilt_scan == 0:
@@ -450,19 +449,11 @@ async def action_scan_client_with_cloudfilt(uplink: 'Defender', user_model: 'MUs
if uplink.cloudfilt_key == '':
return None
service_id = uplink.ctx.Config.SERVICE_ID
service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG
color_red = uplink.ctx.Config.COLORS.red
nogc = uplink.ctx.Config.COLORS.nogc
url = "https://developers18334.cloudfilt.com/"
data = {'ip': remote_ip, 'key': uplink.cloudfilt_key}
with requests.Session() as sess:
response = sess.post(url=url, data=data)
data = {
'ip': remote_ip,
'key': uplink.cloudfilt_key
}
response = requests.post(url=url, data=data)
# Formatted output
decoded_response: dict = loads(response.text)
status_code = response.status_code
@@ -472,30 +463,14 @@ async def action_scan_client_with_cloudfilt(uplink: 'Defender', user_model: 'MUs
result = {
'countryiso': decoded_response.get('countryiso', None),
'listed': decoded_response.get('listed', None),
'listed': decoded_response.get('listed', False),
'listed_by': decoded_response.get('listed_by', None),
'host': decoded_response.get('host', None)
}
# pseudo!ident@host
fullname = f'{nickname}!{username}@{hostname}'
await p.send_priv_msg(
nick_from=service_id,
msg=f"[ {color_red}CLOUDFILT_SCAN{nogc} ] : Connexion de {fullname} ({remote_ip}) ==> Host: {str(result['host'])} | country: {str(result['countryiso'])} | listed: {str(result['listed'])} | listed by : {str(result['listed_by'])}",
channel=service_chanlog)
uplink.ctx.Logs.debug(f"[CLOUDFILT SCAN] ({fullname}) connected from ({result['countryiso']}), Listed: {result['listed']}, by: {result['listed_by']}")
if result['listed']:
await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} Your connexion is listed as dangerous {str(result['listed'])} {str(result['listed_by'])} - detected by cloudfilt")
uplink.ctx.Logs.debug(f"[CLOUDFILT SCAN GLINE] Dangerous connection ({fullname}) from ({result['countryiso']}) Listed: {result['listed']}, by: {result['listed_by']}")
response.close()
return result
async def action_scan_client_with_freeipapi(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]:
def action_scan_client_with_freeipapi(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]:
"""Analyse l'ip avec Freeipapi
Cette methode devra etre lancer toujours via un thread ou un timer.
Args:
@@ -505,29 +480,16 @@ async def action_scan_client_with_freeipapi(uplink: 'Defender', user_model: 'MUs
dict[str, any] | None: les informations du provider
keys : 'countryCode', 'isProxy'
"""
p = uplink.ctx.Irc.Protocol
remote_ip = user_model.remote_ip
username = user_model.username
hostname = user_model.hostname
nickname = user_model.nickname
if remote_ip in uplink.ctx.Config.WHITELISTED_IP:
return None
if uplink.mod_config.freeipapi_scan == 0:
return None
service_id = uplink.ctx.Config.SERVICE_ID
service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG
color_red = uplink.ctx.Config.COLORS.red
nogc = uplink.ctx.Config.COLORS.nogc
with requests.Session() as sess:
url = f'https://freeipapi.com/api/json/{remote_ip}'
headers = {
'Accept': 'application/json',
}
response = requests.request(method='GET', url=url, headers=headers, timeout=uplink.timeout)
headers = {'Accept': 'application/json'}
response = sess.request(method='GET', url=url, headers=headers, timeout=uplink.timeout)
# Formatted output
decoded_response: dict = loads(response.text)
@@ -544,25 +506,9 @@ async def action_scan_client_with_freeipapi(uplink: 'Defender', user_model: 'MUs
'countryCode': decoded_response.get('countryCode', None),
'isProxy': decoded_response.get('isProxy', None)
}
# pseudo!ident@host
fullname = f'{nickname}!{username}@{hostname}'
await p.send_priv_msg(
nick_from=service_id,
msg=f"[ {color_red}FREEIPAPI_SCAN{nogc} ] : Connexion de {fullname} ({remote_ip}) ==> Proxy: {str(result['isProxy'])} | Country : {str(result['countryCode'])}",
channel=service_chanlog)
uplink.ctx.Logs.debug(f"[FREEIPAPI SCAN] ({fullname}) connected from ({result['countryCode']}), Proxy: {result['isProxy']}")
if result['isProxy']:
await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} This server do not allow proxy connexions {str(result['isProxy'])} - detected by freeipapi")
uplink.ctx.Logs.debug(f"[FREEIPAPI SCAN GLINE] Server do not allow proxy connexions {result['isProxy']}")
response.close()
return result
async def action_scan_client_with_abuseipdb(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]:
def action_scan_client_with_abuseipdb(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]:
"""Analyse l'ip avec AbuseIpDB
Cette methode devra etre lancer toujours via un thread ou un timer.
Args:
@@ -572,32 +518,28 @@ async def action_scan_client_with_abuseipdb(uplink: 'Defender', user_model: 'MUs
Returns:
dict[str, str] | None: les informations du provider
"""
p = uplink.ctx.Irc.Protocol
remote_ip = user_model.remote_ip
username = user_model.username
hostname = user_model.hostname
nickname = user_model.nickname
if remote_ip in uplink.ctx.Config.WHITELISTED_IP:
return None
if uplink.mod_config.abuseipdb_scan == 0:
return None
if uplink.abuseipdb_key == '':
return None
with requests.Session() as sess:
url = 'https://api.abuseipdb.com/api/v2/check'
querystring = {
'ipAddress': remote_ip,
'maxAgeInDays': '90'
}
querystring = {'ipAddress': remote_ip, 'maxAgeInDays': '90'}
headers = {
'Accept': 'application/json',
'Key': uplink.abuseipdb_key
}
response = requests.request(method='GET', url=url, headers=headers, params=querystring, timeout=uplink.timeout)
response = sess.request(method='GET', url=url, headers=headers, params=querystring, timeout=uplink.timeout)
if response.status_code != 200:
uplink.ctx.Logs.warning(f'status code = {str(response.status_code)}')
return None
# Formatted output
decoded_response: dict[str, dict] = loads(response.text)
@@ -612,81 +554,45 @@ async def action_scan_client_with_abuseipdb(uplink: 'Defender', user_model: 'MUs
'totalReports': decoded_response.get('data', {}).get('totalReports', 0)
}
service_id = uplink.ctx.Config.SERVICE_ID
service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG
color_red = uplink.ctx.Config.COLORS.red
nogc = uplink.ctx.Config.COLORS.nogc
# pseudo!ident@host
fullname = f'{nickname}!{username}@{hostname}'
await p.send_priv_msg(
nick_from=service_id,
msg=f"[ {color_red}ABUSEIPDB_SCAN{nogc} ] : Connexion de {fullname} ({remote_ip}) ==> Score: {str(result['score'])} | Country : {result['country']} | Tor : {str(result['isTor'])} | Total Reports : {str(result['totalReports'])}",
channel=service_chanlog
)
uplink.ctx.Logs.debug(f"[ABUSEIPDB SCAN] ({fullname}) connected from ({result['country']}), Score: {result['score']}, Tor: {result['isTor']}")
if result['isTor']:
await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} This server do not allow Tor connexions {str(result['isTor'])} - Detected by Abuseipdb")
uplink.ctx.Logs.debug(f"[ABUSEIPDB SCAN GLINE] Server do not allow Tor connections Tor: {result['isTor']}, Score: {result['score']}")
elif result['score'] >= 95:
await p.send2socket(f":{service_id} GLINE +*@{remote_ip} {uplink.ctx.Config.GLINE_DURATION} You were banned from this server because your abuse score is = {str(result['score'])} - Detected by Abuseipdb")
uplink.ctx.Logs.debug(f"[ABUSEIPDB SCAN GLINE] Server do not high risk connections Country: {result['country']}, Score: {result['score']}")
response.close()
return result
async def action_scan_client_with_local_socket(uplink: 'Defender', user_model: 'MUser'):
def action_scan_client_with_local_socket(uplink: 'Defender', user_model: 'MUser') -> Optional[dict[str, str]]:
"""local_scan
Args:
uplink (Defender): Defender instance object
user_model (MUser): l'objet User qui contient l'ip
"""
p = uplink.ctx.Irc.Protocol
remote_ip = user_model.remote_ip
username = user_model.username
hostname = user_model.hostname
nickname = user_model.nickname
fullname = f'{nickname}!{username}@{hostname}'
if remote_ip in uplink.ctx.Config.WHITELISTED_IP:
return None
result = {'opened_ports': [], 'closed_ports': []}
for port in uplink.ctx.Config.PORTS_TO_SCAN:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM or socket.SOCK_NONBLOCK) as sock:
try:
newSocket = ''
newSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM or socket.SOCK_NONBLOCK)
newSocket.settimeout(0.5)
sock.settimeout(0.5)
connection = (remote_ip, uplink.ctx.Base.int_if_possible(port))
newSocket.connect(connection)
sock.connect(connection)
await p.send_priv_msg(
nick_from=uplink.ctx.Config.SERVICE_NICKNAME,
msg=f"[ {uplink.ctx.Config.COLORS.red}PROXY_SCAN{uplink.ctx.Config.COLORS.nogc} ] {fullname} ({remote_ip}) : Port [{str(port)}] ouvert sur l'adresse ip [{remote_ip}]",
channel=uplink.ctx.Config.SERVICE_CHANLOG
)
# print(f"=======> Le port {str(port)} est ouvert !!")
uplink.ctx.Base.running_sockets.append(newSocket)
# print(newSocket)
newSocket.shutdown(socket.SHUT_RDWR)
newSocket.close()
result['opened_ports'].append(port)
uplink.ctx.Base.running_sockets.append(sock)
sock.shutdown(socket.SHUT_RDWR)
uplink.ctx.Base.running_sockets.remove(sock)
return result
except (socket.timeout, ConnectionRefusedError):
uplink.ctx.Logs.info(f"Le port {remote_ip}:{str(port)} est fermé")
uplink.ctx.Logs.debug(f"[LOCAL SCAN] Port {remote_ip}:{str(port)} is close.")
result['closed_ports'].append(port)
except AttributeError as ae:
uplink.ctx.Logs.warning(f"AttributeError ({remote_ip}): {ae}")
except socket.gaierror as err:
uplink.ctx.Logs.warning(f"Address Info Error ({remote_ip}): {err}")
finally:
# newSocket.shutdown(socket.SHUT_RDWR)
newSocket.close()
uplink.ctx.Logs.info('=======> Fermeture de la socket')
async def action_scan_client_with_psutil(uplink: 'Defender', user_model: 'MUser') -> list[int]:
return result
def action_scan_client_with_psutil(uplink: 'Defender', user_model: 'MUser') -> list[int]:
"""psutil_scan for Linux (should be run on the same location as the unrealircd server)
Args:
@@ -695,28 +601,16 @@ async def action_scan_client_with_psutil(uplink: 'Defender', user_model: 'MUser'
Returns:
list[int]: list of ports
"""
p = uplink.ctx.Irc.Protocol
remote_ip = user_model.remote_ip
username = user_model.username
hostname = user_model.hostname
nickname = user_model.nickname
if remote_ip in uplink.ctx.Config.WHITELISTED_IP:
return None
if uplink.mod_config.psutil_scan == 0:
return None
try:
connections = psutil.net_connections(kind='inet')
fullname = f'{nickname}!{username}@{hostname}'
matching_ports = [conn.raddr.port for conn in connections if conn.raddr and conn.raddr.ip == remote_ip]
uplink.ctx.Logs.info(f"Connexion of {fullname} ({remote_ip}) using ports : {str(matching_ports)}")
if matching_ports:
await p.send_priv_msg(
nick_from=uplink.ctx.Config.SERVICE_NICKNAME,
msg=f"[ {uplink.ctx.Config.COLORS.red}PSUTIL_SCAN{uplink.ctx.Config.COLORS.black} ] {fullname} ({remote_ip}) : is using ports {matching_ports}",
channel=uplink.ctx.Config.SERVICE_CHANLOG
)
uplink.ctx.Logs.debug(f"Connexion of ({remote_ip}) using ports : {str(matching_ports)}")
return matching_ports

View File

@@ -1,4 +1,3 @@
import asyncio
from typing import TYPE_CHECKING
if TYPE_CHECKING: