Bound topology dump writes and return the actual length

This commit is contained in:
evgeny committed 2026-10-24 13:45:08 +02:00
1 parent 8dcd2fac3b
commit 324b289968
3 files changed
+76 -13

No files matched your search

+35 -12
View File
@@ -1,6 +1,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <limits.h>
#include "../lib/ll_queue.h"
#include "../lib/debug_config.h"
@@ -971,23 +972,36 @@ void topo_node_dump_all(struct TOPO_GROUP* group) {
/* То же, что dump_all, но в строковый буфер (для control-сервера). */
int topo_node_format_all(struct TOPO_GROUP* group, char* buf, size_t buf_size) {
if (!group || !group->nodes || !buf || buf_size == 0) return 0;
int pos = 0;
if (buf && buf_size) buf[0] = '\0';
if (!group || !group->nodes || !buf || buf_size == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "node dump: invalid arguments group=%p buf=%p capacity=%zu", (void*)group, (void*)buf, buf_size);
return 0;
}
if (buf_size > INT_MAX) buf_size = INT_MAX;
size_t pos = 0;
pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0,
"=== BGP Nodes dump: %d nodes ===\n", queue_entry_count(group->nodes));
#define FMT_ADD(...) do { \
int added = snprintf(buf + pos, buf_size - pos, __VA_ARGS__); \
if (added < 0) { \
buf[pos] = '\0'; \
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "node dump: formatting failed after %zu bytes", pos); \
return -1; \
} \
if ((size_t)added >= buf_size - pos) { pos = buf_size - 1; goto truncated; } \
pos += (size_t)added; \
} while (0)
FMT_ADD("=== BGP Nodes dump: %d nodes ===\n", queue_entry_count(group->nodes));
int idx = 0;
struct ll_entry* e = group->nodes->head;
while (e && (size_t)pos < buf_size) {
while (e) {
struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)e;
struct TOPO_NODE* ni = topo_node_registry_find(group->instance->topo_groups, nq->node_id);
if (!ni) { e = e->next; continue; }
int is_local = (group->local_node == nq);
const char* name_s = ni->node_name ? ni->node_name : "";
#define FMT_ADD(fmt, ...) pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0, fmt, ##__VA_ARGS__)
FMT_ADD("--- Node #%d: id=%016llx name=\"%s\" ver=%u grp=%llu%s ---\n",
idx, (unsigned long long)ni->node_id, name_s, ni->ver,
(unsigned long long)group->group_id, is_local ? " [LOCAL]" : "");
@@ -1052,15 +1066,24 @@ int topo_node_format_all(struct TOPO_GROUP* group, char* buf, size_t buf_size) {
FMT_ADD("]\n");
}
#undef FMT_ADD
e = e->next; idx++;
}
#undef FMT_ADD
if ((size_t)pos >= buf_size) {
pos = (int)buf_size - 14; if (pos < 0) pos = 0;
pos += snprintf(buf + pos, buf_size - pos, "\n[TRUNCATED]\n");
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "node dump: group=%016llx nodes=%d written=%zu capacity=%zu",
(unsigned long long)group->group_id, idx, pos, buf_size);
return (int)pos;
truncated: {
const char marker[] = "\n[TRUNCATED]\n";
size_t marker_len = sizeof(marker) - 1;
if (marker_len > pos) marker_len = pos;
memcpy(buf + pos - marker_len, marker, marker_len);
buf[pos] = '\0';
DEBUG_WARN(DEBUG_CATEGORY_BGP, "node dump truncated: group=%016llx written=%zu capacity=%zu",
(unsigned long long)group->group_id, pos, buf_size);
return (int)pos;
}
return pos;
}
// ===== update my nodeinfo =====
+4 -1
View File
@@ -278,8 +278,11 @@ int topo_node_init_self(struct UTUN_INSTANCE* instance);
int topo_node_update_my_addresses(struct UTUN_INSTANCE* instance); /* 1=changed, 0=unchanged, -1=error */
/* Обработчик изменений локального сокета: обновляет адреса узла. */
void topo_node_on_socket_changed(struct ETCP_SOCKET* sock, int event, void* arg);
/* Вывести диагностику узлов группы в лог / текстовый буфер. */
/* Вывести диагностику узлов группы в лог категории BGP на уровне INFO. */
void topo_node_dump_all(struct TOPO_GROUP* group);
/* Записать дамп в buf: длина без NUL или -1 при ошибке форматирования. Ёмкость ограничена INT_MAX.
* При усечении завершает запись сразу, заменяет хвост доступной частью "\n[TRUNCATED]\n" и возвращает реальную длину.
* При buf != NULL и buf_size > 0 строка всегда завершена NUL. Некорректные аргументы дают 0 и пустую строку, если есть буфер. */
int topo_node_format_all(struct TOPO_GROUP* group, char* buf, size_t buf_size);
/* 1, если хотя бы в одной группе пора повторить ping этого узла; иначе 0. */
int topo_node_ping_request_cbk(struct TOPO_GROUPS* groups, uint64_t node_id);
+37
View File
@@ -142,11 +142,47 @@ static void test_compact_addresses(struct UTUN_INSTANCE *inst) {
sqlite3_close(db); inst->topo_sqlite_db = saved_db;
}
/* Каждый размер буфера: длина, NUL, защитные байты, сохранённый префикс и маркер усечения. */
static void test_node_format(struct TOPO_GROUP* group) {
char full[8192];
int full_len = topo_node_format_all(group, full, sizeof(full));
assert(full_len > 0 && (size_t)full_len == strlen(full) && !strstr(full, "[TRUNCATED]"));
assert(strstr(full, "=== BGP Nodes dump:"));
if (group->nodes->head) assert(strstr(full, " v4addr[0]:") && strstr(full, " path[0]:"));
const char marker[] = "\n[TRUNCATED]\n";
debug_set_category_level(DEBUG_CATEGORY_BGP, DEBUG_LEVEL_ERROR);
for (size_t capacity = 0; capacity <= (size_t)full_len + 2; capacity++) {
uint8_t* storage = u_malloc(capacity + 2);
assert(storage); memset(storage, 0xa5, capacity + 2);
char* buf = (char*)storage + 1;
int length = topo_node_format_all(group, buf, capacity);
assert(storage[0] == 0xa5 && storage[capacity + 1] == 0xa5);
if (!capacity) { assert(!length); u_free(storage); continue; }
size_t expected = (size_t)full_len < capacity ? (size_t)full_len : capacity - 1;
assert(length >= 0 && (size_t)length == expected);
assert(memchr(buf, '\0', capacity) == buf + length);
if (capacity > (size_t)full_len) assert(!memcmp(buf, full, full_len + 1));
else {
size_t marker_len = expected < sizeof(marker) - 1 ? expected : sizeof(marker) - 1;
assert(!memcmp(buf, full, expected - marker_len));
assert(!memcmp(buf + expected - marker_len, marker, marker_len));
}
u_free(storage);
}
char empty[] = "old";
assert(!topo_node_format_all(NULL, empty, sizeof(empty)) && !empty[0]);
assert(!topo_node_format_all(group, NULL, sizeof(empty)));
debug_set_category_level(DEBUG_CATEGORY_BGP, DEBUG_LEVEL_DEBUG);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "[PASS] node dump: nodes=%d capacities=0..%d, lengths, guards, NUL and truncation",
queue_entry_count(group->nodes), full_len + 2);
}
static void test_bgp(struct UTUN_INSTANCE* signer) {
struct UTUN_INSTANCE receiver = { .ua = signer->ua, .topo_groups = signer->topo_groups };
struct TOPO_GROUP group = { .instance = &receiver, .group_id = 10, .group_type = TOPO_GROUP_TYPE_CHAT };
struct ETCP_CONN from = { .instance = &receiver, .peer_node_id = 7 };
group.nodes = queue_new(receiver.ua, 16, 0, 8, "snapshot_bgp");
test_node_format(&group);
struct TOPO_NODE* ni = record(signer, INT64_MAX - 7, "BGP", 1);
from.peer_node_id = ni->node_id; from.links_up = 1;
struct TOPO_GROUP_NODE nq = {0};
@@ -158,6 +194,7 @@ static void test_bgp(struct UTUN_INSTANCE* signer) {
assert(peer && peer->last_timestamp == ni->timestamp && queue_entry_count(peer->paths) == 1);
assert(topo_group_process_nodeinfo(&group, &from, packet, (size_t)len + sizeof(struct TOPOMSG_HEADER)) == 0);
assert(queue_entry_count(peer->paths) == 1);
test_node_format(&group);
struct TOPOMSG_NODEINFO_PKT* wire = (struct TOPOMSG_NODEINFO_PKT*)packet;
wire->node.local_v4_subnets = 1; /* malformed CHAT не меняет уже принятую запись и маршрут */
assert(topo_group_process_nodeinfo(&group, &from, packet, (size_t)len + sizeof(struct TOPOMSG_HEADER)) < 0);