From 4abeafa20ed57bca09dfcd6db1933e69f18e0070 Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 26 Oct 2026 00:14:22 +0100 Subject: [PATCH] Normalize malformed key duplicates before strict config verification --- src/config_updater.c | 18 +++++++++++++++++- src/config_updater.h | 4 +++- src/config_updater_doc.md | 10 +++++++--- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/config_updater.c b/src/config_updater.c index fb21a652..71a26520 100644 --- a/src/config_updater.c +++ b/src/config_updater.c @@ -20,9 +20,12 @@ struct config_field { int section; int present; int update; + int invalid; // хотя бы одно вхождение поля содержит ошибочный синтаксис char value[CONFIG_INI_LINE_SIZE]; }; +static int config_node_id(const char* text, uint64_t* id); + /* Форматировать без усечения; проверка ёмкости не переполняет size_t. */ void bytes_to_hex(const uint8_t *bytes, size_t len, char *hex_str, size_t hex_len) { static const char digits[] = "0123456789abcdef"; @@ -77,7 +80,17 @@ static void config_scan(const char *text, size_t length, struct config_field fie while (isspace((unsigned char)*start)) start++; if (*start == '[') ends[section] = pos; int index = config_field_line(line, §ion, fields, value); - if (index >= 0) { strcpy(fields[index].value, value); fields[index].present = 1; } + if (index >= 0) { + strcpy(fields[index].value, value); fields[index].present = 1; + if (index == CFG_REALITY_IDS) { + int count; + if (config_ini_hex_list(value, NULL, REALITY_SHORT_ID_SIZE, REALITY_MAX_SHORT_IDS, &count) || !count) + fields[index].invalid = 1; + } else if (index == CFG_NODE_ID) { + uint64_t id; + if (config_node_id(value, &id)) fields[index].invalid = 1; + } else if (index != CFG_REALITY_ENABLED && config_ini_hex(value, NULL, SC_PRIVKEY_SIZE)) fields[index].invalid = 1; + } pos = end; } ends[section] = length; @@ -356,6 +369,7 @@ int config_ensure_keys_and_node_id(const char *filename) { fields[CFG_NODE_ID].update = 1; snprintf(fields[CFG_NODE_ID].value, sizeof(fields[CFG_NODE_ID].value), "%016llx", (unsigned long long)node_id); } + for (int i = CFG_PRIVATE; i <= CFG_NODE_ID; i++) if (fields[i].invalid) fields[i].update = 1; int reality_enabled = atoi(fields[CFG_REALITY_ENABLED].value) != 0; if (reality_enabled) { if (config_ini_hex(fields[CFG_REALITY_PRIVATE].value, reality_private, sizeof(reality_private))) { @@ -378,6 +392,8 @@ int config_ensure_keys_and_node_id(const char *filename) { } fields[CFG_REALITY_IDS].update = 1; } + if (fields[CFG_REALITY_PRIVATE].invalid) fields[CFG_REALITY_PRIVATE].update = 1; + if (fields[CFG_REALITY_IDS].invalid) fields[CFG_REALITY_IDS].update = 1; } for (int i = 0; i < CFG_FIELD_COUNT; i++) { if (!fields[i].update) continue; diff --git a/src/config_updater.h b/src/config_updater.h index 10263ef9..30409a56 100644 --- a/src/config_updater.h +++ b/src/config_updater.h @@ -5,7 +5,9 @@ * или отсутствующем приватном ключе создаётся новая идентичность. HEX сравнивается по байтам. * REALITY: недостающий/ошибочный private_key генерируется; пустой/ошибочный short_ids * заменяется четырьмя новыми ID. Корректные параметры и выключенный REALITY сохраняются. - * Исправный файл остаётся побайтно неизменным. При правке сохраняются посторонний текст, + * Ошибочные ранние дубли этих полей заменяются выбранным последним значением, даже если оно корректно: + * штатный парсер проверяет каждое вхождение. Файл без таких ошибок остаётся побайтно неизменным. + * При правке сохраняются посторонний текст, * комментарии и существующие окончания строк; обновляются все дубли исправляемого поля. * Новый файл создаётся только при ENOENT. Встроенный NUL и строки >1022 байтов до LF запрещены. */ #ifndef CONFIG_UPDATER_H diff --git a/src/config_updater_doc.md b/src/config_updater_doc.md index e3711681..647b216f 100644 --- a/src/config_updater_doc.md +++ b/src/config_updater_doc.md @@ -27,7 +27,10 @@ HEX проверяется посимвольно до декодировани ## Изменение текста -Исправный файл остаётся побайтно неизменным. Если поле требует исправления, +Синтаксически ошибочный ранний дубль поля идентичности или включённого REALITY также исправляется +по выбранному последнему значению: штатный парсер проверяет каждое вхождение. +Корректная итоговая идентичность/REALITY не меняется и не генерируется заново только из-за раннего дубля. +Файл без таких ошибок остаётся побайтно неизменным. Если поле требует исправления, обновляются все его вхождения в соответствующих секциях. Недостающие поля добавляются в конец последней подходящей секции; отсутствующая секция создаётся в конце файла. @@ -37,8 +40,9 @@ HEX проверяется посимвольно до декодировани после последней строки без LF. Новый или пустой файл получает [global] с тремя полями идентичности. Перед записью итоговый текст проверяется строгими декодерами и штатным парсером. -Это проверка полей идентичности и необходимых параметров REALITY; парсер продолжает -логировать и пропускать некоторые ошибки прочих опций. +Штатный парсер отклоняет ошибки обработчиков известных опций; при таком отказе исходный файл сохраняется. +Отсутствие необходимости правки идентичности/REALITY не означает проверки остальных настроек: +вызывающая сторона всё равно загружает файл через parse_config. ## Запись и ошибки