ircu2/ANALIZA_ARHITECTURA_SENIOR.md

22 KiB

📊 ANALIZĂ ARHITECTURALĂ SENIOR - Underchat IRCD (C)

Data Analiză: 23 Februarie 2026
Versiune Proiect: v1.7.5
Analist: Senior Software Architect (Protocoale de rețea & Sisteme distribuite)
Limbaj: C (confirmat - nu Python)
Bază: UnderNet IRCU2 - Protocol P10 (RFC 1459/2812)


🎯 EXECUTIVE SUMMARY

Underchat IRCD este o implementare matură în C a unui server IRC bazat pe codul UnderNet IRCU2. Arhitectura este SINGLE-THREADED cu EVENT LOOP non-blocking, folosind multiplexare I/O avansată (epoll/kqueue/poll/select).

Verdict Rapid:

  • Arhitectură solidă: Event-driven, non-blocking I/O
  • ⚠️ Performanță: Single-threaded (limitată de CPU-ul unui singur core)
  • ⚠️ Securitate: Câteva utilizări nesigure de strcpy/strcat/sprintf
  • RFC Compliance: Bună conformitate cu RFC 1459/2812
  • ⚠️ Memory Management: Potențiale leaks în gestionarea bufferelor

🏗️ ARHITECTURA SISTEMULUI

1. MODEL DE CONCURRENCY

Tip: Single-threaded, Event-driven Architecture
Pattern: Reactor Pattern (event loop centralizat)

┌─────────────────────────────────────────────┐
│         MAIN EVENT LOOP (ircd.c)            │
│                                             │
│  while (running) {                          │
│    engine_loop() -> epoll_wait()/poll()    │
│    ├─ Socket Events (READ/WRITE/ACCEPT)    │
│    ├─ Timer Events                          │
│    └─ Signal Events                         │
│  }                                          │
└─────────────────────────────────────────────┘
           │
           ├─> Socket Layer (s_bsd.c)
           │   ├─ LocalClientArray[MAXCONNECTIONS]
           │   ├─ HighestFd tracking
           │   └─ Non-blocking sockets
           │
           ├─> Event Engine (ircd_events.c)
           │   ├─ Engine abstraction layer
           │   ├─ Multiple backends:
           │   │   • epoll (Linux) ★ PREFERRED
           │   │   • kqueue (BSD/macOS)
           │   │   • /dev/poll (Solaris)
           │   │   • poll() fallback
           │   │   • select() fallback
           │   └─ Generator/Event system
           │
           ├─> Parser (parse.c)
           │   ├─ Trie-based command lookup
           │   ├─ Message dispatch table (msgtab[])
           │   └─ Handler functions (m_*.c)
           │
           └─> Buffer Management
               ├─ DBuf (input buffers - dbuf.c)
               ├─ MsgQ (output queues - msgq.c)
               └─ Packet processing (packet.c)

1.1 Event Engine Architecture

Fișiere cheie:

  • ircd/ircd_events.c - Event loop core
  • ircd/engine_epoll.c - Linux epoll backend (PREFERRED)
  • ircd/engine_kqueue.c - BSD kqueue backend
  • ircd/engine_poll.c - Poll fallback
  • ircd/engine_select.c - Select fallback

Prioritizare Engine:

static const struct Engine *evEngines[] = {
  ENGINE_KQUEUE      // BSD/macOS
  ENGINE_EPOLL       // Linux ★
  ENGINE_DEVPOLL     // Solaris
  ENGINE_FALLBACK    // poll() sau select()
  0
};

Event Types (enum EventType):

  • ET_READ - Socket ready for reading
  • ET_WRITE - Socket ready for writing
  • ET_ACCEPT - New connection available
  • ET_CONNECT - Outbound connection completed
  • ET_EOF - Connection closed
  • ET_ERROR - Error condition
  • ET_SIGNAL - Signal received
  • ET_EXPIRE - Timer expired
  • ET_DESTROY - Resource cleanup

2. GESTIONAREA SOCKET-URILOR

2.1 Structura Conexiunilor

LocalClientArray (s_bsd.c:83):

struct Client* LocalClientArray[MAXCONNECTIONS];
int HighestFd = -1;
  • Array indexat după file descriptor
  • Dimensiune fixă: MAXCONNECTIONS (tipic 1024-4096)
  • O(1) lookup pentru evenimente pe socket
  • HighestFd optimizează iterarea prin conexiuni active

