Merge branch 'develop' of github.com:skysql/MaxScale into develop

Conflicts:
	server/core/session.c
	server/modules/protocol/mysql_backend.c
	server/modules/routing/readwritesplit/readwritesplit.c

Resolved.

Addition of user in topfilter report and general tidyup
This commit is contained in:
Mark Riddoch
2014-06-18 17:45:57 +01:00
35 changed files with 2005 additions and 854 deletions

View File

@ -245,7 +245,7 @@ HTTPD_session *client_data = NULL;
}
/* force the client connecton close */
dcb->func.close(dcb);
dcb_close(dcb);
return 0;
}
@ -359,7 +359,6 @@ int n_connect = 0;
static int
httpd_close(DCB *dcb)
{
dcb_close(dcb);
return 0;
}

View File

@ -65,7 +65,9 @@ static int gw_backend_hangup(DCB *dcb);
static int backend_write_delayqueue(DCB *dcb);
static void backend_set_delayqueue(DCB *dcb, GWBUF *queue);
static int gw_change_user(DCB *backend_dcb, SERVER *server, SESSION *in_session, GWBUF *queue);
static int gw_session(DCB *backend_dcb, void *data);
#if defined(NOT_USED)
static int gw_session(DCB *backend_dcb, void *data);
#endif
static MYSQL_session* gw_get_shared_session_auth_info(DCB* dcb);
static GWPROTOCOL MyObject = {
@ -79,7 +81,7 @@ static GWPROTOCOL MyObject = {
gw_backend_close, /* Close */
NULL, /* Listen */
gw_change_user, /* Authentication */
gw_session /* Session */
NULL /* Session */
};
/*
@ -195,6 +197,14 @@ static int gw_read_backend_event(DCB *dcb) {
if (gw_read_backend_handshake(backend_protocol) != 0) {
backend_protocol->state = MYSQL_AUTH_FAILED;
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_backend_event] after "
"gw_read_backend_handshake, fd %d, "
"state = MYSQL_AUTH_FAILED.",
pthread_self(),
backend_protocol->owner_dcb->fd)));
} else {
/* handshake decoded, send the auth credentials */
if (gw_send_authentication_to_backend(
@ -204,6 +214,13 @@ static int gw_read_backend_event(DCB *dcb) {
backend_protocol) != 0)
{
backend_protocol->state = MYSQL_AUTH_FAILED;
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_backend_event] after "
"gw_send_authentication_to_backend "
"fd %d, state = MYSQL_AUTH_FAILED.",
pthread_self(),
backend_protocol->owner_dcb->fd)));
} else {
backend_protocol->state = MYSQL_AUTH_RECV;
}
@ -240,6 +257,7 @@ static int gw_read_backend_event(DCB *dcb) {
router = session->service->router;
router_instance = session->service->router_instance;
rsession = session->router_session;
if (backend_protocol->state == MYSQL_AUTH_RECV) {
/*<
@ -251,13 +269,21 @@ static int gw_read_backend_event(DCB *dcb) {
switch (receive_rc) {
case -1:
backend_protocol->state = MYSQL_AUTH_FAILED;
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_backend_event] after "
"gw_receive_backend_authentication "
"fd %d, state = MYSQL_AUTH_FAILED.",
backend_protocol->owner_dcb->fd,
pthread_self())));
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Backend server didn't "
"accept authentication for user "
"%s.",
current_session->user)));
current_session->user)));
break;
case 1:
backend_protocol->state = MYSQL_IDLE;
@ -298,72 +324,50 @@ static int gw_read_backend_event(DCB *dcb) {
*/
spinlock_release(&dcb->authlock);
spinlock_acquire(&dcb->delayqlock);
/*<
* vraa : errorHandle
* check the delayq before the reply
*/
if (dcb->delayq != NULL) {
/* send an error to the client */
mysql_send_custom_error(
dcb->session->client,
1,
0,
"Connection to backend lost.");
// consume all the delay queue
while ((dcb->delayq = gwbuf_consume(
if (dcb->delayq != NULL)
{
while ((dcb->delayq = gwbuf_consume(
dcb->delayq,
GWBUF_LENGTH(dcb->delayq))) != NULL);
}
spinlock_release(&dcb->delayqlock);
/* try reload users' table for next connection */
service_refresh_users(dcb->session->service);
while (session->state != SESSION_STATE_ROUTER_READY &&
session->state != SESSION_STATE_STOPPING)
{
ss_dassert(
session->state == SESSION_STATE_READY ||
session->state ==
SESSION_STATE_ROUTER_READY ||
session->state == SESSION_STATE_STOPPING);
/**
* Session shouldn't be NULL at this point
* anymore. Just checking..
*/
if (session->client->session == NULL)
{
rc = 1;
goto return_rc;
}
usleep(1);
}
if (session->state == SESSION_STATE_STOPPING)
{
goto return_rc;
GWBUF* errbuf;
bool succp;
/* try reload users' table for next connection */
service_refresh_users(dcb->session->service);
#if defined(SS_DEBUG)
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Backend read error handling.")));
#endif
errbuf = mysql_create_custom_error(
1,
0,
"Authentication with backend failed. "
"Session will be closed.");
router->handleError(router_instance,
rsession,
errbuf,
dcb,
ERRACT_REPLY_CLIENT,
&succp);
ss_dassert(!succp);
if (session != NULL)
{
spinlock_acquire(&session->ses_lock);
session->state = SESSION_STATE_STOPPING;
spinlock_release(&session->ses_lock);
}
dcb_close(dcb);
}
spinlock_acquire(&session->ses_lock);
session->state = SESSION_STATE_STOPPING;
spinlock_release(&session->ses_lock);
/**
* rsession shouldn't be NULL since session
* state indicates that it was initialized
* successfully.
*/
rsession = session->router_session;
ss_dassert(rsession != NULL);
LOGIF(LD, (skygw_log_write_flush(
LOGFILE_DEBUG,
"%lu [gw_read_backend_event] "
"Call closeSession for backend's "
"router client session.",
pthread_self())));
/* close router_session */
router->closeSession(router_instance, rsession);
rc = 1;
goto return_rc;
}
@ -401,17 +405,56 @@ static int gw_read_backend_event(DCB *dcb) {
SESSION *session = dcb->session;
CHK_SESSION(session);
/* read available backend data */
rc = dcb_read(dcb, &writebuf);
router = session->service->router;
router_instance = session->service->router_instance;
rsession = session->router_session;
/* read available backend data */
rc = dcb_read(dcb, &writebuf);
if (rc < 0) {
if (rc < 0)
{
/*< vraa : errorHandle */
/*<
* Backend generated EPOLLIN event and if backend has
* failed, connection must be closed to avoid backend
* dcb from getting hanged.
*/
(dcb->func).close(dcb);
GWBUF* errbuf;
bool succp;
/**
* - send error for client
* - mark failed backend BREF_NOT_USED
* - go through all servers and select one according to
* the criteria that user specified in the beginning.
*/
#if defined(SS_DEBUG)
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Backend read error handling #2.")));
#endif
errbuf = mysql_create_custom_error(
1,
0,
"Read from backend failed");
router->handleError(router_instance,
rsession,
errbuf,
dcb,
ERRACT_NEW_CONNECTION,
&succp);
if (!succp)
{
spinlock_acquire(&session->ses_lock);
session->state = SESSION_STATE_STOPPING;
spinlock_release(&session->ses_lock);
}
dcb_close(dcb);
rc = 0;
goto return_rc;
}
@ -420,18 +463,6 @@ static int gw_read_backend_event(DCB *dcb) {
rc = 0;
goto return_rc;
}
router = session->service->router;
router_instance = session->service->router_instance;
rsession = session->router_session;
/* Note the gwbuf doesn't have here a valid queue->command
* descriptions as it is a fresh new one!
* We only have the copied value in dcb->command from
* previuos func.write() and this will be used by the
* router->clientReply
* and pass now the gwbuf to the router
*/
/*<
* If dcb->session->client is freed already it may be NULL.
*/
@ -443,7 +474,8 @@ static int gw_read_backend_event(DCB *dcb) {
if (client_protocol->state == MYSQL_IDLE)
{
router->clientReply(router_instance,
gwbuf_set_type(writebuf, GWBUF_TYPE_MYSQL);
router->clientReply(router_instance,
rsession,
writebuf,
dcb);
@ -451,6 +483,7 @@ static int gw_read_backend_event(DCB *dcb) {
}
goto return_rc;
} else if (dcb->session->client->dcb_role == DCB_ROLE_INTERNAL) {
gwbuf_set_type(writebuf, GWBUF_TYPE_MYSQL);
router->clientReply(router_instance, rsession, writebuf, dcb);
rc = 1;
}
@ -550,29 +583,6 @@ gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue)
MySQLProtocol *backend_protocol = dcb->protocol;
int rc = 0;
/*<
* Don't write to backend if backend_dcb is not in poll set anymore.
*/
spinlock_acquire(&dcb->dcb_initlock);
if (dcb->state != DCB_STATE_POLLING) {
/*< vraa : errorHandle */
/*< Free buffer memory */
gwbuf_consume(queue, GWBUF_LENGTH(queue));
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_MySQLWrite_backend] Write to backend failed. "
"Backend dcb %p fd %d is %s.",
pthread_self(),
dcb,
dcb->fd,
STRDCBSTATE(dcb->state))));
spinlock_release(&dcb->dcb_initlock);
rc = 0;
goto return_rc;
}
spinlock_release(&dcb->dcb_initlock);
spinlock_acquire(&dcb->authlock);
/**
* Pick action according to state of protocol.
@ -600,11 +610,11 @@ gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue)
queue,
GWBUF_LENGTH(queue))) != NULL);
free(str);
}
rc = 0;
spinlock_release(&dcb->authlock);
goto return_rc;
break;
}
case MYSQL_IDLE:
LOGIF(LD, (skygw_log_write(
@ -616,6 +626,7 @@ gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue)
dcb->fd,
STRPROTOCOLSTATE(backend_protocol->state))));
spinlock_release(&dcb->authlock);
rc = dcb_write(dcb, queue);
goto return_rc;
break;
@ -644,73 +655,57 @@ return_rc:
}
/**
* Backend Error Handling for EPOLLER
*
* Error event handler.
* Create error message, pass it to router's error handler and if error
* handler fails in providing enough backend servers, mark session being
* closed and call DCB close function which triggers closing router session
* and related backends (if any exists.
*/
static int gw_error_backend_event(DCB *dcb) {
SESSION *session;
void *rsession;
ROUTER_OBJECT *router;
ROUTER *router_instance;
int rc = 0;
static int gw_error_backend_event(DCB *dcb)
{
SESSION* session;
void* rsession;
ROUTER_OBJECT* router;
ROUTER* router_instance;
int rc = 0;
GWBUF* errbuf;
bool succp;
CHK_DCB(dcb);
session = dcb->session;
CHK_SESSION(session);
rsession = session->router_session;
router = session->service->router;
router_instance = session->service->router_instance;
router = session->service->router;
router_instance = session->service->router_instance;
#if defined(SS_DEBUG)
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Backend error event handling.")));
#endif
if (dcb->state != DCB_STATE_POLLING) {
/*< vraa : errorHandle */
/*<
* if client is not available it needs to be handled in send
* function. Session != NULL, that is known.
*/
mysql_send_custom_error(
dcb->session->client,
1,
0,
"Writing to backend failed.");
rc = 0;
} else {
/*< vraa : errorHandle */
mysql_send_custom_error(
dcb->session->client,
1,
0,
"Closed backend connection.");
rc = 1;
}
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_DEBUG,
"%lu [gw_error_backend_event] Some error occurred in backend. "
"rc = %d",
pthread_self(),
rc)));
if (session->state == SESSION_STATE_ROUTER_READY)
{
errbuf = mysql_create_custom_error(
1,
0,
"Lost connection to backend server.");
router->handleError(router_instance,
rsession,
errbuf,
dcb,
ERRACT_NEW_CONNECTION,
&succp);
/** There are not required backends available, close session. */
if (!succp) {
spinlock_acquire(&session->ses_lock);
session->state = SESSION_STATE_STOPPING;
spinlock_release(&session->ses_lock);
rsession = session->router_session;
/*<
* rsession should never be NULL here.
*/
ss_dassert(rsession != NULL);
LOGIF(LD, (skygw_log_write_flush(
LOGFILE_DEBUG,
"%lu [gw_error_backend_event] "
"Call closeSession for backend "
"session.",
pthread_self())));
router->closeSession(router_instance, rsession);
}
return rc;
dcb_close(dcb);
return 1;
}
/*
@ -811,7 +806,11 @@ return_fd:
/**
* Hangup routine the backend dcb: it does nothing
* Error event handler.
* Create error message, pass it to router's error handler and if error
* handler fails in providing enough backend servers, mark session being
* closed and call DCB close function which triggers closing router session
* and related backends (if any exists.
*
* @param dcb The current Backend DCB
* @return 1 always
@ -819,21 +818,90 @@ return_fd:
static int
gw_backend_hangup(DCB *dcb)
{
/*< vraa : errorHandle */
SESSION* session;
void* rsession;
ROUTER_OBJECT* router;
ROUTER* router_instance;
int rc = 0;
bool succp;
GWBUF* errbuf;
CHK_DCB(dcb);
session = dcb->session;
CHK_SESSION(session);
rsession = session->router_session;
router = session->service->router;
router_instance = session->service->router_instance;
#if defined(SS_DEBUG)
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Backend hangup error handling.")));
#endif
errbuf = mysql_create_custom_error(
1,
0,
"Lost connection to backend server.");
router->handleError(router_instance,
rsession,
errbuf,
dcb,
ERRACT_NEW_CONNECTION,
&succp);
/** There are not required backends available, close session. */
if (!succp) {
#if defined(SS_DEBUG)
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Backend hangup -> closing session.")));
#endif
spinlock_acquire(&session->ses_lock);
session->state = SESSION_STATE_STOPPING;
spinlock_release(&session->ses_lock);
}
dcb_close(dcb);
return 1;
}
/**
* Close the backend dcb
*
* Send COM_QUIT to backend so that it can be closed.
* @param dcb The current Backend DCB
* @return 1 always
*/
static int
gw_backend_close(DCB *dcb)
{
/*< vraa : errorHandle */
dcb_close(dcb);
DCB* client_dcb;
SESSION* session;
GWBUF* quitbuf;
bool succp;
CHK_DCB(dcb);
session = dcb->session;
CHK_SESSION(session);
quitbuf = mysql_create_com_quit(NULL, 0);
/** Send COM_QUIT to the backend being closed */
mysql_send_com_quit(dcb, 0, quitbuf);
if (session != NULL && session->state == SESSION_STATE_STOPPING)
{
client_dcb = session->client;
if (client_dcb != NULL &&
client_dcb->state == DCB_STATE_POLLING)
{
/** Close client DCB */
dcb_close(client_dcb);
}
}
return 1;
}
@ -883,27 +951,56 @@ static int backend_write_delayqueue(DCB *dcb)
}
else
{
localq = dcb->delayq;
dcb->delayq = NULL;
spinlock_release(&dcb->delayqlock);
rc = dcb_write(dcb, localq);
localq = dcb->delayq;
dcb->delayq = NULL;
spinlock_release(&dcb->delayqlock);
rc = dcb_write(dcb, localq);
}
if (rc == 0) {
if (rc == 0)
{
GWBUF* errbuf;
bool succp;
ROUTER_OBJECT *router = NULL;
ROUTER *router_instance = NULL;
void *rsession = NULL;
SESSION *session = dcb->session;
int receive_rc = 0;
CHK_SESSION(session);
router = session->service->router;
router_instance = session->service->router_instance;
rsession = session->router_session;
#if defined(SS_DEBUG)
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : failed to write buffered data to back-end "
"server. Buffer was empty of back-end was disconnected "
"during operation.")));
mysql_send_custom_error(
dcb->session->client,
1,
0,
"Backend write delayqueue error handling.")));
#endif
errbuf = mysql_create_custom_error(
1,
0,
"Failed to write buffered data to back-end server. "
"Buffer was empty or back-end was disconnected during "
"operation.");
dcb_close(dcb);
"operation. Session will be closed.");
router->handleError(router_instance,
rsession,
errbuf,
dcb,
ERRACT_NEW_CONNECTION,
&succp);
if (!succp)
{
if (session != NULL)
{
spinlock_acquire(&session->ses_lock);
session->state = SESSION_STATE_STOPPING;
spinlock_release(&session->ses_lock);
}
dcb_close(dcb);
}
}
return rc;
@ -911,7 +1008,12 @@ static int backend_write_delayqueue(DCB *dcb)
static int gw_change_user(DCB *backend, SERVER *server, SESSION *in_session, GWBUF *queue) {
static int gw_change_user(
DCB *backend,
SERVER *server,
SESSION *in_session,
GWBUF *queue)
{
MYSQL_session *current_session = NULL;
MySQLProtocol *backend_protocol = NULL;
MySQLProtocol *client_protocol = NULL;
@ -997,6 +1099,7 @@ static int gw_change_user(DCB *backend, SERVER *server, SESSION *in_session, GWB
* @param
* @return always 1
*/
/*
static int gw_session(DCB *backend_dcb, void *data) {
GWBUF *queue = NULL;
@ -1006,3 +1109,4 @@ static int gw_session(DCB *backend_dcb, void *data) {
return 1;
}
*/

View File

@ -483,6 +483,11 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
if (auth_token)
free(auth_token);
if (auth_ret == 0)
{
dcb->user = strdup(client_data->user);
}
return auth_ret;
}
@ -504,75 +509,32 @@ gw_MySQLWrite_client(DCB *dcb, GWBUF *queue)
* @param dcb Descriptor control block
* @return 0 if succeed, 1 otherwise
*/
int gw_read_client_event(DCB* dcb) {
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 b = -1;
int rc = 0;
int nbytes_read = 0;
CHK_DCB(dcb);
protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
CHK_PROTOCOL(protocol);
/**
* Check how many bytes are readable in dcb->fd.
*/
if (ioctl(dcb->fd, FIONREAD, &b) != 0) {
int eno = errno;
errno = 0;
LOGIF(LE, (skygw_log_write(
LOGFILE_ERROR,
"%lu [gw_read_client_event] ioctl FIONREAD for fd "
"%d failed. errno %d, %s. dcb->state = %d",
pthread_self(),
dcb->fd,
eno,
strerror(eno),
dcb->state)));
rc = 1;
goto return_rc;
rc = dcb_read(dcb, &read_buffer);
if (rc < 0)
{
dcb_close(dcb);
}
/*
* Handle the closed client socket.
*/
if (b == 0) {
char c;
int l_errno = 0;
int r = -1;
rc = 0;
/* try to read 1 byte, without consuming the socket buffer */
r = recv(dcb->fd, &c, sizeof(char), MSG_PEEK);
l_errno = errno;
if (r <= 0) {
if ( (l_errno == EAGAIN) || (l_errno == EWOULDBLOCK)) {
goto return_rc;
}
// close client socket and the session too
dcb->func.close(dcb);
} else {
// do nothing if reading 1 byte
}
goto return_rc;
}
rc = gw_read_gwbuff(dcb, &read_buffer, b);
if (rc != 0) {
goto return_rc;
}
nbytes_read = gwbuf_length(read_buffer);
ss_dassert(nbytes_read > 0);
if (nbytes_read == 0)
{
goto return_rc;
}
/**
* if read queue existed appent read to it.
* if length of read buffer is less than 3 or less than mysql packet
@ -602,7 +564,8 @@ int gw_read_client_event(DCB* dcb) {
else
{
/**
* There is at least one complete mysql packet read
* There is at least one complete mysql packet in
* read_buffer.
*/
read_buffer = dcb->dcb_readqueue;
dcb->dcb_readqueue = NULL;
@ -627,58 +590,80 @@ int gw_read_client_event(DCB* dcb) {
switch (protocol->state) {
case MYSQL_AUTH_SENT:
/*
* Read all the data that is available into a chain of buffers
*/
{
int auth_val = -1;
auth_val = gw_mysql_do_authentication(dcb, read_buffer);
// Data handled withot the dcb->func.write
// so consume it now
// be sure to consume it all
read_buffer = gwbuf_consume(read_buffer, nbytes_read);
ss_dassert(read_buffer == NULL || GWBUF_EMPTY(read_buffer));
if (auth_val == 0)
{
SESSION *session = NULL;
protocol->state = MYSQL_AUTH_RECV;
//write to client mysql AUTH_OK packet, packet n. is 2
// start a new session, and connect to backends
/**
* 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) {
if (session != NULL)
{
CHK_SESSION(session);
ss_dassert(session->state != SESSION_STATE_ALLOC);
protocol->state = MYSQL_IDLE;
/**
* Send an AUTH_OK packet to the client,
* packet sequence is # 2
*/
mysql_send_ok(dcb, 2, 0, NULL);
} else {
}
else
{
protocol->state = MYSQL_AUTH_FAILED;
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_client_event] session "
"creation failed. fd %d, "
"state = MYSQL_AUTH_FAILED.",
protocol->owner_dcb->fd,
pthread_self())));
/** Send ERR 1045 to client */
mysql_send_auth_error(
dcb,
2,
0,
"failed to create new session");
dcb->func.close(dcb);
dcb_close(dcb);
}
}
else
{
protocol->state = MYSQL_AUTH_FAILED;
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_client_event] after "
"gw_mysql_do_authentication, fd %d, "
"state = MYSQL_AUTH_FAILED.",
protocol->owner_dcb->fd,
pthread_self())));
/** Send ERR 1045 to client */
mysql_send_auth_error(
dcb,
2,
0,
"Authorization failed");
dcb->func.close(dcb);
dcb_close(dcb);
}
}
break;
case MYSQL_IDLE:
/*
* Read all the data that is available into a chain of buffers
*/
{
uint8_t cap = 0;
uint8_t *ptr_buff = NULL;
@ -686,14 +671,16 @@ int gw_read_client_event(DCB* dcb) {
bool stmt_input; /*< router input type */
session = dcb->session;
ss_dassert( session!= NULL);
// get the backend session, if available
if (session != NULL) {
if (session != NULL)
{
CHK_SESSION(session);
router = session->service->router;
router_instance =
session->service->router_instance;
rsession = session->router_session;
ss_dassert(rsession != NULL);
}
/* Now, we are assuming in the first buffer there is
@ -710,9 +697,11 @@ int gw_read_client_event(DCB* dcb) {
* COM_QUIT : close client dcb
* else : write custom error to client dcb.
*/
if(rsession == NULL) {
if(rsession == NULL)
{
/** COM_QUIT */
if (mysql_command == '\x01') {
if (mysql_command == '\x01')
{
LOGIF(LD, (skygw_log_write_flush(
LOGFILE_DEBUG,
"%lu [gw_read_client_event] Client read "
@ -720,8 +709,20 @@ int gw_read_client_event(DCB* dcb) {
"client dcb %p.",
pthread_self(),
dcb)));
(dcb->func).close(dcb);
} else {
/**
* close router session and that closes
* backends
*/
dcb_close(dcb);
}
else
{
#if defined(SS_DEBUG)
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Client read error handling.")));
#endif
/* Send a custom error as MySQL command reply */
mysql_send_custom_error(
dcb,
@ -729,16 +730,16 @@ int gw_read_client_event(DCB* dcb) {
0,
"Can't route query. Connection to "
"backend lost");
protocol->state = MYSQL_IDLE;
}
rc = 1;
/** Free buffer */
read_buffer = gwbuf_consume(read_buffer, nbytes_read);
goto return_rc;
}
/** Ask what type of input the router expects */
cap = router->getCapabilities(router_instance, rsession);
if (cap == 0 || (cap == RCAP_TYPE_PACKET_INPUT))
{
stmt_input = false;
@ -756,7 +757,6 @@ int gw_read_client_event(DCB* dcb) {
"%lu [gw_read_client_event] Reading router "
"capabilities failed.",
pthread_self())));
mysql_send_custom_error(dcb,
1,
0,
@ -765,19 +765,20 @@ int gw_read_client_event(DCB* dcb) {
rc = 1;
goto return_rc;
}
/** Route COM_QUIT to backend */
if (mysql_command == '\x01') {
if (mysql_command == '\x01')
{
/**
* Sends COM_QUIT packets since buffer is already
* created. A BREF_CLOSED flag is set so dcb_close won't
* send redundant COM_QUIT.
*/
SESSION_ROUTE_QUERY(session, read_buffer);
LOGIF(LD, (skygw_log_write_flush(
LOGFILE_DEBUG,
"%lu [gw_read_client_event] Routed COM_QUIT to "
"backend. Close client dcb %p",
pthread_self(),
dcb)));
/** close client connection, closes router session too */
rc = dcb->func.close(dcb);
/**
* Close router session which causes closing of backends.
*/
dcb_close(dcb);
}
else
{
@ -788,6 +789,7 @@ int gw_read_client_event(DCB* dcb) {
* to router.
*/
rc = route_by_statement(session, read_buffer);
if (read_buffer != NULL)
{
/** add incomplete mysql packet to read queue */
@ -804,13 +806,32 @@ int gw_read_client_event(DCB* dcb) {
if (rc == 1) {
rc = 0; /**< here '0' means success */
} else {
mysql_send_custom_error(dcb,
1,
0,
"Query routing failed. "
"Connection to backend "
"lost.");
protocol->state = MYSQL_IDLE;
GWBUF* errbuf;
bool succp;
errbuf = mysql_create_custom_error(
1,
0,
"Write to backend failed. Session closed.");
#if defined(SS_DEBUG)
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Client routing error handling.")));
#endif
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Routing the query failed. "
"Session will be closed.")));
router->handleError(router_instance,
rsession,
errbuf,
dcb,
ERRACT_REPLY_CLIENT,
&succp);
ss_dassert(!succp);
dcb_close(dcb);
}
}
goto return_rc;
@ -1170,23 +1191,31 @@ int gw_MySQLAccept(DCB *listener)
client_dcb->fd = c_sock;
// get client address
if ( client_conn.sa_family == AF_UNIX) {
if ( client_conn.sa_family == AF_UNIX)
{
// client address
client_dcb->remote = strdup("localhost_from_socket");
// set localhost IP for user authentication
(client_dcb->ipv4).sin_addr.s_addr = 0x0100007F;
} else {
}
else
{
/* client IPv4 in raw data*/
memcpy(&client_dcb->ipv4, (struct sockaddr_in *)&client_conn, sizeof(struct sockaddr_in));
memcpy(&client_dcb->ipv4,
(struct sockaddr_in *)&client_conn,
sizeof(struct sockaddr_in));
/* client IPv4 in string representation */
client_dcb->remote = (char *)calloc(INET_ADDRSTRLEN+1, sizeof(char));
if (client_dcb->remote != NULL) {
inet_ntop(AF_INET, &(client_dcb->ipv4).sin_addr, client_dcb->remote, INET_ADDRSTRLEN);
if (client_dcb->remote != NULL)
{
inet_ntop(AF_INET,
&(client_dcb->ipv4).sin_addr,
client_dcb->remote,
INET_ADDRSTRLEN);
}
}
protocol = mysql_protocol_init(client_dcb, c_sock);
ss_dassert(protocol != NULL);
if (protocol == NULL) {
@ -1223,7 +1252,7 @@ int gw_MySQLAccept(DCB *listener)
0,
"MaxScale internal error.");
/** delete client_dcb */
/** close client_dcb */
dcb_close(client_dcb);
/** Previous state is recovered in poll_add_dcb. */
@ -1260,14 +1289,21 @@ return_rc:
static int gw_error_client_event(
DCB* dcb)
{
{
int rc;
SESSION* session;
CHK_DCB(dcb);
rc = dcb->func.close(dcb);
session = dcb->session;
CHK_SESSION(session);
return rc;
#if defined(SS_DEBUG)
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Client error event handling.")));
#endif
dcb_close(dcb);
return 1;
}
static int
@ -1301,11 +1337,9 @@ gw_client_close(DCB *dcb)
router = session->service->router;
router_instance = session->service->router_instance;
rsession = session->router_session;
/** Close router session and all its connections */
router->closeSession(router_instance, rsession);
}
dcb_close(dcb);
return 1;
}
@ -1320,12 +1354,20 @@ gw_client_close(DCB *dcb)
static int
gw_client_hangup_event(DCB *dcb)
{
int rc;
int rc;
SESSION* session;
CHK_DCB(dcb);
rc = dcb->func.close(dcb);
session = dcb->session;
CHK_SESSION(session);
return rc;
#if defined(SS_DEBUG)
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Client hangup error handling.")));
#endif
dcb_close(dcb);
return 1;
}

View File

@ -126,7 +126,9 @@ void gw_mysql_close(MySQLProtocol **ptr) {
* @param conn MySQL protocol structure
* @return 0 on success, 1 on failure
*/
int gw_read_backend_handshake(MySQLProtocol *conn) {
int gw_read_backend_handshake(
MySQLProtocol *conn)
{
GWBUF *head = NULL;
DCB *dcb = conn->owner_dcb;
int n = -1;
@ -135,12 +137,14 @@ int gw_read_backend_handshake(MySQLProtocol *conn) {
int success = 0;
int packet_len = 0;
if ((n = dcb_read(dcb, &head)) != -1) {
if (head) {
if ((n = dcb_read(dcb, &head)) != -1)
{
if (head)
{
payload = GWBUF_DATA(head);
h_len = gwbuf_length(head);
/*
/**
* The mysql packets content starts at byte fifth
* just return with less bytes
*/
@ -148,10 +152,45 @@ int gw_read_backend_handshake(MySQLProtocol *conn) {
if (h_len <= 4) {
/* log error this exit point */
conn->state = MYSQL_AUTH_FAILED;
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_backend_handshake] after "
"dcb_read, fd %d, "
"state = MYSQL_AUTH_FAILED.",
dcb->fd,
pthread_self())));
return 1;
}
//get mysql packet size, 3 bytes
if (payload[4] == 0xff)
{
size_t len = MYSQL_GET_PACKET_LEN(payload);
uint16_t errcode = MYSQL_GET_ERRCODE(payload);
char* bufstr = strndup(&((char *)payload)[7], len-3);
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_receive_backend_auth] Invalid "
"authentication message from backend dcb %p "
"fd %d, ptr[4] = %p, error code %d, msg %s.",
pthread_self(),
dcb,
dcb->fd,
payload[4],
errcode,
bufstr)));
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Invalid authentication message "
"from backend. Error code: %d, Msg : %s",
errcode,
bufstr)));
free(bufstr);
}
//get mysql packet size, 3 bytes
packet_len = gw_mysql_get_byte3(payload);
if (h_len < (packet_len + 4)) {
@ -160,6 +199,15 @@ int gw_read_backend_handshake(MySQLProtocol *conn) {
* packet. Log error this exit point
*/
conn->state = MYSQL_AUTH_FAILED;
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_backend_handshake] after "
"gw_mysql_get_byte3, fd %d, "
"state = MYSQL_AUTH_FAILED.",
pthread_self(),
dcb->fd,
pthread_self())));
return 1;
}
@ -176,6 +224,15 @@ int gw_read_backend_handshake(MySQLProtocol *conn) {
* log error this exit point
*/
conn->state = MYSQL_AUTH_FAILED;
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_backend_handshake] after "
"gw_decode_mysql_server_handshake, fd %d, "
"state = MYSQL_AUTH_FAILED.",
pthread_self(),
conn->owner_dcb->fd,
pthread_self())));
return 1;
}
@ -202,7 +259,10 @@ int gw_read_backend_handshake(MySQLProtocol *conn) {
* @return 0 on success, < 0 on failure
*
*/
int gw_decode_mysql_server_handshake(MySQLProtocol *conn, uint8_t *payload) {
int gw_decode_mysql_server_handshake(
MySQLProtocol *conn,
uint8_t *payload)
{
uint8_t *server_version_end = NULL;
uint16_t mysql_server_capabilities_one = 0;
uint16_t mysql_server_capabilities_two = 0;
@ -216,8 +276,8 @@ int gw_decode_mysql_server_handshake(MySQLProtocol *conn, uint8_t *payload) {
protocol_version = payload[0];
if (protocol_version != GW_MYSQL_PROTOCOL_VERSION) {
/* log error for this */
if (protocol_version != GW_MYSQL_PROTOCOL_VERSION)
{
return -1;
}
@ -257,19 +317,23 @@ int gw_decode_mysql_server_handshake(MySQLProtocol *conn, uint8_t *payload) {
payload+=2;
// get scramble len
if (payload[0] > 0) {
if (payload[0] > 0)
{
scramble_len = payload[0] -1;
ss_dassert(scramble_len > GW_SCRAMBLE_LENGTH_323);
ss_dassert(scramble_len <= GW_MYSQL_SCRAMBLE_SIZE);
if ( (scramble_len < GW_SCRAMBLE_LENGTH_323) || scramble_len > GW_MYSQL_SCRAMBLE_SIZE) {
if ((scramble_len < GW_SCRAMBLE_LENGTH_323) ||
scramble_len > GW_MYSQL_SCRAMBLE_SIZE)
{
/* log this */
return -2;
return -2;
}
} else {
}
else
{
scramble_len = GW_MYSQL_SCRAMBLE_SIZE;
}
// skip 10 zero bytes
payload += 11;
@ -321,26 +385,27 @@ int gw_receive_backend_auth(
}
else if (ptr[4] == 0xff)
{
size_t packetlen = MYSQL_GET_PACKET_LEN(ptr)+4;
char* bufstr = (char *)calloc(1, packetlen-3);
snprintf(bufstr, packetlen-6, "%s", &ptr[7]);
size_t len = MYSQL_GET_PACKET_LEN(ptr);
char* err = strndup(&((char *)ptr)[8], 5);
char* bufstr = strndup(&((char *)ptr)[13], len-4-5);
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_receive_backend_auth] Invalid "
"authentication message from backend dcb %p "
"fd %d, ptr[4] = %p, msg %s.",
"fd %d, ptr[4] = %p, error %s, msg %s.",
pthread_self(),
dcb,
dcb->fd,
ptr[4],
err,
bufstr)));
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Invalid authentication message "
"from backend. Msg : %s",
"from backend. Error : %s, Msg : %s",
err,
bufstr)));
free(bufstr);
@ -367,7 +432,7 @@ int gw_receive_backend_auth(
/*<
* Remove data from buffer.
*/
head = gwbuf_consume(head, GWBUF_LENGTH(head));
while ((head = gwbuf_consume(head, GWBUF_LENGTH(head))) != NULL);
}
else if (n == 0)
{
@ -634,8 +699,8 @@ int gw_do_connect_to_backend(
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error: Establishing connection to backend server "
"%s:%d failed.\n\t\t Socket creation failed due "
"%d, %s.",
"%s:%d failed.\n\t\t Socket creation failed "
"due %d, %s.",
host,
port,
eno,
@ -736,6 +801,145 @@ gw_mysql_protocol_state2string (int state) {
}
}
GWBUF* mysql_create_com_quit(
GWBUF* bufparam,
int packet_number)
{
uint8_t* data;
GWBUF* buf;
if (bufparam == NULL)
{
buf = gwbuf_alloc(COM_QUIT_PACKET_SIZE);
}
else
{
buf = bufparam;
}
if (buf == NULL)
{
return 0;
}
ss_dassert(GWBUF_LENGTH(buf) == COM_QUIT_PACKET_SIZE);
data = GWBUF_DATA(buf);
*data++ = 0x1;
*data++ = 0x0;
*data++ = 0x0;
*data++ = packet_number;
*data = 0x1;
return buf;
}
int mysql_send_com_quit(
DCB* dcb,
int packet_number,
GWBUF* bufparam)
{
GWBUF *buf;
int nbytes = 0;
CHK_DCB(dcb);
ss_dassert(packet_number <= 255);
if (dcb == NULL || dcb->state == DCB_STATE_ZOMBIE)
{
return 0;
}
if (bufparam == NULL)
{
buf = mysql_create_com_quit(NULL, packet_number);
}
else
{
buf = bufparam;
}
if (buf == NULL)
{
return 0;
}
nbytes = dcb->func.write(dcb, buf);
return nbytes;
}
GWBUF* mysql_create_custom_error(
int packet_number,
int affected_rows,
const char* msg)
{
uint8_t* outbuf = NULL;
uint8_t mysql_payload_size = 0;
uint8_t mysql_packet_header[4];
uint8_t* mysql_payload = NULL;
uint8_t field_count = 0;
uint8_t mysql_err[2];
uint8_t mysql_statemsg[6];
unsigned int mysql_errno = 0;
const char* mysql_error_msg = NULL;
const char* mysql_state = NULL;
GWBUF* errbuf = NULL;
mysql_errno = 2003;
mysql_error_msg = "An errorr occurred ...";
mysql_state = "HY000";
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) +
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));
return errbuf;
}
/**
* mysql_send_custom_error
*
@ -749,79 +953,21 @@ gw_mysql_protocol_state2string (int state) {
* @return packet length
*
*/
int
mysql_send_custom_error (DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message) {
uint8_t *outbuf = NULL;
uint8_t mysql_payload_size = 0;
uint8_t mysql_packet_header[4];
uint8_t *mysql_payload = NULL;
uint8_t field_count = 0;
uint8_t mysql_err[2];
uint8_t mysql_statemsg[6];
unsigned int mysql_errno = 0;
const char *mysql_error_msg = NULL;
const char *mysql_state = NULL;
int mysql_send_custom_error (
DCB *dcb,
int packet_number,
int in_affected_rows,
const char *mysql_message)
{
GWBUF* buf;
int nbytes;
GWBUF *buf = NULL;
if (dcb == NULL ||
dcb->state != DCB_STATE_POLLING)
{
return 0;
}
mysql_errno = 2003;
mysql_error_msg = "An errorr occurred ...";
mysql_state = "HY000";
field_count = 0xff;
gw_mysql_set_byte2(mysql_err, mysql_errno);
mysql_statemsg[0]='#';
memcpy(mysql_statemsg+1, mysql_state, 5);
if (mysql_message != NULL) {
mysql_error_msg = mysql_message;
}
mysql_payload_size = sizeof(field_count) + sizeof(mysql_err) + sizeof(mysql_statemsg) + strlen(mysql_error_msg);
// allocate memory for packet header + payload
buf = gwbuf_alloc(sizeof(mysql_packet_header) + mysql_payload_size);
ss_dassert(buf != NULL);
buf = mysql_create_custom_error(dcb, in_affected_rows, mysql_message);
if (buf == NULL)
{
return 0;
}
outbuf = GWBUF_DATA(buf);
// write packet header with 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 err messg
memcpy(mysql_payload, mysql_error_msg, strlen(mysql_error_msg));
// writing data in the Client buffer queue
nbytes = GWBUF_LENGTH(buf);
dcb->func.write(dcb, buf);
return sizeof(mysql_packet_header) + mysql_payload_size;
return GWBUF_LENGTH(buf);
}
/**
@ -1229,7 +1375,12 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password,
*
*/
int
mysql_send_auth_error (DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message) {
mysql_send_auth_error (
DCB *dcb,
int packet_number,
int in_affected_rows,
const char *mysql_message)
{
uint8_t *outbuf = NULL;
uint8_t mysql_payload_size = 0;
uint8_t mysql_packet_header[4];

View File

@ -343,8 +343,7 @@ TELNETD *telnetd = dcb->protocol;
if (telnetd && telnetd->username)
free(telnetd->username);
dcb_close(dcb);
return 0;
return 0;
}
/**