MXS-1315 Add support for multiple source addresses
Source parameter in NamedServerFilter can now contain a list of source addresses seperated by comma. This parameter is parsed into a list of ip addresses and checked against incoming client connections.
This commit is contained in:
@ -45,14 +45,12 @@
|
||||
#include <maxscale/server.h>
|
||||
#include <maxscale/utils.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
static void generate_param_names(int pairs);
|
||||
|
||||
/* These arrays contain the allowed indexed config parameter names. match01,
|
||||
* target01, match02, target02 ... */
|
||||
static StringArray param_names_match_indexed;
|
||||
static StringArray param_names_target_indexed;
|
||||
static StringVector param_names_match_indexed;
|
||||
static StringVector param_names_target_indexed;
|
||||
|
||||
static const MXS_ENUM_VALUE option_values[] =
|
||||
{
|
||||
@ -66,10 +64,10 @@ static const char MATCH_STR[] = "match";
|
||||
static const char SERVER_STR[] = "server";
|
||||
static const char TARGET_STR[] = "target";
|
||||
|
||||
RegexHintFilter::RegexHintFilter(string user, SourceHost* source,
|
||||
const MappingArray& mapping, int ovector_size)
|
||||
RegexHintFilter::RegexHintFilter(const std::string& user, const SourceHostVector& source,
|
||||
const MappingVector& mapping, int ovector_size)
|
||||
: m_user(user),
|
||||
m_source(source),
|
||||
m_sources(source),
|
||||
m_mapping(mapping),
|
||||
m_ovector_size(ovector_size),
|
||||
m_total_diverted(0),
|
||||
@ -78,7 +76,6 @@ RegexHintFilter::RegexHintFilter(string user, SourceHost* source,
|
||||
|
||||
RegexHintFilter::~RegexHintFilter()
|
||||
{
|
||||
delete m_source;
|
||||
for (unsigned int i = 0; i < m_mapping.size(); i++)
|
||||
{
|
||||
pcre2_code_free(m_mapping.at(i).m_regex);
|
||||
@ -158,8 +155,7 @@ RegexHintFSession* RegexHintFilter::newSession(MXS_SESSION* session)
|
||||
bool session_active = true;
|
||||
|
||||
/* Check client IP against 'source' host option */
|
||||
if (m_source && m_source->m_address.length() &&
|
||||
(remote = session_get_remote(session)) != NULL)
|
||||
if (m_sources.size() > 0 && (remote = session_get_remote(session)) != NULL)
|
||||
{
|
||||
session_active =
|
||||
check_source_host(remote, &(session->client_dcb->ip));
|
||||
@ -235,13 +231,12 @@ RegexHintFilter*
|
||||
RegexHintFilter::create(const char* name, MXS_CONFIG_PARAMETER* params)
|
||||
{
|
||||
bool error = false;
|
||||
SourceHost* source_host = NULL;
|
||||
SourceHostVector source_host;
|
||||
|
||||
const char* source = config_get_string(params, "source");
|
||||
if (*source)
|
||||
{
|
||||
source_host = set_source_address(source);
|
||||
if (!source_host)
|
||||
if (!set_source_addresses(source, source_host))
|
||||
{
|
||||
MXS_ERROR("Failure setting 'source' from %s", source);
|
||||
error = true;
|
||||
@ -250,8 +245,8 @@ RegexHintFilter::create(const char* name, MXS_CONFIG_PARAMETER* params)
|
||||
|
||||
int pcre_ops = config_get_enum(params, "options", option_values);
|
||||
|
||||
string match_val_legacy(config_get_string(params, MATCH_STR));
|
||||
string server_val_legacy(config_get_string(params, SERVER_STR));
|
||||
std::string match_val_legacy(config_get_string(params, MATCH_STR));
|
||||
std::string server_val_legacy(config_get_string(params, SERVER_STR));
|
||||
const bool legacy_mode = (match_val_legacy.length() || server_val_legacy.length());
|
||||
|
||||
if (legacy_mode && (!match_val_legacy.length() || !server_val_legacy.length()))
|
||||
@ -262,7 +257,7 @@ RegexHintFilter::create(const char* name, MXS_CONFIG_PARAMETER* params)
|
||||
error = true;
|
||||
}
|
||||
|
||||
MappingArray mapping;
|
||||
MappingVector mapping;
|
||||
uint32_t max_capcount;
|
||||
/* Try to form the mapping with indexed parameter names */
|
||||
form_regex_server_mapping(params, pcre_ops, &mapping, &max_capcount);
|
||||
@ -292,13 +287,12 @@ RegexHintFilter::create(const char* name, MXS_CONFIG_PARAMETER* params)
|
||||
|
||||
if (error)
|
||||
{
|
||||
delete source_host;
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
RegexHintFilter* instance = NULL;
|
||||
string user(config_get_string(params, "user"));
|
||||
std::string user(config_get_string(params, "user"));
|
||||
MXS_EXCEPTION_GUARD(instance =
|
||||
new RegexHintFilter(user, source_host, mapping, max_capcount + 1));
|
||||
return instance;
|
||||
@ -369,12 +363,13 @@ void RegexHintFilter::diagnostics(DCB* dcb)
|
||||
dcb_printf(dcb, "\t\tTotal no. of queries not diverted by filter (approx.): %d\n",
|
||||
m_total_undiverted);
|
||||
|
||||
if (m_source)
|
||||
for (unsigned int i = 0; i < m_sources.size(); i++)
|
||||
{
|
||||
dcb_printf(dcb,
|
||||
"\t\tReplacement limited to connections from %s\n",
|
||||
m_source->m_address.c_str());
|
||||
m_sources[i].m_address.c_str());
|
||||
}
|
||||
|
||||
if (m_user.length())
|
||||
{
|
||||
dcb_printf(dcb,
|
||||
@ -401,26 +396,32 @@ json_t* RegexHintFilter::diagnostics_json() const
|
||||
{
|
||||
json_t* arr = json_array();
|
||||
|
||||
for (MappingArray::const_iterator it = m_mapping.begin(); it != m_mapping.end(); it++)
|
||||
for (const auto& map : m_mapping)
|
||||
{
|
||||
json_t* obj = json_object();
|
||||
json_t* targets = json_array();
|
||||
|
||||
for (StringArray::const_iterator it2 = it->m_targets.begin(); it2 != it->m_targets.end(); it2++)
|
||||
for (const auto& target : map.m_targets)
|
||||
{
|
||||
json_array_append_new(targets, json_string(it2->c_str()));
|
||||
json_array_append_new(targets, json_string(target.c_str()));
|
||||
}
|
||||
|
||||
json_object_set_new(obj, "match", json_string(it->m_match.c_str()));
|
||||
json_object_set_new(obj, "match", json_string(map.m_match.c_str()));
|
||||
json_object_set_new(obj, "targets", targets);
|
||||
}
|
||||
|
||||
json_object_set_new(rval, "mappings", arr);
|
||||
}
|
||||
|
||||
if (m_source)
|
||||
if (!m_sources.empty())
|
||||
{
|
||||
json_object_set_new(rval, "source", json_string(m_source->m_address.c_str()));
|
||||
json_t* arr = json_array();
|
||||
|
||||
for (const auto& source : m_sources)
|
||||
{
|
||||
json_array_append_new(arr, json_string(source.m_address.c_str()));
|
||||
}
|
||||
json_object_set_new(rval, "sources", arr);
|
||||
}
|
||||
|
||||
if (m_user.length())
|
||||
@ -438,7 +439,7 @@ json_t* RegexHintFilter::diagnostics_json() const
|
||||
* @param server_names The list of servers as read from the config file
|
||||
* @return How many were found
|
||||
*/
|
||||
int RegexToServers::add_servers(string server_names, bool legacy_mode)
|
||||
int RegexToServers::add_servers(const std::string& server_names, bool legacy_mode)
|
||||
{
|
||||
if (legacy_mode)
|
||||
{
|
||||
@ -523,8 +524,8 @@ int RegexToServers::add_servers(string server_names, bool legacy_mode)
|
||||
}
|
||||
|
||||
bool RegexHintFilter::regex_compile_and_add(int pcre_ops, bool legacy_mode,
|
||||
const string& match, const string& servers,
|
||||
MappingArray* mapping, uint32_t* max_capcount)
|
||||
const std::string& match, const std::string& servers,
|
||||
MappingVector* mapping, uint32_t* max_capcount)
|
||||
{
|
||||
bool success = true;
|
||||
int errorcode = -1;
|
||||
@ -588,7 +589,7 @@ bool RegexHintFilter::regex_compile_and_add(int pcre_ops, bool legacy_mode,
|
||||
* @param max_capcount_out The maximum detected pcre2 capture count is written here.
|
||||
*/
|
||||
void RegexHintFilter::form_regex_server_mapping(MXS_CONFIG_PARAMETER* params, int pcre_ops,
|
||||
MappingArray* mapping, uint32_t* max_capcount_out)
|
||||
MappingVector* mapping, uint32_t* max_capcount_out)
|
||||
{
|
||||
mxb_assert(param_names_match_indexed.size() == param_names_target_indexed.size());
|
||||
bool error = false;
|
||||
@ -601,8 +602,8 @@ void RegexHintFilter::form_regex_server_mapping(MXS_CONFIG_PARAMETER* params, in
|
||||
{
|
||||
const char* param_name_match = param_names_match_indexed[i].c_str();
|
||||
const char* param_name_target = param_names_target_indexed[i].c_str();
|
||||
string match(config_get_string(params, param_name_match));
|
||||
string target(config_get_string(params, param_name_target));
|
||||
std::string match(config_get_string(params, param_name_match));
|
||||
std::string target(config_get_string(params, param_name_target));
|
||||
|
||||
/* Check that both the regex and server config parameters are found */
|
||||
if (match.length() < 1 || target.length() < 1)
|
||||
@ -651,41 +652,46 @@ void RegexHintFilter::form_regex_server_mapping(MXS_CONFIG_PARAMETER* params, in
|
||||
int RegexHintFilter::check_source_host(const char* remote, const struct sockaddr_storage *ip)
|
||||
{
|
||||
int ret = 0;
|
||||
struct sockaddr_in check_ipv4;
|
||||
|
||||
memcpy(&check_ipv4, ip, sizeof(check_ipv4));
|
||||
|
||||
switch (m_source->m_netmask)
|
||||
for (const auto& source : m_sources)
|
||||
{
|
||||
case 32:
|
||||
ret = (m_source->m_address == remote) ? 1 : 0;
|
||||
break;
|
||||
case 24:
|
||||
/* Class C check */
|
||||
check_ipv4.sin_addr.s_addr &= 0x00FFFFFF;
|
||||
break;
|
||||
case 16:
|
||||
/* Class B check */
|
||||
check_ipv4.sin_addr.s_addr &= 0x0000FFFF;
|
||||
break;
|
||||
case 8:
|
||||
/* Class A check */
|
||||
check_ipv4.sin_addr.s_addr &= 0x000000FF;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
struct sockaddr_in check_ipv4;
|
||||
|
||||
ret = (m_source->m_netmask < 32) ?
|
||||
(check_ipv4.sin_addr.s_addr == m_source->m_ipv4.sin_addr.s_addr) :
|
||||
ret;
|
||||
memcpy(&check_ipv4, ip, sizeof(check_ipv4));
|
||||
|
||||
if (ret)
|
||||
{
|
||||
MXS_INFO("Client IP %s matches host source %s%s",
|
||||
remote,
|
||||
m_source->m_netmask < 32 ? "with wildcards " : "",
|
||||
m_source->m_address.c_str());
|
||||
switch (source.m_netmask)
|
||||
{
|
||||
case 32:
|
||||
ret = (source.m_address == remote) ? 1 : 0;
|
||||
break;
|
||||
case 24:
|
||||
/* Class C check */
|
||||
check_ipv4.sin_addr.s_addr &= 0x00FFFFFF;
|
||||
break;
|
||||
case 16:
|
||||
/* Class B check */
|
||||
check_ipv4.sin_addr.s_addr &= 0x0000FFFF;
|
||||
break;
|
||||
case 8:
|
||||
/* Class A check */
|
||||
check_ipv4.sin_addr.s_addr &= 0x000000FF;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (source.m_netmask < 32)
|
||||
{
|
||||
ret = (check_ipv4.sin_addr.s_addr == source.m_ipv4.sin_addr.s_addr);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
{
|
||||
MXS_INFO("Client IP %s matches host source %s%s",
|
||||
remote,
|
||||
source.m_netmask < 32 ? "with wildcards " : "",
|
||||
source.m_address.c_str());
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -749,7 +755,7 @@ bool RegexHintFilter::validate_ip_address(const char* host)
|
||||
* @param input_host The config source parameter
|
||||
* @return The filled struct with netmask, or null on error
|
||||
*/
|
||||
SourceHost* RegexHintFilter::set_source_address(const char* input_host)
|
||||
bool RegexHintFilter::add_source_address(const char* input_host, SourceHostVector& source_hosts)
|
||||
{
|
||||
mxb_assert(input_host);
|
||||
|
||||
@ -765,14 +771,15 @@ SourceHost* RegexHintFilter::set_source_address(const char* input_host)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
string address(input_host);
|
||||
std::string address(input_host);
|
||||
struct sockaddr_in ipv4 = {};
|
||||
int netmask = 32;
|
||||
|
||||
/* If no wildcards, leave netmask to 32 and return */
|
||||
if (!strchr(input_host, '%'))
|
||||
{
|
||||
return new SourceHost(address, ipv4, netmask);
|
||||
source_hosts.emplace_back(address, ipv4, netmask);
|
||||
return true;
|
||||
}
|
||||
|
||||
char format_host[strlen(input_host) + 1];
|
||||
@ -827,10 +834,28 @@ SourceHost* RegexHintFilter::set_source_address(const char* input_host)
|
||||
{
|
||||
MXS_WARNING("Found invalid IP address for parameter 'source=%s': %s",
|
||||
input_host, gai_strerror(rc));
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
source_hosts.emplace_back(address, ipv4, netmask);
|
||||
return true;
|
||||
}
|
||||
|
||||
return new SourceHost(address, ipv4, netmask);
|
||||
bool RegexHintFilter::set_source_addresses(const std::string& input_host_names, SourceHostVector& source_hosts)
|
||||
{
|
||||
|
||||
bool rval = true;
|
||||
std::string host_names(input_host_names);
|
||||
|
||||
for (auto host : mxs::strtok(host_names, ","))
|
||||
{
|
||||
char* trimmed_host = trim((char*)host.c_str());
|
||||
|
||||
if (!add_source_address(trimmed_host, source_hosts))
|
||||
{
|
||||
rval = false;
|
||||
}
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,9 +29,9 @@ class RegexHintFSession;
|
||||
struct RegexToServers;
|
||||
struct SourceHost;
|
||||
|
||||
using std::string;
|
||||
typedef std::vector<string> StringArray;
|
||||
typedef std::vector<RegexToServers> MappingArray;
|
||||
using StringVector = std::vector<std::string>;
|
||||
using MappingVector = std::vector<RegexToServers>;
|
||||
using SourceHostVector = std::vector<SourceHost>;
|
||||
|
||||
/**
|
||||
* Filter instance definition
|
||||
@ -39,9 +39,9 @@ typedef std::vector<RegexToServers> MappingArray;
|
||||
class RegexHintFilter : public maxscale::Filter<RegexHintFilter, RegexHintFSession>
|
||||
{
|
||||
private:
|
||||
const string m_user; /* User name to restrict matches with */
|
||||
SourceHost* m_source; /* Source address to restrict matches */
|
||||
MappingArray m_mapping; /* Regular expression to serverlist mapping */
|
||||
const std::string m_user; /* User name to restrict matches with */
|
||||
SourceHostVector m_sources; /* Source addresses to restrict matches */
|
||||
MappingVector m_mapping; /* Regular expression to serverlist mapping */
|
||||
const int m_ovector_size; /* Given to pcre2_match_data_create() */
|
||||
|
||||
int check_source_host(const char *remote, const struct sockaddr_storage *ip);
|
||||
@ -51,7 +51,7 @@ public:
|
||||
volatile unsigned int m_total_diverted;
|
||||
volatile unsigned int m_total_undiverted;
|
||||
|
||||
RegexHintFilter(string user, SourceHost* source, const MappingArray& map,
|
||||
RegexHintFilter(const std::string& user, const SourceHostVector& source, const MappingVector& map,
|
||||
int ovector_size);
|
||||
~RegexHintFilter();
|
||||
static RegexHintFilter* create(const char* zName, MXS_CONFIG_PARAMETER* ppParams);
|
||||
@ -62,11 +62,12 @@ public:
|
||||
const RegexToServers* find_servers(char* sql, int sql_len, pcre2_match_data* mdata);
|
||||
|
||||
static void form_regex_server_mapping(MXS_CONFIG_PARAMETER* params, int pcre_ops,
|
||||
MappingArray* mapping, uint32_t* max_capcount_out);
|
||||
static bool regex_compile_and_add(int pcre_ops, bool legacy_mode, const string& match,
|
||||
const string& servers, MappingArray* mapping, uint32_t* max_capcount);
|
||||
MappingVector* mapping, uint32_t* max_capcount_out);
|
||||
static bool regex_compile_and_add(int pcre_ops, bool legacy_mode, const std::string& match,
|
||||
const std::string& servers, MappingVector* mapping, uint32_t* max_capcount);
|
||||
static bool validate_ip_address(const char *);
|
||||
static SourceHost* set_source_address(const char *);
|
||||
static bool add_source_address(const char *, SourceHostVector&);
|
||||
static bool set_source_addresses(const std::string& input_host_names, SourceHostVector&);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -95,29 +96,29 @@ public:
|
||||
* does not manage the regex memory. That is done by the filter instance. */
|
||||
struct RegexToServers
|
||||
{
|
||||
string m_match; /* Regex in text form */
|
||||
std::string m_match; /* Regex in text form */
|
||||
pcre2_code* m_regex; /* Compiled regex */
|
||||
StringArray m_targets; /* List of target servers. */
|
||||
StringVector m_targets; /* List of target servers. */
|
||||
HINT_TYPE m_htype; /* For special hint types */
|
||||
volatile bool m_error_printed; /* Has an error message about
|
||||
* matching this regex been printed yet? */
|
||||
RegexToServers(string match, pcre2_code* regex)
|
||||
RegexToServers(const std::string& match, pcre2_code* regex)
|
||||
: m_match(match),
|
||||
m_regex(regex),
|
||||
m_htype(HINT_ROUTE_TO_NAMED_SERVER),
|
||||
m_error_printed(false)
|
||||
{}
|
||||
|
||||
int add_servers(string server_names, bool legacy_mode);
|
||||
int add_servers(const std::string& server_names, bool legacy_mode);
|
||||
};
|
||||
|
||||
/* Container for address-specific filtering */
|
||||
struct SourceHost
|
||||
{
|
||||
string m_address;
|
||||
std::string m_address;
|
||||
struct sockaddr_in m_ipv4;
|
||||
int m_netmask;
|
||||
SourceHost(string address, const struct sockaddr_in& ipv4, int netmask)
|
||||
SourceHost(std::string address, const struct sockaddr_in& ipv4, int netmask)
|
||||
: m_address(address),
|
||||
m_ipv4(ipv4),
|
||||
m_netmask(netmask)
|
||||
|
Reference in New Issue
Block a user