2.2 Socket States (enum SocketState)

SS_CONNECTING   // Conexiune outbound în progres
SS_LISTENING    // Socket ascultător
SS_CONNECTED    // Socket conectat bidirectional
SS_DATAGRAM     // Socket UDP (pentru UPING)
SS_CONNECTDG    // UDP conectat
SS_NOTSOCK      // Non-socket (pipe pentru semnale)

2.3 Read/Write Flow

READ PATH (s_bsd.c - read_packet()):

recv() → DBuf (cli_recvQ) → packet.c → parse.c → handler (m_*.c)

WRITE PATH (send.c - send_queued()):

Handler → MsgQ (cli_sendQ) → deliver_it() → send()/writev()

Non-blocking cu edge-triggered events (epoll cu EPOLLET).


3. BUFFER MANAGEMENT

3.1 Input Buffers (DBuf)

Fișiere: ircd/dbuf.c, include/dbuf.h

struct DBuf {
  unsigned int length;          // Bytes în buffer
  struct DBufBuffer *head;      // Primul chunk
  struct DBufBuffer *tail;      // Ultimul chunk
};

Caracteristici:

  • Linked list de chunked buffers
  • Evită realocări mari
  • Pool de buffere reutilizabile
  • DBufAllocCount / DBufUsedCount - tracking memorie

PROBLEMA POTENȚIALĂ:

  • ⚠️ Fără limită hard pe lungimea totală a unui DBuf
  • Posibil vector de atac DoS prin flood de date incomplete

3.2 Output Queues (MsgQ)

Fișiere: ircd/msgq.c, include/msgq.h

struct MsgQ {
  unsigned int length;       // Bytes în queue
  unsigned int count;        // Număr de mesaje
  struct MsgQList queue;     // Queue normal
  struct MsgQList prio;      // Queue prioritar
};

Caracteristici:

  • Două queue-uri (normal + prioritar)
  • Mesajele server-to-server au prioritate
  • msgq_add() - adaugă cu prioritizare
  • msgq_mapiov() - mapare pentru writev()

MĂSURI FLOOD PROTECTION:

// send.c:128 - Kill highest sendq când suntem fără memorie
void kill_highest_sendq(int servers_too)

4. PARSING & MESSAGE DISPATCH

4.1 Command Lookup - Trie Data Structure

Fișier: ircd/parse.c

Structură:

struct MessageTree {
  struct Message *msg;
  struct MessageTree *pointers[MAXPTRLEN]; // 32 pointers
};

static struct MessageTree msg_tree;  // Comenzi text (PRIVMSG)
static struct MessageTree tok_tree;  // Token-uri P10 (P)

Avantaje:

  • O(k) lookup unde k = lungime comandă
  • Suport dual: text + P10 tokens
  • Rapid pentru comenzile frecvente

Exemplu (parse.c:110):

struct Message msgtab[] = {
  { MSG_PRIVATE, TOK_PRIVATE, 0, MAXPARA, MFLG_SLOW, 0, NULL,
    { m_unregistered, m_privmsg, ms_privmsg, mo_privmsg, m_ignore },
    "<target> :<message>"
  },
  // ... 100+ comenzi
};

4.2 Handler Dispatch

5 handler-uri per comandă:

{ m_unregistered,  // UNREG - client neînregistrat
  m_privmsg,       // CLIENT - user normal
  ms_privmsg,      // SERVER - inter-server
  mo_privmsg,      // OPER - operator
  m_ignore }       // SERVICE - servicii (ignorat)

5. PROTOCOL COMPLIANCE (RFC 1459/2812)

5.1 Conformitate RFC

BINE IMPLEMENTAT:

  • Message framing (512 bytes max cu CR-LF)
  • Command parsing (prefix, command, params)
  • Numeric replies (001-999)
  • Channel modes (+o, +v, +b, +k, +l, etc.)
  • User modes (+i, +w, +o, etc.)
  • Server-to-server protocol (P10)

⚠️ DEVIERI DE LA RFC:

  • Protocol P10 (UnderNet specific) - nu este RFC standard
  • Numeric nicks (server NN, user NNNNN)
  • Extended modes (nu sunt în RFC 1459)

