Merge branch 'develop-MXS-544-merge' into develop
This commit is contained in:
@ -1517,15 +1517,15 @@ GWBUF* gen_dummy_error(FW_SESSION* session, char* msg)
|
||||
unsigned int errlen;
|
||||
|
||||
if (session == NULL || session->session == NULL ||
|
||||
session->session->data == NULL ||
|
||||
session->session->client == NULL)
|
||||
session->session->client_dcb == NULL ||
|
||||
session->session->client_dcb->data == NULL)
|
||||
{
|
||||
MXS_ERROR("Firewall filter session missing data.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dcb = session->session->client;
|
||||
mysql_session = (MYSQL_session*) session->session->data;
|
||||
dcb = session->session->client_dcb;
|
||||
mysql_session = (MYSQL_session*) dcb->data;
|
||||
errlen = msg != NULL ? strlen(msg) : 0;
|
||||
errmsg = (char*) malloc((512 + errlen) * sizeof(char));
|
||||
|
||||
@ -2042,7 +2042,7 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue)
|
||||
{
|
||||
FW_SESSION *my_session = (FW_SESSION *) session;
|
||||
FW_INSTANCE *my_instance = (FW_INSTANCE *) instance;
|
||||
DCB *dcb = my_session->session->client;
|
||||
DCB *dcb = my_session->session->client_dcb;
|
||||
int rval = 0;
|
||||
ss_dassert(dcb && dcb->session);
|
||||
|
||||
@ -2057,9 +2057,7 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue)
|
||||
}
|
||||
else
|
||||
{
|
||||
USER *user = find_user_data(my_instance->htable,
|
||||
my_session->session->client->user,
|
||||
my_session->session->client->remote);
|
||||
USER *user = find_user_data(my_instance->htable, dcb->user, dcb->remote);
|
||||
bool query_ok = false;
|
||||
|
||||
if (user)
|
||||
|
@ -991,7 +991,7 @@ newSession(FILTER *instance, SESSION *session)
|
||||
my_session->was_query = false;
|
||||
my_session->uid = NULL;
|
||||
my_session->session = session;
|
||||
sessauth = my_session->session->data;
|
||||
sessauth = my_session->session->client_dcb->data;
|
||||
if (sessauth->db && strnlen(sessauth->db, 128) > 0)
|
||||
{
|
||||
my_session->db = strdup(sessauth->db);
|
||||
|
@ -251,7 +251,7 @@ orphan_free(void* data)
|
||||
*/
|
||||
|
||||
if (ptr->session->state == SESSION_STATE_STOPPING &&
|
||||
ptr->session->refcount == 0 && ptr->session->client == NULL)
|
||||
ptr->session->refcount == 0 && ptr->session->client_dcb == NULL)
|
||||
{
|
||||
ptr->session->state = SESSION_STATE_TO_BE_FREED;
|
||||
}
|
||||
@ -511,7 +511,7 @@ newSession(FILTER *instance, SESSION *session)
|
||||
my_session->active = 1;
|
||||
my_session->residual = 0;
|
||||
my_session->tee_replybuf = NULL;
|
||||
my_session->client_dcb = session->client;
|
||||
my_session->client_dcb = session->client_dcb;
|
||||
my_session->instance = my_instance;
|
||||
my_session->client_multistatement = false;
|
||||
my_session->queue = NULL;
|
||||
@ -544,7 +544,7 @@ newSession(FILTER *instance, SESSION *session)
|
||||
FILTER_DEF* dummy;
|
||||
UPSTREAM* dummy_upstream;
|
||||
|
||||
if ((dcb = dcb_clone(session->client)) == NULL)
|
||||
if ((dcb = dcb_clone(session->client_dcb)) == NULL)
|
||||
{
|
||||
freeSession(instance, (void *) my_session);
|
||||
my_session = NULL;
|
||||
@ -604,7 +604,7 @@ newSession(FILTER *instance, SESSION *session)
|
||||
}
|
||||
|
||||
ses->tail = *dummy_upstream;
|
||||
MySQLProtocol* protocol = (MySQLProtocol*) session->client->protocol;
|
||||
MySQLProtocol* protocol = (MySQLProtocol*) session->client_dcb->protocol;
|
||||
my_session->use_ok = protocol->client_capabilities & (1 << 6);
|
||||
free(dummy_upstream);
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ int dcbfun(struct dcb* dcb, GWBUF * buffer)
|
||||
int harness_init(int argc, char** argv, HARNESS_INSTANCE** inst){
|
||||
|
||||
|
||||
int i = 0,rval = 0;
|
||||
int i = 0,rval = 0;
|
||||
MYSQL_session* mysqlsess;
|
||||
DCB* dcb;
|
||||
char cwd[1024];
|
||||
@ -19,12 +19,12 @@ int harness_init(int argc, char** argv, HARNESS_INSTANCE** inst){
|
||||
if(!(argc == 2 && strcmp(argv[1],"-h") == 0)){
|
||||
mxs_log_init(NULL,NULL,MXS_LOG_TARGET_DEFAULT);
|
||||
}
|
||||
|
||||
|
||||
if(!(instance.head = calloc(1,sizeof(FILTERCHAIN))))
|
||||
{
|
||||
printf("Error: Out of memory\n");
|
||||
MXS_ERROR("Out of memory\n");
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -41,26 +41,26 @@ int harness_init(int argc, char** argv, HARNESS_INSTANCE** inst){
|
||||
mysqlsess = calloc(1,sizeof(MYSQL_session));
|
||||
|
||||
sprintf(mysqlsess->user,"dummyuser");
|
||||
sprintf(mysqlsess->db,"dummydb");
|
||||
sprintf(mysqlsess->db,"dummydb");
|
||||
dcb->func.write = dcbfun;
|
||||
dcb->remote = strdup("0.0.0.0");
|
||||
dcb->user = strdup("user");
|
||||
instance.session->client = (void*)dcb;
|
||||
instance.session->data = (void*)mysqlsess;
|
||||
instance.session->client_dcb = (void*)dcb;
|
||||
instance.session->client_dcb->data = (void*)mysqlsess;
|
||||
|
||||
getcwd(cwd,sizeof(cwd));
|
||||
sprintf(tmp,"%s",cwd);
|
||||
|
||||
mxs_log_init(NULL, tmp, MXS_LOG_TARGET_DEFAULT);
|
||||
|
||||
|
||||
rval = process_opts(argc,argv);
|
||||
|
||||
|
||||
if(!(instance.thrpool = malloc(instance.thrcount * sizeof(pthread_t)))){
|
||||
printf("Error: Out of memory\n");
|
||||
MXS_ERROR("Out of memory\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/**Initialize worker threads*/
|
||||
pthread_mutex_lock(&instance.work_mtx);
|
||||
size_t thr_num = 1;
|
||||
@ -100,14 +100,14 @@ void free_buffers()
|
||||
if(instance.buffer){
|
||||
int i;
|
||||
for(i = 0;i<instance.buffer_count;i++){
|
||||
gwbuf_free(instance.buffer[i]);
|
||||
gwbuf_free(instance.buffer[i]);
|
||||
}
|
||||
free(instance.buffer);
|
||||
instance.buffer = NULL;
|
||||
instance.buffer_count = 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
if(instance.infile >= 0){
|
||||
close(instance.infile);
|
||||
free(instance.infile_name);
|
||||
@ -167,7 +167,7 @@ FILTER_PARAMETER** read_params(int* paramc)
|
||||
}
|
||||
pc++;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if(pc >= 64){
|
||||
do_read = 0;
|
||||
@ -183,7 +183,7 @@ FILTER_PARAMETER** read_params(int* paramc)
|
||||
}
|
||||
free(names[i]);
|
||||
free(values[i]);
|
||||
}
|
||||
}
|
||||
params[pc] = NULL;
|
||||
*paramc = pc;
|
||||
}
|
||||
@ -207,7 +207,7 @@ int routeQuery(void* ins, void* session, GWBUF* queue)
|
||||
buffsz += strnlen(queue->hint->value,1024);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
qstr = calloc(buffsz + 1,sizeof(char));
|
||||
|
||||
if(qstr){
|
||||
@ -235,7 +235,7 @@ int routeQuery(void* ins, void* session, GWBUF* queue)
|
||||
case HINT_ROUTE_TO_ALL:
|
||||
sprintf(ptr,"|HINT_ROUTE_TO_ALL");
|
||||
break;
|
||||
|
||||
|
||||
case HINT_PARAMETER:
|
||||
sprintf(ptr,"|HINT_PARAMETER");
|
||||
break;
|
||||
@ -264,9 +264,9 @@ int routeQuery(void* ins, void* session, GWBUF* queue)
|
||||
}
|
||||
|
||||
if(instance.verbose){
|
||||
printf("Query endpoint: %s\n", qstr);
|
||||
printf("Query endpoint: %s\n", qstr);
|
||||
}
|
||||
|
||||
|
||||
if(instance.outfile>=0){
|
||||
write(instance.outfile,qstr,strlen(qstr));
|
||||
write(instance.outfile,"\n",1);
|
||||
@ -279,7 +279,7 @@ int routeQuery(void* ins, void* session, GWBUF* queue)
|
||||
|
||||
int clientReply(void* ins, void* session, GWBUF* queue)
|
||||
{
|
||||
|
||||
|
||||
if(instance.verbose){
|
||||
pthread_mutex_lock(&instance.work_mtx);
|
||||
unsigned char* ptr = (unsigned char*)queue->start;
|
||||
@ -291,13 +291,13 @@ int clientReply(void* ins, void* session, GWBUF* queue)
|
||||
printf("\n");
|
||||
pthread_mutex_unlock(&instance.work_mtx);
|
||||
}
|
||||
|
||||
|
||||
if(instance.outfile>=0){
|
||||
int qlen = queue->end - queue->start;
|
||||
write(instance.outfile,"Reply: ",strlen("Reply: "));
|
||||
write(instance.outfile,queue->start,qlen);
|
||||
write(instance.outfile,"\n",1);
|
||||
|
||||
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -313,7 +313,7 @@ int clientReply(void* ins, void* session, GWBUF* queue)
|
||||
int fdgets(int fd, char* buff, int size)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
|
||||
while(i < size - 1 && read(fd,&buff[i],1))
|
||||
{
|
||||
if(buff[i] == '\n' || buff[i] == '\0')
|
||||
@ -322,7 +322,7 @@ int fdgets(int fd, char* buff, int size)
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
|
||||
buff[i] = '\0';
|
||||
return i;
|
||||
}
|
||||
@ -365,24 +365,24 @@ int load_query()
|
||||
rval = 1;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
|
||||
query_list = tmpbuff;
|
||||
qbuff_sz *= 2;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
query_list[qcount] = calloc((offset + 1),sizeof(char));
|
||||
strcpy(query_list[qcount],buffer);
|
||||
offset = 0;
|
||||
qcount++;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**TODO check what messes up the first querystring*/
|
||||
GWBUF** tmpbff = malloc(sizeof(GWBUF*)*(qcount + 1));
|
||||
if(tmpbff){
|
||||
for(i = 0;i<qcount;i++){
|
||||
|
||||
|
||||
tmpbff[i] = gwbuf_alloc(strlen(query_list[i]) + 6);
|
||||
|
||||
if(tmpbff[i] == NULL)
|
||||
@ -413,7 +413,7 @@ int load_query()
|
||||
instance.buffer = tmpbff;
|
||||
}else{
|
||||
printf("Error: cannot allocate enough memory for buffers.\n");
|
||||
MXS_ERROR("cannot allocate enough memory for buffers.\n");
|
||||
MXS_ERROR("cannot allocate enough memory for buffers.\n");
|
||||
free_buffers();
|
||||
rval = 1;
|
||||
goto retblock;
|
||||
@ -423,7 +423,7 @@ int load_query()
|
||||
rval = 1;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
|
||||
instance.buffer_count = qcount;
|
||||
|
||||
retblock:
|
||||
@ -478,7 +478,7 @@ int handler(void* user, const char* section, const char* name,
|
||||
|
||||
/**Section not found, creating a new one*/
|
||||
if(iter == NULL){
|
||||
|
||||
|
||||
CONFIG* nxt = malloc(sizeof(CONFIG));
|
||||
if(nxt && (nxt->item = malloc(sizeof(CONFIG_ITEM)))){
|
||||
nxt->section = strdup(section);
|
||||
@ -576,7 +576,7 @@ int load_config( char* fname)
|
||||
while(iter){
|
||||
item = iter->item;
|
||||
while(item){
|
||||
|
||||
|
||||
if(!strcmp("module",item->name)){
|
||||
|
||||
if(instance.mod_dir){
|
||||
@ -600,7 +600,7 @@ int load_config( char* fname)
|
||||
|
||||
}else{
|
||||
if(instance.verbose){
|
||||
printf("\t%s\n",iter->section);
|
||||
printf("\t%s\n",iter->section);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -620,7 +620,7 @@ int load_config( char* fname)
|
||||
item = instance.conf->item;
|
||||
}
|
||||
instance.conf = instance.conf->next;
|
||||
|
||||
|
||||
}
|
||||
|
||||
cleanup:
|
||||
@ -629,7 +629,7 @@ int load_config( char* fname)
|
||||
instance.conf = instance.conf->next;
|
||||
item = iter->item;
|
||||
|
||||
while(item){
|
||||
while(item){
|
||||
free(item->name);
|
||||
free(item->value);
|
||||
free(item);
|
||||
@ -651,9 +651,9 @@ int load_filter(FILTERCHAIN* fc, CONFIG* cnf)
|
||||
int sess_err = 0;
|
||||
int x;
|
||||
if(cnf == NULL){
|
||||
|
||||
|
||||
fparams = read_params(¶mc);
|
||||
|
||||
|
||||
}else{
|
||||
|
||||
CONFIG* iter = cnf;
|
||||
@ -661,14 +661,14 @@ int load_filter(FILTERCHAIN* fc, CONFIG* cnf)
|
||||
while(iter){
|
||||
paramc = -1;
|
||||
item = iter->item;
|
||||
|
||||
|
||||
while(item){
|
||||
|
||||
/**Matching configuration found*/
|
||||
if(!strcmp(item->name,"module") && !strcmp(item->value,fc->name)){
|
||||
paramc = 0;
|
||||
item = iter->item;
|
||||
|
||||
|
||||
while(item){
|
||||
if(strcmp(item->name,"module") && strcmp(item->name,"type")){
|
||||
paramc++;
|
||||
@ -678,7 +678,7 @@ int load_filter(FILTERCHAIN* fc, CONFIG* cnf)
|
||||
item = iter->item;
|
||||
fparams = calloc((paramc + 1),sizeof(FILTER_PARAMETER*));
|
||||
if(fparams){
|
||||
|
||||
|
||||
int i = 0;
|
||||
while(item){
|
||||
if(strcmp(item->name,"module") != 0 &&
|
||||
@ -740,7 +740,7 @@ int load_filter(FILTERCHAIN* fc, CONFIG* cnf)
|
||||
MXS_WARNING("The filter %s does not support client replies.\n",fc->name);
|
||||
}
|
||||
|
||||
if(fc->next && fc->next->next){
|
||||
if(fc->next && fc->next->next){
|
||||
|
||||
fc->down[i]->routeQuery = (void*)fc->next->instance->routeQuery;
|
||||
fc->down[i]->session = fc->next->session[i];
|
||||
@ -775,7 +775,7 @@ int load_filter(FILTERCHAIN* fc, CONFIG* cnf)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if(sess_err){
|
||||
for(i = 0;i<instance.session_count;i++){
|
||||
if(fc->filter && fc->session[i]){
|
||||
@ -788,9 +788,9 @@ int load_filter(FILTERCHAIN* fc, CONFIG* cnf)
|
||||
free(fc->name);
|
||||
free(fc);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
error:
|
||||
error:
|
||||
|
||||
|
||||
if(fparams){
|
||||
@ -809,9 +809,9 @@ int load_filter(FILTERCHAIN* fc, CONFIG* cnf)
|
||||
FILTERCHAIN* load_filter_module(char* str)
|
||||
{
|
||||
FILTERCHAIN* flt_ptr = NULL;
|
||||
if((flt_ptr = calloc(1,sizeof(FILTERCHAIN))) != NULL &&
|
||||
if((flt_ptr = calloc(1,sizeof(FILTERCHAIN))) != NULL &&
|
||||
(flt_ptr->session = calloc(instance.session_count,sizeof(SESSION*))) != NULL &&
|
||||
(flt_ptr->down = calloc(instance.session_count,sizeof(DOWNSTREAM*))) != NULL &&
|
||||
(flt_ptr->down = calloc(instance.session_count,sizeof(DOWNSTREAM*))) != NULL &&
|
||||
(flt_ptr->up = calloc(instance.session_count,sizeof(UPSTREAM*))) != NULL){
|
||||
flt_ptr->next = instance.head;
|
||||
}
|
||||
@ -839,7 +839,7 @@ void route_buffers()
|
||||
fin = instance.buffer_count*instance.session_count,
|
||||
step = (fin/50.f)/fin;
|
||||
FILTERCHAIN* fc = instance.head;
|
||||
|
||||
|
||||
while(fc->next->next){
|
||||
fc = fc->next;
|
||||
}
|
||||
@ -866,7 +866,7 @@ void route_buffers()
|
||||
while(instance.last_ind < instance.session_count){
|
||||
struct timespec ts1;
|
||||
ts1.tv_sec = 0;
|
||||
|
||||
|
||||
tprg = ((bprg + (float)instance.last_ind)/fin);
|
||||
if(!instance.verbose){
|
||||
if(tprg >= trig){
|
||||
@ -883,7 +883,7 @@ void route_buffers()
|
||||
instance.sess_ind = 0;
|
||||
instance.last_ind = 0;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
if(!instance.verbose){
|
||||
@ -911,7 +911,7 @@ void work_buffer(void* thr_num)
|
||||
instance.buff_ind < instance.buffer_count)
|
||||
{
|
||||
struct timespec ts1;
|
||||
ts1.tv_sec = 0;
|
||||
ts1.tv_sec = 0;
|
||||
|
||||
if(instance.head->instance->routeQuery(instance.head->filter,
|
||||
|
||||
@ -954,7 +954,7 @@ GWBUF* gen_packet(PACKET pkt)
|
||||
if(psize > 0){
|
||||
buff = gwbuf_alloc(psize);
|
||||
ptr = (unsigned char*)buff->start;
|
||||
|
||||
|
||||
switch(pkt){
|
||||
case PACKET_OK:
|
||||
|
||||
@ -1007,8 +1007,8 @@ int process_opts(int argc, char** argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
if( (rval = lseek(fd,0,SEEK_END)) < 0 ||
|
||||
|
||||
if( (rval = lseek(fd,0,SEEK_END)) < 0 ||
|
||||
lseek(fd,0,SEEK_SET) < 0){
|
||||
printf("Error: Cannot seek file.\n");
|
||||
close(fd);
|
||||
@ -1033,9 +1033,9 @@ int process_opts(int argc, char** argv)
|
||||
}
|
||||
tok = strtok_r(NULL,"=",&saveptr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
free(buff);
|
||||
instance.verbose = 1;
|
||||
|
||||
@ -1114,7 +1114,7 @@ int process_opts(int argc, char** argv)
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
48
server/modules/include/mysql_auth.h
Normal file
48
server/modules/include/mysql_auth.h
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef _MYSQL_AUTH_H
|
||||
#define _MYSQL_AUTH_H
|
||||
/*
|
||||
* This file is distributed as part of the MariaDB Corporation MaxScale. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright MariaDB Corporation Ab 2013-2014
|
||||
*/
|
||||
|
||||
/*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 02/02/2016 Martin Brampton Initial implementation
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
#include <dcb.h>
|
||||
#include <buffer.h>
|
||||
#include <stdint.h>
|
||||
#include <mysql_client_server_protocol.h>
|
||||
|
||||
int mysql_auth_set_protocol_data(DCB *dcb, GWBUF *buf);
|
||||
bool mysql_auth_is_client_ssl_capable (DCB *dcb);
|
||||
int mysql_auth_authenticate(DCB *dcb, GWBUF **buf);
|
||||
int gw_check_mysql_scramble_data(DCB *dcb,
|
||||
uint8_t *token,
|
||||
unsigned int token_len,
|
||||
uint8_t *scramble,
|
||||
unsigned int scramble_len,
|
||||
char *username,
|
||||
uint8_t *stage1_hash);
|
||||
int check_db_name_after_auth(DCB *dcb, char *database, int auth_ret);
|
||||
|
||||
#endif /** _MYSQL_AUTH_H */
|
@ -33,9 +33,10 @@
|
||||
* and repository to gw_check_mysql_scramble_data()
|
||||
* It's now possible to specify a different users' table than
|
||||
* dcb->service->users default
|
||||
* 26-02-2014 Massimiliano Pinto Removed previouvsly added parameters to gw_check_mysql_scramble_data() and
|
||||
* 26-02-2014 Massimiliano Pinto Removed previously added parameters to gw_check_mysql_scramble_data() and
|
||||
* gw_find_mysql_user_password_sha1()
|
||||
* 28-02-2014 Massimiliano Pinto MYSQL_DATABASE_MAXLEN,MYSQL_USER_MAXLEN moved to dbusers.h
|
||||
* 07-02-2016 Martin Brampton Extend MYSQL_session type; add MYSQL_AUTH_SUCCEEDED
|
||||
*
|
||||
*/
|
||||
|
||||
@ -96,18 +97,27 @@
|
||||
#define COM_QUIT_PACKET_SIZE (4+1)
|
||||
struct dcb;
|
||||
|
||||
#define MYSQL_AUTH_SUCCEEDED 0
|
||||
#define MYSQL_FAILED_AUTH 1
|
||||
#define MYSQL_FAILED_AUTH_DB 2
|
||||
#define MYSQL_FAILED_AUTH_SSL 3
|
||||
#define MYSQL_AUTH_SSL_INCOMPLETE 4
|
||||
#define MYSQL_AUTH_NO_SESSION 5
|
||||
|
||||
typedef enum {
|
||||
MYSQL_ALLOC,
|
||||
MYSQL_ALLOC, /* Initial state of protocol auth state */
|
||||
/* The following are used only for backend connections */
|
||||
MYSQL_PENDING_CONNECT,
|
||||
MYSQL_CONNECTED,
|
||||
/* The following can be used for either client or backend */
|
||||
/* The comments have only been checked for client use at present */
|
||||
MYSQL_AUTH_SENT,
|
||||
MYSQL_AUTH_RECV,
|
||||
MYSQL_AUTH_FAILED,
|
||||
MYSQL_AUTH_RECV, /* This is only ever a transient value */
|
||||
MYSQL_AUTH_FAILED, /* Once this is set, the connection */
|
||||
/* will be ended, so this is transient */
|
||||
/* The following is used only for backend connections */
|
||||
MYSQL_HANDSHAKE_FAILED,
|
||||
/* The following are obsolete and will be removed */
|
||||
MYSQL_AUTH_SSL_REQ, /*< client requested SSL but SSL_accept hasn't beed called */
|
||||
MYSQL_AUTH_SSL_HANDSHAKE_DONE, /*< SSL handshake has been fully completed */
|
||||
MYSQL_AUTH_SSL_HANDSHAKE_FAILED, /*< SSL handshake failed for any reason */
|
||||
@ -131,9 +141,11 @@ typedef struct mysql_session {
|
||||
#if defined(SS_DEBUG)
|
||||
skygw_chk_t myses_chk_top;
|
||||
#endif
|
||||
uint8_t client_sha1[MYSQL_SCRAMBLE_LEN]; /*< SHA1(passowrd) */
|
||||
uint8_t client_sha1[MYSQL_SCRAMBLE_LEN]; /*< SHA1(password) */
|
||||
char user[MYSQL_USER_MAXLEN+1]; /*< username */
|
||||
char db[MYSQL_DATABASE_MAXLEN+1]; /*< database */
|
||||
int auth_token_len; /*< token length */
|
||||
uint8_t *auth_token; /*< token */
|
||||
#if defined(SS_DEBUG)
|
||||
skygw_chk_t myses_chk_tail;
|
||||
#endif
|
||||
@ -243,30 +255,30 @@ typedef enum mysql_server_cmd {
|
||||
MYSQL_COM_INIT_DB,
|
||||
MYSQL_COM_QUERY,
|
||||
MYSQL_COM_FIELD_LIST,
|
||||
MYSQL_COM_CREATE_DB,
|
||||
MYSQL_COM_CREATE_DB,
|
||||
MYSQL_COM_DROP_DB,
|
||||
MYSQL_COM_REFRESH,
|
||||
MYSQL_COM_SHUTDOWN,
|
||||
MYSQL_COM_REFRESH,
|
||||
MYSQL_COM_SHUTDOWN,
|
||||
MYSQL_COM_STATISTICS,
|
||||
MYSQL_COM_PROCESS_INFO,
|
||||
MYSQL_COM_CONNECT,
|
||||
MYSQL_COM_PROCESS_KILL,
|
||||
MYSQL_COM_DEBUG,
|
||||
MYSQL_COM_PROCESS_INFO,
|
||||
MYSQL_COM_CONNECT,
|
||||
MYSQL_COM_PROCESS_KILL,
|
||||
MYSQL_COM_DEBUG,
|
||||
MYSQL_COM_PING,
|
||||
MYSQL_COM_TIME,
|
||||
MYSQL_COM_DELAYED_INSERT,
|
||||
MYSQL_COM_CHANGE_USER,
|
||||
MYSQL_COM_TIME,
|
||||
MYSQL_COM_DELAYED_INSERT,
|
||||
MYSQL_COM_CHANGE_USER,
|
||||
MYSQL_COM_BINLOG_DUMP,
|
||||
MYSQL_COM_TABLE_DUMP,
|
||||
MYSQL_COM_CONNECT_OUT,
|
||||
MYSQL_COM_TABLE_DUMP,
|
||||
MYSQL_COM_CONNECT_OUT,
|
||||
MYSQL_COM_REGISTER_SLAVE,
|
||||
MYSQL_COM_STMT_PREPARE,
|
||||
MYSQL_COM_STMT_EXECUTE,
|
||||
MYSQL_COM_STMT_SEND_LONG_DATA,
|
||||
MYSQL_COM_STMT_PREPARE,
|
||||
MYSQL_COM_STMT_EXECUTE,
|
||||
MYSQL_COM_STMT_SEND_LONG_DATA,
|
||||
MYSQL_COM_STMT_CLOSE,
|
||||
MYSQL_COM_STMT_RESET,
|
||||
MYSQL_COM_SET_OPTION,
|
||||
MYSQL_COM_STMT_FETCH,
|
||||
MYSQL_COM_STMT_RESET,
|
||||
MYSQL_COM_SET_OPTION,
|
||||
MYSQL_COM_STMT_FETCH,
|
||||
MYSQL_COM_DAEMON,
|
||||
MYSQL_COM_END /*< Must be the last */
|
||||
} mysql_server_cmd_t;
|
||||
@ -274,9 +286,9 @@ typedef enum mysql_server_cmd {
|
||||
|
||||
static const mysql_server_cmd_t MYSQL_COM_UNDEFINED = (mysql_server_cmd_t)-1;
|
||||
|
||||
/**
|
||||
/**
|
||||
* List of server commands, and number of response packets are stored here.
|
||||
* server_command_t is used in MySQLProtocol structure, so for each DCB there is
|
||||
* server_command_t is used in MySQLProtocol structure, so for each DCB there is
|
||||
* one MySQLProtocol and one server command list.
|
||||
*/
|
||||
typedef struct server_command_st {
|
||||
@ -288,8 +300,8 @@ typedef struct server_command_st {
|
||||
|
||||
/**
|
||||
* MySQL Protocol specific state data.
|
||||
*
|
||||
* Protocol carries information from client side to backend side, such as
|
||||
*
|
||||
* Protocol carries information from client side to backend side, such as
|
||||
* MySQL session command information and history of earlier session commands.
|
||||
*/
|
||||
typedef struct {
|
||||
@ -299,7 +311,7 @@ typedef struct {
|
||||
int fd; /*< The socket descriptor */
|
||||
struct dcb *owner_dcb; /*< The DCB of the socket
|
||||
* we are running on */
|
||||
SPINLOCK protocol_lock;
|
||||
SPINLOCK protocol_lock;
|
||||
server_command_t protocol_command; /*< session command list */
|
||||
server_command_t* protocol_cmd_history; /*< session command history */
|
||||
mysql_auth_state_t protocol_auth_state; /*< Authentication status */
|
||||
@ -313,7 +325,6 @@ typedef struct {
|
||||
unsigned long tid; /*< MySQL Thread ID, in
|
||||
* handshake */
|
||||
unsigned int charset; /*< MySQL character set at connect time */
|
||||
bool use_ssl;
|
||||
#if defined(SS_DEBUG)
|
||||
skygw_chk_t protocol_chk_tail;
|
||||
#endif
|
||||
@ -358,7 +369,7 @@ int mysql_send_custom_error (
|
||||
const char* mysql_message);
|
||||
|
||||
GWBUF* mysql_create_custom_error(
|
||||
int packet_number,
|
||||
int packet_number,
|
||||
int affected_rows,
|
||||
const char* msg);
|
||||
|
||||
@ -376,14 +387,6 @@ int gw_find_mysql_user_password_sha1(
|
||||
char *username,
|
||||
uint8_t *gateway_password,
|
||||
DCB *dcb);
|
||||
int gw_check_mysql_scramble_data(
|
||||
DCB *dcb,
|
||||
uint8_t *token,
|
||||
unsigned int token_len,
|
||||
uint8_t *scramble,
|
||||
unsigned int scramble_len,
|
||||
char *username,
|
||||
uint8_t *stage1_hash);
|
||||
int mysql_send_auth_error (
|
||||
DCB *dcb,
|
||||
int packet_number,
|
||||
@ -423,9 +426,9 @@ void protocol_archive_srv_command(MySQLProtocol* p);
|
||||
|
||||
|
||||
void init_response_status (
|
||||
GWBUF* buf,
|
||||
mysql_server_cmd_t cmd,
|
||||
int* npackets,
|
||||
GWBUF* buf,
|
||||
mysql_server_cmd_t cmd,
|
||||
int* npackets,
|
||||
ssize_t* nbytes);
|
||||
|
||||
#endif /** _MYSQL_PROTOCOL_H */
|
||||
|
@ -1,9 +1,9 @@
|
||||
add_library(MySQLClient SHARED mysql_client.c mysql_common.c)
|
||||
add_library(MySQLClient SHARED mysql_client.c mysql_common.c mysql_auth.c)
|
||||
target_link_libraries(MySQLClient maxscale-common)
|
||||
set_target_properties(MySQLClient PROPERTIES VERSION "1.0.0")
|
||||
install(TARGETS MySQLClient DESTINATION ${MAXSCALE_LIBDIR})
|
||||
|
||||
add_library(MySQLBackend SHARED mysql_backend.c mysql_common.c)
|
||||
add_library(MySQLBackend SHARED mysql_backend.c mysql_common.c mysql_auth.c)
|
||||
target_link_libraries(MySQLBackend maxscale-common)
|
||||
set_target_properties(MySQLBackend PROPERTIES VERSION "2.0.0")
|
||||
install(TARGETS MySQLBackend DESTINATION ${MAXSCALE_LIBDIR})
|
||||
|
@ -38,6 +38,7 @@
|
||||
*/
|
||||
|
||||
#include <httpd.h>
|
||||
#include <gw_protocol.h>
|
||||
#include <gw.h>
|
||||
#include <modinfo.h>
|
||||
#include <log_manager.h>
|
||||
@ -356,6 +357,7 @@ static int httpd_accept(DCB *dcb)
|
||||
|
||||
if ((client = dcb_alloc(DCB_ROLE_REQUEST_HANDLER)))
|
||||
{
|
||||
client->listen_ssl = dcb->listen_ssl;
|
||||
client->fd = so;
|
||||
client->remote = strdup(inet_ntoa(addr.sin_addr));
|
||||
memcpy(&client->func, &MyObject, sizeof(GWPROTOCOL));
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <string.h>
|
||||
#include <dcb.h>
|
||||
#include <buffer.h>
|
||||
#include <gw_protocol.h>
|
||||
#include <service.h>
|
||||
#include <session.h>
|
||||
#include <sys/ioctl.h>
|
||||
@ -267,6 +268,7 @@ static int maxscaled_accept(DCB *dcb)
|
||||
close(so);
|
||||
return n_connect;
|
||||
}
|
||||
client_dcb->listen_ssl = dcb->listen_ssl;
|
||||
client_dcb->fd = so;
|
||||
client_dcb->remote = strdup(inet_ntoa(addr.sin_addr));
|
||||
memcpy(&client_dcb->func, &MyObject, sizeof(GWPROTOCOL));
|
||||
|
552
server/modules/protocol/mysql_auth.c
Normal file
552
server/modules/protocol/mysql_auth.c
Normal file
@ -0,0 +1,552 @@
|
||||
/*
|
||||
* This file is distributed as part of the MariaDB Corporation MaxScale. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright MariaDB Corporation Ab 2013-2014
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file mysql_auth.c
|
||||
*
|
||||
* MySQL Authentication module for handling the checking of clients credentials
|
||||
* in the MySQL protocol.
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
* Date Who Description
|
||||
* 02/02/2016 Martin Brampton Initial version
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
#include <mysql_auth.h>
|
||||
#include <mysql_client_server_protocol.h>
|
||||
|
||||
static int combined_auth_check(
|
||||
DCB *dcb,
|
||||
uint8_t *auth_token,
|
||||
size_t auth_token_len,
|
||||
MySQLProtocol *protocol,
|
||||
char *username,
|
||||
uint8_t *stage1_hash,
|
||||
char *database
|
||||
);
|
||||
static int mysql_auth_set_client_data(
|
||||
MYSQL_session *client_data,
|
||||
MySQLProtocol *protocol,
|
||||
uint8_t *client_auth_packet,
|
||||
int client_auth_packet_size);
|
||||
|
||||
/**
|
||||
* @brief Authenticates a MySQL user who is a client to MaxScale.
|
||||
*
|
||||
* First call the SSL authentication function, passing the DCB and a boolean
|
||||
* indicating whether the client is SSL capable. If SSL authentication is
|
||||
* successful, check whether connection is complete. Fail if we do not have a
|
||||
* user name. Call other functions to validate the user, reloading the user
|
||||
* data if the first attempt fails.
|
||||
*
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @param buffer Pointer to pointer to buffer containing data from client
|
||||
* @return Authentication status
|
||||
* @note Authentication status codes are defined in mysql_client_server_protocol.h
|
||||
*/
|
||||
int
|
||||
mysql_auth_authenticate(DCB *dcb, GWBUF **buffer)
|
||||
{
|
||||
MySQLProtocol *protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
|
||||
MYSQL_session *client_data = (MYSQL_session *)dcb->data;
|
||||
int auth_ret, ssl_ret;
|
||||
|
||||
if (0 != (ssl_ret = ssl_authenticate_client(dcb, client_data->user, mysql_auth_is_client_ssl_capable(dcb))))
|
||||
{
|
||||
auth_ret = (SSL_ERROR_CLIENT_NOT_SSL == ssl_ret) ? MYSQL_FAILED_AUTH_SSL : MYSQL_FAILED_AUTH;
|
||||
}
|
||||
|
||||
else if (!ssl_is_connection_healthy(dcb))
|
||||
{
|
||||
auth_ret = MYSQL_AUTH_SSL_INCOMPLETE;
|
||||
}
|
||||
|
||||
else if (0 == strlen(client_data->user))
|
||||
{
|
||||
auth_ret = MYSQL_FAILED_AUTH;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
MXS_DEBUG("Receiving connection from '%s' to database '%s'.",
|
||||
client_data->user, client_data->db);
|
||||
|
||||
auth_ret = combined_auth_check(dcb, client_data->auth_token, client_data->auth_token_len,
|
||||
protocol, client_data->user, client_data->client_sha1, client_data->db);
|
||||
|
||||
/* On failed authentication try to load user table from backend database */
|
||||
/* Success for service_refresh_users returns 0 */
|
||||
if (MYSQL_AUTH_SUCCEEDED != auth_ret && 0 == service_refresh_users(dcb->service))
|
||||
{
|
||||
auth_ret = combined_auth_check(dcb, client_data->auth_token, client_data->auth_token_len, protocol,
|
||||
client_data->user, client_data->client_sha1, client_data->db);
|
||||
}
|
||||
|
||||
/* on successful authentication, set user into dcb field */
|
||||
if (MYSQL_AUTH_SUCCEEDED == auth_ret)
|
||||
{
|
||||
dcb->user = strdup(client_data->user);
|
||||
}
|
||||
else if (dcb->service->log_auth_warnings)
|
||||
{
|
||||
MXS_NOTICE("%s: login attempt for user '%s', authentication failed.",
|
||||
dcb->service->name, client_data->user);
|
||||
if (dcb->ipv4.sin_addr.s_addr == 0x0100007F &&
|
||||
!dcb->service->localhost_match_wildcard_host)
|
||||
{
|
||||
MXS_NOTICE("If you have a wildcard grant that covers"
|
||||
" this address, try adding "
|
||||
"'localhost_match_wildcard_host=true' for "
|
||||
"service '%s'. ", dcb->service->name);
|
||||
}
|
||||
}
|
||||
|
||||
/* let's free the auth_token now */
|
||||
if (client_data->auth_token)
|
||||
{
|
||||
free(client_data->auth_token);
|
||||
client_data->auth_token = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return auth_ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transfer data from the authentication request to the DCB.
|
||||
*
|
||||
* The request handler DCB has a field called data that contains protocol
|
||||
* specific information. This function examines a buffer containing MySQL
|
||||
* authentication data and puts it into a structure that is referred to
|
||||
* by the DCB. If the information in the buffer is invalid, then a failure
|
||||
* code is returned. A call to mysql_auth_set_client_data does the
|
||||
* detailed work.
|
||||
*
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @param buffer Pointer to pointer to buffer containing data from client
|
||||
* @return Authentication status
|
||||
* @note Authentication status codes are defined in mysql_client_server_protocol.h
|
||||
* @see https://dev.mysql.com/doc/internals/en/client-server-protocol.html
|
||||
*/
|
||||
int
|
||||
mysql_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
||||
{
|
||||
uint8_t *client_auth_packet = GWBUF_DATA(buf);
|
||||
MySQLProtocol *protocol = NULL;
|
||||
MYSQL_session *client_data = NULL;
|
||||
int client_auth_packet_size = 0;
|
||||
|
||||
protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
|
||||
CHK_PROTOCOL(protocol);
|
||||
if (dcb->data == NULL)
|
||||
{
|
||||
if (NULL == (client_data = (MYSQL_session *)calloc(1, sizeof(MYSQL_session))))
|
||||
{
|
||||
return MYSQL_FAILED_AUTH;
|
||||
}
|
||||
#if defined(SS_DEBUG)
|
||||
client_data->myses_chk_top = CHK_NUM_MYSQLSES;
|
||||
client_data->myses_chk_tail = CHK_NUM_MYSQLSES;
|
||||
#endif
|
||||
dcb->data = client_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
client_data = (MYSQL_session *)dcb->data;
|
||||
}
|
||||
|
||||
client_auth_packet_size = gwbuf_length(buf);
|
||||
|
||||
/* For clients supporting CLIENT_PROTOCOL_41
|
||||
* the Handshake Response Packet is:
|
||||
*
|
||||
* 4 bytes mysql protocol heade
|
||||
* 4 bytes capability flags
|
||||
* 4 max-packet size
|
||||
* 1 byte character set
|
||||
* string[23] reserved (all [0])
|
||||
* ...
|
||||
* ...
|
||||
* Note that the fixed elements add up to 36
|
||||
*/
|
||||
|
||||
/* Detect now if there are enough bytes to continue */
|
||||
if (client_auth_packet_size < (4 + 4 + 4 + 1 + 23))
|
||||
{
|
||||
/* Packet is not big enough */
|
||||
return MYSQL_FAILED_AUTH;
|
||||
}
|
||||
|
||||
return mysql_auth_set_client_data(client_data, protocol, client_auth_packet,
|
||||
client_auth_packet_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transfer detailed data from the authentication request to the DCB.
|
||||
*
|
||||
* The caller has created the data structure pointed to by the DCB, and this
|
||||
* function fills in the details. If problems are found with the data, the
|
||||
* return code indicates failure.
|
||||
*
|
||||
* @param client_data The data structure for the DCB
|
||||
* @param protocol The protocol structure for this connection
|
||||
* @param client_auth_packet The data from the buffer received from client
|
||||
* @param client_auth_packet size An integer giving the size of the data
|
||||
* @return Authentication status
|
||||
* @note Authentication status codes are defined in mysql_client_server_protocol.h
|
||||
* @see https://dev.mysql.com/doc/internals/en/client-server-protocol.html
|
||||
*/
|
||||
static int
|
||||
mysql_auth_set_client_data(
|
||||
MYSQL_session *client_data,
|
||||
MySQLProtocol *protocol,
|
||||
uint8_t *client_auth_packet,
|
||||
int client_auth_packet_size)
|
||||
{
|
||||
/* The numbers are the fixed elements in the client handshake packet */
|
||||
int auth_packet_base_size = 4 + 4 + 4 + 1 + 23;
|
||||
int packet_length_used = 0;
|
||||
|
||||
/* Take data from fixed locations first */
|
||||
memcpy(&protocol->client_capabilities, client_auth_packet + 4, 4);
|
||||
protocol->charset = 0;
|
||||
memcpy(&protocol->charset, client_auth_packet + 4 + 4 + 4, 1);
|
||||
|
||||
/* Make username and database a null string in case none is provided */
|
||||
client_data->user[0] = 0;
|
||||
client_data->db[0] = 0;
|
||||
/* Make authentication token length 0 and token null in case none is provided */
|
||||
client_data->auth_token_len = 0;
|
||||
client_data->auth_token = NULL;
|
||||
|
||||
if (client_auth_packet_size > auth_packet_base_size)
|
||||
{
|
||||
/* Should have a username */
|
||||
char *first_letter_of_username = (char *)(client_auth_packet + auth_packet_base_size);
|
||||
int user_length = strlen(first_letter_of_username);
|
||||
if (client_auth_packet_size > (auth_packet_base_size + user_length)
|
||||
&& user_length <= MYSQL_USER_MAXLEN)
|
||||
{
|
||||
strcpy(client_data->user, first_letter_of_username);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Packet has incomplete or too long username */
|
||||
return MYSQL_FAILED_AUTH;
|
||||
}
|
||||
if (client_auth_packet_size > (auth_packet_base_size + user_length + 1))
|
||||
{
|
||||
/* Extra 1 is for the terminating null after user name */
|
||||
packet_length_used = auth_packet_base_size + user_length + 1;
|
||||
/* We should find an authentication token next */
|
||||
/* One byte of packet is the length of authentication token */
|
||||
memcpy(&client_data->auth_token_len,
|
||||
client_auth_packet + packet_length_used,
|
||||
1);
|
||||
if (client_auth_packet_size >
|
||||
(packet_length_used + client_data->auth_token_len))
|
||||
{
|
||||
/* Packet is large enough for authentication token */
|
||||
if (NULL != (client_data->auth_token = (uint8_t *)malloc(client_data->auth_token_len)))
|
||||
{
|
||||
/* The extra 1 is for the token length byte, just extracted*/
|
||||
memcpy(client_data->auth_token,
|
||||
client_auth_packet + auth_packet_base_size + user_length + 1 +1,
|
||||
client_data->auth_token_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Failed to allocate space for authentication token string */
|
||||
return MYSQL_FAILED_AUTH;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Packet was too small to contain authentication token */
|
||||
return MYSQL_FAILED_AUTH;
|
||||
}
|
||||
packet_length_used += 1 + client_data->auth_token_len;
|
||||
/*
|
||||
* Note: some clients may pass empty database, CONNECT_WITH_DB !=0 but database =""
|
||||
*/
|
||||
if (GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB &
|
||||
gw_mysql_get_byte4((uint32_t *)&protocol->client_capabilities)
|
||||
&& client_auth_packet_size > packet_length_used)
|
||||
{
|
||||
char *database = (char *)(client_auth_packet + packet_length_used);
|
||||
int database_length = strlen(database);
|
||||
if (client_auth_packet_size >
|
||||
(packet_length_used + database_length)
|
||||
&& strlen(database) <= MYSQL_DATABASE_MAXLEN)
|
||||
{
|
||||
strcpy(client_data->db, database);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Packet is too short to contain database string */
|
||||
/* or database string in packet is too long */
|
||||
return MYSQL_FAILED_AUTH;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return MYSQL_AUTH_SUCCEEDED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determine whether the client is SSL capable
|
||||
*
|
||||
* The authentication request from the client will indicate whether the client
|
||||
* is expecting to make an SSL connection. The information has been extracted
|
||||
* in the previous functions.
|
||||
*
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @return Boolean indicating whether client is SSL capable
|
||||
*/
|
||||
bool
|
||||
mysql_auth_is_client_ssl_capable(DCB *dcb)
|
||||
{
|
||||
MySQLProtocol *protocol;
|
||||
|
||||
protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
|
||||
return (protocol->client_capabilities & GW_MYSQL_CAPABILITIES_SSL) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Check authentication token received against stage1_hash and scramble
|
||||
*
|
||||
* @param dcb The current dcb
|
||||
* @param token The token sent by the client in the authentication request
|
||||
* @param token_len The token size in bytes
|
||||
* @param scramble The scramble data sent by the server during handshake
|
||||
* @param scramble_len The scramble size in bytes
|
||||
* @param username The current username in the authentication request
|
||||
* @param stage1_hash The SHA1(candidate_password) decoded by this routine
|
||||
* @return Authentication status
|
||||
* @note Authentication status codes are defined in mysql_client_server_protocol.h
|
||||
*
|
||||
*/
|
||||
int
|
||||
gw_check_mysql_scramble_data(DCB *dcb,
|
||||
uint8_t *token,
|
||||
unsigned int token_len,
|
||||
uint8_t *scramble,
|
||||
unsigned int scramble_len,
|
||||
char *username,
|
||||
uint8_t *stage1_hash)
|
||||
{
|
||||
uint8_t step1[GW_MYSQL_SCRAMBLE_SIZE]="";
|
||||
uint8_t step2[GW_MYSQL_SCRAMBLE_SIZE +1]="";
|
||||
uint8_t check_hash[GW_MYSQL_SCRAMBLE_SIZE]="";
|
||||
char hex_double_sha1[2 * GW_MYSQL_SCRAMBLE_SIZE + 1]="";
|
||||
uint8_t password[GW_MYSQL_SCRAMBLE_SIZE]="";
|
||||
/* The following can be compared using memcmp to detect a null password */
|
||||
uint8_t null_client_sha1[MYSQL_SCRAMBLE_LEN]="";
|
||||
|
||||
|
||||
if ((username == NULL) || (scramble == NULL) || (stage1_hash == NULL))
|
||||
{
|
||||
return MYSQL_FAILED_AUTH;
|
||||
}
|
||||
|
||||
/*<
|
||||
* get the user's password from repository in SHA1(SHA1(real_password));
|
||||
* please note 'real_password' is unknown!
|
||||
*/
|
||||
|
||||
if (gw_find_mysql_user_password_sha1(username, password, dcb))
|
||||
{
|
||||
/* if password was sent, fill stage1_hash with at least 1 byte in order
|
||||
* to create right error message: (using password: YES|NO)
|
||||
*/
|
||||
if (token_len)
|
||||
memcpy(stage1_hash, (char *)"_", 1);
|
||||
|
||||
return MYSQL_FAILED_AUTH;
|
||||
}
|
||||
|
||||
if (token && token_len)
|
||||
{
|
||||
/*<
|
||||
* convert in hex format: this is the content of mysql.user table.
|
||||
* The field password is without the '*' prefix and it is 40 bytes long
|
||||
*/
|
||||
|
||||
gw_bin2hex(hex_double_sha1, password, SHA_DIGEST_LENGTH);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check if the password is not set in the user table */
|
||||
return memcmp(password, null_client_sha1, MYSQL_SCRAMBLE_LEN) ?
|
||||
MYSQL_FAILED_AUTH : MYSQL_AUTH_SUCCEEDED;
|
||||
}
|
||||
|
||||
/*<
|
||||
* Auth check in 3 steps
|
||||
*
|
||||
* Note: token = XOR (SHA1(real_password), SHA1(CONCAT(scramble, SHA1(SHA1(real_password)))))
|
||||
* the client sends token
|
||||
*
|
||||
* Now, server side:
|
||||
*
|
||||
*
|
||||
* step 1: compute the STEP1 = SHA1(CONCAT(scramble, gateway_password))
|
||||
* the result in step1 is SHA_DIGEST_LENGTH long
|
||||
*/
|
||||
|
||||
gw_sha1_2_str(scramble, scramble_len, password, SHA_DIGEST_LENGTH, step1);
|
||||
|
||||
/*<
|
||||
* step2: STEP2 = XOR(token, STEP1)
|
||||
*
|
||||
* token is transmitted form client and it's based on the handshake scramble and SHA1(real_passowrd)
|
||||
* step1 has been computed in the previous step
|
||||
* the result STEP2 is SHA1(the_password_to_check) and is SHA_DIGEST_LENGTH long
|
||||
*/
|
||||
|
||||
gw_str_xor(step2, token, step1, token_len);
|
||||
|
||||
/*<
|
||||
* copy the stage1_hash back to the caller
|
||||
* stage1_hash will be used for backend authentication
|
||||
*/
|
||||
|
||||
memcpy(stage1_hash, step2, SHA_DIGEST_LENGTH);
|
||||
|
||||
/*<
|
||||
* step 3: prepare the check_hash
|
||||
*
|
||||
* compute the SHA1(STEP2) that is SHA1(SHA1(the_password_to_check)), and is SHA_DIGEST_LENGTH long
|
||||
*/
|
||||
|
||||
gw_sha1_str(step2, SHA_DIGEST_LENGTH, check_hash);
|
||||
|
||||
|
||||
#ifdef GW_DEBUG_CLIENT_AUTH
|
||||
{
|
||||
char inpass[128]="";
|
||||
gw_bin2hex(inpass, check_hash, SHA_DIGEST_LENGTH);
|
||||
|
||||
fprintf(stderr, "The CLIENT hex(SHA1(SHA1(password))) for \"%s\" is [%s]", username, inpass);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* now compare SHA1(SHA1(gateway_password)) and check_hash: return 0 is MYSQL_AUTH_OK */
|
||||
return (0 == memcmp(password, check_hash, SHA_DIGEST_LENGTH)) ?
|
||||
MYSQL_AUTH_SUCCEEDED : MYSQL_FAILED_AUTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief If the client connection specifies a database, check existence
|
||||
*
|
||||
* The client can specify a default database, but if so, it must be one
|
||||
* that exists. This function is chained from the previous one, and will
|
||||
* amend the given return code if it is previously showing success.
|
||||
*
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @param database A string containing the database name
|
||||
* @param auth_ret The authentication status prior to calling this function.
|
||||
* @return Authentication status
|
||||
* @note Authentication status codes are defined in mysql_client_server_protocol.h
|
||||
*/
|
||||
int
|
||||
check_db_name_after_auth(DCB *dcb, char *database, int auth_ret)
|
||||
{
|
||||
int db_exists = -1;
|
||||
|
||||
/* check for database name and possible match in resource hashtable */
|
||||
if (database && strlen(database))
|
||||
{
|
||||
/* if database names are loaded we can check if db name exists */
|
||||
if (dcb->service->resources != NULL)
|
||||
{
|
||||
if (hashtable_fetch(dcb->service->resources, database))
|
||||
{
|
||||
db_exists = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
db_exists = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if database names are not loaded we don't allow connection with db name*/
|
||||
db_exists = -1;
|
||||
}
|
||||
|
||||
if (db_exists == 0 && auth_ret == MYSQL_AUTH_SUCCEEDED)
|
||||
{
|
||||
auth_ret = MYSQL_FAILED_AUTH_DB;
|
||||
}
|
||||
|
||||
if (db_exists < 0 && auth_ret == MYSQL_AUTH_SUCCEEDED)
|
||||
{
|
||||
auth_ret = MYSQL_FAILED_AUTH;
|
||||
}
|
||||
}
|
||||
|
||||
return auth_ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function to easily call authentication and database checks.
|
||||
*
|
||||
* The two functions are called one after the other, with the return from
|
||||
* the first passed to the second. For convenience and clarity this function
|
||||
* combines the calls.
|
||||
*
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @param auth_token A string of bytes containing the authentication token
|
||||
* @param auth_token_len An integer, the length of the preceding parameter
|
||||
* @param protocol The protocol structure for the connection
|
||||
* @param username String containing username
|
||||
* @param stage1_hash A password hash for authentication
|
||||
* @param database A string containing the name for the default database
|
||||
* @return Authentication status
|
||||
* @note Authentication status codes are defined in mysql_client_server_protocol.h
|
||||
*/
|
||||
static int combined_auth_check(
|
||||
DCB *dcb,
|
||||
uint8_t *auth_token,
|
||||
size_t auth_token_len,
|
||||
MySQLProtocol *protocol,
|
||||
char *username,
|
||||
uint8_t *stage1_hash,
|
||||
char *database
|
||||
)
|
||||
{
|
||||
int auth_ret;
|
||||
|
||||
auth_ret = gw_check_mysql_scramble_data(dcb,
|
||||
auth_token,
|
||||
auth_token_len,
|
||||
protocol->scramble,
|
||||
sizeof(protocol->scramble),
|
||||
username,
|
||||
stage1_hash);
|
||||
|
||||
/* check for database name match in resource hashtable */
|
||||
auth_ret = check_db_name_after_auth(dcb, database, auth_ret);
|
||||
return auth_ret;
|
||||
}
|
||||
|
@ -50,6 +50,8 @@
|
||||
*
|
||||
*/
|
||||
#include <modinfo.h>
|
||||
#include <gw_protocol.h>
|
||||
#include <mysql_auth.h>
|
||||
|
||||
MODULE_INFO info = {
|
||||
MODULE_API_PROTOCOL,
|
||||
@ -142,7 +144,7 @@ static bool gw_get_shared_session_auth_info(DCB* dcb, MYSQL_session* session)
|
||||
if (dcb->session->state != SESSION_STATE_ALLOC &&
|
||||
dcb->session->state != SESSION_STATE_DUMMY)
|
||||
{
|
||||
memcpy(session, dcb->session->data, sizeof(MYSQL_session));
|
||||
memcpy(session, dcb->session->client_dcb->data, sizeof(MYSQL_session));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -174,7 +176,7 @@ static int gw_read_backend_event(DCB *dcb)
|
||||
goto return_rc;
|
||||
}
|
||||
|
||||
if (dcb->session == NULL)
|
||||
if (dcb->dcb_is_zombie || dcb->session == NULL)
|
||||
{
|
||||
goto return_rc;
|
||||
}
|
||||
@ -554,8 +556,8 @@ static int gw_read_backend_event(DCB *dcb)
|
||||
* still listening the socket for replies.
|
||||
*/
|
||||
if (dcb->session->state == SESSION_STATE_ROUTER_READY &&
|
||||
dcb->session->client != NULL &&
|
||||
dcb->session->client->state == DCB_STATE_POLLING &&
|
||||
dcb->session->client_dcb != NULL &&
|
||||
dcb->session->client_dcb->state == DCB_STATE_POLLING &&
|
||||
(session->router_session || router->getCapabilities() & RCAP_TYPE_NO_RSESSION))
|
||||
{
|
||||
client_protocol = SESSION_PROTOCOL(dcb->session,
|
||||
@ -576,7 +578,7 @@ static int gw_read_backend_event(DCB *dcb)
|
||||
}
|
||||
goto return_rc;
|
||||
}
|
||||
else if (dcb->session->client->dcb_role == DCB_ROLE_INTERNAL)
|
||||
else if (dcb->session->client_dcb->dcb_role == DCB_ROLE_INTERNAL)
|
||||
{
|
||||
gwbuf_set_type(read_buffer, GWBUF_TYPE_MYSQL);
|
||||
router->clientReply(router_instance, session->router_session, read_buffer, dcb);
|
||||
@ -619,14 +621,14 @@ static int gw_write_backend_event(DCB *dcb)
|
||||
{
|
||||
data = (uint8_t *) GWBUF_DATA(dcb->writeq);
|
||||
|
||||
if (dcb->session->client == NULL)
|
||||
if (dcb->session->client_dcb == NULL)
|
||||
{
|
||||
rc = 0;
|
||||
}
|
||||
else if (!(MYSQL_IS_COM_QUIT(data)))
|
||||
{
|
||||
/*< vraa : errorHandle */
|
||||
mysql_send_custom_error(dcb->session->client,
|
||||
mysql_send_custom_error(dcb->session->client_dcb,
|
||||
1,
|
||||
0,
|
||||
"Writing to backend failed due invalid Maxscale "
|
||||
@ -942,14 +944,14 @@ static int gw_create_backend_connection(DCB *backend_dcb,
|
||||
}
|
||||
|
||||
/** Copy client flags to backend protocol */
|
||||
if (backend_dcb->session->client->protocol)
|
||||
if (backend_dcb->session->client_dcb->protocol)
|
||||
{
|
||||
/** Copy client flags to backend protocol */
|
||||
protocol->client_capabilities =
|
||||
((MySQLProtocol *) (backend_dcb->session->client->protocol))->client_capabilities;
|
||||
((MySQLProtocol *)(backend_dcb->session->client_dcb->protocol))->client_capabilities;
|
||||
/** Copy client charset to backend protocol */
|
||||
protocol->charset =
|
||||
((MySQLProtocol *) (backend_dcb->session->client->protocol))->charset;
|
||||
((MySQLProtocol *)(backend_dcb->session->client_dcb->protocol))->charset;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -976,7 +978,7 @@ static int gw_create_backend_connection(DCB *backend_dcb,
|
||||
server->name,
|
||||
server->port,
|
||||
protocol->fd,
|
||||
session->client->fd);
|
||||
session->client_dcb->fd);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
@ -989,7 +991,7 @@ static int gw_create_backend_connection(DCB *backend_dcb,
|
||||
server->name,
|
||||
server->port,
|
||||
protocol->fd,
|
||||
session->client->fd);
|
||||
session->client_dcb->fd);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -1001,7 +1003,7 @@ static int gw_create_backend_connection(DCB *backend_dcb,
|
||||
server->name,
|
||||
server->port,
|
||||
protocol->fd,
|
||||
session->client->fd);
|
||||
session->client_dcb->fd);
|
||||
break;
|
||||
} /*< switch */
|
||||
|
||||
@ -1152,7 +1154,7 @@ static int gw_backend_close(DCB *dcb)
|
||||
CHK_SESSION(session);
|
||||
/**
|
||||
* The lock is needed only to protect the read of session->state and
|
||||
* session->client values. Client's state may change by other thread
|
||||
* session->client_dcb values. Client's state may change by other thread
|
||||
* but client's close and adding client's DCB to zombies list is executed
|
||||
* only if client's DCB's state does _not_ change in parallel.
|
||||
*/
|
||||
@ -1162,17 +1164,15 @@ static int gw_backend_close(DCB *dcb)
|
||||
* Otherwise only this backend connection is closed.
|
||||
*/
|
||||
if (session->state == SESSION_STATE_STOPPING &&
|
||||
session->client != NULL)
|
||||
session->client_dcb != NULL)
|
||||
{
|
||||
if (session->client->state == DCB_STATE_POLLING)
|
||||
if (session->client_dcb->state == DCB_STATE_POLLING)
|
||||
{
|
||||
DCB *temp;
|
||||
spinlock_release(&session->ses_lock);
|
||||
|
||||
/** Close client DCB */
|
||||
temp = session->client;
|
||||
session->client = NULL;
|
||||
dcb_close(temp);
|
||||
dcb_close(session->client_dcb);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1330,9 +1330,9 @@ static int gw_change_user(DCB *backend,
|
||||
int rv = -1;
|
||||
int auth_ret = 1;
|
||||
|
||||
current_session = (MYSQL_session *)in_session->client->data;
|
||||
current_session = (MYSQL_session *)in_session->client_dcb->data;
|
||||
backend_protocol = backend->protocol;
|
||||
client_protocol = in_session->client->protocol;
|
||||
client_protocol = in_session->client_dcb->protocol;
|
||||
|
||||
/* now get the user, after 4 bytes header and 1 byte command */
|
||||
client_auth_packet += 5;
|
||||
@ -1391,7 +1391,7 @@ static int gw_change_user(DCB *backend,
|
||||
* Decode the token and check the password.
|
||||
* Note: if auth_token_len == 0 && auth_token == NULL, user is without password
|
||||
*/
|
||||
auth_ret = gw_check_mysql_scramble_data(backend->session->client,
|
||||
auth_ret = gw_check_mysql_scramble_data(backend->session->client_dcb,
|
||||
auth_token, auth_token_len,
|
||||
client_protocol->scramble,
|
||||
sizeof(client_protocol->scramble),
|
||||
@ -1401,14 +1401,14 @@ static int gw_change_user(DCB *backend,
|
||||
|
||||
if (auth_ret != 0)
|
||||
{
|
||||
if (service_refresh_users(backend->session->client->service) == 0)
|
||||
if (service_refresh_users(backend->session->client_dcb->service) == 0)
|
||||
{
|
||||
/* Try authentication again with new repository data */
|
||||
/* Note: if no auth client authentication will fail */
|
||||
spinlock_acquire(&in_session->ses_lock);
|
||||
strncpy(current_session->db, "", MYSQL_DATABASE_MAXLEN);
|
||||
auth_ret = gw_check_mysql_scramble_data(
|
||||
backend->session->client,
|
||||
backend->session->client_dcb,
|
||||
auth_token, auth_token_len,
|
||||
client_protocol->scramble,
|
||||
sizeof(client_protocol->scramble),
|
||||
@ -1447,7 +1447,7 @@ static int gw_change_user(DCB *backend,
|
||||
* reply to the client.
|
||||
*/
|
||||
message = create_auth_fail_str(username,
|
||||
backend->session->client->remote,
|
||||
backend->session->client_dcb->remote,
|
||||
password_set,
|
||||
"",
|
||||
auth_ret);
|
||||
|
@ -46,10 +46,14 @@
|
||||
* 09/09/2015 Martin Brampton Modify error handler calls
|
||||
* 11/01/2016 Martin Brampton Remove SSL write code, now handled at lower level;
|
||||
* replace gwbuf_consume by gwbuf_free (multiple).
|
||||
* 07/02/2016 Martin Brampton Split off authentication and SSL.
|
||||
*/
|
||||
#include <gw_protocol.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <mysql_client_server_protocol.h>
|
||||
#include <mysql_auth.h>
|
||||
#include <gw_ssl.h>
|
||||
#include <maxscale/poll.h>
|
||||
#include <gw.h>
|
||||
#include <modinfo.h>
|
||||
@ -75,17 +79,12 @@ static int gw_MySQLWrite_client(DCB *dcb, GWBUF *queue);
|
||||
static int gw_error_client_event(DCB *dcb);
|
||||
static int gw_client_close(DCB *dcb);
|
||||
static int gw_client_hangup_event(DCB *dcb);
|
||||
int gw_read_client_event_SSL(DCB* dcb);
|
||||
int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message);
|
||||
int MySQLSendHandshake(DCB* dcb);
|
||||
static int gw_mysql_do_authentication(DCB *dcb, GWBUF **queue);
|
||||
static int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message);
|
||||
static int MySQLSendHandshake(DCB* dcb);
|
||||
static int route_by_statement(SESSION *, GWBUF **);
|
||||
extern char* get_username_from_auth(char* ptr, uint8_t* data);
|
||||
extern int check_db_name_after_auth(DCB *, char *, int);
|
||||
static void mysql_client_auth_error_handling(DCB *dcb, int auth_val);
|
||||
extern char* create_auth_fail_str(char *username, char *hostaddr, char *sha1, char *db,int);
|
||||
|
||||
int do_ssl_accept(MySQLProtocol* protocol);
|
||||
|
||||
/*
|
||||
* The "module object" for the mysqld client protocol module.
|
||||
*/
|
||||
@ -336,14 +335,10 @@ int MySQLSendHandshake(DCB* dcb)
|
||||
|
||||
mysql_server_capabilities_one[0] &= ~GW_MYSQL_CAPABILITIES_COMPRESS;
|
||||
|
||||
if (dcb->service->ssl_mode != SSL_DISABLED)
|
||||
if (ssl_required_by_dcb(dcb))
|
||||
{
|
||||
mysql_server_capabilities_one[1] |= GW_MYSQL_CAPABILITIES_SSL >> 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
mysql_server_capabilities_one[0] &= ~GW_MYSQL_CAPABILITIES_SSL;
|
||||
}
|
||||
|
||||
memcpy(mysql_handshake_payload, mysql_server_capabilities_one, sizeof(mysql_server_capabilities_one));
|
||||
mysql_handshake_payload = mysql_handshake_payload + sizeof(mysql_server_capabilities_one);
|
||||
@ -396,246 +391,6 @@ int MySQLSendHandshake(DCB* dcb)
|
||||
return sizeof(mysql_packet_header) + mysql_payload_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* gw_mysql_do_authentication
|
||||
*
|
||||
* Performs the MySQL protocol 4.1 authentication, using data in GWBUF **queue.
|
||||
*
|
||||
* (MYSQL_session*)client_data including: user, db, client_sha1 are copied into
|
||||
* the dcb->data and later to dcb->session->data. client_capabilities are copied
|
||||
* into the dcb->protocol.
|
||||
*
|
||||
* If SSL is enabled for the service, the SSL handshake will be done before the
|
||||
* MySQL authentication.
|
||||
*
|
||||
* @param dcb Descriptor Control Block of the client
|
||||
* @param queue Pointer to the location of the GWBUF with data from client
|
||||
* @return 0 If succeed, otherwise non-zero value
|
||||
*
|
||||
* @note in case of failure, dcb->data is freed before returning. If succeed,
|
||||
* dcb->data is freed in session.c:session_free.
|
||||
*/
|
||||
static int gw_mysql_do_authentication(DCB *dcb, GWBUF **buf)
|
||||
{
|
||||
GWBUF* queue = *buf;
|
||||
MySQLProtocol *protocol = NULL;
|
||||
/* int compress = -1; */
|
||||
int connect_with_db = -1;
|
||||
uint8_t *client_auth_packet = GWBUF_DATA(queue);
|
||||
int client_auth_packet_size = 0;
|
||||
char *username = NULL;
|
||||
char *database = NULL;
|
||||
unsigned int auth_token_len = 0;
|
||||
uint8_t *auth_token = NULL;
|
||||
uint8_t *stage1_hash = NULL;
|
||||
int auth_ret = -1;
|
||||
MYSQL_session *client_data = NULL;
|
||||
int ssl = 0;
|
||||
CHK_DCB(dcb);
|
||||
|
||||
protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
|
||||
CHK_PROTOCOL(protocol);
|
||||
if (dcb->data == NULL)
|
||||
{
|
||||
client_data = (MYSQL_session *)calloc(1, sizeof(MYSQL_session));
|
||||
#if defined(SS_DEBUG)
|
||||
client_data->myses_chk_top = CHK_NUM_MYSQLSES;
|
||||
client_data->myses_chk_tail = CHK_NUM_MYSQLSES;
|
||||
#endif
|
||||
dcb->data = client_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
client_data = (MYSQL_session *)dcb->data;
|
||||
}
|
||||
|
||||
stage1_hash = client_data->client_sha1;
|
||||
username = client_data->user;
|
||||
|
||||
client_auth_packet_size = gwbuf_length(queue);
|
||||
|
||||
/* For clients supporting CLIENT_PROTOCOL_41
|
||||
* the Handshake Response Packet is:
|
||||
*
|
||||
* 4 bytes mysql protocol heade
|
||||
* 4 bytes capability flags
|
||||
* 4 max-packet size
|
||||
* 1 byte character set
|
||||
* string[23] reserved (all [0])
|
||||
* ...
|
||||
* ...
|
||||
*/
|
||||
|
||||
/* Detect now if there are enough bytes to continue */
|
||||
if (client_auth_packet_size < (4 + 4 + 4 + 1 + 23))
|
||||
{
|
||||
return MYSQL_FAILED_AUTH;
|
||||
}
|
||||
|
||||
memcpy(&protocol->client_capabilities, client_auth_packet + 4, 4);
|
||||
|
||||
connect_with_db =
|
||||
GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB &
|
||||
gw_mysql_get_byte4((uint32_t *)&protocol->client_capabilities);
|
||||
/*
|
||||
compress =
|
||||
GW_MYSQL_CAPABILITIES_COMPRESS & gw_mysql_get_byte4(
|
||||
&protocol->client_capabilities);
|
||||
*/
|
||||
|
||||
/** Skip this if the SSL handshake is already done.
|
||||
* If not, start the SSL handshake. */
|
||||
if (protocol->protocol_auth_state != MYSQL_AUTH_SSL_HANDSHAKE_DONE)
|
||||
{
|
||||
ssl = protocol->client_capabilities & GW_MYSQL_CAPABILITIES_SSL;
|
||||
|
||||
/** Client didn't requested SSL when SSL mode was required*/
|
||||
if (!ssl && protocol->owner_dcb->service->ssl_mode == SSL_REQUIRED)
|
||||
{
|
||||
MXS_INFO("User %s@%s connected to service '%s' without SSL when SSL was required.",
|
||||
protocol->owner_dcb->user,
|
||||
protocol->owner_dcb->remote,
|
||||
protocol->owner_dcb->service->name);
|
||||
return MYSQL_FAILED_AUTH_SSL;
|
||||
}
|
||||
|
||||
if (MXS_LOG_PRIORITY_IS_ENABLED(LOG_INFO) && ssl)
|
||||
{
|
||||
MXS_INFO("User %s@%s connected to service '%s' with SSL.",
|
||||
protocol->owner_dcb->user,
|
||||
protocol->owner_dcb->remote,
|
||||
protocol->owner_dcb->service->name);
|
||||
}
|
||||
|
||||
/** Do the SSL Handshake */
|
||||
if (ssl && protocol->owner_dcb->service->ssl_mode != SSL_DISABLED)
|
||||
{
|
||||
protocol->protocol_auth_state = MYSQL_AUTH_SSL_REQ;
|
||||
|
||||
if (do_ssl_accept(protocol) < 0)
|
||||
{
|
||||
return MYSQL_FAILED_AUTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (dcb->service->ssl_mode == SSL_ENABLED)
|
||||
{
|
||||
/** This is a non-SSL connection to a SSL enabled service.
|
||||
* We have only read enough of the packet to know that the client
|
||||
* is not requesting SSL and the rest of the auth packet is still
|
||||
* waiting in the socket. We need to read the data from the socket
|
||||
* to find out the username of the connecting client. */
|
||||
int bytes = dcb_read(dcb,&queue, 0);
|
||||
queue = gwbuf_make_contiguous(queue);
|
||||
client_auth_packet = GWBUF_DATA(queue);
|
||||
client_auth_packet_size = gwbuf_length(queue);
|
||||
*buf = queue;
|
||||
MXS_DEBUG("%lu Read %d bytes from fd %d",pthread_self(),bytes,dcb->fd);
|
||||
}
|
||||
}
|
||||
|
||||
username = get_username_from_auth(username, client_auth_packet);
|
||||
|
||||
if (username == NULL)
|
||||
{
|
||||
return MYSQL_FAILED_AUTH;
|
||||
}
|
||||
|
||||
/* get charset */
|
||||
memcpy(&protocol->charset, client_auth_packet + 4 + 4 + 4, sizeof (int));
|
||||
|
||||
/* get the auth token len */
|
||||
memcpy(&auth_token_len,
|
||||
client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + 1,
|
||||
1);
|
||||
|
||||
/*
|
||||
* Note: some clients may pass empty database, connect_with_db !=0 but database =""
|
||||
*/
|
||||
if (connect_with_db)
|
||||
{
|
||||
database = client_data->db;
|
||||
strncpy(database,
|
||||
(char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) +
|
||||
1 + 1 + auth_token_len), MYSQL_DATABASE_MAXLEN);
|
||||
}
|
||||
|
||||
/* allocate memory for token only if auth_token_len > 0 */
|
||||
if (auth_token_len)
|
||||
{
|
||||
auth_token = (uint8_t *)malloc(auth_token_len);
|
||||
memcpy(auth_token,
|
||||
client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + 1 + 1,
|
||||
auth_token_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode the token and check the password
|
||||
* Note: if auth_token_len == 0 && auth_token == NULL, user is without password
|
||||
*/
|
||||
MXS_DEBUG("Receiving connection from '%s' to database '%s'.",username,database);
|
||||
auth_ret = gw_check_mysql_scramble_data(dcb,
|
||||
auth_token,
|
||||
auth_token_len,
|
||||
protocol->scramble,
|
||||
sizeof(protocol->scramble),
|
||||
username,
|
||||
stage1_hash);
|
||||
|
||||
/* check for database name match in resource hashtable */
|
||||
auth_ret = check_db_name_after_auth(dcb, database, auth_ret);
|
||||
|
||||
/* On failed auth try to load users' table from backend database */
|
||||
if (auth_ret != 0)
|
||||
{
|
||||
if (!service_refresh_users(dcb->service))
|
||||
{
|
||||
/* Try authentication again with new repository data */
|
||||
/* Note: if no auth client authentication will fail */
|
||||
auth_ret = gw_check_mysql_scramble_data(dcb,
|
||||
auth_token,
|
||||
auth_token_len,
|
||||
protocol->scramble,
|
||||
sizeof(protocol->scramble),
|
||||
username,
|
||||
stage1_hash);
|
||||
|
||||
/* Do again the database check */
|
||||
auth_ret = check_db_name_after_auth(dcb, database, auth_ret);
|
||||
}
|
||||
}
|
||||
|
||||
/* on succesful auth set user into dcb field */
|
||||
if (auth_ret == 0)
|
||||
{
|
||||
dcb->user = strdup(client_data->user);
|
||||
}
|
||||
else if (dcb->service->log_auth_warnings)
|
||||
{
|
||||
MXS_NOTICE("%s: login attempt for user '%s', authentication failed.",
|
||||
dcb->service->name, username);
|
||||
if (dcb->ipv4.sin_addr.s_addr == 0x0100007F &&
|
||||
!dcb->service->localhost_match_wildcard_host)
|
||||
{
|
||||
MXS_NOTICE("If you have a wildcard grant that covers"
|
||||
" this address, try adding "
|
||||
"'localhost_match_wildcard_host=true' for "
|
||||
"service '%s'. ", dcb->service->name);
|
||||
}
|
||||
}
|
||||
|
||||
/* let's free the auth_token now */
|
||||
if (auth_token)
|
||||
{
|
||||
free(auth_token);
|
||||
}
|
||||
|
||||
return auth_ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write function for client DCB: writes data from MaxScale to Client
|
||||
*
|
||||
@ -648,23 +403,18 @@ int gw_MySQLWrite_client(DCB *dcb, GWBUF *queue)
|
||||
}
|
||||
|
||||
/**
|
||||
* Client read event triggered by EPOLLIN
|
||||
* @brief Client read event triggered by EPOLLIN
|
||||
*
|
||||
* @param dcb Descriptor control block
|
||||
* @return 0 if succeed, 1 otherwise
|
||||
*/
|
||||
int gw_read_client_event(DCB* dcb)
|
||||
{
|
||||
SESSION *session = NULL;
|
||||
ROUTER_OBJECT *router = NULL;
|
||||
ROUTER *router_instance = NULL;
|
||||
void *rsession = NULL;
|
||||
MySQLProtocol *protocol = NULL;
|
||||
GWBUF *read_buffer = NULL;
|
||||
int rc = 0;
|
||||
int nbytes_read = 0;
|
||||
uint8_t cap = 0;
|
||||
bool stmt_input = false; /*< router input type */
|
||||
int max_bytes = 0;
|
||||
|
||||
CHK_DCB(dcb);
|
||||
protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
|
||||
@ -676,73 +426,149 @@ int gw_read_client_event(DCB* dcb)
|
||||
|
||||
#endif
|
||||
|
||||
/** SSL authentication is still going on, we need to call do_ssl_accept
|
||||
* until it return 1 for success or -1 for error */
|
||||
if (protocol->protocol_auth_state == MYSQL_AUTH_SSL_HANDSHAKE_ONGOING ||
|
||||
protocol->protocol_auth_state == MYSQL_AUTH_SSL_REQ)
|
||||
/**
|
||||
* The use of max_bytes seems like a hack, but no better option is available
|
||||
* at the time of writing. When a MySQL server receives a new connection
|
||||
* request, it sends an Initial Handshake Packet. Where the client wants to
|
||||
* use SSL, it responds with an SSL Request Packet (in place of a Handshake
|
||||
* Response Packet). The SSL Request Packet contains only the basic header,
|
||||
* and not the user credentials. It is 36 bytes long. The server then
|
||||
* initiates the SSL handshake (via calls to OpenSSL).
|
||||
*
|
||||
* In many cases, this is what happens. But occasionally, the client seems
|
||||
* to send a packet much larger than 36 bytes (in tests it was 333 bytes).
|
||||
* If the whole of the packet is read, it is then lost to the SSL handshake
|
||||
* process. Why this happens is presently unknown. Reading just 36 bytes
|
||||
* when the server requires SSL and SSL has not yet been negotiated seems
|
||||
* to solve the problem.
|
||||
*
|
||||
* If a neater solution can be found, so much the better.
|
||||
*/
|
||||
if (ssl_required_but_not_negotiated(dcb))
|
||||
{
|
||||
|
||||
switch(do_ssl_accept(protocol))
|
||||
{
|
||||
case 0:
|
||||
return 0;
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
int b = 0;
|
||||
ioctl(dcb->fd,FIONREAD,&b);
|
||||
if (b == 0)
|
||||
{
|
||||
MXS_DEBUG("[gw_read_client_event] No data in socket after SSL auth");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case -1:
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
max_bytes = 36;
|
||||
}
|
||||
|
||||
if (protocol->use_ssl)
|
||||
{
|
||||
/** SSL handshake is done, communication is now encrypted with SSL */
|
||||
rc = dcb_read_SSL(dcb, &read_buffer);
|
||||
}
|
||||
else if (dcb->service->ssl_mode != SSL_DISABLED &&
|
||||
protocol->protocol_auth_state == MYSQL_AUTH_SENT)
|
||||
{
|
||||
/** The service allows both SSL and non-SSL connections.
|
||||
* read only enough of the auth packet to know if the client is
|
||||
* requesting SSL. If the client is not requesting SSL the rest of
|
||||
the auth packet will be read later. */
|
||||
rc = dcb_read(dcb, &read_buffer,(4 + 4 + 4 + 1 + 23));
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Normal non-SSL connection */
|
||||
rc = dcb_read(dcb, &read_buffer, 0);
|
||||
}
|
||||
|
||||
rc = dcb_read(dcb, &read_buffer, max_bytes);
|
||||
if (rc < 0)
|
||||
{
|
||||
dcb_close(dcb);
|
||||
}
|
||||
nbytes_read = gwbuf_length(read_buffer);
|
||||
|
||||
if (nbytes_read == 0)
|
||||
if (0 == (nbytes_read = gwbuf_length(read_buffer)))
|
||||
{
|
||||
goto return_rc;
|
||||
}
|
||||
|
||||
session = dcb->session;
|
||||
switch (protocol->protocol_auth_state)
|
||||
{
|
||||
/**
|
||||
*
|
||||
* When a listener receives a new connection request, it creates a
|
||||
* request handler DCB to for the client connection. The listener also
|
||||
* sends the initial authentication request to the client. The first
|
||||
* time this function is called from the poll loop, the client reply
|
||||
* to the authentication request should be available.
|
||||
*
|
||||
* If the authentication is successful the protocol authentication state
|
||||
* will be changed to MYSQL_IDLE (see below).
|
||||
*
|
||||
*/
|
||||
case MYSQL_AUTH_SENT:
|
||||
{
|
||||
/* int compress = -1; */
|
||||
int auth_val, packet_number;
|
||||
MySQLProtocol *protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
|
||||
|
||||
if (protocol->protocol_auth_state == MYSQL_IDLE && session != NULL &&
|
||||
SESSION_STATE_DUMMY != session->state)
|
||||
packet_number = ssl_required_by_dcb(dcb) ? 3 : 2;
|
||||
|
||||
/**
|
||||
* The first step in the authentication process is to extract the
|
||||
* relevant information from the buffer supplied and place it
|
||||
* into a data structure pointed to by the DCB. The "success"
|
||||
* result is not final, it implies only that the process is so
|
||||
* far successful, not that authentication has completed. If the
|
||||
* data extraction succeeds, then a call is made to
|
||||
* mysql_auth_authenticate to carry out the actual user checks.
|
||||
*/
|
||||
if (MYSQL_AUTH_SUCCEEDED == (
|
||||
auth_val = mysql_auth_set_protocol_data(dcb, read_buffer)))
|
||||
{
|
||||
/*
|
||||
compress =
|
||||
GW_MYSQL_CAPABILITIES_COMPRESS & gw_mysql_get_byte4(
|
||||
&protocol->client_capabilities);
|
||||
*/
|
||||
auth_val = mysql_auth_authenticate(dcb, &read_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* At this point, if the auth_val return code indicates success
|
||||
* the user authentication has been successfully completed.
|
||||
* But in order to have a working connection, a session has to
|
||||
* be created. Provided that is successful (indicated by a
|
||||
* non-null session) then the whole process has succeeded. In all
|
||||
* other cases an error return is made.
|
||||
*/
|
||||
if (MYSQL_AUTH_SUCCEEDED == auth_val)
|
||||
{
|
||||
SESSION *session;
|
||||
|
||||
protocol->protocol_auth_state = MYSQL_AUTH_RECV;
|
||||
/**
|
||||
* Create session, and a router session for it.
|
||||
* If successful, there will be backend connection(s)
|
||||
* after this point.
|
||||
*/
|
||||
session = session_alloc(dcb->service, dcb);
|
||||
|
||||
if (session != NULL)
|
||||
{
|
||||
CHK_SESSION(session);
|
||||
ss_dassert(session->state != SESSION_STATE_ALLOC &&
|
||||
session->state != SESSION_STATE_DUMMY);
|
||||
|
||||
protocol->protocol_auth_state = MYSQL_IDLE;
|
||||
/**
|
||||
* Send an AUTH_OK packet to the client,
|
||||
* packet sequence is # packet_number
|
||||
*/
|
||||
mysql_send_ok(dcb, packet_number, 0, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
auth_val = MYSQL_AUTH_NO_SESSION;
|
||||
}
|
||||
}
|
||||
if (MYSQL_AUTH_SUCCEEDED != auth_val && MYSQL_AUTH_SSL_INCOMPLETE != auth_val)
|
||||
{
|
||||
protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
|
||||
mysql_client_auth_error_handling(dcb, auth_val);
|
||||
/**
|
||||
* Close DCB and which will release MYSQL_session
|
||||
*/
|
||||
dcb_close(dcb);
|
||||
}
|
||||
/* One way or another, the buffer is now fully processed */
|
||||
gwbuf_free(read_buffer);
|
||||
read_buffer = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
/**
|
||||
*
|
||||
* Once a client connection is authenticated, the protocol authentication
|
||||
* state will be MYSQL_IDLE and so every event of data received will
|
||||
* result in a call that comes to this section of code.
|
||||
*
|
||||
*/
|
||||
case MYSQL_IDLE:
|
||||
{
|
||||
ROUTER_OBJECT *router = NULL;
|
||||
ROUTER *router_instance = NULL;
|
||||
void *rsession = NULL;
|
||||
uint8_t cap = 0;
|
||||
bool stmt_input = false; /*< router input type */
|
||||
SESSION *session = dcb->session;
|
||||
if (session != NULL && SESSION_STATE_DUMMY != session->state)
|
||||
{
|
||||
CHK_SESSION(session);
|
||||
router = session->service->router;
|
||||
@ -831,224 +657,6 @@ int gw_read_client_event(DCB* dcb)
|
||||
/**
|
||||
* Now there should be at least one complete mysql packet in read_buffer.
|
||||
*/
|
||||
switch (protocol->protocol_auth_state)
|
||||
{
|
||||
case MYSQL_AUTH_SENT:
|
||||
{
|
||||
read_buffer = gwbuf_make_contiguous(read_buffer);
|
||||
int auth_val = gw_mysql_do_authentication(dcb, &read_buffer);
|
||||
|
||||
if (protocol->protocol_auth_state == MYSQL_AUTH_SSL_REQ ||
|
||||
protocol->protocol_auth_state == MYSQL_AUTH_SSL_HANDSHAKE_ONGOING ||
|
||||
protocol->protocol_auth_state == MYSQL_AUTH_SSL_HANDSHAKE_DONE ||
|
||||
protocol->protocol_auth_state == MYSQL_AUTH_SSL_HANDSHAKE_FAILED)
|
||||
{
|
||||
/** SSL was requested and the handshake is either done or
|
||||
* still ongoing. After the handshake is done, the client
|
||||
* will send another auth packet. */
|
||||
gwbuf_free(read_buffer);
|
||||
read_buffer = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (auth_val == 0)
|
||||
{
|
||||
SESSION *session;
|
||||
|
||||
protocol->protocol_auth_state = MYSQL_AUTH_RECV;
|
||||
/**
|
||||
* Create session, and a router session for it.
|
||||
* If successful, there will be backend connection(s)
|
||||
* after this point.
|
||||
*/
|
||||
session = session_alloc(dcb->service, dcb);
|
||||
|
||||
if (session != NULL)
|
||||
{
|
||||
CHK_SESSION(session);
|
||||
ss_dassert(session->state != SESSION_STATE_ALLOC &&
|
||||
session->state != SESSION_STATE_DUMMY);
|
||||
|
||||
protocol->protocol_auth_state = MYSQL_IDLE;
|
||||
/**
|
||||
* Send an AUTH_OK packet to the client,
|
||||
* packet sequence is # 2
|
||||
*/
|
||||
mysql_send_ok(dcb, 2, 0, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
|
||||
MXS_DEBUG("%lu [gw_read_client_event] session "
|
||||
"creation failed. fd %d, "
|
||||
"state = MYSQL_AUTH_FAILED.",
|
||||
pthread_self(),
|
||||
protocol->owner_dcb->fd);
|
||||
|
||||
/** Send ERR 1045 to client */
|
||||
mysql_send_auth_error(dcb,
|
||||
2,
|
||||
0,
|
||||
"failed to create new session");
|
||||
|
||||
dcb_close(dcb);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char* fail_str = NULL;
|
||||
|
||||
protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
|
||||
|
||||
if (auth_val == 2)
|
||||
{
|
||||
/** Send error 1049 to client */
|
||||
int message_len = 25 + MYSQL_DATABASE_MAXLEN;
|
||||
|
||||
fail_str = calloc(1, message_len+1);
|
||||
snprintf(fail_str, message_len, "Unknown database '%s'",
|
||||
(char*)((MYSQL_session *)dcb->data)->db);
|
||||
|
||||
modutil_send_mysql_err_packet(dcb, 2, 0, 1049, "42000", fail_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Send error 1045 to client */
|
||||
fail_str = create_auth_fail_str((char *)((MYSQL_session *)dcb->data)->user,
|
||||
dcb->remote,
|
||||
(char*)((MYSQL_session *)dcb->data)->client_sha1,
|
||||
(char*)((MYSQL_session *)dcb->data)->db,auth_val);
|
||||
modutil_send_mysql_err_packet(dcb, 2, 0, 1045, "28000", fail_str);
|
||||
}
|
||||
if (fail_str)
|
||||
{
|
||||
free(fail_str);
|
||||
}
|
||||
|
||||
MXS_DEBUG("%lu [gw_read_client_event] after "
|
||||
"gw_mysql_do_authentication, fd %d, "
|
||||
"state = MYSQL_AUTH_FAILED.",
|
||||
pthread_self(),
|
||||
protocol->owner_dcb->fd);
|
||||
/**
|
||||
* Release MYSQL_session since it is not used anymore.
|
||||
*/
|
||||
if (!DCB_IS_CLONE(dcb))
|
||||
{
|
||||
free(dcb->data);
|
||||
}
|
||||
dcb->data = NULL;
|
||||
|
||||
dcb_close(dcb);
|
||||
}
|
||||
gwbuf_free(read_buffer);
|
||||
read_buffer = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case MYSQL_AUTH_SSL_HANDSHAKE_DONE:
|
||||
{
|
||||
int auth_val;
|
||||
|
||||
auth_val = gw_mysql_do_authentication(dcb, &read_buffer);
|
||||
|
||||
if (auth_val == 0)
|
||||
{
|
||||
SESSION *session;
|
||||
|
||||
protocol->protocol_auth_state = MYSQL_AUTH_RECV;
|
||||
/**
|
||||
* Create session, and a router session for it.
|
||||
* If successful, there will be backend connection(s)
|
||||
* after this point.
|
||||
*/
|
||||
session = session_alloc(dcb->service, dcb);
|
||||
|
||||
if (session != NULL)
|
||||
{
|
||||
CHK_SESSION(session);
|
||||
ss_dassert(session->state != SESSION_STATE_ALLOC &&
|
||||
session->state != SESSION_STATE_DUMMY);
|
||||
|
||||
protocol->protocol_auth_state = MYSQL_IDLE;
|
||||
/**
|
||||
* Send an AUTH_OK packet to the client,
|
||||
* packet sequence is # 2
|
||||
*/
|
||||
mysql_send_ok(dcb, 3, 0, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
|
||||
MXS_DEBUG("%lu [gw_read_client_event] session "
|
||||
"creation failed. fd %d, "
|
||||
"state = MYSQL_AUTH_FAILED.",
|
||||
pthread_self(),
|
||||
protocol->owner_dcb->fd);
|
||||
|
||||
/** Send ERR 1045 to client */
|
||||
mysql_send_auth_error(dcb,
|
||||
3,
|
||||
0,
|
||||
"failed to create new session");
|
||||
|
||||
dcb_close(dcb);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char* fail_str = NULL;
|
||||
|
||||
protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
|
||||
|
||||
if (auth_val == 2)
|
||||
{
|
||||
/** Send error 1049 to client */
|
||||
int message_len = 25 + MYSQL_DATABASE_MAXLEN;
|
||||
|
||||
fail_str = calloc(1, message_len+1);
|
||||
snprintf(fail_str, message_len, "Unknown database '%s'",
|
||||
(char*)((MYSQL_session *)dcb->data)->db);
|
||||
|
||||
modutil_send_mysql_err_packet(dcb, 3, 0, 1049, "42000", fail_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Send error 1045 to client */
|
||||
fail_str = create_auth_fail_str((char *)((MYSQL_session *)dcb->data)->user,
|
||||
dcb->remote,
|
||||
(char*)((MYSQL_session *)dcb->data)->client_sha1,
|
||||
(char*)((MYSQL_session *)dcb->data)->db,auth_val);
|
||||
modutil_send_mysql_err_packet(dcb, 3, 0, 1045, "28000", fail_str);
|
||||
}
|
||||
if (fail_str)
|
||||
{
|
||||
free(fail_str);
|
||||
}
|
||||
|
||||
MXS_DEBUG("%lu [gw_read_client_event] after "
|
||||
"gw_mysql_do_authentication, fd %d, "
|
||||
"state = MYSQL_AUTH_FAILED.",
|
||||
pthread_self(),
|
||||
protocol->owner_dcb->fd);
|
||||
/**
|
||||
* Release MYSQL_session since it is not used anymore.
|
||||
*/
|
||||
if (!DCB_IS_CLONE(dcb))
|
||||
{
|
||||
free(dcb->data);
|
||||
}
|
||||
dcb->data = NULL;
|
||||
|
||||
dcb_close(dcb);
|
||||
}
|
||||
gwbuf_free(read_buffer);
|
||||
read_buffer = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case MYSQL_IDLE:
|
||||
{
|
||||
uint8_t* payload = NULL;
|
||||
session_state_t ses_state;
|
||||
|
||||
@ -1189,6 +797,98 @@ return_rc:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Analyse authentication errors and write appropriate log messages
|
||||
*
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @param auth_val The type of authentication failure
|
||||
* @note Authentication status codes are defined in mysql_client_server_protocol.h
|
||||
*/
|
||||
static void
|
||||
mysql_client_auth_error_handling(DCB *dcb, int auth_val)
|
||||
{
|
||||
int packet_number, message_len;
|
||||
char *fail_str = NULL;
|
||||
|
||||
packet_number = ssl_required_by_dcb(dcb) ? 3 : 2;
|
||||
|
||||
switch (auth_val)
|
||||
{
|
||||
case MYSQL_AUTH_NO_SESSION:
|
||||
MXS_DEBUG("%lu [gw_read_client_event] session "
|
||||
"creation failed. fd %d, "
|
||||
"state = MYSQL_AUTH_NO_SESSION.",
|
||||
pthread_self(),
|
||||
dcb->fd);
|
||||
|
||||
/** Send ERR 1045 to client */
|
||||
mysql_send_auth_error(dcb,
|
||||
packet_number,
|
||||
0,
|
||||
"failed to create new session");
|
||||
case MYSQL_FAILED_AUTH_DB:
|
||||
MXS_DEBUG("%lu [gw_read_client_event] database "
|
||||
"specified was not valid. fd %d, "
|
||||
"state = MYSQL_FAILED_AUTH_DB.",
|
||||
pthread_self(),
|
||||
dcb->fd);
|
||||
/** Send error 1049 to client */
|
||||
message_len = 25 + MYSQL_DATABASE_MAXLEN;
|
||||
|
||||
fail_str = calloc(1, message_len+1);
|
||||
snprintf(fail_str, message_len, "Unknown database '%s'",
|
||||
(char*)((MYSQL_session *)dcb->data)->db);
|
||||
|
||||
modutil_send_mysql_err_packet(dcb, packet_number, 0, 1049, "42000", fail_str);
|
||||
case MYSQL_FAILED_AUTH_SSL:
|
||||
MXS_DEBUG("%lu [gw_read_client_event] client is "
|
||||
"not SSL capable for SSL listener. fd %d, "
|
||||
"state = MYSQL_FAILED_AUTH_SSL.",
|
||||
pthread_self(),
|
||||
dcb->fd);
|
||||
|
||||
/** Send ERR 1045 to client */
|
||||
mysql_send_auth_error(dcb,
|
||||
packet_number,
|
||||
0,
|
||||
"failed to complete SSL authentication");
|
||||
case MYSQL_AUTH_SSL_INCOMPLETE:
|
||||
MXS_DEBUG("%lu [gw_read_client_event] unable to "
|
||||
"complete SSL authentication. fd %d, "
|
||||
"state = MYSQL_AUTH_SSL_INCOMPLETE.",
|
||||
pthread_self(),
|
||||
dcb->fd);
|
||||
|
||||
/** Send ERR 1045 to client */
|
||||
mysql_send_auth_error(dcb,
|
||||
packet_number,
|
||||
0,
|
||||
"failed to complete SSL authentication");
|
||||
case MYSQL_FAILED_AUTH:
|
||||
MXS_DEBUG("%lu [gw_read_client_event] authentication failed. fd %d, "
|
||||
"state = MYSQL_FAILED_AUTH.",
|
||||
pthread_self(),
|
||||
dcb->fd);
|
||||
/** Send error 1045 to client */
|
||||
fail_str = create_auth_fail_str((char *)((MYSQL_session *)dcb->data)->user,
|
||||
dcb->remote,
|
||||
(char*)((MYSQL_session *)dcb->data)->client_sha1,
|
||||
(char*)((MYSQL_session *)dcb->data)->db, auth_val);
|
||||
modutil_send_mysql_err_packet(dcb, packet_number, 0, 1045, "28000", fail_str);
|
||||
default:
|
||||
MXS_DEBUG("%lu [gw_read_client_event] authentication failed. fd %d, "
|
||||
"state unrecognized.",
|
||||
pthread_self(),
|
||||
dcb->fd);
|
||||
/** Send error 1045 to client */
|
||||
fail_str = create_auth_fail_str((char *)((MYSQL_session *)dcb->data)->user,
|
||||
dcb->remote,
|
||||
(char*)((MYSQL_session *)dcb->data)->client_sha1,
|
||||
(char*)((MYSQL_session *)dcb->data)->db, auth_val);
|
||||
modutil_send_mysql_err_packet(dcb, packet_number, 0, 1045, "28000", fail_str);
|
||||
}
|
||||
free(fail_str);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// client write event to Client triggered by EPOLLOUT
|
||||
@ -1588,6 +1288,7 @@ int gw_MySQLAccept(DCB *listener)
|
||||
goto return_rc;
|
||||
}
|
||||
|
||||
client_dcb->listen_ssl = listener->listen_ssl;
|
||||
client_dcb->service = listener->session->service;
|
||||
client_dcb->session = session_set_dummy(client_dcb);
|
||||
client_dcb->fd = c_sock;
|
||||
@ -1871,71 +1572,3 @@ static int route_by_statement(SESSION* session, GWBUF** p_readbuf)
|
||||
return_rc:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the SSL authentication handshake.
|
||||
* This creates the DCB SSL structure if one has not been created and starts the
|
||||
* SSL handshake handling.
|
||||
* @param protocol Protocol to connect with SSL
|
||||
* @return 1 on success, 0 when the handshake is ongoing or -1 on error
|
||||
*/
|
||||
int do_ssl_accept(MySQLProtocol* protocol)
|
||||
{
|
||||
int rval,errnum;
|
||||
char errbuf[2014];
|
||||
DCB* dcb = protocol->owner_dcb;
|
||||
if (dcb->ssl == NULL)
|
||||
{
|
||||
if (dcb_create_SSL(dcb) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
rval = dcb_accept_SSL(dcb);
|
||||
|
||||
switch(rval)
|
||||
{
|
||||
case 0:
|
||||
/** Not all of the data has been read. Go back to the poll
|
||||
queue and wait for more.*/
|
||||
|
||||
rval = 0;
|
||||
MXS_INFO("SSL_accept ongoing for %s@%s",
|
||||
protocol->owner_dcb->user,
|
||||
protocol->owner_dcb->remote);
|
||||
return 0;
|
||||
break;
|
||||
case 1:
|
||||
spinlock_acquire(&protocol->protocol_lock);
|
||||
protocol->protocol_auth_state = MYSQL_AUTH_SSL_HANDSHAKE_DONE;
|
||||
protocol->use_ssl = true;
|
||||
spinlock_release(&protocol->protocol_lock);
|
||||
|
||||
rval = 1;
|
||||
|
||||
MXS_INFO("SSL_accept done for %s@%s",
|
||||
protocol->owner_dcb->user,
|
||||
protocol->owner_dcb->remote);
|
||||
break;
|
||||
|
||||
case -1:
|
||||
spinlock_acquire(&protocol->protocol_lock);
|
||||
protocol->protocol_auth_state = MYSQL_AUTH_SSL_HANDSHAKE_FAILED;
|
||||
spinlock_release(&protocol->protocol_lock);
|
||||
rval = -1;
|
||||
MXS_ERROR("Fatal error in SSL_accept for %s",
|
||||
protocol->owner_dcb->remote);
|
||||
break;
|
||||
|
||||
default:
|
||||
MXS_ERROR("Fatal error in SSL_accept, returned value was %d.", rval);
|
||||
break;
|
||||
}
|
||||
#ifdef SS_DEBUG
|
||||
MXS_DEBUG("[do_ssl_accept] Protocol state: %s",
|
||||
gw_mysql_protocol_state2string(protocol->protocol_auth_state));
|
||||
#endif
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
@ -41,7 +41,8 @@
|
||||
* 03/10/2014 Massimiliano Pinto Added netmask for wildcard in IPv4 hosts.
|
||||
* 24/10/2014 Massimiliano Pinto Added Mysql user@host @db authentication support
|
||||
* 10/11/2014 Massimiliano Pinto Charset at connect is passed to backend during authentication
|
||||
* 07/07/15 Martin Brampton Fix problem recognising null password
|
||||
* 07/07/2015 Martin Brampton Fix problem recognising null password
|
||||
* 07/02/2016 Martin Brampton Remove authentication functions to mysql_auth.c
|
||||
*
|
||||
*/
|
||||
|
||||
@ -59,7 +60,6 @@ extern int gw_read_backend_event(DCB* dcb);
|
||||
extern int gw_write_backend_event(DCB *dcb);
|
||||
extern int gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue);
|
||||
extern int gw_error_backend_event(DCB *dcb);
|
||||
char* get_username_from_auth(char* ptr, uint8_t* data);
|
||||
|
||||
static server_command_t* server_command_init(server_command_t* srvcmd, mysql_server_cmd_t cmd);
|
||||
|
||||
@ -1282,7 +1282,7 @@ int gw_send_change_user_to_backend(char *dbname,
|
||||
int rc;
|
||||
MYSQL_session* mses;
|
||||
|
||||
mses = (MYSQL_session*)conn->owner_dcb->session->client->data;
|
||||
mses = (MYSQL_session*)conn->owner_dcb->session->client_dcb->data;
|
||||
buffer = gw_create_change_user_packet(mses, conn);
|
||||
rc = conn->owner_dcb->func.write(conn->owner_dcb, buffer);
|
||||
|
||||
@ -1293,132 +1293,6 @@ int gw_send_change_user_to_backend(char *dbname,
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* gw_check_mysql_scramble_data
|
||||
*
|
||||
* Check authentication token received against stage1_hash and scramble
|
||||
*
|
||||
* @param dcb The current dcb
|
||||
* @param token The token sent by the client in the authentication request
|
||||
* @param token_len The token size in bytes
|
||||
* @param scramble The scramble data sent by the server during handshake
|
||||
* @param scramble_len The scrable size in bytes
|
||||
* @param username The current username in the authentication request
|
||||
* @param stage1_hash The SHA1(candidate_password) decoded by this routine
|
||||
* @return 0 on succesful check or 1 on failure
|
||||
*
|
||||
*/
|
||||
int gw_check_mysql_scramble_data(DCB *dcb,
|
||||
uint8_t *token,
|
||||
unsigned int token_len,
|
||||
uint8_t *scramble,
|
||||
unsigned int scramble_len,
|
||||
char *username,
|
||||
uint8_t *stage1_hash)
|
||||
{
|
||||
uint8_t step1[GW_MYSQL_SCRAMBLE_SIZE]="";
|
||||
uint8_t step2[GW_MYSQL_SCRAMBLE_SIZE +1]="";
|
||||
uint8_t check_hash[GW_MYSQL_SCRAMBLE_SIZE]="";
|
||||
char hex_double_sha1[2 * GW_MYSQL_SCRAMBLE_SIZE + 1]="";
|
||||
uint8_t password[GW_MYSQL_SCRAMBLE_SIZE]="";
|
||||
int ret_val = 1;
|
||||
|
||||
if ((username == NULL) || (scramble == NULL) || (stage1_hash == NULL))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*<
|
||||
* get the user's password from repository in SHA1(SHA1(real_password));
|
||||
* please note 'real_password' is unknown!
|
||||
*/
|
||||
|
||||
ret_val = gw_find_mysql_user_password_sha1(username, password, dcb);
|
||||
|
||||
if (ret_val)
|
||||
{
|
||||
/* if password was sent, fill stage1_hash with at least 1 byte in order
|
||||
* to create right error message: (using password: YES|NO)
|
||||
*/
|
||||
if (token_len)
|
||||
memcpy(stage1_hash, (char *)"_", 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (token && token_len)
|
||||
{
|
||||
/*<
|
||||
* convert in hex format: this is the content of mysql.user table.
|
||||
* The field password is without the '*' prefix and it is 40 bytes long
|
||||
*/
|
||||
|
||||
gw_bin2hex(hex_double_sha1, password, SHA_DIGEST_LENGTH);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check if the password is not set in the user table */
|
||||
return memcmp(password, null_client_sha1, MYSQL_SCRAMBLE_LEN) ? 1 : 0;
|
||||
}
|
||||
|
||||
/*<
|
||||
* Auth check in 3 steps
|
||||
*
|
||||
* Note: token = XOR (SHA1(real_password), SHA1(CONCAT(scramble, SHA1(SHA1(real_password)))))
|
||||
* the client sends token
|
||||
*
|
||||
* Now, server side:
|
||||
*
|
||||
*
|
||||
* step 1: compute the STEP1 = SHA1(CONCAT(scramble, gateway_password))
|
||||
* the result in step1 is SHA_DIGEST_LENGTH long
|
||||
*/
|
||||
|
||||
gw_sha1_2_str(scramble, scramble_len, password, SHA_DIGEST_LENGTH, step1);
|
||||
|
||||
/*<
|
||||
* step2: STEP2 = XOR(token, STEP1)
|
||||
*
|
||||
* token is transmitted form client and it's based on the handshake scramble and SHA1(real_passowrd)
|
||||
* step1 has been computed in the previous step
|
||||
* the result STEP2 is SHA1(the_password_to_check) and is SHA_DIGEST_LENGTH long
|
||||
*/
|
||||
|
||||
gw_str_xor(step2, token, step1, token_len);
|
||||
|
||||
/*<
|
||||
* copy the stage1_hash back to the caller
|
||||
* stage1_hash will be used for backend authentication
|
||||
*/
|
||||
|
||||
memcpy(stage1_hash, step2, SHA_DIGEST_LENGTH);
|
||||
|
||||
/*<
|
||||
* step 3: prepare the check_hash
|
||||
*
|
||||
* compute the SHA1(STEP2) that is SHA1(SHA1(the_password_to_check)), and is SHA_DIGEST_LENGTH long
|
||||
*/
|
||||
|
||||
gw_sha1_str(step2, SHA_DIGEST_LENGTH, check_hash);
|
||||
|
||||
|
||||
#ifdef GW_DEBUG_CLIENT_AUTH
|
||||
{
|
||||
char inpass[128]="";
|
||||
gw_bin2hex(inpass, check_hash, SHA_DIGEST_LENGTH);
|
||||
|
||||
fprintf(stderr, "The CLIENT hex(SHA1(SHA1(password))) for \"%s\" is [%s]", username, inpass);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* now compare SHA1(SHA1(gateway_password)) and check_hash: return 0 is MYSQL_AUTH_OK */
|
||||
ret_val = memcmp(password, check_hash, SHA_DIGEST_LENGTH);
|
||||
|
||||
if (ret_val != 0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gw_find_mysql_user_password_sha1
|
||||
@ -2088,89 +1962,6 @@ char* create_auth_failed_msg(GWBUF*readbuf,
|
||||
return errstr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read username from MySQL authentication packet.
|
||||
*
|
||||
* Only for client to server packet, COM_CHANGE_USER packet has different format.
|
||||
*
|
||||
* @param ptr address where to write the result or NULL if memory
|
||||
* is allocated here.
|
||||
* @param data Address of MySQL packet.
|
||||
*
|
||||
* @return Pointer to a copy of the username. NULL if memory allocation
|
||||
* failed or if username was empty.
|
||||
*/
|
||||
char* get_username_from_auth(char* ptr,
|
||||
uint8_t* data)
|
||||
{
|
||||
char* first_letter;
|
||||
char* rval;
|
||||
|
||||
first_letter = (char *)(data + 4 + 4 + 4 + 1 + 23);
|
||||
|
||||
if (*first_letter == '\0')
|
||||
{
|
||||
rval = NULL;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
if (ptr == NULL)
|
||||
{
|
||||
if ((rval = (char *)malloc(MYSQL_USER_MAXLEN + 1)) == NULL)
|
||||
{
|
||||
goto retblock;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rval = ptr;
|
||||
}
|
||||
snprintf(rval, MYSQL_USER_MAXLEN + 1, "%s", first_letter);
|
||||
|
||||
retblock:
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
int check_db_name_after_auth(DCB *dcb, char *database, int auth_ret)
|
||||
{
|
||||
int db_exists = -1;
|
||||
|
||||
/* check for dabase name and possible match in resource hashtable */
|
||||
if (database && strlen(database))
|
||||
{
|
||||
/* if database names are loaded we can check if db name exists */
|
||||
if (dcb->service->resources != NULL)
|
||||
{
|
||||
if (hashtable_fetch(dcb->service->resources, database))
|
||||
{
|
||||
db_exists = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
db_exists = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if database names are not loaded we don't allow connection with db name*/
|
||||
db_exists = -1;
|
||||
}
|
||||
|
||||
if (db_exists == 0 && auth_ret == 0)
|
||||
{
|
||||
auth_ret = 2;
|
||||
}
|
||||
|
||||
if (db_exists < 0 && auth_ret == 0)
|
||||
{
|
||||
auth_ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return auth_ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a message error string to send via MySQL ERR packet.
|
||||
*
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <buffer.h>
|
||||
#include <service.h>
|
||||
#include <session.h>
|
||||
#include <gw_protocol.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
@ -296,6 +297,7 @@ static int telnetd_accept(DCB *dcb)
|
||||
close(so);
|
||||
return n_connect;
|
||||
}
|
||||
client_dcb->listen_ssl = dcb->listen_ssl;
|
||||
client_dcb->fd = so;
|
||||
client_dcb->remote = strdup(inet_ntoa(addr.sin_addr));
|
||||
memcpy(&client_dcb->func, &MyObject, sizeof(GWPROTOCOL));
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <modinfo.h>
|
||||
#include <dcb.h>
|
||||
#include <buffer.h>
|
||||
#include <gw_protocol.h>
|
||||
|
||||
MODULE_INFO info =
|
||||
{
|
||||
|
@ -3,13 +3,13 @@ set_target_properties(binlogrouter PROPERTIES INSTALL_RPATH ${CMAKE_INSTALL_RPAT
|
||||
set_target_properties(binlogrouter PROPERTIES LINK_FLAGS -Wl,-z,defs)
|
||||
# libbinlogrouter refers to my_uuid_init and my_uuid. They are non-public functions and
|
||||
# should not be used. They are found only from the embedded lib.
|
||||
target_link_libraries(binlogrouter maxscale-common ${MYSQL_EMBEDDED_LIBRARIES})
|
||||
target_link_libraries(binlogrouter maxscale-common ${MYSQL_EMBEDDED_LIBRARIES} ${PCRE_LINK_FLAGS})
|
||||
install(TARGETS binlogrouter DESTINATION ${MAXSCALE_LIBDIR})
|
||||
|
||||
add_executable(maxbinlogcheck maxbinlogcheck.c blr_file.c blr_cache.c blr_master.c blr_slave.c blr.c)
|
||||
# maxbinlogcheck refers to my_uuid_init and my_uuid. They are non-public functions and
|
||||
# should not be used. They are found only from the embedded lib.
|
||||
target_link_libraries(maxbinlogcheck maxscale-common ${MYSQL_EMBEDDED_LIBRARIES})
|
||||
target_link_libraries(maxbinlogcheck maxscale-common ${MYSQL_EMBEDDED_LIBRARIES} ${PCRE_LINK_FLAGS})
|
||||
|
||||
install(TARGETS maxbinlogcheck DESTINATION bin)
|
||||
|
||||
|
@ -183,7 +183,7 @@ GetModuleObject()
|
||||
* The process of creating the instance causes the router to register
|
||||
* with the master server and begin replication of the binlogs from
|
||||
* the master server to MaxScale.
|
||||
*
|
||||
*
|
||||
* @param service The service this router is being create for
|
||||
* @param options An array of options for this query router
|
||||
*
|
||||
@ -455,7 +455,7 @@ char task_name[BLRM_TASK_NAME_LEN+1] = "";
|
||||
break;
|
||||
}
|
||||
inst->burst_size = size;
|
||||
|
||||
|
||||
}
|
||||
else if (strcmp(options[i], "heartbeat") == 0)
|
||||
{
|
||||
@ -614,7 +614,7 @@ char task_name[BLRM_TASK_NAME_LEN+1] = "";
|
||||
" Fix errors in it or configure with CHANGE MASTER TO ...",
|
||||
inst->service->name, inst->binlogdir);
|
||||
}
|
||||
|
||||
|
||||
/* Set service user or load db users */
|
||||
blr_set_service_mysql_user(inst->service);
|
||||
|
||||
@ -626,7 +626,7 @@ char task_name[BLRM_TASK_NAME_LEN+1] = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the binlog router
|
||||
* Initialise the binlog router
|
||||
*/
|
||||
if (inst->master_state == BLRM_UNCONNECTED) {
|
||||
|
||||
@ -754,7 +754,7 @@ ROUTER_SLAVE *slave;
|
||||
slave->uuid = NULL;
|
||||
slave->hostname = NULL;
|
||||
spinlock_init(&slave->catch_lock);
|
||||
slave->dcb = session->client;
|
||||
slave->dcb = session->client_dcb;
|
||||
slave->router = inst;
|
||||
#ifdef BLFILE_IN_SLAVE
|
||||
slave->file = NULL;
|
||||
@ -775,7 +775,7 @@ ROUTER_SLAVE *slave;
|
||||
spinlock_release(&inst->lock);
|
||||
|
||||
CHK_CLIENT_RSES(slave);
|
||||
|
||||
|
||||
return (void *)slave;
|
||||
}
|
||||
|
||||
@ -798,10 +798,10 @@ static void freeSession(
|
||||
ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)router_instance;
|
||||
ROUTER_SLAVE *slave = (ROUTER_SLAVE *)router_client_ses;
|
||||
int prev_val;
|
||||
|
||||
|
||||
prev_val = atomic_add(&router->stats.n_slaves, -1);
|
||||
ss_dassert(prev_val > 0);
|
||||
|
||||
|
||||
/*
|
||||
* Remove the slave session form the list of slaves that are using the
|
||||
* router currently.
|
||||
@ -811,11 +811,11 @@ int prev_val;
|
||||
router->slaves = slave->next;
|
||||
} else {
|
||||
ROUTER_SLAVE *ptr = router->slaves;
|
||||
|
||||
|
||||
while (ptr != NULL && ptr->next != slave) {
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
|
||||
if (ptr != NULL) {
|
||||
ptr->next = slave->next;
|
||||
}
|
||||
@ -846,7 +846,7 @@ int prev_val;
|
||||
* @param instance The router instance data
|
||||
* @param router_session The session being closed
|
||||
*/
|
||||
static void
|
||||
static void
|
||||
closeSession(ROUTER *instance, void *router_session)
|
||||
{
|
||||
ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance;
|
||||
@ -927,12 +927,12 @@ ROUTER_SLAVE *slave = (ROUTER_SLAVE *)router_session;
|
||||
* @param queue The queue of data buffers to route
|
||||
* @return The number of bytes sent
|
||||
*/
|
||||
static int
|
||||
static int
|
||||
routeQuery(ROUTER *instance, void *router_session, GWBUF *queue)
|
||||
{
|
||||
ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance;
|
||||
ROUTER_SLAVE *slave = (ROUTER_SLAVE *)router_session;
|
||||
|
||||
|
||||
return blr_slave_request(router, slave, queue);
|
||||
}
|
||||
|
||||
@ -1020,7 +1020,7 @@ struct tm tm;
|
||||
min15 /= 15.0;
|
||||
min10 /= 10.0;
|
||||
min5 /= 5.0;
|
||||
|
||||
|
||||
if (router_inst->master)
|
||||
dcb_printf(dcb, "\tMaster connection DCB: %p\n",
|
||||
router_inst->master);
|
||||
@ -1032,7 +1032,7 @@ struct tm tm;
|
||||
|
||||
localtime_r(&router_inst->stats.lastReply, &tm);
|
||||
asctime_r(&tm, buf);
|
||||
|
||||
|
||||
dcb_printf(dcb, "\tBinlog directory: %s\n",
|
||||
router_inst->binlogdir);
|
||||
dcb_printf(dcb, "\tHeartbeat period (seconds): %lu\n",
|
||||
@ -1046,7 +1046,7 @@ struct tm tm;
|
||||
dcb_printf(dcb, "\tCurrent binlog position: %lu\n",
|
||||
router_inst->current_pos);
|
||||
if (router_inst->trx_safe) {
|
||||
if (router_inst->pending_transaction) {
|
||||
if (router_inst->pending_transaction) {
|
||||
dcb_printf(dcb, "\tCurrent open transaction pos: %lu\n",
|
||||
router_inst->binlog_position);
|
||||
}
|
||||
@ -1273,7 +1273,7 @@ struct tm tm;
|
||||
}
|
||||
else if ((session->cstate & CS_UPTODATE) == 0)
|
||||
{
|
||||
dcb_printf(dcb, "\t\tSlave_mode: catchup. %s%s\n",
|
||||
dcb_printf(dcb, "\t\tSlave_mode: catchup. %s%s\n",
|
||||
((session->cstate & CS_EXPECTCB) == 0 ? "" :
|
||||
"Waiting for DCB queue to drain."),
|
||||
((session->cstate & CS_BUSY) == 0 ? "" :
|
||||
@ -1426,35 +1426,35 @@ unsigned long mysql_errno;
|
||||
}
|
||||
|
||||
/** to be inline'd */
|
||||
/**
|
||||
/**
|
||||
* @node Acquires lock to router client session if it is not closed.
|
||||
*
|
||||
* Parameters:
|
||||
* @param rses - in, use
|
||||
*
|
||||
*
|
||||
*
|
||||
* @return true if router session was not closed. If return value is true
|
||||
* it means that router is locked, and must be unlocked later. False, if
|
||||
* router was closed before lock was acquired.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @details (write detailed description here)
|
||||
*
|
||||
*/
|
||||
static bool rses_begin_locked_router_action(ROUTER_SLAVE *rses)
|
||||
{
|
||||
bool succp = false;
|
||||
|
||||
|
||||
CHK_CLIENT_RSES(rses);
|
||||
|
||||
spinlock_acquire(&rses->rses_lock);
|
||||
succp = true;
|
||||
|
||||
|
||||
return succp;
|
||||
}
|
||||
|
||||
/** to be inline'd */
|
||||
/**
|
||||
/**
|
||||
* @node Releases router client session lock.
|
||||
*
|
||||
* Parameters:
|
||||
@ -1463,7 +1463,7 @@ static bool rses_begin_locked_router_action(ROUTER_SLAVE *rses)
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
*
|
||||
*
|
||||
* @details (write detailed description here)
|
||||
*
|
||||
*/
|
||||
@ -1569,7 +1569,7 @@ GWBUF *ret;
|
||||
*ptr++ = 0;
|
||||
*ptr++ = 0;
|
||||
*ptr++ = 1;
|
||||
*ptr = 0; // OK
|
||||
*ptr = 0; // OK
|
||||
|
||||
return slave->dcb->func.write(slave->dcb, ret);
|
||||
}
|
||||
@ -1591,7 +1591,7 @@ GWBUF *ret;
|
||||
*
|
||||
*/
|
||||
int
|
||||
blr_send_custom_error(DCB *dcb, int packet_number, int affected_rows, char *msg, char *statemsg, unsigned int errcode)
|
||||
blr_send_custom_error(DCB *dcb, int packet_number, int affected_rows, char *msg, char *statemsg, unsigned int errcode)
|
||||
{
|
||||
uint8_t *outbuf = NULL;
|
||||
uint32_t mysql_payload_size = 0;
|
||||
@ -1604,8 +1604,8 @@ unsigned int mysql_errno = 0;
|
||||
const char *mysql_error_msg = NULL;
|
||||
const char *mysql_state = NULL;
|
||||
GWBUF *errbuf = NULL;
|
||||
|
||||
if (errcode == 0)
|
||||
|
||||
if (errcode == 0)
|
||||
mysql_errno = 1064;
|
||||
else
|
||||
mysql_errno = errcode;
|
||||
@ -1615,52 +1615,52 @@ GWBUF *errbuf = NULL;
|
||||
mysql_state = "42000";
|
||||
else
|
||||
mysql_state = statemsg;
|
||||
|
||||
|
||||
field_count = 0xff;
|
||||
gw_mysql_set_byte2(mysql_err, mysql_errno);
|
||||
mysql_statemsg[0]='#';
|
||||
memcpy(mysql_statemsg+1, mysql_state, 5);
|
||||
|
||||
|
||||
if (msg != NULL) {
|
||||
mysql_error_msg = msg;
|
||||
}
|
||||
|
||||
mysql_payload_size = sizeof(field_count) +
|
||||
sizeof(mysql_err) +
|
||||
sizeof(mysql_statemsg) +
|
||||
|
||||
mysql_payload_size = sizeof(field_count) +
|
||||
sizeof(mysql_err) +
|
||||
sizeof(mysql_statemsg) +
|
||||
strlen(mysql_error_msg);
|
||||
|
||||
|
||||
/** allocate memory for packet header + payload */
|
||||
errbuf = gwbuf_alloc(sizeof(mysql_packet_header) + mysql_payload_size);
|
||||
ss_dassert(errbuf != NULL);
|
||||
|
||||
|
||||
if (errbuf == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
outbuf = GWBUF_DATA(errbuf);
|
||||
|
||||
|
||||
/** write packet header and packet number */
|
||||
gw_mysql_set_byte3(mysql_packet_header, mysql_payload_size);
|
||||
mysql_packet_header[3] = packet_number;
|
||||
|
||||
|
||||
/** write header */
|
||||
memcpy(outbuf, mysql_packet_header, sizeof(mysql_packet_header));
|
||||
|
||||
|
||||
mysql_payload = outbuf + sizeof(mysql_packet_header);
|
||||
|
||||
|
||||
/** write field */
|
||||
memcpy(mysql_payload, &field_count, sizeof(field_count));
|
||||
mysql_payload = mysql_payload + sizeof(field_count);
|
||||
|
||||
|
||||
/** write errno */
|
||||
memcpy(mysql_payload, mysql_err, sizeof(mysql_err));
|
||||
mysql_payload = mysql_payload + sizeof(mysql_err);
|
||||
|
||||
|
||||
/** write sqlstate */
|
||||
memcpy(mysql_payload, mysql_statemsg, sizeof(mysql_statemsg));
|
||||
mysql_payload = mysql_payload + sizeof(mysql_statemsg);
|
||||
|
||||
|
||||
/** write error message */
|
||||
memcpy(mysql_payload, mysql_error_msg, strlen(mysql_error_msg));
|
||||
|
||||
|
@ -2,6 +2,6 @@ if(BUILD_TESTS)
|
||||
add_executable(testbinlogrouter testbinlog.c ../blr.c ../blr_slave.c ../blr_master.c ../blr_file.c ../blr_cache.c)
|
||||
# testbinlogrouter refers to my_uuid_init and my_uuin. They are non-public functions and
|
||||
# should not be used. They are found only from the embedded lib.
|
||||
target_link_libraries(testbinlogrouter maxscale-common ${MYSQL_EMBEDDED_LIBRARIES})
|
||||
target_link_libraries(testbinlogrouter maxscale-common ${MYSQL_EMBEDDED_LIBRARIES} ${PCRE_LINK_FLAGS})
|
||||
add_test(TestBinlogRouter ${CMAKE_CURRENT_BINARY_DIR}/testbinlogrouter)
|
||||
endif()
|
||||
|
@ -121,7 +121,7 @@ GetModuleObject()
|
||||
/**
|
||||
* Create an instance of the router for a particular service
|
||||
* within the gateway.
|
||||
*
|
||||
*
|
||||
* @param service The service this router is being create for
|
||||
* @param options Any array of options for the query router
|
||||
*
|
||||
@ -202,14 +202,14 @@ CLI_SESSION *client;
|
||||
session->state = SESSION_STATE_READY;
|
||||
client->mode = inst->mode;
|
||||
|
||||
dcb_printf(session->client, "Welcome the MariaDB Corporation MaxScale Debug Interface (%s).\n",
|
||||
dcb_printf(session->client_dcb, "Welcome the MariaDB Corporation MaxScale Debug Interface (%s).\n",
|
||||
version_str);
|
||||
if (client->mode == CLIM_DEVELOPER)
|
||||
{
|
||||
dcb_printf(session->client, "WARNING: This interface is meant for developer usage,\n");
|
||||
dcb_printf(session->client, "passing incorrect addresses to commands can endanger your MaxScale server.\n\n");
|
||||
dcb_printf(session->client_dcb, "WARNING: This interface is meant for developer usage,\n");
|
||||
dcb_printf(session->client_dcb, "passing incorrect addresses to commands can endanger your MaxScale server.\n\n");
|
||||
}
|
||||
dcb_printf(session->client, "Type help for a list of available commands.\n\n");
|
||||
dcb_printf(session->client_dcb, "Type help for a list of available commands.\n\n");
|
||||
|
||||
return (void *)client;
|
||||
}
|
||||
@ -221,7 +221,7 @@ CLI_SESSION *client;
|
||||
* @param instance The router instance data
|
||||
* @param router_session The session being closed
|
||||
*/
|
||||
static void
|
||||
static void
|
||||
closeSession(ROUTER *instance, void *router_session)
|
||||
{
|
||||
CLI_INSTANCE *inst = (CLI_INSTANCE *)instance;
|
||||
@ -270,7 +270,7 @@ static void freeSession(
|
||||
* @param queue The queue of data buffers to route
|
||||
* @return The number of bytes sent
|
||||
*/
|
||||
static int
|
||||
static int
|
||||
execute(ROUTER *instance, void *router_session, GWBUF *queue)
|
||||
{
|
||||
CLI_SESSION *session = (CLI_SESSION *)router_session;
|
||||
@ -285,9 +285,9 @@ CLI_SESSION *session = (CLI_SESSION *)router_session;
|
||||
if (strrchr(session->cmdbuf, '\n'))
|
||||
{
|
||||
if (execute_cmd(session))
|
||||
dcb_printf(session->session->client, "MaxScale> ");
|
||||
dcb_printf(session->session->client_dcb, "MaxScale> ");
|
||||
else
|
||||
dcb_close(session->session->client);
|
||||
dcb_close(session->session->client_dcb);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
@ -926,7 +926,7 @@ convert_arg(int mode, char *arg, int arg_type)
|
||||
int
|
||||
execute_cmd(CLI_SESSION *cli)
|
||||
{
|
||||
DCB *dcb = cli->session->client;
|
||||
DCB *dcb = cli->session->client_dcb;
|
||||
int argc, i, j, found = 0;
|
||||
char *args[MAXARGS + 1];
|
||||
unsigned long arg1, arg2, arg3;
|
||||
|
@ -148,7 +148,7 @@ GetModuleObject()
|
||||
/**
|
||||
* Create an instance of the router for a particular service
|
||||
* within the gateway.
|
||||
*
|
||||
*
|
||||
* @param service The service this router is being create for
|
||||
* @param options Any array of options for the query router
|
||||
*
|
||||
@ -212,7 +212,7 @@ INFO_SESSION *client;
|
||||
return NULL;
|
||||
}
|
||||
client->session = session;
|
||||
client->dcb = session->client;
|
||||
client->dcb = session->client_dcb;
|
||||
client->queue = NULL;
|
||||
|
||||
spinlock_acquire(&inst->lock);
|
||||
@ -232,7 +232,7 @@ INFO_SESSION *client;
|
||||
* @param instance The router instance data
|
||||
* @param router_session The session being closed
|
||||
*/
|
||||
static void
|
||||
static void
|
||||
closeSession(ROUTER *instance, void *router_session)
|
||||
{
|
||||
INFO_INSTANCE *inst = (INFO_INSTANCE *)instance;
|
||||
@ -310,19 +310,19 @@ static void handleError(
|
||||
}
|
||||
spinlock_acquire(&session->ses_lock);
|
||||
sesstate = session->state;
|
||||
client_dcb = session->client;
|
||||
|
||||
client_dcb = session->client_dcb;
|
||||
|
||||
if (sesstate == SESSION_STATE_ROUTER_READY)
|
||||
{
|
||||
CHK_DCB(client_dcb);
|
||||
spinlock_release(&session->ses_lock);
|
||||
spinlock_release(&session->ses_lock);
|
||||
client_dcb->func.write(client_dcb, gwbuf_clone(errbuf));
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
spinlock_release(&session->ses_lock);
|
||||
}
|
||||
|
||||
|
||||
/** false because connection is not available anymore */
|
||||
dcb_close(backend_dcb);
|
||||
*succp = false;
|
||||
@ -337,7 +337,7 @@ static void handleError(
|
||||
* @param queue The queue of data buffers to route
|
||||
* @return The number of bytes sent
|
||||
*/
|
||||
static int
|
||||
static int
|
||||
execute(ROUTER *rinstance, void *router_session, GWBUF *queue)
|
||||
{
|
||||
INFO_INSTANCE *instance = (INFO_INSTANCE *)rinstance;
|
||||
@ -471,7 +471,7 @@ int len;
|
||||
*ptr++ = 0;
|
||||
*ptr++ = 0;
|
||||
*ptr++ = 1;
|
||||
*ptr = 0; // OK
|
||||
*ptr = 0; // OK
|
||||
|
||||
return session->dcb->func.write(session->dcb, ret);
|
||||
}
|
||||
@ -501,7 +501,7 @@ RESULT_ROW *row;
|
||||
|
||||
/**
|
||||
* The hardwired select @@vercom response
|
||||
*
|
||||
*
|
||||
* @param dcb The DCB of the client
|
||||
*/
|
||||
static void
|
||||
@ -548,7 +548,7 @@ static char buf[40];
|
||||
|
||||
/**
|
||||
* The hardwired select ... as starttime response
|
||||
*
|
||||
*
|
||||
* @param dcb The DCB of the client
|
||||
*/
|
||||
static void
|
||||
@ -704,7 +704,7 @@ static struct uri_table {
|
||||
* @param queue The queue of data buffers to route
|
||||
* @return The number of bytes sent
|
||||
*/
|
||||
static int
|
||||
static int
|
||||
handle_url(INFO_INSTANCE *instance, INFO_SESSION *session, GWBUF *queue)
|
||||
{
|
||||
char *uri;
|
||||
|
@ -847,7 +847,7 @@ diagnostics(ROUTER *router, DCB *dcb)
|
||||
static void
|
||||
clientReply(ROUTER *instance, void *router_session, GWBUF *queue, DCB *backend_dcb)
|
||||
{
|
||||
ss_dassert(backend_dcb->session->client != NULL);
|
||||
ss_dassert(backend_dcb->session->client_dcb != NULL);
|
||||
SESSION_ROUTE_REPLY(backend_dcb->session, queue);
|
||||
}
|
||||
|
||||
@ -886,7 +886,7 @@ static void handleError(ROUTER *instance, void *router_session, GWBUF *errbuf,
|
||||
}
|
||||
spinlock_acquire(&session->ses_lock);
|
||||
sesstate = session->state;
|
||||
client_dcb = session->client;
|
||||
client_dcb = session->client_dcb;
|
||||
|
||||
if (sesstate == SESSION_STATE_ROUTER_READY)
|
||||
{
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -986,8 +986,8 @@ static void* newSession(ROUTER* router_inst, SESSION* session)
|
||||
int router_nservers = 0; /*< # of servers in total */
|
||||
int i;
|
||||
char db[MYSQL_DATABASE_MAXLEN+1];
|
||||
MySQLProtocol* protocol = session->client->protocol;
|
||||
MYSQL_session* data = session->data;
|
||||
MySQLProtocol* protocol = session->client_dcb->protocol;
|
||||
MYSQL_session* data = session->client_dcb->data;
|
||||
bool using_db = false;
|
||||
bool have_db = false;
|
||||
|
||||
@ -1028,12 +1028,12 @@ static void* newSession(ROUTER* router_inst, SESSION* session)
|
||||
#endif
|
||||
|
||||
client_rses->router = router;
|
||||
client_rses->rses_mysql_session = (MYSQL_session*)session->data;
|
||||
client_rses->rses_client_dcb = (DCB*)session->client;
|
||||
client_rses->rses_mysql_session = (MYSQL_session*)session->client_dcb->data;
|
||||
client_rses->rses_client_dcb = (DCB*)session->client_dcb;
|
||||
|
||||
spinlock_acquire(&router->lock);
|
||||
|
||||
shard_map_t *map = hashtable_fetch(router->shard_maps, session->client->user);
|
||||
shard_map_t *map = hashtable_fetch(router->shard_maps, session->client_dcb->user);
|
||||
enum shard_map_state state;
|
||||
|
||||
if (map)
|
||||
@ -2492,7 +2492,7 @@ static void clientReply(ROUTER* instance,
|
||||
}
|
||||
/** Holding lock ensures that router session remains open */
|
||||
ss_dassert(backend_dcb->session != NULL);
|
||||
client_dcb = backend_dcb->session->client;
|
||||
client_dcb = backend_dcb->session->client_dcb;
|
||||
|
||||
/** Unlock */
|
||||
rses_end_locked_router_action(router_cli_ses);
|
||||
@ -3974,7 +3974,7 @@ static void handle_error_reply_client(SESSION* ses,
|
||||
|
||||
spinlock_acquire(&ses->ses_lock);
|
||||
sesstate = ses->state;
|
||||
client_dcb = ses->client;
|
||||
client_dcb = ses->client_dcb;
|
||||
spinlock_release(&ses->ses_lock);
|
||||
|
||||
/**
|
||||
@ -4065,7 +4065,7 @@ static bool handle_error_new_connection(ROUTER_INSTANCE* inst,
|
||||
if (BREF_IS_WAITING_RESULT(bref))
|
||||
{
|
||||
DCB* client_dcb;
|
||||
client_dcb = ses->client;
|
||||
client_dcb = ses->client_dcb;
|
||||
client_dcb->func.write(client_dcb, gwbuf_clone(errmsg));
|
||||
bref_clear_state(bref, BREF_WAITING_RESULT);
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ MODULE_INFO info = {
|
||||
|
||||
|
||||
/**
|
||||
* @file shardrouter.c
|
||||
* @file shardrouter.c
|
||||
*
|
||||
* This is the sharding router that uses MaxScale's services to abstract
|
||||
* the actual implementation of the backend database. Queries are routed based on
|
||||
@ -364,13 +364,13 @@ parse_mapping_response(ROUTER_CLIENT_SES* rses, char* target, GWBUF* buf)
|
||||
*/
|
||||
bool subsvc_is_valid(SUBSERVICE* sub)
|
||||
{
|
||||
|
||||
if(sub->session == NULL ||
|
||||
|
||||
if(sub->session == NULL ||
|
||||
sub->service->router == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
spinlock_acquire(&sub->session->ses_lock);
|
||||
session_state_t ses_state = sub->session->state;
|
||||
spinlock_release(&sub->session->ses_lock);
|
||||
@ -420,7 +420,7 @@ gen_subsvc_dblist(ROUTER_INSTANCE* inst, ROUTER_CLIENT_SES* session)
|
||||
if(SUBSVC_IS_OK(session->subservice[i]))
|
||||
{
|
||||
clone = gwbuf_clone(buffer);
|
||||
|
||||
|
||||
rval |= !SESSION_ROUTE_QUERY(session->subservice[i]->session,clone);
|
||||
subsvc_set_state(session->subservice[i],SUBSVC_WAITING_RESULT|SUBSVC_QUERY_ACTIVE);
|
||||
}
|
||||
@ -485,7 +485,7 @@ get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, GWBUF*
|
||||
MXS_INFO("shardrouter: SHOW TABLES with specific database '%s' on server '%s'", tok, tmp);
|
||||
}
|
||||
free(query);
|
||||
|
||||
|
||||
if(tmp == NULL)
|
||||
{
|
||||
rval = (char*) hashtable_fetch(ht, client->rses_mysql_session->db);
|
||||
@ -494,12 +494,12 @@ get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, GWBUF*
|
||||
}
|
||||
else
|
||||
{
|
||||
rval = tmp;
|
||||
rval = tmp;
|
||||
has_dbs = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if(buffer->hint && buffer->hint->type == HINT_ROUTE_TO_NAMED_SERVER)
|
||||
{
|
||||
for(i = 0; i < client->n_subservice; i++)
|
||||
@ -512,14 +512,14 @@ get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, GWBUF*
|
||||
{
|
||||
rval = srvrf->server->unique_name;
|
||||
MXS_INFO("shardrouter: Routing hint found (%s)",rval);
|
||||
|
||||
|
||||
}
|
||||
srvrf = srvrf->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if(rval == NULL && !has_dbs && client->rses_mysql_session->db[0] != '\0')
|
||||
{
|
||||
/**
|
||||
@ -533,7 +533,7 @@ get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, GWBUF*
|
||||
MXS_INFO("shardrouter: Using active database '%s'",client->rses_mysql_session->db);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
@ -730,7 +730,7 @@ filterReply(FILTER* instance, void *session, GWBUF *reply)
|
||||
|
||||
/**
|
||||
* This function reads the DCB's readqueue and sends it as a reply to the session
|
||||
* who owns the DCB.
|
||||
* who owns the DCB.
|
||||
* @param dcb The dummy DCB
|
||||
* @return 1 on success, 0 on failure
|
||||
*/
|
||||
@ -760,7 +760,7 @@ int fakeQuery(DCB* dcb)
|
||||
GWBUF* tmp = dcb->dcb_readqueue;
|
||||
void* rinst = dcb->session->service->router_instance;
|
||||
void *rses = dcb->session->router_session;
|
||||
|
||||
|
||||
dcb->dcb_readqueue = NULL;
|
||||
return dcb->session->service->router->routeQuery(rinst,rses,tmp);
|
||||
}
|
||||
@ -841,8 +841,8 @@ refreshInstance(
|
||||
}
|
||||
/*else if (paramtype == STRING_TYPE)
|
||||
{
|
||||
if (strncmp(param->name,
|
||||
"ignore_databases",
|
||||
if (strncmp(param->name,
|
||||
"ignore_databases",
|
||||
MAX_PARAM_LEN) == 0)
|
||||
{
|
||||
router->ignore_list = tokenize_string(param->qfd.valstr);
|
||||
@ -862,7 +862,7 @@ refreshInstance(
|
||||
/**
|
||||
* Create an instance of shardrouter statement router within the MaxScale.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param service The service this router is being create for
|
||||
* @param options The options for this query router
|
||||
*
|
||||
@ -964,7 +964,7 @@ createInstance(SERVICE *service, char **options)
|
||||
router->bitvalue = 0;
|
||||
|
||||
/**
|
||||
* Read config version number from service to inform what configuration
|
||||
* Read config version number from service to inform what configuration
|
||||
* is used if any.
|
||||
*/
|
||||
router->shardrouter_version = service->svc_config_version;
|
||||
@ -1018,8 +1018,8 @@ newSession(
|
||||
#endif
|
||||
|
||||
client_rses->router = router;
|
||||
client_rses->rses_mysql_session = (MYSQL_session*) session->data;
|
||||
client_rses->rses_client_dcb = (DCB*) session->client;
|
||||
client_rses->rses_mysql_session = (MYSQL_session*) session->client_dcb->data;
|
||||
client_rses->rses_client_dcb = (DCB*) session->client_dcb;
|
||||
client_rses->rses_autocommit_enabled = true;
|
||||
client_rses->rses_transaction_active = false;
|
||||
client_rses->session = session;
|
||||
@ -1027,12 +1027,12 @@ newSession(
|
||||
client_rses->replydcb->func.read = fakeReply;
|
||||
client_rses->replydcb->state = DCB_STATE_POLLING;
|
||||
client_rses->replydcb->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);
|
||||
|
||||
client_rses->subservice = calloc(router->n_services, sizeof(SUBSERVICE*));
|
||||
@ -1051,10 +1051,10 @@ newSession(
|
||||
{
|
||||
goto errorblock;
|
||||
}
|
||||
|
||||
|
||||
/* TODO: add NULL value checks */
|
||||
client_rses->subservice[i] = subsvc;
|
||||
|
||||
|
||||
subsvc->scur = calloc(1,sizeof(sescmd_cursor_t));
|
||||
if(subsvc->scur == NULL)
|
||||
{
|
||||
@ -1066,15 +1066,15 @@ newSession(
|
||||
subsvc->scur->scmd_cur_ptr_property = client_rses->rses_properties;
|
||||
subsvc->service = router->services[i];
|
||||
subsvc->dcb = dcb_clone(client_rses->rses_client_dcb);
|
||||
|
||||
|
||||
if(subsvc->dcb == NULL){
|
||||
subsvc_set_state(subsvc,SUBSVC_FAILED);
|
||||
MXS_ERROR("Failed to clone client DCB in shardrouter.");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
subsvc->session = session_alloc(subsvc->service,subsvc->dcb);
|
||||
|
||||
|
||||
if(subsvc->session == NULL){
|
||||
dcb_close(subsvc->dcb);
|
||||
subsvc->dcb = NULL;
|
||||
@ -1082,9 +1082,9 @@ newSession(
|
||||
MXS_ERROR("Failed to create subsession for service %s in shardrouter.",subsvc->service->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
dummy_filterdef = filter_alloc("tee_dummy","tee_dummy");
|
||||
|
||||
|
||||
if(dummy_filterdef == NULL)
|
||||
{
|
||||
subsvc_set_state(subsvc,SUBSVC_FAILED);
|
||||
@ -1092,21 +1092,21 @@ newSession(
|
||||
continue;
|
||||
}
|
||||
dummy_filterdef->obj = &dummyObject;
|
||||
dummy_filterdef->filter = (FILTER*)client_rses;
|
||||
dummy_filterdef->filter = (FILTER*)client_rses;
|
||||
dummy_upstream = filterUpstream(dummy_filterdef,subsvc->session,&subsvc->session->tail);
|
||||
|
||||
|
||||
if(dummy_upstream == NULL)
|
||||
{
|
||||
subsvc_set_state(subsvc,SUBSVC_FAILED);
|
||||
MXS_ERROR("Failed to set filterUpstream in shardrouter.");
|
||||
continue;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
subsvc->session->tail = *dummy_upstream;
|
||||
|
||||
|
||||
|
||||
|
||||
subsvc_set_state(subsvc,SUBSVC_OK);
|
||||
|
||||
|
||||
free(dummy_upstream);
|
||||
}
|
||||
|
||||
@ -1171,9 +1171,9 @@ closeSession(
|
||||
int i;
|
||||
MXS_DEBUG("%lu [RWSplit:closeSession]", pthread_self());
|
||||
|
||||
/**
|
||||
/**
|
||||
* router session can be NULL if newSession failed and it is discarding
|
||||
* its connections and DCB's.
|
||||
* its connections and DCB's.
|
||||
*/
|
||||
if(router_session == NULL)
|
||||
{
|
||||
@ -1191,17 +1191,17 @@ closeSession(
|
||||
ROUTER_OBJECT* rtr;
|
||||
ROUTER* rinst;
|
||||
void *rses;
|
||||
SESSION *ses;
|
||||
|
||||
SESSION *one_session;
|
||||
|
||||
for(i = 0;i<router_cli_ses->n_subservice;i++)
|
||||
{
|
||||
rtr = router_cli_ses->subservice[i]->service->router;
|
||||
rinst = router_cli_ses->subservice[i]->service->router_instance;
|
||||
ses = router_cli_ses->subservice[i]->session;
|
||||
if(ses != NULL)
|
||||
one_session = router_cli_ses->subservice[i]->session;
|
||||
if(one_session != NULL)
|
||||
{
|
||||
rses = ses->router_session;
|
||||
ses->state = SESSION_STATE_STOPPING;
|
||||
rses = one_session->router_session;
|
||||
one_session->state = SESSION_STATE_STOPPING;
|
||||
rtr->closeSession(rinst,rses);
|
||||
}
|
||||
router_cli_ses->subservice[i]->state = SUBSVC_CLOSED;
|
||||
@ -1210,7 +1210,7 @@ closeSession(
|
||||
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);
|
||||
}
|
||||
@ -1226,10 +1226,10 @@ freeSession(
|
||||
|
||||
router_cli_ses = (ROUTER_CLIENT_SES *) router_client_session;
|
||||
|
||||
|
||||
/**
|
||||
* For each property type, walk through the list, finalize properties
|
||||
* and free the allocated memory.
|
||||
|
||||
/**
|
||||
* For each property type, walk through the list, finalize properties
|
||||
* and free the allocated memory.
|
||||
*/
|
||||
for(i = RSES_PROP_TYPE_FIRST; i < RSES_PROP_TYPE_COUNT; i++)
|
||||
{
|
||||
@ -1243,10 +1243,10 @@ freeSession(
|
||||
p = q;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for(i = 0;i<router_cli_ses->n_subservice;i++)
|
||||
{
|
||||
|
||||
|
||||
/* TODO: free router client session */
|
||||
free(router_cli_ses->subservice[i]);
|
||||
}
|
||||
@ -1258,7 +1258,7 @@ freeSession(
|
||||
* all the memory and other resources associated
|
||||
* to the client session.
|
||||
*/
|
||||
hashtable_free(router_cli_ses->dbhash);
|
||||
hashtable_free(router_cli_ses->dbhash);
|
||||
free(router_cli_ses);
|
||||
return;
|
||||
}
|
||||
@ -1266,12 +1266,12 @@ freeSession(
|
||||
/**
|
||||
* Examine the query type, transaction state and routing hints. Find out the
|
||||
* target for query routing.
|
||||
*
|
||||
* @param qtype Type of query
|
||||
*
|
||||
* @param qtype Type of query
|
||||
* @param trx_active Is transcation active or not
|
||||
* @param hint Pointer to list of hints attached to the query buffer
|
||||
*
|
||||
* @return bitfield including the routing target, or the target server name
|
||||
*
|
||||
* @return bitfield including the routing target, or the target server name
|
||||
* if the query would otherwise be routed to slave.
|
||||
*/
|
||||
static route_target_t
|
||||
@ -1556,14 +1556,14 @@ routeQuery(ROUTER* instance,
|
||||
}
|
||||
|
||||
rses_end_locked_router_action(router_cli_ses);
|
||||
|
||||
|
||||
packet = GWBUF_DATA(querybuf);
|
||||
packet_type = packet[4];
|
||||
|
||||
if(rses_is_closed)
|
||||
{
|
||||
/**
|
||||
* MYSQL_COM_QUIT may have sent by client and as a part of backend
|
||||
/**
|
||||
* MYSQL_COM_QUIT may have sent by client and as a part of backend
|
||||
* closing procedure.
|
||||
*/
|
||||
if(packet_type != MYSQL_COM_QUIT)
|
||||
@ -1631,7 +1631,7 @@ routeQuery(ROUTER* instance,
|
||||
default:
|
||||
break;
|
||||
} /**< switch by packet type */
|
||||
|
||||
|
||||
if(packet_type == MYSQL_COM_INIT_DB)
|
||||
{
|
||||
if(!(change_successful = change_current_db(router_cli_ses->current_db,
|
||||
@ -1647,15 +1647,15 @@ routeQuery(ROUTER* instance,
|
||||
}
|
||||
|
||||
/**
|
||||
* Find out whether the query should be routed to single server or to
|
||||
* Find out whether the query should be routed to single server or to
|
||||
* all of them.
|
||||
*/
|
||||
if(QUERY_IS_TYPE(qtype, QUERY_TYPE_SHOW_DATABASES))
|
||||
{
|
||||
/**
|
||||
* Generate custom response that contains all the databases
|
||||
* Generate custom response that contains all the databases
|
||||
*/
|
||||
|
||||
|
||||
GWBUF* dbres = gen_show_dbs_response(inst,router_cli_ses);
|
||||
poll_add_epollin_event_to_dcb(router_cli_ses->replydcb,dbres);
|
||||
ret = 1;
|
||||
@ -1670,7 +1670,7 @@ routeQuery(ROUTER* instance,
|
||||
{
|
||||
tname = hashtable_fetch(router_cli_ses->dbhash, router_cli_ses->rses_mysql_session->db);
|
||||
route_target = TARGET_NAMED_SERVER;
|
||||
|
||||
|
||||
}
|
||||
else if(route_target != TARGET_ALL &&
|
||||
(tname = get_shard_target_name(inst, router_cli_ses, querybuf, qtype)) != NULL)
|
||||
@ -1697,7 +1697,7 @@ routeQuery(ROUTER* instance,
|
||||
* No current database and no databases in query or
|
||||
* the database is ignored, route to first available backend.
|
||||
*/
|
||||
|
||||
|
||||
route_target = TARGET_ANY;
|
||||
|
||||
}
|
||||
@ -1706,7 +1706,7 @@ routeQuery(ROUTER* instance,
|
||||
if(!change_successful)
|
||||
{
|
||||
/**
|
||||
* Bad shard status. The changing of the database
|
||||
* Bad shard status. The changing of the database
|
||||
* was not successful and the error message was already sent.
|
||||
*/
|
||||
|
||||
@ -1777,12 +1777,12 @@ routeQuery(ROUTER* instance,
|
||||
if(TARGET_IS_NAMED_SERVER(route_target))
|
||||
{
|
||||
/**
|
||||
* Search backend server by name or replication lag.
|
||||
* Search backend server by name or replication lag.
|
||||
* If it fails, then try to find valid slave or master.
|
||||
*/
|
||||
|
||||
succp = get_shard_subsvc(&target_subsvc,router_cli_ses,tname);
|
||||
|
||||
|
||||
if(!succp)
|
||||
{
|
||||
MXS_INFO("Was supposed to route to named server "
|
||||
@ -1796,12 +1796,12 @@ routeQuery(ROUTER* instance,
|
||||
{
|
||||
sescmd_cursor_t* scur;
|
||||
scur = target_subsvc->scur;
|
||||
/**
|
||||
* Store current stmt if execution of previous session command
|
||||
/**
|
||||
* Store current stmt if execution of previous session command
|
||||
* haven't completed yet. Note that according to MySQL protocol
|
||||
* there can only be one such non-sescmd stmt at the time.
|
||||
*/
|
||||
if(scur && sescmd_cursor_is_active(scur))
|
||||
if(scur && sescmd_cursor_is_active(scur))
|
||||
{
|
||||
target_subsvc->pending_cmd = gwbuf_clone(querybuf);
|
||||
rses_end_locked_router_action(router_cli_ses);
|
||||
@ -1811,16 +1811,16 @@ routeQuery(ROUTER* instance,
|
||||
|
||||
if(SESSION_ROUTE_QUERY(target_subsvc->session,querybuf) == 1)
|
||||
{
|
||||
|
||||
|
||||
|
||||
atomic_add(&inst->stats.n_queries, 1);
|
||||
/**
|
||||
* Add one query response waiter to backend reference
|
||||
*/
|
||||
subsvc_set_state(target_subsvc,SUBSVC_QUERY_ACTIVE|SUBSVC_WAITING_RESULT);
|
||||
|
||||
|
||||
atomic_add(&target_subsvc->n_res_waiting, 1);
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1839,18 +1839,18 @@ retblock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @node Acquires lock to router client session if it is not closed.
|
||||
*
|
||||
* Parameters:
|
||||
* @param rses - in, use
|
||||
*
|
||||
*
|
||||
*
|
||||
* @return true if router session was not closed. If return value is true
|
||||
* it means that router is locked, and must be unlocked later. False, if
|
||||
* router was closed before lock was acquired.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @details (write detailed description here)
|
||||
*
|
||||
*/
|
||||
@ -1881,7 +1881,7 @@ return_succp:
|
||||
|
||||
/** to be inline'd */
|
||||
|
||||
/**
|
||||
/**
|
||||
* @node Releases router client session lock.
|
||||
*
|
||||
* Parameters:
|
||||
@ -1890,7 +1890,7 @@ return_succp:
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
*
|
||||
*
|
||||
* @details (write detailed description here)
|
||||
*
|
||||
*/
|
||||
@ -1955,7 +1955,7 @@ diagnostic(ROUTER *instance, DCB *dcb)
|
||||
"Operations\n");
|
||||
dcb_printf(dcb,
|
||||
"\t\t Global Router\n");
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -1967,7 +1967,7 @@ diagnostic(ROUTER *instance, DCB *dcb)
|
||||
* The routine will reply to client for session change with master server data
|
||||
*
|
||||
* @param instance The router instance
|
||||
* @param router_session The router session
|
||||
* @param router_session The router session
|
||||
* @param backend_dcb The backend DCB
|
||||
* @param queue The GWBUF with reply data
|
||||
*/
|
||||
@ -1978,12 +1978,12 @@ clientReply(
|
||||
GWBUF* writebuf,
|
||||
DCB* backend_dcb)
|
||||
{
|
||||
|
||||
|
||||
SESSION_ROUTE_REPLY(backend_dcb->session, writebuf);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Create a generic router session property strcture.
|
||||
*/
|
||||
static rses_property_t*
|
||||
@ -2044,7 +2044,7 @@ rses_property_done(
|
||||
* Add property to the router_client_ses structure's rses_properties
|
||||
* array. The slot is determined by the type of property.
|
||||
* In each slot there is a list of properties of similar type.
|
||||
*
|
||||
*
|
||||
* Router client session must be locked.
|
||||
*/
|
||||
static void
|
||||
@ -2075,7 +2075,7 @@ rses_property_add(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Router session must be locked.
|
||||
* Return session command pointer if succeed, NULL if failed.
|
||||
*/
|
||||
@ -2139,14 +2139,14 @@ mysql_sescmd_done(
|
||||
* command are handled here.
|
||||
* Read session commands from property list. If command is already replied,
|
||||
* discard packet. Else send reply to client. In both cases move cursor forward
|
||||
* until all session command replies are handled.
|
||||
*
|
||||
* until all session command replies are handled.
|
||||
*
|
||||
* Cases that are expected to happen and which are handled:
|
||||
* s = response not yet replied to client, S = already replied response,
|
||||
* q = query
|
||||
* 1. q+ for example : select * from mysql.user
|
||||
* 2. s+ for example : set autocommit=1
|
||||
* 3. S+
|
||||
* 3. S+
|
||||
* 4. sq+
|
||||
* 5. Sq+
|
||||
* 6. Ss+
|
||||
@ -2168,9 +2168,9 @@ sescmd_cursor_process_replies(
|
||||
|
||||
CHK_GWBUF(replybuf);
|
||||
|
||||
/**
|
||||
* Walk through packets in the message and the list of session
|
||||
* commands.
|
||||
/**
|
||||
* Walk through packets in the message and the list of session
|
||||
* commands.
|
||||
*/
|
||||
while(scmd != NULL && replybuf != NULL)
|
||||
{
|
||||
@ -2191,7 +2191,7 @@ sescmd_cursor_process_replies(
|
||||
replybuf = gwbuf_consume(replybuf, buflen);
|
||||
}
|
||||
/** Set response status received */
|
||||
|
||||
|
||||
subsvc_clear_state(subsvc, SUBSVC_WAITING_RESULT);
|
||||
}
|
||||
/** Response is in the buffer and it will be sent to client. */
|
||||
@ -2219,7 +2219,7 @@ sescmd_cursor_process_replies(
|
||||
|
||||
/**
|
||||
* Get the address of current session command.
|
||||
*
|
||||
*
|
||||
* Router session must be locked */
|
||||
static mysql_sescmd_t*
|
||||
sescmd_cursor_get_command(
|
||||
@ -2261,9 +2261,9 @@ sescmd_cursor_set_active(
|
||||
sescmd_cursor->scmd_cur_active = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone session command's command buffer.
|
||||
* Router session must be locked
|
||||
/**
|
||||
* Clone session command's command buffer.
|
||||
* Router session must be locked
|
||||
*/
|
||||
static GWBUF*
|
||||
sescmd_cursor_clone_querybuf(
|
||||
@ -2339,12 +2339,12 @@ execute_sescmd_history(
|
||||
|
||||
/**
|
||||
* If session command cursor is passive, sends the command to backend for
|
||||
* execution.
|
||||
*
|
||||
* execution.
|
||||
*
|
||||
* Returns true if command was sent or added successfully to the queue.
|
||||
* Returns false if command sending failed or if there are no pending session
|
||||
* commands.
|
||||
*
|
||||
*
|
||||
* Router session must be locked.
|
||||
*/
|
||||
static bool
|
||||
@ -2353,21 +2353,21 @@ execute_sescmd_in_backend(SUBSERVICE* subsvc)
|
||||
bool succp;
|
||||
int rc = 0;
|
||||
sescmd_cursor_t* scur;
|
||||
|
||||
|
||||
|
||||
if(SUBSVC_IS_CLOSED(subsvc) || !SUBSVC_IS_OK(subsvc))
|
||||
{
|
||||
succp = false;
|
||||
goto return_succp;
|
||||
}
|
||||
|
||||
|
||||
if(!subsvc_is_valid(subsvc))
|
||||
{
|
||||
succp = false;
|
||||
goto return_succp;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Get cursor pointer and copy of command buffer to cursor.
|
||||
*/
|
||||
scur = subsvc->scur;
|
||||
@ -2397,8 +2397,8 @@ execute_sescmd_in_backend(SUBSERVICE* subsvc)
|
||||
|
||||
case MYSQL_COM_QUERY:
|
||||
default:
|
||||
/**
|
||||
* Mark session command buffer, it triggers writing
|
||||
/**
|
||||
* Mark session command buffer, it triggers writing
|
||||
* MySQL command to protocol
|
||||
*/
|
||||
gwbuf_set_type(scur->scmd_cur_cmd->my_sescmd_buf, GWBUF_TYPE_SESCMD);
|
||||
@ -2422,8 +2422,8 @@ return_succp:
|
||||
* Moves cursor to next property and copied address of its sescmd to cursor.
|
||||
* Current propery must be non-null.
|
||||
* If current property is the last on the list, *scur->scmd_ptr_property == NULL
|
||||
*
|
||||
* Router session must be locked
|
||||
*
|
||||
* Router session must be locked
|
||||
*/
|
||||
static bool
|
||||
sescmd_cursor_next(
|
||||
@ -2509,11 +2509,11 @@ getCapabilities()
|
||||
/**
|
||||
* Execute in backends used by current router session.
|
||||
* Save session variable commands to router session property
|
||||
* struct. Thus, they can be replayed in backends which are
|
||||
* struct. Thus, they can be replayed in backends which are
|
||||
* started and joined later.
|
||||
*
|
||||
*
|
||||
* Suppress redundant OK packets sent by backends.
|
||||
*
|
||||
*
|
||||
* The first OK packet is replied to the client.
|
||||
* Return true if succeed, false is returned if router session was closed or
|
||||
* if execute_sescmd_in_backend failed.
|
||||
@ -2535,7 +2535,7 @@ route_session_write(
|
||||
|
||||
/**
|
||||
* These are one-way messages and server doesn't respond to them.
|
||||
* Therefore reply processing is unnecessary and session
|
||||
* Therefore reply processing is unnecessary and session
|
||||
* command property is not needed. It is just routed to all available
|
||||
* backends.
|
||||
*/
|
||||
@ -2592,8 +2592,8 @@ route_session_write(
|
||||
succp = false;
|
||||
goto return_succp;
|
||||
}
|
||||
/**
|
||||
* Additional reference is created to querybuf to
|
||||
/**
|
||||
* Additional reference is created to querybuf to
|
||||
* prevent it from being released before properties
|
||||
* are cleaned up as a part of router sessionclean-up.
|
||||
*/
|
||||
@ -2606,7 +2606,7 @@ route_session_write(
|
||||
for(i = 0; i < router_cli_ses->n_subservice; i++)
|
||||
{
|
||||
subsvc = router_cli_ses->subservice[i];
|
||||
|
||||
|
||||
if(!SUBSVC_IS_CLOSED(subsvc))
|
||||
{
|
||||
sescmd_cursor_t* scur;
|
||||
@ -2619,18 +2619,18 @@ route_session_write(
|
||||
i+1 >= router_cli_ses->n_subservice ? "<" : "");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
scur = subsvc->scur;
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Add one waiter to backend reference.
|
||||
*/
|
||||
subsvc_set_state(subsvc,SUBSVC_WAITING_RESULT);
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Start execution if cursor is not already executing.
|
||||
* Otherwise, cursor will execute pending commands
|
||||
* when it completes with previous commands.
|
||||
@ -2668,7 +2668,7 @@ return_succp:
|
||||
|
||||
/**
|
||||
* Error Handler routine to resolve _backend_ failures. If it succeeds then there
|
||||
* are enough operative backends available and connected. Otherwise it fails,
|
||||
* are enough operative backends available and connected. Otherwise it fails,
|
||||
* and session is terminated.
|
||||
*
|
||||
* @param instance The router instance
|
||||
@ -2677,7 +2677,7 @@ return_succp:
|
||||
* @param backend_dcb The backend DCB
|
||||
* @param action The action: ERRACT_NEW_CONNECTION or ERRACT_REPLY_CLIENT
|
||||
* @param succp Result of action: true if router can continue
|
||||
*
|
||||
*
|
||||
* Even if succp == true connecting to new slave may have failed. succp is to
|
||||
* tell whether router has enough master/slave connections to continue work.
|
||||
*/
|
||||
@ -2694,11 +2694,11 @@ handleError(
|
||||
ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES *) router_session;
|
||||
|
||||
CHK_DCB(backend_dcb);
|
||||
|
||||
|
||||
/** Don't handle same error twice on same DCB */
|
||||
if(backend_dcb->dcb_errhandle_called)
|
||||
{
|
||||
/** we optimistically assume that previous call succeed */
|
||||
/** we optimistically assume that previous call succeed */
|
||||
*succp = true;
|
||||
return;
|
||||
}
|
||||
@ -2763,13 +2763,13 @@ static SUBSERVICE* get_subsvc_from_ses(ROUTER_CLIENT_SES* rses, SESSION* ses)
|
||||
return rses->subservice[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calls hang-up function for DCB if it is not both running and in
|
||||
* Calls hang-up function for DCB if it is not both running and in
|
||||
* master/slave/joined/ndb role. Called by DCB's callback routine.
|
||||
*
|
||||
* TODO: See if there's a way to inject this into the subservices
|
||||
|
@ -139,7 +139,7 @@ GetModuleObject()
|
||||
/**
|
||||
* Create an instance of the router for a particular service
|
||||
* within the gateway.
|
||||
*
|
||||
*
|
||||
* @param service The service this router is being create for
|
||||
* @param options The options for this query router
|
||||
*
|
||||
@ -183,7 +183,7 @@ WEB_SESSION *wsession;
|
||||
* @param instance The router instance data
|
||||
* @param session The session being closed
|
||||
*/
|
||||
static void
|
||||
static void
|
||||
closeSession(ROUTER *instance, void *session)
|
||||
{
|
||||
free(session);
|
||||
@ -196,7 +196,7 @@ static void freeSession(
|
||||
return;
|
||||
}
|
||||
|
||||
static int
|
||||
static int
|
||||
routeQuery(ROUTER *instance, void *session, GWBUF *queue)
|
||||
{
|
||||
WEB_SESSION *wsession = (WEB_SESSION *)session;
|
||||
@ -367,7 +367,7 @@ send_static_html(DCB *dcb, char *html)
|
||||
static void
|
||||
send_index(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
DCB *dcb = session->session->client_dcb;
|
||||
|
||||
send_html_header(dcb);
|
||||
send_static_html(dcb, index_page);
|
||||
@ -382,7 +382,7 @@ DCB *dcb = session->session->client;
|
||||
static void
|
||||
send_css(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
DCB *dcb = session->session->client_dcb;
|
||||
|
||||
send_html_header(dcb);
|
||||
send_static_html(dcb, css);
|
||||
@ -397,7 +397,7 @@ DCB *dcb = session->session->client;
|
||||
static void
|
||||
send_title(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
DCB *dcb = session->session->client_dcb;
|
||||
|
||||
send_html_header(dcb);
|
||||
send_static_html(dcb, title_page);
|
||||
@ -412,7 +412,7 @@ DCB *dcb = session->session->client;
|
||||
static void
|
||||
send_frame1(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
DCB *dcb = session->session->client_dcb;
|
||||
|
||||
send_html_header(dcb);
|
||||
send_static_html(dcb, frame1_page);
|
||||
@ -427,7 +427,7 @@ DCB *dcb = session->session->client;
|
||||
static void
|
||||
send_menu(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
DCB *dcb = session->session->client_dcb;
|
||||
|
||||
send_html_header(dcb);
|
||||
send_static_html(dcb, menu_page);
|
||||
@ -442,7 +442,7 @@ DCB *dcb = session->session->client;
|
||||
static void
|
||||
send_blank(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
DCB *dcb = session->session->client_dcb;
|
||||
|
||||
send_html_header(dcb);
|
||||
send_static_html(dcb, blank_page);
|
||||
@ -465,7 +465,7 @@ service_row(SERVICE *service, DCB *dcb)
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the services page. This produces a table by means of the
|
||||
* Send the services page. This produces a table by means of the
|
||||
* serviceIterate call.
|
||||
*
|
||||
* @param session The router session
|
||||
@ -473,7 +473,7 @@ service_row(SERVICE *service, DCB *dcb)
|
||||
static void
|
||||
send_services(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
DCB *dcb = session->session->client_dcb;
|
||||
|
||||
send_html_header(dcb);
|
||||
dcb_printf(dcb, "<HTML><HEAD>");
|
||||
@ -497,8 +497,8 @@ static void
|
||||
session_row(SESSION *session, DCB *dcb)
|
||||
{
|
||||
dcb_printf(dcb, "<TR><TD>%-16p</TD><TD>%s</TD><TD>%s</TD><TD>%s</TD></TR>\n",
|
||||
session, ((session->client && session->client->remote)
|
||||
? session->client->remote : ""),
|
||||
session, ((session->client_dcb && session->client_dcb->remote)
|
||||
? session->client_dcb->remote : ""),
|
||||
(session->service && session->service->name
|
||||
? session->service->name : ""),
|
||||
session_state(session->state));
|
||||
@ -514,7 +514,7 @@ session_row(SESSION *session, DCB *dcb)
|
||||
static void
|
||||
send_sessions(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
DCB *dcb = session->session->client_dcb;
|
||||
|
||||
send_html_header(dcb);
|
||||
dcb_printf(dcb, "<HTML><HEAD>");
|
||||
@ -550,7 +550,7 @@ server_row(SERVER *server, DCB *dcb)
|
||||
static void
|
||||
send_servers(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
DCB *dcb = session->session->client_dcb;
|
||||
|
||||
send_html_header(dcb);
|
||||
dcb_printf(dcb, "<HTML><HEAD>");
|
||||
@ -586,7 +586,7 @@ monitor_row(MONITOR *monitor, DCB *dcb)
|
||||
static void
|
||||
send_monitors(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
DCB *dcb = session->session->client_dcb;
|
||||
|
||||
send_html_header(dcb);
|
||||
dcb_printf(dcb, "<HTML><HEAD>");
|
||||
@ -608,7 +608,7 @@ DCB *dcb = session->session->client;
|
||||
static void
|
||||
respond_error(WEB_SESSION *session, int err, char *msg)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
DCB *dcb = session->session->client_dcb;
|
||||
|
||||
dcb_printf(dcb, "HTTP/1.1 %d %s\n", err, msg);
|
||||
dcb_printf(dcb, "Content-Type: text/html\n");
|
||||
|
Reference in New Issue
Block a user