1
This commit is contained in:
1 parent
26f4da1b7c
commit
712d1b53df
29 files changed
+1029
-1138
No files matched your search
@@ -52,6 +52,7 @@ typedef enum {
|
||||
SECTION_GUI,
|
||||
SECTION_CHAT,
|
||||
SECTION_NTP,
|
||||
SECTION_CHATSERVER,
|
||||
SECTION_LOG_UDP
|
||||
} section_type_t;
|
||||
|
||||
@@ -718,6 +719,51 @@ static int parse_client(const char *key, const char *value, struct CFG_CLIENT *c
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int parse_size_with_suffix(const char *value, uint64_t *out_bytes) {
|
||||
if (!value || !out_bytes) return -1;
|
||||
char *end = NULL;
|
||||
uint64_t num = strtoull(value, &end, 10);
|
||||
if (end == value) return -1;
|
||||
while (*end && isspace((unsigned char)*end)) end++;
|
||||
if (*end == '\0') { *out_bytes = num; return 0; }
|
||||
if (*end == 'M' || *end == 'm') { *out_bytes = num * 1024ULL * 1024ULL; return 0; }
|
||||
if (*end == 'G' || *end == 'g') { *out_bytes = num * 1024ULL * 1024ULL * 1024ULL; return 0; }
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int parse_chatserver(const char *key, const char *value, struct global_config *global, const char *filename, int line_num) {
|
||||
if (strcmp(key, "join_password") == 0) {
|
||||
strncpy(global->chatserver_join_password, value, sizeof(global->chatserver_join_password) - 1);
|
||||
global->chatserver_join_password[sizeof(global->chatserver_join_password) - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "allowed_groups") == 0) {
|
||||
strncpy(global->chatserver_allowed_groups, value, sizeof(global->chatserver_allowed_groups) - 1);
|
||||
global->chatserver_allowed_groups[sizeof(global->chatserver_allowed_groups) - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "storage_total_size") == 0) {
|
||||
uint64_t sz;
|
||||
if (parse_size_with_suffix(value, &sz) < 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Invalid chatserver storage_total_size '%s' (expected number with M/G suffix)", filename, line_num, value);
|
||||
return -1;
|
||||
}
|
||||
global->chatserver_storage_total_size = sz;
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "storage_unit_size") == 0) {
|
||||
uint64_t sz;
|
||||
if (parse_size_with_suffix(value, &sz) < 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Invalid chatserver storage_unit_size '%s' (expected number with M/G suffix)", filename, line_num, value);
|
||||
return -1;
|
||||
}
|
||||
global->chatserver_storage_unit_size = sz;
|
||||
return 0;
|
||||
}
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown chatserver option '%s'. Valid: join_policy, join_password, allowed_groups, storage_total_size, storage_unit_size", filename, line_num, key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int parse_network(const char *key, const char *value, struct CFG_NETWORK *net, const char *filename, int line_num) {
|
||||
if (strcmp(key, "id") == 0) {
|
||||
net->id = strtoull(value, NULL, 16);
|
||||
@@ -758,6 +804,7 @@ static section_type_t parse_section_header(const char *line, char *name, size_t
|
||||
if (strcasecmp(section, "ntp") == 0) return SECTION_NTP;
|
||||
if (strcasecmp(section, "gui") == 0) return SECTION_GUI;
|
||||
if (strcasecmp(section, "chat") == 0) return SECTION_CHAT;
|
||||
if (strcasecmp(section, "chatserver") == 0) return SECTION_CHATSERVER;
|
||||
if (strcasecmp(section, "log_udp") == 0) return SECTION_LOG_UDP;
|
||||
|
||||
char *colon = strchr(section, ':');
|
||||
@@ -802,6 +849,11 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
|
||||
cfg->global.ntp_servers = NULL;
|
||||
cfg->global.ntp_server_count = 0;
|
||||
cfg->global.ntp_resync_interval = 3600;
|
||||
cfg->global.chatserver_enabled = 0;
|
||||
cfg->global.chatserver_join_password[0] = '\0';
|
||||
cfg->global.chatserver_allowed_groups[0] = '\0';
|
||||
cfg->global.chatserver_storage_total_size = 0;
|
||||
cfg->global.chatserver_storage_unit_size = 0;
|
||||
|
||||
section_type_t cur_section = SECTION_UNKNOWN;
|
||||
struct CFG_SERVER *cur_server = NULL;
|
||||
@@ -993,6 +1045,12 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
|
||||
/* chat_setting_set уже напечатал ошибку */
|
||||
}
|
||||
break;
|
||||
case SECTION_CHATSERVER:
|
||||
cfg->global.chatserver_enabled = 1;
|
||||
if (parse_chatserver(key, value, &cfg->global, filename, line_num) < 0) {
|
||||
/* parse_chatserver already printed the error */
|
||||
}
|
||||
break;
|
||||
case SECTION_GUI:
|
||||
break;
|
||||
default:
|
||||
@@ -1076,6 +1134,11 @@ struct utun_config* parse_config_from_buf(const char *buf, size_t len, const cha
|
||||
cfg->global.ntp_servers = NULL;
|
||||
cfg->global.ntp_server_count = 0;
|
||||
cfg->global.ntp_resync_interval = 3600;
|
||||
cfg->global.chatserver_enabled = 0;
|
||||
cfg->global.chatserver_join_password[0] = '\0';
|
||||
cfg->global.chatserver_allowed_groups[0] = '\0';
|
||||
cfg->global.chatserver_storage_total_size = 0;
|
||||
cfg->global.chatserver_storage_unit_size = 0;
|
||||
|
||||
section_type_t cur_section = SECTION_UNKNOWN;
|
||||
struct CFG_SERVER *cur_server = NULL;
|
||||
@@ -1117,6 +1180,7 @@ struct utun_config* parse_config_from_buf(const char *buf, size_t len, const cha
|
||||
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) { struct CFG_NTP_SERVER *ns = u_calloc(1, sizeof(struct CFG_NTP_SERVER)); if (ns) { strncpy(ns->name, value, sizeof(ns->name) - 1); ns->name[sizeof(ns->name) - 1] = '\0'; ns->next = cfg->global.ntp_servers; cfg->global.ntp_servers = ns; 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_CHAT: chat_setting_set(key, value); break;
|
||||
case SECTION_CHATSERVER: cfg->global.chatserver_enabled = 1; parse_chatserver(key, value, &cfg->global, filename, line_num); break;
|
||||
case SECTION_GUI: break;
|
||||
case SECTION_LOG_UDP:
|
||||
if (strcmp(key, "ip") == 0) strncpy(cfg->global.log_udp_ip, value, sizeof(cfg->global.log_udp_ip) - 1);
|
||||
@@ -1323,6 +1387,112 @@ void print_config(const struct utun_config *cfg) {
|
||||
}
|
||||
}
|
||||
|
||||
static const char invite_b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
void print_chatserver_invite_link(const struct utun_config *config, const struct CFG_SERVER *servers) {
|
||||
if (!config || !servers) return;
|
||||
const struct global_config *g = &config->global;
|
||||
if (!g->chatserver_enabled) return;
|
||||
if (strlen(g->my_public_key_hex) < SC_PUBKEY_SIZE * 2) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "print_chatserver_invite_link: no public key");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t pub_bin[SC_PUBKEY_SIZE];
|
||||
/* Use first SC_PUBKEY_SIZE*2 hex chars (X25519 pubkey, may be part of longer combined key) */
|
||||
{
|
||||
char pub_hex_first[SC_PUBKEY_SIZE * 2 + 1];
|
||||
memcpy(pub_hex_first, g->my_public_key_hex, SC_PUBKEY_SIZE * 2);
|
||||
pub_hex_first[SC_PUBKEY_SIZE * 2] = '\0';
|
||||
if (hex_to_binary(pub_hex_first, pub_bin, SC_PUBKEY_SIZE) < 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "print_chatserver_invite_link: invalid pubkey hex");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Collect server addresses */
|
||||
struct {
|
||||
int family;
|
||||
uint8_t addr[16];
|
||||
uint16_t port;
|
||||
uint8_t proto;
|
||||
} addrs[32];
|
||||
int addr_count = 0;
|
||||
for (const struct CFG_SERVER *s = servers; s && addr_count < 32; s = s->next) {
|
||||
if (s->ip.ss_family == AF_INET) {
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *)&s->ip;
|
||||
addrs[addr_count].family = 4;
|
||||
memcpy(addrs[addr_count].addr, &sin->sin_addr, 4);
|
||||
addrs[addr_count].port = ntohs(sin->sin_port);
|
||||
addrs[addr_count].proto = (s->transport == 1) ? 0x02 : 0x01;
|
||||
addr_count++;
|
||||
} else if (s->ip.ss_family == AF_INET6) {
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&s->ip;
|
||||
addrs[addr_count].family = 6;
|
||||
memcpy(addrs[addr_count].addr, &sin6->sin6_addr, 16);
|
||||
addrs[addr_count].port = ntohs(sin6->sin6_port);
|
||||
addrs[addr_count].proto = (s->transport == 1) ? 0x02 : 0x01;
|
||||
addr_count++;
|
||||
}
|
||||
}
|
||||
if (addr_count == 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "print_chatserver_invite_link: no server addresses");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Build binary blob */
|
||||
uint8_t raw[4096];
|
||||
size_t pos = 0;
|
||||
int has_pass = g->chatserver_join_password[0] ? 1 : 0;
|
||||
raw[pos++] = has_pass ? 0x02 : 0x01; /* version */
|
||||
if (has_pass) {
|
||||
size_t plen = strlen(g->chatserver_join_password);
|
||||
raw[pos++] = (uint8_t)plen;
|
||||
memcpy(raw + pos, g->chatserver_join_password, plen); pos += plen;
|
||||
}
|
||||
/* channel_id = 0 (8 bytes BE) */
|
||||
for (int i = 0; i < 8; i++) raw[pos++] = 0;
|
||||
|
||||
int ia = 0;
|
||||
while (ia < addr_count) {
|
||||
int rem = addr_count - ia;
|
||||
int cnt = (rem > 4) ? 4 : rem;
|
||||
uint8_t header = (uint8_t)((cnt - 1) & 0x03);
|
||||
for (int j = 0; j < cnt; j++) if (addrs[ia + j].family == 6) header |= (uint8_t)(1 << (2 + j));
|
||||
raw[pos++] = header;
|
||||
memcpy(raw + pos, pub_bin, SC_PUBKEY_SIZE); pos += SC_PUBKEY_SIZE;
|
||||
for (int j = 0; j < cnt; j++) {
|
||||
int ip_len = (addrs[ia + j].family == 6) ? 16 : 4;
|
||||
raw[pos++] = 0; /* socketId */
|
||||
raw[pos++] = addrs[ia + j].proto; /* proto */
|
||||
memcpy(raw + pos, addrs[ia + j].addr, (size_t)ip_len); pos += (size_t)ip_len;
|
||||
raw[pos++] = (uint8_t)(addrs[ia + j].port >> 8);
|
||||
raw[pos++] = (uint8_t)(addrs[ia + j].port & 0xFF);
|
||||
}
|
||||
ia += cnt;
|
||||
}
|
||||
|
||||
/* Base64 encode */
|
||||
char link[2048];
|
||||
memcpy(link, "utun://", 7);
|
||||
size_t opos = 7;
|
||||
size_t i = 0;
|
||||
while (i < pos) {
|
||||
uint32_t val = (uint32_t)raw[i] << 16;
|
||||
val |= (i + 1 < pos) ? (uint32_t)raw[i + 1] << 8 : 0;
|
||||
val |= (i + 2 < pos) ? (uint32_t)raw[i + 2] : 0;
|
||||
link[opos++] = invite_b64[(val >> 18) & 0x3F];
|
||||
link[opos++] = invite_b64[(val >> 12) & 0x3F];
|
||||
link[opos++] = (i + 1 < pos) ? invite_b64[(val >> 6) & 0x3F] : '=';
|
||||
link[opos++] = (i + 2 < pos) ? invite_b64[val & 0x3F] : '=';
|
||||
i += 3;
|
||||
}
|
||||
link[opos] = '\0';
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Chatserver invite link: %s", link);
|
||||
fprintf(stderr, "Chatserver invite link: %s\n", link);
|
||||
}
|
||||
|
||||
int update_config_keys(const char *filename, const char *priv_key, const char *pub_key) {
|
||||
// Minimal implementation: just append to file
|
||||
FILE *fp = fopen(filename, "a");
|
||||
|
||||
Reference in new issue
Block a user