stcp: ed25519 pubkey exchange in handshake, fix instance destroy segfault, fix chat_tcp_test
- u_async.c: process immediate_queue BEFORE epoll_wait in uasync_poll - stcp_link.c: defensive NULL etcp_conn/etcp_link in stcp_link_close - utun_instance.c: move stcp_server_list_destroy_all to Phase J (before ETCP), add deferred drain in both Phase J and L - stcp.h/c: STCP_HS_ENC_CLIENT 6→38, STCP_HS_ENC_SERVER 7→39, exchange ed25519_pubkey in both handshake directions - etcp_connections.c: copy peer_ed25519 from STCP to ETCP_CONN in etcp_link_enter_ready_tcp; guard link->etcp in tcp_link_close_cb - chat_sync.c: fix lk->conn NULL dereference for TCP links - test_stcp.c: update stcp_server_create/stcp_client_connect callsites - tools/chat_tcp_test: change transport udp→tcp
This commit is contained in:
1 parent
a3783b5f55
commit
38c9f229bc
1272 files changed
+968388
-1
No files matched your search
@@ -0,0 +1,514 @@
|
||||
/**
|
||||
* Debug module
|
||||
*/
|
||||
|
||||
#include "debug_config.h"
|
||||
#include "platform_compat.h"
|
||||
#include "mem.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
void log_dump(int level, int category, const char* prefix, const uint8_t* data, size_t len) {
|
||||
if (!debug_should_output(level, category)) return;
|
||||
|
||||
char hex_buf[513] = {'\0'};
|
||||
size_t hex_len = 0;
|
||||
size_t show_len = (len > 128) ? 128 : len;
|
||||
|
||||
for (size_t i = 0; i < show_len && hex_len < 512 - 3; i++) {
|
||||
hex_len += snprintf(hex_buf + hex_len, sizeof(hex_buf) - hex_len, "%02x", data[i]);
|
||||
if (i < show_len - 1 && (i + 1) % 32 == 0)
|
||||
hex_len += snprintf(hex_buf + hex_len, sizeof(hex_buf) - hex_len, " ");
|
||||
}
|
||||
|
||||
if (len > 128)
|
||||
hex_len += snprintf(hex_buf + hex_len, sizeof(hex_buf) - hex_len, "...");
|
||||
|
||||
switch (level) {
|
||||
case DEBUG_LEVEL_ERROR: DEBUG_ERROR(category, "%s: len=%zu hex=%s", prefix, len, hex_buf); break;
|
||||
case DEBUG_LEVEL_WARN: DEBUG_WARN(category, "%s: len=%zu hex=%s", prefix, len, hex_buf); break;
|
||||
case DEBUG_LEVEL_INFO: DEBUG_INFO(category, "%s: len=%zu hex=%s", prefix, len, hex_buf); break;
|
||||
case DEBUG_LEVEL_DEBUG: DEBUG_DEBUG(category, "%s: len=%zu hex=%s", prefix, len, hex_buf); break;
|
||||
case DEBUG_LEVEL_TRACE: DEBUG_TRACE(category, "%s: len=%zu hex=%s", prefix, len, hex_buf); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ip_str_t ip_to_str(const void *addr, int family) {
|
||||
ip_str_t result;
|
||||
result.str[0] = '\0';
|
||||
|
||||
if (!addr) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (family == AF_INET) {
|
||||
inet_ntop(AF_INET, addr, result.str, 16);
|
||||
} else if (family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, addr, result.str, 48);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ip_str_t sockaddr_storage_to_str(const struct sockaddr_storage *addr) {
|
||||
ip_str_t result;
|
||||
result.str[0] = '\0';
|
||||
|
||||
if (!addr) {
|
||||
return result;
|
||||
}
|
||||
|
||||
char ip_buf[INET6_ADDRSTRLEN];
|
||||
ip_buf[0] = '\0';
|
||||
|
||||
uint16_t port = 0;
|
||||
void *src = NULL;
|
||||
|
||||
if (addr->ss_family == AF_INET) {
|
||||
const struct sockaddr_in *ipv4 = (const struct sockaddr_in *)addr;
|
||||
src = (void *)&ipv4->sin_addr;
|
||||
port = ntohs(ipv4->sin_port);
|
||||
}
|
||||
else if (addr->ss_family == AF_INET6) {
|
||||
const struct sockaddr_in6 *ipv6 = (const struct sockaddr_in6 *)addr;
|
||||
src = (void *)&ipv6->sin6_addr;
|
||||
port = ntohs(ipv6->sin6_port);
|
||||
}
|
||||
else {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!inet_ntop(addr->ss_family, src, ip_buf, sizeof(ip_buf))) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (addr->ss_family == AF_INET6) {
|
||||
// IPv6 обязательно в []
|
||||
snprintf(result.str, sizeof(result.str), "[%s]:%u", ip_buf, port);
|
||||
} else {
|
||||
snprintf(result.str, sizeof(result.str), "%s:%u", ip_buf, port);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const struct {
|
||||
const char* name;
|
||||
debug_category_t category;
|
||||
} g_debug_categories[] = {
|
||||
{"none", DEBUG_CATEGORY_NONE},
|
||||
{"sys", DEBUG_CATEGORY_SYS},
|
||||
{"connection", DEBUG_CATEGORY_CONNECTION},
|
||||
{"etcp", DEBUG_CATEGORY_ETCP},
|
||||
{"crypto", DEBUG_CATEGORY_CRYPTO},
|
||||
{"config", DEBUG_CATEGORY_CONFIG},
|
||||
{"tun", DEBUG_CATEGORY_TUN},
|
||||
{"routing", DEBUG_CATEGORY_ROUTING},
|
||||
{"timers", DEBUG_CATEGORY_TIMERS},
|
||||
{"bgp", DEBUG_CATEGORY_BGP},
|
||||
{"socket", DEBUG_CATEGORY_SOCKET},
|
||||
{"control", DEBUG_CATEGORY_CONTROL},
|
||||
{"dump", DEBUG_CATEGORY_DUMP},
|
||||
{"traffic", DEBUG_CATEGORY_TRAFFIC},
|
||||
{"debug", DEBUG_CATEGORY_DEBUG},
|
||||
{"general", DEBUG_CATEGORY_GENERAL},
|
||||
{"nat", DEBUG_CATEGORY_NAT},
|
||||
{"keepalive", DEBUG_CATEGORY_KEEPALIVE},
|
||||
{"etcp_route", DEBUG_CATEGORY_ETCPROUTE},
|
||||
{"etcp_dump", DEBUG_CATEGORY_ETCP_DUMP},
|
||||
{"chat_sync", DEBUG_CATEGORY_CHAT_SYNC},
|
||||
{"member_sync", DEBUG_CATEGORY_MEMBER_SYNC},
|
||||
{"all", DEBUG_CATEGORY_ALL},
|
||||
{NULL, DEBUG_CATEGORY_NONE}
|
||||
};
|
||||
|
||||
static const struct {
|
||||
const char* name;
|
||||
debug_level_t level;
|
||||
} g_level_table[] = {
|
||||
{"disabled", DEBUG_LEVEL_DISABLED},
|
||||
{"none", DEBUG_LEVEL_NONE},
|
||||
{"error", DEBUG_LEVEL_ERROR},
|
||||
{"warn", DEBUG_LEVEL_WARN},
|
||||
{"info", DEBUG_LEVEL_INFO},
|
||||
{"debug", DEBUG_LEVEL_DEBUG},
|
||||
{"trace", DEBUG_LEVEL_TRACE},
|
||||
{NULL, DEBUG_LEVEL_NONE}
|
||||
};
|
||||
|
||||
debug_category_t get_category_by_name(const char* name) {
|
||||
if (!name) return DEBUG_CATEGORY_NONE;
|
||||
for (int i = 0; g_debug_categories[i].name; i++) {
|
||||
if (strcasecmp(g_debug_categories[i].name, name) == 0) {
|
||||
return g_debug_categories[i].category;
|
||||
}
|
||||
}
|
||||
return DEBUG_CATEGORY_NONE;
|
||||
}
|
||||
|
||||
debug_level_t debug_level_from_name(const char* name) {
|
||||
if (!name) return DEBUG_LEVEL_NONE;
|
||||
for (int i = 0; g_level_table[i].name; i++) {
|
||||
if (strcasecmp(g_level_table[i].name, name) == 0) {
|
||||
return g_level_table[i].level;
|
||||
}
|
||||
}
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid debug level '%s' (valid: disabled,none,error,warn,info,debug,trace)", name);
|
||||
return DEBUG_LEVEL_INFO; /* fallback */
|
||||
}
|
||||
|
||||
void debug_set_category_level_by_name(const char* category_name, const char* level_name) {
|
||||
if (!category_name || !level_name) return;
|
||||
debug_category_t cat = get_category_by_name(category_name);
|
||||
if (cat == DEBUG_CATEGORY_NONE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Unknown debug category '%s'", category_name);
|
||||
return;
|
||||
}
|
||||
debug_level_t lvl = debug_level_from_name(level_name);
|
||||
debug_set_category_level(cat, lvl);
|
||||
}
|
||||
|
||||
int debug_apply_category_config(const char* category_name, const char* level_name) {
|
||||
if (!category_name || !level_name) return -1;
|
||||
debug_category_t cat = get_category_by_name(category_name);
|
||||
if (cat == DEBUG_CATEGORY_NONE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Unknown debug category '%s'", category_name);
|
||||
return -1;
|
||||
}
|
||||
debug_level_t lvl = debug_level_from_name(level_name);
|
||||
debug_set_category_level(cat, lvl);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Applied per-category debug: %s=%s", category_name, level_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void debug_apply_global_level(const char* level_name) {
|
||||
if (!level_name || !*level_name) return;
|
||||
debug_level_t level = debug_level_from_name(level_name);
|
||||
debug_set_level(level);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Applied global debug_level: %s", level_name);
|
||||
}
|
||||
|
||||
/* Get category name for logging */
|
||||
const char* debug_get_category_name(debug_category_t category_idx) {
|
||||
for (int i = 0; g_debug_categories[i].name; i++)
|
||||
if (g_debug_categories[i].category == category_idx) return g_debug_categories[i].name;
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
/* Global debug configuration */
|
||||
debug_config_t g_debug_config;
|
||||
static debug_log_hook_fn g_log_hook = NULL;
|
||||
|
||||
/* Initialize debug system with default settings */
|
||||
void debug_config_init(void) {
|
||||
g_debug_config.level = DEBUG_LEVEL_ERROR; // Global level: errors only by default
|
||||
for (int i = 0; i < DEBUG_CATEGORY_COUNT; i++) {
|
||||
g_debug_config.category_levels[i] = DEBUG_LEVEL_NONE; // 0 = disabled per category
|
||||
}
|
||||
g_debug_config.timestamp_enabled = 1;
|
||||
g_debug_config.function_name_enabled = 1;
|
||||
g_debug_config.file_line_enabled = 1;
|
||||
g_debug_config.output_file = NULL;
|
||||
|
||||
// Dual output defaults
|
||||
g_debug_config.file_output = NULL; // No file by default
|
||||
g_debug_config.console_enabled = 1; // Console enabled
|
||||
g_debug_config.thread_marker = 0; // No thread marker by default
|
||||
}
|
||||
|
||||
/* Set debug level */
|
||||
void debug_set_level(debug_level_t level) {
|
||||
g_debug_config.level = level;
|
||||
}
|
||||
|
||||
/* Set debug level for specific category (0 = disabled, otherwise uses that level) */
|
||||
void debug_set_category_level(debug_category_t category_idx, debug_level_t level) {
|
||||
if (category_idx == DEBUG_CATEGORY_NONE || category_idx >= DEBUG_CATEGORY_COUNT) return;
|
||||
g_debug_config.category_levels[category_idx] = level;
|
||||
}
|
||||
|
||||
/* Enable/disable specific categories (now sets level) */
|
||||
void debug_enable_category(debug_category_t category_idx) {
|
||||
if (category_idx >= 0 && category_idx < DEBUG_CATEGORY_COUNT) {
|
||||
g_debug_config.category_levels[category_idx] = g_debug_config.level;
|
||||
}
|
||||
}
|
||||
|
||||
void debug_disable_category(debug_category_t category_idx) {
|
||||
if (category_idx >= 0 && category_idx < DEBUG_CATEGORY_COUNT) {
|
||||
g_debug_config.category_levels[category_idx] = DEBUG_LEVEL_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void debug_set_categories(debug_category_t category_idx) {
|
||||
/* Deprecated - use per-category levels */
|
||||
if (category_idx >= 0 && category_idx < DEBUG_CATEGORY_COUNT) {
|
||||
g_debug_config.category_levels[category_idx] = g_debug_config.level;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set level for category (main API) */
|
||||
void debug_set_masks(debug_category_t category_idx, debug_level_t level) {
|
||||
debug_set_category_level(category_idx, level);
|
||||
}
|
||||
|
||||
void debug_enable_function_name(int enable) {
|
||||
g_debug_config.function_name_enabled = enable;
|
||||
}
|
||||
|
||||
void debug_enable_file_line(int enable) {
|
||||
g_debug_config.file_line_enabled = enable;
|
||||
}
|
||||
|
||||
void debug_set_output_file(const char* file_path) {
|
||||
if (g_debug_config.file_output) return;// уже установлен, кто первый того и тапки
|
||||
FILE* new_file = fopen(file_path, "a");
|
||||
if (new_file) {
|
||||
g_debug_config.file_output = new_file;
|
||||
g_debug_config.output_file = file_path;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set console log level */
|
||||
void debug_enable_console(int enable) {
|
||||
g_debug_config.console_enabled = enable;
|
||||
}
|
||||
|
||||
/* Enable file output with optional truncation */
|
||||
void debug_enable_file_output(const char* file_path, int truncate) {
|
||||
if (!file_path) return;
|
||||
|
||||
// Close existing file if open
|
||||
if (g_debug_config.file_output) {
|
||||
fclose(g_debug_config.file_output);
|
||||
g_debug_config.file_output = NULL;
|
||||
}
|
||||
|
||||
// Open new file
|
||||
const char* mode = truncate ? "w" : "a";
|
||||
FILE* new_file = fopen(file_path, mode);
|
||||
if (new_file) {
|
||||
g_debug_config.file_output = new_file;
|
||||
g_debug_config.output_file = file_path;
|
||||
g_debug_config.console_enabled = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable file output */
|
||||
void debug_disable_file_output(void) {
|
||||
if (g_debug_config.file_output) {
|
||||
fclose(g_debug_config.file_output);
|
||||
g_debug_config.file_output = NULL;
|
||||
g_debug_config.output_file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reopen log file (append mode) for SIGHUP. Complete operation with full error logging. */
|
||||
int debug_reopen_log(void) {
|
||||
if (!g_debug_config.output_file) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "No log path for reopen");
|
||||
return -1;
|
||||
}
|
||||
const char* path = g_debug_config.output_file;
|
||||
debug_disable_file_output();
|
||||
debug_enable_file_output(path, 0); // append
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Log reopened: %s", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if debug output should be shown for given level and category */
|
||||
int debug_should_output(debug_level_t level, debug_category_t category_idx) {
|
||||
if (category_idx < 0 || category_idx >= DEBUG_CATEGORY_COUNT) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
debug_level_t cat_level = g_debug_config.category_levels[category_idx];
|
||||
if (cat_level == DEBUG_LEVEL_DISABLED) {
|
||||
return 0;
|
||||
}
|
||||
if (cat_level == DEBUG_LEVEL_NONE) {
|
||||
cat_level = g_debug_config.level;
|
||||
}
|
||||
|
||||
/* Check if level is sufficient */
|
||||
if (level > cat_level) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Get current debug level */
|
||||
debug_level_t debug_get_level(void) {
|
||||
return g_debug_config.level;
|
||||
}
|
||||
|
||||
/* Get level name */
|
||||
static const char* get_level_name(debug_level_t level) {
|
||||
switch (level) {
|
||||
case DEBUG_LEVEL_ERROR: return "ERROR";
|
||||
case DEBUG_LEVEL_WARN: return "WARN";
|
||||
case DEBUG_LEVEL_INFO: return "INFO";
|
||||
case DEBUG_LEVEL_DEBUG: return "DEBUG";
|
||||
case DEBUG_LEVEL_TRACE: return "TRACE";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
/* Format and output debug message */
|
||||
void debug_output(debug_level_t level, debug_category_t category_idx,
|
||||
const char* function, const char* file, int line,
|
||||
const char* format, ...) {
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
char buffer[BUFFER_SIZE];
|
||||
int offset = 0;
|
||||
size_t remaining = BUFFER_SIZE;
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
/* Add timestamp with microseconds: hh:mm:ss-xxx.yyy */
|
||||
struct timeval tv;
|
||||
utun_gettimeofday(&tv, NULL);
|
||||
time_t tv_sec = (time_t)tv.tv_sec;
|
||||
struct tm* tm_info = localtime(&tv_sec);
|
||||
char time_str[32];
|
||||
strftime(time_str, sizeof(time_str), "%H:%M:%S", tm_info);
|
||||
offset += snprintf(buffer + offset, remaining, "[%s-%03ld.%03ld] ", time_str, tv.tv_usec / 1000, tv.tv_usec % 1000);
|
||||
remaining = BUFFER_SIZE - offset;
|
||||
|
||||
/* Add thread marker if set */
|
||||
if (g_debug_config.thread_marker > 0) {
|
||||
offset += snprintf(buffer + offset, remaining, "[%d] ", g_debug_config.thread_marker);
|
||||
remaining = BUFFER_SIZE - offset;
|
||||
}
|
||||
|
||||
/* Add level */
|
||||
const char* level_name = get_level_name(level);
|
||||
offset += snprintf(buffer + offset, remaining, "[%s] ", level_name);
|
||||
remaining = BUFFER_SIZE - offset;
|
||||
|
||||
/* Add category name */
|
||||
const char* cat_name = debug_get_category_name(category_idx);
|
||||
offset += snprintf(buffer + offset, remaining, "[%s] ", cat_name);
|
||||
remaining = BUFFER_SIZE - offset;
|
||||
|
||||
/* Add file:line if enabled */
|
||||
if (g_debug_config.file_line_enabled && file) {
|
||||
const char* bn = strrchr(file, '/');
|
||||
bn = bn ? bn + 1 : file;
|
||||
offset += snprintf(buffer + offset, remaining, "(%s:%d) ", bn, line);
|
||||
remaining = BUFFER_SIZE - offset;
|
||||
}
|
||||
|
||||
/* Add function name if enabled */
|
||||
if (g_debug_config.function_name_enabled && function) {
|
||||
offset += snprintf(buffer + offset, remaining, "%s() ", function);
|
||||
remaining = BUFFER_SIZE - offset;
|
||||
}
|
||||
|
||||
/* Add the actual message */
|
||||
offset += vsnprintf(buffer + offset, remaining, format, args);
|
||||
remaining = BUFFER_SIZE - offset;
|
||||
|
||||
/* Add newline */
|
||||
if (remaining > 1) {
|
||||
buffer[offset] = '\n';
|
||||
buffer[offset + 1] = '\0';
|
||||
} else {
|
||||
buffer[BUFFER_SIZE - 2] = '\n';
|
||||
buffer[BUFFER_SIZE - 1] = '\0';
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
|
||||
/* Route to external hook (JNI bridge, etc.) */
|
||||
if (g_log_hook) {
|
||||
const char* cat_name = debug_get_category_name(category_idx);
|
||||
g_log_hook((int)level, cat_name, buffer);
|
||||
}
|
||||
|
||||
/* Output to console if enabled and level is sufficient */
|
||||
if (g_debug_config.file_output) {
|
||||
fprintf(g_debug_config.file_output, "%s", buffer);
|
||||
fflush(g_debug_config.file_output);
|
||||
} else if (g_debug_config.console_enabled) {
|
||||
fprintf(stdout, "%s", buffer);
|
||||
fflush(stdout);
|
||||
}
|
||||
#undef BUFFER_SIZE
|
||||
}
|
||||
|
||||
void debug_set_log_hook(debug_log_hook_fn hook) {
|
||||
g_log_hook = hook;
|
||||
}
|
||||
|
||||
/* Parse debug configuration from string (e.g., "ll_queue:debug,uasync:info,memory:warn,etcp:error") */
|
||||
int debug_parse_config(const char* config_string) {
|
||||
if (!config_string || !*config_string) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* str = u_strdup(config_string);
|
||||
if (!str) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
debug_category_t categories = 0;
|
||||
debug_level_t level = DEBUG_LEVEL_NONE;
|
||||
|
||||
char* token = strtok(str, ",");
|
||||
while (token) {
|
||||
/* Trim leading spaces */
|
||||
while (isspace(*token)) {
|
||||
token++;
|
||||
}
|
||||
|
||||
char* colon = strchr(token, ':');
|
||||
if (!colon) {
|
||||
u_free(str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*colon = '\0';
|
||||
char* category_name = token;
|
||||
char* level_name = colon + 1;
|
||||
|
||||
/* Trim leading spaces in level_name */
|
||||
while (isspace(*level_name)) {
|
||||
level_name++;
|
||||
}
|
||||
|
||||
debug_category_t cat = get_category_by_name(category_name);
|
||||
if (cat == DEBUG_CATEGORY_NONE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Unknown debug category '%s'", category_name);
|
||||
u_free(str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
debug_level_t lev = debug_level_from_name(level_name);
|
||||
|
||||
/* Set per-category level */
|
||||
int idx = 0;
|
||||
while (!(cat & (1ULL << idx)) && idx < DEBUG_CATEGORY_COUNT) {
|
||||
idx++;
|
||||
}
|
||||
if (idx < DEBUG_CATEGORY_COUNT) {
|
||||
g_debug_config.category_levels[idx] = lev;
|
||||
}
|
||||
|
||||
token = strtok(NULL, ",");
|
||||
}
|
||||
|
||||
u_free(str);
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user