/** * Debug module */ #include "debug_config.h" #include #include #include #include #include #include #include #include #include #include #include #include void log_dump(const char* prefix, const uint8_t* data, size_t len) { // Build packet data as hex string for single-line output char hex_buf[513]; // 256 bytes * 2 chars + 1 for null terminator size_t hex_len = 0; size_t show_len = (len > 128) ? 128 : len; // Show max 128 bytes 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) { // Add space every 32 bytes 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, "..."); } // Single-line debug output with packet info DEBUG_INFO(DEBUG_CATEGORY_ETCP, "%s: len=%zu hex=%s", prefix, len, hex_buf); // Additional debug info for first few bytes // if (len >= 2) { // DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "%s: first_bytes=%02x%02x last_bytes=%02x%02x", // prefix, data[0], data[1], data[len-2], data[len-1]); // } } size_t addr_to_string(const struct sockaddr_storage *addr, char *buf, size_t buflen) { if (!buf || buflen == 0) return 0; buf[0] = '\0'; // всегда валидная строка char ip[INET6_ADDRSTRLEN]; uint16_t port; int n; if (addr->ss_family == AF_INET) { const struct sockaddr_in *a = (const struct sockaddr_in *)addr; inet_ntop(AF_INET, &a->sin_addr, ip, sizeof(ip)); port = ntohs(a->sin_port); n = snprintf(buf, buflen, "%s:%u", ip, port); } else if (addr->ss_family == AF_INET6) { const struct sockaddr_in6 *a = (const struct sockaddr_in6 *)addr; inet_ntop(AF_INET6, &a->sin6_addr, ip, sizeof(ip)); port = ntohs(a->sin6_port); n = snprintf(buf, buflen, "[%s]:%u", ip, port); } else { return 0; } // snprintf гарантирует '\0', но на всякий случай: buf[buflen - 1] = '\0'; // Возвращаем фактическую длину (как snprintf) return (n < 0) ? 0 : (size_t)n; } /* Get category by name */ static debug_category_t get_category_by_name(const char* name) { struct { const char* n; debug_category_t c; } map[] = { {"none", DEBUG_CATEGORY_NONE}, {"uasync", DEBUG_CATEGORY_UASYNC}, {"ll_queue", DEBUG_CATEGORY_LL_QUEUE}, {"connection", DEBUG_CATEGORY_CONNECTION}, {"etcp", DEBUG_CATEGORY_ETCP}, {"crypto", DEBUG_CATEGORY_CRYPTO}, {"memory", DEBUG_CATEGORY_MEMORY}, {"timing", DEBUG_CATEGORY_TIMING}, {"config", DEBUG_CATEGORY_CONFIG}, {"tun", DEBUG_CATEGORY_TUN}, {"routing", DEBUG_CATEGORY_ROUTING}, {"timers", DEBUG_CATEGORY_TIMERS}, {"all", DEBUG_CATEGORY_ALL}, {NULL, 0} }; for (int i = 0; map[i].n; i++) { if (strcasecmp(map[i].n, name) == 0) { return map[i].c; } } return DEBUG_CATEGORY_NONE; } /* Global debug configuration */ debug_config_t g_debug_config; FILE* debug_output_file = NULL; /* Initialize debug system with default settings */ void debug_config_init(void) { g_debug_config.level = DEBUG_LEVEL_ERROR; g_debug_config.categories = DEBUG_CATEGORY_ALL; 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; if (debug_output_file && debug_output_file != stdout && debug_output_file != stderr) { fclose(debug_output_file); } debug_output_file = stdout; } /* Set debug level */ void debug_set_level(debug_level_t level) { g_debug_config.level = level; } /* Enable/disable specific categories */ void debug_enable_category(debug_category_t category) { g_debug_config.categories |= category; } void debug_disable_category(debug_category_t category) { g_debug_config.categories &= ~category; } void debug_set_categories(debug_category_t categories) { g_debug_config.categories = categories; } /* Set masks directly */ void debug_set_masks(debug_category_t categories, debug_level_t level) { g_debug_config.categories = categories; g_debug_config.level = 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 (debug_output_file) return;// уже установлен, кто первый того и тапки FILE* new_file = fopen(file_path, "a"); if (new_file) { debug_output_file = new_file; g_debug_config.output_file = file_path; } } /* Check if debug output should be shown for given level and category */ int debug_should_output(debug_level_t level, debug_category_t category) { /* Check if category is enabled */ if (!(g_debug_config.categories & category)) { return 0; } /* Check if level is sufficient */ if (level > g_debug_config.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, 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); FILE* output = debug_output_file ? debug_output_file : stdout; /* Add timestamp if enabled */ time_t now = time(NULL); struct tm* tm_info = localtime(&now); char time_str[32]; strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info); offset += snprintf(buffer + offset, remaining, "[%s] ", time_str); 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 */ offset += snprintf(buffer + offset, remaining, "[%llu] ", (unsigned long long)category); 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 file:line if enabled */ if (g_debug_config.file_line_enabled && file) { offset += snprintf(buffer + offset, remaining, "(%s:%d) ", file, line); 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); /* Output the entire line at once */ fprintf(output, "%s", buffer); fflush(output); #undef BUFFER_SIZE } /* Parse debug configuration from string */ int debug_parse_config(const char* config_string) { if (!config_string || !*config_string) { return 0; } char* str = 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* eq = strchr(token, '='); if (!eq) { free(str); return -1; } *eq = '\0'; char* key = token; char* value = eq + 1; /* Trim leading spaces in value */ while (isspace(*value)) { value++; } if (strcasecmp(key, "modules") == 0) { char* mod_token = strtok(value, ","); while (mod_token) { /* Trim leading spaces */ while (isspace(*mod_token)) { mod_token++; } debug_category_t cat = get_category_by_name(mod_token); if (cat == DEBUG_CATEGORY_NONE) { free(str); return -1; } categories |= cat; mod_token = strtok(NULL, ","); } } else if (strcasecmp(key, "level") == 0) { int lev_num = atoi(value); if (lev_num < DEBUG_LEVEL_NONE || lev_num > DEBUG_LEVEL_TRACE) { free(str); return -1; } level = (debug_level_t)lev_num; } else { free(str); return -1; } token = strtok(NULL, ";"); } debug_set_masks(categories, level); free(str); return 0; }