1006 lines
24 KiB
Markdown
1006 lines
24 KiB
Markdown
# 🔧 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 <string.h>
|
||
|
||
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 <assert.h>
|
||
#include <string.h>
|
||
|
||
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 <host> <port> <num_clients> [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.*
|
||
|