|
|
|
|
@ -30,7 +30,8 @@ typedef enum {
|
|
|
|
|
SECTION_CLIENT, |
|
|
|
|
SECTION_ROUTING, |
|
|
|
|
SECTION_DEBUG, |
|
|
|
|
SECTION_FIREWALL |
|
|
|
|
SECTION_FIREWALL, |
|
|
|
|
SECTION_CONTROL |
|
|
|
|
} section_type_t; |
|
|
|
|
|
|
|
|
|
static char* trim(char *str) { |
|
|
|
|
@ -258,6 +259,51 @@ static int parse_firewall_rule(const char *rule_str, struct global_config *globa
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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'; |
|
|
|
|
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); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
uint32_t ip = ntohl(addr.s_addr); |
|
|
|
|
uint32_t mask = (cidr == 32) ? 0xFFFFFFFF : (~0U << (32 - cidr)); |
|
|
|
|
|
|
|
|
|
if ((global->control_allow_count % INITIAL_ARRAY_CAPACITY) == 0) { |
|
|
|
|
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)); |
|
|
|
|
if (!new_allows) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to realloc control allows"); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
global->control_allows = new_allows; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
struct CFG_CONTROL_ALLOW *rule = &global->control_allows[global->control_allow_count]; |
|
|
|
|
rule->network = ip & mask; |
|
|
|
|
rule->netmask = mask; |
|
|
|
|
global->control_allow_count++; |
|
|
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Added control_allow: %s/%u", rule_str, (unsigned)cidr); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static struct CFG_SERVER* find_server_by_name(struct CFG_SERVER *servers, const char *name) { |
|
|
|
|
struct CFG_SERVER *srv = servers; |
|
|
|
|
while (srv) { |
|
|
|
|
@ -303,29 +349,6 @@ static int parse_global(const char *key, const char *value, struct global_config
|
|
|
|
|
global->keepalive_interval = atoi(value); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
if (strcmp(key, "control_ip") == 0) { |
|
|
|
|
// Store control_ip for later processing with control_port
|
|
|
|
|
strncpy(global->control_ip, value, sizeof(global->control_ip) - 1); |
|
|
|
|
global->control_ip[sizeof(global->control_ip) - 1] = '\0'; |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
if (strcmp(key, "control_port") == 0) { |
|
|
|
|
// Use control_ip from config, fallback to localhost if not set
|
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
if (strcmp(key, "net_debug") == 0) { |
|
|
|
|
global->net_debug = atoi(value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (strcmp(key, "debug_level") == 0) { |
|
|
|
|
return assign_string(global->debug_level, sizeof(global->debug_level), value); |
|
|
|
|
} |
|
|
|
|
@ -356,6 +379,30 @@ static int parse_global(const char *key, const char *value, struct global_config
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int parse_control(const char *key, const char *value, struct global_config *global) { |
|
|
|
|
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'; |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
if (strcmp(key, "allow") == 0 || strcmp(key, "control_allow") == 0) { |
|
|
|
|
return parse_control_allow(value, global); |
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int parse_server(const char *key, const char *value, struct CFG_SERVER *srv) { |
|
|
|
|
if (strcmp(key, "addr") == 0) { |
|
|
|
|
return parse_address_and_port(value, &srv->ip); |
|
|
|
|
@ -455,6 +502,7 @@ static section_type_t parse_section_header(const char *line, char *name, size_t
|
|
|
|
|
if (strcasecmp(section, "routing") == 0) return SECTION_ROUTING; |
|
|
|
|
if (strcasecmp(section, "debug") == 0) return SECTION_DEBUG; |
|
|
|
|
if (strcasecmp(section, "firewall") == 0) return SECTION_FIREWALL; |
|
|
|
|
if (strcasecmp(section, "control") == 0) return SECTION_CONTROL; |
|
|
|
|
|
|
|
|
|
char *colon = strchr(section, ':'); |
|
|
|
|
if (!colon) return SECTION_UNKNOWN; |
|
|
|
|
@ -485,6 +533,8 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
|
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
section_type_t cur_section = SECTION_UNKNOWN; |
|
|
|
|
struct CFG_SERVER *cur_server = NULL; |
|
|
|
|
@ -583,6 +633,11 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case SECTION_CONTROL: |
|
|
|
|
if (parse_control(key, value, &cfg->global) < 0) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: Invalid control key '%s'", filename, line_num, key); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: Key outside section: %s", filename, line_num, key); |
|
|
|
|
break; |
|
|
|
|
@ -657,6 +712,8 @@ void free_config(struct utun_config *config) {
|
|
|
|
|
|
|
|
|
|
// Free firewall rules
|
|
|
|
|
u_free(config->global.firewall_rules); |
|
|
|
|
// Free control allow rules (array allocated with realloc)
|
|
|
|
|
u_free(config->global.control_allows); |
|
|
|
|
|
|
|
|
|
u_free(config); |
|
|
|
|
} |
|
|
|
|
@ -741,6 +798,8 @@ void print_config(const struct utun_config *cfg) {
|
|
|
|
|
} else { |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, " no rules"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Control allows: %d rules (default deny all if 0)", g->control_allow_count); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int update_config_keys(const char *filename, const char *priv_key, const char *pub_key) { |
|
|
|
|
|