Merge branch 'develop' into plainrouter
This commit is contained in:
@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file fwfilter.c
|
||||
* @file dbfwfilter.c
|
||||
* @author Markus Mäkelä
|
||||
* @date 13.2.2015
|
||||
* @version 1.0.0
|
||||
@ -61,11 +61,9 @@
|
||||
* users NAME ... match [any|all|strict_all] rules RULE ...
|
||||
*@endcode
|
||||
*/
|
||||
|
||||
#include <my_config.h>
|
||||
#include <stdint.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <filter.h>
|
||||
#include <string.h>
|
||||
#include <atomic.h>
|
||||
@ -74,13 +72,11 @@
|
||||
#include <query_classifier.h>
|
||||
#include <mysql_client_server_protocol.h>
|
||||
#include <spinlock.h>
|
||||
#include <session.h>
|
||||
#include <plugin.h>
|
||||
#include <skygw_types.h>
|
||||
#include <skygw_debug.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <regex.h>
|
||||
|
||||
MODULE_INFO info = {
|
||||
MODULE_API_FILTER,
|
||||
MODULE_ALPHA_RELEASE,
|
||||
@ -197,7 +193,7 @@ typedef struct rulelist_t{
|
||||
|
||||
typedef struct user_t{
|
||||
char* name;/*< Name of the user */
|
||||
SPINLOCK* lock;/*< User spinlock */
|
||||
SPINLOCK lock;/*< User spinlock */
|
||||
QUERYSPEED* qs_limit;/*< The query speed structure unique to this user */
|
||||
RULELIST* rules_or;/*< If any of these rules match the action is triggered */
|
||||
RULELIST* rules_and;/*< All of these rules must match for the action to trigger */
|
||||
@ -298,24 +294,27 @@ void* rlistdup(void* fval)
|
||||
|
||||
static void* hrulefree(void* fval)
|
||||
{
|
||||
USER* user = (USER*)fval;
|
||||
RULELIST *ptr = user->rules_or,*tmp;
|
||||
RULELIST *ptr = (RULELIST*)fval;
|
||||
while(ptr){
|
||||
tmp = ptr;
|
||||
RULELIST *tmp = ptr;
|
||||
ptr = ptr->next;
|
||||
free(tmp);
|
||||
}
|
||||
ptr = user->rules_and;
|
||||
while(ptr){
|
||||
tmp = ptr;
|
||||
ptr = ptr->next;
|
||||
free(tmp);
|
||||
}
|
||||
free(user->name);
|
||||
free(user);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void* huserfree(void* fval)
|
||||
{
|
||||
USER* value = (USER*)fval;
|
||||
|
||||
hrulefree(value->rules_and);
|
||||
hrulefree(value->rules_or);
|
||||
hrulefree(value->rules_strict_and);
|
||||
free(value->qs_limit);
|
||||
free(value->name);
|
||||
free(value);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips the single or double quotes from a string.
|
||||
@ -445,7 +444,7 @@ bool check_time(char* str)
|
||||
|
||||
char* ptr = str;
|
||||
int colons = 0,numbers = 0,dashes = 0;
|
||||
while(*ptr){
|
||||
while(*ptr && ptr - str < 18){
|
||||
if(isdigit(*ptr)){numbers++;}
|
||||
else if(*ptr == ':'){colons++;}
|
||||
else if(*ptr == '-'){dashes++;}
|
||||
@ -486,7 +485,7 @@ TIMERANGE* parse_time(char* str, FW_INSTANCE* instance)
|
||||
tr = (TIMERANGE*)calloc(1,sizeof(TIMERANGE));
|
||||
|
||||
if(tr == NULL){
|
||||
skygw_log_write(LOGFILE_ERROR, "fwfilter: malloc returned NULL.");
|
||||
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: malloc returned NULL.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -499,7 +498,7 @@ TIMERANGE* parse_time(char* str, FW_INSTANCE* instance)
|
||||
while(ptr - str < 19){
|
||||
if(isdigit(*ptr)){
|
||||
*sdest = *ptr;
|
||||
}else if(*ptr == ':' ||*ptr == '-' || *ptr == '\0'){
|
||||
}else if(*ptr == ':' ||*ptr == '-' || *ptr == '\0' || *ptr == ' '){
|
||||
*sdest = '\0';
|
||||
*idest++ = atoi(strbuffer);
|
||||
sdest = strbuffer;
|
||||
@ -512,7 +511,7 @@ TIMERANGE* parse_time(char* str, FW_INSTANCE* instance)
|
||||
|
||||
CHK_TIMES(tmptr);
|
||||
|
||||
if(*ptr == '\0'){
|
||||
if(*ptr == '\0' || *ptr == ' '){
|
||||
return tr;
|
||||
}
|
||||
|
||||
@ -526,8 +525,8 @@ TIMERANGE* parse_time(char* str, FW_INSTANCE* instance)
|
||||
sdest++;
|
||||
}
|
||||
|
||||
|
||||
return tr;
|
||||
free(tr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@ -636,13 +635,14 @@ void add_users(char* rule, FW_INSTANCE* instance)
|
||||
* @param rule Rule string to parse
|
||||
* @param instance The FW_FILTER instance
|
||||
*/
|
||||
void link_rules(char* rule, FW_INSTANCE* instance)
|
||||
bool link_rules(char* orig, FW_INSTANCE* instance)
|
||||
{
|
||||
assert(rule != NULL && instance != NULL);
|
||||
|
||||
/**Apply rules to users*/
|
||||
|
||||
bool match_any = true;
|
||||
bool rval = true;
|
||||
char *rule = strdup(orig);
|
||||
char *tok, *ruleptr, *userptr, *modeptr;
|
||||
char *saveptr = NULL;
|
||||
RULELIST* rulelist = NULL;
|
||||
@ -653,16 +653,30 @@ void link_rules(char* rule, FW_INSTANCE* instance)
|
||||
|
||||
if((userptr == NULL || ruleptr == NULL || modeptr == NULL)||
|
||||
(userptr > modeptr || userptr > ruleptr || modeptr > ruleptr)) {
|
||||
skygw_log_write(LOGFILE_ERROR, "fwfilter: Rule syntax incorrect, right keywords not found in the correct order: %s",rule);
|
||||
return;
|
||||
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, right keywords not found in the correct order: %s",orig);
|
||||
rval = false;
|
||||
goto parse_err;
|
||||
}
|
||||
|
||||
*modeptr++ = '\0';
|
||||
*ruleptr++ = '\0';
|
||||
|
||||
tok = strtok_r(modeptr," ",&saveptr);
|
||||
if(tok && strcmp(tok,"match") == 0){
|
||||
|
||||
if(tok == NULL)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, right keywords not found in the correct order: %s",orig);
|
||||
rval = false;
|
||||
goto parse_err;
|
||||
}
|
||||
if(strcmp(tok,"match") == 0){
|
||||
tok = strtok_r(NULL," ",&saveptr);
|
||||
if(tok == NULL)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, missing keyword after 'match': %s",orig);
|
||||
rval = false;
|
||||
goto parse_err;
|
||||
}
|
||||
if(strcmp(tok,"any") == 0){
|
||||
match_any = true;
|
||||
}else if(strcmp(tok,"all") == 0){
|
||||
@ -671,28 +685,40 @@ void link_rules(char* rule, FW_INSTANCE* instance)
|
||||
match_any = false;
|
||||
strict = true;
|
||||
}else{
|
||||
skygw_log_write(LOGFILE_ERROR, "fwfilter: Rule syntax incorrect, 'match' was not followed by 'any' or 'all': %s",rule);
|
||||
return;
|
||||
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, 'match' was not followed by correct keyword: %s",orig);
|
||||
rval = false;
|
||||
goto parse_err;
|
||||
}
|
||||
}
|
||||
|
||||
tok = strtok_r(ruleptr," ",&saveptr);
|
||||
tok = strtok_r(NULL," ",&saveptr);
|
||||
|
||||
while(tok)
|
||||
{
|
||||
RULE* rule_found = NULL;
|
||||
|
||||
if((rule_found = find_rule(tok,instance)) != NULL)
|
||||
{
|
||||
RULELIST* tmp_rl = (RULELIST*)calloc(1,sizeof(RULELIST));
|
||||
tmp_rl->rule = rule_found;
|
||||
tmp_rl->next = rulelist;
|
||||
rulelist = tmp_rl;
|
||||
|
||||
}
|
||||
tok = strtok_r(NULL," ",&saveptr);
|
||||
}
|
||||
if(tok == NULL)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, no rules given: %s",orig);
|
||||
rval = false;
|
||||
goto parse_err;
|
||||
}
|
||||
|
||||
while(tok)
|
||||
{
|
||||
RULE* rule_found = NULL;
|
||||
|
||||
if((rule_found = find_rule(tok,instance)) != NULL)
|
||||
{
|
||||
RULELIST* tmp_rl = (RULELIST*)calloc(1,sizeof(RULELIST));
|
||||
tmp_rl->rule = rule_found;
|
||||
tmp_rl->next = rulelist;
|
||||
rulelist = tmp_rl;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, could not find rule '%s'.",tok);
|
||||
}
|
||||
tok = strtok_r(NULL," ",&saveptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this list of rules to all the listed users
|
||||
@ -702,8 +728,22 @@ void link_rules(char* rule, FW_INSTANCE* instance)
|
||||
userptr = strtok_r(rule," ",&saveptr);
|
||||
userptr = strtok_r(NULL," ",&saveptr);
|
||||
|
||||
if(userptr == NULL)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, no users given: %s",orig);
|
||||
rval = false;
|
||||
goto parse_err;
|
||||
}
|
||||
|
||||
if(rulelist == NULL)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Rule syntax incorrect, no rules found: %s",orig);
|
||||
rval = false;
|
||||
goto parse_err;
|
||||
}
|
||||
|
||||
while(userptr)
|
||||
{
|
||||
{
|
||||
USER* user;
|
||||
RULELIST *tl = NULL,*tail = NULL;
|
||||
|
||||
@ -713,17 +753,12 @@ void link_rules(char* rule, FW_INSTANCE* instance)
|
||||
user = (USER*)calloc(1,sizeof(USER));
|
||||
|
||||
if(user == NULL){
|
||||
free(rulelist);
|
||||
return;
|
||||
}
|
||||
|
||||
if((user->lock = (SPINLOCK*)malloc(sizeof(SPINLOCK))) == NULL){
|
||||
free(user);
|
||||
free(rulelist);
|
||||
return;
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: dbfwfilter: failed to allocate memory when parsing rules.");
|
||||
rval = false;
|
||||
goto parse_err;
|
||||
}
|
||||
|
||||
spinlock_init(user->lock);
|
||||
spinlock_init(&user->lock);
|
||||
}
|
||||
|
||||
user->name = (char*)strdup(userptr);
|
||||
@ -753,121 +788,179 @@ void link_rules(char* rule, FW_INSTANCE* instance)
|
||||
(void *)user);
|
||||
|
||||
userptr = strtok_r(NULL," ",&saveptr);
|
||||
|
||||
}
|
||||
|
||||
parse_err:
|
||||
|
||||
free(rule);
|
||||
|
||||
while(rulelist)
|
||||
{
|
||||
RULELIST *tmp = rulelist;
|
||||
rulelist = rulelist->next;
|
||||
free(tmp);
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a TIMERANGE struct
|
||||
* @param tr pointer to a TIMERANGE struct
|
||||
*/
|
||||
void tr_free(TIMERANGE* tr)
|
||||
{
|
||||
TIMERANGE *node,*tmp;
|
||||
|
||||
node = tr;
|
||||
|
||||
while(node)
|
||||
{
|
||||
tmp = node;
|
||||
node = node->next;
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Parse the configuration value either as a new rule or a list of users.
|
||||
* @param rule The string to parse
|
||||
* @param instance The FW_FILTER instance
|
||||
*/
|
||||
void parse_rule(char* rule, FW_INSTANCE* instance)
|
||||
bool parse_rule(char* rule, FW_INSTANCE* instance)
|
||||
{
|
||||
ss_dassert(rule != NULL && instance != NULL);
|
||||
ss_dassert(rule != NULL && instance != NULL);
|
||||
|
||||
char *rulecpy = strdup(rule);
|
||||
char *saveptr = NULL;
|
||||
char *tok = strtok_r(rulecpy," ,",&saveptr);
|
||||
bool allow,deny,mode;
|
||||
RULE* ruledef = NULL;
|
||||
|
||||
if(tok == NULL) goto retblock;
|
||||
char *rulecpy = strdup(rule);
|
||||
char *saveptr = NULL;
|
||||
char *tok = strtok_r(rulecpy," ,",&saveptr);
|
||||
bool allow,deny,mode;
|
||||
RULE* ruledef = NULL;
|
||||
bool rval = true;
|
||||
|
||||
if(strcmp("rule",tok) == 0){ /**Define a new rule*/
|
||||
if(tok == NULL)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, no rule rule: %s",rule);
|
||||
rval = false;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
tok = strtok_r(NULL," ,",&saveptr);
|
||||
|
||||
if(tok == NULL) goto retblock;
|
||||
|
||||
RULELIST* rlist = NULL;
|
||||
|
||||
ruledef = (RULE*)calloc(1,sizeof(RULE));
|
||||
|
||||
if(ruledef == NULL)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Error : Memory allocation failed.");
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
rlist = (RULELIST*)calloc(1,sizeof(RULELIST));
|
||||
|
||||
if(rlist == NULL)
|
||||
{
|
||||
free(ruledef);
|
||||
skygw_log_write(LOGFILE_ERROR,"Error : Memory allocation failed.");
|
||||
goto retblock;
|
||||
}
|
||||
ruledef->name = strdup(tok);
|
||||
ruledef->type = RT_UNDEFINED;
|
||||
ruledef->on_queries = QUERY_OP_UNDEFINED;
|
||||
rlist->rule = ruledef;
|
||||
rlist->next = instance->rules;
|
||||
instance->rules = rlist;
|
||||
if(strcmp("rule",tok) == 0)
|
||||
{
|
||||
/**Define a new rule*/
|
||||
|
||||
}else if(strcmp("users",tok) == 0){
|
||||
tok = strtok_r(NULL," ,",&saveptr);
|
||||
|
||||
/**Apply rules to users*/
|
||||
add_users(rule, instance);
|
||||
goto retblock;
|
||||
if(tok == NULL)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, incomplete rule: %s",rule);
|
||||
rval = false;
|
||||
goto retblock;
|
||||
}
|
||||
else
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Error : Unknown token in rule file: %s",tok);
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
RULELIST* rlist = NULL;
|
||||
|
||||
ruledef = (RULE*)calloc(1,sizeof(RULE));
|
||||
|
||||
if(ruledef == NULL)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: Memory allocation failed.");
|
||||
rval = false;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
rlist = (RULELIST*)calloc(1,sizeof(RULELIST));
|
||||
|
||||
if(rlist == NULL)
|
||||
{
|
||||
free(ruledef);
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: Memory allocation failed.");
|
||||
rval = false;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
ruledef->name = strdup(tok);
|
||||
ruledef->type = RT_UNDEFINED;
|
||||
ruledef->on_queries = QUERY_OP_UNDEFINED;
|
||||
rlist->rule = ruledef;
|
||||
rlist->next = instance->rules;
|
||||
instance->rules = rlist;
|
||||
|
||||
}else if(strcmp("users",tok) == 0)
|
||||
{
|
||||
|
||||
/**Apply rules to users*/
|
||||
add_users(rule, instance);
|
||||
goto retblock;
|
||||
}
|
||||
else
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Error : Unknown token in rule file: %s",tok);
|
||||
rval = false;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
tok = strtok_r(NULL, " ,",&saveptr);
|
||||
|
||||
|
||||
if((allow = (strcmp(tok,"allow") == 0)) ||
|
||||
(deny = (strcmp(tok,"deny") == 0)))
|
||||
{
|
||||
|
||||
mode = allow ? true:false;
|
||||
ruledef->allow = mode;
|
||||
ruledef->type = RT_PERMISSION;
|
||||
tok = strtok_r(NULL, " ,",&saveptr);
|
||||
|
||||
|
||||
if((allow = (strcmp(tok,"allow") == 0)) ||
|
||||
(deny = (strcmp(tok,"deny") == 0))){
|
||||
|
||||
mode = allow ? true:false;
|
||||
ruledef->allow = mode;
|
||||
ruledef->type = RT_PERMISSION;
|
||||
tok = strtok_r(NULL, " ,",&saveptr);
|
||||
|
||||
|
||||
while(tok){
|
||||
if(strcmp(tok,"wildcard") == 0)
|
||||
while(tok)
|
||||
{
|
||||
if(strcmp(tok,"wildcard") == 0)
|
||||
{
|
||||
ruledef->type = RT_WILDCARD;
|
||||
}
|
||||
else if(strcmp(tok,"columns") == 0)
|
||||
else if(strcmp(tok,"columns") == 0)
|
||||
{
|
||||
STRLINK *tail = NULL,*current;
|
||||
ruledef->type = RT_COLUMN;
|
||||
tok = strtok_r(NULL, " ,",&saveptr);
|
||||
while(tok && strcmp(tok,"at_times") != 0){
|
||||
while(tok && strcmp(tok,"at_times") != 0 &&
|
||||
strcmp(tok,"on_queries") != 0){
|
||||
current = malloc(sizeof(STRLINK));
|
||||
current->value = strdup(tok);
|
||||
current->next = tail;
|
||||
tail = current;
|
||||
tok = strtok_r(NULL, " ,",&saveptr);
|
||||
}
|
||||
|
||||
|
||||
ruledef->data = (void*)tail;
|
||||
continue;
|
||||
|
||||
}
|
||||
else if(strcmp(tok,"at_times") == 0)
|
||||
else if(strcmp(tok,"at_times") == 0)
|
||||
{
|
||||
|
||||
tok = strtok_r(NULL, " ,",&saveptr);
|
||||
TIMERANGE *tr = NULL;
|
||||
bool not_valid = false;
|
||||
while(tok){
|
||||
|
||||
if(!check_time(tok))
|
||||
{
|
||||
not_valid = true;
|
||||
break;
|
||||
}
|
||||
|
||||
TIMERANGE *tmp = parse_time(tok,instance);
|
||||
|
||||
if(IS_RVRS_TIME(tmp)){
|
||||
|
||||
if(tmp == NULL)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Rule parsing failed, unexpected characters after time definition.");
|
||||
rval = false;
|
||||
tr_free(tr);
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
if(IS_RVRS_TIME(tmp))
|
||||
{
|
||||
tmp = split_reverse_time(tmp);
|
||||
}
|
||||
tmp->next = tr;
|
||||
@ -875,44 +968,68 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
|
||||
tok = strtok_r(NULL, " ,",&saveptr);
|
||||
}
|
||||
ruledef->active = tr;
|
||||
|
||||
if(not_valid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
else if(strcmp(tok,"regex") == 0)
|
||||
else if(strcmp(tok,"regex") == 0)
|
||||
{
|
||||
bool escaped = false;
|
||||
regex_t *re;
|
||||
char* start, *str;
|
||||
tok = strtok_r(NULL," ",&saveptr);
|
||||
char delim = '\'';
|
||||
while(*tok == '\'' || *tok == '"'){
|
||||
char delim = '\'';
|
||||
int n_char = 0;
|
||||
|
||||
while(*tok == '\'' || *tok == '"')
|
||||
{
|
||||
delim = *tok;
|
||||
tok++;
|
||||
}
|
||||
|
||||
start = tok;
|
||||
|
||||
while(isspace(*tok) || *tok == delim){
|
||||
|
||||
while(isspace(*tok) || *tok == delim)
|
||||
{
|
||||
tok++;
|
||||
}
|
||||
|
||||
while(true){
|
||||
|
||||
if((*tok == delim) && !escaped){
|
||||
while(n_char < 2048)
|
||||
{
|
||||
|
||||
/** Hard-coded regex length cap */
|
||||
|
||||
if((*tok == delim) && !escaped)
|
||||
{
|
||||
break;
|
||||
}
|
||||
escaped = (*tok == '\\');
|
||||
tok++;
|
||||
n_char++;
|
||||
}
|
||||
|
||||
if(n_char >= 2048)
|
||||
{
|
||||
skygw_log_write_flush(LOGFILE_ERROR, "dbfwfilter: Failed to parse rule, regular expression length is over 2048 characters.");
|
||||
rval = false;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
str = calloc(((tok - start) + 1),sizeof(char));
|
||||
if(str == NULL)
|
||||
{
|
||||
skygw_log_write_flush(LOGFILE_ERROR, "Fatal Error: malloc returned NULL.");
|
||||
rval = false;
|
||||
goto retblock;
|
||||
}
|
||||
re = (regex_t*)malloc(sizeof(regex_t));
|
||||
|
||||
if(re == NULL){
|
||||
skygw_log_write_flush(LOGFILE_ERROR, "Fatal Error: malloc returned NULL.");
|
||||
skygw_log_write_flush(LOGFILE_ERROR, "Fatal Error: malloc returned NULL.");
|
||||
rval = false;
|
||||
free(str);
|
||||
goto retblock;
|
||||
}
|
||||
@ -920,8 +1037,10 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
|
||||
memcpy(str, start, (tok-start));
|
||||
|
||||
if(regcomp(re, str,REG_NOSUB)){
|
||||
skygw_log_write(LOGFILE_ERROR, "fwfilter: Invalid regular expression '%s'.", str);
|
||||
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Invalid regular expression '%s'.", str);
|
||||
rval = false;
|
||||
free(re);
|
||||
goto retblock;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -931,9 +1050,9 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
|
||||
free(str);
|
||||
|
||||
}
|
||||
else if(strcmp(tok,"limit_queries") == 0)
|
||||
else if(strcmp(tok,"limit_queries") == 0)
|
||||
{
|
||||
|
||||
|
||||
QUERYSPEED* qs = (QUERYSPEED*)calloc(1,sizeof(QUERYSPEED));
|
||||
|
||||
spinlock_acquire(instance->lock);
|
||||
@ -943,6 +1062,8 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
|
||||
tok = strtok_r(NULL," ",&saveptr);
|
||||
if(tok == NULL){
|
||||
free(qs);
|
||||
rval = false;
|
||||
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Missing parameter in limit_queries: '%s'.", rule);
|
||||
goto retblock;
|
||||
}
|
||||
qs->limit = atoi(tok);
|
||||
@ -950,41 +1071,57 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
|
||||
tok = strtok_r(NULL," ",&saveptr);
|
||||
if(tok == NULL){
|
||||
free(qs);
|
||||
rval = false;
|
||||
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Missing parameter in limit_queries: '%s'.", rule);
|
||||
goto retblock;
|
||||
}
|
||||
qs->period = atof(tok);
|
||||
tok = strtok_r(NULL," ",&saveptr);
|
||||
if(tok == NULL){
|
||||
free(qs);
|
||||
rval = false;
|
||||
skygw_log_write(LOGFILE_ERROR, "dbfwfilter: Missing parameter in limit_queries: '%s'.", rule);
|
||||
goto retblock;
|
||||
}
|
||||
qs->cooldown = atof(tok);
|
||||
ruledef->type = RT_THROTTLE;
|
||||
ruledef->data = (void*)qs;
|
||||
}
|
||||
else if(strcmp(tok,"no_where_clause") == 0)
|
||||
else if(strcmp(tok,"no_where_clause") == 0)
|
||||
{
|
||||
ruledef->type = RT_CLAUSE;
|
||||
ruledef->data = (void*)mode;
|
||||
}
|
||||
else if(strcmp(tok,"on_operations") == 0)
|
||||
else if(strcmp(tok,"on_queries") == 0)
|
||||
{
|
||||
tok = strtok_r(NULL," ",&saveptr);
|
||||
if(!parse_querytypes(tok,ruledef)){
|
||||
skygw_log_write(LOGFILE_ERROR,
|
||||
"fwfilter: Invalid query type"
|
||||
"requirements on where/having clauses: %s."
|
||||
,tok);
|
||||
}
|
||||
}
|
||||
tok = strtok_r(NULL," ,",&saveptr);
|
||||
|
||||
if(tok == NULL)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,
|
||||
"dbfwfilter: Missing parameter for 'on_queries'.");
|
||||
rval = false;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
goto retblock;
|
||||
if(!parse_querytypes(tok,ruledef)){
|
||||
skygw_log_write(LOGFILE_ERROR,
|
||||
"dbfwfilter: Invalid query type"
|
||||
"requirements: %s."
|
||||
,tok);
|
||||
rval = false;
|
||||
goto retblock;
|
||||
}
|
||||
}
|
||||
tok = strtok_r(NULL," ,",&saveptr);
|
||||
}
|
||||
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
retblock:
|
||||
free(rulecpy);
|
||||
free(rulecpy);
|
||||
return rval;
|
||||
|
||||
}
|
||||
|
||||
@ -1006,7 +1143,8 @@ createInstance(char **options, FILTER_PARAMETER **params)
|
||||
char *filename = NULL, *nl;
|
||||
char buffer[2048];
|
||||
FILE* file;
|
||||
|
||||
bool err = false;
|
||||
|
||||
if ((my_instance = calloc(1, sizeof(FW_INSTANCE))) == NULL ||
|
||||
(my_instance->lock = (SPINLOCK*)malloc(sizeof(SPINLOCK))) == NULL){
|
||||
skygw_log_write(LOGFILE_ERROR, "Memory allocation for firewall filter failed.");
|
||||
@ -1021,7 +1159,7 @@ createInstance(char **options, FILTER_PARAMETER **params)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hashtable_memory_fns(ht,(HASHMEMORYFN)strdup,NULL,(HASHMEMORYFN)free,hrulefree);
|
||||
hashtable_memory_fns(ht,(HASHMEMORYFN)strdup,NULL,(HASHMEMORYFN)free,huserfree);
|
||||
|
||||
my_instance->htable = ht;
|
||||
my_instance->def_op = true;
|
||||
@ -1077,19 +1215,41 @@ createInstance(char **options, FILTER_PARAMETER **params)
|
||||
*nl = '\0';
|
||||
}
|
||||
|
||||
parse_rule(buffer,my_instance);
|
||||
if(!parse_rule(buffer,my_instance))
|
||||
{
|
||||
fclose(file);
|
||||
err = true;
|
||||
goto retblock;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
/**Apply the rules to users*/
|
||||
|
||||
ptr = my_instance->userstrings;
|
||||
|
||||
while(ptr){
|
||||
link_rules(ptr->value,my_instance);
|
||||
tmp = ptr;
|
||||
ptr = ptr->next;
|
||||
free(tmp->value);
|
||||
free(tmp);
|
||||
|
||||
if(!link_rules(ptr->value,my_instance))
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"dbfwfilter: Failed to parse rule: %s",ptr->value);
|
||||
err = true;
|
||||
}
|
||||
tmp = ptr;
|
||||
ptr = ptr->next;
|
||||
free(tmp->value);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
retblock:
|
||||
|
||||
if(err)
|
||||
{
|
||||
hrulefree(my_instance->rules);
|
||||
hashtable_free(my_instance->htable);
|
||||
free(my_instance);
|
||||
my_instance = NULL;
|
||||
}
|
||||
|
||||
return (FILTER *)my_instance;
|
||||
@ -1340,7 +1500,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
|
||||
|
||||
if(!rulelist->rule->allow){
|
||||
msg = strdup("Permission denied, query matched regular expression.");
|
||||
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': regex matched on query",rulelist->rule->name);
|
||||
skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': regex matched on query",rulelist->rule->name);
|
||||
goto queryresolved;
|
||||
}else{
|
||||
break;
|
||||
@ -1353,7 +1513,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
|
||||
if(!rulelist->rule->allow){
|
||||
matches = true;
|
||||
msg = strdup("Permission denied at this time.");
|
||||
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': query denied at: %s",rulelist->rule->name,asctime(tm_now));
|
||||
skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query denied at: %s",rulelist->rule->name,asctime(tm_now));
|
||||
goto queryresolved;
|
||||
}else{
|
||||
break;
|
||||
@ -1376,7 +1536,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
|
||||
|
||||
if(!rulelist->rule->allow){
|
||||
sprintf(emsg,"Permission denied to column '%s'.",strln->value);
|
||||
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': query targets forbidden column: %s",rulelist->rule->name,strln->value);
|
||||
skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query targets forbidden column: %s",rulelist->rule->name,strln->value);
|
||||
msg = strdup(emsg);
|
||||
goto queryresolved;
|
||||
}else{
|
||||
@ -1406,7 +1566,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
|
||||
|
||||
matches = true;
|
||||
msg = strdup("Usage of wildcard denied.");
|
||||
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': query contains a wildcard.",rulelist->rule->name);
|
||||
skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query contains a wildcard.",rulelist->rule->name);
|
||||
goto queryresolved;
|
||||
}
|
||||
}
|
||||
@ -1424,9 +1584,9 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
|
||||
rule_qs = (QUERYSPEED*)rulelist->rule->data;
|
||||
spinlock_release(my_instance->lock);
|
||||
|
||||
spinlock_acquire(user->lock);
|
||||
spinlock_acquire(&user->lock);
|
||||
queryspeed = user->qs_limit;
|
||||
spinlock_release(user->lock);
|
||||
spinlock_release(&user->lock);
|
||||
|
||||
while(queryspeed){
|
||||
if(queryspeed->id == rule_qs->id){
|
||||
@ -1457,7 +1617,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
|
||||
double blocked_for = queryspeed->cooldown - difftime(time_now,queryspeed->triggered);
|
||||
|
||||
sprintf(emsg,"Queries denied for %f seconds",blocked_for);
|
||||
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': user denied for %f seconds",rulelist->rule->name,blocked_for);
|
||||
skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': user denied for %f seconds",rulelist->rule->name,blocked_for);
|
||||
msg = strdup(emsg);
|
||||
|
||||
matches = true;
|
||||
@ -1478,7 +1638,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
|
||||
queryspeed->active = true;
|
||||
|
||||
skygw_log_write(LOGFILE_TRACE,
|
||||
"fwfilter: rule '%s': query limit triggered (%d queries in %f seconds), denying queries from user for %f seconds.",
|
||||
"dbfwfilter: rule '%s': query limit triggered (%d queries in %f seconds), denying queries from user for %f seconds.",
|
||||
rulelist->rule->name,
|
||||
queryspeed->limit,
|
||||
queryspeed->period,
|
||||
@ -1508,7 +1668,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
|
||||
{
|
||||
matches = true;
|
||||
msg = strdup("Required WHERE/HAVING clause is missing.");
|
||||
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': query has no where/having clause, query is denied.",
|
||||
skygw_log_write(LOGFILE_TRACE, "dbfwfilter: rule '%s': query has no where/having clause, query is denied.",
|
||||
rulelist->rule->name);
|
||||
}
|
||||
break;
|
||||
@ -1600,6 +1760,7 @@ bool check_match_any(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *qu
|
||||
bool check_match_all(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue, USER* user,bool strict_all)
|
||||
{
|
||||
bool is_sql, rval = true;
|
||||
bool have_active_rule = false;
|
||||
int qlen;
|
||||
unsigned char* memptr = (unsigned char*)queue->start;
|
||||
char *fullquery = NULL,*ptr;
|
||||
@ -1640,7 +1801,8 @@ bool check_match_all(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *qu
|
||||
rulelist = rulelist->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
have_active_rule = true;
|
||||
|
||||
if(!rule_matches(my_instance,my_session,queue,user,rulelist,fullquery)){
|
||||
rval = false;
|
||||
@ -1650,6 +1812,12 @@ bool check_match_all(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *qu
|
||||
rulelist = rulelist->next;
|
||||
}
|
||||
|
||||
if(!have_active_rule)
|
||||
{
|
||||
/** No active rules */
|
||||
rval = false;
|
||||
}
|
||||
|
||||
retblock:
|
||||
|
||||
free(fullquery);
|
||||
|
||||
@ -1063,6 +1063,7 @@ clientReply (FILTER* instance, void *session, GWBUF *reply)
|
||||
|
||||
if(!my_session->active)
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE,"Tee: Failed to return reply, session is closed");
|
||||
gwbuf_free(reply);
|
||||
rc = 0;
|
||||
if(my_session->waiting[PARENT])
|
||||
@ -1104,6 +1105,7 @@ clientReply (FILTER* instance, void *session, GWBUF *reply)
|
||||
|
||||
if(my_session->replies[branch] == 0)
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE,"Tee: First reply to a query for [%s].",branch == PARENT ? "PARENT":"CHILD");
|
||||
/* Reply is in a single packet if it is an OK, ERR or LOCAL_INFILE packet.
|
||||
* Otherwise the reply is a result set and the amount of packets is unknown.
|
||||
*/
|
||||
@ -1116,6 +1118,11 @@ clientReply (FILTER* instance, void *session, GWBUF *reply)
|
||||
{
|
||||
flags = get_response_flags(ptr,true);
|
||||
more_results = (flags & 0x08) && my_session->client_multistatement;
|
||||
if(more_results)
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE,
|
||||
"Tee: [%s] waiting for more results.",branch == PARENT ? "PARENT":"CHILD");
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef SS_DEBUG
|
||||
|
||||
@ -159,7 +159,20 @@ typedef struct subservice_t{
|
||||
int state;
|
||||
int n_res_waiting;
|
||||
bool mapped;
|
||||
}SUBSERVICE;
|
||||
}SUBSERVICE;
|
||||
|
||||
/**
|
||||
* Bitmask values for the router session's initialization. These values are used
|
||||
* to prevent responses from internal commands being forwarded to the client.
|
||||
*/
|
||||
typedef enum shard_init_mask
|
||||
{
|
||||
INIT_READY = 0x0,
|
||||
INIT_MAPPING = 0x1,
|
||||
INIT_USE_DB = 0x02,
|
||||
INIT_UNINT = 0x04
|
||||
|
||||
} shard_init_mask_t;
|
||||
|
||||
/**
|
||||
* The client session structure used within this router.
|
||||
@ -172,8 +185,8 @@ struct router_client_session {
|
||||
int rses_versno; /*< even = no active update, else odd. not used 4/14 */
|
||||
bool rses_closed; /*< true when closeSession is called */
|
||||
DCB* rses_client_dcb;
|
||||
DCB* dummy_dcb; /* DCB used to send the client write messages from the router itself */
|
||||
DCB* queue_dcb; /* DCB used to send queued queries to the router */
|
||||
DCB* replydcb; /* DCB used to send the client write messages from the router itself */
|
||||
DCB* routedcb; /* DCB used to send queued queries to the router */
|
||||
MYSQL_session* rses_mysql_session;
|
||||
/** Properties listed by their type */
|
||||
rses_property_t* rses_properties[RSES_PROP_TYPE_COUNT];
|
||||
@ -190,6 +203,8 @@ struct router_client_session {
|
||||
bool hash_init;
|
||||
SESSION* session;
|
||||
GWBUF* queue;
|
||||
char connect_db[MYSQL_DATABASE_MAXLEN+1]; /*< Database the user was trying to connect to */
|
||||
shard_init_mask_t init; /*< Initialization state bitmask */
|
||||
#if defined(SS_DEBUG)
|
||||
skygw_chk_t rses_chk_tail;
|
||||
#endif
|
||||
|
||||
@ -530,6 +530,12 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
|
||||
if (auth_ret == 0) {
|
||||
dcb->user = strdup(client_data->user);
|
||||
}
|
||||
else
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,
|
||||
"%s: login attempt for user '%s', authentication failed.",
|
||||
dcb->service->name, username);
|
||||
}
|
||||
|
||||
/* let's free the auth_token now */
|
||||
if (auth_token) {
|
||||
@ -900,7 +906,7 @@ int gw_read_client_event(
|
||||
dcb,
|
||||
ERRACT_NEW_CONNECTION,
|
||||
&succp);
|
||||
free(errbuf);
|
||||
gwbuf_free(errbuf);
|
||||
/**
|
||||
* If there are not enough backends close
|
||||
* session
|
||||
|
||||
@ -1530,14 +1530,20 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password,
|
||||
* user@% not found.
|
||||
*/
|
||||
|
||||
LOGIF(LD,
|
||||
(skygw_log_write_flush(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [MySQL Client Auth], user [%s@%s] not existent",
|
||||
pthread_self(),
|
||||
key.user,
|
||||
dcb->remote)));
|
||||
break;
|
||||
LOGIF(LD,
|
||||
(skygw_log_write_flush(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [MySQL Client Auth], user [%s@%s] not existent",
|
||||
pthread_self(),
|
||||
key.user,
|
||||
dcb->remote)));
|
||||
|
||||
LOGIF(LT,skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Authentication Failed: user [%s@%s] not found.",
|
||||
key.user,
|
||||
dcb->remote));
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@ -89,6 +89,8 @@
|
||||
|
||||
#include <mysql_client_server_protocol.h>
|
||||
|
||||
#include "modutil.h"
|
||||
|
||||
/** Defined in log_manager.cc */
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
extern size_t log_ses_count[];
|
||||
@ -535,6 +537,7 @@ BACKEND *master_host = NULL;
|
||||
pthread_self(),
|
||||
candidate->server->port,
|
||||
candidate->current_connection_count)));
|
||||
|
||||
/*
|
||||
* Open a backend connection, putting the DCB for this
|
||||
* connection in the client_rses->backend_dcb
|
||||
@ -564,7 +567,13 @@ BACKEND *master_host = NULL;
|
||||
spinlock_release(&inst->lock);
|
||||
|
||||
CHK_CLIENT_RSES(client_rses);
|
||||
|
||||
|
||||
skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Readconnroute: New session for server %s. "
|
||||
"Connections : %d",
|
||||
candidate->server->unique_name,
|
||||
candidate->current_connection_count);
|
||||
return (void *)client_rses;
|
||||
}
|
||||
|
||||
@ -718,10 +727,19 @@ routeQuery(ROUTER *instance, void *router_session, GWBUF *queue)
|
||||
"Error : Failed to route MySQL command %d to backend "
|
||||
"server.",
|
||||
mysql_command)));
|
||||
skygw_log_write(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Failed to route MySQL command %d to backend "
|
||||
"server %s.",
|
||||
mysql_command,
|
||||
router_cli_ses->backend->server->unique_name);
|
||||
rc = 0;
|
||||
goto return_rc;
|
||||
|
||||
}
|
||||
|
||||
char* trc = NULL;
|
||||
|
||||
switch(mysql_command) {
|
||||
case MYSQL_COM_CHANGE_USER:
|
||||
rc = backend_dcb->func.auth(
|
||||
@ -730,7 +748,8 @@ routeQuery(ROUTER *instance, void *router_session, GWBUF *queue)
|
||||
backend_dcb->session,
|
||||
queue);
|
||||
break;
|
||||
|
||||
case MYSQL_COM_QUERY:
|
||||
LOGIF(LOGFILE_TRACE,(trc = modutil_get_SQL(queue)));
|
||||
default:
|
||||
rc = backend_dcb->func.write(backend_dcb, queue);
|
||||
break;
|
||||
@ -745,6 +764,15 @@ routeQuery(ROUTER *instance, void *router_session, GWBUF *queue)
|
||||
mysql_command,
|
||||
backend_dcb,
|
||||
rc)));
|
||||
|
||||
LOGIF(LOGFILE_TRACE,skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Routed command [%#x] to '%s'%s%s",
|
||||
mysql_command,
|
||||
backend_dcb->server->unique_name,
|
||||
trc?": ":".",
|
||||
trc?trc:""));
|
||||
free(trc);
|
||||
return_rc:
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -402,7 +402,7 @@ int gen_databaselist(ROUTER_INSTANCE* inst, ROUTER_CLIENT_SES* session)
|
||||
rval);
|
||||
}
|
||||
}
|
||||
|
||||
gwbuf_free(buffer);
|
||||
return !rval;
|
||||
}
|
||||
|
||||
@ -514,6 +514,10 @@ char* get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client,
|
||||
*/
|
||||
|
||||
rval = (char*) hashtable_fetch(ht, client->rses_mysql_session->db);
|
||||
if(rval)
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE,"schemarouter: Using active database '%s'",client->rses_mysql_session->db);
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
@ -1932,6 +1936,7 @@ static int routeQuery(
|
||||
*/
|
||||
|
||||
route_target = TARGET_ANY;
|
||||
skygw_log_write(LOGFILE_TRACE,"schemarouter: Routing query to first available backend.");
|
||||
|
||||
}
|
||||
else
|
||||
@ -2008,6 +2013,9 @@ static int routeQuery(
|
||||
|
||||
/**No valid backends alive*/
|
||||
skygw_log_write(LOGFILE_TRACE,"schemarouter: No backends are running");
|
||||
skygw_log_write(LOGFILE_ERROR,
|
||||
"Error: Schemarouter: Failed to route query, "
|
||||
"no backends are available.");
|
||||
rses_end_locked_router_action(router_cli_ses);
|
||||
ret = 0;
|
||||
goto retblock;
|
||||
@ -2215,6 +2223,7 @@ static void clientReply (
|
||||
*/
|
||||
if (!rses_begin_locked_router_action(router_cli_ses))
|
||||
{
|
||||
while((writebuf = gwbuf_consume(writebuf,gwbuf_length(writebuf))));
|
||||
goto lock_failed;
|
||||
}
|
||||
/** Holding lock ensures that router session remains open */
|
||||
@ -2244,6 +2253,7 @@ static void clientReply (
|
||||
if (!rses_begin_locked_router_action(router_cli_ses))
|
||||
{
|
||||
/** Log to debug that router was closed */
|
||||
while((writebuf = gwbuf_consume(writebuf,gwbuf_length(writebuf))));
|
||||
goto lock_failed;
|
||||
}
|
||||
bref = get_bref_from_dcb(router_cli_ses, backend_dcb);
|
||||
@ -2252,6 +2262,7 @@ static void clientReply (
|
||||
{
|
||||
/** Unlock router session */
|
||||
rses_end_locked_router_action(router_cli_ses);
|
||||
while((writebuf = gwbuf_consume(writebuf,gwbuf_length(writebuf))));
|
||||
goto lock_failed;
|
||||
}
|
||||
|
||||
@ -2299,7 +2310,7 @@ static void clientReply (
|
||||
}
|
||||
}
|
||||
|
||||
gwbuf_free(writebuf);
|
||||
while((writebuf = gwbuf_consume(writebuf,gwbuf_length(writebuf))));
|
||||
|
||||
if(mapped)
|
||||
{
|
||||
@ -2322,7 +2333,10 @@ static void clientReply (
|
||||
router_cli_ses->connect_db);
|
||||
router_cli_ses->rses_closed = true;
|
||||
if(router_cli_ses->queue)
|
||||
gwbuf_free(router_cli_ses->queue);
|
||||
{
|
||||
while((router_cli_ses->queue = gwbuf_consume(
|
||||
router_cli_ses->queue,gwbuf_length(router_cli_ses->queue))));
|
||||
}
|
||||
rses_end_locked_router_action(router_cli_ses);
|
||||
return;
|
||||
}
|
||||
@ -2376,7 +2390,7 @@ static void clientReply (
|
||||
{
|
||||
GWBUF* tmp = router_cli_ses->queue;
|
||||
router_cli_ses->queue = router_cli_ses->queue->next;
|
||||
tmp->next = NULL;
|
||||
tmp->next = NULL;
|
||||
char* querystr = modutil_get_SQL(tmp);
|
||||
skygw_log_write(LOGFILE_DEBUG,"schemarouter: Sending queued buffer for session %p: %s",
|
||||
router_cli_ses->rses_client_dcb->session,
|
||||
@ -2416,6 +2430,8 @@ static void clientReply (
|
||||
strcpy(router_cli_ses->rses_mysql_session->db,router_cli_ses->connect_db);
|
||||
ss_dassert(router_cli_ses->init == INIT_READY);
|
||||
rses_end_locked_router_action(router_cli_ses);
|
||||
if(writebuf)
|
||||
while((writebuf = gwbuf_consume(writebuf,gwbuf_length(writebuf))));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -119,7 +119,6 @@ static route_target_t get_shard_route_target(
|
||||
|
||||
static uint8_t getCapabilities(ROUTER* inst, void* router_session);
|
||||
|
||||
//bool parse_db_ignore_list(ROUTER_INSTANCE* router, char* param);
|
||||
static void subsvc_clear_state(SUBSERVICE* svc,subsvc_state_t state);
|
||||
static void subsvc_set_state(SUBSERVICE* svc,subsvc_state_t state);
|
||||
static bool get_shard_subsvc(SUBSERVICE** subsvc,ROUTER_CLIENT_SES* session,char* target);
|
||||
@ -414,8 +413,14 @@ bool subsvc_is_valid(SUBSERVICE* sub)
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the databases of all subservices.
|
||||
* @param inst router instance
|
||||
* @param session router session
|
||||
* @return 0 on success, 1 on error
|
||||
*/
|
||||
int
|
||||
gen_tablelist(ROUTER_INSTANCE* inst, ROUTER_CLIENT_SES* session)
|
||||
gen_subsvc_dblist(ROUTER_INSTANCE* inst, ROUTER_CLIENT_SES* session)
|
||||
{
|
||||
const char* query = "SHOW DATABASES;";
|
||||
GWBUF *buffer, *clone;
|
||||
@ -475,7 +480,6 @@ get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, GWBUF*
|
||||
|
||||
if(sz > 0)
|
||||
{
|
||||
has_dbs = true;
|
||||
for(i = 0; i < sz; i++)
|
||||
{
|
||||
|
||||
@ -489,9 +493,8 @@ get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, GWBUF*
|
||||
else
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE,"shardrouter: Query targets database '%s' on server '%s",dbnms[i],rval);
|
||||
has_dbs = true;
|
||||
}
|
||||
for(j = i; j < sz; j++) free(dbnms[j]);
|
||||
break;
|
||||
}
|
||||
free(dbnms[i]);
|
||||
}
|
||||
@ -554,6 +557,10 @@ get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, GWBUF*
|
||||
*/
|
||||
|
||||
rval = (char*) hashtable_fetch(ht, client->rses_mysql_session->db);
|
||||
if(rval)
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE,"shardrouter: Using active database '%s'",client->rses_mysql_session->db);
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
@ -612,64 +619,160 @@ filterReply(FILTER* instance, void *session, GWBUF *reply)
|
||||
ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES*) instance;
|
||||
SUBSERVICE* subsvc;
|
||||
int i, rv = 1;
|
||||
bool mapped = true;
|
||||
sescmd_cursor_t* scur;
|
||||
GWBUF* tmp = NULL;
|
||||
|
||||
skygw_log_write_flush(LOGFILE_TRACE,"shardrouter: filterReply mapped: %s",rses->hash_init ? "true" : "false");
|
||||
|
||||
if(!rses_begin_locked_router_action(rses))
|
||||
{
|
||||
tmp = reply;
|
||||
while((tmp = gwbuf_consume(tmp,gwbuf_length(tmp))));
|
||||
return 0;
|
||||
}
|
||||
|
||||
subsvc = get_subsvc_from_ses(rses, session);
|
||||
|
||||
if(SUBSVC_IS_WAITING(subsvc))
|
||||
if(rses->init & INIT_MAPPING)
|
||||
{
|
||||
subsvc_clear_state(subsvc, SUBSVC_WAITING_RESULT);
|
||||
bool mapped = true, logged = false;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < rses->n_subservice; i++)
|
||||
{
|
||||
|
||||
if(subsvc->session == rses->subservice[i]->session &&
|
||||
!SUBSVC_IS_MAPPED(rses->subservice[i]))
|
||||
{
|
||||
rses->subservice[i]->state |= SUBSVC_MAPPED;
|
||||
parse_mapping_response(rses,
|
||||
rses->subservice[i]->service->name,
|
||||
reply);
|
||||
|
||||
}
|
||||
|
||||
if(SUBSVC_IS_OK(rses->subservice[i]) &&
|
||||
!SUBSVC_IS_MAPPED(rses->subservice[i]))
|
||||
{
|
||||
mapped = false;
|
||||
if(!logged)
|
||||
{
|
||||
/*
|
||||
skygw_log_write(LOGFILE_DEBUG,"schemarouter: Still waiting for reply to SHOW DATABASES from %s for session %p",
|
||||
bkrf[i].bref_backend->backend_server->unique_name,
|
||||
rses->rses_client_dcb->session);
|
||||
*/
|
||||
logged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(mapped)
|
||||
{
|
||||
/*
|
||||
* Check if the session is reconnecting with a database name
|
||||
* that is not in the hashtable. If the database is not found
|
||||
* then close the session.
|
||||
*/
|
||||
|
||||
rses->init &= ~INIT_MAPPING;
|
||||
|
||||
if(rses->init & INIT_USE_DB)
|
||||
{
|
||||
char* target;
|
||||
|
||||
if((target = hashtable_fetch(rses->dbhash,
|
||||
rses->connect_db)) == NULL)
|
||||
{
|
||||
skygw_log_write_flush(LOGFILE_TRACE,"schemarouter: Connecting to a non-existent database '%s'",
|
||||
rses->connect_db);
|
||||
rses->rses_closed = true;
|
||||
if(rses->queue)
|
||||
{
|
||||
while((rses->queue = gwbuf_consume(
|
||||
rses->queue,gwbuf_length(rses->queue))));
|
||||
}
|
||||
rses_end_locked_router_action(rses);
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
/* Send a COM_INIT_DB packet to the server with the right database
|
||||
* and set it as the client's active database */
|
||||
|
||||
unsigned int qlen;
|
||||
GWBUF* buffer;
|
||||
|
||||
qlen = strlen(rses->connect_db);
|
||||
buffer = gwbuf_alloc(qlen + 5);
|
||||
if(buffer == NULL)
|
||||
{
|
||||
skygw_log_write_flush(LOGFILE_ERROR,"Error : Buffer allocation failed.");
|
||||
rses->rses_closed = true;
|
||||
if(rses->queue)
|
||||
gwbuf_free(rses->queue);
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
gw_mysql_set_byte3((unsigned char*)buffer->start,qlen+1);
|
||||
gwbuf_set_type(buffer,GWBUF_TYPE_MYSQL);
|
||||
*((unsigned char*)buffer->start + 3) = 0x0;
|
||||
*((unsigned char*)buffer->start + 4) = 0x2;
|
||||
memcpy(buffer->start+5,rses->connect_db,qlen);
|
||||
DCB* dcb = NULL;
|
||||
|
||||
SESSION_ROUTE_QUERY(subsvc->session,buffer);
|
||||
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
if(rses->queue)
|
||||
{
|
||||
GWBUF* tmp = rses->queue;
|
||||
rses->queue = rses->queue->next;
|
||||
tmp->next = NULL;
|
||||
char* querystr = modutil_get_SQL(tmp);
|
||||
skygw_log_write(LOGFILE_DEBUG,"schemarouter: Sending queued buffer for session %p: %s",
|
||||
rses->rses_client_dcb->session,
|
||||
querystr);
|
||||
poll_add_epollin_event_to_dcb(rses->routedcb,tmp);
|
||||
free(querystr);
|
||||
|
||||
}
|
||||
skygw_log_write_flush(LOGFILE_DEBUG,"session [%p] database map finished.",
|
||||
rses);
|
||||
}
|
||||
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
subsvc_clear_state(subsvc, SUBSVC_QUERY_ACTIVE);
|
||||
|
||||
if(!rses->hash_init)
|
||||
{
|
||||
subsvc_set_state(subsvc, SUBSVC_MAPPED);
|
||||
parse_mapping_response(rses, subsvc->service->name, reply);
|
||||
|
||||
for(i = 0; i < rses->n_subservice; i++)
|
||||
{
|
||||
if(SUBSVC_IS_OK(rses->subservice[i]) && !SUBSVC_IS_MAPPED(rses->subservice[i]))
|
||||
{
|
||||
mapped = false;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
gwbuf_free(reply);
|
||||
|
||||
if(mapped)
|
||||
{
|
||||
rses->hash_init = true;
|
||||
if(rses->queue)
|
||||
{
|
||||
tmp = rses->queue;
|
||||
rses->queue = rses->queue->next;
|
||||
}
|
||||
}
|
||||
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(rses->queue)
|
||||
{
|
||||
tmp = rses->queue;
|
||||
rses->queue = rses->queue->next;
|
||||
GWBUF* tmp = rses->queue;
|
||||
rses->queue = rses->queue->next;
|
||||
tmp->next = NULL;
|
||||
char* querystr = modutil_get_SQL(tmp);
|
||||
skygw_log_write(LOGFILE_DEBUG,"schemarouter: Sending queued buffer for session %p: %s",
|
||||
rses->rses_client_dcb->session,
|
||||
querystr);
|
||||
poll_add_epollin_event_to_dcb(rses->routedcb,tmp);
|
||||
free(querystr);
|
||||
tmp = NULL;
|
||||
}
|
||||
|
||||
if(rses->init & INIT_USE_DB)
|
||||
{
|
||||
skygw_log_write(LOGFILE_DEBUG,"schemarouter: Reply to USE '%s' received for session %p",
|
||||
rses->connect_db,
|
||||
rses->rses_client_dcb->session);
|
||||
rses->init &= ~INIT_USE_DB;
|
||||
strcpy(rses->rses_mysql_session->db,rses->connect_db);
|
||||
ss_dassert(rses->init == INIT_READY);
|
||||
if(reply)
|
||||
{
|
||||
tmp = reply;
|
||||
while((tmp = gwbuf_consume(tmp,gwbuf_length(tmp))));
|
||||
tmp = NULL;
|
||||
}
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
scur = subsvc->scur;
|
||||
|
||||
@ -688,12 +791,8 @@ filterReply(FILTER* instance, void *session, GWBUF *reply)
|
||||
|
||||
rv = SESSION_ROUTE_REPLY(rses->session, reply);
|
||||
|
||||
retblock:
|
||||
rses_end_locked_router_action(rses);
|
||||
if(tmp)
|
||||
{
|
||||
poll_add_epollin_event_to_dcb(rses->queue_dcb,tmp);
|
||||
}
|
||||
retblock:
|
||||
rses_end_locked_router_action(rses);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -843,7 +942,7 @@ static ROUTER *
|
||||
createInstance(SERVICE *service, char **options)
|
||||
{
|
||||
ROUTER_INSTANCE* router;
|
||||
char *services, *tok;
|
||||
char *services, *tok, *saveptr;
|
||||
SERVICE **res_svc, **temp;
|
||||
CONFIG_PARAMETER* conf;
|
||||
int i = 0, sz;
|
||||
@ -858,7 +957,6 @@ createInstance(SERVICE *service, char **options)
|
||||
spinlock_init(&router->lock);
|
||||
|
||||
conf = config_get_param(service->svc_config_param, "subservices");
|
||||
/*
|
||||
|
||||
if(conf == NULL)
|
||||
{
|
||||
@ -869,14 +967,20 @@ createInstance(SERVICE *service, char **options)
|
||||
}
|
||||
|
||||
services = strdup(conf->value);
|
||||
*/
|
||||
sz = 2;
|
||||
res_svc = calloc(sz, sizeof(SERVICE*));
|
||||
/*
|
||||
tok = strtok(services, ",");
|
||||
*/
|
||||
|
||||
while(options[i])
|
||||
if((res_svc = calloc(sz, sizeof(SERVICE*))) == NULL)
|
||||
{
|
||||
free(router);
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: Memory allocation failed.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tok = strtok_r(services, ",",&saveptr);
|
||||
|
||||
|
||||
|
||||
while(tok)
|
||||
{
|
||||
if(sz <= i)
|
||||
{
|
||||
@ -884,9 +988,9 @@ createInstance(SERVICE *service, char **options)
|
||||
if(temp == NULL)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR, "Error : Memory reallocation failed.");
|
||||
skygw_log_write(LOGFILE_DEBUG, "shardrouter.c: realloc returned NULL. "
|
||||
LOGIF(LD,(skygw_log_write(LOGFILE_DEBUG, "shardrouter.c: realloc returned NULL. "
|
||||
"service count[%d] buffer size [%u] tried to allocate [%u]",
|
||||
sz, sizeof(SERVICE*)*(sz), sizeof(SERVICE*)*(sz * 2));
|
||||
sz, sizeof(SERVICE*)*(sz), sizeof(SERVICE*)*(sz * 2))));
|
||||
free(res_svc);
|
||||
free(router);
|
||||
return NULL;
|
||||
@ -895,19 +999,27 @@ createInstance(SERVICE *service, char **options)
|
||||
res_svc = temp;
|
||||
}
|
||||
|
||||
res_svc[i] = service_find(options[i]);
|
||||
res_svc[i] = service_find(tok);
|
||||
if(res_svc[i] == NULL)
|
||||
{
|
||||
free(res_svc);
|
||||
free(router);
|
||||
skygw_log_write(LOGFILE_ERROR, "Error : No service named '%s' found.", options[i]);
|
||||
return NULL;
|
||||
}
|
||||
i++;
|
||||
tok = strtok_r(NULL,",",&saveptr);
|
||||
}
|
||||
/*
|
||||
|
||||
free(services);
|
||||
*/
|
||||
|
||||
|
||||
router->services = res_svc;
|
||||
router->n_services = i;
|
||||
|
||||
if(i < min_nsvc)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR, "Error : Not enough services. Shardrouter requires at least %d "
|
||||
skygw_log_write(LOGFILE_ERROR, "Error : Not enough parameters for 'subservice' router option. Shardrouter requires at least %d "
|
||||
"configured services to work.", min_nsvc);
|
||||
free(router->services);
|
||||
free(router);
|
||||
@ -980,15 +1092,15 @@ newSession(
|
||||
client_rses->rses_autocommit_enabled = true;
|
||||
client_rses->rses_transaction_active = false;
|
||||
client_rses->session = session;
|
||||
client_rses->dummy_dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER);
|
||||
client_rses->dummy_dcb->func.read = fakeReply;
|
||||
client_rses->dummy_dcb->state = DCB_STATE_POLLING;
|
||||
client_rses->dummy_dcb->session = session;
|
||||
client_rses->replydcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER);
|
||||
client_rses->replydcb->func.read = fakeReply;
|
||||
client_rses->replydcb->state = DCB_STATE_POLLING;
|
||||
client_rses->replydcb->session = session;
|
||||
|
||||
client_rses->queue_dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER);
|
||||
client_rses->queue_dcb->func.read = fakeQuery;
|
||||
client_rses->queue_dcb->state = DCB_STATE_POLLING;
|
||||
client_rses->queue_dcb->session = session;
|
||||
client_rses->routedcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER);
|
||||
client_rses->routedcb->func.read = fakeQuery;
|
||||
client_rses->routedcb->state = DCB_STATE_POLLING;
|
||||
client_rses->routedcb->session = session;
|
||||
|
||||
spinlock_init(&client_rses->rses_lock);
|
||||
|
||||
@ -1168,10 +1280,10 @@ closeSession(
|
||||
}
|
||||
router_cli_ses->subservice[i]->state = SUBSVC_CLOSED;
|
||||
}
|
||||
router_cli_ses->dummy_dcb->session = NULL;
|
||||
router_cli_ses->queue_dcb->session = NULL;
|
||||
dcb_close(router_cli_ses->dummy_dcb);
|
||||
dcb_close(router_cli_ses->queue_dcb);
|
||||
router_cli_ses->replydcb->session = NULL;
|
||||
router_cli_ses->routedcb->session = NULL;
|
||||
dcb_close(router_cli_ses->replydcb);
|
||||
dcb_close(router_cli_ses->routedcb);
|
||||
|
||||
/** Unlock */
|
||||
rses_end_locked_router_action(router_cli_ses);
|
||||
@ -1480,32 +1592,46 @@ routeQuery(ROUTER* instance,
|
||||
ret = 0;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
if(!router_cli_ses->hash_init)
|
||||
{
|
||||
gen_tablelist(inst, router_cli_ses);
|
||||
|
||||
skygw_log_write(LOGFILE_TRACE,"shardrouter: got a query while mapping databases.");
|
||||
GWBUF* tmp = router_cli_ses->queue;
|
||||
|
||||
while(tmp && tmp->next)
|
||||
if(!(rses_is_closed = router_cli_ses->rses_closed))
|
||||
{
|
||||
tmp = tmp->next;
|
||||
if(router_cli_ses->init & INIT_UNINT)
|
||||
{
|
||||
/* Generate database list */
|
||||
gen_subsvc_dblist(inst,router_cli_ses);
|
||||
|
||||
}
|
||||
|
||||
if(router_cli_ses->init & INIT_MAPPING)
|
||||
{
|
||||
|
||||
char* querystr = modutil_get_SQL(querybuf);
|
||||
skygw_log_write(LOGFILE_DEBUG,"shardrouter: Storing query for session %p: %s",
|
||||
router_cli_ses->rses_client_dcb->session,
|
||||
querystr);
|
||||
free(querystr);
|
||||
gwbuf_make_contiguous(querybuf);
|
||||
GWBUF* ptr = router_cli_ses->queue;
|
||||
|
||||
while(ptr && ptr->next)
|
||||
{
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
if(ptr == NULL)
|
||||
{
|
||||
router_cli_ses->queue = querybuf;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr->next = querybuf;
|
||||
|
||||
}
|
||||
rses_end_locked_router_action(router_cli_ses);
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(tmp == NULL)
|
||||
{
|
||||
router_cli_ses->queue = querybuf;
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp->next = querybuf;
|
||||
}
|
||||
|
||||
rses_end_locked_router_action(router_cli_ses);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
rses_end_locked_router_action(router_cli_ses);
|
||||
|
||||
packet = GWBUF_DATA(querybuf);
|
||||
@ -1608,7 +1734,7 @@ routeQuery(ROUTER* instance,
|
||||
*/
|
||||
|
||||
GWBUF* dbres = gen_show_dbs_response(inst,router_cli_ses);
|
||||
poll_add_epollin_event_to_dcb(router_cli_ses->dummy_dcb,dbres);
|
||||
poll_add_epollin_event_to_dcb(router_cli_ses->replydcb,dbres);
|
||||
ret = 1;
|
||||
goto retblock;
|
||||
}
|
||||
@ -2982,7 +3108,7 @@ reply_error:
|
||||
* Create an incoming event for randomly selected backend DCB which
|
||||
* will then be notified and replied 'back' to the client.
|
||||
*/
|
||||
poll_add_epollin_event_to_dcb(rses->dummy_dcb,
|
||||
poll_add_epollin_event_to_dcb(rses->replydcb,
|
||||
gwbuf_clone(errbuf));
|
||||
gwbuf_free(errbuf);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user