MXS-1065: add wildcards support to source option in namedfilter
It’s possible to add % wildcards in the ‘source’ option of namedserverfilter. Allowed values, for an IP address only are: X.%.%.% X.Y.%.% X.Y.Z.% The match any ‘%’ is not allowed
This commit is contained in:
parent
6dc6d6280a
commit
57b530a2e8
@ -23,6 +23,7 @@
|
||||
#include <regex.h>
|
||||
#include <maxscale/hint.h>
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/utils.h>
|
||||
|
||||
/**
|
||||
* @file namedserverfilter.c - a very simple regular expression based filter
|
||||
@ -51,18 +52,32 @@ static int routeQuery(MXS_FILTER *instance, MXS_FILTER_SESSION *fsession, GWBUF
|
||||
static void diagnostic(MXS_FILTER *instance, MXS_FILTER_SESSION *fsession, DCB *dcb);
|
||||
static uint64_t getCapabilities(void);
|
||||
|
||||
typedef struct source_host
|
||||
{
|
||||
const char *address;
|
||||
struct sockaddr_in ipv4;
|
||||
int netmask;
|
||||
} REGEXHINT_SOURCE_HOST;
|
||||
|
||||
/**
|
||||
* Instance structure
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char *source; /* Source address to restrict matches */
|
||||
REGEXHINT_SOURCE_HOST *source; /* Source address to restrict matches */
|
||||
char *user; /* User name to restrict matches */
|
||||
char *match; /* Regular expression to match */
|
||||
char *server; /* Server to route to */
|
||||
regex_t re; /* Compiled regex text */
|
||||
} REGEXHINT_INSTANCE;
|
||||
|
||||
static bool validate_ip_address(const char *);
|
||||
static int check_source_host(REGEXHINT_INSTANCE *,
|
||||
const char *,
|
||||
const struct sockaddr_in *);
|
||||
static REGEXHINT_SOURCE_HOST *set_source_address(const char *);
|
||||
static void free_instance(REGEXHINT_INSTANCE *);
|
||||
|
||||
/**
|
||||
* The session structuee for this regex filter
|
||||
*/
|
||||
@ -151,16 +166,30 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
static MXS_FILTER *
|
||||
createInstance(const char *name, char **options, CONFIG_PARAMETER *params)
|
||||
{
|
||||
REGEXHINT_INSTANCE *my_instance = (REGEXHINT_INSTANCE*)MXS_MALLOC(sizeof(REGEXHINT_INSTANCE));
|
||||
REGEXHINT_INSTANCE *my_instance = (REGEXHINT_INSTANCE*)MXS_CALLOC(1, sizeof(REGEXHINT_INSTANCE));
|
||||
|
||||
if (my_instance)
|
||||
{
|
||||
REGEXHINT_SOURCE_HOST *source = NULL;
|
||||
const char *cfg_param = config_get_string(params, "source");
|
||||
if (*cfg_param)
|
||||
{
|
||||
my_instance->source = set_source_address(cfg_param);
|
||||
if (!my_instance->source)
|
||||
{
|
||||
MXS_ERROR("Failure setting 'source' from %s",
|
||||
cfg_param);
|
||||
|
||||
free_instance(my_instance);
|
||||
my_instance = NULL;
|
||||
return (MXS_FILTER *)my_instance;
|
||||
}
|
||||
}
|
||||
|
||||
my_instance->match = MXS_STRDUP_A(config_get_string(params, "match"));
|
||||
my_instance->server = MXS_STRDUP_A(config_get_string(params, "server"));
|
||||
my_instance->source = config_copy_string(params, "source");
|
||||
my_instance->user = config_copy_string(params, "user");
|
||||
bool error = false;
|
||||
|
||||
int cflags = config_get_enum(params, "options", option_values);
|
||||
|
||||
if (regcomp(&my_instance->re, my_instance->match, cflags))
|
||||
@ -173,15 +202,7 @@ createInstance(const char *name, char **options, CONFIG_PARAMETER *params)
|
||||
|
||||
if (error)
|
||||
{
|
||||
if (my_instance->match)
|
||||
{
|
||||
regfree(&my_instance->re);
|
||||
MXS_FREE(my_instance->match);
|
||||
}
|
||||
MXS_FREE(my_instance->server);
|
||||
MXS_FREE(my_instance->source);
|
||||
MXS_FREE(my_instance->user);
|
||||
MXS_FREE(my_instance);
|
||||
free_instance(my_instance);
|
||||
my_instance = NULL;
|
||||
}
|
||||
}
|
||||
@ -208,15 +229,18 @@ newSession(MXS_FILTER *instance, MXS_SESSION *session)
|
||||
my_session->n_diverted = 0;
|
||||
my_session->n_undiverted = 0;
|
||||
my_session->active = 1;
|
||||
if (my_instance->source
|
||||
&& (remote = session_get_remote(session)) != NULL)
|
||||
|
||||
/* Check client IP against 'source' host option */
|
||||
if (my_instance->source &&
|
||||
my_instance->source->address &&
|
||||
(remote = session_get_remote(session)) != NULL)
|
||||
{
|
||||
if (strcmp(remote, my_instance->source))
|
||||
{
|
||||
my_session->active = 0;
|
||||
}
|
||||
my_session->active = check_source_host(my_instance,
|
||||
remote,
|
||||
&session->client_dcb->ipv4);
|
||||
}
|
||||
|
||||
/* Check client user against 'user' option */
|
||||
if (my_instance->user && (user = session_get_user(session))
|
||||
&& strcmp(user, my_instance->user))
|
||||
{
|
||||
@ -339,7 +363,7 @@ diagnostic(MXS_FILTER *instance, MXS_FILTER_SESSION *fsession, DCB *dcb)
|
||||
{
|
||||
dcb_printf(dcb,
|
||||
"\t\tReplacement limited to connections from %s\n",
|
||||
my_instance->source);
|
||||
my_instance->source->address);
|
||||
}
|
||||
if (my_instance->user)
|
||||
{
|
||||
@ -358,3 +382,228 @@ static uint64_t getCapabilities(void)
|
||||
{
|
||||
return RCAP_TYPE_CONTIGUOUS_INPUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate IP address string againt three dots
|
||||
* and last char not being a dot.
|
||||
*
|
||||
* Match any, '%' or '%.%.%.%', is not allowed
|
||||
*
|
||||
*/
|
||||
static bool validate_ip_address(const char *host)
|
||||
{
|
||||
int n_dots = 0;
|
||||
|
||||
/**
|
||||
* Match any is not allowed
|
||||
* Start with dot not allowed
|
||||
* Host len can't be greater than INET_ADDRSTRLEN
|
||||
*/
|
||||
if (*host == '%' ||
|
||||
*host == '.' ||
|
||||
strlen(host) > INET_ADDRSTRLEN)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check each byte */
|
||||
while (*host != '\0')
|
||||
{
|
||||
if (!isdigit(*host) && *host != '.' && *host != '%')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Dot found */
|
||||
if (*host == '.')
|
||||
{
|
||||
n_dots++;
|
||||
}
|
||||
|
||||
host++;
|
||||
}
|
||||
|
||||
/* Check IPv4 max number of dots and last char */
|
||||
if (n_dots == 3 && (*(host - 1) != '.'))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the client IP
|
||||
* matches the configured 'source' host
|
||||
* which can have up to three % wildcards
|
||||
*
|
||||
* @param instance The filter instance
|
||||
* @param remote The clientIP
|
||||
* @param ipv4 The client IPv4 struct
|
||||
* @return 1 for match, 0 otherwise
|
||||
*/
|
||||
static int check_source_host(REGEXHINT_INSTANCE *instance,
|
||||
const char *remote,
|
||||
const struct sockaddr_in *ipv4)
|
||||
{
|
||||
int ret = 0;
|
||||
struct sockaddr_in check_ipv4;
|
||||
|
||||
memcpy(&check_ipv4, ipv4, sizeof(check_ipv4));
|
||||
|
||||
switch (instance->source->netmask)
|
||||
{
|
||||
case 32:
|
||||
ret = strcmp(instance->source->address, remote) == 0 ? 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;
|
||||
}
|
||||
|
||||
ret = (instance->source->netmask < 32) ?
|
||||
(check_ipv4.sin_addr.s_addr == instance->source->ipv4.sin_addr.s_addr) :
|
||||
ret;
|
||||
|
||||
if (ret)
|
||||
{
|
||||
MXS_INFO("Client IP %s matches host source %s%s",
|
||||
remote,
|
||||
instance->source->netmask < 32 ? "with wildcards " : "",
|
||||
instance->source->address);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the 'source' option into a proper struct
|
||||
*
|
||||
* Input IP, which could have wildcards %, is checked
|
||||
* and the netmask 32/24/16/8 is added.
|
||||
*
|
||||
* In case of errors the 'address' field of
|
||||
* struct REGEXHINT_SOURCE_HOST is set to NULL
|
||||
*
|
||||
* @param input_host The config source parameter
|
||||
* @return The filled struct with netmask
|
||||
*
|
||||
*/
|
||||
static REGEXHINT_SOURCE_HOST *set_source_address(const char *input_host)
|
||||
{
|
||||
int netmask = 32;
|
||||
int bytes = 0;
|
||||
int found_wildcard = 0;
|
||||
struct sockaddr_in serv_addr;
|
||||
REGEXHINT_SOURCE_HOST *source_host = MXS_CALLOC(1, sizeof(REGEXHINT_SOURCE_HOST));
|
||||
|
||||
if (!input_host || !source_host)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!validate_ip_address(input_host))
|
||||
{
|
||||
MXS_WARNING("The given 'source' parameter source=%s"
|
||||
" is not a valid IP address: it will not be used.",
|
||||
input_host);
|
||||
|
||||
source_host->address = NULL;
|
||||
return source_host;
|
||||
}
|
||||
|
||||
source_host->address = input_host;
|
||||
|
||||
/* If no wildcards don't check it, set netmask to 32 and return */
|
||||
if (!strchr(input_host, '%'))
|
||||
{
|
||||
source_host->netmask = netmask;
|
||||
return source_host;
|
||||
}
|
||||
|
||||
char format_host[strlen(input_host) + 1];
|
||||
char *p = (char *)input_host;
|
||||
char *out = format_host;
|
||||
|
||||
while (*p && bytes <= 3)
|
||||
{
|
||||
if (*p == '.')
|
||||
{
|
||||
bytes++;
|
||||
}
|
||||
|
||||
if (*p == '%')
|
||||
{
|
||||
found_wildcard = 1;
|
||||
*out = bytes == 3 ? '1' : '0';
|
||||
netmask -= 8;
|
||||
|
||||
out++;
|
||||
p++;
|
||||
}
|
||||
else
|
||||
{
|
||||
*out++ = *p++;
|
||||
}
|
||||
}
|
||||
|
||||
*out ='\0';
|
||||
source_host->netmask = netmask;
|
||||
|
||||
/* fill IPv4 data struct */
|
||||
if (setipaddress(&source_host->ipv4.sin_addr, format_host) && strlen(format_host))
|
||||
{
|
||||
|
||||
/* if netmask < 32 there are % wildcards */
|
||||
if (source_host->netmask < 32)
|
||||
{
|
||||
/* let's zero the last IP byte: a.b.c.0 we may have set above to 1*/
|
||||
source_host->ipv4.sin_addr.s_addr &= 0x00FFFFFF;
|
||||
}
|
||||
|
||||
MXS_INFO("Input %s is valid with netmask %d\n",
|
||||
source_host->address,
|
||||
source_host->netmask);
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_WARNING("Found invalid IP address for parameter 'source=%s',"
|
||||
" it will not be used.",
|
||||
input_host);
|
||||
source_host->address = NULL;
|
||||
}
|
||||
|
||||
return (REGEXHINT_SOURCE_HOST *)source_host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free allocated memory
|
||||
*
|
||||
* @param instance The filter instance
|
||||
*/
|
||||
static void free_instance(REGEXHINT_INSTANCE *instance)
|
||||
{
|
||||
if (instance->match)
|
||||
{
|
||||
regfree(&instance->re);
|
||||
MXS_FREE(instance->match);
|
||||
}
|
||||
|
||||
MXS_FREE(instance->server);
|
||||
MXS_FREE(instance->source);
|
||||
MXS_FREE(instance->user);
|
||||
MXS_FREE(instance);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user