Browse Source

config: одно открытие файла — parse_config_from_buf вместо повторного fopen; убран read_file_to_mem

topo_upd
Evgeny 3 months ago
parent
commit
e325b84a8e
  1. 93
      src/config_parser.c
  2. 1
      src/config_parser.h
  3. 157
      src/config_updater.c

93
src/config_parser.c

@ -1047,6 +1047,99 @@ error:
return NULL;
}
/* ── Buffer line reader for parse_config_from_buf ── */
typedef struct { const char *buf; size_t len; size_t pos; } blr_t;
static int blr_get_line(blr_t *blr, char *line, size_t maxlen) {
if (blr->pos >= blr->len) return 0;
size_t i = 0;
while (blr->pos < blr->len && i < maxlen - 1) {
char c = blr->buf[blr->pos++];
line[i++] = c;
if (c == '\n') break;
}
line[i] = '\0';
return 1;
}
struct utun_config* parse_config_from_buf(const char *buf, size_t len, const char *filename) {
struct utun_config *cfg = u_calloc(1, sizeof(struct utun_config));
if (!cfg) { DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "failed to allocate memory for config structure"); return NULL; }
cfg->global.name[0] = '\0';
cfg->global.keepalive_timeout = 2000;
cfg->global.keepalive_interval = 200;
cfg->global.keepalive_adaptive = 1;
cfg->global.bbr_max_cwnd = 1048576;
cfg->global.tcp_recv_buf = 0;
cfg->global.firewall_rules = NULL;
cfg->global.firewall_rule_count = 0;
cfg->global.firewall_bypass_all = 0;
cfg->global.control_allows = NULL;
cfg->global.control_allow_count = 0;
cfg->global.db_sync_enabled = 0;
cfg->global.db_sync_ttl = 86400;
cfg->global.ntp_enabled = 0;
cfg->global.ntp_server_count = 0;
cfg->global.ntp_resync_interval = 3600;
section_type_t cur_section = SECTION_UNKNOWN;
struct CFG_SERVER *cur_server = NULL;
struct CFG_CLIENT *cur_client = NULL;
struct CFG_NETWORK *cur_network = NULL;
blr_t blr = {buf, len, 0};
char line[MAX_LINE_LEN];
int line_num = 0;
while (blr_get_line(&blr, line, sizeof(line))) {
line_num++;
char *trimmed = trim(line);
if (trimmed[0] == '\0' || trimmed[0] == '#') continue;
if (trimmed[0] == '[') {
if (cur_section == SECTION_SERVER && cur_server) { cur_server->next = cfg->servers; cfg->servers = cur_server; cur_server = NULL; }
if (cur_section == SECTION_CLIENT && cur_client) { cur_client->next = cfg->clients; cfg->clients = cur_client; cur_client = NULL; }
if (cur_section == SECTION_NETWORK && cur_network) { cur_network->next = cfg->networks; cfg->networks = cur_network; cur_network = NULL; }
char name[MAX_CONN_NAME_LEN];
cur_section = parse_section_header(trimmed, name, sizeof(name));
if (cur_section == SECTION_SERVER) { cur_server = u_calloc(1, sizeof(struct CFG_SERVER)); if (!cur_server) goto error; strcpy(cur_server->name, name); cur_server->fib = -1; }
else if (cur_section == SECTION_CLIENT) { cur_client = u_calloc(1, sizeof(struct CFG_CLIENT)); if (!cur_client) goto error; strcpy(cur_client->name, name); }
else if (cur_section == SECTION_NETWORK) { cur_network = u_calloc(1, sizeof(struct CFG_NETWORK)); if (!cur_network) goto error; strcpy(cur_network->name, name); }
continue;
}
char key[MAX_LINE_LEN], value[MAX_LINE_LEN];
if (parse_key_value(trimmed, key, sizeof(key), value, sizeof(value)) < 0) { DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: Invalid key=value format", filename, line_num); continue; }
switch (cur_section) {
case SECTION_GLOBAL: parse_global(key, value, &cfg->global, filename, line_num); break;
case SECTION_SERVER: if (cur_server) parse_server(key, value, cur_server, filename, line_num); break;
case SECTION_CLIENT: if (cur_client) parse_client(key, value, cur_client, cfg->servers, filename, line_num); break;
case SECTION_ROUTING: if (strcmp(key, "route_subnet") == 0) add_route_entry(&cfg->route_subnets, value); else if (strcmp(key, "my_subnet") == 0) add_route_entry(&cfg->my_subnets, value); else DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown routing option '%s'", filename, line_num, key); break;
case SECTION_DEBUG: if (cfg->global.debug_levels.count < 16) { strncpy(cfg->global.debug_levels.category[cfg->global.debug_levels.count], key, 15); cfg->global.debug_levels.category[cfg->global.debug_levels.count][15] = '\0'; strncpy(cfg->global.debug_levels.level[cfg->global.debug_levels.count], value, 15); cfg->global.debug_levels.level[cfg->global.debug_levels.count][15] = '\0'; cfg->global.debug_levels.count++; } break;
case SECTION_FIREWALL: if (strcmp(key, "allow") == 0) parse_firewall_rule(value, &cfg->global); else DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown firewall option '%s'", filename, line_num, key); break;
case SECTION_CONTROL: parse_control(key, value, &cfg->global, filename, line_num); break;
case SECTION_ALLOWED_KEYS: if (strcmp(key, "allow_all") == 0) cfg->global.allowed_keys_allow_all = strcasecmp(value, "yes") == 0 || strcasecmp(value, "1") == 0 || strcasecmp(value, "true") == 0; else if (strcmp(key, "key") == 0) { struct CFG_ALLOWED_KEY *nk = u_calloc(1, sizeof(struct CFG_ALLOWED_KEY)); if (nk && strlen(value) == SC_PUBKEY_SIZE * 2 && hex_to_binary(value, nk->key_bin, SC_PUBKEY_SIZE) == 0) { nk->next = cfg->global.allowed_keys; cfg->global.allowed_keys = nk; cfg->global.allowed_keys_count++; } else u_free(nk); } else DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown allowed_keys option '%s'", filename, line_num, key); break;
case SECTION_NAT: cfg->global.nat_enabled = 1; parse_nat(key, value, &cfg->global, filename, line_num); break;
case SECTION_TCP_PROXY_CLIENT: cfg->global.tcp_proxy_client_enabled = 1; parse_tcp_proxy_client(key, value, &cfg->global, filename, line_num); break;
case SECTION_TCP_PROXY_SERVER: cfg->global.tcp_proxy_server_enabled = 1; if (strcmp(key, "tcp_recv_buf") == 0) cfg->global.tcp_recv_buf = atoi(value); break;
case SECTION_MSG_TRANSPORT: parse_msg_transport(key, value, &cfg->global, filename, line_num); break;
case SECTION_NETWORK: if (cur_network) parse_network(key, value, cur_network, filename, line_num); break;
case SECTION_NTP: if (strcmp(key, "enabled") == 0) cfg->global.ntp_enabled = strcasecmp(value, "yes") == 0 || strcasecmp(value, "1") == 0 || strcasecmp(value, "true") == 0; else if (strcmp(key, "server") == 0 && cfg->global.ntp_server_count < 5) { int idx = cfg->global.ntp_server_count; strncpy(cfg->global.ntp_servers[idx], value, sizeof(cfg->global.ntp_servers[idx]) - 1); cfg->global.ntp_servers[idx][sizeof(cfg->global.ntp_servers[idx]) - 1] = '\0'; cfg->global.ntp_server_count++; } else if (strcmp(key, "interval") == 0) { cfg->global.ntp_resync_interval = atoi(value); if (cfg->global.ntp_resync_interval < 60) cfg->global.ntp_resync_interval = 60; } else DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown ntp option '%s'", filename, line_num, key); break;
case SECTION_GUI: break;
default: DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: Key outside section: %s", filename, line_num, key); break;
}
}
if (cur_section == SECTION_SERVER && cur_server) { cur_server->next = cfg->servers; cfg->servers = cur_server; }
if (cur_section == SECTION_CLIENT && cur_client) { cur_client->next = cfg->clients; cfg->clients = cur_client; }
if (cur_section == SECTION_NETWORK && cur_network) { cur_network->next = cfg->networks; cfg->networks = cur_network; }
if (!cfg->global.tun_enabled && cfg->global.tun_ifname[0] == '\0' && cfg->global.tun_ip.family == AF_UNSPEC) {}
else if (!cfg->global.tun_enabled) {}
else { cfg->global.tun_enabled = 1; if (cfg->global.tun_ifname[0] == '\0') { snprintf(cfg->global.tun_ifname, sizeof(cfg->global.tun_ifname), "tun0"); } }
return cfg;
error:
if (cur_server) u_free(cur_server);
if (cur_client) { free_cfg_client_links(cur_client->links); u_free(cur_client); }
if (cur_network) u_free(cur_network);
free_config(cfg);
return NULL;
}
struct utun_config* parse_config(const char *filename) {
DEBUG_DEBUG(DEBUG_CATEGORY_CONFIG, "Opening config file: %s", filename);
FILE *fp = fopen(filename, "r");

1
src/config_parser.h

@ -200,6 +200,7 @@ struct utun_config {
};
struct utun_config* parse_config(const char *filename);
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);
int update_config_keys(const char *filename, const char *priv_key, const char *pub_key);

