You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

192 lines
8.4 KiB

// test_reality_config.c — автогенерация reality private_key и short_ids в конфиге
//
// Проверяет config_ensure_keys_and_node_id():
// 1. [reality] enabled=1 без private_key/short_ids → генерируются (privkey + 4 id).
// 2. Пустая строка short_ids= заменяется на 4 id (без пустых строк и дублей).
// 3. [reality] enabled=0 → ничего не генерируется.
// 4. Без секции [reality] → ничего не генерируется.
// 5. Идемпотентность: повторный вызов не добавляет дублей.
#include "config_parser.h"
#include "config_updater.h"
#include "../lib/debug_config.h"
#include "test_utils.h"
#include <stdio.h>
#include <string.h>
static int test_failed = 0;
#define CHECK(expr, msg) do { \
if (!(expr)) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "FAIL: %s", msg); test_failed = 1; } \
else { DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "PASS: %s", msg); } \
} while (0)
static int write_file(const char *path, const char *content) {
FILE *f = fopen(path, "w");
if (!f) return -1;
fputs(content, f);
fclose(f);
return 0;
}
static int read_file(const char *path, char *buf, size_t cap) {
FILE *f = fopen(path, "rb");
if (!f) return -1;
size_t n = fread(buf, 1, cap - 1, f);
buf[n] = '\0';
fclose(f);
return (int)n;
}
// Возвращает: 0 — в секции [section] нет пустых строк, option встречается ровно want раз.
// 1 — секции нет, 2 — есть пустая строка, 3 — неверное число вхождений option.
static int check_section(const char *content, const char *section, const char *option, int want) {
char hdr[64]; snprintf(hdr, sizeof(hdr), "[%s]", section);
const char *sec = strstr(content, hdr);
if (!sec) return 1;
sec += strlen(hdr);
while (*sec == '\r' || *sec == '\n') sec++;
const char *end = strchr(sec, '[');
if (!end) end = content + strlen(content);
int found = 0, empty = 0;
const char *line = sec;
while (line < end) {
const char *nl = strchr(line, '\n');
const char *line_end = (nl && nl < end) ? nl : end;
size_t len = (size_t)(line_end - line);
while (len && (line[0] == ' ' || line[0] == '\t' || line[0] == '\r')) { line++; len--; }
if (len == 0) empty++;
if (option && strncmp(line, option, strlen(option)) == 0 && line[strlen(option)] == '=') found++;
line = nl ? nl + 1 : end;
}
if (empty) return 2;
if (found != want) return 3;
return 0;
}
int main(void) {
debug_config_init();
debug_set_level(DEBUG_LEVEL_INFO);
char tdir[] = "/tmp/utun_reality_cfg_XXXXXX";
if (test_mkdtemp(tdir) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "mkdtemp failed"); return 1; }
char path[512];
snprintf(path, sizeof(path), "%s/r.conf", tdir);
// ── Сценарий 1: enabled=1 без ключей → генерация ──
{
const char *cfg =
"[global]\nmy_node_name=test\n"
"[reality]\n"
"enabled=1\n"
"server_name=www.microsoft.com\n"
"dest=www.microsoft.com:443\n";
CHECK(write_file(path, cfg) == 0, "write config (scenario 1)");
CHECK(config_ensure_keys_and_node_id(path) == 0, "ensure keys (scenario 1)");
struct utun_config *uc = parse_config(path);
CHECK(uc != NULL, "parse after generate (scenario 1)");
if (uc) {
CHECK(uc->global.reality.enabled == 1, "enabled=1");
CHECK(uc->global.reality.has_private_key == 1, "private_key generated");
CHECK(uc->global.reality.short_id_count == 4, "4 short_ids generated");
free_config(uc);
}
char content[4096];
CHECK(read_file(path, content, sizeof(content)) > 0, "read file (scenario 1)");
CHECK(check_section(content, "reality", NULL, 0) == 0, "no empty lines in [reality]");
CHECK(check_section(content, "reality", "private_key", 1) == 0, "exactly one private_key");
CHECK(check_section(content, "reality", "short_ids", 1) == 0, "exactly one short_ids");
}
// ── Сценарий 2: пустая строка short_ids= заменяется ──
{
const char *cfg =
"[global]\nmy_node_name=test\n"
"[reality]\n"
"enabled=1\n"
"short_ids=\n"
"server_name=www.microsoft.com\n";
CHECK(write_file(path, cfg) == 0, "write config (scenario 2)");
CHECK(config_ensure_keys_and_node_id(path) == 0, "ensure keys (scenario 2)");
struct utun_config *uc = parse_config(path);
CHECK(uc != NULL, "parse after generate (scenario 2)");
if (uc) {
CHECK(uc->global.reality.short_id_count == 4, "4 short_ids generated (replace)");
CHECK(uc->global.reality.has_private_key == 1, "private_key generated (scenario 2)");
free_config(uc);
}
char content[4096];
CHECK(read_file(path, content, sizeof(content)) > 0, "read file (scenario 2)");
CHECK(check_section(content, "reality", NULL, 0) == 0, "no empty lines (scenario 2)");
CHECK(check_section(content, "reality", "short_ids", 1) == 0, "short_ids replaced, no dup (scenario 2)");
}
// ── Сценарий 3: enabled=0 → без генерации ──
{
const char *cfg =
"[global]\nmy_node_name=test\n"
"[reality]\n"
"enabled=0\n"
"server_name=www.microsoft.com\n";
CHECK(write_file(path, cfg) == 0, "write config (scenario 3)");
CHECK(config_ensure_keys_and_node_id(path) == 0, "ensure keys (scenario 3)");
struct utun_config *uc = parse_config(path);
CHECK(uc != NULL, "parse (scenario 3)");
if (uc) {
CHECK(uc->global.reality.has_private_key == 0, "no private_key (disabled)");
CHECK(uc->global.reality.short_id_count == 0, "no short_ids (disabled)");
free_config(uc);
}
}
// ── Сценарий 4: без секции [reality] → без генерации ──
{
const char *cfg = "[global]\nmy_node_name=test\n";
CHECK(write_file(path, cfg) == 0, "write config (scenario 4)");
CHECK(config_ensure_keys_and_node_id(path) == 0, "ensure keys (scenario 4)");
struct utun_config *uc = parse_config(path);
CHECK(uc != NULL, "parse (scenario 4)");
if (uc) {
CHECK(uc->global.reality.enabled == 0, "reality disabled (no section)");
CHECK(uc->global.reality.has_private_key == 0, "no private_key (no section)");
CHECK(uc->global.reality.short_id_count == 0, "no short_ids (no section)");
free_config(uc);
}
}
// ── Сценарий 5: идемпотентность ──
{
const char *cfg =
"[global]\nmy_node_name=test\n"
"[reality]\n"
"enabled=1\n"
"server_name=www.microsoft.com\n";
CHECK(write_file(path, cfg) == 0, "write config (scenario 5)");
CHECK(config_ensure_keys_and_node_id(path) == 0, "ensure #1 (scenario 5)");
struct utun_config *uc1 = parse_config(path);
CHECK(uc1 != NULL, "parse #1 (scenario 5)");
CHECK(config_ensure_keys_and_node_id(path) == 0, "ensure #2 (scenario 5)");
struct utun_config *uc2 = parse_config(path);
CHECK(uc2 != NULL, "parse #2 (scenario 5)");
if (uc1 && uc2) {
CHECK(uc1->global.reality.short_id_count == 4 && uc2->global.reality.short_id_count == 4, "ids stable");
CHECK(memcmp(uc1->global.reality.private_key, uc2->global.reality.private_key, REALITY_AUTH_KEY_SIZE) == 0, "privkey stable");
free_config(uc1);
free_config(uc2);
}
char content[4096];
CHECK(read_file(path, content, sizeof(content)) > 0, "read file (scenario 5)");
CHECK(check_section(content, "reality", "private_key", 1) == 0, "no dup private_key (scenario 5)");
CHECK(check_section(content, "reality", "short_ids", 1) == 0, "no dup short_ids (scenario 5)");
}
test_unlink(path);
test_rmdir(tdir);
if (test_failed) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "=== Reality Config Test: FAILED ===");
return 1;
}
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "=== Reality Config Test: PASSED ===");
return 0;
}