Now the MySQL connect phase is fully asynchronous

EINPROGRESS will set the MYSQL_PENDING_CONNECT protocol state
This commit is contained in:
Massimiliano Pinto 2013-07-04 12:43:16 +02:00
parent 2cb2a60a96
commit 4adf121de9
2 changed files with 253 additions and 516 deletions

View File

@ -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

View File

@ -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)";
}
}
/////