From bff354f9ccc13842a897c8518793aba4408a002d Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 26 Oct 2026 00:07:54 +0100 Subject: [PATCH] Validate complete protocol versions, network keys and endpoint ports --- src/config_parser.c | 76 ++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/src/config_parser.c b/src/config_parser.c index f2c00508..2480e947 100644 --- a/src/config_parser.c +++ b/src/config_parser.c @@ -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);