Now the MySQL connect phase is fully asynchronous
EINPROGRESS will set the MYSQL_PENDING_CONNECT protocol state
This commit is contained in:
parent
2cb2a60a96
commit
4adf121de9
@ -34,10 +34,10 @@
|
||||
* and necessary headers.
|
||||
* 01/07/2013 Massimiliano Pinto Put Log Manager example code behind SS_DEBUG macros.
|
||||
* 03/07/2013 Massimiliano Pinto Added delayq for incoming data before mysql connection
|
||||
* 04/07/2013 Massimiliano Pinto Added asyncrhronous MySQL protocol connection to backend
|
||||
*/
|
||||
|
||||
static char *version_str = "V1.0.0";
|
||||
extern char *gw_strend(register const char *s);
|
||||
int gw_mysql_connect(char *host, int port, char *dbname, char *user, uint8_t *passwd, MySQLProtocol *conn);
|
||||
static int gw_create_backend_connection(DCB *client_dcb, SERVER *server, SESSION *in_session);
|
||||
static int gw_read_backend_event(DCB* dcb);
|
||||
@ -45,15 +45,18 @@ static int gw_write_backend_event(DCB *dcb);
|
||||
static int gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue);
|
||||
static int gw_error_backend_event(DCB *dcb);
|
||||
static int gw_backend_close(DCB *dcb);
|
||||
static int gw_backend_hangup(DCB *dcb);
|
||||
static int backend_write_delayqueue(DCB *dcb);
|
||||
static void backend_set_delayqueue(DCB *dcb, GWBUF *queue);
|
||||
|
||||
extern char *gw_strend(register const char *s);
|
||||
|
||||
static GWPROTOCOL MyObject = {
|
||||
gw_read_backend_event, /* Read - EPOLLIN handler */
|
||||
gw_MySQLWrite_backend, /* Write - data from gateway */
|
||||
gw_write_backend_event, /* WriteReady - EPOLLOUT handler */
|
||||
gw_error_backend_event, /* Error - EPOLLERR handler */
|
||||
NULL, /* HangUp - EPOLLHUP handler */
|
||||
gw_backend_hangup, /* HangUp - EPOLLHUP handler */
|
||||
NULL, /* Accept */
|
||||
gw_create_backend_connection, /* Connect */
|
||||
gw_backend_close, /* Close */
|
||||
@ -101,56 +104,89 @@ GetModuleObject()
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
//backend read event triggered by EPOLLIN
|
||||
//////////////////////////////////////////
|
||||
/**
|
||||
* Backend Read Event for EPOLLIN on the MySQL backend protocol module
|
||||
* @param dcb The backend Descriptor Control Block
|
||||
* @return 1 on operation, 0 for no action
|
||||
*/
|
||||
|
||||
static int gw_read_backend_event(DCB *dcb) {
|
||||
int n;
|
||||
MySQLProtocol *client_protocol = NULL;
|
||||
MySQLProtocol *backend_protocol = NULL;
|
||||
MYSQL_session *current_session = NULL;
|
||||
|
||||
if (dcb)
|
||||
if(dcb->session)
|
||||
if (dcb) {
|
||||
if(dcb->session) {
|
||||
client_protocol = SESSION_PROTOCOL(dcb->session, MySQLProtocol);
|
||||
}
|
||||
|
||||
#ifdef GW_DEBUG_READ_EVENT
|
||||
fprintf(stderr, "Backend ready! Read from Backend %i, write to client %i, client state %i\n", dcb->fd, dcb->session->client->fd, client_protocol->state);
|
||||
#endif
|
||||
backend_protocol = (MySQLProtocol *) dcb->protocol;
|
||||
}
|
||||
|
||||
current_session = (MYSQL_session *)dcb->session->data;
|
||||
|
||||
// backend is not yet ready
|
||||
if ( (backend_protocol->state == MYSQL_ALLOC) || (backend_protocol->state == MYSQL_PENDING_CONNECT)) {
|
||||
fprintf(stderr, ">>>> The backend %i is not ready\n", dcb->fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// backend is conected: read server handshake and write auth request and return
|
||||
if (backend_protocol->state == MYSQL_CONNECTED) {
|
||||
|
||||
gw_read_backend_handshake(backend_protocol);
|
||||
|
||||
gw_send_authentication_to_backend(current_session->db, current_session->user, current_session->client_sha1, backend_protocol);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ready to check the authentication reply
|
||||
if (backend_protocol->state == MYSQL_AUTH_RECV) {
|
||||
int rv = -1;
|
||||
rv = gw_receive_backend_auth(backend_protocol);
|
||||
|
||||
switch (rv) {
|
||||
case MYSQL_FAILED_AUTHENTICATION:
|
||||
backend_protocol->state = MYSQL_AUTH_FAILED;
|
||||
|
||||
fprintf(stderr, ">>>> BACKEND EPOLLIN %i , AUTH FAILED %i\n", dcb->fd, backend_protocol->state);
|
||||
|
||||
dcb_close(dcb);
|
||||
|
||||
return 1;
|
||||
|
||||
case MYSQL_SUCCESFUL_AUTHENTICATION:
|
||||
spinlock_acquire(&dcb->authlock);
|
||||
|
||||
backend_protocol->state = MYSQL_IDLE;
|
||||
|
||||
fprintf(stderr, ">>>> BACKEND EPOLLIN %i , auth is OK, %i\n", dcb->fd, backend_protocol->state);
|
||||
|
||||
// check the delay queue
|
||||
if(dcb->delayq) {
|
||||
fprintf(stderr, ">>> Mysql Backend is ok, Force writing to the backend from delay queue. Backend Proto state is %i, Client Proto state is %i. Writing %i bytes\n", backend_protocol->state, client_protocol->state, gwbuf_length(dcb->delayq));
|
||||
backend_write_delayqueue(dcb);
|
||||
spinlock_release(&dcb->authlock);
|
||||
return 1;
|
||||
}
|
||||
spinlock_release(&dcb->authlock);
|
||||
|
||||
return 1;
|
||||
|
||||
default:
|
||||
// no other authentication state here right now, so just return
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// reading MySQL command output from backend and writing to the client
|
||||
|
||||
if ((client_protocol->state == MYSQL_WAITING_RESULT) || (client_protocol->state == MYSQL_IDLE)) {
|
||||
int b = -1;
|
||||
GWBUF *buffer, *head;
|
||||
GWBUF *head = NULL;
|
||||
|
||||
if (ioctl(dcb->fd, FIONREAD, &b)) {
|
||||
fprintf(stderr, "Backend Ioctl FIONREAD error %i, %s\n", errno , strerror(errno));
|
||||
} else {
|
||||
//fprintf(stderr, "Backend IOCTL FIONREAD bytes to read = %i\n", b);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read all the data that is available into a chain of buffers
|
||||
*/
|
||||
head = NULL;
|
||||
while (b > 0)
|
||||
{
|
||||
int bufsize = b < MAX_BUFFER_SIZE ? b : MAX_BUFFER_SIZE;
|
||||
if ((buffer = gwbuf_alloc(bufsize)) == NULL)
|
||||
{
|
||||
/* Bad news, we have run out of memory */
|
||||
return 0;
|
||||
}
|
||||
GW_NOINTR_CALL(n = read(dcb->fd, GWBUF_DATA(buffer), bufsize); dcb->stats.n_reads++);
|
||||
if (n < 0)
|
||||
{
|
||||
// if eerno == EAGAIN || EWOULDBLOCK is missing
|
||||
// do the right task, not just break
|
||||
break;
|
||||
}
|
||||
|
||||
head = gwbuf_append(head, buffer);
|
||||
|
||||
// how many bytes left
|
||||
b -= n;
|
||||
}
|
||||
// read data
|
||||
dcb_read(dcb, &head);
|
||||
|
||||
// write the gwbuffer to client
|
||||
dcb->session->client->func.write(dcb->session->client, head);
|
||||
@ -168,6 +204,23 @@ static int gw_read_backend_event(DCB *dcb) {
|
||||
* @return The number of bytes written
|
||||
*/
|
||||
static int gw_write_backend_event(DCB *dcb) {
|
||||
MySQLProtocol *backend_protocol = dcb->protocol;
|
||||
|
||||
//fprintf(stderr, ">>>> Backend %i, protocol state [%s]\n", backend_protocol->fd, gw_mysql_protocol_state2string(backend_protocol->state));
|
||||
|
||||
// spinlock_acquire(&dcb->connectlock);
|
||||
|
||||
if (backend_protocol->state == MYSQL_PENDING_CONNECT) {
|
||||
//fprintf(stderr, ">>>> Now the backend %i is CONNECTED\n", backend_protocol->fd);
|
||||
backend_protocol->state = MYSQL_CONNECTED;
|
||||
|
||||
// spinlock_release(&dcb->connectlock);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// spinlock_release(&dcb->connectlock);
|
||||
|
||||
return dcb_drain_writeq(dcb);
|
||||
}
|
||||
|
||||
@ -181,9 +234,29 @@ static int gw_write_backend_event(DCB *dcb) {
|
||||
static int
|
||||
gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue)
|
||||
{
|
||||
MySQLProtocol *backend_protocol = dcb->protocol;
|
||||
|
||||
spinlock_acquire(&dcb->authlock);
|
||||
|
||||
// put incoming data to the delay queue unless backend is connected with auth ok
|
||||
if (backend_protocol->state != MYSQL_IDLE) {
|
||||
fprintf(stderr, ">>> Writing in the backend %i delay queue\n", dcb->fd);
|
||||
|
||||
backend_set_delayqueue(dcb, queue);
|
||||
spinlock_release(&dcb->authlock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
spinlock_release(&dcb->authlock);
|
||||
|
||||
// Normal flow of backend write;
|
||||
return dcb_write(dcb, queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Backend Error Handling
|
||||
*
|
||||
*/
|
||||
static int gw_error_backend_event(DCB *dcb) {
|
||||
|
||||
fprintf(stderr, "#### Handle Backend error function for %i\n", dcb->fd);
|
||||
@ -193,9 +266,6 @@ static int gw_error_backend_event(DCB *dcb) {
|
||||
fprintf(stderr, "Backend poll_remove_dcb: from events check failed to delete %i, [%i]:[%s]\n", dcb->fd, errno, strerror(errno));
|
||||
}
|
||||
|
||||
#ifdef GW_EVENT_DEBUG
|
||||
fprintf(stderr, "Backend closing fd [%i]=%i, from events check\n", dcb->fd, protocol->fd);
|
||||
#endif
|
||||
if (dcb->fd) {
|
||||
dcb->state = DCB_STATE_DISCONNECTED;
|
||||
fprintf(stderr, "Freeing backend MySQL conn %p, %p\n", dcb->protocol, &dcb->protocol);
|
||||
@ -208,60 +278,91 @@ static int gw_error_backend_event(DCB *dcb) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new MySQL backend connection.
|
||||
* Create a new ackend connection.
|
||||
*
|
||||
* This routine performs the MySQL connection to the backend and fills the session->backends of the callier dcb
|
||||
* with the new allocatetd dcb and adds the new socket to the poll set
|
||||
* This routine will connect to a backend server
|
||||
*
|
||||
* - backend dcb allocation
|
||||
* - MySQL session data fetch
|
||||
* - backend connection using data in MySQL session
|
||||
*
|
||||
* @param client_dcb The client DCB struct
|
||||
* @param backend The Backend DCB allocated from dcb_connect
|
||||
* @param server The selected server to connect to
|
||||
* @param session The current session from Client DCB
|
||||
* @return 0 on Success or 1 on Failure.
|
||||
*/
|
||||
|
||||
static int gw_create_backend_connection(DCB *backend, SERVER *server, SESSION *session) {
|
||||
MySQLProtocol *ptr_proto = NULL;
|
||||
MySQLProtocol *protocol = NULL;
|
||||
MYSQL_session *s_data = NULL;
|
||||
int rv = -1;
|
||||
|
||||
fprintf(stderr, "HERE, the server to connect is [%s]:[%i]\n", server->name, server->port);
|
||||
//fprintf(stderr, "HERE, the server to connect is [%s]:[%i]\n", server->name, server->port);
|
||||
|
||||
backend->protocol = (MySQLProtocol *) calloc(1, sizeof(MySQLProtocol));
|
||||
protocol = (MySQLProtocol *) calloc(1, sizeof(MySQLProtocol));
|
||||
protocol->state = MYSQL_ALLOC;
|
||||
|
||||
backend->protocol = protocol;
|
||||
|
||||
// put the backend dcb in the protocol struct
|
||||
protocol->descriptor = backend;
|
||||
|
||||
ptr_proto = (MySQLProtocol *)backend->protocol;
|
||||
s_data = (MYSQL_session *)session->client->data;
|
||||
|
||||
// fprintf(stderr, "HERE before connect, s_data is [%p]\n", s_data);
|
||||
// fprintf(stderr, "HERE before connect, username is [%s]\n", s_data->user);
|
||||
// let's try to connecte to a backend server, only connect sys call
|
||||
// The socket descriptor is in Non Blocking status, this is set in the function
|
||||
rv = gw_do_connect_to_backend(server->name, server->port, protocol);
|
||||
|
||||
// this is blocking until auth done
|
||||
if (gw_mysql_connect(server->name, server->port, s_data->db, s_data->user, s_data->client_sha1, backend->protocol) == 0) {
|
||||
memcpy(&backend->fd, &ptr_proto->fd, sizeof(backend->fd));
|
||||
// we could also move later, this in to the gw_do_connect_to_backend using protocol->descriptor
|
||||
|
||||
setnonblocking(backend->fd);
|
||||
fprintf(stderr, "Connected to backend mysql server. fd is %i\n", backend->fd);
|
||||
} else {
|
||||
fprintf(stderr, "<<<< NOT Connected to backend mysql server!!!\n");
|
||||
backend->fd = -1;
|
||||
return -1;
|
||||
memcpy(&backend->fd, &protocol->fd, sizeof(backend->fd));
|
||||
|
||||
switch (rv) {
|
||||
|
||||
case 0:
|
||||
fprintf(stderr, "Connected to backend mysql server. fd is %i\n", backend->fd);
|
||||
protocol->state = MYSQL_CONNECTED;
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
fprintf(stderr, "Connection is PENDING to backend mysql server. fd is %i\n", backend->fd);
|
||||
protocol->state = MYSQL_PENDING_CONNECT;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "<<<< NOT Connected to backend mysql server!!!\n");
|
||||
backend->fd = -1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// if connected, it will be addeed to the epoll from the caller of connect()
|
||||
fprintf(stderr, "--> Backend conn added [%i], in the client session [%i]\n", backend->fd, session->client->fd);
|
||||
|
||||
if (backend->fd <= 0) {
|
||||
perror("ERROR: epoll_ctl: backend sock");
|
||||
backend->fd = -1;
|
||||
return -1;
|
||||
} else {
|
||||
fprintf(stderr, "--> Backend conn added, bk_fd [%i], scramble [%s], is session with client_fd [%i]\n", backend->fd, ptr_proto->scramble, session->client->fd);
|
||||
backend->state = DCB_STATE_POLLING;
|
||||
backend->state = DCB_STATE_POLLING;
|
||||
|
||||
return backend->fd;
|
||||
}
|
||||
return -1;
|
||||
return backend->fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hangup routine the backend dcb: it does nothing right now
|
||||
*
|
||||
* @param dcb The current Backend DCB
|
||||
* @return 1 always
|
||||
*/
|
||||
static int
|
||||
gw_backend_hangup(DCB *dcb)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the backend dcb
|
||||
*
|
||||
* @param dcb The current Backend DCB
|
||||
* @return 1 always
|
||||
*/
|
||||
static int
|
||||
gw_backend_close(DCB *dcb)
|
||||
{
|
||||
@ -269,444 +370,6 @@ gw_backend_close(DCB *dcb)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new MySQL connection.
|
||||
*
|
||||
* This routine performs the full MySQL connection to the specified server.
|
||||
* It does
|
||||
* - socket init
|
||||
* - socket connect
|
||||
* - server handshake parsing
|
||||
* - authenticatio reply
|
||||
* - the Auth ack receive
|
||||
*
|
||||
* Please note, all socket operation are in blocking state
|
||||
* Status: work in progress.
|
||||
*
|
||||
* @param host The TCP/IP host address to connect to
|
||||
* @param port The TCP/IP host port to connect to
|
||||
* @param dbname The optional database name. Use NULL if not interested in
|
||||
* @param user The MySQL database Username: required
|
||||
* @param passwd The MySQL database Password: required
|
||||
* @param conn The MySQLProtocol structure to be filled: must be preallocated with gw_mysql_init()
|
||||
* @return 0 on Success or 1 on Failure.
|
||||
*/
|
||||
int gw_mysql_connect(char *host, int port, char *dbname, char *user, uint8_t *passwd, MySQLProtocol *conn) {
|
||||
|
||||
struct sockaddr_in serv_addr;
|
||||
int compress = 0;
|
||||
int rv;
|
||||
int so = 0;
|
||||
int ciclo = 0;
|
||||
uint8_t buffer[SMALL_CHUNK];
|
||||
uint8_t packet_buffer[SMALL_CHUNK];
|
||||
uint8_t *payload = NULL;
|
||||
int server_protocol;
|
||||
uint8_t *server_version_end = NULL;
|
||||
uint16_t mysql_server_capabilities_one;
|
||||
uint16_t mysql_server_capabilities_two;
|
||||
unsigned long tid =0;
|
||||
long bytes;
|
||||
uint8_t scramble_data_1[8 + 1] = "";
|
||||
uint8_t scramble_data_2[12 + 1] = "";
|
||||
uint8_t capab_ptr[4];
|
||||
int scramble_len;
|
||||
uint8_t scramble[GW_MYSQL_SCRAMBLE_SIZE + 1];
|
||||
uint8_t client_scramble[GW_MYSQL_SCRAMBLE_SIZE + 1];
|
||||
uint8_t client_capabilities[4];
|
||||
uint32_t server_capabilities;
|
||||
uint32_t final_capabilities;
|
||||
char dbpass[129]="";
|
||||
|
||||
char *curr_db = NULL;
|
||||
uint8_t *curr_passwd = NULL;
|
||||
|
||||
if (strlen(dbname))
|
||||
curr_db = dbname;
|
||||
|
||||
if (strlen((char *)passwd))
|
||||
curr_passwd = passwd;
|
||||
|
||||
conn->state = MYSQL_ALLOC;
|
||||
conn->fd = -1;
|
||||
|
||||
memset(&server_capabilities, '\0', sizeof(server_capabilities));
|
||||
memset(&final_capabilities, '\0', sizeof(final_capabilities));
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
//fprintf(stderr, ")))) Connect to MySQL: user[%s], SHA1(passwd)[%s], db [%s]\n", user, passwd, dbname);
|
||||
#endif
|
||||
|
||||
memset(&serv_addr, 0, sizeof serv_addr);
|
||||
serv_addr.sin_family = AF_INET;
|
||||
|
||||
so = socket(AF_INET,SOCK_STREAM,0);
|
||||
if (so < 0) {
|
||||
fprintf(stderr, "Errore creazione socket: [%s] %i\n", strerror(errno), errno);
|
||||
return 1;
|
||||
}
|
||||
|
||||
conn->fd = so;
|
||||
|
||||
setipaddress(&serv_addr.sin_addr, host);
|
||||
serv_addr.sin_port = htons(port);
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "Socket initialized\n");
|
||||
fflush(stderr);
|
||||
#endif
|
||||
|
||||
while(1) {
|
||||
if ((rv = connect(so, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) < 0) {
|
||||
fprintf(stderr, "Errore connect %i, %s: RV = [%i]\n", errno, strerror(errno), rv);
|
||||
|
||||
if (errno == EINPROGRESS) {
|
||||
continue;
|
||||
} else {
|
||||
close(so);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "CONNECT is DONE\n");
|
||||
fprintf(stderr, "Socket FD is %i\n", so);
|
||||
fflush(stderr);
|
||||
#endif
|
||||
|
||||
|
||||
memset(&buffer, '\0', sizeof(buffer));
|
||||
|
||||
bytes = SMALL_CHUNK;
|
||||
|
||||
rv = read(so, buffer, bytes);
|
||||
|
||||
if ( rv >0 ) {
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "RESPONSE ciclo %i HO letto [%s] bytes %li\n",ciclo, buffer, bytes);
|
||||
fflush(stderr);
|
||||
#endif
|
||||
ciclo++;
|
||||
} else {
|
||||
if (rv == 0 && errno == EOF) {
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "EOF reached. Bytes = %li\n", bytes);
|
||||
fflush(stderr);
|
||||
#endif
|
||||
} else {
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "###### Receive error FINAL : connection not completed %i %s: RV = [%i]\n", errno, strerror(errno), rv);
|
||||
#endif
|
||||
close(so);
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fwrite(buffer, bytes, 1, stderr);
|
||||
fflush(stderr);
|
||||
#endif
|
||||
|
||||
//decode mysql handshake
|
||||
|
||||
payload = buffer + 4;
|
||||
server_protocol= payload[0];
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "Server Protocol [%i]\n", server_protocol);
|
||||
|
||||
#endif
|
||||
payload++;
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "Protocol Version [%s]\n", payload);
|
||||
fflush(stderr);
|
||||
#endif
|
||||
|
||||
server_version_end = (uint8_t *) gw_strend((char*) payload);
|
||||
payload = server_version_end + 1;
|
||||
|
||||
// TID
|
||||
tid = gw_mysql_get_byte4(payload);
|
||||
memcpy(&conn->tid, &tid, 4);
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "Thread ID is %lu\n", conn->tid);
|
||||
fflush(stderr);
|
||||
#endif
|
||||
|
||||
payload +=4;
|
||||
|
||||
// scramble_part 1
|
||||
memcpy(scramble_data_1, payload, 8);
|
||||
payload += 8;
|
||||
|
||||
// 1 filler
|
||||
payload++;
|
||||
|
||||
mysql_server_capabilities_one = gw_mysql_get_byte2(payload);
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "Capab_1[\n");
|
||||
fwrite(&mysql_server_capabilities_one, 2, 1, stderr);
|
||||
fflush(stderr);
|
||||
#endif
|
||||
|
||||
//2 capab_part 1 + 1 language + 2 server_status
|
||||
payload +=5;
|
||||
|
||||
mysql_server_capabilities_two = gw_mysql_get_byte2(payload);
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "]Capab_2[\n");
|
||||
fwrite(&mysql_server_capabilities_two, 2, 1, stderr);
|
||||
fprintf(stderr, "]\n");
|
||||
fflush(stderr);
|
||||
#endif
|
||||
|
||||
memcpy(&capab_ptr, &mysql_server_capabilities_one, 2);
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "Capab_1[\n");
|
||||
fwrite(capab_ptr, 2, 1, stderr);
|
||||
fflush(stderr);
|
||||
#endif
|
||||
|
||||
memcpy(&(capab_ptr[2]), &mysql_server_capabilities_two, 2);
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "Capab_2[\n");
|
||||
fwrite(capab_ptr, 2, 1, stderr);
|
||||
fflush(stderr);
|
||||
#endif
|
||||
|
||||
// 2 capab_part 2
|
||||
payload+=2;
|
||||
|
||||
scramble_len = payload[0] -1;
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "Scramble_len [%i]\n", scramble_len);
|
||||
fflush(stderr);
|
||||
#endif
|
||||
|
||||
payload += 11;
|
||||
|
||||
memcpy(scramble_data_2, payload, scramble_len - 8);
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "Scramble_buff1[");
|
||||
fwrite(scramble_data_1, 8, 1, stderr);
|
||||
fprintf(stderr, "]\nScramble_buff2 [");
|
||||
fwrite(scramble_data_2, scramble_len - 8, 1, stderr);
|
||||
fprintf(stderr, "]\n");
|
||||
fflush(stderr);
|
||||
#endif
|
||||
|
||||
memcpy(scramble, scramble_data_1, 8);
|
||||
memcpy(scramble + 8, scramble_data_2, scramble_len - 8);
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "Full Scramble 20 bytes is [\n");
|
||||
fwrite(scramble, GW_MYSQL_SCRAMBLE_SIZE, 1, stderr);
|
||||
fprintf(stderr, "\n]\n");
|
||||
fflush(stderr);
|
||||
#endif
|
||||
|
||||
memcpy(conn->scramble, scramble, GW_MYSQL_SCRAMBLE_SIZE);
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "Scramble from MYSQL_Conn is [\n");
|
||||
fwrite(scramble, GW_MYSQL_SCRAMBLE_SIZE, 1, stderr);
|
||||
fprintf(stderr, "\n]\n");
|
||||
fflush(stderr);
|
||||
fprintf(stderr, "Now sending user, pass & db\n[");
|
||||
fwrite(&server_capabilities, 4, 1, stderr);
|
||||
fprintf(stderr, "]\n");
|
||||
#endif
|
||||
|
||||
final_capabilities = gw_mysql_get_byte4((uint8_t *)&server_capabilities);
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "CAPABS [%u]\n", final_capabilities);
|
||||
fflush(stderr);
|
||||
#endif
|
||||
memset(packet_buffer, '\0', sizeof(packet_buffer));
|
||||
//packet_header(byte3 +1 pack#)
|
||||
packet_buffer[3] = '\x01';
|
||||
|
||||
final_capabilities |= GW_MYSQL_CAPABILITIES_PROTOCOL_41;
|
||||
final_capabilities |= GW_MYSQL_CAPABILITIES_CLIENT;
|
||||
if (compress) {
|
||||
final_capabilities |= GW_MYSQL_CAPABILITIES_COMPRESS;
|
||||
fprintf(stderr, "Backend Connection with compression\n");
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
if (curr_passwd != NULL) {
|
||||
uint8_t hash1[GW_MYSQL_SCRAMBLE_SIZE]="";
|
||||
uint8_t hash2[GW_MYSQL_SCRAMBLE_SIZE]="";
|
||||
uint8_t new_sha[GW_MYSQL_SCRAMBLE_SIZE]="";
|
||||
|
||||
|
||||
memcpy(hash1, passwd, GW_MYSQL_SCRAMBLE_SIZE);
|
||||
gw_sha1_str(hash1, GW_MYSQL_SCRAMBLE_SIZE, hash2);
|
||||
gw_bin2hex(dbpass, hash2, GW_MYSQL_SCRAMBLE_SIZE);
|
||||
gw_sha1_2_str(scramble, GW_MYSQL_SCRAMBLE_SIZE, hash2, GW_MYSQL_SCRAMBLE_SIZE, new_sha);
|
||||
gw_str_xor(client_scramble, new_sha, hash1, GW_MYSQL_SCRAMBLE_SIZE);
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "Hash1 [%s]\n", hash1);
|
||||
fprintf(stderr, "Hash2 [%s]\n", hash2);
|
||||
fprintf(stderr, "SHA1(SHA1(password in hex)\n");
|
||||
fprintf(stderr, "PAss [%s]\n", dbpass);
|
||||
fflush(stderr);
|
||||
fprintf(stderr, "newsha [%s]\n", new_sha);
|
||||
fprintf(stderr, "Client send scramble 20 [\n");
|
||||
fwrite(client_scramble, GW_MYSQL_SCRAMBLE_SIZE, 1, stderr);
|
||||
fprintf(stderr, "\n]\n");
|
||||
fflush(stderr);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (curr_db == NULL) {
|
||||
// now without db!!
|
||||
final_capabilities &= ~GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB;
|
||||
} else {
|
||||
final_capabilities |= GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB;
|
||||
}
|
||||
|
||||
payload = packet_buffer + 4;
|
||||
|
||||
final_capabilities |= GW_MYSQL_CAPABILITIES_PLUGIN_AUTH;
|
||||
|
||||
gw_mysql_set_byte4(client_capabilities, final_capabilities);
|
||||
memcpy(payload, client_capabilities, 4);
|
||||
|
||||
//packet_buffer[4] = '\x8d';
|
||||
//packet_buffer[5] = '\xa6';
|
||||
//packet_buffer[6] = '\x0f';
|
||||
//packet_buffer[7] = '\x00';
|
||||
|
||||
// set now the max-packet size
|
||||
payload += 4;
|
||||
gw_mysql_set_byte4(payload, 16777216);
|
||||
|
||||
// set the charset
|
||||
payload += 4;
|
||||
*payload = '\x08';
|
||||
|
||||
payload++;
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "User is [%s]\n", user);
|
||||
fflush(stderr);
|
||||
#endif
|
||||
|
||||
|
||||
// 4 + 4 + 4 + 1 + 23 = 36
|
||||
payload += 23;
|
||||
memcpy(payload, user, strlen(user));
|
||||
|
||||
// 4 + 4 + 1 + 23 = 32 + 1 (scramble_len) + 20 (fixed_scramble) + 1 (user NULL term) + 1 (db NULL term) = 55
|
||||
bytes = 32;
|
||||
|
||||
bytes += strlen(user);
|
||||
// the NULL
|
||||
bytes++;
|
||||
|
||||
payload += strlen(user);
|
||||
payload++;
|
||||
|
||||
if (curr_passwd != NULL) {
|
||||
// set the auth-length
|
||||
*payload = GW_MYSQL_SCRAMBLE_SIZE;
|
||||
payload++;
|
||||
bytes++;
|
||||
|
||||
//copy the 20 bytes scramble data after packet_buffer+36+user+NULL+1 (byte of auth-length)
|
||||
memcpy(payload, client_scramble, GW_MYSQL_SCRAMBLE_SIZE);
|
||||
|
||||
payload += GW_MYSQL_SCRAMBLE_SIZE;
|
||||
bytes += GW_MYSQL_SCRAMBLE_SIZE;
|
||||
|
||||
} else {
|
||||
// skip the auth-length and write a NULL
|
||||
payload++;
|
||||
bytes++;
|
||||
}
|
||||
|
||||
// if the db is not NULL append it
|
||||
if (curr_db) {
|
||||
memcpy(payload, curr_db, strlen(curr_db));
|
||||
payload += strlen(curr_db);
|
||||
payload++;
|
||||
bytes += strlen(curr_db);
|
||||
// the NULL
|
||||
bytes++;
|
||||
}
|
||||
|
||||
memcpy(payload, "mysql_native_password", strlen("mysql_native_password"));
|
||||
|
||||
payload += strlen("mysql_native_password");
|
||||
payload++;
|
||||
|
||||
bytes +=strlen("mysql_native_password");
|
||||
bytes++;
|
||||
|
||||
gw_mysql_set_byte3(packet_buffer, bytes);
|
||||
|
||||
// the packet header
|
||||
bytes += 4;
|
||||
|
||||
rv = write(so, packet_buffer, bytes);
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "Sent [%s], [%i] bytes\n", packet_buffer, bytes);
|
||||
fflush(stderr);
|
||||
#endif
|
||||
|
||||
if (rv == -1) {
|
||||
fprintf(stderr, "CONNECT Error in send auth\n");
|
||||
}
|
||||
|
||||
bytes = SMALL_CHUNK;
|
||||
|
||||
memset(buffer, '\0', sizeof (buffer));
|
||||
|
||||
rv = read(so, buffer, SMALL_CHUNK);
|
||||
|
||||
if (rv == -1) {
|
||||
fprintf(stderr, "CONNCET Error in recv OK for auth\n");
|
||||
}
|
||||
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "ok packet\[");
|
||||
fwrite(buffer, bytes, 1, stderr);
|
||||
fprintf(stderr, "]\n");
|
||||
fflush(stderr);
|
||||
#endif
|
||||
if (buffer[4] == '\x00') {
|
||||
#ifdef MYSQL_CONN_DEBUG
|
||||
fprintf(stderr, "OK packet received, packet # %i\n", buffer[3]);
|
||||
fflush(stderr);
|
||||
#endif
|
||||
conn->state = MYSQL_IDLE;
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
|
||||
close(so);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine put into the delay queue the input queue
|
||||
* The input is what backend DCB is receiving
|
||||
|
@ -22,7 +22,9 @@
|
||||
* Revision History
|
||||
* Date Who Description
|
||||
* 17/06/2013 Massimiliano Pinto Common MySQL protocol routines
|
||||
* 02/06/2013 Massimiliano Pinto MySQL connect asynchronous phases
|
||||
* 02/07/2013 Massimiliano Pinto MySQL connect asynchronous phases
|
||||
* 04/07/2013 Massimiliano Pinto MySQL connect routine supports EAGAIN
|
||||
* Added gw_mysql_protocol_state2string for printing MySQL the protocol status
|
||||
*/
|
||||
|
||||
#include "mysql_client_server_protocol.h"
|
||||
@ -432,4 +434,76 @@ int gw_send_authentication_to_backend(char *dbname, char *user, uint8_t *passwd,
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only backend connect syscall
|
||||
*/
|
||||
int gw_do_connect_to_backend(char *host, int port, MySQLProtocol *conn) {
|
||||
struct sockaddr_in serv_addr;
|
||||
int rv;
|
||||
int so = 0;
|
||||
|
||||
memset(&serv_addr, 0, sizeof serv_addr);
|
||||
serv_addr.sin_family = AF_INET;
|
||||
|
||||
so = socket(AF_INET,SOCK_STREAM,0);
|
||||
|
||||
conn->fd = so;
|
||||
|
||||
if (so < 0) {
|
||||
fprintf(stderr, "Errore creazione socket: [%s] %i\n", strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
setipaddress(&serv_addr.sin_addr, host);
|
||||
serv_addr.sin_port = htons(port);
|
||||
|
||||
setnonblocking(so);
|
||||
|
||||
if ((rv = connect(so, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) < 0) {
|
||||
fprintf(stderr, "Errore connect %i, %s: RV = [%i]\n", errno, strerror(errno), rv);
|
||||
|
||||
if (errno == EINPROGRESS) {
|
||||
return 1;
|
||||
} else {
|
||||
close(so);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string representation of a MySQL protocol state.
|
||||
*
|
||||
* @param state The protocol state
|
||||
* @return String representation of the state
|
||||
*
|
||||
*/
|
||||
const char *
|
||||
gw_mysql_protocol_state2string (int state) {
|
||||
switch(state) {
|
||||
case MYSQL_ALLOC:
|
||||
return "MySQL Protocl struct allocated";
|
||||
case MYSQL_PENDING_CONNECT:
|
||||
return "MySQL Backend socket PENDING connect";
|
||||
case MYSQL_CONNECTED:
|
||||
return "MySQL Backend socket CONNECTED";
|
||||
case MYSQL_AUTH_SENT:
|
||||
return "MySQL Authentication handshake has been sent";
|
||||
case MYSQL_AUTH_RECV:
|
||||
return "MySQL Received user, password, db and capabilities";
|
||||
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";
|
||||
default:
|
||||
return "MySQL (unknown protocol state)";
|
||||
}
|
||||
}
|
||||
/////
|
||||
|
Loading…
x
Reference in New Issue
Block a user