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 os
import re import re
import json import json
import time
import socket import socket
import threading import threading
import ipaddress import ipaddress
import ast import ast
import requests import requests
import concurrent.futures
from dataclasses import fields 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 base64 import b64decode, b64encode
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
import core.definition as dfn
if TYPE_CHECKING: if TYPE_CHECKING:
from core.loader import Loader from core.loader import Loader
@@ -36,12 +37,15 @@ class Base:
# Liste des threads en cours # Liste des threads en cours
self.running_threads: list[threading.Thread] = self.Settings.RUNNING_THREADS 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 # Les sockets ouvert
self.running_sockets: list[socket.socket] = self.Settings.RUNNING_SOCKETS 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 # Liste des fonctions en attentes
self.periodic_func: dict[object] = self.Settings.PERIODIC_FUNC self.periodic_func: dict[object] = self.Settings.PERIODIC_FUNC
@@ -71,7 +75,6 @@ class Base:
return None return None
def __get_latest_defender_version(self) -> None: def __get_latest_defender_version(self) -> None:
try:
self.logs.debug(f'-- Looking for a new version available on Github') self.logs.debug(f'-- Looking for a new version available on Github')
token = '' token = ''
json_url = f'https://raw.githubusercontent.com/adator85/DEFENDER/main/version.json' json_url = f'https://raw.githubusercontent.com/adator85/DEFENDER/main/version.json'
@@ -80,17 +83,18 @@ class Base:
'Accept': 'application/vnd.github.v3.raw' # Indique à GitHub que nous voulons le contenu brut du fichier 'Accept': 'application/vnd.github.v3.raw' # Indique à GitHub que nous voulons le contenu brut du fichier
} }
with requests.Session() as sess:
try:
if token == '': if token == '':
response = requests.get(json_url, timeout=self.Config.API_TIMEOUT) response = sess.get(json_url, timeout=self.Config.API_TIMEOUT)
else: 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 response.raise_for_status() # Vérifie si la requête a réussi
json_response:dict = response.json() json_response:dict = response.json()
# self.LATEST_DEFENDER_VERSION = json_response["version"] self.Config.LATEST_VERSION = json_response.get('version', '')
self.Config.LATEST_VERSION = json_response['version']
return None return None
except requests.HTTPError as err: except requests.HTTPError as err:
self.logs.error(f'Github not available to fetch latest version: {err}') self.logs.error(f'Github not available to fetch latest version: {err}')
except: except:
@@ -359,8 +363,8 @@ class Base:
except Exception as err: except Exception as err:
self.logs.error(err, exc_info=True) self.logs.error(err, exc_info=True)
def create_asynctask(self, func: Any, *, async_name: str = None, run_once: bool = False) -> asyncio.Task: 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_asynctasks variable """Create a new asynchrone and store it into running_iotasks variable
Args: Args:
func (Callable): The function you want to call in asynchrone way 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 = asyncio.create_task(func, name=name)
task.add_done_callback(self.asynctask_done) 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 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 """Log task when done
Args: Args:
task (asyncio.Task): The Asyncio Task callback 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: try:
if task.exception(): 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: 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: 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: 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: def is_thread_alive(self, thread_name: str) -> bool:
"""Check if the thread is still running! using the is_alive method of Threads. """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.running_sockets.remove(soc)
self.logs.debug(f"-- Socket ==> closed {str(soc.fileno())}") 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]: def db_init(self) -> tuple[Engine, Connection]:
db_directory = self.Config.DB_PATH db_directory = self.Config.DB_PATH

View File

