329 lines
14 KiB
Markdown
329 lines
14 KiB
Markdown
# 🌐 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<Connection*>`)
|
|
- 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_<nume>(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_<msg_X>(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*
|
|
|