157
src/config_updater.c

@ -13,6 +13,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include "../lib/mem.h"
#define PRIV_HEXKEY_LEN 65 // 32 bytes * 2 hex chars + null
@ -53,48 +54,6 @@ static int is_valid_node_id(uint64_t node_id) {
return node_id != 0;
}
static int read_file_to_mem(const char *filename, char **buffer, size_t *size) {
FILE *fp = fopen(filename, "r");
if (!fp) return -1;
fseek(fp, 0, SEEK_END);
long file_size = ftell(fp);
if (file_size < 0) {
fclose(fp);
return -1;
}
fseek(fp, 0, SEEK_SET);
*buffer = u_malloc(file_size + 1);
if (!*buffer) {
fclose(fp);
return -1;
}
size_t read_size = fread(*buffer, 1, file_size, fp);
if (read_size != (size_t)file_size) {
u_free(*buffer);
fclose(fp);
return -1;
}
(*buffer)[file_size] = '\0';
*size = file_size;
fclose(fp);
return 0;
}
static int write_mem_to_file(const char *filename, const char *buffer, size_t size) {
FILE *fp = fopen(filename, "w");
@ -261,30 +220,35 @@ static int insert_or_replace_option(char **buf, size_t *buf_len, size_t *buf_cap
}
int config_ensure_keys_and_node_id(const char *filename) {
struct utun_config *config = parse_config(filename);
if (!config) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to parse config: %s", filename);
// ── Read config file into memory (single open) ──
FILE *fp = fopen(filename, "rb");
if (!fp) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to open config file: %s (errno=%d)", filename, errno);
return -1;
}
fseek(fp, 0, SEEK_END); long fsz = ftell(fp); fseek(fp, 0, SEEK_SET);
if (fsz < 0) { fclose(fp); return -1; }
size_t file_size = (size_t)fsz;
char *file_buf = u_malloc(file_size + 1);
if (!file_buf || fread(file_buf, 1, file_size, fp) != file_size) { u_free(file_buf); fclose(fp); return -1; }
file_buf[file_size] = '\0';
fclose(fp);
struct utun_config *config = parse_config_from_buf(file_buf, file_size, filename);
if (!config) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to parse config from buffer: %s", filename); u_free(file_buf); return -1; }
struct global_config *global = &config->global;
// Debug: print what we found
DEBUG_DEBUG(DEBUG_CATEGORY_CONFIG, "Checking config - priv_key='%s' (len=%zu), pub_key='%s' (len=%zu), node_id=%016llx",
global->my_private_key_hex[0] ? global->my_private_key_hex : "NULL",
global->my_private_key_hex[0] ? strlen(global->my_private_key_hex) : 0,
global->my_public_key_hex[0] ? global->my_public_key_hex : "NULL",
global->my_public_key_hex[0] ? global->my_public_key_hex : "NULL",
global->my_public_key_hex[0] ? strlen(global->my_public_key_hex) : 0,
(unsigned long long)global->my_node_id);
// Check if we need to generate anything
int need_priv_key = !is_valid_priv_key(global->my_private_key_hex);
int need_pub_key = 0;
int need_node_id = 0;
// Generate keys if needed
char new_priv_key[PRIV_HEXKEY_LEN] = {0};
char new_pub_key[PUB_HEXKEY_LEN] = {0};
uint64_t new_node_id = 0;
@ -294,34 +258,21 @@ int config_ensure_keys_and_node_id(const char *filename) {
if (need_priv_key) {
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Generating NEW keypair — private key was invalid/missing in %s", filename);
struct SC_MYKEYS mykeys;
if (sc_generate_keypair(&mykeys) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to generate keypair");
free_config(config);
return -1;
}
if (sc_generate_keypair(&mykeys) != SC_OK) { free_config(config); u_free(file_buf); return -1; }
bytes_to_hex(mykeys.private_key, SC_PRIVKEY_SIZE, new_priv_key, sizeof(new_priv_key));
memcpy(priv_bin, mykeys.private_key, SC_PRIVKEY_SIZE);
} else {
// Convert existing privkey hex → binary
for (int i = 0; i < SC_PRIVKEY_SIZE; i++) {
unsigned int byte;
if (sscanf(global->my_private_key_hex + i * 2, "%2x", &byte) != 1) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid private key hex format");
free_config(config);
return -1;
}
if (sscanf(global->my_private_key_hex + i * 2, "%2x", &byte) != 1) { free_config(config); u_free(file_buf); return -1; }
priv_bin[i] = (uint8_t)byte;
}
bytes_to_hex(priv_bin, SC_PRIVKEY_SIZE, new_priv_key, sizeof(new_priv_key));
}
// ── Step 2: derive pubkey from privkey (always check) ──
// ── Step 2: derive pubkey from privkey ──
uint8_t pub_bin[SC_PUBKEY_SIZE];
if (sc_compute_public_key_from_private(priv_bin, pub_bin) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to compute public key from private key");
free_config(config);
return -1;
}
if (sc_compute_public_key_from_private(priv_bin, pub_bin) != SC_OK) { free_config(config); u_free(file_buf); return -1; }
bytes_to_hex(pub_bin, SC_PUBKEY_SIZE, new_pub_key, sizeof(new_pub_key));
if (!is_valid_pub_key(global->my_public_key_hex) || strcmp(global->my_public_key_hex, new_pub_key) != 0) {
need_pub_key = 1;
@ -330,15 +281,8 @@ int config_ensure_keys_and_node_id(const char *filename) {
}
// ── Step 3: derive node_id from privkey via SHA-256 ──
{
uint8_t sha_hash[32];
SC_SHA256_CTX ctx;
sc_sha256_init(&ctx);
sc_sha256_update(&ctx, priv_bin, SC_PRIVKEY_SIZE);
sc_sha256_final(&ctx, sha_hash);
memcpy(&new_node_id, sha_hash, 8);
new_node_id &= 0x7FFFFFFFFFFFFFFFULL;
}
{ uint8_t sha_hash[32]; SC_SHA256_CTX ctx; sc_sha256_init(&ctx); sc_sha256_update(&ctx, priv_bin, SC_PRIVKEY_SIZE); sc_sha256_final(&ctx, sha_hash);
memcpy(&new_node_id, sha_hash, 8); new_node_id &= 0x7FFFFFFFFFFFFFFFULL; }
if (!is_valid_node_id(global->my_node_id) || global->my_node_id != new_node_id) {
need_node_id = 1;
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Node_id mismatch (or missing), fixing: config=%016llx derived=%016llx file=%s",
@ -348,36 +292,17 @@ int config_ensure_keys_and_node_id(const char *filename) {
DEBUG_DEBUG(DEBUG_CATEGORY_CONFIG, "Validation results - need_priv_key=%d, need_pub_key=%d, need_node_id=%d",
need_priv_key, need_pub_key, need_node_id);
if (!need_priv_key && !need_pub_key && !need_node_id) {
free_config(config);
return 0;
}
if (!need_priv_key && !need_pub_key && !need_node_id) { free_config(config); u_free(file_buf); return 0; }
free_config(config);
// Read entire config file to memory
char *file_buf = NULL;
size_t file_size = 0;
if (read_file_to_mem(filename, &file_buf, &file_size) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to read config file: %s", filename);
return -1;
}
// Update config file
// ── Modify file buffer in place ──
size_t buf_capacity = file_size + 1024;
char *work_buf = u_malloc(buf_capacity);
if (!work_buf) {
u_free(file_buf);
return -1;
}
if (!work_buf) { u_free(file_buf); return -1; }
memcpy(work_buf, file_buf, file_size);
u_free(file_buf);
size_t work_len = file_size;
int ret = 0;
int write_keys = (need_priv_key || need_pub_key || need_node_id);
@ -385,43 +310,25 @@ int config_ensure_keys_and_node_id(const char *filename) {
char node_id_hex[HEXNODEID_LEN + 1];
snprintf(node_id_hex, sizeof(node_id_hex), "%016llx", (unsigned long long)new_node_id);
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Writing my_node_id=%s to %s", node_id_hex, filename);
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_node_id", node_id_hex) < 0) {
ret = -1;
}
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_node_id", node_id_hex) < 0) ret = -1;
}
if (write_keys && ret == 0) {
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Writing my_private_key to %s", filename);
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_private_key", new_priv_key) < 0) {
ret = -1;
}
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_private_key", new_priv_key) < 0) ret = -1;
}
if (write_keys && ret == 0) {
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Writing my_public_key to %s", filename);
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_public_key", new_pub_key) < 0) {
ret = -1;
}
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_public_key", new_pub_key) < 0) ret = -1;
}
if (ret == 0) {
DEBUG_DEBUG(DEBUG_CATEGORY_CONFIG, "Writing updated config file, work_len=%zu", work_len);
if (ret == 0) {
if (write_mem_to_file(filename, work_buf, work_len) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to write updated config file: %s", filename);
ret = -1;
} else {
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Config file updated: %s (gen_priv=%d gen_pub=%d gen_nodeid=%d)", filename, need_priv_key, need_pub_key, need_node_id);
}
}
u_free(file_buf);
u_free(work_buf);
return ret;
}

Loading…
Cancel
Save