summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladislav Vaintroub <wlad@mariadb.com>2018-03-21 18:09:52 +0000
committerVladislav Vaintroub <wlad@mariadb.com>2018-03-23 20:23:12 +0000
commit96ecf3ff23011ee7e8b92da5d9561d9cd7e75d53 (patch)
treef584ddc42bd0b8540b4813c3609522af8117c55e
parent865cec928a0f33ea1e6f71d0a3d8ac0d0570252a (diff)
downloadmariadb-git-96ecf3ff23011ee7e8b92da5d9561d9cd7e75d53.tar.gz
MDEV-15501 : Make `proxy_protocol_networks` variable read-write.
-rw-r--r--sql/mysqld.cc4
-rw-r--r--sql/proxy_protocol.cc137
-rw-r--r--sql/proxy_protocol.h6
-rw-r--r--sql/sys_vars.cc21
-rw-r--r--tests/mysql_client_test.c12
5 files changed, 151 insertions, 29 deletions
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 5a16cbac31c..1872db71d7a 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -2322,7 +2322,7 @@ void clean_up(bool print_message)
my_free(const_cast<char*>(relay_log_index));
#endif
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
@@ -2718,7 +2718,7 @@ static void network_init(void)
if (MYSQL_CALLBACK_ELSE(thread_scheduler, init, (), 0))
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);
set_ports();
diff --git a/sql/proxy_protocol.cc b/sql/proxy_protocol.cc
index dbdb1566bc7..2215a22d2b2 100644
--- a/sql/proxy_protocol.cc
+++ b/sql/proxy_protocol.cc
@@ -23,11 +23,14 @@
#include <violite.h>
#include <proxy_protocol.h>
#include <log.h>
+#include <my_pthread.h>
#define PROXY_PROTOCOL_V1_SIGNATURE "PROXY"
#define PROXY_PROTOCOL_V2_SIGNATURE "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"
#define MAX_PROXY_HEADER_LEN 256
+static mysql_rwlock_t lock;
+
/*
Parse proxy protocol version 1 header (text)
*/
@@ -341,31 +344,41 @@ static int parse_subnet(char *addr_str, struct subnet *subnet)
@param[in] subnets_str : networks in CIDR format,
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
*/
-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)
- return 0;
+ {
+ ret= 0;
+ goto end;
+ }
- size_t max_subnets= MY_MAX(3,strlen(subnets_str)/2);
- proxy_protocol_subnets= (subnet *)my_malloc(max_subnets * sizeof(subnet),MY_ZEROFILL);
+ max_subnets= MY_MAX(3,strlen(subnets_str)/2);
+ subnets= (subnet *)my_malloc(max_subnets * sizeof(subnet),MY_ZEROFILL);
/* Check for special case '*'. */
if (strcmp(subnets_str, "*") == 0)
{
-
- proxy_protocol_subnets[0].family= AF_INET;
- proxy_protocol_subnets[1].family= AF_INET6;
- proxy_protocol_subnets[2].family= AF_UNIX;
- proxy_protocol_subnet_count= 3;
- return 0;
+ subnets[0].family= AF_INET;
+ subnets[1].family= AF_INET6;
+ subnets[2].family= AF_UNIX;
+ count= 3;
+ ret= 0;
+ goto end;
}
char token[256];
- const char *p= subnets_str;
- for(proxy_protocol_subnet_count= 0;; proxy_protocol_subnet_count++)
+ for(count= 0;; count++)
{
while(*p && (*p ==',' || *p == ' '))
p++;
@@ -377,19 +390,76 @@ int set_proxy_protocol_networks(const char *subnets_str)
token[cnt++]= *p++;
token[cnt++]=0;
- if (cnt == sizeof(token))
- return -1;
+ if (cnt == sizeof(token))
+ 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);
- return -1;
+ my_printf_error(ER_PARSE_ERROR,"Error parsing proxy_protocol_networks parameter, near '%s'",MYF(0),token);
+ 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;
}
/**
+ 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.
The difference to memcmp() is that size parameter is the
bit count, not byte count.
@@ -475,20 +545,39 @@ bool is_proxy_protocol_allowed(const sockaddr *addr)
break;
default:
DBUG_ASSERT(0);
- }
+ }
+ bool ret= false;
+ mysql_rwlock_rdlock(&lock);
for (size_t i= 0; i < proxy_protocol_subnet_count; 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);
- proxy_protocol_subnets= 0;
- proxy_protocol_subnet_count= 0;
+#ifdef HAVE_PSI_INTERFACE
+ static PSI_rwlock_key psi_rwlock_key;
+ 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);
+}
diff --git a/sql/proxy_protocol.h b/sql/proxy_protocol.h
index 6f9bcf73307..0f873e2492c 100644
--- a/sql/proxy_protocol.h
+++ b/sql/proxy_protocol.h
@@ -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 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 void cleanup_proxy_protocol_networks();
+extern bool proxy_protocol_networks_valid(const char *spec);
+
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index ce2d41f009b..c144eb593a0 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -4416,7 +4416,22 @@ static Sys_var_charptr Sys_license(
READ_ONLY GLOBAL_VAR(license), NO_CMD_LINE, IN_SYSTEM_CHARSET,
DEFAULT(STRINGIFY_ARG(LICENSE)));
+#include <proxy_protocol.h>
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(
"proxy_protocol_networks", "Enable proxy protocol for these source "
"networks. The syntax is a comma separated list of IPv4 and IPv6 "
@@ -4424,8 +4439,10 @@ static Sys_var_charptr Sys_proxy_protocol_networks(
"a single host. \"*\" represents all networks and must the only "
"directive on the line. String \"localhost\" represents non-TCP "
"local connections (Unix domain socket, Windows named pipe or shared memory).",
- READ_ONLY GLOBAL_VAR(my_proxy_protocol_networks),
- CMD_LINE(REQUIRED_ARG), IN_FS_CHARSET, DEFAULT(""));
+ GLOBAL_VAR(my_proxy_protocol_networks), CMD_LINE(REQUIRED_ARG),
+ 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)
{
diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c
index f188287a001..0289e26ad05 100644
--- a/tests/mysql_client_test.c
+++ b/tests/mysql_client_test.c
@@ -20220,6 +20220,18 @@ static void test_proxy_header_ignore()
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);
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);
}