Parse CIDR, control endpoints and storage sizes without truncation
This commit is contained in:
1 parent
869421cc05
commit
7593120bd9
1 file changed
+70
-75
+70
-75
@@ -52,56 +52,58 @@ typedef enum {
|
||||
} section_type_t;
|
||||
|
||||
static int assign_string(char *dest, size_t dest_size, const char *src) {
|
||||
if (!dest || !src || strlen(src) >= dest_size) return -1;
|
||||
if (!dest || !src || strlen(src) >= dest_size) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "configuration string exceeds capacity=%zu or has a missing argument", dest_size);
|
||||
return -1;
|
||||
}
|
||||
strcpy(dest, src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Полное беззнаковое число без знака и хвоста; значение меняется только после всех проверок. */
|
||||
static int parse_unsigned(const char* text, int base, uint64_t maximum, uint64_t* out) {
|
||||
char* end;
|
||||
errno = 0;
|
||||
unsigned long long value = strtoull(text, &end, base);
|
||||
if ((base == 16 ? config_ini_nibble((unsigned char)*text) < 0 : !isdigit((unsigned char)*text)) ||
|
||||
end == text || *end || errno || value > maximum) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "invalid unsigned base-%d value '%s', maximum=%llu", base, text, (unsigned long long)maximum);
|
||||
return -1;
|
||||
}
|
||||
*out = value; return 0;
|
||||
}
|
||||
|
||||
static int parse_ip_with_netmask(const char *str, struct IP *ip, uint8_t *netmask) {
|
||||
char ip_str[64];
|
||||
strncpy(ip_str, str, sizeof(ip_str) - 1);
|
||||
ip_str[sizeof(ip_str) - 1] = '\0';
|
||||
|
||||
if (assign_string(ip_str, sizeof(ip_str), str)) return -1;
|
||||
char *slash = strchr(ip_str, '/');
|
||||
if (slash) {
|
||||
*slash = '\0';
|
||||
*netmask = atoi(slash + 1);
|
||||
} else {
|
||||
*netmask = 32; // Default IPv4 netmask
|
||||
if (slash) *slash++ = '\0';
|
||||
struct IP parsed = {0};
|
||||
unsigned maximum;
|
||||
if (inet_pton(AF_INET, ip_str, &parsed.addr.v4) == 1) { parsed.family = AF_INET; maximum = 32; }
|
||||
else if (inet_pton(AF_INET6, ip_str, &parsed.addr.v6) == 1) { parsed.family = AF_INET6; maximum = 128; }
|
||||
else {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "invalid IP address: %s", str); return -1;
|
||||
}
|
||||
|
||||
// Try IPv4 first
|
||||
struct in_addr addr4;
|
||||
if (inet_pton(AF_INET, ip_str, &addr4) == 1) {
|
||||
ip->family = AF_INET;
|
||||
ip->addr.v4 = addr4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Try IPv6
|
||||
struct in6_addr addr6;
|
||||
if (inet_pton(AF_INET6, ip_str, &addr6) == 1) {
|
||||
ip->family = AF_INET6;
|
||||
ip->addr.v6 = addr6;
|
||||
if (*netmask == 32) *netmask = 128; // Default IPv6 netmask
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "parse_ip_with_netmask: invalid IP address format: %s", str);
|
||||
return -1;
|
||||
uint64_t prefix = maximum;
|
||||
if (slash && parse_unsigned(slash, 10, maximum, &prefix)) return -1;
|
||||
*ip = parsed; *netmask = (uint8_t)prefix; return 0;
|
||||
}
|
||||
|
||||
static int parse_sockaddr(const char *addr_str, const char *port_str, struct sockaddr_storage *sockaddr) {
|
||||
struct addrinfo hints = {0};
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_flags = AI_NUMERICSERV;
|
||||
uint64_t port;
|
||||
if (!addr_str[0] || parse_unsigned(port_str, 10, UINT16_MAX, &port)) return -1;
|
||||
|
||||
char clean_addr[MAX_ADDR_LEN];
|
||||
const char *a = addr_str;
|
||||
if (addr_str[0] == '[') {
|
||||
size_t len = strlen(addr_str);
|
||||
if (len > 2 && addr_str[len - 1] == ']') {
|
||||
if (len - 2 >= sizeof(clean_addr)) return -1;
|
||||
if (len - 2 >= sizeof(clean_addr)) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "bracketed address is too long"); return -1; }
|
||||
memcpy(clean_addr, addr_str + 1, len - 2);
|
||||
clean_addr[len - 2] = '\0';
|
||||
a = clean_addr;
|
||||
@@ -109,10 +111,15 @@ static int parse_sockaddr(const char *addr_str, const char *port_str, struct soc
|
||||
}
|
||||
|
||||
struct addrinfo *result;
|
||||
if (getaddrinfo(a, port_str, &hints, &result) != 0) {
|
||||
int rc = getaddrinfo(a, port_str, &hints, &result);
|
||||
if (rc != 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "cannot resolve configured endpoint host=%s port=%s error=%d", a, port_str, rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!result || result->ai_addrlen > sizeof(*sockaddr)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "resolved endpoint has an invalid address size"); freeaddrinfo(result); return -1;
|
||||
}
|
||||
memset(sockaddr, 0, sizeof(*sockaddr));
|
||||
memcpy(sockaddr, result->ai_addr, result->ai_addrlen);
|
||||
freeaddrinfo(result);
|
||||
return 0;
|
||||
@@ -120,11 +127,10 @@ static int parse_sockaddr(const char *addr_str, const char *port_str, struct soc
|
||||
|
||||
static int parse_address_and_port(const char *str, struct sockaddr_storage *sockaddr) {
|
||||
char addr_copy[MAX_ADDR_LEN];
|
||||
if (strlen(str) >= sizeof(addr_copy)) return -1;
|
||||
strcpy(addr_copy, str);
|
||||
if (assign_string(addr_copy, sizeof(addr_copy), str)) return -1;
|
||||
|
||||
char *port_str = strrchr(addr_copy, ':');
|
||||
if (!port_str) return -1;
|
||||
if (!port_str) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "endpoint requires host:port: %s", str); return -1; }
|
||||
|
||||
*port_str = '\0';
|
||||
port_str++;
|
||||
@@ -151,6 +157,7 @@ static struct CFG_CLIENT_LINK* create_client_link(struct CFG_SERVER* local_srv,
|
||||
}
|
||||
|
||||
link->local_srv = local_srv;
|
||||
if (local_srv) snprintf(link->server_name, sizeof(link->server_name), "%s", local_srv->name);
|
||||
|
||||
if (parse_address_and_port(remote_addr, &link->remote_addr) < 0) {
|
||||
u_free(link);
|
||||
@@ -271,31 +278,19 @@ static int parse_firewall_rule(const char *rule_str, struct global_config *globa
|
||||
}
|
||||
|
||||
static int parse_control_allow(const char *rule_str, struct global_config *global) {
|
||||
if (!rule_str || !global) return -1;
|
||||
|
||||
char rule_copy[64];
|
||||
strncpy(rule_copy, rule_str, sizeof(rule_copy) - 1);
|
||||
rule_copy[sizeof(rule_copy) - 1] = '\0';
|
||||
config_ini_trim(rule_copy);
|
||||
|
||||
uint8_t cidr = 32;
|
||||
char *slash = strchr(rule_copy, '/');
|
||||
if (slash) {
|
||||
*slash = '\0';
|
||||
cidr = (uint8_t)atoi(slash + 1);
|
||||
if (cidr > 32) cidr = 32;
|
||||
}
|
||||
|
||||
struct in_addr addr;
|
||||
if (inet_pton(AF_INET, rule_copy, &addr) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "parse_control_allow: invalid IP: %s", rule_str);
|
||||
struct IP address;
|
||||
uint8_t cidr;
|
||||
if (parse_ip_with_netmask(rule_str, &address, &cidr) || address.family != AF_INET) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "control allow requires IPv4[/0..32]: %s", rule_str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t ip = ntohl(addr.s_addr);
|
||||
uint32_t mask = (cidr == 32) ? 0xFFFFFFFF : (~0U << (32 - cidr));
|
||||
uint32_t ip = ntohl(address.addr.v4.s_addr);
|
||||
uint32_t mask = cidr ? UINT32_MAX << (32 - cidr) : 0;
|
||||
|
||||
if ((global->control_allow_count % INITIAL_ARRAY_CAPACITY) == 0) {
|
||||
if ((uint32_t)global->control_allow_count > UINT32_MAX / sizeof(struct CFG_CONTROL_ALLOW) - INITIAL_ARRAY_CAPACITY) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "too many control allow rules"); return -1;
|
||||
}
|
||||
int new_cap = global->control_allow_count + INITIAL_ARRAY_CAPACITY;
|
||||
struct CFG_CONTROL_ALLOW *new_allows = u_realloc(global->control_allows,
|
||||
new_cap * sizeof(struct CFG_CONTROL_ALLOW));
|
||||
@@ -461,21 +456,19 @@ static int parse_global(const char *key, const char *value, struct global_config
|
||||
|
||||
static int parse_control(const char *key, const char *value, struct global_config *global, const char *filename, int line_num) {
|
||||
if (strcmp(key, "ip") == 0 || strcmp(key, "control_ip") == 0) {
|
||||
strncpy(global->control_ip, value, sizeof(global->control_ip) - 1);
|
||||
global->control_ip[sizeof(global->control_ip) - 1] = '\0';
|
||||
if (assign_string(global->control_ip, sizeof(global->control_ip), value)) return -1;
|
||||
if (global->control_sock.ss_family) {
|
||||
uint16_t port = global->control_sock.ss_family == AF_INET ?
|
||||
ntohs(((struct sockaddr_in*)&global->control_sock)->sin_port) :
|
||||
ntohs(((struct sockaddr_in6*)&global->control_sock)->sin6_port);
|
||||
char port_str[6]; snprintf(port_str, sizeof(port_str), "%u", port);
|
||||
return parse_sockaddr(value, port_str, &global->control_sock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "port") == 0 || strcmp(key, "control_port") == 0) {
|
||||
char control_ip[MAX_ADDR_LEN];
|
||||
if (global->control_ip[0] != '\0') {
|
||||
strncpy(control_ip, global->control_ip, sizeof(control_ip) - 1);
|
||||
} else {
|
||||
strcpy(control_ip, "127.0.0.1");
|
||||
}
|
||||
char port_str[16];
|
||||
snprintf(port_str, sizeof(port_str), "%s", value);
|
||||
parse_sockaddr(control_ip, port_str, &global->control_sock);
|
||||
return 0;
|
||||
const char* control_ip = global->control_ip[0] ? global->control_ip : "127.0.0.1";
|
||||
return parse_sockaddr(control_ip, value, &global->control_sock);
|
||||
}
|
||||
if (strcmp(key, "allow") == 0 || strcmp(key, "control_allow") == 0) {
|
||||
return parse_control_allow(value, global);
|
||||
@@ -824,15 +817,17 @@ static int parse_client(const char *key, const char *value, struct CFG_CLIENT *c
|
||||
}
|
||||
|
||||
static int parse_size_with_suffix(const char *value, uint64_t *out_bytes) {
|
||||
if (!value || !out_bytes) return -1;
|
||||
char *end = NULL;
|
||||
char *end;
|
||||
errno = 0;
|
||||
uint64_t num = strtoull(value, &end, 10);
|
||||
if (end == value) return -1;
|
||||
if (!isdigit((unsigned char)*value) || errno || 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;
|
||||
uint64_t scale = 1;
|
||||
if (*end == 'M' || *end == 'm') { scale = UINT64_C(1024) * 1024; end++; }
|
||||
else if (*end == 'G' || *end == 'g') { scale = UINT64_C(1024) * 1024 * 1024; end++; }
|
||||
while (*end && isspace((unsigned char)*end)) end++;
|
||||
if (*end || num > UINT64_MAX / scale) return -1;
|
||||
*out_bytes = num * scale; return 0;
|
||||
}
|
||||
|
||||
static int parse_chatserver(const char *key, const char *value, struct global_config *global, const char *filename, int line_num) {
|
||||
|
||||
Reference in new issue
Block a user