@@ -1,3 +1,4 @@
import asyncio
import importlib import importlib
import sys import sys
import time import time
@@ -26,7 +27,6 @@ REHASH_MODULES = [
'core.classes.protocols.inspircd' 'core.classes.protocols.inspircd'
] ]
async def restart_service(uplink: 'Loader', reason: str = "Restarting with no reason!") -> None: 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"] need_a_restart = ["SERVEUR_ID"]
uplink.Settings.set_cache('db_commands', uplink.Commands.DB_COMMANDS) uplink.Settings.set_cache('db_commands', uplink.Commands.DB_COMMANDS)
await uplink.RpcServer.stop_server() await uplink.RpcServer.stop_rpc_server()
restart_flag = False restart_flag = False
config_model_bakcup = uplink.Config config_model_bakcup = uplink.Config
@@ -122,10 +122,68 @@ async def rehash_service(uplink: 'Loader', nickname: str) -> None:
uplink.Irc.Protocol.register_command() uplink.Irc.Protocol.register_command()
uplink.RpcServer = uplink.RpcServerModule.JSonRpcServer(uplink) 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 # Reload Service modules
for module in uplink.ModuleUtils.model_get_loaded_modules().copy(): for module in uplink.ModuleUtils.model_get_loaded_modules().copy():
await uplink.ModuleUtils.reload_one_module(module.module_name, nickname) await uplink.ModuleUtils.reload_one_module(module.module_name, nickname)
return None 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: 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._ctx = context
self.live: bool = False self.live: bool = False
self.host = hostname self.host = context.Config.RPC_HOST
self.port = port self.port = context.Config.RPC_PORT
self.routes: list[Route] = [] self.routes: list[Route] = []
self.server: Optional[uvicorn.Server] = None self.server: Optional[uvicorn.Server] = None
@@ -34,12 +34,12 @@ class JSonRpcServer:
'command.get.by.module': RPCCommand(context).command_get_by_module '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: if not self.live:
self.routes = [Route('/api', self.request_handler, methods=['POST'])] self.routes = [Route('/api', self.request_handler, methods=['POST'])]
self.app_jsonrpc = Starlette(debug=False, routes=self.routes) 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.server = uvicorn.Server(config)
self.live = True self.live = True
await self._ctx.Irc.Protocol.send_priv_msg( await self._ctx.Irc.Protocol.send_priv_msg(
@@ -52,7 +52,7 @@ class JSonRpcServer:
else: else:
self._ctx.Logs.debug("Server already running") self._ctx.Logs.debug("Server already running")
async def stop_server(self): async def stop_rpc_server(self):
if self.server: if self.server:
self.server.should_exit = True self.server.should_exit = True

View File

@@ -126,7 +126,11 @@ class Irc:
async def listen(self): async def listen(self):
self.ctx.Base.create_asynctask( 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: while self.signal:
@@ -343,8 +347,13 @@ class Irc:
async def thread_check_for_new_version(self, fromuser: str) -> None: async def thread_check_for_new_version(self, fromuser: str) -> None:
dnickname = self.ctx.Config.SERVICE_NICKNAME 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=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") await self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=" Please run (git pull origin main) in the current folder")
else: else:

View File

@@ -81,7 +81,7 @@ class Loader:
self.PFactory: factory.ProtocolFactorty = factory.ProtocolFactorty(self) 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__)) self.Logs.debug(self.Utils.tr("Loader %s success", __name__))

View File

@@ -1,4 +1,5 @@
from dataclasses import dataclass from dataclasses import dataclass
import logging
from typing import Any, TYPE_CHECKING, Optional from typing import Any, TYPE_CHECKING, Optional
from core.classes.interfaces.imodule import IModule from core.classes.interfaces.imodule import IModule
import mods.defender.schemas as schemas import mods.defender.schemas as schemas
@@ -26,6 +27,8 @@ class Defender(IModule):
def __init__(self, context: 'Loader') -> None: def __init__(self, context: 'Loader') -> None:
super().__init__(context) super().__init__(context)
self._mod_config: Optional[schemas.ModConfModel] = None self._mod_config: Optional[schemas.ModConfModel] = None
self.Schemas = schemas.RepDB()
self.Threads = thds
@property @property
def mod_config(self) -> ModConfModel: def mod_config(self) -> ModConfModel:
@@ -55,40 +58,43 @@ class Defender(IModule):
return None return None
async def load(self): async def load(self):
# Variable qui va contenir les options de configuration du module Defender # Variable qui va contenir les options de configuration du module Defender
self._mod_config: schemas.ModConfModel = self.ModConfModel() self._mod_config: schemas.ModConfModel = self.ModConfModel()
# sync the database with local variable (Mandatory) # sync the database with local variable (Mandatory)
await self.sync_db() await self.sync_db()
# Add module schemas
self.Schemas = schemas
# Add module utils functions # Add module utils functions
self.mod_utils = utils self.mod_utils = utils
# Create module commands (Mandatory) # Create module commands (Mandatory)
self.ctx.Irc.build_command(0, self.module_name, 'code', 'Display the code or key for access') self.ctx.Commands.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.Commands.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.Commands.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.Commands.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.Commands.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.Commands.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.Commands.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.Commands.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(3, self.module_name, 'sentinel', 'Monitor and guard the channel or server')
self.timeout = self.ctx.Config.API_TIMEOUT self.timeout = self.ctx.Config.API_TIMEOUT
# Listes qui vont contenir les ip a scanner avec les différentes API # 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_ABUSEIPDB_USERS = []
self.Schemas.DB_PSUTIL_USERS = self.Schemas.DB_LOCALSCAN_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 # Variables qui indique que les threads sont en cours d'éxecutions
self.abuseipdb_isRunning = self.freeipapi_isRunning = self.cloudfilt_isRunning = True self.abuseipdb_isRunning = True if self.mod_config.abuseipdb_scan == 1 else False
self.psutil_isRunning = self.localscan_isRunning = self.reputationTimer_isRunning = True self.freeipapi_isRunning = True if self.mod_config.freeipapi_scan == 1 else False
self.autolimit_isRunning = True 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 # Variable qui va contenir les users
self.flood_system = {} self.flood_system = {}
@@ -101,19 +107,21 @@ class Defender(IModule):
self.cloudfilt_key = 'r1gEtjtfgRQjtNBDMxsg' self.cloudfilt_key = 'r1gEtjtfgRQjtNBDMxsg'
# Démarrer les threads pour démarrer les api # Démarrer les threads pour démarrer les api
self.ctx.Base.create_asynctask(thds.coro_freeipapi_scan(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(thds.coro_cloudfilt_scan(self)) 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(thds.coro_abuseipdb_scan(self)) 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(thds.coro_local_scan(self)) 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(thds.coro_psutil_scan(self)) 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(thds.coro_apply_reputation_sanctions(self)) 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.autolimit == 1:
self.ctx.Base.create_asynctask(thds.coro_autolimit(self))
if self.mod_config.reputation == 1: if self.mod_config.reputation == 1:
await self.ctx.Irc.Protocol.send_sjoin(self.ctx.Config.SALON_JAIL) 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}") 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): def __onload(self):
@@ -138,7 +146,7 @@ class Defender(IModule):
if localscan: if localscan:
self.Schemas.DB_LOCALSCAN_USERS = 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 """Cette methode sera executée a chaque désactivation ou
rechargement de module rechargement de module
""" """
@@ -158,6 +166,13 @@ class Defender(IModule):
self.ctx.Commands.drop_command_by_module(self.module_name) 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 return None
async def insert_db_trusted(self, uid: str, nickname:str) -> 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: 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) 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) 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_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.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} 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: if self.mod_config.reputation_sg == 1:
for chan in self.ctx.Channel.UID_CHANNEL_DB: for chan in self.ctx.Channel.UID_CHANNEL_DB:
if chan.name != jail_chan: 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.send_set_mode('+b', channel_name=chan.name, params='~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(
'+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) await self.ctx.Channel.db_query_channel('add', self.module_name, jail_chan)
@@ -441,20 +460,26 @@ class Defender(IModule):
return False return False
await self.update_configuration(key, 0) await self.update_configuration(key, 0)
self.reputationTimer_isRunning = False
await self.ctx.Irc.Protocol.send_priv_msg( await self.ctx.Irc.Protocol.send_priv_msg(
nick_from=dnickname, nick_from=dnickname,
msg=f"[ {self.ctx.Config.COLORS.red}REPUTATION{self.ctx.Config.COLORS.black} ] : Deactivated by {fromuser}", msg=f"[ {self.ctx.Config.COLORS.red}REPUTATION{self.ctx.Config.COLORS.black} ] : Deactivated by {fromuser}",
channel=dchanlog 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} SAMODE {jail_chan} -{dumodes} {dnickname}")
await self.ctx.Irc.Protocol.send2socket(f":{service_id} MODE {jail_chan} -sS") await self.ctx.Irc.Protocol.send_set_mode('-sS', channel_name=jail_chan)
await self.ctx.Irc.Protocol.send2socket(f":{service_id} PART {jail_chan}") await self.ctx.Irc.Protocol.send_part_chan(service_id, jail_chan)
for chan in self.ctx.Channel.UID_CHANNEL_DB: for chan in self.ctx.Channel.UID_CHANNEL_DB:
if chan.name != jail_chan: 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.send_set_mode('-b', channel_name=chan.name, params='~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(
'-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) await self.ctx.Channel.db_query_channel('del', self.module_name, jail_chan)
@@ -464,12 +489,17 @@ class Defender(IModule):
match get_options: match get_options:
case 'release': case 'release':
# .reputation release [nick] # .reputation release [nick]
p = await self.ctx.Irc.Protocol
link = self.ctx.Config.SERVEUR_LINK link = self.ctx.Config.SERVEUR_LINK
jailed_salon = self.ctx.Config.SALON_JAIL jailed_salon = self.ctx.Config.SALON_JAIL
welcome_salon = self.ctx.Config.SALON_LIBERER welcome_salon = self.ctx.Config.SALON_LIBERER
client_obj = self.ctx.User.get_user(str(cmd[2])) 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: if client_obj is None:
await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname, await self.ctx.Irc.Protocol.send_notice(nick_from=dnickname,
nick_to=fromuser, 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) 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 return None
self.ctx.Base.create_asynctask(self.Threads.coro_local_scan(self))
await self.update_configuration(option, 1) 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) 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 return None
await self.update_configuration(option, 0) 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) 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) 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 return None
self.ctx.Base.create_asynctask(self.Threads.coro_psutil_scan(self))
await self.update_configuration(option, 1) 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) 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 return None
await self.update_configuration(option, 0) 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) 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) 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 return None
self.ctx.Base.create_asynctask(self.Threads.coro_abuseipdb_scan(self))
await self.update_configuration(option, 1) 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) 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 return None
await self.update_configuration(option, 0) 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) 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) 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 return None
self.ctx.Base.create_asynctask(self.Threads.coro_freeipapi_scan(self))
await self.update_configuration(option, 1) 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) 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 return None
await self.update_configuration(option, 0) 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) 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) 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 return None
self.ctx.Base.create_asynctask(self.Threads.coro_cloudfilt_scan(self))
await self.update_configuration(option, 1) 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) 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 return None
await self.update_configuration(option, 0) 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) 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() await self.join_saved_channels()
return None return None
case _:
pass

View File

@@ -28,6 +28,8 @@ class FloodUser(MainModel):
nbr_msg: int = 0 nbr_msg: int = 0
first_msg_time: int = 0 first_msg_time: int = 0
class RepDB:
DB_FLOOD_USERS: list[FloodUser] = [] DB_FLOOD_USERS: list[FloodUser] = []
DB_ABUSEIPDB_USERS: list[MUser] = [] DB_ABUSEIPDB_USERS: list[MUser] = []
DB_FREEIPAPI_USERS: list[MUser] = [] DB_FREEIPAPI_USERS: list[MUser] = []

View File

@@ -1,89 +1,251 @@
import asyncio import asyncio
from typing import TYPE_CHECKING from typing import TYPE_CHECKING, Optional
from time import sleep
if TYPE_CHECKING: if TYPE_CHECKING:
from mods.defender.mod_defender import Defender from mods.defender.mod_defender import Defender
async def coro_apply_reputation_sanctions(uplink: 'Defender'): async def coro_apply_reputation_sanctions(uplink: 'Defender'):
uplink.reputationTimer_isRunning = True
while uplink.reputationTimer_isRunning: while uplink.reputationTimer_isRunning:
await uplink.mod_utils.action_apply_reputation_santions(uplink) await uplink.mod_utils.action_apply_reputation_santions(uplink)
await asyncio.sleep(5) await asyncio.sleep(5)
async def coro_cloudfilt_scan(uplink: 'Defender'): 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: while uplink.cloudfilt_isRunning:
try:
list_to_remove:list = [] list_to_remove:list = []
for user in uplink.Schemas.DB_CLOUDFILT_USERS: 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) 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) await asyncio.sleep(1)
for user_model in list_to_remove: for user_model in list_to_remove:
uplink.Schemas.DB_CLOUDFILT_USERS.remove(user_model) 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'): 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: while uplink.freeipapi_isRunning:
try:
list_to_remove: list = [] list_to_remove: list = []
for user in uplink.Schemas.DB_FREEIPAPI_USERS: 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) list_to_remove.append(user)
await asyncio.sleep(1) await asyncio.sleep(1)
# remove users from the list
for user_model in list_to_remove: for user_model in list_to_remove:
uplink.Schemas.DB_FREEIPAPI_USERS.remove(user_model) 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'): 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 = [] list_to_remove: list = []
print(uplink.Schemas.DB_ABUSEIPDB_USERS)
for user in 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) 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) await asyncio.sleep(1)
print(list_to_remove)
for user_model in list_to_remove: for user_model in list_to_remove:
uplink.Schemas.DB_ABUSEIPDB_USERS.remove(user_model) 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'): 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: while uplink.localscan_isRunning:
try:
list_to_remove:list = [] list_to_remove:list = []
for user in uplink.Schemas.DB_LOCALSCAN_USERS: 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) 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) await asyncio.sleep(1)
for user_model in list_to_remove: for user_model in list_to_remove:
uplink.Schemas.DB_LOCALSCAN_USERS.remove(user_model) 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'): 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: while uplink.psutil_isRunning:
try:
list_to_remove:list = [] list_to_remove:list = []
for user in uplink.Schemas.DB_PSUTIL_USERS: 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) 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) await asyncio.sleep(1)
for user_model in list_to_remove: for user_model in list_to_remove:
uplink.Schemas.DB_PSUTIL_USERS.remove(user_model) 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'): 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] chan_list: list[str] = [c.name for c in uplink.ctx.Channel.UID_CHANNEL_DB]
while uplink.autolimit_isRunning: while uplink.autolimit_isRunning:
if uplink.mod_config.autolimit == 0: if uplink.mod_config.autolimit == 0:
uplink.ctx.Logs.debug("autolimit deactivated ... stopping the current thread") uplink.ctx.Logs.debug("autolimit deactivated ... stopping the current thread")
break break
@@ -112,7 +273,7 @@ async def coro_autolimit(uplink: 'Defender'):
for chan in uplink.ctx.Channel.UID_CHANNEL_DB: for chan in uplink.ctx.Channel.UID_CHANNEL_DB:
for chan_copy in chanObj_copy: for chan_copy in chanObj_copy:
if chan_copy["name"] == chan.name and len(chan.uids) != chan_copy["uids_count"]: 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) chan_copy["uids_count"] = len(chan.uids)
if chan.name not in chan_list: if chan.name not in chan_list:
@@ -128,13 +289,13 @@ async def coro_autolimit(uplink: 'Defender'):
# Si c'est la premiere execution # Si c'est la premiere execution
if INIT == 1: if INIT == 1:
for chan in uplink.ctx.Channel.UID_CHANNEL_DB: 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 # Si le nouveau amount est différent de l'initial
if init_amount != uplink.mod_config.autolimit_amount: if init_amount != uplink.mod_config.autolimit_amount:
init_amount = uplink.mod_config.autolimit_amount init_amount = uplink.mod_config.autolimit_amount
for chan in uplink.ctx.Channel.UID_CHANNEL_DB: 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 INIT = 0
@@ -142,7 +303,6 @@ async def coro_autolimit(uplink: 'Defender'):
await asyncio.sleep(uplink.mod_config.autolimit_interval) await asyncio.sleep(uplink.mod_config.autolimit_interval)
for chan in uplink.ctx.Channel.UID_CHANNEL_DB: 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) await p.send_set_mode('-l', channel_name=chan.name)
uplink.ctx.Irc.autolimit_started = False 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 channel (str): The related channel
""" """
service_id = uplink.ctx.Config.SERVICE_ID
timeout = uplink.mod_config.flood_timer timeout = uplink.mod_config.flood_timer
await asyncio.sleep(timeout) await asyncio.sleep(timeout)
@@ -169,6 +328,6 @@ async def coro_release_mode_mute(uplink: 'Defender', action: str, channel: str):
match action: match action:
case 'mode-m': case 'mode-m':
# Action -m sur le salon # 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 _: case _:
pass 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 <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 # [:<sid>] UID nickname hopcount timestamp username hostname uid servicestamp umodes virthost cloakedhost ip :gecos
async def action_on_flood(uplink: 'Defender', srvmsg: list[str]): async def action_on_flood(uplink: 'Defender', srvmsg: list[str]):
confmodel = uplink.mod_config confmodel = uplink.mod_config
@@ -316,17 +317,16 @@ async def action_on_flood(uplink: 'Defender', srvmsg: list[str]):
fu.nbr_msg = 0 fu.nbr_msg = 0
get_diff_secondes = unixtime - fu.first_msg_time get_diff_secondes = unixtime - fu.first_msg_time
elif fu.nbr_msg > flood_message: 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( await p.send_priv_msg(
nick_from=dnickname, nick_from=dnickname,
msg=f"{color_red} {color_bold} Flood detected. Apply the +m mode (Ô_o)", msg=f"{color_red} {color_bold} Flood detected. Apply the +m mode (Ô_o)",
channel=channel channel=channel
) )
await p.send2socket(f":{service_id} MODE {channel} +m") uplink.ctx.Logs.debug(f'[FLOOD] {get_detected_nickname} triggered +m mode on the channel {channel}')
uplink.ctx.Logs.info(f'FLOOD Détecté sur {get_detected_nickname} mode +m appliqué sur le salon {channel}')
fu.nbr_msg = 0 fu.nbr_msg = 0
fu.first_msg_time = unixtime 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 ): 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 confmodel = uplink.mod_config
get_reputation = uplink.ctx.Reputation.get_reputation(jailed_uid) get_reputation = uplink.ctx.Reputation.get_reputation(jailed_uid)
if get_reputation is None: if get_reputation is None:
uplink.ctx.Logs.warning(f'UID {jailed_uid} has not been found') 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_logs = gconfig.SERVICE_CHANLOG
salon_jail = gconfig.SALON_JAIL 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 # Si le user ne vient pas de webIrc
await p.send_sajoin(nick_to_sajoin=jailed_nickname, channel_name=salon_jail) await p.send_sajoin(nick_to_sajoin=jailed_nickname, channel_name=salon_jail)
await p.send_priv_msg(nick_from=gconfig.SERVICE_NICKNAME, 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 channel=salon_logs
) )
await p.send_notice( 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: if reputation_ban_all_chan == 1:
for chan in uplink.ctx.Channel.UID_CHANNEL_DB: for chan in uplink.ctx.Channel.UID_CHANNEL_DB:
if chan.name != salon_jail: 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}") 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})") 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: for chan in uplink.ctx.Channel.UID_CHANNEL_DB:
if chan.name != salon_jail and ban_all_chan == 1: if chan.name != salon_jail and ban_all_chan == 1:
get_user_reputation = uplink.ctx.Reputation.get_reputation(uid) 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}. # Lorsqu'un utilisateur quitte, il doit être supprimé de {UID_DB}.
uplink.ctx.Channel.delete_user_from_all_channel(uid) uplink.ctx.Channel.delete_user_from_all_channel(uid)
uplink.ctx.Reputation.delete(uid) uplink.ctx.Reputation.delete(uid)
uplink.ctx.User.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 """Analyse l'ip avec cloudfilt
Cette methode devra etre lancer toujours via un thread ou un timer. Cette methode devra etre lancer toujours via un thread ou un timer.
Args: Args:
@@ -438,11 +442,6 @@ async def action_scan_client_with_cloudfilt(uplink: 'Defender', user_model: 'MUs
""" """
remote_ip = user_model.remote_ip 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: if remote_ip in uplink.ctx.Config.WHITELISTED_IP:
return None return None
if uplink.mod_config.cloudfilt_scan == 0: 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 == '': if uplink.cloudfilt_key == '':
return None 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/" 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 # Formatted output
decoded_response: dict = loads(response.text) decoded_response: dict = loads(response.text)
status_code = response.status_code status_code = response.status_code
@@ -472,30 +463,14 @@ async def action_scan_client_with_cloudfilt(uplink: 'Defender', user_model: 'MUs
result = { result = {
'countryiso': decoded_response.get('countryiso', None), '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), 'listed_by': decoded_response.get('listed_by', None),
'host': decoded_response.get('host', 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 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 """Analyse l'ip avec Freeipapi
Cette methode devra etre lancer toujours via un thread ou un timer. Cette methode devra etre lancer toujours via un thread ou un timer.
Args: 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 dict[str, any] | None: les informations du provider
keys : 'countryCode', 'isProxy' keys : 'countryCode', 'isProxy'
""" """
p = uplink.ctx.Irc.Protocol
remote_ip = user_model.remote_ip 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: if remote_ip in uplink.ctx.Config.WHITELISTED_IP:
return None return None
if uplink.mod_config.freeipapi_scan == 0: if uplink.mod_config.freeipapi_scan == 0:
return None return None
service_id = uplink.ctx.Config.SERVICE_ID with requests.Session() as sess:
service_chanlog = uplink.ctx.Config.SERVICE_CHANLOG
color_red = uplink.ctx.Config.COLORS.red
nogc = uplink.ctx.Config.COLORS.nogc
url = f'https://freeipapi.com/api/json/{remote_ip}' url = f'https://freeipapi.com/api/json/{remote_ip}'
headers = {'Accept': 'application/json'}
headers = { response = sess.request(method='GET', url=url, headers=headers, timeout=uplink.timeout)
'Accept': 'application/json',
}
response = requests.request(method='GET', url=url, headers=headers, timeout=uplink.timeout)
# Formatted output # Formatted output
decoded_response: dict = loads(response.text) 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), 'countryCode': decoded_response.get('countryCode', None),
'isProxy': decoded_response.get('isProxy', 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 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 """Analyse l'ip avec AbuseIpDB
Cette methode devra etre lancer toujours via un thread ou un timer. Cette methode devra etre lancer toujours via un thread ou un timer.
Args: Args:
@@ -572,32 +518,28 @@ async def action_scan_client_with_abuseipdb(uplink: 'Defender', user_model: 'MUs
Returns: Returns:
dict[str, str] | None: les informations du provider dict[str, str] | None: les informations du provider
""" """
p = uplink.ctx.Irc.Protocol
remote_ip = user_model.remote_ip 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: if remote_ip in uplink.ctx.Config.WHITELISTED_IP:
return None return None
if uplink.mod_config.abuseipdb_scan == 0: if uplink.mod_config.abuseipdb_scan == 0:
return None return None
if uplink.abuseipdb_key == '': if uplink.abuseipdb_key == '':
return None return None
with requests.Session() as sess:
url = 'https://api.abuseipdb.com/api/v2/check' url = 'https://api.abuseipdb.com/api/v2/check'
querystring = { querystring = {'ipAddress': remote_ip, 'maxAgeInDays': '90'}
'ipAddress': remote_ip,
'maxAgeInDays': '90'
}
headers = { headers = {
'Accept': 'application/json', 'Accept': 'application/json',
'Key': uplink.abuseipdb_key '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 # Formatted output
decoded_response: dict[str, dict] = loads(response.text) 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) '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 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 """local_scan
Args: Args:
uplink (Defender): Defender instance object uplink (Defender): Defender instance object
user_model (MUser): l'objet User qui contient l'ip user_model (MUser): l'objet User qui contient l'ip
""" """
p = uplink.ctx.Irc.Protocol
remote_ip = user_model.remote_ip 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: if remote_ip in uplink.ctx.Config.WHITELISTED_IP:
return None return None
result = {'opened_ports': [], 'closed_ports': []}
for port in uplink.ctx.Config.PORTS_TO_SCAN: 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: try:
newSocket = '' sock.settimeout(0.5)
newSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM or socket.SOCK_NONBLOCK)
newSocket.settimeout(0.5)
connection = (remote_ip, uplink.ctx.Base.int_if_possible(port)) connection = (remote_ip, uplink.ctx.Base.int_if_possible(port))
newSocket.connect(connection) sock.connect(connection)
await p.send_priv_msg( result['opened_ports'].append(port)
nick_from=uplink.ctx.Config.SERVICE_NICKNAME, uplink.ctx.Base.running_sockets.append(sock)
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}]", sock.shutdown(socket.SHUT_RDWR)
channel=uplink.ctx.Config.SERVICE_CHANLOG uplink.ctx.Base.running_sockets.remove(sock)
) return result
# print(f"=======> Le port {str(port)} est ouvert !!")
uplink.ctx.Base.running_sockets.append(newSocket)
# print(newSocket)
newSocket.shutdown(socket.SHUT_RDWR)
newSocket.close()
except (socket.timeout, ConnectionRefusedError): 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: except AttributeError as ae:
uplink.ctx.Logs.warning(f"AttributeError ({remote_ip}): {ae}") uplink.ctx.Logs.warning(f"AttributeError ({remote_ip}): {ae}")
except socket.gaierror as err: except socket.gaierror as err:
uplink.ctx.Logs.warning(f"Address Info Error ({remote_ip}): {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) """psutil_scan for Linux (should be run on the same location as the unrealircd server)
Args: Args:
@@ -695,28 +601,16 @@ async def action_scan_client_with_psutil(uplink: 'Defender', user_model: 'MUser'
Returns: Returns:
list[int]: list of ports list[int]: list of ports
""" """
p = uplink.ctx.Irc.Protocol
remote_ip = user_model.remote_ip 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: if remote_ip in uplink.ctx.Config.WHITELISTED_IP:
return None return None
if uplink.mod_config.psutil_scan == 0:
return None
try: try:
connections = psutil.net_connections(kind='inet') 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] 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)}") uplink.ctx.Logs.debug(f"Connexion of ({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
)
return matching_ports return matching_ports

View File

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