Validate complete protocol versions, network keys and endpoint ports

This commit is contained in:
evgeny committed 2026-10-26 09:08:23 +01:00
1 parent 7593120bd9
commit bff354f9cc
1 file changed
+40 -36
+40 -36
View File
@@ -73,6 +73,26 @@ static int parse_unsigned(const char* text, int base, uint64_t maximum, uint64_t
*out = value; return 0;
}
/* Версия содержит ровно три десятичных октета, без хвоста; выход применяется целиком. */
static int parse_version(const char* value, uint8_t out[3]) {
char text[12];
if (assign_string(text, sizeof(text), value)) return -1;
char* cursor = text;
uint8_t version[3];
for (unsigned i = 0; i < 3; i++) {
char* dot = strchr(cursor, '.');
if ((i < 2 && !dot) || (i == 2 && dot)) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "invalid version format, expected x.y.z"); return -1;
}
if (dot) *dot = '\0';
uint64_t number;
if (parse_unsigned(cursor, 10, UINT8_MAX, &number)) return -1;
version[i] = (uint8_t)number;
if (dot) cursor = dot + 1;
}
memcpy(out, version, sizeof(version)); return 0;
}
static int parse_ip_with_netmask(const char *str, struct IP *ip, uint8_t *netmask) {
char ip_str[64];
if (assign_string(ip_str, sizeof(ip_str), str)) return -1;
@@ -668,26 +688,18 @@ static int parse_nat(const char *key, const char *value, struct global_config *g
static int parse_server(const char *key, const char *value, struct CFG_SERVER *srv, const char *filename, int line_num) {
if (strcmp(key, "addr") == 0) {
if (strncmp(value, "temporary_ipv6:", 15) == 0) {
srv->ipv6_mode = CFG_IPV6_MODE_TEMPORARY;
const char* port_str = value + 15;
if (!port_str[0]) return -1;
int temporary = strncmp(value, "temporary_ipv6:", 15) == 0;
if (temporary || strncmp(value, "permanent_ipv6:", 15) == 0) {
uint64_t port;
if (parse_unsigned(value + 15, 10, UINT16_MAX, &port)) return -1;
srv->ipv6_mode = temporary ? CFG_IPV6_MODE_TEMPORARY : CFG_IPV6_MODE_PERMANENT;
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&srv->ip;
memset(&srv->ip, 0, sizeof(srv->ip));
sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons((uint16_t)atoi(port_str));
return 0;
}
if (strncmp(value, "permanent_ipv6:", 15) == 0) {
srv->ipv6_mode = CFG_IPV6_MODE_PERMANENT;
const char* port_str = value + 15;
if (!port_str[0]) return -1;
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&srv->ip;
memset(&srv->ip, 0, sizeof(srv->ip));
sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons((uint16_t)atoi(port_str));
sin6->sin6_port = htons((uint16_t)port);
return 0;
}
srv->ipv6_mode = CFG_IPV6_MODE_NONE;
return parse_address_and_port(value, &srv->ip);
}
if (strcmp(key, "so_mark") == 0) {
@@ -712,7 +724,7 @@ static int parse_server(const char *key, const char *value, struct CFG_SERVER *s
} else if (strcmp(value, "local") == 0) {
srv->type = CFG_SERVER_TYPE_LOCAL;
} else {
srv->type = CFG_SERVER_TYPE_UNKNOWN;
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: unknown server type '%s'", filename, line_num, value); return -1;
}
return 0;
}
@@ -726,7 +738,8 @@ static int parse_server(const char *key, const char *value, struct CFG_SERVER *s
}
if (strcmp(key, "transport") == 0) {
if (strcmp(value, "tcp") == 0) srv->transport = 1;
else srv->transport = 0; // default udp
else if (strcmp(value, "udp") == 0) srv->transport = 0;
else { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: unknown server transport '%s'", filename, line_num, value); return -1; }
return 0;
}
if (strcmp(key, "reality") == 0) {
@@ -798,13 +811,7 @@ static int parse_client(const char *key, const char *value, struct CFG_CLIENT *c
return 0;
}
if (strcmp(key, "version") == 0) {
int x = 0, y = 0, z = 0;
if (sscanf(value, "%d.%d.%d", &x, &y, &z) != 3 || x < 0 || x > 255 || y < 0 || y > 255 || z < 0 || z > 255) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: bad client reality version '%s' (expected x.y.z)", filename, line_num, value);
return -1;
}
cli->reality.version[0] = (uint8_t)x; cli->reality.version[1] = (uint8_t)y; cli->reality.version[2] = (uint8_t)z;
return 0;
return parse_version(value, cli->reality.version);
}
if (strcmp(key, "fingerprint") == 0) {
if (strcmp(value, "openssl") == 0) { cli->reality.fingerprint = REALITY_FP_OPENSSL; return 0; }
@@ -879,13 +886,14 @@ static int parse_chatserver(const char *key, const char *value, struct global_co
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);
return 0;
return parse_unsigned(value, 16, UINT64_C(0x00ffffffffffffff), &net->id);
}
if (strcmp(key, "pubkey") == 0) {
if (config_ini_hex(value, NULL, SC_PUBKEY_SIZE)) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "network pubkey requires 64 HEX characters"); return -1; }
return assign_string(net->pubkey_hex, sizeof(net->pubkey_hex), value);
}
if (strcmp(key, "signing_key") == 0) {
if (config_ini_hex(value, NULL, SC_PRIVKEY_SIZE)) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "network signing key requires 64 HEX characters"); return -1; }
return assign_string(net->signing_key_hex, sizeof(net->signing_key_hex), value);
}
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown network option '%s'. Valid: id, pubkey, signing_key", filename, line_num, key);
@@ -930,13 +938,7 @@ static int parse_reality(const char *key, const char *value, struct reality_conf
return 0;
}
if (strcmp(key, "version") == 0) {
int x = 0, y = 0, z = 0;
if (sscanf(value, "%d.%d.%d", &x, &y, &z) != 3 || x < 0 || x > 255 || y < 0 || y > 255 || z < 0 || z > 255) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: bad reality version '%s' (expected x.y.z)", filename, line_num, value);
return -1;
}
rc->version[0] = (uint8_t)x; rc->version[1] = (uint8_t)y; rc->version[2] = (uint8_t)z;
return 0;
return parse_version(value, rc->version);
}
if (strcmp(key, "time_window") == 0) { rc->time_window_sec = atoi(value); return 0; }
if (strcmp(key, "relay_idle_timeout") == 0) { rc->relay_idle_timeout_sec = atoi(value); return 0; }
@@ -958,11 +960,13 @@ static int parse_socks(const char *key, const char *value, struct global_config
if (strcmp(key, "addr") == 0) {
const char *colon = strrchr(value, ':');
if (!colon) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: socks addr must be 'host:port'", filename, line_num); return -1; }
int port = atoi(colon + 1);
if (port <= 0 || port > 65535) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: bad socks port '%s'", filename, line_num, colon + 1); return -1; }
uint64_t port;
if (parse_unsigned(colon + 1, 10, UINT16_MAX, &port) || !port) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: bad socks port", filename, line_num); return -1;
}
char host[64];
size_t hlen = (size_t)(colon - value);
if (hlen >= sizeof(host)) hlen = sizeof(host) - 1;
if (!hlen || hlen >= sizeof(host)) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: invalid socks host length", filename, line_num); return -1; }
memcpy(host, value, hlen); host[hlen] = '\0';
if (hlen >= 2 && host[0] == '[' && host[hlen - 1] == ']') { host[hlen - 1] = '\0'; memmove(host, host + 1, strlen(host)); }
strncpy(global->socks_host, host, sizeof(global->socks_host) - 1);