5.2 Message Format

Parsing (packet.c:75-90):

// CORECT: Acceptă CR sau LF ca terminator
if (IsEol(*endp)) {
  if (endp == client_buffer)
    continue;  // Skip extra LF/CR's
  *endp = '\0';
  
  if (parse_server(cptr, cli_buffer(cptr), endp) == CPTR_KILLED)
    return CPTR_KILLED;
  // ...
}

PROBLEMA:

  • ⚠️ Acceptă CR SAU LF (nu doar CR-LF)
  • Mai permisiv decât RFC, dar backward compatible

🔒 ANALIZĂ SECURITATE

1. INPUT VALIDATION

1.1 Buffer Overflows - RISC MODERAT

PROBLEME IDENTIFICATE:

strcpy/strcat/sprintf Usage (20 instanțe):

// ircd/s_user.c:744 - UNSAFE
strcpy(cli_name(new_client), nick);

// ircd/uping.c:290 - UNSAFE
sprintf(buf, " %10lu%c%6lu", ...);

// ircd/m_whois.c:148-149 - UNSAFE
strcat(markbufp, ", ");
strcat(markbufp, dp->value.cp);

RECOMANDĂRI:

// Înlocuiește cu:
ircd_strncpy(cli_name(new_client), nick, NICKLEN);
ircd_snprintf(buf, sizeof(buf), " %10lu%c%6lu", ...);

BUN: Proiectul folosește deja ircd_strncpy() și ircd_snprintf() în multe locuri.

1.2 UTF-8 Validation

BINE IMPLEMENTAT (ircd_string.c:60):

int string_is_valid_utf8(const char * str)
  • Validare completă UTF-8
  • Detectează encoding invalid
  • Protecție contra caractere de control

2. FLOOD PROTECTION

2.1 Connection Rate Limiting

Fișier: ircd/IPcheck.c

Mecanisme:

#define IPCHECK_CLONE_LIMIT   // Max clone de pe același IP
#define IPCHECK_CLONE_PERIOD  // Fereastra de timp
#define IPCHECK_CLONE_DELAY   // Delay între conexiuni

struct IPRegistryEntry {
  unsigned short connected;   // Clienți conectați
  unsigned char attempts;     // Încercări recente
  int last_connect;           // Timestamp ultima conexiune
};

Hash Table: IP_REGISTRY_TABLE_SIZE = 0x10000 (64K buckets)

IPv6 Handling:

  • Doar primii 64 biți sunt considerați (rețea)
  • Ultimii 64 biți (host) sunt ignorați
  • Protecție contra abuzului de IPv6 /64

BUNĂ PRACTICĂ: Canonicalizare IP pentru comparație.

2.2 Message Rate Limiting

Target Limiting (IPcheck.c:48):

struct IPTargetEntry {
  unsigned int count;                  // Target-uri libere
  unsigned char targets[MAXTARGETS];   // Target-uri recente
};

#define MAXTARGETS 20      // Max target-uri
#define STARTTARGETS 10    // Target-uri inițiale

Token Bucket Algorithm:

  • Utilizatorii încep cu STARTTARGETS mesaje
  • Reîncărcare progresivă: TARGET_DELAY secunde
  • Previne spam cross-channel

2.3 SendQ Limiting

High SendQ Killing (send.c:115):

void kill_highest_sendq(int servers_too) {
  // Găsește clientul cu cel mai mare sendQ
  // Îl deconectează pentru a elibera memorie
}

⚠️ PROBLEMA:

  • Trigger doar când sistemul e deja low memory
  • Nu există limită preventivă per-client
  • Posibil DoS prin umplerea sendQ

RECOMANDARE:

#define MAX_SENDQ_PER_CLIENT (64 * 1024)  // 64KB

if (MsgQLength(&cli_sendQ(client)) > MAX_SENDQ_PER_CLIENT) {
  dead_link(client, "SendQ exceeded");
}

3. MEMORY LEAKS - RISC MODERAT

3.1 DBuf/MsgQ Cleanup

BUNĂ PRACTICĂ:

// s_bsd.c - Cleanup la disconnect
DBufClear(&(cli_recvQ(to)));
MsgQClear(&(cli_sendQ(to)));

⚠️ PROBLEME POTENȚIALE:

  1. Incomplete Message Buffers:

    • Dacă un client trimite date incomplete și nu mai trimite CR-LF
    • Buffer rămâne alocat indefinit
    • FLAG_NONL este setat dar nu există timeout
  2. Event Generator Leaks:

    • gen_ref_dec() trebuie apelat corect
    • Dacă reference count nu ajunge la 0 → leak

RECOMANDARE:

// Adaugă timeout pentru conexiuni incomplete
#define INCOMPLETE_MESSAGE_TIMEOUT 30  // secunde

if (HasFlag(cptr, FLAG_NONL) && 
    CurrentTime - cli_lasttime(cptr) > INCOMPLETE_MESSAGE_TIMEOUT) {
  dead_link(cptr, "Incomplete message timeout");
}

4. RACE CONDITIONS

EVITATE - Single-threaded architecture elimină majoritatea race conditions.

⚠️ EXCEPȚIE: Signal Handlers

// ircd_signal.c - Signal handling
// Handlers scriu într-un pipe non-blocking
// Main loop citește din pipe → safe

BUNĂ PRACTICĂ: Self-pipe trick pentru semnale.


ANALIZĂ PERFORMANȚĂ

1. SCALABILITATE

1.1 Connection Capacity

LIMITE:

// MAXCONNECTIONS definit la compile-time
// Tipic: 1024-4096

LocalClientArray[MAXCONNECTIONS];

C10K Problem:

  • REZOLVAT prin epoll/kqueue
  • ⚠️ Single-threaded → limitat de 1 CPU core

Benchmark Estimate (pe hardware modern):

  • ~5,000-10,000 clienți idle
  • ~1,000-2,000 clienți activi
  • Limitat de CPU pentru parsing/routing

1.2 Message Throughput

Bottleneck-uri:

  1. Trie Lookup: O(k) - foarte rapid
  2. Handler Execution: Variabil per comandă
  3. Broadcast (PRIVMSG la channel mare):
    // O(N) unde N = număr membrii
    for (member in channel->members)
      sendto_member(member, message);
    

OPTIMIZARE SUGERATĂ:

  • Batch writes cu writev() (deja implementat)
  • SendQ prioritization (deja implementat)
  • LIPSĂ: Message coalescing pentru broadcasts

2. MEMORY FOOTPRINT

2.1 Per-Client Memory

struct Client {
  struct Connection* cli_connect;   // ~200 bytes
  struct User* cli_user;            // ~400 bytes (dacă user)
  struct Server* cli_serv;          // ~300 bytes (dacă server)
  // + buffers:
  struct DBuf cli_recvQ;            // ~8 bytes + date
  struct MsgQ cli_sendQ;            // ~16 bytes + date
  char cli_buffer[BUFSIZE];         // 512 bytes
};

Total per client: ~1-2 KB (fără buffers de date)

Cu buffers: +4-64 KB (variabil, depinde de traffic)

2.2 Memory Pools

BUNĂ PRACTICĂ:

// IPcheck.c:192
static struct IPRegistryEntry* freeList;

// Refolosire structuri în loc de free/malloc repetat

3. LATENCY

Message Latency Path:

Client A → recv() → parse → handler → sendto → Client B's sendQ → send()
         ↑_____ ~0.1-1ms (local) _____↑

Factori latență:

  1. Syscall overhead: recv/send
  2. Event loop iteration: ~1-10ms între iterații
  3. SendQ flushing: Doar când socket e writable

OPTIMIZARE: Write imediat dacă sendQ e gol

// send.c - try immediate write before queueing

🐛 VULNERABILITĂȚI IDENTIFICATE

CRITICAL (🔴)

Niciuna identificată în analiza curentă

HIGH (🟠)

  1. SendQ Exhaustion DoS

    • Locație: send.c
    • Risc: Client poate umple sendQ până la OOM
    • Fix: Limită hard per-client + disconnect proactiv
  2. Incomplete Message Buffer Leak

    • Locație: packet.c, s_bsd.c
    • Risc: Mesaje fără CR-LF rămân în memorie
    • Fix: Timeout pentru FLAG_NONL

