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:
evgeny committed 2026-08-07 00:48:27 +03:00
1 parent a3783b5f55
commit 38c9f229bc
1272 files changed
+968388 -1

No files matched your search

+96
View File
@@ -0,0 +1,96 @@
# Config Parser (config_parser.c/h)
## 1. Назначение
INI-style парсер конфигурационного файла uTun. Читает текстовый конфиг, разбирает его на структуры и возвращает заполненный `struct utun_config` — корневое представление всех настроек.
Поддерживает два режима входа:
- `parse_config(filename)` — чтение из файла
- `parse_config_from_buf(buf, len, filename)` — чтение из буфера (для chatgui/in-memory использования)
Формат: стандартные `.ini`-секции `[section]`, строки `key=value`, комментарии `#`. Именованные секции через двоеточие: `[server:main]`, `[client:peer1]`, `[network:mynet]`.
## 2. Как пользоваться
```c
struct utun_config *cfg = parse_config("/etc/utun/config.ini");
if (!cfg) { /* ошибка — смотри логи DEBUG_CATEGORY_CONFIG */ }
// Работа с конфигом...
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;
}
// Освобождение
free_config(cfg);
```
**Нюансы:**
- `parse_config` возвращает `NULL` при ошибках открытия/парсинга — проверяй возврат.
- Все строковые поля обрезаются по размерам буфера, переполнение логируется.
- Ключи клиента `link` требуют, чтобы сервер (`[server:имя]`) был объявлен выше по файлу. Иначе ошибка.
- `tun_enabled` по умолчанию `1`; если задан `tun_ifname` или `tun_ip` — генерируется имя `tun0` при отсутствии. Если нет ни одного TUN-параметра — считается выключенным (режим без root).
- IPv6-адреса в `[server:...] addr` поддерживают префиксы `temporary_ipv6:` и `permanent_ipv6:`.
## 3. API
### Структуры
| Структура | Назначение |
|-----------|------------|
| `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-сервер: имя хоста |
### Секции конфига
| Секция | Обработчик | Ключевые поля |
|--------|-----------|---------------|
| `[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` (IP:port или `all` для bypass) |
| `[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:ip: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` |
| `[gui]` | игнорируется | (зарезервировано) |
### Функции
| Функция | Назначение |
|---------|------------|
| `parse_config(filename)` | Открывает файл, парсит через `parse_config_internal`, возвращает `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)
| Функция | Назначение |
|---------|------------|
| `trim(str)` | Обрезает пробелы слева и справа (in-place) |
| `parse_key_value(line, ...)` | Разбирает строку `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=IP[:port]` или `all` |
| `parse_control_allow(rule_str, global)` | Парсит `allow=IP[/CIDR]` |
| `hex_to_binary(hex_str, binary, len)` | Конвертит hex-строку в бинарный массив |