Document strict parser ownership and validate provided peer keys

This commit is contained in:
evgeny committed 2026-10-26 09:08:23 +01:00
1 parent 1041849030
commit 9a7091b595
2 files changed
+35 -18

No files matched your search

+6
View File
@@ -360,6 +360,9 @@ static struct CFG_SERVER* find_server_by_name(struct CFG_SERVER *servers, const
}
static int parse_global(const char *key, const char *value, struct global_config *global, const char *filename, int line_num) {
if ((!strcmp(key, "my_private_key") || !strcmp(key, "my_public_key")) && *value && config_ini_hex(value, NULL, SC_PRIVKEY_SIZE)) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: %s requires 64 HEX characters", filename, line_num, key); return -1;
}
if (!strcmp(key, "my_node_name")) {
if (strlen(value) >= sizeof(global->name))
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: my_node_name truncated to %zu bytes", filename, line_num, sizeof(global->name) - 1);
@@ -735,6 +738,9 @@ static int parse_client(const char *key, const char *value, struct CFG_CLIENT *c
}
if (strcmp(key, "peer_public_key") == 0) {
if (*value && config_ini_hex(value, NULL, SC_PUBKEY_SIZE)) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: peer_public_key requires 64 HEX characters", filename, line_num); return -1;
}
return assign_string(cli->peer_public_key_hex, MAX_KEY_LEN, value);
}
+29 -18
View File
@@ -3,9 +3,13 @@
* Успех возвращает выделенную utun_config со списками; освобождение — только free_config.
* Файл и буфер проходят один разбор. Пробелы вокруг секций, ключей и значений удаляются;
* имена секций нечувствительны к регистру, имена ключей чувствительны. Комментарий — #.
* HEX проверяется целиком; ошибочный список REALITY short_ids не применяется частично.
* Ошибка обработчика известной опции отклоняет весь конфиг с освобождением частичной модели.
* HEX проверяется целиком; network требует оба ключа. Неизвестные секции игнорируются с WARN;
* [gui] обрабатывает только utun_enabled. Числа проверяются до сужения, bool — yes/no/true/false/1/0.
* Повторные значения применяются по порядку; ошибочный ранний дубль также отклоняется.
* Встроенный NUL и строки >1022 байтов до LF запрещены (CR входит в этот предел).
* Парсер заполняет модель, запуск сетевых ресурсов выполняет utun_instance. */
* Парсер не открывает сетевые сокеты, но синхронно разрешает addr/control через getaddrinfo;
* на Windows вызывающий заранее инициализирует socket subsystem. Сервисы запускает utun_instance. */
#ifndef CONFIG_PARSER_H
#define CONFIG_PARSER_H
@@ -26,7 +30,7 @@ extern "C" {
#define MAX_ADDR_LEN 64
struct IP {
sa_family_t family;
sa_family_t family; // AF_INET/AF_INET6, 0 — адрес не задан; байты addr в сетевом порядке
union {
struct in_addr v4;
struct in6_addr v6;
@@ -55,32 +59,32 @@ struct IP {
#define CFG_IPV6_MODE_PERMANENT 2 // permanent IPv6 from interface
struct CFG_SERVER {
struct CFG_SERVER* next;
char name[MAX_CONN_NAME_LEN];
struct CFG_SERVER* next; // собственная цепочка конфигурации; список в обратном порядке секций
char name[MAX_CONN_NAME_LEN]; // имя [server:name], до 63 байт
struct sockaddr_storage ip; // ip:port
uint32_t netif_index;// if_nameindex, 0 - no interface specified
int so_mark;
int fib; // FreeBSD FIB (routing table), -1 = don't set
uint8_t type; // public/nat/private
uint8_t type; // CFG_SERVER_TYPE_*; 0 если явно не задан
uint8_t transport; // 0=udp (default), 1=tcp
uint8_t reality_enabled; // 1 = TCP-порт с REALITY-камуфляжем (только transport=1)
uint8_t socks_enabled; // 1 = UDP-сокет туннелировать через SOCKS5 (только transport=0)
uint8_t ipv6_mode; // CFG_IPV6_MODE_*
int mtu;
int mtu; // 0 — не задан, при явной опции 0..65535; ресурс проверяет применимость
uint8_t only_local; // 1 = only local connections, no forwarding
};
struct CFG_CLIENT_LINK {
struct CFG_CLIENT_LINK *next; // Next link in linked list
struct CFG_SERVER* local_srv;
char server_name[MAX_CONN_NAME_LEN]; // Name of local server for this link
struct CFG_SERVER* local_srv; // заимствован у того же utun_config; NULL — remote-only
char server_name[MAX_CONN_NAME_LEN]; // копия имени local_srv; пуста для remote-only
struct sockaddr_storage remote_addr; // ip:port
};
struct CFG_CLIENT {
char name[MAX_CONN_NAME_LEN];
char peer_public_key_hex[MAX_KEY_LEN];
int keepalive;
int keepalive; // необязательная клиентская настройка; 0 по умолчанию, неотрицательный int
struct CFG_CLIENT_LINK *links; // Linked list of links
struct CFG_CLIENT *next; // Next client in linked list
uint8_t reality_enabled; // 1 = TCP-линк с REALITY-камуфляжем
@@ -97,8 +101,8 @@ struct CFG_NETWORK {
struct CFG_ROUTE_ENTRY {
struct CFG_ROUTE_ENTRY* next;
struct IP ip;
uint8_t netmask;
struct IP ip; // исходный адрес подсети (host-биты парсер не нормализует)
uint8_t netmask; // префикс 0..32/0..128; без / по умолчанию 32/128 по семейству
};
struct CFG_FIREWALL_RULE {
@@ -117,15 +121,15 @@ struct CFG_ALLOWED_KEY {
};
struct CFG_NTP_SERVER {
char name[256];
char name[256]; // NUL-terminated hostname/IP; парсер не разрешает его, список идёт в обратном порядке
struct CFG_NTP_SERVER *next;
};
/* STUN-серверы обходятся в порядке конфигурации; DNS выполняет модуль stun_nat. */
struct CFG_STUN_SERVER {
struct CFG_STUN_SERVER* next;
char host[256];
uint16_t port;
char host[256]; // IPv4/имя A-сервера; DNS выполняет stun_nat
uint16_t port; // 1..65535, по умолчанию 3478
};
#define MAX_TCP_PROXY_CLIENT_MAPPINGS 32
@@ -273,16 +277,23 @@ struct utun_config {
struct CFG_NETWORK* networks;
};
/* Непустой путь; результат освобождать free_config. NULL при ошибке открытия, чтения,
* закрытия, памяти или обязательной проверки формата, включая ошибки [global]. Часть опций других секций только
* логируется и пропускается; ненулевой результат не гарантирует корректность всех полей. */
/* Непустой путь; результат освобождать free_config. NULL при ошибке открытия/чтения/закрытия,
* памяти, формата или обработчика известной опции. Значения по умолчанию и полноту настройки
* для конкретного сервиса дополнительно проверяет его start; ненулевой результат не гарантирует запуск.
* Файл не изменяется, ключи не генерируются: для этого отдельно вызвать config_ensure_keys_and_node_id. */
struct utun_config* parse_config(const char *filename);
/* buf — len байтов без завершающего NUL (NULL допустим при len=0), буфер не меняется.
* filename — источник для диагностики, NULL означает <buffer>. Возврат как у parse_config. */
struct utun_config* parse_config_from_buf(const char *buf, size_t len, const char *filename);
// Освобождает модель и все собственные списки/массивы; заимствованные ссылки становятся недействительны; NULL допустим.
void free_config(struct utun_config *config);
// Пишет диагностический снимок в config/INFO; private/signing keys не выводятся; NULL — no-op.
void print_config(const struct utun_config *config);
// Печатает в config/INFO и stderr bootstrap utun:// v3 с channel_id/join_key=0 по адресам servers (до 32).
// Чат должен быть включён; используются конфигурационные адреса, не фактический bind/NAT. NULL — no-op.
void print_chatserver_invite_link(const struct utun_config *config, const struct CFG_SERVER *servers);
// Устаревшее добавление [global] с двумя 32-byte HEX ключами в конец файла; 0/-1 с логом.
// Не атомарно, частичная запись возможна. Для согласованных ключей/node_id использовать config_updater.
int update_config_keys(const char *filename, const char *priv_key, const char *pub_key);
/* Узел (по pubkey, 32 байта) присутствует в списке [client] конфига? 1=да, 0=нет. */