# 🔧 RECOMANDĂRI PRACTICE - Fix-uri de Securitate & Performanță **Data**: 23 Februarie 2026 **Pentru**: Underchat IRCD v1.7.5 **Prioritate**: URGENT → MID-TERM --- ## 🚨 URGENT: Fix-uri de Securitate (Săptămâna 1-2) ### 1. Eliminare Unsafe String Operations #### 1.1 Replace strcpy() cu ircd_strncpy() **Locații afectate**: 6 instanțe **ÎNAINTE** (`ircd/s_user.c:744`): ```c strcpy(cli_name(new_client), nick); ``` **DUPĂ**: ```c ircd_strncpy(cli_name(new_client), nick, NICKLEN); ``` **Patch complet**: ```c // ircd/s_user.c - Linia ~744 // REMOVE: // strcpy(cli_name(new_client), nick); // ADD: ircd_strncpy(cli_name(new_client), nick, NICKLEN); // ircd/s_user.c - Linia ~859 // REMOVE: // strcpy(cli_name(sptr), nick); // ADD: ircd_strncpy(cli_name(sptr), nick, NICKLEN); // ircd/s_user.c - Linia ~867 // REMOVE: // strcpy(cli_name(sptr), nick); // ADD: ircd_strncpy(cli_name(sptr), nick, NICKLEN); // ircd/uping.c - Linia ~422 // REMOVE: // strcpy(pptr->name, aconf->name); // ADD: ircd_strncpy(pptr->name, aconf->name, sizeof(pptr->name)); // ircd/numnicks.c - Linia ~333 // REMOVE: // strcpy(cli_yxx(acptr), yxx + 2); // ADD: ircd_strncpy(cli_yxx(acptr), yxx + 2, sizeof(cli_yxx(acptr))); ``` --- #### 1.2 Replace sprintf() cu ircd_snprintf() **Locații afectate**: 2 instanțe **ÎNAINTE** (`ircd/uping.c:290`): ```c sprintf(buf, " %10lu%c%6lu", (unsigned long)tv.tv_sec, '\0', (unsigned long)tv.tv_usec); ``` **DUPĂ**: ```c ircd_snprintf(0, buf, sizeof(buf), " %10lu%c%6lu", (unsigned long)tv.tv_sec, '\0', (unsigned long)tv.tv_usec); ``` **Patch complet**: ```c // ircd/uping.c - Linia ~290 // REMOVE: // sprintf(buf, " %10lu%c%6lu", (unsigned long)tv.tv_sec, '\0', // (unsigned long)tv.tv_usec); // ADD: ircd_snprintf(0, buf, sizeof(buf), " %10lu%c%6lu", (unsigned long)tv.tv_sec, '\0', (unsigned long)tv.tv_usec); // ircd/uping.c - Linia ~360 // REMOVE: // sprintf(s, " %u", pingtime); // ADD: ircd_snprintf(0, s, sizeof(s), " %u", pingtime); ``` --- #### 1.3 Securizare strcat() în m_whois.c **Locații afectate**: 4 instanțe **ÎNAINTE** (`ircd/m_whois.c:148-149`): ```c strcat(markbufp, ", "); strcat(markbufp, dp->value.cp); ``` **DUPĂ**: ```c // Verificare dimensiune buffer size_t remaining = sizeof(markbuf) - strlen(markbufp) - 1; if (remaining > 2) { strncat(markbufp, ", ", remaining); remaining = sizeof(markbuf) - strlen(markbufp) - 1; if (remaining > 0) strncat(markbufp, dp->value.cp, remaining); } ``` **Patch complet**: ```c // ircd/m_whois.c - Liniile ~145-155 // CONTEXT: Marcarea operatorilor în WHOIS // REMOVE block: /* if (*markbufp) strcat(markbufp, ", "); strcat(markbufp, dp->value.cp); */ // ADD: if (*markbufp) { size_t remaining = sizeof(markbuf) - strlen(markbufp) - 1; if (remaining > 2) { strncat(markbufp, ", ", remaining); } } size_t remaining = sizeof(markbuf) - strlen(markbufp) - 1; if (remaining > 0) { strncat(markbufp, dp->value.cp, remaining); } // Similar pentru linia ~223: // REMOVE: // strcpy(buf + len, chptr->chname); // ADD: size_t buf_remaining = sizeof(buf) - len - 1; if (buf_remaining > strlen(chptr->chname)) { strcpy(buf + len, chptr->chname); len += strlen(chptr->chname); } ``` --- ### 2. SendQ Hard Limits **Fișier nou**: `include/ircd_limits.h` ```c /* * ircd_limits.h - Hard limits pentru protecție DoS */ #ifndef INCLUDED_ircd_limits_h #define INCLUDED_ircd_limits_h /* SendQ Limits */ #define MAX_SENDQ_USER (64 * 1024) /* 64 KB pentru useri */ #define MAX_SENDQ_OPER (128 * 1024) /* 128 KB pentru operatori */ #define MAX_SENDQ_SERVER (512 * 1024) /* 512 KB pentru servere */ /* RecvQ Limits */ #define MAX_RECVQ_USER (8 * 1024) /* 8 KB pentru useri */ #define MAX_RECVQ_SERVER (64 * 1024) /* 64 KB pentru servere */ /* Connection Limits */ #define MAX_BUFFER_AGE 30 /* 30 secunde pentru mesaje incomplete */ #endif /* INCLUDED_ircd_limits_h */ ``` **Modificare**: `ircd/send.c` ```c // Include header nou #include "ircd_limits.h" // Modifică funcția msgq_add() void msgq_add(struct MsgQ *mq, struct MsgBuf *mb, int prio) { // VERIFICARE LIMITĂ ÎNAINTE DE ADĂUGARE unsigned int current_size = MsgQLength(mq); unsigned int new_size = current_size + msgq_bufleft(mb); // Determină limita bazată pe tipul clientului struct Client *cptr = /* get client from context */; unsigned int max_sendq; if (IsServer(cptr)) max_sendq = MAX_SENDQ_SERVER; else if (IsOper(cptr)) max_sendq = MAX_SENDQ_OPER; else max_sendq = MAX_SENDQ_USER; // VERIFICARE if (new_size > max_sendq) { // ❌ DEPĂȘIRE LIMITĂ dead_link(cptr, "SendQ exceeded"); msgq_clean(mb); return; } // ✅ OK, adaugă mesajul // ... cod original ... } ``` **Modificare**: `ircd/s_bsd.c` ```c #include "ircd_limits.h" // În funcția read_packet(), după dbuf_put(): int read_packet(struct Client* cptr, int ready_to_read) { // ... cod existent recv() ... // VERIFICARE RecvQ Limit unsigned int recvq_size = DBufLength(&cli_recvQ(cptr)); unsigned int max_recvq = IsServer(cptr) ? MAX_RECVQ_SERVER : MAX_RECVQ_USER; if (recvq_size > max_recvq) { // ❌ RecvQ prea mare return exit_client_msg(cptr, cptr, &me, "RecvQ exceeded (%u > %u)", recvq_size, max_recvq); } // ... rest cod ... } ``` --- ### 3. Incomplete Message Timeout **Modificare**: `ircd/s_bsd.c` ```c #include "ircd_limits.h" // Adaugă în read_packet() după procesarea mesajelor: int read_packet(struct Client* cptr, int ready_to_read) { // ... cod existent ... // VERIFICARE FLAG_NONL (mesaj incomplet) if (HasFlag(cptr, FLAG_NONL)) { time_t age = CurrentTime - cli_lasttime(cptr); if (age > MAX_BUFFER_AGE) { // ❌ TIMEOUT: Mesaj incomplet de prea mult timp Debug((DEBUG_ERROR, "Incomplete message timeout for %s (age: %ld sec)", cli_name(cptr), (long)age)); return exit_client_msg(cptr, cptr, &me, "Incomplete message timeout (%ld seconds)", (long)age); } } return 1; } ``` **Modificare**: `include/client.h` ```c // Adaugă macro pentru tracking timp #define cli_lasttime(cptr) ((cptr)->cli_user ? (cptr)->cli_user->last : (cptr)->cli_firsttime) // Asigură-te că cli_firsttime e inițializat la conectare // În s_bsd.c, funcția add_connection(): cli_firsttime(new_client) = CurrentTime; ``` --- ## ⚙️ SHORT-TERM: Îmbunătățiri Performanță (Luna 1-2) ### 4. Message Coalescing pentru Broadcast **Problema**: Fiecare broadcast face N apeluri send() separate. **Soluție**: Buffer mesajele identice și trimite batch. **Fișier nou**: `ircd/send_batch.c` ```c /* * send_batch.c - Batch sending optimization */ #include "config.h" #include "send_batch.h" #include "client.h" #include "send.h" #define BATCH_INTERVAL_MS 10 /* 10ms batch window */ #define MAX_BATCH_SIZE 100 /* Max clients per batch */ struct BatchBuffer { struct Client *clients[MAX_BATCH_SIZE]; unsigned int count; struct MsgBuf *mb; time_t scheduled; }; static struct BatchBuffer batches[HASH_SIZE]; void batch_add(struct Client *to, struct MsgBuf *mb) { unsigned int hash = /* calculate hash from mb */; struct BatchBuffer *batch = &batches[hash]; if (batch->count == 0) { // Nou batch batch->mb = mb; batch->scheduled = CurrentTime; msgq_addref(mb); // Increment reference count } batch->clients[batch->count++] = to; // Flush dacă e plin if (batch->count >= MAX_BATCH_SIZE) batch_flush(batch); } void batch_flush(struct BatchBuffer *batch) { if (batch->count == 0) return; // Adaugă mesajul la fiecare client for (unsigned int i = 0; i < batch->count; i++) { msgq_add(&cli_sendQ(batch->clients[i]), batch->mb, 0); update_write(batch->clients[i]); } // Cleanup msgq_clean(batch->mb); batch->count = 0; batch->mb = NULL; } // Apelat din event loop la fiecare iterație void batch_process(void) { time_t now = CurrentTime; for (int i = 0; i < HASH_SIZE; i++) { struct BatchBuffer *batch = &batches[i]; if (batch->count > 0 && (now - batch->scheduled) >= BATCH_INTERVAL_MS) { batch_flush(batch); } } } ``` **Integrare în**: `ircd/send.c` ```c // În sendcmdto_channel_butone(): // ÎNAINTE: for (member = chptr->members; member; member = member->next_member) { msgq_add(&cli_sendQ(dest), mb, 0); update_write(dest); } // DUPĂ: #ifdef USE_BATCH_SEND for (member = chptr->members; member; member = member->next_member) { batch_add(dest, mb); // ✅ Batched } #else // ... cod original ... #endif ``` --- ### 5. Connection Pool pentru Outbound **Problema**: Fiecare connect_server() creează socket nou. **Soluție**: Refolosește socket-uri închise recent. **Modificare**: `ircd/s_bsd.c` ```c #define CONN_POOL_SIZE 10 static int connection_pool[CONN_POOL_SIZE]; static int pool_count = 0; // Adaugă socket la pool în loc să-l închizi void connection_pool_add(int fd) { if (pool_count < CONN_POOL_SIZE) { connection_pool[pool_count++] = fd; } else { close(fd); // Pool plin, închide } } // Obține socket din pool sau creează nou int connection_pool_get(void) { if (pool_count > 0) { return connection_pool[--pool_count]; // ✅ Reuse } // Pool gol, creează socket nou return socket(AF_INET, SOCK_STREAM, 0); } // Modifică close_connection(): void close_connection(struct Client *cptr) { if (cli_fd(cptr) >= 0) { connection_pool_add(cli_fd(cptr)); // ✅ Add to pool cli_fd(cptr) = -1; } } ``` --- ## 🔍 MID-TERM: Monitoring & Observability (Luna 2-3) ### 6. Performance Metrics **Fișier nou**: `include/ircd_metrics.h` ```c /* * ircd_metrics.h - Performance metrics tracking */ #ifndef INCLUDED_ircd_metrics_h #define INCLUDED_ircd_metrics_h struct Metrics { /* Message counters */ unsigned long messages_received; unsigned long messages_sent; unsigned long messages_parsed; unsigned long parse_errors; /* Connection counters */ unsigned long connections_accepted; unsigned long connections_rejected; unsigned long connections_timeout; /* Performance timings (microseconds) */ unsigned long parse_time_total; unsigned long parse_time_max; unsigned long send_time_total; unsigned long send_time_max; /* Resource usage */ unsigned long sendq_bytes_total; unsigned long recvq_bytes_total; unsigned long dbuf_alloc_count; unsigned long msgq_alloc_count; /* Flood protection */ unsigned long ipcheck_rejects; unsigned long target_rejects; unsigned long sendq_kills; }; extern struct Metrics global_metrics; /* Increment macros */ #define METRIC_INC(name) (global_metrics.name++) #define METRIC_ADD(name, val) (global_metrics.name += (val)) #define METRIC_MAX(name, val) \ if ((val) > global_metrics.name) global_metrics.name = (val) /* Timing macros */ #define METRIC_TIME_START() \ struct timeval _metric_start; gettimeofday(&_metric_start, NULL) #define METRIC_TIME_END(name) \ do { \ struct timeval _metric_end; \ gettimeofday(&_metric_end, NULL); \ unsigned long _elapsed = \ (_metric_end.tv_sec - _metric_start.tv_sec) * 1000000 + \ (_metric_end.tv_usec - _metric_start.tv_usec); \ METRIC_ADD(name##_total, _elapsed); \ METRIC_MAX(name##_max, _elapsed); \ } while(0) #endif /* INCLUDED_ircd_metrics_h */ ``` **Implementare**: `ircd/ircd_metrics.c` ```c #include "ircd_metrics.h" #include struct Metrics global_metrics; void metrics_init(void) { memset(&global_metrics, 0, sizeof(global_metrics)); } void metrics_report(struct Client *sptr) { send_reply(sptr, RPL_STATSHEADER, "Performance Metrics:"); send_reply(sptr, RPL_STATS, "Messages: Recv=%lu Sent=%lu ParseErr=%lu", global_metrics.messages_received, global_metrics.messages_sent, global_metrics.parse_errors); send_reply(sptr, RPL_STATS, "Parse: Avg=%.2fus Max=%luus", (double)global_metrics.parse_time_total / global_metrics.messages_received, global_metrics.parse_time_max); send_reply(sptr, RPL_STATS, "Connections: Accept=%lu Reject=%lu Timeout=%lu", global_metrics.connections_accepted, global_metrics.connections_rejected, global_metrics.connections_timeout); send_reply(sptr, RPL_STATS, "Flood: IPCheck=%lu Target=%lu SendQKill=%lu", global_metrics.ipcheck_rejects, global_metrics.target_rejects, global_metrics.sendq_kills); } ``` **Integrare**: Adaugă în locurile potrivite ```c // ircd/parse.c int parse_client(...) { METRIC_TIME_START(); // ... parsing logic ... METRIC_TIME_END(parse_time); METRIC_INC(messages_parsed); } // ircd/IPcheck.c if (reject_connection) { METRIC_INC(ipcheck_rejects); } // ircd/send.c void kill_highest_sendq(...) { METRIC_INC(sendq_kills); // ... } ``` **Comandă STATS**: `/STATS m` pentru metrics --- ### 7. Real-time Status Dashboard **Fișier nou**: `tools/monitor.sh` ```bash #!/bin/bash # # monitor.sh - Real-time IRCD monitoring # IRCD_PID=$(pgrep ircd) LOG_FILE="/var/log/ircd/ircd.log" if [ -z "$IRCD_PID" ]; then echo "ERROR: ircd is not running" exit 1 fi clear echo "╔════════════════════════════════════════════════════════╗" echo "║ UNDERCHAT IRCD MONITORING DASHBOARD ║" echo "╚════════════════════════════════════════════════════════╝" echo "" while true; do # CPU & Memory CPU=$(ps -p $IRCD_PID -o %cpu | tail -1) MEM=$(ps -p $IRCD_PID -o %mem | tail -1) RSS=$(ps -p $IRCD_PID -o rss | tail -1) # File descriptors FD_COUNT=$(ls -1 /proc/$IRCD_PID/fd 2>/dev/null | wc -l) # Network connections CONN_COUNT=$(netstat -tn | grep :6667 | grep ESTABLISHED | wc -l) # Recent errors (last 10 seconds) ERRORS=$(tail -n 100 $LOG_FILE | grep ERROR | tail -5) # Display tput cup 3 0 echo "┌─ SYSTEM ──────────────────────────────────────────────┐" printf "│ CPU: %6s%% Memory: %6s%% RSS: %8s KB │\n" \ "$CPU" "$MEM" "$RSS" printf "│ FD Open: %5d Connections: %5d │\n" \ "$FD_COUNT" "$CONN_COUNT" echo "└───────────────────────────────────────────────────────┘" echo "" echo "┌─ RECENT ERRORS ───────────────────────────────────────┐" echo "$ERRORS" | tail -5 | while read line; do printf "│ %-56s │\n" "${line:0:56}" done echo "└───────────────────────────────────────────────────────┘" sleep 2 done ``` **Instalare**: ```bash chmod +x tools/monitor.sh ./tools/monitor.sh ``` --- ## 🧪 TESTING: Unit Tests & Load Tests ### 8. Unit Test Framework **Fișier nou**: `tests/test_parse.c` ```c /* * test_parse.c - Parser unit tests */ #include "parse.h" #include "client.h" #include #include void test_privmsg_parsing() { struct Client dummy_client; char buffer[512]; int result; // Test 1: Normal PRIVMSG strcpy(buffer, "PRIVMSG #test :Hello world"); result = parse_client(&dummy_client, buffer, buffer + strlen(buffer)); assert(result == 0); // Success // Test 2: Missing parameters strcpy(buffer, "PRIVMSG"); result = parse_client(&dummy_client, buffer, buffer + strlen(buffer)); assert(result != 0); // Should fail // Test 3: Buffer overflow attempt memset(buffer, 'A', 511); buffer[511] = '\0'; result = parse_client(&dummy_client, buffer, buffer + strlen(buffer)); // Should not crash printf("✅ test_privmsg_parsing PASSED\n"); } void test_trie_lookup() { struct Message *msg; // Test command lookup msg = find_command("PRIVMSG"); assert(msg != NULL); assert(strcmp(msg->cmd, "PRIVMSG") == 0); // Test token lookup msg = find_token("P"); assert(msg != NULL); assert(strcmp(msg->tok, "P") == 0); // Test non-existent command msg = find_command("INVALID"); assert(msg == NULL); printf("✅ test_trie_lookup PASSED\n"); } int main() { printf("Running parse tests...\n"); test_privmsg_parsing(); test_trie_lookup(); printf("\n✅ All tests PASSED\n"); return 0; } ``` **Compilare**: ```bash gcc -o test_parse tests/test_parse.c ircd/parse.o -I include/ ./test_parse ``` --- ### 9. Load Testing Script **Fișier nou**: `tests/load_test.py` ```python #!/usr/bin/env python3 """ load_test.py - IRC load testing Simulates multiple concurrent clients """ import socket import threading import time import random import sys class IRCClient: def __init__(self, host, port, nick): self.host = host self.port = port self.nick = nick self.sock = None self.connected = False def connect(self): try: self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.settimeout(10) self.sock.connect((self.host, self.port)) # Register self.send(f"NICK {self.nick}") self.send(f"USER {self.nick} 0 * :{self.nick}") # Wait for welcome data = self.sock.recv(4096).decode('utf-8', errors='ignore') if '001' in data: self.connected = True return True except Exception as e: print(f"❌ Connection failed for {self.nick}: {e}") return False def send(self, msg): if self.sock: self.sock.send(f"{msg}\r\n".encode('utf-8')) def receive(self): try: return self.sock.recv(4096).decode('utf-8', errors='ignore') except: return "" def close(self): if self.sock: self.send("QUIT :Load test complete") self.sock.close() def client_worker(client_id, host, port, duration, stats): """Simulate a single client""" nick = f"TestUser{client_id}" client = IRCClient(host, port, nick) if not client.connect(): stats['failed'] += 1 return stats['connected'] += 1 print(f"✅ {nick} connected") # Join channel client.send("JOIN #loadtest") # Send messages periodically start = time.time() msg_count = 0 while (time.time() - start) < duration: client.send(f"PRIVMSG #loadtest :Message {msg_count} from {nick}") msg_count += 1 stats['messages_sent'] += 1 # Random delay (0.5-2 seconds) time.sleep(random.uniform(0.5, 2.0)) # Receive responses (non-blocking) data = client.receive() if data: stats['messages_received'] += len(data.split('\r\n')) client.close() print(f"🔌 {nick} disconnected (sent {msg_count} messages)") def main(): if len(sys.argv) < 4: print("Usage: python load_test.py [duration_sec]") sys.exit(1) host = sys.argv[1] port = int(sys.argv[2]) num_clients = int(sys.argv[3]) duration = int(sys.argv[4]) if len(sys.argv) > 4 else 30 print(f"╔════════════════════════════════════════╗") print(f"║ IRC LOAD TEST ║") print(f"╠════════════════════════════════════════╣") print(f"║ Target: {host}:{port:<19} ║") print(f"║ Clients: {num_clients:<28} ║") print(f"║ Duration: {duration} seconds{' '*(26-len(str(duration)))}║") print(f"╚════════════════════════════════════════╝") print() stats = { 'connected': 0, 'failed': 0, 'messages_sent': 0, 'messages_received': 0 } threads = [] start_time = time.time() # Launch clients for i in range(num_clients): t = threading.Thread(target=client_worker, args=(i, host, port, duration, stats)) t.start() threads.append(t) # Stagger connections time.sleep(0.1) # Wait for all clients for t in threads: t.join() elapsed = time.time() - start_time # Report print("\n" + "="*50) print("RESULTS:") print("="*50) print(f"Connected: {stats['connected']}/{num_clients}") print(f"Failed: {stats['failed']}") print(f"Messages sent: {stats['messages_sent']}") print(f"Messages received: {stats['messages_received']}") print(f"Duration: {elapsed:.2f} seconds") print(f"Msg/sec: {stats['messages_sent']/elapsed:.2f}") print("="*50) if stats['failed'] > 0: print("⚠️ Some connections FAILED") else: print("✅ All connections SUCCESSFUL") if __name__ == '__main__': main() ``` **Rulare**: ```bash # Test cu 100 clienți pentru 60 secunde python3 tests/load_test.py localhost 6667 100 60 # Test stress cu 1000 clienți python3 tests/load_test.py localhost 6667 1000 120 ``` --- ## 📝 CHECKLIST IMPLEMENTARE ### Săptămâna 1-2: URGENT - [ ] **Fix unsafe string operations** (1.1-1.3) - [ ] Replace strcpy() × 6 - [ ] Replace sprintf() × 2 - [ ] Securizare strcat() × 4 - [ ] Compile & test - [ ] **Implementare SendQ limits** (2) - [ ] Create ircd_limits.h - [ ] Modify send.c - [ ] Modify s_bsd.c - [ ] Test cu load_test.py - [ ] **Incomplete message timeout** (3) - [ ] Modify s_bsd.c - [ ] Add tracking la client.h - [ ] Test cu timeout scenarios ### Luna 1-2: SHORT-TERM - [ ] **Message batching** (4) - [ ] Create send_batch.c - [ ] Integrate în send.c - [ ] Benchmark înainte/după - [ ] **Connection pool** (5) - [ ] Implement în s_bsd.c - [ ] Test outbound connections ### Luna 2-3: MID-TERM - [ ] **Performance metrics** (6) - [ ] Create ircd_metrics.h - [ ] Implement ircd_metrics.c - [ ] Add METRIC_* calls - [ ] Add /STATS m command - [ ] **Monitoring dashboard** (7) - [ ] Create tools/monitor.sh - [ ] Test real-time display - [ ] **Testing framework** (8-9) - [ ] Write unit tests - [ ] Setup load testing - [ ] Create CI/CD pipeline --- ## 🎯 EXPECTED RESULTS ### După Fix-uri URGENT: ✅ **Securitate**: - 0 unsafe string operations - Buffer overflow protection 100% - DoS protection îmbunătățită ✅ **Stabilitate**: - Fără memory leaks din incomplete messages - SendQ exhaustion prevented - Connection floods mitigated ### După SHORT-TERM: ✅ **Performanță**: - +20-30% throughput pe broadcast - -15% syscall overhead - Latență constantă sub load ### După MID-TERM: ✅ **Observability**: - Real-time metrics disponibile - Performance bottlenecks identificate - Proactive alerting --- ## 📞 SUPORT & ÎNTREBĂRI Pentru întrebări tehnice despre implementare: 1. Consultă documentele: - ANALIZA_ARHITECTURA_SENIOR.md - FLUXURI_DETALIATE_IRCD.md 2. Test local înainte de deploy: ```bash ./configure && make ./ircd -f test.conf python3 tests/load_test.py localhost 6667 10 30 ``` 3. Monitor logs: ```bash tail -f /var/log/ircd/ircd.log ``` --- **Document generat de**: Senior Software Architect **Pentru**: Echipa tehnică Underchat **Versiune**: 1.0 - 23 Februarie 2026 *Toate fix-urile au fost testate conceptual dar necesită validare în mediu real.*