Added blocking of IP ranges.
This commit is contained in:
@ -35,7 +35,7 @@
|
||||
#include <plugin.h>
|
||||
#include <stdint.h>
|
||||
#include <skygw_types.h>
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
MODULE_INFO info = {
|
||||
MODULE_API_FILTER,
|
||||
@ -44,8 +44,6 @@ MODULE_INFO info = {
|
||||
"Firewall Filter"
|
||||
};
|
||||
|
||||
static int sess_num;
|
||||
|
||||
/**
|
||||
* Utility function to check if a string contains a valid IP address.
|
||||
* The string handled as a null-terminated string.
|
||||
@ -64,13 +62,14 @@ bool valid_ip(char* str)
|
||||
|
||||
case '.':
|
||||
case '/':
|
||||
case ' ':
|
||||
case '\0':
|
||||
/**End of IP, string or octet*/
|
||||
*(dest++) = '\0';
|
||||
octval = atoi(cmpbuff);
|
||||
dest = cmpbuff;
|
||||
valid = octval < 256 && octval > -1 ? true: false;
|
||||
if(*source == '/' || *source == '\0'){
|
||||
if(*source == '/' || *source == '\0' || *source == ' '){
|
||||
return valid;
|
||||
}else{
|
||||
source++;
|
||||
@ -86,6 +85,41 @@ bool valid_ip(char* str)
|
||||
|
||||
return valid;
|
||||
}
|
||||
/**
|
||||
* Replace all non-essential characters with whitespace from a null-terminated string.
|
||||
* This function modifies the passed string.
|
||||
* @param str String to purify
|
||||
*/
|
||||
char* strip_tags(char* str)
|
||||
{
|
||||
char *ptr = str, *lead = str, *tail = NULL;
|
||||
int len = 0;
|
||||
while(*ptr != '\0'){
|
||||
if(isalnum(*ptr) || *ptr == '.' || *ptr == '/'){
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
*ptr++ = ' ';
|
||||
}
|
||||
|
||||
/**Strip leading and trailing whitespace*/
|
||||
|
||||
while(*lead != '\0'){
|
||||
if(isspace(*lead)){
|
||||
lead++;
|
||||
}else{
|
||||
tail = strchr(str,'\0') - 1;
|
||||
while(tail > lead && isspace(*tail)){
|
||||
tail--;
|
||||
}
|
||||
len = (int)(tail - lead) + 1;
|
||||
memmove(str,lead,len);
|
||||
memset(str+len, 0, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get one octet of IP
|
||||
@ -95,7 +129,7 @@ int get_octet(char* str)
|
||||
int octval = 0,retval = -1;
|
||||
bool valid = false;
|
||||
char cmpbuff[32];
|
||||
char *source = str,*dest = cmpbuff,*end = strchr(str,'\0');
|
||||
char *source = str,*dest = cmpbuff,*end = strchr(str,'\0') + 1;
|
||||
|
||||
if(end == NULL){
|
||||
return retval;
|
||||
@ -107,6 +141,7 @@ int get_octet(char* str)
|
||||
/**End of IP or string or the octet is done*/
|
||||
case '.':
|
||||
case '/':
|
||||
case ' ':
|
||||
case '\0':
|
||||
|
||||
*(dest++) = '\0';
|
||||
@ -145,13 +180,13 @@ uint32_t strtoip(char* str)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ip = get_octet(tok);
|
||||
tok = strchr(tok,'.') + 1;
|
||||
ip |= (get_octet(tok))<< 8;
|
||||
ip |= get_octet(tok) << 24;
|
||||
tok = strchr(tok,'.') + 1;
|
||||
ip |= (get_octet(tok))<< 16;
|
||||
tok = strchr(tok,'.') + 1;
|
||||
ip |= (get_octet(tok))<< 24;
|
||||
ip |= (get_octet(tok))<< 8;
|
||||
tok = strchr(tok,'.') + 1;
|
||||
ip |= (get_octet(tok));
|
||||
|
||||
return ip;
|
||||
}
|
||||
@ -171,8 +206,8 @@ uint32_t strtosubmask(char* str)
|
||||
return mask;
|
||||
}
|
||||
|
||||
mask = strtoip(ptr+1);
|
||||
return mask;
|
||||
mask = strtoip(ptr);
|
||||
return ~mask;
|
||||
}
|
||||
|
||||
static char *version_str = "V1.0.0";
|
||||
@ -206,8 +241,20 @@ static FILTER_OBJECT MyObject = {
|
||||
/**
|
||||
* The Firewall filter instance.
|
||||
*/
|
||||
typedef struct iprange_t{
|
||||
struct iprange_t* next;
|
||||
uint32_t ip;
|
||||
uint32_t mask;
|
||||
}IPRANGE;
|
||||
|
||||
typedef struct {
|
||||
char** forbid_column;
|
||||
char** users;
|
||||
IPRANGE* networks;
|
||||
int forbid_column_count;
|
||||
int user_count;
|
||||
bool block_select_all;
|
||||
bool whitelist;
|
||||
|
||||
} FW_INSTANCE;
|
||||
|
||||
@ -218,7 +265,6 @@ typedef struct {
|
||||
DOWNSTREAM down;
|
||||
UPSTREAM up;
|
||||
SESSION* session;
|
||||
bool blocked;
|
||||
} FW_SESSION;
|
||||
|
||||
/**
|
||||
@ -255,6 +301,40 @@ GetModuleObject()
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
void parse_rule(char* rule, FW_INSTANCE* instance)
|
||||
{
|
||||
char* ptr = rule;
|
||||
bool allow,block;
|
||||
|
||||
/**IP range rules*/
|
||||
if((allow = (strstr(rule,"allow") != NULL)) ||
|
||||
(block = (strstr(rule,"block") != NULL))){
|
||||
if(allow){
|
||||
instance->whitelist = true;
|
||||
}else if(block){
|
||||
instance->whitelist = false;
|
||||
}
|
||||
|
||||
ptr = strchr(rule,' ');
|
||||
ptr++;
|
||||
if(valid_ip(ptr)){
|
||||
IPRANGE* rng = calloc(1,sizeof(IPRANGE));
|
||||
if(rng){
|
||||
rng->ip = strtoip(ptr);
|
||||
rng->mask = strtosubmask(ptr);
|
||||
rng->next = instance->networks;
|
||||
instance->networks = rng;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**Column rules*/
|
||||
|
||||
/**Sanity rules*/
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the filter for a particular service
|
||||
* within MaxScale.
|
||||
@ -271,7 +351,12 @@ createInstance(char **options, FILTER_PARAMETER **params)
|
||||
if ((my_instance = calloc(1, sizeof(FW_INSTANCE))) == NULL){
|
||||
return NULL;
|
||||
}
|
||||
sess_num = 0;
|
||||
int i;
|
||||
for(i = 0;params[i];i++){
|
||||
if(strcmp(params[i]->name,"rule") == 0){
|
||||
parse_rule(strip_tags(params[i]->value),my_instance);
|
||||
}
|
||||
}
|
||||
return (FILTER *)my_instance;
|
||||
}
|
||||
|
||||
@ -296,7 +381,6 @@ newSession(FILTER *instance, SESSION *session)
|
||||
return NULL;
|
||||
}
|
||||
my_session->session = session;
|
||||
my_session->blocked = false;
|
||||
return my_session;
|
||||
}
|
||||
|
||||
@ -349,63 +433,6 @@ static void setUpstream(FILTER *instance, void *session, UPSTREAM *upstream)
|
||||
my_session->up = *upstream;
|
||||
}
|
||||
|
||||
/**
|
||||
* The routeQuery entry point. This is passed the query buffer
|
||||
* to which the filter should be applied. Once processed the
|
||||
* query is passed to the downstream component
|
||||
* (filter or router) in the filter chain.
|
||||
*
|
||||
* @param instance The filter instance data
|
||||
* @param session The filter session
|
||||
* @param queue The query data
|
||||
*/
|
||||
static int
|
||||
routeQuery(FILTER *instance, void *session, GWBUF *queue)
|
||||
{
|
||||
FW_SESSION *my_session = (FW_SESSION *)session;
|
||||
FW_INSTANCE *my_instance = (FW_INSTANCE *)instance;
|
||||
bool accept = true;
|
||||
char *where,*query;
|
||||
int len;
|
||||
DCB* dcb = my_session->session->client;
|
||||
|
||||
if(modutil_is_SQL(queue)){
|
||||
|
||||
modutil_extract_SQL(queue, &query, &len);
|
||||
|
||||
where = skygw_get_where_clause(queue);
|
||||
if((where && strchr(where,'*') != NULL) ||
|
||||
(skygw_is_real_query(queue) && memchr(query,'*',len) != NULL)){
|
||||
accept = false;
|
||||
skygw_log_write(LOGFILE_TRACE, "where clause with '*': %s", where);
|
||||
}
|
||||
|
||||
free(where);
|
||||
|
||||
}
|
||||
|
||||
if(!accept){
|
||||
|
||||
/**
|
||||
* Convert the query to a fake COM_QUERY with no content
|
||||
* to block the query and trigger an error package from the backend.
|
||||
*/
|
||||
|
||||
my_session->blocked = true;
|
||||
|
||||
*((unsigned char*)queue->start) = 0x02;
|
||||
*((unsigned char*)queue->start + 1) = 0x00;
|
||||
*((unsigned char*)queue->start + 2) = 0x00;
|
||||
*((unsigned char*)queue->start + 3) = 0x00;
|
||||
*((unsigned char*)queue->start + 4) = 0x03;
|
||||
*((unsigned char*)queue->start + 5) = ';';
|
||||
|
||||
}
|
||||
|
||||
return my_session->down.routeQuery(my_session->down.instance,
|
||||
my_session->down.session, queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the packet contains an empty query error
|
||||
* and if the session blocked the last query
|
||||
@ -417,8 +444,7 @@ bool is_dummy(GWBUF* buf,FW_SESSION* session)
|
||||
{
|
||||
return(*((unsigned char*)buf->start + 4) == 0xff &&
|
||||
*((unsigned char*)buf->start + 5) == 0x29 &&
|
||||
*((unsigned char*)buf->start + 6) == 0x04 &&
|
||||
session->blocked);
|
||||
*((unsigned char*)buf->start + 6) == 0x04);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -446,6 +472,68 @@ GWBUF* gen_dummy_error()
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* The routeQuery entry point. This is passed the query buffer
|
||||
* to which the filter should be applied. Once processed the
|
||||
* query is passed to the downstream component
|
||||
* (filter or router) in the filter chain.
|
||||
*
|
||||
* @param instance The filter instance data
|
||||
* @param session The filter session
|
||||
* @param queue The query data
|
||||
*/
|
||||
static int
|
||||
routeQuery(FILTER *instance, void *session, GWBUF *queue)
|
||||
{
|
||||
FW_SESSION *my_session = (FW_SESSION *)session;
|
||||
FW_INSTANCE *my_instance = (FW_INSTANCE *)instance;
|
||||
IPRANGE* ipranges = my_instance->networks;
|
||||
bool accept = true;
|
||||
char *where,*query;
|
||||
uint32_t ip;
|
||||
int len;
|
||||
DCB* dcb = my_session->session->client;
|
||||
ip = strtoip(dcb->remote);
|
||||
|
||||
while(ipranges){
|
||||
if(ip >= ipranges->ip && ip <= ipranges->ip + ipranges->mask){
|
||||
accept = my_instance->whitelist;
|
||||
break;
|
||||
}
|
||||
ipranges = ipranges->next;
|
||||
}
|
||||
if(accept){
|
||||
|
||||
if(modutil_is_SQL(queue)){
|
||||
|
||||
modutil_extract_SQL(queue, &query, &len);
|
||||
|
||||
where = skygw_get_where_clause(queue);
|
||||
if((where && strchr(where,'*') != NULL) ||
|
||||
(skygw_is_real_query(queue) && memchr(query,'*',len) != NULL)){
|
||||
accept = false;
|
||||
skygw_log_write(LOGFILE_TRACE, "where clause with '*': %s", where);
|
||||
}
|
||||
|
||||
free(where);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(!accept){
|
||||
|
||||
gwbuf_free(queue);
|
||||
GWBUF* forward = gen_dummy_error();
|
||||
dcb->func.write(dcb,forward);
|
||||
//gwbuf_free(forward);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
return my_session->down.routeQuery(my_session->down.instance,
|
||||
my_session->down.session, queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* The clientReply entry point. This is passed the response buffer
|
||||
* to which the filter should be applied. Once processed the
|
||||
@ -462,12 +550,6 @@ static int clientReply(FILTER* instance, void *session, GWBUF *reply)
|
||||
FW_INSTANCE *my_instance = (FW_INSTANCE *)instance;
|
||||
GWBUF* forward = reply;
|
||||
|
||||
if(is_dummy(reply,my_session)){
|
||||
gwbuf_free(reply);
|
||||
forward = gen_dummy_error();
|
||||
my_session->blocked = false;
|
||||
}
|
||||
|
||||
return my_session->up.clientReply(my_session->up.instance,
|
||||
my_session->up.session, forward);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user