diff --git a/src/config_parser.h b/src/config_parser.h index 53b64045..03815cd0 100644 --- a/src/config_parser.h +++ b/src/config_parser.h @@ -1,6 +1,10 @@ /* INI-конфигурация ядра: слушающие сокеты, исходящие пиры, сети/подсети и настройки сервисов. * parse_config читает файл, parse_config_from_buf — текст; filename задаёт источник в диагностике. * Успех возвращает выделенную utun_config со списками; освобождение — только free_config. + * Файл и буфер проходят один разбор. Пробелы вокруг секций, ключей и значений удаляются; + * имена секций нечувствительны к регистру, имена ключей чувствительны. Комментарий — #. + * HEX проверяется целиком; ошибочный список REALITY short_ids не применяется частично. + * Встроенный NUL и строки >1022 байтов до LF запрещены (CR входит в этот предел). * Парсер заполняет модель, запуск сетевых ресурсов выполняет utun_instance. */ #ifndef CONFIG_PARSER_H #define CONFIG_PARSER_H @@ -133,7 +137,7 @@ struct global_config { char my_public_key_hex[MAX_KEY_LEN]; uint64_t my_node_id; uint8_t client_type; // 0=server, 1=desktop, 2=mobile - int tun_enabled; // 1 = create TUN (default), 0 = skip TUN (run without root) + int tun_enabled; // 1 = запрошен TUN, 0 = выключен (значение по умолчанию) char tun_ifname[16]; // TUN interface name (e.g., "tun12") struct IP tun_ip; int mtu; @@ -141,7 +145,7 @@ struct global_config { char control_ip[MAX_ADDR_LEN]; // Control server IP address // Debug and logging configuration char log_file[256]; // Path to log file (empty = stdout) - char db_path[256]; // Path to SQLite database (empty = disabled) + char db_path[256]; // Каталог chats.db; пусто — ядро использует SQLite :memory: int db_sync_enabled; // 1 = enable distributed DB sync (default: 0) int chatserver_enabled; // 1 = chat server mode (set by [chatserver] section presence) uint32_t db_sync_ttl; // TTL for unsent records in seconds (default: 86400) @@ -165,7 +169,7 @@ struct global_config { int keepalive_timeout; // keepalive timeout in ms (default: 2000) int keepalive_interval; // keepalive interval in ms (default: 200) int keepalive_adaptive; // 1 = adaptive period (default), 0 = fixed interval - int bbr_max_cwnd; // BBR cwnd cap in bytes (default: 100000) + int bbr_max_cwnd; // BBR cwnd cap in bytes (default: 1048576) // Firewall configuration struct CFG_FIREWALL_RULE *firewall_rules; @@ -261,7 +265,12 @@ struct utun_config { struct CFG_NETWORK* networks; }; +/* Непустой путь; результат освобождать free_config. NULL при ошибке открытия, чтения, + * закрытия, памяти или обязательной проверки формата. Часть ошибочных опций только + * логируется и пропускается; ненулевой результат не гарантирует корректность всех полей. */ struct utun_config* parse_config(const char *filename); +/* buf — len байтов без завершающего NUL (NULL допустим при len=0), буфер не меняется. + * filename — источник для диагностики, NULL означает . Возврат как у parse_config. */ struct utun_config* parse_config_from_buf(const char *buf, size_t len, const char *filename); void free_config(struct utun_config *config); void print_config(const struct utun_config *config); diff --git a/src/config_updater_doc.md b/src/config_updater_doc.md index c69b2134..e3711681 100644 --- a/src/config_updater_doc.md +++ b/src/config_updater_doc.md @@ -1,67 +1,84 @@ # Config Updater (config_updater.c/h) -## 1. Назначение +Модуль обеспечивает согласованные X25519-ключи и node_id в INI-файле до его загрузки ядром. +При включённом REALITY также обеспечивает private_key и непустой список short_ids. +Публичный контракт приведён в config_updater.h, общий разбор строк — в config_ini.h. -Утилита автоматической инициализации и обновления ключей в конфигурационном файле uTun. +## Выбор значений -Основная задача — гарантировать, что в конфиге всегда присутствуют корректные и согласованные: -- `my_private_key` (X25519, 64 hex символа) -- `my_public_key` (производный от private, 64 hex) -- `my_node_id` (SHA256-идентификатор узла, 16 hex символов, вычисляется из private key) +Имена секций сравниваются целиком без учёта регистра, пробелы вокруг имени секции, +ключа и значения удаляются. Имена ключей чувствительны к регистру. Комментарии — #. +Последнее текстовое значение ключа во всех соответствующих секциях определяет результат, +включая пустое или некорректное. Опции из других секций не участвуют. -Если чего-то нет или значение неконсистентно — модуль генерирует недостающее через `secure_channel.h` и атомарно перезаписывает конфиг, сохраняя всё остальное содержимое файла нетронутым. +- Корректный my_private_key — ровно 64 HEX-символа; его бинарное значение сохраняется. +- my_public_key вычисляется через sc_compute_public_key_from_private(). +- my_node_id вычисляется через sc_derive_node_id_from_pubkey(): SHA256 публичного ключа, + первые восемь байтов в представлении функции, старший бит сброшен. +- Если приватный ключ отсутствует или некорректен, создаётся новая идентичность. +- Сравнение HEX выполняется по байтам: регистр букв не вызывает перезапись. +- При включённом REALITY некорректный/отсутствующий private_key генерируется заново. + Некорректный/пустой/отсутствующий short_ids заменяется четырьмя случайными ID. + Корректный список сохраняется целиком. Ошибка одного элемента отклоняет весь список. +- При выключенном REALITY его параметры не исправляются. -## 2. Как пользоваться +HEX проверяется посимвольно до декодирования. Частичные результаты проверки не применяются. +Встроенный NUL и физические строки длиннее 1022 байтов до LF запрещены; CR учитывается в этом пределе. -```c -#include "config_updater.h" +## Изменение текста -int ret = config_ensure_keys_and_node_id("/etc/utun/config.ini"); -if (ret == 0) { - // Конфиг готов: ключи и node_id гарантированно на месте -} else { - // Ошибка — смотри DEBUG_*(DEBUG_CATEGORY_CONFIG) -} -``` +Исправный файл остаётся побайтно неизменным. Если поле требует исправления, +обновляются все его вхождения в соответствующих секциях. Недостающие поля добавляются +в конец последней подходящей секции; отсутствующая секция создаётся в конце файла. -**Типовой сценарий (первый запуск):** -1. Файла нет → создаётся минимальный `[global]` с `my_node_name`, сгенерированными ключами и `my_node_id`. -2. Файл есть, но нет `my_private_key` → генерируется новая X25519-пара, записываются все три поля. -3. Файл есть с `my_private_key`, но нет `my_public_key` или `my_node_id` → вычисляются из private key, дописываются. -4. Все поля есть, но `my_public_key` не соответствует `my_private_key` или `my_node_id` не совпадает → перезаписываются на корректные. +Посторонние строки сохраняются побайтно. В изменяемых строках сохраняются пробелы, +комментарии и окончания строк вокруг заменяемого значения. Добавляемые строки используют +тип первого окончания строки в файле, либо LF. При необходимости добавляется разделитель +после последней строки без LF. Новый или пустой файл получает [global] с тремя полями идентичности. -**Нюансы:** -- Вычисление pubkey: `sc_compute_public_key_from_private(priv_bin, pub_bin)`. -- Вычисление node_id: `sc_derive_node_id(priv_bin)`. -- Генерация новой пары ключей: `sc_generate_keypair(&mykeys)`. -- Файл читается целиком в память, модифицируется через `insert_or_replace_option`, затем пишется обратно. Это атомарно: либо файл полностью перезаписан, либо остался нетронутым (при ошибке). -- Редактирование in-place через `memmove`: строки не сдвигают остальной конфиг, только вставляются/заменяются нужные опции внутри секции `[global]`. -- Для нового файла создаётся минимальный `[global]` с именем узла из `my_node_name` (или `"utun"` если не задано). +Перед записью итоговый текст проверяется строгими декодерами и штатным парсером. +Это проверка полей идентичности и необходимых параметров REALITY; парсер продолжает +логировать и пропускать некоторые ошибки прочих опций. -## 3. API +## Запись и ошибки -### Функции +config_ensure_keys_and_node_id(filename) принимает непустой путь. Вызывающая сторона +исключает параллельную запись конфигурации. Возврат 0 означает корректные значения и, +если требовалось обновление, успешную запись. Возврат -1 означает ошибку с диагностикой CONFIG. -| Функция | Назначение | -|---------|------------| -| `config_ensure_keys_and_node_id(filename)` | **Основная функция.** Читает конфиг, проверяет/генерирует ключи и node_id, записывает обратно. Возвращает 0 при успехе, -1 при ошибке. | -| `bytes_to_hex(bytes, len, hex_str, hex_len)` | Вспомогательная: конвертация бинарного массива в hex-строку. Безопасна для нулевых указателей и маленьких буферов. | +Новый файл создаётся только при ENOENT. Ошибка доступа, чтения, выделения памяти или +закрытия входного файла прекращает операцию. Временный файл создаётся эксклюзивно +рядом с исходным. Проверяются запись, fflush, синхронизация и fclose до замены имени. -### Внутренние функции (static) +На POSIX применяется rename, затем fsync каталога: +- До успешного rename исходный файл при ошибках сохраняется. +- Ошибка fsync каталога после rename возвращает -1 и логируется с replaced=1. + Файл уже заменён; устойчивость замены к аварии не подтверждена. +- Сохраняются uid/gid и обычные права доступа (0777); новый файл имеет 0600. + ACL, xattrs и специальные биты режима не копируются. +- Временный файл удаляется при ошибках до публикации; ошибка удаления логируется. -| Функция | Назначение | -|---------|------------| -| `is_valid_priv_key(key)` | Проверяет, что строка — 64 hex-символа | -| `is_valid_pub_key(key)` | То же для публичного ключа | -| `is_valid_node_id(node_id)` | Проверяет `node_id != 0` | -| `write_mem_to_file(filename, buffer, size)` | Пишет буфер в файл (одной операцией) | -| `find_section_global(buf, buf_len)` | Ищет позицию после `[global]` в текстовом буфере | -| `find_option(buf, buf_len, option)` | Ищет строку `option=...` в буфере | -| `insert_or_replace_option(buf, buf_len, buf_capacity, option, value)` | Заменяет значение опции при наличии, либо вставляет новую строку после `[global]`. Работает с `memmove`/`memcpy`, перевыделяет буфер при необходимости. | +На Windows используются _commit и ReplaceFileA для существующего файла либо +MoveFileExA с MOVEFILE_WRITE_THROUGH для нового. Модуль не расширяет гарантии +восстановления после ошибок этих Windows API. -### Зависимости +Если требуется запись, символические ссылки/reparse points и файлы с несколькими +жёсткими ссылками отклоняются. Проверка исправного файла по ссылке возможна без записи. -- `secure_channel.h` — `sc_generate_keypair()`, `sc_compute_public_key_from_private()`, `sc_derive_node_id()` -- `config_parser.h` — `parse_config_from_buf()`, `free_config()`, структуры `utun_config`/`global_config` -- `lib/mem.h` — `u_malloc`, `u_realloc`, `u_free` -- `lib/debug_config.h` — `DEBUG_*` +Логи показывают причину генерации, изменяемые поля, этап ошибки и факт замены файла. +Значения приватных ключей updater не выводит. + +## Вспомогательная функция + +bytes_to_hex(bytes, len, hex_str, hex_len) записывает 2*len строчных HEX-символов и NUL. +Буферы не перекрываются. bytes и hex_str обязательны; hex_len задаёт ёмкость результата. +При недостаточной ёмкости или NULL функция обнуляет доступный hex_str[0]; усечения нет. +Проверка ёмкости не вычисляет переполняющееся 2*len+1. При len=0 результат пустой. + +## Проверки + +tests/test_config_updater.c проверяет сохранение идентичности, регистр, пробелы, +секции и дубли, CRLF, повторный вызов, строгий HEX/списки, длинные строки/NUL, ссылки, +права доступа, ошибки файловых операций и отказы памяти. Внедрение ошибок через +Linux/ELF linker wrappers не добавляет тестовых веток в рабочий код. +tests/test_reality_config.c проверяет базовые сценарии генерации REALITY.