Describe parser validation, list order and service defaults
This commit is contained in:
1 parent
9a7091b595
commit
9888820146
1 file changed
+86
-93
+86
-93
@@ -1,109 +1,102 @@
|
||||
# Config Parser (config_parser.c/h)
|
||||
# INI-конфигурация ядра
|
||||
|
||||
## 1. Назначение
|
||||
`config_parser.c/h` загружает собственную модель `utun_config` с настройками и списками.
|
||||
`parse_config(path)` читает файл тем же разборщиком, что `parse_config_from_buf`.
|
||||
Парсер не запускает ядро и не генерирует ключи; для файлового запуска отдельно используется config_updater.
|
||||
|
||||
INI-style парсер конфигурационного файла uTun. Читает текстовый конфиг, разбирает его на структуры и возвращает заполненный `struct utun_config` — корневое представление всех настроек.
|
||||
## Вход, владение и ошибки
|
||||
|
||||
Поддерживает два режима входа:
|
||||
- `parse_config(filename)` — чтение из файла
|
||||
- `parse_config_from_buf(buf, len, filename)` — чтение из буфера (для chatgui/in-memory использования)
|
||||
Файл открывается только для чтения. Ошибки открытия/чтения/закрытия дают NULL.
|
||||
Буфер содержит ровно len байтов без завершающего NUL, NULL допустим при len=0.
|
||||
filename буферного API используется только в диагностике; NULL заменяется на `<buffer>`.
|
||||
Вход не меняется. Результат и все списки освобождает `free_config`; NULL безопасен.
|
||||
`CFG_CLIENT_LINK.local_srv` заимствован из servers того же конфига; server_name содержит копию имени.
|
||||
|
||||
Формат: стандартные `.ini`-секции `[section]`, строки `key=value`, комментарии `#`. Именованные секции через двоеточие: `[server:main]`, `[client:peer1]`, `[network:mynet]`.
|
||||
Ошибка обработчика известной опции, памяти или key=value отклоняет всю модель.
|
||||
Частичные списки освобождаются. Каждое вхождение проверяется: ошибочный ранний дубль
|
||||
не маскируется поздним корректным. Повторные корректные скаляры применяются по порядку.
|
||||
Config updater исправляет управляемые ключи/node_id и ошибочные ранние дубли перед загрузкой.
|
||||
|
||||
Файл и буфер проходят один разбор. Общие функции в `config_ini.h` удаляют пробелы
|
||||
вокруг имён секций, ключей и значений. Имена секций нечувствительны к регистру,
|
||||
имена ключей чувствительны. Встроенный NUL и физические строки длиннее 1022 байтов
|
||||
до LF запрещены; CR учитывается в этом пределе.
|
||||
Ошибки разбора опций `[nat]` отклоняют весь конфиг. Формат статического проброса:
|
||||
`forward=proto:node_id_hex:IPv4:internal_port:external_port`; узел назначения обязателен,
|
||||
оба порта — десятичные 1..65535. Динамический диапазон дополнительно проверяется при запуске NAT.
|
||||
Ненулевой результат не гарантирует запуск: обязательность отсутствующих полей,
|
||||
устройства, комбинации опций и доступность портов дополнительно проверяют владельцы ресурсов.
|
||||
Адреса server/client/control разрешаются синхронно через getaddrinfo с выбором первого адреса.
|
||||
На Windows вызывающий заранее инициализирует socket subsystem; сетевые сокеты парсер не открывает.
|
||||
|
||||
## 2. Как пользоваться
|
||||
## Формат и списки
|
||||
|
||||
```c
|
||||
struct utun_config *cfg = parse_config("/etc/utun/config.ini");
|
||||
if (!cfg) { /* ошибка — смотри логи DEBUG_CATEGORY_CONFIG */ }
|
||||
Пробелы вокруг секций, ключей и значений удаляются. Секции нечувствительны к регистру, ключи чувствительны.
|
||||
Комментарий # удаляется из значения; заголовок секции должен занимать полную строку.
|
||||
Встроенный NUL и физические строки длиннее 1022 байтов до LF запрещены; CR входит в предел.
|
||||
Неизвестные/нераспознанные секции и их ключи игнорируются с WARN.
|
||||
Известные секции отклоняют неизвестные опции, кроме ограниченной обработки debug/gui.
|
||||
В debug хранится до DEBUG_CATEGORY_COUNT строк; лишние игнорируются с WARN.
|
||||
Категории/уровни хранятся в полях до 15 байтов, фактические имена проверяются debug API при применении.
|
||||
GUI читает только utun_enabled, остальные ключи остаются frontend.
|
||||
|
||||
// Работа с конфигом...
|
||||
const char *tun_name = cfg->global.tun_ifname;
|
||||
struct CFG_SERVER *srv = cfg->servers;
|
||||
while (srv) {
|
||||
ip_str_t addr = sockaddr_storage_to_str(&srv->ip);
|
||||
srv = srv->next;
|
||||
}
|
||||
Именованные server/client/network секции создают отдельные записи, дубли имён не объединяются.
|
||||
Servers, clients, networks, маршруты, allowed_keys и NTP добавляются в голову списка;
|
||||
порядок обхода обратный вводу. STUN сохраняет порядок.
|
||||
В link=local_server:host:port локальный сервер должен быть объявлен выше.
|
||||
Если имя не найдено, весь link рассматривается как remote-only host:port с local_srv=NULL.
|
||||
|
||||
// Освобождение
|
||||
free_config(cfg);
|
||||
```
|
||||
## Значения
|
||||
|
||||
**Нюансы:**
|
||||
- `parse_config` возвращает `NULL` при ошибках открытия, чтения, закрытия, памяти и обязательных проверок формата. Некоторые ошибки отдельных опций только логируются и пропускаются; ненулевой результат не гарантирует корректность всех полей.
|
||||
- Обработка длинных значений зависит от поля: часть строк обрезается, `assign_string` отклоняет значение без изменения поля.
|
||||
- Ключи клиента `link` требуют, чтобы сервер (`[server:имя]`) был объявлен выше по файлу. Иначе ошибка.
|
||||
- `tun_enabled` по умолчанию `0`. При явном включении и отсутствии имени интерфейса используется `tun0`.
|
||||
- При `auto_sockets=yes` параметр `[global] auto_socket_protocols=tcp|udp|tcp+udp` выбирает протоколы автосокетов.
|
||||
По умолчанию `tcp+udp`; неизвестное или пустое значение отклоняется. Ручные `[server]` используют свой `transport`.
|
||||
В Android выбранные протоколы генерирует frontend в отдельные `[server]` с портами на интерфейс/семейство/протокол.
|
||||
- IPv6-адреса в `[server:...] addr` поддерживают префиксы `temporary_ipv6:` и `permanent_ipv6:`.
|
||||
- HEX декодируется после проверки каждого символа. Ошибочный список REALITY `short_ids` отклоняется целиком и оставляет `short_id_count=0`; ошибочный последний REALITY-ключ сбрасывает соответствующий `has_*_key`.
|
||||
- Endpoint: IPv4/IPv6/hostname и десятичный порт 0..65535; IPv6 допускает скобки.
|
||||
Порт 0 оставлен для автоматического bind, применимость проверяет ресурс.
|
||||
- SOCKS: непустой host до 63 байтов, порт 1..65535. STUN: IPv4/A-hostname[:port], по умолчанию 3478;
|
||||
при включении минимум две разные строки server, независимость IP проверяет stun_nat.
|
||||
- Подсеть: IPv4/IPv6[/prefix], диапазон 0..32/0..128. Без / используется /32 или /128;
|
||||
явный IPv6 /32 сохраняется. Host-биты не нормализуются. Control allow — только IPv4, включая /0.
|
||||
- Числа проверяются целиком до сужения. Hex-ID допускает 0x, беззнаковые значения не принимают знак.
|
||||
Bool: yes/no, true/false, 1/0. Keepalive interval помещается в uint16_t; таймеры проверяются до умножения.
|
||||
NTP interval меньше 60 секунд приводится к 60, по умолчанию 3600.
|
||||
- Storage: uint64_t байты либо один суффикс M/G (1024²/1024³), без переполнения, знака и хвоста MB.
|
||||
MTU: 0..65535, дополнительная проверка у ресурса.
|
||||
- HEX проверяется целиком. Network требует оба 32-byte ключа и ID до 56 бит.
|
||||
Пустые необязательные ключи идентичности/пира остаются незаданными для последующей инициализации.
|
||||
- REALITY short_ids проверяется всем списком; version — ровно три десятичных октета.
|
||||
time_window=0..3600, relay_idle_timeout=0..INT_MAX/10000.
|
||||
- Слишком длинные строки отклоняются; my_node_name явно усекается до 63 байтов с WARN.
|
||||
|
||||
## 3. API
|
||||
## Секции
|
||||
|
||||
### Структуры
|
||||
| Секция | Основные опции |
|
||||
|---|---|
|
||||
| global | ключи/node_id, client_type, TUN, MTU, keepalive, BBR, auto_sockets, log/debug, db_sync |
|
||||
| server:name | addr, netif, so_mark, fib, type, transport, mtu, only_local, reality, socks |
|
||||
| client:name | peer_public_key, link, keepalive и клиентские REALITY-параметры |
|
||||
| network:name | id, pubkey, signing_key |
|
||||
| routing | route_subnet, my_subnet |
|
||||
| firewall | allow=all или IPv4[:port]; 0 означает весь IP, CIDR запрещён |
|
||||
| control | ip/control_ip, port/control_port, allow/control_allow; порядок ip/port не влияет на итог |
|
||||
| allowed_keys | allow_all, key (32-byte HEX) |
|
||||
| nat | tun_ifname, tun_ip (IPv4), nat_via, port_start/end, forward=proto:node_id:IPv4:internal_port:external_port |
|
||||
| tcp_proxy_client | enabled, tun_name/ip, mtu, via_node, socks/http_proxy enabled/addr, forward=port -> IPv4:port |
|
||||
| tcp_proxy_server | tcp_recv_buf, fib, so_mark, netif, bind_ip, connect_timeout |
|
||||
| chatserver / chat | db_path, join_password, allowed_groups, storage sizes, headless/audio binds и chat_setting API |
|
||||
| ntp | enabled, server, interval |
|
||||
| stun | enabled, server |
|
||||
| reality | enabled, server_name, dest, short_id/short_ids, public/private_key, version, time_window, relay_idle_timeout, fingerprint |
|
||||
| socks | enabled, addr, username, password |
|
||||
| log_udp | ip, port |
|
||||
| debug | category=level |
|
||||
| gui | utun_enabled |
|
||||
|
||||
| Структура | Назначение |
|
||||
|-----------|------------|
|
||||
| `struct utun_config` | Корневой контейнер: `global` + связные списки `servers`, `clients`, `route_subnets`, `my_subnets`, `networks` |
|
||||
| `struct global_config` | Глобальные настройки (ключи, TUN, таймауты, firewall, NAT, SOCKS/HTTP-прокси, NTP, debug, allowed_keys...) |
|
||||
| `struct CFG_SERVER` | Локальный серверный линк: `addr` (ip:port), `netif_index`, `so_mark`, `fib`, `type`, `transport` (udp/tcp), `mtu`, `only_local` |
|
||||
| `struct CFG_CLIENT` | Удалённый пир: `peer_public_key_hex`, `keepalive`, связный список `links` |
|
||||
| `struct CFG_CLIENT_LINK` | Привязка клиента к локальному серверу: `local_srv` (указатель на `CFG_SERVER`), `remote_addr` |
|
||||
| `struct CFG_ROUTE_ENTRY` | Маршрут: `IP` + `netmask` |
|
||||
| `struct CFG_NETWORK` | Именованная сеть: `id` (56-bit), `pubkey_hex`, `signing_key_hex` |
|
||||
| `struct CFG_FIREWALL_RULE` | Правило фаервола: `ip` (host order), `port`, `bypass` |
|
||||
| `struct CFG_ALLOWED_KEY` | Разрешённый публичный ключ (32 байта binary) для контрольного сервера |
|
||||
| `struct CFG_CONTROL_ALLOW` | Разрешённая подсеть для control-сервера |
|
||||
| `struct CFG_NTP_SERVER` | NTP-сервер: имя хоста |
|
||||
Наличие nat, tcp_proxy_client/server, chat/chatserver включает соответствующий режим даже при пустой секции;
|
||||
chat также включает db_sync. Явный tcp_proxy_client enabled=no сохраняется при последующих опциях.
|
||||
tun_enabled по умолчанию 0; при включении без имени используется tun0.
|
||||
auto_sockets принимает no/yes/android, auto_socket_protocols — tcp/udp/tcp+udp (по умолчанию оба).
|
||||
В Android frontend генерирует отдельные server записи. Addr temporary_ipv6:port/permanent_ipv6:port
|
||||
задаёт выбор IPv6 интерфейса; обычный повторный addr сбрасывает этот режим.
|
||||
|
||||
### Секции конфига
|
||||
## Диагностика и вспомогательные API
|
||||
|
||||
| Секция | Обработчик | Ключевые поля |
|
||||
|--------|-----------|---------------|
|
||||
| `[global]` | `parse_global()` | `my_node_name`, `my_private_key`, `my_public_key`, `my_node_id`, `tun_ifname`, `tun_ip`, `mtu`, `keepalive_*`, `bbr_max_cwnd`, `debug_level`, `log_file`, `db_path`, `db_sync_*`, `enable_timestamp`, `enable_colors`, `tun_test_mode` |
|
||||
| `[server:name]` | `parse_server()` | `addr`, `so_mark`, `fib`, `netif`, `type` (public/nat/private/local), `transport` (udp/tcp), `mtu`, `only_local` |
|
||||
| `[client:name]` | `parse_client()` | `link` (формат: `server_name:ip:port`), `peer_public_key`, `keepalive` |
|
||||
| `[network:name]` | `parse_network()` | `id` (hex), `pubkey`, `signing_key` |
|
||||
| `[routing]` | inline | `route_subnet`, `my_subnet` |
|
||||
| `[firewall]` | `parse_firewall_rule()` | `allow=IPv4[:port]` или `allow=all`; порт 0–65535, 0 разрешает весь IP; CIDR запрещён |
|
||||
| `[control]` | `parse_control()` | `ip`, `port`, `allow` (подсеть) |
|
||||
| `[allowed_keys]` | inline | `allow_all`, `key` (64 hex) |
|
||||
| `[nat]` | `parse_nat()` | `tun_ifname`, `tun_ip`, `nat_via`, `port_start`, `port_end`, `forward` (`proto:node_id_hex:IPv4:in_port:ext_port`) |
|
||||
| `[tcp_proxy_client]` | `parse_tcp_proxy_client()` | `enabled`, `tun_name`, `tun_ip`, `mtu`, `via_node`, `socks_*`, `http_proxy_*`, `forward` (port -> ip:port) |
|
||||
| `[tcp_proxy_server]` | inline | `tcp_recv_buf` |
|
||||
| `[ntp]` | inline | `enabled`, `server`, `interval` |
|
||||
| `[stun]` | `parse_stun()` | `enabled`, повторяющийся `server=host[:port]`; минимум два сервера при включении |
|
||||
| `[gui]` | игнорируется | (зарезервировано) |
|
||||
`print_config` выводит снимок в config/INFO. Private/signing keys заменены признаками наличия;
|
||||
сети выводятся независимо от количества firewall rules.
|
||||
`print_chatserver_invite_link` печатает bootstrap utun:// v3 в config/INFO и stderr при включённом чате:
|
||||
channel_id=join_key=0, до 32 конфигурационных адресов, socketId=0, SNI до 63 байтов.
|
||||
Это не invite конкретного канала и не снимок фактического bind/NAT.
|
||||
|
||||
### Функции
|
||||
|
||||
| Функция | Назначение |
|
||||
|---------|------------|
|
||||
| `parse_config(filename)` | Читает и закрывает файл, затем вызывает `parse_config_from_buf`. Возвращает `utun_config*` или `NULL`. |
|
||||
| `parse_config_from_buf(buf, len, filename)` | Парсит конфиг из буфера (in-memory). `filename` используется только для сообщений об ошибках. |
|
||||
| `free_config(config)` | Рекурсивно освобождает все связные списки и сам `utun_config`. Безопасен для `NULL`. |
|
||||
| `print_config(config)` | Выводит всё содержимое конфига через `DEBUG_INFO(DEBUG_CATEGORY_CONFIG, ...)`. Полезно для проверки после парсинга. |
|
||||
| `update_config_keys(filename, priv_key, pub_key)` | **Устарела.** Дописывает ключи в конец файла. Используй `config_ensure_keys_and_node_id()` из `config_updater.h`. |
|
||||
|
||||
### Внутренние хелперы (static)
|
||||
|
||||
| Функция | Назначение |
|
||||
|---------|------------|
|
||||
| `config_ini_trim(str)` | Общий INI-код: удаляет пробелы слева и справа на месте, сохраняя адрес строки |
|
||||
| `config_ini_key_value(line, ...)` | Общий INI-код: разбирает `key=value`, удаляет комментарий после `#` и пробелы |
|
||||
| `parse_ip_with_netmask(str, ip, netmask)` | Парсит `1.2.3.4/24` или `::1/64` в `struct IP` |
|
||||
| `parse_sockaddr(addr, port, sockaddr)` | Преобразует адрес+порт в `sockaddr_storage` через `getaddrinfo` |
|
||||
| `parse_address_and_port(str, sockaddr)` | Разбирает строку `ip:port` → вызов `parse_sockaddr` |
|
||||
| `get_netif_index(ifname)` | Получает индекс сетевого интерфейса через `if_nametoindex` |
|
||||
| `parse_firewall_rule(rule_str, global)` | Строго разбирает `allow=IPv4[:port]` или `all`; ошибка/неизвестная опция firewall отменяет весь новый конфиг |
|
||||
| `parse_control_allow(rule_str, global)` | Парсит `allow=IP[/CIDR]` |
|
||||
| `config_ini_hex(hex_str, binary, len)` | Общий INI-код: проверяет весь HEX до декодирования |
|
||||
`update_config_keys` — устаревшее добавление global-секции с двумя проверенными HEX-ключами в конец файла.
|
||||
Возвращает 0/-1, проверяет запись/закрытие, но не атомарно и не согласует node_id; штатный путь — config_updater.
|
||||
`config_peer_in_clients(config, pubkey[32])` возвращает 1 при бинарном совпадении ключа, 0 при отсутствии/NULL.
|
||||
Reference in new issue
Block a user