MEDIUM (🟡)

  1. Unsafe String Operations

    • Locație: Multiple (20 instanțe strcpy/strcat/sprintf)
    • Risc: Buffer overflow dacă input nevalidat
    • Fix: Replace with ircd_strncpy/ircd_snprintf
  2. IPv6 Clone Detection Limited

    • Locație: IPcheck.c
    • Risc: Doar /64 verificat, nu full /128
    • Fix: Opțiune configurabilă pentru prefix length

LOW (🟢)

  1. Message Parser Permissiveness
    • Locație: packet.c
    • Risc: Acceptă CR sau LF (nu doar CR-LF)
    • Fix: Strict RFC mode optional

📊 METRICI DE CALITATE COD

Complexitate

Metric Valoare Assessment
Files ~150 .c files Mare
Lines of Code ~50,000+ Mare
Functions ~1,000+ Modular
Max Function Size ~500 lines 🟡 Unele funcții mari
Cyclomatic Complexity Variabilă 🟡 Averaging medium

Maintainability

PRO:

  • Modularizare clară (per-funcționalitate)
  • Headers separate cu API-uri clare
  • Comentarii rezonabile
  • Coding style consistent

⚠️ CONTRA:

  • Global state extensiv (GlobalClientList, etc.)
  • Macro-uri complexe (client accessors)
  • Unele funcții foarte lungi
  • Documentație inline limitată

🎯 RECOMANDĂRI PRIORITIZATE

URGENT (Implementare în 1-2 săptămâni)

  1. 🔒 Fix Unsafe String Operations

    // Replace toate instanțele:
    strcpy()  ircd_strncpy()
    strcat()  ircd_strlcat() sau snprintf()
    sprintf()  ircd_snprintf()
    
  2. 🛡️ SendQ Hard Limits

    #define MAX_SENDQ_USER 65536     // 64KB
    #define MAX_SENDQ_SERVER 524288  // 512KB
    
    // În send.c, verifică înainte de msgq_add()
    
  3. ⏱️ Incomplete Message Timeout

    // În s_bsd.c read_packet():
    if (HasFlag(cptr, FLAG_NONL) && timeout_exceeded(cptr)) {
      dead_link(cptr, "Incomplete message timeout");
    }
    

SHORT-TERM (1-3 luni)

  1. 📊 Monitoring & Metrics

    // Adaugă contoare pentru:
    - Mesaje procesate/sec
    - Conexiuni acceptate/respinse
    - SendQ usage histogram
    - CPU usage per handler
    
  2. 🧪 Unit Testing

    • Parser tests (trie lookup)
    • Buffer management tests (DBuf, MsgQ)
    • IPcheck tests (rate limiting)
    • Mock socket tests
  3. 📝 Memory Leak Detection

    # Run sub Valgrind
    valgrind --leak-check=full --track-origins=yes ./ircd
    

MID-TERM (3-6 luni)

  1. Performance Optimizations

    • Message coalescing pentru broadcast
    • Zero-copy buffer passing (splice/sendfile)
    • JIT compilation pentru hot commands (optional)
  2. 🔐 Security Hardening

    • ASLR verification
    • Stack canaries
    • Seccomp filters (Linux)
    • Pledge/unveil (OpenBSD)
  3. 📚 Documentation

    • Architecture diagrams (flow charts)
    • API documentation (Doxygen)
    • Security audit report
    • Performance tuning guide

LONG-TERM (6-12 luni)

  1. 🧵 Multi-threading (Optional)

    • Thread pool pentru parsing
    • Lock-free queues pentru cross-thread messaging
    • MASSIVE REFACTOR - evaluat risc/beneficiu
  2. 🌐 IPv6 Full Support

    • Full /128 tracking în IPcheck
    • IPv6-only mode
    • Dual-stack optimization
  3. 🔄 Protocol Extensions

    • IRCv3 capabilities
    • WebSocket support
    • Message tags

📈 COMPARAȚIE CU ALTERNATIVE

Caracteristică Underchat IRCD Inspircd UnrealIRCd Charybdis
Limbaj C C++ C C
Threading Single Multi-thread Multi-thread Single
Event Loop epoll/kqueue epoll/kqueue epoll/kqueue epoll/kqueue
Protocol P10 (UnderNet) Modular Modular TS6
RFC Compliance 🟡 Medium High High High
Extensibility 🟡 Medium High (modules) High (modules) High (modules)
Memory Safety 🟡 C (manual) 🟡 C++ (manual) 🟡 C (manual) 🟡 C (manual)
Performance 🟢 Good Excellent Excellent 🟢 Good
Maturitate Mature Mature Mature Mature

