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