# 🌐 GNUWorld — Servicii IRC pentru Undernet/P10 ## Prezentare Generală **GNUWorld** este un cadru (framework) de servicii IRC scris în **C++**, proiectat pentru rețele bazate pe protocolul **P10** (ircu/Undernet). Arhitectura este modulară, permițând încărcarea dinamică a clienților de servicii (CService, CControl, ChanFix, DroneScan etc.) prin intermediul bibliotecii `libtool` (`lt_dlopen`). Proiectul este o adaptare și extensie a GNUWorld original, personalizat pentru nevoile rețelei **Underchat**. --- ## 📐 Arhitectura Generală ### Ierarhia de Clase Principale ``` NetworkTarget ← Clasa de bază (adresare numerică P10 base64: YY + XXX) ├── iClient ← Utilizator de rețea (nick, user, host, moduri, canale) ├── iServer ← Server de rețea (uplink, stare burst, flag-uri) ├── xClient (+ TimerHandler) ← Client de servicii (bază pentru module: cservice, ccontrol etc.) └── xServer (+ ConnectionManager ← Serverul principal — inima sistemului + ConnectionHandler + NetworkTarget) Channel ← Canal IRC (moduri, utilizatori, ban-uri) ChannelUser ← Asociere utilizator-canal (op, voice, half-op) xNetwork ← Container global — stochează toți clienții, serverele și canalele ``` ### Componentele Cheie | Componentă | Locație | Descriere | |---|---|---| | **xServer** | `include/server.h`, `src/server.cc`, `src/main.cc` | Serverul principal — bucla de evenimente, gestionarea conexiunii, rutarea mesajelor | | **xClient** | `include/client.h` | Clasa de bază pentru toate modulele de servicii | | **xNetwork** | `include/Network.h`, `src/Network.cc` | Containerul global al rețelei — tabele pentru clienți, servere, canale | | **ConnectionManager** | `libgnuworld/ConnectionManager.h`, `libgnuworld/ConnectionManager.cc` | Gestionarea conexiunilor I/O prin `select()` | | **iClient / iServer** | `include/iClient.h`, `include/iServer.h` | Reprezentarea entităților de rețea | | **Channel / ChannelUser** | `include/Channel.h`, `include/ChannelUser.h` | Reprezentarea canalelor și a utilizatorilor din canale | | **Baza de Date** | `db/gnuworldDB.h`, `db/pgsqlDB.h` | Strat abstract cu implementare PostgreSQL | --- ## 🔄 Bucla Principală de Evenimente Punctul de intrare este `main()` din `src/main.cc`, care instanțiază `xServer` și apelează `run()` → `mainLoop()`. ### Fluxul `mainLoop()` (din `src/main.cc`, linia 335): ``` ┌─────────────────────────────────────────────┐ │ while( keepRunning ) │ │ │ │ 1. Verifică reconectarea (dacă e cazul) │ │ 2. Verifică dacă este ultima iterație │ │ 3. Calculează timeout-ul pe baza timerelor │ │ 4. ConnectionManager::Poll(seconds) │ │ └─> select() pe toate socket-urile │ │ └─> handleRead() / handleWrite() │ │ └─> OnRead() → Process() → dispatch │ │ 5. CheckTimers() — execută timerele │ │ 6. Verifică semnalele (Signal::getSignal) │ │ 7. PostSignal() către module │ │ │ │ La ieșire: doShutdown() │ └─────────────────────────────────────────────┘ ``` ### Multiplexarea I/O — `select()` `ConnectionManager::Poll()` (din `libgnuworld/ConnectionManager.cc`, linia 559) folosește apelul **`select()`** clasic: - Iterează prin `handlerMap` (mapare `ConnectionHandler* → set`) - Configurează `fd_set` (readfds / writefds) pentru fiecare conexiune - Apelează `::select(1 + highestFD, &readfds, &writefds, 0, &to)` - Procesează rezultatele: citire, scriere, conexiuni noi, timeout-uri > ⚠️ **Observație:** Se folosește `select()` și nu `poll()`/`epoll()`. Aceasta este o limitare pentru rețele foarte mari (limita FD_SETSIZE, de obicei 1024). --- ## 🔗 Procesarea BURST și Sincronizarea cu Rețeaua Sincronizarea cu rețeaua urmează protocolul P10 complet: ### Secvența de Burst: ``` 1. msg_Server → Creează iServer pentru uplink, pornește burst-ul 2. msg_S → Adaugă servere noi (EVT_NETJOIN) 3. msg_N → Adaugă clienți de rețea (iClient) în timpul burst-ului 4. msg_B → BURST canale: moduri, utilizatori (base64), ban-uri (libircu/msg_B.cc — 541 linii, parser complex) 5. msg_EB → End of Burst: ├─ setUseHoldBuffer(false) — eliberează bufferul reținut ├─ BurstClients() — trimite clienții de servicii ├─ BurstChannels() — trimite canalele serviciilor ├─ setBursting(false) ├─ PostEvent(EVT_BURST_CMPLT) ├─ Trimite EB + EA └─ WriteBurstBuffer() — golește datele reținute 6. msg_EA → End of Burst Acknowledge — confirmă sincronizarea ``` ### Mecanismul `burstHoldBuffer`: În timpul burst-ului, `Write()` redirecționează datele într-un `burstHoldBuffer` separat (variabila `useHoldBuffer`). Aceasta previne trimiterea prematură de date către rețea înainte ca sincronizarea să fie completă. După `msg_EB`, datele sunt transferate în bufferul normal de ieșire prin `WriteBurstBuffer()`. --- ## 🧩 Sistemul de Module (Pluginuri) ### Încărcare Dinamică Modulele sunt încărcate prin `libtool` (`lt_dlopen`) folosind clasa template `moduleLoader<>` din `include/moduleLoader.h`. **Două tipuri de module:** 1. **Module de Servicii** (`xClient`) — încărcate prin `loadClients()`: - Fiecare subclasează `xClient` - Exportă o funcție fabrică: `_gnuwinit_(configFile)` - Exemple: `mod.cservice`, `mod.ccontrol`, `mod.chanfix` 2. **Handlere de Comenzi Server** (`ServerCommandHandler`) — încărcate prin `loadCommandHandlers()`: - Fiecare procesează un mesaj P10 specific (B, N, Q, M, J, etc.) - Exportă: `_gnuwinit_(xServer*)` - Locație: `libircu/msg_*.cc` (~42 de handlere) - Macrourile `CREATE_HANDLER` / `CREATE_LOADER` simplifică declararea ### Handlere P10 Disponibile (libircu/) | Token | Handler | Descriere | |---|---|---| | `B` | `msg_B` | BURST — sincronizare canale | | `N` | `msg_N` | NICK — client nou / schimbare nick | | `S` | `msg_S` | SERVER — server nou | | `J` | `msg_J` | JOIN — intrare în canal | | `L` | `msg_L` | PART — ieșire din canal | | `Q` | `msg_Q` | QUIT — deconectare client | | `M` | `msg_M` | MODE — schimbare moduri | | `P` | `msg_P` | PRIVMSG — mesaj privat | | `K` | `msg_K` | KICK — eliminare din canal | | `G` / `GL` | `msg_G` / `msg_GL` | GLINE — ban global | | `EB` | `msg_EB` | End of Burst | | `EA` | `msg_EA` | End of Burst Acknowledge | | `SQ` | `msg_SQ` | SQUIT — deconectare server | | `AC` | `msg_AC` | ACCOUNT — autentificare | | `T` | `msg_T` | TOPIC | | `CM` | `msg_CM` | CLEARMODE | | `W` | `msg_W` | WHOIS | | `I` | `msg_I` | INVITE | | `XQ` / `XR` | `msg_XQ` / `msg_XR` | Interogări/Răspunsuri extinse | --- ## 📊 Sistemul de Evenimente ### Evenimente de Rețea (definite în `include/events.h`): | Eveniment | Descriere | |---|---| | `EVT_NICK` | Client nou conectat | | `EVT_QUIT` | Client deconectat | | `EVT_KILL` | Client eliminat (kill) | | `EVT_CHNICK` | Schimbare de nick | | `EVT_OPER` | Operator IRC | | `EVT_NETJOIN` | Server nou conectat | | `EVT_NETBREAK` | Server deconectat (netsplit) | | `EVT_BURST_CMPLT` | Burst complet | | `EVT_BURST_ACK` | Confirmare burst | | `EVT_GLINE` / `EVT_REMGLINE` | Adăugare/Ștergere GLINE | | `EVT_ACCOUNT` | Autentificare cont | ### Evenimente de Canal: | Eveniment | Descriere | |---|---| | `EVT_JOIN` | Intrare în canal | | `EVT_PART` | Ieșire din canal | | `EVT_KICK` | Eliminare din canal | | `EVT_TOPIC` | Schimbare topic | | `EVT_CREATE` | Canal creat | Modulele se înregistrează prin `RegisterEvent()` / `RegisterChannelEvent()` și primesc notificări prin `OnEvent()` / `OnChannelEvent()`. --- ## 🗄️ Baza de Date ### Arhitectura: ``` gnuworldDB (abstract) ← db/gnuworldDB.h — Interfață abstractă (Exec, isConnected) └── pgsqlDB ← db/pgsqlDB.h — Implementare PostgreSQL (libpq) ``` ### Modele SQL per Modul (exemplu: mod.cservice): | Clasă | Fișier | Descriere | |---|---|---| | `sqlUser` | `mod.cservice/sqlUser.h` | Utilizator CService | | `sqlChannel` | `mod.cservice/sqlChannel.h` | Canal înregistrat | | `sqlLevel` | `mod.cservice/sqlLevel.h` | Nivel de acces utilizator-canal | | `sqlBan` | `mod.cservice/sqlBan.h` | Ban pe canal | Scripturile SQL de inițializare se găsesc în directorul `doc/` (`cservice.sql`, `ccontrol.sql`, etc.). --- ## ⚠️ Gestionarea Memoriei — Probleme Identificate ### Folosire exclusivă de `new`/`delete` brut Codul nu utilizează **deloc** smart pointers (`std::unique_ptr`, `std::shared_ptr`). Toate alocările sunt manuale: ```cpp // Exemple de alocare tipice: iClient* theClient = new (std::nothrow) iClient(...); Channel* theChan = new (std::nothrow) Channel(...); ChannelUser* chanUser = new (std::nothrow) ChannelUser(theClient); ``` ### Zone Critice de Dealocare: | Locație | Ce se dezalocă | |---|---| | `xServer::doShutdown()` | Clienți locali, clienți de rețea, canale, servere, gline-uri, timere | | `xNetwork::removeClient()` | `ChannelUser` asociați, canale goale | | `xNetwork::removeServer()` | Toți clienții de pe serverul eliminat | | `msg_Q::Execute()` | iClient la QUIT | | `msg_B::Execute()` | Channel + ChannelUser la BURST (cu verificare de erori) | ### Potențiale Scurgeri de Memorie: 1. **`doShutdown()` — Iterare cu break prematur:** Buclele de cleanup iterează și fac `break` dacă containerul devine gol, dar sar elemente intermediare dacă mai sunt. 2. **`msg_B::parseBurstBans()`** — Vectorul de ban-uri este creat cu `st.size()` elemente goale *și* apoi se face `push_back`, rezultând elemente duplicate goale. 3. **`channelEventMap`** — Listele alocate cu `new` pentru evenimentele de canal trebuie dezalocate manual. 4. **Excepții negestionate** — Fără RAII, orice excepție între `new` și `delete` creează o scurgere. --- ## 📁 Structura Directoarelor ``` underchat-gnuworld/ ├── src/ ← Codul sursă principal (main.cc, server.cc, Network.cc) ├── include/ ← Fișierele header principale ├── libircu/ ← Handlere protocol P10 (msg_*.cc — ~42 fișiere) ├── libgnuworld/ ← Biblioteci auxiliare (ConnectionManager, ELog, EConfig, Buffer) ├── db/ ← Stratul de bază de date (abstract + PostgreSQL) ├── mod.cservice/ ← Modulul CService (X) — managementul canalelor ├── mod.ccontrol/ ← Modulul CControl — administrare rețea ├── mod.chanfix/ ← Modulul ChanFix — reparare canale ├── mod.dronescan/ ← Modulul DroneScan — detectare boți ├── mod.nickserv/ ← Modulul NickServ — servicii de nick ├── mod.openchanfix/ ← Modulul OpenChanFix ├── mod.scanner/ ← Modulul Scanner ├── mod.snoop/ ← Modulul Snoop — monitorizare ├── mod.stats/ ← Modulul Stats — statistici ├── mod.cloner/ ← Modulul Cloner (testare) ├── mod.gnutest/ ← Modulul de test ├── mod.clientExample/ ← Exemplu de modul client ├── bin/ ← Fișiere de configurare (.conf, .sh) ├── doc/ ← Documentație și scripturi SQL ├── contrib/ ← Utilitare auxiliare ├── test/ ← Teste └── tools/ ← Unelte de dezvoltare ``` --- ## ⚙️ Compilare și Instalare ### Cerințe: - **Compilator C++** (GCC / G++ — standard pre-C++11) - **PostgreSQL** (libpq-dev) - **libtool** (pentru încărcarea dinamică a modulelor) - **autotools** (autoconf, automake) ### Pași de compilare: ```bash ./autogen.sh ./configure make make install ``` ### Configurare: Fișierele de configurare se găsesc în directorul `bin/`: - `GNUWorld.example.conf.in` — Configurația principală a serverului - `cservice.example.conf.in` — Configurația CService - `ccontrol.example.conf.in` — Configurația CControl - Alte fișiere `.conf.in` pentru fiecare modul --- ## 🔧 Recomandări de Modernizare | Zonă | Starea Curentă | Recomandare | |---|---|---| | **Multiplexare I/O** | `select()` | Migrare la `epoll()` (Linux) sau `kqueue()` (BSD) | | **Gestionare Memorie** | `new`/`delete` brut | Adoptare `std::unique_ptr` / `std::shared_ptr` (RAII) | | **Standard C++** | Pre-C++11 | Migrare la C++17 minim | | **Încărcare Module** | `libtool` (`lt_dlopen`) | `dlopen()` nativ sau C++20 modules | | **Bază de Date** | PostgreSQL sincron | Query-uri asincrone (`PQsendQuery`) | | **Parsare Stringuri** | `StringTokenizer` propriu | `std::string_view` + `std::regex` | | **Logging** | `ELog` propriu / `log4cplus` opțional | Standardizare pe `spdlog` sau similar | | **Testare** | Minimală | Adăugare teste unitare (Google Test / Catch2) | --- ## 📜 Licență Acest proiect este distribuit sub licența **GNU General Public License v2** (GPL-2.0). --- ## 🤝 Contribuții Proiect menținut de echipa **Underchat**. Contribuțiile sunt binevenite prin intermediul serverului Gitea: 🔗 **Repository:** [https://gitlab.back.ro/underchat/gnuworld.git](https://gitlab.back.ro/underchat/gnuworld.git) --- *Generat pe baza analizei codului sursă — Martie 2026*