Commit 96ecf3ff authored by Vladislav Vaintroub's avatar Vladislav Vaintroub

MDEV-15501 : Make `proxy_protocol_networks` variable read-write.

parent 865cec92
...@@ -2322,7 +2322,7 @@ void clean_up(bool print_message) ...@@ -2322,7 +2322,7 @@ void clean_up(bool print_message)
my_free(const_cast<char*>(relay_log_index)); my_free(const_cast<char*>(relay_log_index));
#endif #endif
free_list(opt_plugin_load_list_ptr); free_list(opt_plugin_load_list_ptr);
cleanup_proxy_protocol_networks(); destroy_proxy_protocol_networks();
/* /*
The following lines may never be executed as the main thread may have The following lines may never be executed as the main thread may have
...@@ -2718,7 +2718,7 @@ static void network_init(void) ...@@ -2718,7 +2718,7 @@ static void network_init(void)
if (MYSQL_CALLBACK_ELSE(thread_scheduler, init, (), 0)) if (MYSQL_CALLBACK_ELSE(thread_scheduler, init, (), 0))
unireg_abort(1); /* purecov: inspected */ unireg_abort(1); /* purecov: inspected */
if (set_proxy_protocol_networks(my_proxy_protocol_networks)) if (init_proxy_protocol_networks(my_proxy_protocol_networks))
unireg_abort(1); unireg_abort(1);
set_ports(); set_ports();
......
...@@ -23,11 +23,14 @@ ...@@ -23,11 +23,14 @@
#include <violite.h> #include <violite.h>
#include <proxy_protocol.h> #include <proxy_protocol.h>
#include <log.h> #include <log.h>
#include <my_pthread.h>
#define PROXY_PROTOCOL_V1_SIGNATURE "PROXY" #define PROXY_PROTOCOL_V1_SIGNATURE "PROXY"
#define PROXY_PROTOCOL_V2_SIGNATURE "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A" #define PROXY_PROTOCOL_V2_SIGNATURE "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"
#define MAX_PROXY_HEADER_LEN 256 #define MAX_PROXY_HEADER_LEN 256
static mysql_rwlock_t lock;
/* /*
Parse proxy protocol version 1 header (text) Parse proxy protocol version 1 header (text)
*/ */
...@@ -341,31 +344,41 @@ static int parse_subnet(char *addr_str, struct subnet *subnet) ...@@ -341,31 +344,41 @@ static int parse_subnet(char *addr_str, struct subnet *subnet)
@param[in] subnets_str : networks in CIDR format, @param[in] subnets_str : networks in CIDR format,
separated by comma and/or space separated by comma and/or space
@param[out] out_subnets : parsed subnets;
@param[out] out_count : number of parsed subnets
@return 0 if success, otherwise -1 @return 0 if success, otherwise -1
*/ */
int set_proxy_protocol_networks(const char *subnets_str) static int parse_networks(const char *subnets_str, subnet **out_subnets, size_t *out_count)
{ {
int ret= -1;
subnet *subnets= 0;
size_t count= 0;
const char *p= subnets_str;
size_t max_subnets;
if (!subnets_str || !*subnets_str) if (!subnets_str || !*subnets_str)
return 0; {
ret= 0;
goto end;
}
size_t max_subnets= MY_MAX(3,strlen(subnets_str)/2); max_subnets= MY_MAX(3,strlen(subnets_str)/2);
proxy_protocol_subnets= (subnet *)my_malloc(max_subnets * sizeof(subnet),MY_ZEROFILL); subnets= (subnet *)my_malloc(max_subnets * sizeof(subnet),MY_ZEROFILL);
/* Check for special case '*'. */ /* Check for special case '*'. */
if (strcmp(subnets_str, "*") == 0) if (strcmp(subnets_str, "*") == 0)
{ {
subnets[0].family= AF_INET;
proxy_protocol_subnets[0].family= AF_INET; subnets[1].family= AF_INET6;
proxy_protocol_subnets[1].family= AF_INET6; subnets[2].family= AF_UNIX;
proxy_protocol_subnets[2].family= AF_UNIX; count= 3;
proxy_protocol_subnet_count= 3; ret= 0;
return 0; goto end;
} }
char token[256]; char token[256];
const char *p= subnets_str; for(count= 0;; count++)
for(proxy_protocol_subnet_count= 0;; proxy_protocol_subnet_count++)
{ {
while(*p && (*p ==',' || *p == ' ')) while(*p && (*p ==',' || *p == ' '))
p++; p++;
...@@ -377,18 +390,75 @@ int set_proxy_protocol_networks(const char *subnets_str) ...@@ -377,18 +390,75 @@ int set_proxy_protocol_networks(const char *subnets_str)
token[cnt++]= *p++; token[cnt++]= *p++;
token[cnt++]=0; token[cnt++]=0;
if (cnt == sizeof(token)) if (cnt == sizeof(token))
return -1; goto end;
if (parse_subnet(token, &proxy_protocol_subnets[proxy_protocol_subnet_count])) if (parse_subnet(token, &subnets[count]))
{ {
sql_print_error("Error parsing proxy_protocol_networks parameter, near '%s'",token); my_printf_error(ER_PARSE_ERROR,"Error parsing proxy_protocol_networks parameter, near '%s'",MYF(0),token);
return -1; goto end;
} }
} }
ret = 0;
end:
if (ret)
{
my_free(subnets);
*out_subnets= NULL;
*out_count= 0;
return ret;
}
*out_subnets = subnets;
*out_count= count;
return 0; return 0;
} }
/**
Check validity of proxy_protocol_networks parameter
@param[in] in - input string
@return : true, if input is list of CIDR-style networks
separated by command or space
*/
bool proxy_protocol_networks_valid(const char *in)
{
subnet *new_subnets;
size_t new_count;
int ret= parse_networks(in, &new_subnets, &new_count);
my_free(new_subnets);
return !ret;
}
/**
Set 'proxy_protocol_networks' parameter.
@param[in] spec : networks in CIDR format,
separated by comma and/or space
@return 0 if success, otherwise -1
*/
int set_proxy_protocol_networks(const char *spec)
{
subnet *new_subnets;
subnet *old_subnet = 0;
size_t new_count;
int ret= parse_networks(spec, &new_subnets, &new_count);
if (ret)
return ret;
mysql_rwlock_wrlock(&lock);
old_subnet = proxy_protocol_subnets;
proxy_protocol_subnets = new_subnets;
proxy_protocol_subnet_count = new_count;
mysql_rwlock_unlock(&lock);
my_free(old_subnet);
return ret;
}
/** /**
Compare memory areas, in memcmp().similar fashion. Compare memory areas, in memcmp().similar fashion.
The difference to memcmp() is that size parameter is the The difference to memcmp() is that size parameter is the
...@@ -475,20 +545,39 @@ bool is_proxy_protocol_allowed(const sockaddr *addr) ...@@ -475,20 +545,39 @@ bool is_proxy_protocol_allowed(const sockaddr *addr)
break; break;
default: default:
DBUG_ASSERT(0); DBUG_ASSERT(0);
} }
bool ret= false;
mysql_rwlock_rdlock(&lock);
for (size_t i= 0; i < proxy_protocol_subnet_count; i++) for (size_t i= 0; i < proxy_protocol_subnet_count; i++)
{
if (addr_matches_subnet(normalized_addr, &proxy_protocol_subnets[i])) if (addr_matches_subnet(normalized_addr, &proxy_protocol_subnets[i]))
return true; {
ret= true;
break;
}
}
mysql_rwlock_unlock(&lock);
return false; return ret;
} }
void cleanup_proxy_protocol_networks() int init_proxy_protocol_networks(const char *spec)
{ {
my_free(proxy_protocol_subnets); #ifdef HAVE_PSI_INTERFACE
proxy_protocol_subnets= 0; static PSI_rwlock_key psi_rwlock_key;
proxy_protocol_subnet_count= 0; static PSI_rwlock_info psi_rwlock_info={ &psi_rwlock_key, "rwlock", 0 };
mysql_rwlock_register("proxy_protocol", &psi_rwlock_info, 1);
#endif
mysql_rwlock_init(psi_rwlock_key, &lock);
return set_proxy_protocol_networks(spec);
} }
void destroy_proxy_protocol_networks()
{
my_free(proxy_protocol_subnets);
mysql_rwlock_destroy(&lock);
}
...@@ -11,5 +11,9 @@ extern bool has_proxy_protocol_header(NET *net); ...@@ -11,5 +11,9 @@ extern bool has_proxy_protocol_header(NET *net);
extern int parse_proxy_protocol_header(NET *net, proxy_peer_info *peer_info); extern int parse_proxy_protocol_header(NET *net, proxy_peer_info *peer_info);
extern bool is_proxy_protocol_allowed(const sockaddr *remote_addr); extern bool is_proxy_protocol_allowed(const sockaddr *remote_addr);
extern int init_proxy_protocol_networks(const char *spec);
extern void destroy_proxy_protocol_networks();
extern int set_proxy_protocol_networks(const char *spec); extern int set_proxy_protocol_networks(const char *spec);
extern void cleanup_proxy_protocol_networks(); extern bool proxy_protocol_networks_valid(const char *spec);
...@@ -4416,7 +4416,22 @@ static Sys_var_charptr Sys_license( ...@@ -4416,7 +4416,22 @@ static Sys_var_charptr Sys_license(
READ_ONLY GLOBAL_VAR(license), NO_CMD_LINE, IN_SYSTEM_CHARSET, READ_ONLY GLOBAL_VAR(license), NO_CMD_LINE, IN_SYSTEM_CHARSET,
DEFAULT(STRINGIFY_ARG(LICENSE))); DEFAULT(STRINGIFY_ARG(LICENSE)));
#include <proxy_protocol.h>
char *my_proxy_protocol_networks; char *my_proxy_protocol_networks;
static bool check_proxy_protocol_networks(sys_var *, THD *, set_var *var)
{
if (!var->value)
return false;
return !proxy_protocol_networks_valid(var->save_result.string_value.str);
}
static bool fix_proxy_protocol_networks(sys_var *, THD *, enum_var_type)
{
return (bool)set_proxy_protocol_networks(my_proxy_protocol_networks);
}
static Sys_var_charptr Sys_proxy_protocol_networks( static Sys_var_charptr Sys_proxy_protocol_networks(
"proxy_protocol_networks", "Enable proxy protocol for these source " "proxy_protocol_networks", "Enable proxy protocol for these source "
"networks. The syntax is a comma separated list of IPv4 and IPv6 " "networks. The syntax is a comma separated list of IPv4 and IPv6 "
...@@ -4424,8 +4439,10 @@ static Sys_var_charptr Sys_proxy_protocol_networks( ...@@ -4424,8 +4439,10 @@ static Sys_var_charptr Sys_proxy_protocol_networks(
"a single host. \"*\" represents all networks and must the only " "a single host. \"*\" represents all networks and must the only "
"directive on the line. String \"localhost\" represents non-TCP " "directive on the line. String \"localhost\" represents non-TCP "
"local connections (Unix domain socket, Windows named pipe or shared memory).", "local connections (Unix domain socket, Windows named pipe or shared memory).",
READ_ONLY GLOBAL_VAR(my_proxy_protocol_networks), GLOBAL_VAR(my_proxy_protocol_networks), CMD_LINE(REQUIRED_ARG),
CMD_LINE(REQUIRED_ARG), IN_FS_CHARSET, DEFAULT("")); IN_FS_CHARSET, DEFAULT(""), NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(check_proxy_protocol_networks), ON_UPDATE(fix_proxy_protocol_networks));
static bool check_log_path(sys_var *self, THD *thd, set_var *var) static bool check_log_path(sys_var *self, THD *thd, set_var *var)
{ {
......
...@@ -20220,6 +20220,18 @@ static void test_proxy_header_ignore() ...@@ -20220,6 +20220,18 @@ static void test_proxy_header_ignore()
mysql_optionsv(m, MARIADB_OPT_PROXY_HEADER, &v2_header,16); mysql_optionsv(m, MARIADB_OPT_PROXY_HEADER, &v2_header,16);
DIE_UNLESS(mysql_real_connect(m, opt_host, "root", "", NULL, opt_port, opt_unix_socket, 0) == m); DIE_UNLESS(mysql_real_connect(m, opt_host, "root", "", NULL, opt_port, opt_unix_socket, 0) == m);
mysql_close(m); mysql_close(m);
/* test for connection denied with empty proxy_protocol_networks */
int rc = mysql_query(mysql, "select @@proxy_protocol_networks into @sv_proxy_protocol_networks");
myquery(rc);
mysql_query(mysql, "set global proxy_protocol_networks=default");
myquery(rc);
m = mysql_client_init(NULL);
mysql_optionsv(m, MARIADB_OPT_PROXY_HEADER, &v2_header,16);
DIE_UNLESS(mysql_real_connect(m, opt_host, "root", "", NULL, opt_port, opt_unix_socket, 0) == 0);
mysql_close(m);
mysql_query(mysql, "set global proxy_protocol_networks= @sv_proxy_protocol_networks");
myquery(rc);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment