fix(chat): build complete diagnostics with checked buffers and SQL results

This commit is contained in:
evgeny committed 2026-10-27 05:31:19 +01:00
1 parent 2504dd0b40
commit 8de9cfb1d2
3 files changed
+82 -56

No files matched your search

+9 -1
View File
@@ -11,6 +11,8 @@
#define STRBUF_MIN_CAP 16
void strbuf_init(struct strbuf *sb, size_t initial_cap) {
*sb = strbuf_new();
if (initial_cap > UINT32_MAX) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "strbuf: capacity exceeds allocator range=%zu", initial_cap); return; }
if (initial_cap < STRBUF_MIN_CAP) initial_cap = STRBUF_MIN_CAP;
sb->buf = u_malloc(initial_cap);
if (sb->buf) { sb->buf[0] = '\0'; sb->cap = initial_cap; }
@@ -35,8 +37,9 @@ void strbuf_reset(struct strbuf *sb) {
}
static int strbuf_grow(struct strbuf *sb, size_t need) {
if (need > UINT32_MAX) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "strbuf: size exceeds allocator range=%zu", need); return -1; }
size_t nc = sb->cap ? sb->cap : STRBUF_MIN_CAP;
while (nc < need) { nc *= 2; if (nc < STRBUF_MIN_CAP) return -1; }
while (nc < need) { if (nc > UINT32_MAX / 2) { nc = need; break; } nc *= 2; }
char *n = u_realloc(sb->buf, nc);
if (!n) return -1;
sb->buf = n; sb->cap = nc;
@@ -51,6 +54,9 @@ int strbuf_addf(struct strbuf *sb, const char *fmt, ...) {
int need = vsnprintf(NULL, 0, fmt, ap);
va_end(ap);
if (need < 0) { return -1; }
if ((size_t)need > UINT32_MAX - 1 - sb->len) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "strbuf: formatted text exceeds allocator range"); return -1;
}
/* grow */
size_t want = sb->len + (size_t)need + 1; /* +1 for '\0' */
@@ -65,6 +71,7 @@ int strbuf_addf(struct strbuf *sb, const char *fmt, ...) {
}
void strbuf_addc(struct strbuf *sb, char c) {
if (sb->len > UINT32_MAX - 2) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "strbuf: character exceeds allocator range"); return; }
if (sb->len + 2 > sb->cap && strbuf_grow(sb, sb->len + 2) < 0) return;
sb->buf[sb->len++] = c;
sb->buf[sb->len] = '\0';
@@ -96,6 +103,7 @@ failed:
void strbuf_adds(struct strbuf *sb, const char *s) {
if (!s) return;
size_t slen = strlen(s);
if (slen > UINT32_MAX - 1 - sb->len) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "strbuf: string exceeds allocator range"); return; }
if (sb->len + slen + 1 > sb->cap && strbuf_grow(sb, sb->len + slen + 1) < 0) return;
memcpy(sb->buf + sb->len, s, slen);
sb->len += slen;
+15 -7
View File
@@ -528,17 +528,25 @@ overflow:
}
int chat_core_get_node_name(struct UTUN_INSTANCE* inst, uint64_t node_id, char* out, size_t sz) {
struct chat_core_ctx* cc = CC(inst);
if (!cc || !cc->initialized || !out) return -1;
out[0] = '\0';
struct chat_core_ctx* cc = inst ? CC(inst) : NULL;
if (out && sz) out[0] = 0;
if (!cc || !cc->initialized || !cc->db || !out || !sz) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "node name: invalid context/output node=%016llx", (unsigned long long)node_id); return -1;
}
sqlite3_stmt* st = NULL;
if (sqlite3_prepare_v2(cc->db, "SELECT name FROM nodes WHERE node_id=?", -1, &st, NULL) != SQLITE_OK) return -1;
sqlite3_bind_int64(st, 1, (sqlite3_int64)node_id);
if (sqlite3_step(st) == SQLITE_ROW) {
int rc = sqlite3_prepare_v2(cc->db, "SELECT name FROM nodes WHERE node_id=?", -1, &st, NULL);
if (rc == SQLITE_OK) rc = sqlite3_bind_int64(st, 1, (sqlite3_int64)node_id);
if (rc == SQLITE_OK) rc = sqlite3_step(st);
if (rc == SQLITE_ROW) {
const char* n = (const char*)sqlite3_column_text(st, 0);
if (n) snprintf(out, sz, "%s", n);
else if (sqlite3_errcode(cc->db) == SQLITE_NOMEM) rc = SQLITE_NOMEM;
}
int final = sqlite3_finalize(st);
if ((rc != SQLITE_ROW && rc != SQLITE_DONE) || final != SQLITE_OK) {
out[0] = 0; DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "node name: lookup node=%016llx rc=%d finalize=%d: %s",
(unsigned long long)node_id, rc, final, sqlite3_errmsg(cc->db)); return -1;
}
sqlite3_finalize(st);
return 0;
}
+58 -48
View File
@@ -13,6 +13,8 @@
#include "../ntp_time.h"
#include "../../lib/ll_queue.h"
#include "../../lib/mem.h"
#include "../../lib/strbuf.h"
#include <limits.h>
#include "../../lib/platform_compat.h"
#include "../transport_layer/etcp.h"
#include "../transport_layer/etcp_connections.h"
@@ -23,6 +25,9 @@
#include "../routing_layer/topo_group.h"
#include <string.h>
/* Ошибка форматирования/памяти не публикует частичный ответ. */
#define STATUS_APPEND(...) do { if (strbuf_addf(&text, __VA_ARGS__) < 0 || text.len > INT_MAX) goto error; } while (0)
struct address_profile_text { char text[80]; };
static struct address_profile_text link_address_profile_text(const struct ETCP_LINK *link) {
@@ -59,23 +64,13 @@ static struct address_profile_text link_address_profile_text(const struct ETCP_L
}
static void get_node_name(struct UTUN_INSTANCE* inst, uint64_t node_id, char* out, size_t sz) {
struct chat_core_ctx* cc = CC(inst);
out[0] = '\0';
if (!cc || !cc->db || node_id == 0) return;
sqlite3_stmt* st = NULL;
if (sqlite3_prepare_v2(cc->db, "SELECT name FROM nodes WHERE node_id=?", -1, &st, NULL) != SQLITE_OK) return;
sqlite3_bind_int64(st, 1, (sqlite3_int64)node_id);
if (sqlite3_step(st) == SQLITE_ROW) {
const char* n = (const char*)sqlite3_column_text(st, 0);
if (n) snprintf(out, sz, "%s", n);
}
sqlite3_finalize(st);
chat_core_get_node_name(inst, node_id, out, sz);
}
static void chat_core_collect_status(struct UTUN_INSTANCE* inst) {
char buf[8192]; int off = 0;
char buf[96]; int off = 0; struct strbuf text = strbuf_new();
if (!CC(inst) || !CC(inst)->initialized || !inst) {
if (!inst || !CC(inst) || !CC(inst)->initialized) {
off = snprintf(buf, sizeof(buf), "uTun not initialized\n");
chat_event_post(inst, CHAT_EVT_STATUS_REFRESH, (const uint8_t*)buf, off);
return;
@@ -83,32 +78,32 @@ static void chat_core_collect_status(struct UTUN_INSTANCE* inst) {
struct uasync_load load;
if (uasync_get_load(inst->ua, &load) == ERR_OK && load.valid) {
off += snprintf(buf + off, sizeof(buf) - off, "uasync CPU: %.1f%%, average %.1f%% (window %.0f ms, age %.0f ms)\n",
STATUS_APPEND( "uasync CPU: %.1f%%, average %.1f%% (window %.0f ms, age %.0f ms)\n",
load.current_percent, load.average_percent, load.window_us / 1000.0,
(get_time_us() - load.sample_time_us) / 1000.0);
} else off += snprintf(buf + off, sizeof(buf) - off, "uasync CPU: unavailable\n");
} else STATUS_APPEND( "uasync CPU: unavailable\n");
struct NTP_TIME* ntp = &inst->ntp;
off += snprintf(buf + off, sizeof(buf) - off, "=== NTP ===\n");
off += snprintf(buf + off, sizeof(buf) - off, "Enabled: %s\n", ntp->enabled ? "yes" : "no");
off += snprintf(buf + off, sizeof(buf) - off, "Synced: %s\n", ntp->synced ? "yes" : "no");
off += snprintf(buf + off, sizeof(buf) - off, "Offset: %.1f s\n", ntp->offset_us / 1000000.0);
STATUS_APPEND( "=== NTP ===\n");
STATUS_APPEND( "Enabled: %s\n", ntp->enabled ? "yes" : "no");
STATUS_APPEND( "Synced: %s\n", ntp->synced ? "yes" : "no");
STATUS_APPEND( "Offset: %.1f s\n", ntp->offset_us / 1000000.0);
if (ntp->server_count > 0 && ntp->servers) {
off += snprintf(buf + off, sizeof(buf) - off, "Servers: %d\n", ntp->server_count);
STATUS_APPEND( "Servers: %d\n", ntp->server_count);
for (int i = 0; i < ntp->server_count && ntp->servers[i]; i++)
off += snprintf(buf + off, sizeof(buf) - off, " [%d] %s%s\n", i, ntp->servers[i],
STATUS_APPEND( " [%d] %s%s\n", i, ntp->servers[i],
i == ntp->server_current ? " (current)" : "");
}
if (ntp->synced && ntp->last_sync_tb > 0) {
uint64_t now_tb = get_time_tb();
uint64_t ago_sec = (now_tb - ntp->last_sync_tb) / 10000;
off += snprintf(buf + off, sizeof(buf) - off, "Last sync: %llu sec ago\n", (unsigned long long)ago_sec);
STATUS_APPEND( "Last sync: %llu sec ago\n", (unsigned long long)ago_sec);
}
int conn_count = 0;
struct ll_entry* entry = inst->connections ? inst->connections->head : NULL;
while (entry) { conn_count++; entry = entry->next; }
off += snprintf(buf + off, sizeof(buf) - off, "=== Connections (%d) ===\n", conn_count);
STATUS_APPEND( "=== Connections (%d) ===\n", conn_count);
uint64_t my_id = inst->node_id;
entry = inst->connections ? inst->connections->head : NULL;
@@ -133,11 +128,11 @@ static void chat_core_collect_status(struct UTUN_INSTANCE* inst) {
get_node_name(inst, pid, peername, sizeof(peername));
if (peername[0])
off += snprintf(buf + off, sizeof(buf) - off, "[%s]→[%s] %s ETCP:%s%s(%dL)\n",
STATUS_APPEND( "[%s]→[%s] %s ETCP:%s%s(%dL)\n",
myhex, peerhex, peername,
conn->links_up ? "UP" : "DOWN", init_str, link_count);
else
off += snprintf(buf + off, sizeof(buf) - off, "[%s]→[%s] ETCP:%s%s(%dL)\n",
STATUS_APPEND( "[%s]→[%s] ETCP:%s%s(%dL)\n",
myhex, peerhex,
conn->links_up ? "UP" : "DOWN", init_str, link_count);
@@ -146,7 +141,7 @@ static void chat_core_collect_status(struct UTUN_INSTANCE* inst) {
while (link) {
char rtt_str[32]; rtt_str[0] = '\0';
if (link->rtt_last > 0) snprintf(rtt_str, sizeof(rtt_str), " / rtt=%ums", link->rtt_last / 10);
off += snprintf(buf + off, sizeof(buf) - off, " LINK#%d: %s %s %s NAT=%s%s\n",
STATUS_APPEND( " LINK#%d: %s %s %s NAT=%s%s\n",
link_idx,
link->is_tcp ? "TCP" : "UDP",
link->conn ? link->conn->name : "?",
@@ -159,7 +154,10 @@ static void chat_core_collect_status(struct UTUN_INSTANCE* inst) {
entry = entry->next;
}
chat_event_post(inst, CHAT_EVT_STATUS_REFRESH, (const uint8_t*)buf, off);
chat_event_post(inst, CHAT_EVT_STATUS_REFRESH, (const uint8_t*)strbuf_str(&text), (int)text.len);
strbuf_free(&text); return;
error:
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "status: formatting/allocation failed bytes=%zu", text.len); strbuf_free(&text);
}
/* ── Бинарный список соединений: [count:2][entry:46B]* ── */
@@ -167,16 +165,16 @@ static void chat_core_collect_status(struct UTUN_INSTANCE* inst) {
#define CONN_LIST_ENTRY_SIZE 46
static void collect_conn_list(struct UTUN_INSTANCE* inst) {
if (!CC(inst) || !CC(inst)->initialized || !inst || !inst->connections) return;
if (!inst || !CC(inst) || !CC(inst)->initialized || !inst->connections) return;
uint16_t count = 0;
struct ll_entry* entry = inst->connections->head;
while (entry) { count++; entry = entry->next; }
if (count > 250) count = 250;
while (entry && count < 250) { count++; entry = entry->next; }
if (entry) DEBUG_DEBUG(DEBUG_CATEGORY_CHAT_SYNC, "connection list limited to250 entries");
size_t buf_sz = 2 + (size_t)count * CONN_LIST_ENTRY_SIZE;
uint8_t* buf = (uint8_t*)u_malloc(buf_sz);
if (!buf) return;
if (!buf) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "connection list allocation failed bytes=%zu", buf_sz); return; }
uint16_t* hdr = (uint16_t*)buf;
*hdr = count;
@@ -200,14 +198,15 @@ static void collect_conn_list(struct UTUN_INSTANCE* inst) {
*p++ = flags;
uint8_t lc = 0;
struct ETCP_LINK* tl = conn->links; while (tl) { lc++; tl = tl->next; }
struct ETCP_LINK* tl = conn->links; while (tl && lc < UINT8_MAX) { lc++; tl = tl->next; }
*p++ = lc;
uint16_t rtt = 0;
if (conn->links) rtt = conn->links->rtt_last;
memcpy(p, &rtt, 2); p += 2;
uint16_t inflight_kb = (uint16_t)(conn->unacked_bytes / 1024);
uint64_t inflight = conn->unacked_bytes / 1024;
uint16_t inflight_kb = inflight > UINT16_MAX ? UINT16_MAX : (uint16_t)inflight;
memcpy(p, &inflight_kb, 2); p += 2;
char name[32]; memset(name, 0, 32);
@@ -222,9 +221,9 @@ static void collect_conn_list(struct UTUN_INSTANCE* inst) {
/* ── Полный текстовый дамп одного соединения ── */
static void collect_conn_metrics(struct UTUN_INSTANCE* inst, uint64_t peer_node_id) {
char buf[8192]; int off = 0;
char buf[96]; int off = 0; struct strbuf text = strbuf_new();
if (!CC(inst) || !CC(inst)->initialized || !inst || !inst->connections) {
if (!inst || !CC(inst) || !CC(inst)->initialized || !inst->connections) {
off = snprintf(buf, sizeof(buf), "uTun not initialized\n");
chat_event_post(inst, CHAT_EVT_CONN_METRICS, (const uint8_t*)buf, off);
return;
@@ -255,7 +254,7 @@ static void collect_conn_metrics(struct UTUN_INSTANCE* inst, uint64_t peer_node_
int link_count = 0, links_up_count = 0;
struct ETCP_LINK* tl = conn->links; while (tl) { if (tl->link_status) links_up_count++; link_count++; tl = tl->next; }
off += snprintf(buf + off, sizeof(buf) - off,
STATUS_APPEND(
"=== PEER: %04llX\u2192%04llX [%s] ===\n"
"Role: %s\n"
"Status: %s Links: %d/%d Initialized: %s MTU: %d\n"
@@ -278,25 +277,25 @@ static void collect_conn_metrics(struct UTUN_INSTANCE* inst, uint64_t peer_node_
conn->rx_dup_count, conn->tx_dup_count,
conn->next_tx_id, conn->last_rx_id, conn->last_delivered_id, conn->rx_ack_till);
if (conn->input_queue) off += snprintf(buf + off, sizeof(buf) - off, "input_q: %uB/%dp ", (unsigned)queue_total_bytes(conn->input_queue), queue_entry_count(conn->input_queue));
if (conn->input_send_q) off += snprintf(buf + off, sizeof(buf) - off, "send_q: %uB/%dp ", (unsigned)queue_total_bytes(conn->input_send_q), queue_entry_count(conn->input_send_q));
if (conn->input_wait_ack) off += snprintf(buf + off, sizeof(buf) - off, "wait_ack: %uB/%dp\n", (unsigned)queue_total_bytes(conn->input_wait_ack), queue_entry_count(conn->input_wait_ack));
if (conn->input_queue) STATUS_APPEND( "input_q: %uB/%dp ", (unsigned)queue_total_bytes(conn->input_queue), queue_entry_count(conn->input_queue));
if (conn->input_send_q) STATUS_APPEND( "send_q: %uB/%dp ", (unsigned)queue_total_bytes(conn->input_send_q), queue_entry_count(conn->input_send_q));
if (conn->input_wait_ack) STATUS_APPEND( "wait_ack: %uB/%dp\n", (unsigned)queue_total_bytes(conn->input_wait_ack), queue_entry_count(conn->input_wait_ack));
unsigned ack_bytes = 0, ack_packets = 0;
for (struct ETCP_LINK* link = conn->links; link; link = link->next) {
ack_bytes += (unsigned)queue_total_bytes(link->ack_q); ack_packets += queue_entry_count(link->ack_q);
}
off += snprintf(buf + off, sizeof(buf) - off, "ack_q: %uB/%up ", ack_bytes, ack_packets);
if (conn->recv_q) off += snprintf(buf + off, sizeof(buf) - off, "recv_q: %uB/%dp ", (unsigned)queue_total_bytes(conn->recv_q), queue_entry_count(conn->recv_q));
if (conn->output_queue) off += snprintf(buf + off, sizeof(buf) - off, "out_q: %uB/%dp\n", (unsigned)queue_total_bytes(conn->output_queue), queue_entry_count(conn->output_queue));
STATUS_APPEND( "ack_q: %uB/%up ", ack_bytes, ack_packets);
if (conn->recv_q) STATUS_APPEND( "recv_q: %uB/%dp ", (unsigned)queue_total_bytes(conn->recv_q), queue_entry_count(conn->recv_q));
if (conn->output_queue) STATUS_APPEND( "out_q: %uB/%dp\n", (unsigned)queue_total_bytes(conn->output_queue), queue_entry_count(conn->output_queue));
off += snprintf(buf + off, sizeof(buf) - off, "\n--- ACK Debug ---\n"
STATUS_APPEND( "\n--- ACK Debug ---\n"
"hit_inf=%u hit_sndq=%u miss=%u link_wait=%u\n",
conn->cnt_ack_hit_inf, conn->cnt_ack_hit_sndq, conn->cnt_ack_miss, conn->cnt_link_wait);
/* Normalizer */
if (conn->normalizer) {
struct PKTNORM* pn = conn->normalizer;
off += snprintf(buf + off, sizeof(buf) - off,
STATUS_APPEND(
"\n--- Normalizer ---\n"
"in: %llu pkts / %llu B out: %llu pkts / %llu B\n"
"frag_size=%u data_ptr=%u data_size=%u alloc_err=%u logic_err=%u\n",
@@ -313,7 +312,8 @@ static void collect_conn_metrics(struct UTUN_INSTANCE* inst, uint64_t peer_node_
char local_str[54], remote_str[54];
strncpy(local_str, link->conn ? (link->conn->interface_addr.ss_family ? sockaddr_storage_to_str(&link->conn->interface_addr).str : sockaddr_storage_to_str(&link->conn->local_addr).str) : "stcp", sizeof(local_str) - 1);
strncpy(remote_str, sockaddr_storage_to_str(&link->remote_addr).str, sizeof(remote_str) - 1);
off += snprintf(buf + off, sizeof(buf) - off,
local_str[sizeof(local_str) - 1] = 0; remote_str[sizeof(remote_str) - 1] = 0;
STATUS_APPEND(
"\n--- LINK#%d: %s %s NAT=%s ---\n"
"addr: %s -> %s\n"
"RTT: %.1f ms TT: %.1f ms\n"
@@ -330,7 +330,11 @@ static void collect_conn_metrics(struct UTUN_INSTANCE* inst, uint64_t peer_node_
link = link->next; link_idx++;
}
chat_event_post(inst, CHAT_EVT_CONN_METRICS, (const uint8_t*)buf, off);
chat_event_post(inst, CHAT_EVT_CONN_METRICS, (const uint8_t*)strbuf_str(&text), (int)text.len);
strbuf_free(&text); return;
error:
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "connection metrics: formatting/allocation failed peer=%016llx bytes=%zu",
(unsigned long long)peer_node_id, text.len); strbuf_free(&text);
}
void chat_core_collect_status_trampoline(void* arg) {
@@ -381,7 +385,8 @@ int chat_core_parse_member_detail(const uint8_t* data, size_t len, struct chat_m
v.hop_count = data[28]; v.link_count = data[29]; v.sock_count = data[30];
size_t expected = CHAT_MEMBER_DETAIL_HEADER_SIZE + (size_t)v.hop_count * 8 +
(size_t)v.link_count * CHAT_MEMBER_DETAIL_LINK_SIZE + (size_t)v.sock_count * CHAT_MEMBER_DETAIL_SOCK_SIZE;
if (!v.request_id || !v.group_id || !v.node_id || v.result > CHAT_MEMBER_DETAIL_ERROR || v.transport_flags > 7 ||
if (((!v.request_id || !v.group_id || !v.node_id) && v.result != CHAT_MEMBER_DETAIL_INVALID) ||
v.result > CHAT_MEMBER_DETAIL_ERROR || v.transport_flags > 7 ||
v.hop_count > CHAT_MEMBER_DETAIL_MAX_HOPS || v.link_count > CHAT_MEMBER_DETAIL_MAX_LINKS ||
v.sock_count > CHAT_MEMBER_DETAIL_MAX_SOCKS || expected != len ||
(v.result && (v.transport_flags || v.own_tcp_active || v.hop_count || v.link_count || v.sock_count))) {
@@ -445,7 +450,12 @@ static void collect_member_detail(const struct chat_member_detail_req* req) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "member detail: query request=%llu rc=%d: %s",
(unsigned long long)req->request_id, rc, sqlite3_errmsg(CC(inst)->db));
}
sqlite3_finalize(st);
int final = sqlite3_finalize(st);
if (final != SQLITE_OK) {
buf[24] = CHAT_MEMBER_DETAIL_ERROR;
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "member detail: finalize request=%llu rc=%d: %s",
(unsigned long long)req->request_id, final, sqlite3_errmsg(CC(inst)->db));
}
}
if (buf[24]) {
DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "member detail: rejected request=%llu group=%016llx node=%016llx result=%u",