Merge branch 'develop' into blr
Add instrumentation Remove mutexes Improve gwbuf_append performance Conflicts: server/core/dcb.c server/modules/protocol/mysql_backend.c
This commit is contained in:
@ -22,6 +22,7 @@
|
||||
# headers so that liblog_manager.so can
|
||||
# be linked in.
|
||||
# 09/07/2013 Massimiliano Pinto Added the HTTPD protocol module
|
||||
# 13/06/2014 Mark Riddoch Added thr MaxScale protocol module
|
||||
#
|
||||
include ../../../build_gateway.inc
|
||||
|
||||
@ -45,10 +46,14 @@ TELNETDSRCS=telnetd.c
|
||||
TELNETDOBJ=$(TELNETDSRCS:.c=.o)
|
||||
HTTPDSRCS=httpd.c
|
||||
HTTPDOBJ=$(HTTPDSRCS:.c=.o)
|
||||
SRCS=$(MYSQLCLIENTSRCS) $(MYSQLBACKENDSRCS) $(TELNETDSRCS) $(HTTPDSRCS)
|
||||
MAXSCALEDSRCS=maxscaled.c
|
||||
MAXSCALEDOBJ=$(MAXSCALEDSRCS:.c=.o)
|
||||
SRCS=$(MYSQLCLIENTSRCS) $(MYSQLBACKENDSRCS) $(TELNETDSRCS) $(HTTPDSRCS) \
|
||||
$(MAXSCALEDSRCS)
|
||||
OBJ=$(SRCS:.c=.o)
|
||||
LIBS=$(UTILSPATH)/skygw_utils.o
|
||||
MODULES=libMySQLClient.so libMySQLBackend.so libtelnetd.so libHTTPD.so
|
||||
MODULES=libMySQLClient.so libMySQLBackend.so libtelnetd.so libHTTPD.so \
|
||||
libmaxscaled.so
|
||||
|
||||
all: $(MODULES)
|
||||
|
||||
@ -64,6 +69,9 @@ libtelnetd.so: $(TELNETDOBJ)
|
||||
libHTTPD.so: $(HTTPDOBJ)
|
||||
$(CC) $(LDFLAGS) $(HTTPDOBJ) $(LIBS) -o $@
|
||||
|
||||
libmaxscaled.so: $(MAXSCALEDOBJ)
|
||||
$(CC) $(LDFLAGS) $(MAXSCALEDOBJ) $(LIBS) -lcrypt -o $@
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
@ -74,7 +82,7 @@ tags:
|
||||
ctags $(SRCS) $(HDRS)
|
||||
|
||||
install: $(MODULES)
|
||||
install -D $(MODULES) $(DEST)/MaxScale/modules
|
||||
install -D $(MODULES) $(DEST)/modules
|
||||
|
||||
depend:
|
||||
rm -f depend.mk
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
381
server/modules/protocol/maxscaled.c
Normal file
381
server/modules/protocol/maxscaled.c
Normal file
@ -0,0 +1,381 @@
|
||||
/*
|
||||
* This file is distributed as part of 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 SkySQL Ab 2014
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <dcb.h>
|
||||
#include <buffer.h>
|
||||
#include <service.h>
|
||||
#include <session.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <router.h>
|
||||
#include <poll.h>
|
||||
#include <atomic.h>
|
||||
#include <gw.h>
|
||||
#include <adminusers.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <modinfo.h>
|
||||
#include <maxscaled.h>
|
||||
|
||||
MODULE_INFO info = {
|
||||
MODULE_API_PROTOCOL,
|
||||
MODULE_BETA_RELEASE,
|
||||
GWPROTOCOL_VERSION,
|
||||
"A maxscale protocol for the administration interface"
|
||||
};
|
||||
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
|
||||
/**
|
||||
* @file maxscaled.c - MaxScale administration protocol
|
||||
*
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
* Date Who Description
|
||||
* 13/06/2014 Mark Riddoch Initial implementation
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
static char *version_str = "V1.0.0";
|
||||
|
||||
static int maxscaled_read_event(DCB* dcb);
|
||||
static int maxscaled_write_event(DCB *dcb);
|
||||
static int maxscaled_write(DCB *dcb, GWBUF *queue);
|
||||
static int maxscaled_error(DCB *dcb);
|
||||
static int maxscaled_hangup(DCB *dcb);
|
||||
static int maxscaled_accept(DCB *dcb);
|
||||
static int maxscaled_close(DCB *dcb);
|
||||
static int maxscaled_listen(DCB *dcb, char *config);
|
||||
|
||||
/**
|
||||
* The "module object" for the maxscaled protocol module.
|
||||
*/
|
||||
static GWPROTOCOL MyObject = {
|
||||
maxscaled_read_event, /**< Read - EPOLLIN handler */
|
||||
maxscaled_write, /**< Write - data from gateway */
|
||||
maxscaled_write_event, /**< WriteReady - EPOLLOUT handler */
|
||||
maxscaled_error, /**< Error - EPOLLERR handler */
|
||||
maxscaled_hangup, /**< HangUp - EPOLLHUP handler */
|
||||
maxscaled_accept, /**< Accept */
|
||||
NULL, /**< Connect */
|
||||
maxscaled_close, /**< Close */
|
||||
maxscaled_listen, /**< Create a listener */
|
||||
NULL, /**< Authentication */
|
||||
NULL /**< Session */
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Initialise MaxScaled Protocol module.\n")));
|
||||
}
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
GWPROTOCOL *
|
||||
GetModuleObject()
|
||||
{
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read event for EPOLLIN on the maxscaled protocol module.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
* @return
|
||||
*/
|
||||
static int
|
||||
maxscaled_read_event(DCB* dcb)
|
||||
{
|
||||
int n;
|
||||
GWBUF *head = NULL;
|
||||
SESSION *session = dcb->session;
|
||||
MAXSCALED *maxscaled = (MAXSCALED *)dcb->protocol;
|
||||
char *password;
|
||||
|
||||
if ((n = dcb_read(dcb, &head)) != -1)
|
||||
{
|
||||
if (head)
|
||||
{
|
||||
unsigned char *ptr = GWBUF_DATA(head);
|
||||
ptr = GWBUF_DATA(head);
|
||||
if (GWBUF_LENGTH(head))
|
||||
{
|
||||
switch (maxscaled->state)
|
||||
{
|
||||
case MAXSCALED_STATE_LOGIN:
|
||||
maxscaled->username = strndup(GWBUF_DATA(head), GWBUF_LENGTH(head));
|
||||
maxscaled->state = MAXSCALED_STATE_PASSWD;
|
||||
dcb_printf(dcb, "PASSWORD");
|
||||
gwbuf_consume(head, GWBUF_LENGTH(head));
|
||||
break;
|
||||
case MAXSCALED_STATE_PASSWD:
|
||||
password = strndup(GWBUF_DATA(head), GWBUF_LENGTH(head));
|
||||
if (admin_verify(maxscaled->username, password))
|
||||
{
|
||||
dcb_printf(dcb, "OK----");
|
||||
maxscaled->state = MAXSCALED_STATE_DATA;
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_printf(dcb, "FAILED");
|
||||
maxscaled->state = MAXSCALED_STATE_LOGIN;
|
||||
free(maxscaled->username);
|
||||
}
|
||||
gwbuf_consume(head, GWBUF_LENGTH(head));
|
||||
free(password);
|
||||
break;
|
||||
case MAXSCALED_STATE_DATA:
|
||||
SESSION_ROUTE_QUERY(session, head);
|
||||
dcb_printf(dcb, "OK");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Force the free of the buffer header
|
||||
gwbuf_consume(head, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* EPOLLOUT handler for the maxscaled protocol module.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
* @return
|
||||
*/
|
||||
static int
|
||||
maxscaled_write_event(DCB *dcb)
|
||||
{
|
||||
return dcb_drain_writeq(dcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write routine for the maxscaled protocol module.
|
||||
*
|
||||
* Writes the content of the buffer queue to the socket
|
||||
* observing the non-blocking principles of MaxScale.
|
||||
*
|
||||
* @param dcb Descriptor Control Block for the socket
|
||||
* @param queue Linked list of buffes to write
|
||||
*/
|
||||
static int
|
||||
maxscaled_write(DCB *dcb, GWBUF *queue)
|
||||
{
|
||||
int rc;
|
||||
rc = dcb_write(dcb, queue);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the EPOLLERR event.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
*/
|
||||
static int
|
||||
maxscaled_error(DCB *dcb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the EPOLLHUP event.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
*/
|
||||
static int
|
||||
maxscaled_hangup(DCB *dcb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the EPOLLIN event when the DCB refers to the listening
|
||||
* socket for the protocol.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
* @return The number of new connections created
|
||||
*/
|
||||
static int
|
||||
maxscaled_accept(DCB *dcb)
|
||||
{
|
||||
int n_connect = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int so;
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addrlen = sizeof(struct sockaddr);
|
||||
DCB *client_dcb;
|
||||
MAXSCALED *maxscaled_pr = NULL;
|
||||
|
||||
so = accept(dcb->fd, (struct sockaddr *)&addr, &addrlen);
|
||||
|
||||
if (so == -1)
|
||||
return n_connect;
|
||||
else
|
||||
{
|
||||
atomic_add(&dcb->stats.n_accepts, 1);
|
||||
client_dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER);
|
||||
|
||||
if (client_dcb == NULL)
|
||||
|
||||
{
|
||||
return n_connect;
|
||||
}
|
||||
client_dcb->fd = so;
|
||||
client_dcb->remote = strdup(inet_ntoa(addr.sin_addr));
|
||||
memcpy(&client_dcb->func, &MyObject, sizeof(GWPROTOCOL));
|
||||
client_dcb->session =
|
||||
session_alloc(dcb->session->service, client_dcb);
|
||||
maxscaled_pr = (MAXSCALED *)malloc(sizeof(MAXSCALED));
|
||||
client_dcb->protocol = (void *)maxscaled_pr;
|
||||
|
||||
if (maxscaled_pr == NULL)
|
||||
{
|
||||
dcb_add_to_zombieslist(client_dcb);
|
||||
return n_connect;
|
||||
}
|
||||
|
||||
if (poll_add_dcb(client_dcb) == -1)
|
||||
{
|
||||
dcb_add_to_zombieslist(dcb);
|
||||
return n_connect;
|
||||
}
|
||||
n_connect++;
|
||||
maxscaled_pr->state = MAXSCALED_STATE_LOGIN;
|
||||
maxscaled_pr->username = NULL;
|
||||
dcb_printf(client_dcb, "USER");
|
||||
}
|
||||
}
|
||||
return n_connect;
|
||||
}
|
||||
|
||||
/**
|
||||
* The close handler for the descriptor. Called by the gateway to
|
||||
* explicitly close a connection.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
*/
|
||||
|
||||
static int
|
||||
maxscaled_close(DCB *dcb)
|
||||
{
|
||||
MAXSCALED *maxscaled = dcb->protocol;
|
||||
|
||||
if (maxscaled && maxscaled->username)
|
||||
free(maxscaled->username);
|
||||
|
||||
dcb_close(dcb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maxscale daemon listener entry point
|
||||
*
|
||||
* @param listener The Listener DCB
|
||||
* @param config Configuration (ip:port)
|
||||
*/
|
||||
static int
|
||||
maxscaled_listen(DCB *listener, char *config)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int one = 1;
|
||||
int rc;
|
||||
|
||||
memcpy(&listener->func, &MyObject, sizeof(GWPROTOCOL));
|
||||
|
||||
if (!parse_bindconfig(config, 6033, &addr))
|
||||
return 0;
|
||||
|
||||
|
||||
if ((listener->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// socket options
|
||||
setsockopt(listener->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));
|
||||
// set NONBLOCKING mode
|
||||
setnonblocking(listener->fd);
|
||||
// bind address and port
|
||||
if (bind(listener->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = listen(listener->fd, SOMAXCONN);
|
||||
|
||||
if (rc == 0) {
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"Listening maxscale connections at %s\n",
|
||||
config)));
|
||||
} else {
|
||||
int eno = errno;
|
||||
errno = 0;
|
||||
LOGIF(LE, (skygw_log_write(
|
||||
LOGFILE_ERROR,
|
||||
"Failed to start listening for maxscale admin connections "
|
||||
"due error %d, %s\n\n",
|
||||
eno,
|
||||
strerror(eno))));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (poll_add_dcb(listener) == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -44,7 +44,7 @@
|
||||
|
||||
MODULE_INFO info = {
|
||||
MODULE_API_PROTOCOL,
|
||||
MODULE_ALPHA_RELEASE,
|
||||
MODULE_BETA_RELEASE,
|
||||
GWPROTOCOL_VERSION,
|
||||
"The client to MaxScale MySQL protocol implementation"
|
||||
};
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -495,7 +500,7 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
|
||||
int
|
||||
gw_MySQLWrite_client(DCB *dcb, GWBUF *queue)
|
||||
{
|
||||
return dcb_write(dcb, queue);
|
||||
return dcb_write(dcb, queue);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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;
|
||||
@ -611,9 +574,8 @@ int gw_read_client_event(DCB* dcb) {
|
||||
else
|
||||
{
|
||||
uint8_t* data = (uint8_t *)GWBUF_DATA(read_buffer);
|
||||
size_t packetlen = MYSQL_GET_PACKET_LEN(data)+4;
|
||||
|
||||
if (nbytes_read < 3 || nbytes_read < packetlen)
|
||||
if (nbytes_read < 3 || nbytes_read < MYSQL_GET_PACKET_LEN(data)+4)
|
||||
{
|
||||
gwbuf_append(dcb->dcb_readqueue, read_buffer);
|
||||
rc = 0;
|
||||
@ -624,95 +586,117 @@ int gw_read_client_event(DCB* dcb) {
|
||||
/**
|
||||
* Now there should be at least one complete mysql packet in read_buffer.
|
||||
*/
|
||||
switch (protocol->state) {
|
||||
switch (protocol->protocol_auth_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
|
||||
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) {
|
||||
if (session != NULL)
|
||||
{
|
||||
CHK_SESSION(session);
|
||||
ss_dassert(session->state != SESSION_STATE_ALLOC);
|
||||
protocol->state = MYSQL_IDLE;
|
||||
|
||||
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->state = MYSQL_AUTH_FAILED;
|
||||
}
|
||||
else
|
||||
{
|
||||
protocol->protocol_auth_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;
|
||||
protocol->protocol_auth_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;
|
||||
int mysql_command = -1;
|
||||
uint8_t* payload = NULL;
|
||||
bool stmt_input; /*< router input type */
|
||||
|
||||
ss_dassert(nbytes_read >= 5);
|
||||
|
||||
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
|
||||
* the information form mysql command */
|
||||
ptr_buff = GWBUF_DATA(read_buffer);
|
||||
|
||||
/* get mysql commang at fifth byte */
|
||||
if (ptr_buff) {
|
||||
ss_dassert(nbytes_read >= 5);
|
||||
mysql_command = ptr_buff[4];
|
||||
}
|
||||
payload = GWBUF_DATA(read_buffer);
|
||||
/**
|
||||
* Without rsession there is no access to backend.
|
||||
* 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_IS_COM_QUIT(payload))
|
||||
{
|
||||
LOGIF(LD, (skygw_log_write_flush(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [gw_read_client_event] Client read "
|
||||
@ -720,8 +704,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 +725,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 +752,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 +760,20 @@ int gw_read_client_event(DCB* dcb) {
|
||||
rc = 1;
|
||||
goto return_rc;
|
||||
}
|
||||
|
||||
|
||||
/** Route COM_QUIT to backend */
|
||||
if (mysql_command == '\x01') {
|
||||
if (MYSQL_IS_COM_QUIT(payload))
|
||||
{
|
||||
/**
|
||||
* 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 +784,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 */
|
||||
@ -801,16 +798,35 @@ int gw_read_client_event(DCB* dcb) {
|
||||
}
|
||||
|
||||
/** succeed */
|
||||
if (rc == 1) {
|
||||
if (rc) {
|
||||
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;
|
||||
@ -872,8 +888,8 @@ int gw_write_client_event(DCB *dcb)
|
||||
}
|
||||
protocol = (MySQLProtocol *)dcb->protocol;
|
||||
CHK_PROTOCOL(protocol);
|
||||
|
||||
if (protocol->state == MYSQL_IDLE)
|
||||
|
||||
if (protocol->protocol_auth_state == MYSQL_IDLE)
|
||||
{
|
||||
dcb_drain_writeq(dcb);
|
||||
goto return_1;
|
||||
@ -1170,23 +1186,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) {
|
||||
@ -1207,7 +1231,7 @@ int gw_MySQLAccept(DCB *listener)
|
||||
MySQLSendHandshake(client_dcb);
|
||||
|
||||
// client protocol state change
|
||||
protocol->state = MYSQL_AUTH_SENT;
|
||||
protocol->protocol_auth_state = MYSQL_AUTH_SENT;
|
||||
|
||||
/**
|
||||
* Set new descriptor to event set. At the same time,
|
||||
@ -1223,7 +1247,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 +1284,29 @@ return_rc:
|
||||
|
||||
static int gw_error_client_event(
|
||||
DCB* dcb)
|
||||
{
|
||||
int rc;
|
||||
{
|
||||
SESSION* session;
|
||||
|
||||
CHK_DCB(dcb);
|
||||
|
||||
rc = dcb->func.close(dcb);
|
||||
|
||||
return rc;
|
||||
session = dcb->session;
|
||||
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [gw_error_client_event] Error event handling for DCB %p "
|
||||
"in state %s, session %p.",
|
||||
pthread_self(),
|
||||
dcb,
|
||||
STRDCBSTATE(dcb->state),
|
||||
(session != NULL ? session : NULL))));
|
||||
|
||||
#if defined(SS_DEBUG)
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Client error event handling.")));
|
||||
#endif
|
||||
dcb_close(dcb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1286,26 +1325,38 @@ gw_client_close(DCB *dcb)
|
||||
CHK_PROTOCOL(protocol);
|
||||
}
|
||||
#endif
|
||||
mysql_protocol_done(dcb);
|
||||
|
||||
session = dcb->session;
|
||||
/**
|
||||
* session may be NULL if session_alloc failed.
|
||||
* In that case, router session wasn't created.
|
||||
*/
|
||||
if (session != NULL) {
|
||||
if (session != NULL)
|
||||
{
|
||||
CHK_SESSION(session);
|
||||
spinlock_acquire(&session->ses_lock);
|
||||
session->state = SESSION_STATE_STOPPING;
|
||||
spinlock_release(&session->ses_lock);
|
||||
|
||||
if (session->state == SESSION_STATE_STOPPING)
|
||||
{
|
||||
/**
|
||||
* Session is already getting closed so avoid
|
||||
* redundant calls
|
||||
*/
|
||||
spinlock_release(&session->ses_lock);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
session->state = SESSION_STATE_STOPPING;
|
||||
spinlock_release(&session->ses_lock);
|
||||
}
|
||||
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,18 +1371,28 @@ gw_client_close(DCB *dcb)
|
||||
static int
|
||||
gw_client_hangup_event(DCB *dcb)
|
||||
{
|
||||
int rc;
|
||||
SESSION* session;
|
||||
|
||||
CHK_DCB(dcb);
|
||||
rc = dcb->func.close(dcb);
|
||||
session = dcb->session;
|
||||
|
||||
return rc;
|
||||
if (session != NULL && session->state == SESSION_STATE_ROUTER_READY)
|
||||
{
|
||||
CHK_SESSION(session);
|
||||
}
|
||||
#if defined(SS_DEBUG)
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Client hangup error handling.")));
|
||||
#endif
|
||||
dcb_close(dcb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Detect if buffer includes partial mysql packet or multiple packets.
|
||||
* Store partial packet to pendingqueue. Send complete packets one by one
|
||||
* Store partial packet to dcb_readqueue. Send complete packets one by one
|
||||
* to router.
|
||||
*
|
||||
* It is assumed readbuf includes at least one complete packet.
|
||||
@ -1342,14 +1403,42 @@ static int route_by_statement(SESSION *session, GWBUF *readbuf)
|
||||
{
|
||||
int rc = -1;
|
||||
GWBUF* packetbuf;
|
||||
#if defined(SS_DEBUG)
|
||||
gwbuf_type_t prevtype;
|
||||
GWBUF* tmpbuf;
|
||||
|
||||
tmpbuf = readbuf;
|
||||
while (tmpbuf != NULL)
|
||||
{
|
||||
ss_dassert(GWBUF_IS_TYPE_MYSQL(tmpbuf));
|
||||
tmpbuf=tmpbuf->next;
|
||||
}
|
||||
#endif
|
||||
do
|
||||
{
|
||||
ss_dassert(GWBUF_IS_TYPE_MYSQL(readbuf));
|
||||
|
||||
packetbuf = gw_MySQL_get_next_packet(&readbuf);
|
||||
|
||||
ss_dassert(GWBUF_IS_TYPE_MYSQL(packetbuf));
|
||||
|
||||
if (packetbuf != NULL)
|
||||
{
|
||||
CHK_GWBUF(packetbuf);
|
||||
/**
|
||||
* This means that buffer includes exactly one MySQL
|
||||
* statement.
|
||||
* backend func.write uses the information. MySQL backend
|
||||
* protocol, for example, stores the command identifier
|
||||
* to protocol structure. When some other thread reads
|
||||
* the corresponding response the command tells how to
|
||||
* handle response.
|
||||
*
|
||||
* Set it here instead of gw_read_client_event to make
|
||||
* sure it is set to each (MySQL) packet.
|
||||
*/
|
||||
gwbuf_set_type(packetbuf, GWBUF_TYPE_SINGLE_STMT);
|
||||
/** Route query */
|
||||
rc = SESSION_ROUTE_QUERY(session, packetbuf);
|
||||
}
|
||||
else
|
||||
|
||||
@ -41,6 +41,9 @@ 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);
|
||||
|
||||
static server_command_t* server_command_init(server_command_t* srvcmd,
|
||||
mysql_server_cmd_t cmd);
|
||||
|
||||
|
||||
/**
|
||||
* Creates MySQL protocol structure
|
||||
@ -76,7 +79,10 @@ MySQLProtocol* mysql_protocol_init(
|
||||
strerror(eno))));
|
||||
goto return_p;
|
||||
}
|
||||
p->state = MYSQL_ALLOC;
|
||||
p->protocol_auth_state = MYSQL_ALLOC;
|
||||
p->protocol_command.scom_cmd = MYSQL_COM_UNDEFINED;
|
||||
p->protocol_command.scom_nresponse_packets = 0;
|
||||
p->protocol_command.scom_nbytes_to_read = 0;
|
||||
#if defined(SS_DEBUG)
|
||||
p->protocol_chk_top = CHK_NUM_PROTOCOL;
|
||||
p->protocol_chk_tail = CHK_NUM_PROTOCOL;
|
||||
@ -90,6 +96,31 @@ return_p:
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mysql_protocol_done
|
||||
*
|
||||
* free protocol allocations.
|
||||
*
|
||||
* @param dcb owner DCB
|
||||
*
|
||||
*/
|
||||
void mysql_protocol_done (
|
||||
DCB* dcb)
|
||||
{
|
||||
server_command_t* scmd = ((MySQLProtocol *)dcb->protocol)->protocol_cmd_history;
|
||||
server_command_t* scmd2;
|
||||
|
||||
while (scmd != NULL)
|
||||
{
|
||||
scmd2 = scmd->scom_next;
|
||||
free(scmd);
|
||||
scmd = scmd2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* gw_mysql_close
|
||||
*
|
||||
@ -126,7 +157,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,23 +168,60 @@ 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
|
||||
*/
|
||||
|
||||
if (h_len <= 4) {
|
||||
/* log error this exit point */
|
||||
conn->state = MYSQL_AUTH_FAILED;
|
||||
conn->protocol_auth_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)) {
|
||||
@ -159,7 +229,16 @@ int gw_read_backend_handshake(MySQLProtocol *conn) {
|
||||
* data in buffer less than expected in the
|
||||
* packet. Log error this exit point
|
||||
*/
|
||||
conn->state = MYSQL_AUTH_FAILED;
|
||||
conn->protocol_auth_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;
|
||||
}
|
||||
|
||||
@ -175,11 +254,20 @@ int gw_read_backend_handshake(MySQLProtocol *conn) {
|
||||
* we cannot continue
|
||||
* log error this exit point
|
||||
*/
|
||||
conn->state = MYSQL_AUTH_FAILED;
|
||||
conn->protocol_auth_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;
|
||||
}
|
||||
|
||||
conn->state = MYSQL_AUTH_SENT;
|
||||
conn->protocol_auth_state = MYSQL_AUTH_SENT;
|
||||
|
||||
// consume all the data here
|
||||
head = gwbuf_consume(head, GWBUF_LENGTH(head));
|
||||
@ -202,7 +290,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 +307,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 +348,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 +416,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 +463,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 +730,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,
|
||||
@ -724,18 +820,151 @@ gw_mysql_protocol_state2string (int state) {
|
||||
case MYSQL_AUTH_FAILED:
|
||||
return "MySQL Authentication failed";
|
||||
case MYSQL_IDLE:
|
||||
return "MySQL Auth done. Protocol is idle, waiting for statements";
|
||||
case MYSQL_ROUTING:
|
||||
return "MySQL received command has been routed to backend(s)";
|
||||
case MYSQL_WAITING_RESULT:
|
||||
return "MySQL Waiting for result set";
|
||||
case MYSQL_SESSION_CHANGE:
|
||||
return "MySQL change session";
|
||||
return "MySQL authentication is succesfully done.";
|
||||
default:
|
||||
return "MySQL (unknown protocol 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 +978,19 @@ 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;
|
||||
|
||||
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(packet_number, 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
|
||||
dcb->func.write(dcb, buf);
|
||||
|
||||
return sizeof(mysql_packet_header) + mysql_payload_size;
|
||||
return GWBUF_LENGTH(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1229,7 +1398,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];
|
||||
@ -1349,7 +1523,7 @@ GWBUF* gw_MySQL_get_next_packet(
|
||||
packetbuf = NULL;
|
||||
goto return_packetbuf;
|
||||
}
|
||||
|
||||
/** there is one complete packet in the buffer */
|
||||
if (packetlen == buflen)
|
||||
{
|
||||
packetbuf = gwbuf_clone_portion(readbuf, 0, packetlen);
|
||||
@ -1390,3 +1564,328 @@ return_packetbuf:
|
||||
return packetbuf;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Move <npackets> from buffer pointed to by <*p_readbuf>.
|
||||
*/
|
||||
GWBUF* gw_MySQL_get_packets(
|
||||
GWBUF** p_srcbuf,
|
||||
int* npackets)
|
||||
{
|
||||
GWBUF* packetbuf;
|
||||
GWBUF* targetbuf = NULL;
|
||||
|
||||
while (*npackets > 0 && (packetbuf = gw_MySQL_get_next_packet(p_srcbuf)) != NULL)
|
||||
{
|
||||
targetbuf = gwbuf_append(targetbuf, packetbuf);
|
||||
*npackets -= 1;
|
||||
}
|
||||
ss_dassert(*npackets < 128);
|
||||
ss_dassert(*npackets >= 0);
|
||||
|
||||
return targetbuf;
|
||||
}
|
||||
|
||||
|
||||
static server_command_t* server_command_init(
|
||||
server_command_t* srvcmd,
|
||||
mysql_server_cmd_t cmd)
|
||||
{
|
||||
server_command_t* c;
|
||||
|
||||
if (srvcmd != NULL)
|
||||
{
|
||||
c = srvcmd;
|
||||
}
|
||||
else
|
||||
{
|
||||
c = (server_command_t *)malloc(sizeof(server_command_t));
|
||||
}
|
||||
c->scom_cmd = cmd;
|
||||
c->scom_nresponse_packets = -1;
|
||||
c->scom_nbytes_to_read = 0;
|
||||
c->scom_next = NULL;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static server_command_t* server_command_copy(
|
||||
server_command_t* srvcmd)
|
||||
{
|
||||
server_command_t* c =
|
||||
(server_command_t *)malloc(sizeof(server_command_t));
|
||||
*c = *srvcmd;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
#define MAX_CMD_HISTORY 10
|
||||
|
||||
void protocol_archive_srv_command(
|
||||
MySQLProtocol* p)
|
||||
{
|
||||
server_command_t* s1;
|
||||
server_command_t** s2;
|
||||
int len = 0;
|
||||
|
||||
spinlock_acquire(&p->protocol_lock);
|
||||
|
||||
s1 = &p->protocol_command;
|
||||
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Move command %s from fd %d to command history.",
|
||||
STRPACKETTYPE(s1->scom_cmd),
|
||||
p->owner_dcb->fd)));
|
||||
|
||||
/** Copy to history list */
|
||||
s2 = &p->protocol_cmd_history;
|
||||
|
||||
if (*s2 != NULL)
|
||||
{
|
||||
while ((*s2)->scom_next != NULL)
|
||||
{
|
||||
*s2 = (*s2)->scom_next;
|
||||
len += 1;
|
||||
}
|
||||
}
|
||||
*s2 = server_command_copy(s1);
|
||||
|
||||
/** Keep history limits, remove oldest */
|
||||
if (len > MAX_CMD_HISTORY)
|
||||
{
|
||||
server_command_t* c = p->protocol_cmd_history;
|
||||
p->protocol_cmd_history = p->protocol_cmd_history->scom_next;
|
||||
free(c);
|
||||
}
|
||||
|
||||
/** Remove from command list */
|
||||
if (s1->scom_next == NULL)
|
||||
{
|
||||
p->protocol_command.scom_cmd = MYSQL_COM_UNDEFINED;
|
||||
}
|
||||
else
|
||||
{
|
||||
p->protocol_command = *(s1->scom_next);
|
||||
free(s1->scom_next);
|
||||
}
|
||||
spinlock_release(&p->protocol_lock);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If router expects to get separate, complete statements, add MySQL command
|
||||
* to MySQLProtocol structure. It is removed when response has arrived.
|
||||
*/
|
||||
void protocol_add_srv_command(
|
||||
MySQLProtocol* p,
|
||||
mysql_server_cmd_t cmd)
|
||||
{
|
||||
server_command_t* c;
|
||||
|
||||
spinlock_acquire(&p->protocol_lock);
|
||||
|
||||
/** this is the only server command in protocol */
|
||||
if (p->protocol_command.scom_cmd == MYSQL_COM_UNDEFINED)
|
||||
{
|
||||
/** write into structure */
|
||||
server_command_init(&p->protocol_command, cmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
/** add to the end of list */
|
||||
p->protocol_command.scom_next = server_command_init(NULL, cmd);
|
||||
}
|
||||
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Added command %s to fd %d.",
|
||||
STRPACKETTYPE(cmd),
|
||||
p->owner_dcb->fd)));
|
||||
|
||||
#if defined(SS_DEBUG)
|
||||
c = &p->protocol_command;
|
||||
|
||||
while (c != NULL && c->scom_cmd != MYSQL_COM_UNDEFINED)
|
||||
{
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"fd %d : %d %s",
|
||||
p->owner_dcb->fd,
|
||||
c->scom_cmd,
|
||||
STRPACKETTYPE(c->scom_cmd))));
|
||||
c = c->scom_next;
|
||||
}
|
||||
#endif
|
||||
spinlock_release(&p->protocol_lock);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If router processes separate statements, every stmt has corresponding MySQL
|
||||
* command stored in MySQLProtocol structure.
|
||||
*
|
||||
* Remove current (=oldest) command.
|
||||
*/
|
||||
void protocol_remove_srv_command(
|
||||
MySQLProtocol* p)
|
||||
{
|
||||
server_command_t* s;
|
||||
spinlock_acquire(&p->protocol_lock);
|
||||
s = &p->protocol_command;
|
||||
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Removed command %s from fd %d.",
|
||||
STRPACKETTYPE(s->scom_cmd),
|
||||
p->owner_dcb->fd)));
|
||||
|
||||
if (s->scom_next == NULL)
|
||||
{
|
||||
p->protocol_command.scom_cmd = MYSQL_COM_UNDEFINED;
|
||||
}
|
||||
else
|
||||
{
|
||||
p->protocol_command = *(s->scom_next);
|
||||
free(s->scom_next);
|
||||
}
|
||||
|
||||
spinlock_release(&p->protocol_lock);
|
||||
}
|
||||
|
||||
mysql_server_cmd_t protocol_get_srv_command(
|
||||
MySQLProtocol* p,
|
||||
bool removep)
|
||||
{
|
||||
mysql_server_cmd_t cmd;
|
||||
|
||||
cmd = p->protocol_command.scom_cmd;
|
||||
|
||||
if (removep)
|
||||
{
|
||||
protocol_remove_srv_command(p);
|
||||
}
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [protocol_get_srv_command] Read command %s for fd %d.",
|
||||
pthread_self(),
|
||||
STRPACKETTYPE(cmd),
|
||||
p->owner_dcb->fd)));
|
||||
return cmd;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Examine command type and the readbuf. Conclude response
|
||||
* packet count from the command type or from the first packet
|
||||
* content.
|
||||
* Fails if read buffer doesn't include enough data to read the
|
||||
* packet length.
|
||||
*/
|
||||
void init_response_status (
|
||||
GWBUF* buf,
|
||||
mysql_server_cmd_t cmd,
|
||||
int* npackets,
|
||||
size_t* nbytes_left)
|
||||
{
|
||||
uint8_t* packet;
|
||||
int nparam;
|
||||
int nattr;
|
||||
uint8_t* data;
|
||||
|
||||
ss_dassert(gwbuf_length(buf) >= 3);
|
||||
|
||||
data = (uint8_t *)buf->start;
|
||||
|
||||
if (data[4] == 0xff) /*< error */
|
||||
{
|
||||
*npackets = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (cmd) {
|
||||
case MYSQL_COM_STMT_PREPARE:
|
||||
packet = (uint8_t *)GWBUF_DATA(buf);
|
||||
/** ok + nparam + eof + nattr + eof */
|
||||
nparam = MYSQL_GET_STMTOK_NPARAM(packet);
|
||||
nattr = MYSQL_GET_STMTOK_NATTR(packet);
|
||||
|
||||
*npackets = 1 + nparam + MIN(1, nparam) +
|
||||
nattr + MIN(nattr, 1);
|
||||
break;
|
||||
|
||||
case MYSQL_COM_QUIT:
|
||||
case MYSQL_COM_STMT_SEND_LONG_DATA:
|
||||
case MYSQL_COM_STMT_CLOSE:
|
||||
*npackets = 0; /*< these don't reply anything */
|
||||
break;
|
||||
|
||||
default:
|
||||
/**
|
||||
* assume that other session commands respond
|
||||
* OK or ERR
|
||||
*/
|
||||
*npackets = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*nbytes_left = MYSQL_GET_PACKET_LEN(data) + MYSQL_HEADER_LEN;
|
||||
/**
|
||||
* There is at least one complete packet in the buffer so buffer is bigger
|
||||
* than packet
|
||||
*/
|
||||
ss_dassert(*nbytes_left > 0);
|
||||
ss_dassert(*npackets > 0);
|
||||
ss_dassert(*npackets<128);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Read how many packets are left from current response and how many bytes there
|
||||
* is still to be read from the current packet.
|
||||
*/
|
||||
bool protocol_get_response_status (
|
||||
MySQLProtocol* p,
|
||||
int* npackets,
|
||||
size_t* nbytes)
|
||||
{
|
||||
bool succp;
|
||||
|
||||
CHK_PROTOCOL(p);
|
||||
|
||||
spinlock_acquire(&p->protocol_lock);
|
||||
*npackets = p->protocol_command.scom_nresponse_packets;
|
||||
*nbytes = p->protocol_command.scom_nbytes_to_read;
|
||||
spinlock_release(&p->protocol_lock);
|
||||
|
||||
if (*npackets < 0 && *nbytes == 0)
|
||||
{
|
||||
succp = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
succp = true;
|
||||
}
|
||||
|
||||
return succp;
|
||||
}
|
||||
|
||||
void protocol_set_response_status (
|
||||
MySQLProtocol* p,
|
||||
int npackets_left,
|
||||
size_t nbytes)
|
||||
{
|
||||
|
||||
CHK_PROTOCOL(p);
|
||||
|
||||
spinlock_acquire(&p->protocol_lock);
|
||||
|
||||
p->protocol_command.scom_nbytes_to_read = nbytes;
|
||||
ss_dassert(p->protocol_command.scom_nbytes_to_read >= 0);
|
||||
|
||||
p->protocol_command.scom_nresponse_packets = npackets_left;
|
||||
|
||||
spinlock_release(&p->protocol_lock);
|
||||
}
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
|
||||
MODULE_INFO info = {
|
||||
MODULE_API_PROTOCOL,
|
||||
MODULE_ALPHA_RELEASE,
|
||||
MODULE_BETA_RELEASE,
|
||||
GWPROTOCOL_VERSION,
|
||||
"A telnet deamon protocol for simple administration interface"
|
||||
};
|
||||
@ -83,17 +83,17 @@ static int telnetd_listen(DCB *dcb, char *config);
|
||||
* The "module object" for the telnetd protocol module.
|
||||
*/
|
||||
static GWPROTOCOL MyObject = {
|
||||
telnetd_read_event, /**< Read - EPOLLIN handler */
|
||||
telnetd_write, /**< Write - data from gateway */
|
||||
telnetd_write_event, /**< WriteReady - EPOLLOUT handler */
|
||||
telnetd_error, /**< Error - EPOLLERR handler */
|
||||
telnetd_hangup, /**< HangUp - EPOLLHUP handler */
|
||||
telnetd_accept, /**< Accept */
|
||||
NULL, /**< Connect */
|
||||
telnetd_close, /**< Close */
|
||||
telnetd_listen, /**< Create a listener */
|
||||
NULL, /**< Authentication */
|
||||
NULL /**< Session */
|
||||
telnetd_read_event, /**< Read - EPOLLIN handler */
|
||||
telnetd_write, /**< Write - data from gateway */
|
||||
telnetd_write_event, /**< WriteReady - EPOLLOUT handler */
|
||||
telnetd_error, /**< Error - EPOLLERR handler */
|
||||
telnetd_hangup, /**< HangUp - EPOLLHUP handler */
|
||||
telnetd_accept, /**< Accept */
|
||||
NULL, /**< Connect */
|
||||
telnetd_close, /**< Close */
|
||||
telnetd_listen, /**< Create a listener */
|
||||
NULL, /**< Authentication */
|
||||
NULL /**< Session */
|
||||
};
|
||||
|
||||
static void telnetd_command(DCB *, unsigned char *cmd);
|
||||
@ -343,8 +343,7 @@ TELNETD *telnetd = dcb->protocol;
|
||||
if (telnetd && telnetd->username)
|
||||
free(telnetd->username);
|
||||
|
||||
dcb_close(dcb);
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user