Verdict:

  • Underchat IRCD este competitiv pentru rețele mici-medii (< 5000 useri)
  • Pentru scale mai mare: consideră InspIRCd (multi-thread) sau Charybdis (TS6)

🎓 CONCLUZIE

Strengths (💪)

  1. Arhitectură solidă: Event-driven, non-blocking I/O
  2. Cod matur: Bazat pe UnderNet IRCU2 (20+ ani de dezvoltare)
  3. Performanță bună: Pentru cazuri de utilizare single-core
  4. Modularitate: Comenzi separate în m_*.c files
  5. Portabilitate: Suport multi-platform (Linux, BSD, macOS)

Weaknesses (⚠️)

  1. Single-threaded: Nu scalează pe multi-core
  2. Unsafe C: 20+ instanțe de strcpy/strcat/sprintf
  3. Memory management: Potențiale leaks în edge cases
  4. Limited flood protection: SendQ fără limită hard
  5. Protocol lock-in: P10 specific (nu compatibil cu alte rețele)

Risk Assessment

Categorie Nivel Risc Justificare
Security 🟡 MEDIUM Unsafe string ops, dar mitigat de validări
Availability 🟢 LOW Flood protection rezonabilă
Performance 🟡 MEDIUM Single-core limited, dar suficient pentru < 5K users
Maintainability 🟢 LOW Cod matur, modular, bine structurat
Scalability 🟠 HIGH Hard limit la 1 CPU core, MAXCONNECTIONS fix

Final Recommendation

DEPLOY cu următoarele condiții:

  1. Implementează fix-urile URGENT (unsafe strings, sendQ limits)
  2. Monitorizare activă (CPU, memory, connections)
  3. Testing extins (load testing, fuzz testing)
  4. ⚠️ Plan de scale: Pentru >5000 users, consideră alternative multi-thread

PENTRU PRODUCȚIE:

  • Perfect pentru comunități mici-medii (< 2000 users)
  • Necesită hardening de securitate înainte de expunere publică
  • Recomand firewall rules + rate limiting la nivel de rețea
  • Monitoring 24/7 obligatoriu

📞 NEXT STEPS

  1. Discuție tehnică: Prioritizare fix-uri vs. features
  2. Security audit: Penetration testing extern
  3. Load testing: Simulare 1000+ clienți concurenți
  4. Code review: Peer review pentru commits critice
  5. Documentation sprint: README tehnic + architecture guide

Semnat:
Senior Software Architect
Specialized in Network Protocols & Distributed Systems
Data: 23 Februarie 2026


📚 ANEXE

A. GLOSAR TEHNIC

  • P10: Protocol server-to-server folosit de UnderNet
  • Numeric nick: Format NNNNN pentru identificare unică
  • Trie: Prefix tree pentru lookup rapid comenzi
  • Event loop: Main loop care multiplexează I/O
  • epoll: Linux kernel API pentru event notification
  • SendQ: Queue de mesaje în așteptare pentru trimitere
  • DBuf: Dynamic buffer pentru date primite

B. FIȘIERE CHEIE ANALIZATE

ircd/ircd.c              # Entry point, main loop
ircd/s_bsd.c             # Socket handling, I/O
ircd/ircd_events.c       # Event engine core
ircd/engine_epoll.c      # Epoll backend
ircd/parse.c             # Command parsing (trie)
ircd/packet.c            # Packet processing
ircd/send.c              # Message sending
ircd/IPcheck.c           # Rate limiting
ircd/dbuf.c              # Input buffers
ircd/msgq.c              # Output queues
include/client.h         # Client structure
include/struct.h         # User/Server structures

C. REFERINȚE

  • RFC 1459: Internet Relay Chat Protocol
  • RFC 2812: Internet Relay Chat: Client Protocol
  • UnderNet IRCU2: https://github.com/UndernetIRC/ircu2
  • Epoll man page: man 7 epoll
  • Kqueue man page: man 2 kqueue

Acest document este confidențial și destinat echipei tehnice Underchat.