diff options
author | Vladislav Vaintroub <wlad@mariadb.com> | 2018-03-21 18:09:52 +0000 |
---|---|---|
committer | Vladislav Vaintroub <wlad@mariadb.com> | 2018-03-23 20:23:12 +0000 |
commit | 96ecf3ff23011ee7e8b92da5d9561d9cd7e75d53 (patch) | |
tree | f584ddc42bd0b8540b4813c3609522af8117c55e | |
parent | 865cec928a0f33ea1e6f71d0a3d8ac0d0570252a (diff) | |
download | mariadb-git-96ecf3ff23011ee7e8b92da5d9561d9cd7e75d53.tar.gz |
MDEV-15501 : Make `proxy_protocol_networks` variable read-write.
-rw-r--r-- | sql/mysqld.cc | 4 | ||||
-rw-r--r-- | sql/proxy_protocol.cc | 137 | ||||
-rw-r--r-- | sql/proxy_protocol.h | 6 | ||||
-rw-r--r-- | sql/sys_vars.cc | 21 | ||||
-rw-r--r-- | tests/mysql_client_test.c | 12 |
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); } |