mysql_client_server_protocol.h, new file name that avoids conflicts with previous one

This commit is contained in:
Massimiliano Pinto 2013-06-17 22:47:14 +02:00
parent f4711f5980
commit 4d5215e267
5 changed files with 330 additions and 32 deletions

View File

@ -0,0 +1,201 @@
#ifndef _MYSQL_PROTOCOL_H
#define _MYSQL_PROTOCOL_H
/*
* This file is distributed as part of the SkySQL Gateway. 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 2013
*/
/*
* Revision History
*
* Date Who Description
* 01-06-2013 Mark Riddoch Initial implementation
* 14-06-2013 Massimiliano Pinto Added specific data
* for MySQL session
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <openssl/sha.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdbool.h>
#include <fcntl.h>
#include "dcb.h"
#include "session.h"
#include "buffer.h"
#ifndef MYSQL_SCRAMBLE_LEN
#define MYSQL_SCRAMBLE_LEN GW_MYSQL_SCRAMBLE_SIZE
#endif
#define MYSQL_USER_MAXLEN 128
#define MYSQL_DATABASE_MAXLEN 128
#define GW_VERSION "0.1.0"
#define GW_MYSQL_VERSION "5.5.22-SKYSQL-" GW_VERSION
#define GW_MYSQL_LOOP_TIMEOUT 300000000
#define GW_MYSQL_READ 0
#define GW_MYSQL_WRITE 1
#define GW_MYSQL_PROTOCOL_VERSION 10 // version is 10
#define GW_MYSQL_HANDSHAKE_FILLER 0x00
#define GW_MYSQL_SERVER_CAPABILITIES_BYTE1 0xff
#define GW_MYSQL_SERVER_CAPABILITIES_BYTE2 0xf7
#define GW_MYSQL_SERVER_LANGUAGE 0x08
#define GW_MYSQL_MAX_PACKET_LEN 0xffffffL;
#define GW_MYSQL_SCRAMBLE_SIZE 20
#define GW_NOINTR_CALL(A) do { errno = 0; A; } while (errno == EINTR)
// network buffer is 32K
#define MAX_BUFFER_SIZE 32768
// socket send buffer for backend
#define GW_BACKEND_SO_SNDBUF 1024
#define SMALL_CHUNK 1024
#define MAX_CHUNK SMALL_CHUNK * 8 * 4
#define ToHex(Y) (Y>='0'&&Y<='9'?Y-'0':Y-'A'+10)
struct dcb;
/*
* MySQL Protocol specific state data
*/
typedef struct {
int fd; /* The socket descriptor */
struct dcb *descriptor; /* The DCB of the socket we are running on */
int state; /* Current descriptor state */
char scramble[MYSQL_SCRAMBLE_LEN]; /* server scramble, created or received */
uint32_t server_capabilities; /* server capabilities, created or received */
uint32_t client_capabilities; /* client capabilities, created or received */
unsigned long tid; /* MySQL Thread ID, in handshake */
} MySQLProtocol;
/*
* MySQL session specific data
*
*/
typedef struct mysql_session {
uint8_t client_sha1[MYSQL_SCRAMBLE_LEN]; /* SHA1(passowrd) */
char user[MYSQL_USER_MAXLEN]; /* username */
char db[MYSQL_DATABASE_MAXLEN]; /* database */
} MYSQL_session;
/* MySQL Protocol States */
#define MYSQL_ALLOC 0 /* Allocate data */
#define MYSQL_AUTH_SENT 1 /* Authentication handshake has been sent */
#define MYSQL_AUTH_RECV 2 /* Received user, password, db and capabilities */
#define MYSQL_AUTH_FAILED 3 /* Auth failed, return error packet */
#define MYSQL_IDLE 4 /* Auth done. Protocol is idle, waiting for statements */
#define MYSQL_ROUTING 5 /* The received command has been routed to backend(s) */
#define MYSQL_WAITING_RESULT 6 /* Waiting for result set */
/* Protocol packing macros. */
#define gw_mysql_set_byte2(__buffer, __int) do { \
(__buffer)[0]= (uint8_t)((__int) & 0xFF); \
(__buffer)[1]= (uint8_t)(((__int) >> 8) & 0xFF); } while (0)
#define gw_mysql_set_byte3(__buffer, __int) do { \
(__buffer)[0]= (uint8_t)((__int) & 0xFF); \
(__buffer)[1]= (uint8_t)(((__int) >> 8) & 0xFF); \
(__buffer)[2]= (uint8_t)(((__int) >> 16) & 0xFF); } while (0)
#define gw_mysql_set_byte4(__buffer, __int) do { \
(__buffer)[0]= (uint8_t)((__int) & 0xFF); \
(__buffer)[1]= (uint8_t)(((__int) >> 8) & 0xFF); \
(__buffer)[2]= (uint8_t)(((__int) >> 16) & 0xFF); \
(__buffer)[3]= (uint8_t)(((__int) >> 24) & 0xFF); } while (0)
/* Protocol unpacking macros. */
#define gw_mysql_get_byte2(__buffer) \
(uint16_t)((__buffer)[0] | \
((__buffer)[1] << 8))
#define gw_mysql_get_byte3(__buffer) \
(uint32_t)((__buffer)[0] | \
((__buffer)[1] << 8) | \
((__buffer)[2] << 16))
#define gw_mysql_get_byte4(__buffer) \
(uint32_t)((__buffer)[0] | \
((__buffer)[1] << 8) | \
((__buffer)[2] << 16) | \
((__buffer)[3] << 24))
#define gw_mysql_get_byte8(__buffer) \
((uint64_t)(__buffer)[0] | \
((uint64_t)(__buffer)[1] << 8) | \
((uint64_t)(__buffer)[2] << 16) | \
((uint64_t)(__buffer)[3] << 24) | \
((uint64_t)(__buffer)[4] << 32) | \
((uint64_t)(__buffer)[5] << 40) | \
((uint64_t)(__buffer)[6] << 48) | \
((uint64_t)(__buffer)[7] << 56))
/* MySQL protocol constants */
typedef enum
{
GW_MYSQL_CAPABILITIES_NONE= 0,
GW_MYSQL_CAPABILITIES_LONG_PASSWORD= (1 << 0),
GW_MYSQL_CAPABILITIES_FOUND_ROWS= (1 << 1),
GW_MYSQL_CAPABILITIES_LONG_FLAG= (1 << 2),
GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB= (1 << 3),
GW_MYSQL_CAPABILITIES_NO_SCHEMA= (1 << 4),
GW_MYSQL_CAPABILITIES_COMPRESS= (1 << 5),
GW_MYSQL_CAPABILITIES_ODBC= (1 << 6),
GW_MYSQL_CAPABILITIES_LOCAL_FILES= (1 << 7),
GW_MYSQL_CAPABILITIES_IGNORE_SPACE= (1 << 8),
GW_MYSQL_CAPABILITIES_PROTOCOL_41= (1 << 9),
GW_MYSQL_CAPABILITIES_INTERACTIVE= (1 << 10),
GW_MYSQL_CAPABILITIES_SSL= (1 << 11),
GW_MYSQL_CAPABILITIES_IGNORE_SIGPIPE= (1 << 12),
GW_MYSQL_CAPABILITIES_TRANSACTIONS= (1 << 13),
GW_MYSQL_CAPABILITIES_RESERVED= (1 << 14),
GW_MYSQL_CAPABILITIES_SECURE_CONNECTION= (1 << 15),
GW_MYSQL_CAPABILITIES_MULTI_STATEMENTS= (1 << 16),
GW_MYSQL_CAPABILITIES_MULTI_RESULTS= (1 << 17),
GW_MYSQL_CAPABILITIES_PS_MULTI_RESULTS= (1 << 18),
GW_MYSQL_CAPABILITIES_PLUGIN_AUTH= (1 << 19),
GW_MYSQL_CAPABILITIES_SSL_VERIFY_SERVER_CERT= (1 << 30),
GW_MYSQL_CAPABILITIES_REMEMBER_OPTIONS= (1 << 31),
GW_MYSQL_CAPABILITIES_CLIENT= (GW_MYSQL_CAPABILITIES_LONG_PASSWORD |
GW_MYSQL_CAPABILITIES_FOUND_ROWS |
GW_MYSQL_CAPABILITIES_LONG_FLAG |
GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB |
GW_MYSQL_CAPABILITIES_LOCAL_FILES |
GW_MYSQL_CAPABILITIES_PLUGIN_AUTH |
GW_MYSQL_CAPABILITIES_TRANSACTIONS |
GW_MYSQL_CAPABILITIES_PROTOCOL_41 |
GW_MYSQL_CAPABILITIES_MULTI_STATEMENTS |
GW_MYSQL_CAPABILITIES_MULTI_RESULTS |
GW_MYSQL_CAPABILITIES_PS_MULTI_RESULTS |
GW_MYSQL_CAPABILITIES_SECURE_CONNECTION),
GW_MYSQL_CAPABILITIES_CLIENT_COMPRESS= (GW_MYSQL_CAPABILITIES_LONG_PASSWORD |
GW_MYSQL_CAPABILITIES_FOUND_ROWS |
GW_MYSQL_CAPABILITIES_LONG_FLAG |
GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB |
GW_MYSQL_CAPABILITIES_LOCAL_FILES |
GW_MYSQL_CAPABILITIES_PLUGIN_AUTH |
GW_MYSQL_CAPABILITIES_TRANSACTIONS |
GW_MYSQL_CAPABILITIES_PROTOCOL_41 |
GW_MYSQL_CAPABILITIES_MULTI_STATEMENTS |
GW_MYSQL_CAPABILITIES_MULTI_RESULTS |
GW_MYSQL_CAPABILITIES_PS_MULTI_RESULTS |
GW_MYSQL_CAPABILITIES_COMPRESS
),
} gw_mysql_capabilities_t;
#endif

View File

@ -20,7 +20,7 @@
# 17/06/2013 Massimiliano Pinto Added mysql_common top both libraries
CC=cc
CFLAGS=-c -fPIC -I/usr/include -I../include -I../../include -Wall
CFLAGS=-c -fPIC -I/usr/include -I../include -I../../include
LDFLAGS=-shared
MYSQLCLIENTSRCS=mysql_client.c mysql_common.c
MYSQLCLIENTOBJ=$(MYSQLCLIENTSRCS:.c=.o)

View File

@ -16,7 +16,7 @@
* Copyright SkySQL Ab 2013
*/
#include "mysql_protocol.h"
#include "mysql_client_server_protocol.h"
/*
* MySQL Protocol module for handling the protocol between the gateway
@ -30,10 +30,10 @@
static char *version_str = "V1.0.0";
static int gw_read_backend_event(DCB* dcb, int epfd);
static int gw_write_backend_event(DCB *dcb, int epfd);
static int gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue);
static int gw_error_backend_event(DCB *dcb, int epfd, int event);
int gw_read_backend_event(DCB* dcb, int epfd);
int gw_write_backend_event(DCB *dcb, int epfd);
int gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue);
int gw_error_backend_event(DCB *dcb, int epfd, int event);
static GWPROTOCOL MyObject = {
gw_read_backend_event, /* Read - EPOLLIN handler */
@ -43,7 +43,8 @@ static GWPROTOCOL MyObject = {
NULL, /* HangUp - EPOLLHUP handler */
NULL, /* Accept */
NULL, /* Connect */
NULL /* Close */
NULL, /* Close */
NULL /* Listen */
};
/*
@ -85,7 +86,6 @@ GetModuleObject()
//////////////////////////////////////////
//backend read event triggered by EPOLLIN
//////////////////////////////////////////
int gw_read_backend_event(DCB *dcb, int epfd) {
int n;
MySQLProtocol *client_protocol = NULL;
@ -144,7 +144,7 @@ int gw_read_backend_event(DCB *dcb, int epfd) {
return 1;
}
return 1;
return 0;
}
//////////////////////////////////////////
@ -156,7 +156,7 @@ int gw_write_backend_event(DCB *dcb, int epfd) {
}
/*
* Write function for client DCB
* Write function for backend DCB
*
* @param dcb The DCB of the client
* @param queue Queue of buffers to write
@ -223,10 +223,10 @@ int w, saved_errno = 0;
if (queue && (saved_errno != EAGAIN || saved_errno != EWOULDBLOCK))
{
/* We had a real write failure that we must deal with */
return 0;
return 1;
}
return 1;
return 0;
}
int gw_error_backend_event(DCB *dcb, int epfd, int event) {

View File

@ -26,12 +26,12 @@
* 17/06/2013 Massimiliano Pinto Added Client To Gateway routines
*/
#include "mysql_protocol.h"
#include "mysql_client_server_protocol.h"
static char *version_str = "V1.0.0";
static int gw_MySQLAccept(DCB *listener, int efd);
static void gw_MySQLListener(int epfd, char *config_bind);
static int gw_MySQLListener(int epfd, char *config_bind);
static int gw_read_client_event(DCB* dcb, int epfd);
static int gw_write_client_event(DCB *dcb, int epfd);
static int gw_MySQLWrite_client(DCB *dcb, GWBUF *queue);
@ -42,6 +42,7 @@ static int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_pas
int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message);
int mysql_send_auth_error (DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message);
int MySQLSendHandshake(DCB* dcb);
static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue);
/*
* The "module object" for the mysqld client protocol module.
@ -50,11 +51,12 @@ static GWPROTOCOL MyObject = {
gw_read_client_event, /* Read - EPOLLIN handler */
gw_MySQLWrite_client, /* Write - data from gateway */
gw_write_client_event, /* WriteReady - EPOLLOUT handler */
gw_error_client_event, /* Error - EPOLLERR handler */
gw_error_client_event, /* Error - EPOLLERR handler */
NULL, /* HangUp - EPOLLHUP handler */
gw_MySQLAccept, /* Accept */
NULL, /* Connect */
NULL /* Close */
NULL, /* Close */
gw_MySQLListener /* Listen */
};
/*
@ -167,7 +169,7 @@ mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mys
memcpy(mysql_payload, mysql_message, strlen(mysql_message));
}
// write data
// writing data in the Client buffer queue
dcb->func.write(dcb, buf);
return sizeof(mysql_packet_header) + mysql_payload_size;
@ -251,7 +253,7 @@ mysql_send_auth_error (DCB *dcb, int packet_number, int in_affected_rows, const
// write err messg
memcpy(mysql_payload, mysql_error_msg, strlen(mysql_error_msg));
// write data
// writing data in the Client buffer queue
dcb->func.write(dcb, buf);
return sizeof(mysql_packet_header) + mysql_payload_size;
@ -398,7 +400,7 @@ MySQLSendHandshake(DCB* dcb)
mysql_handshake_payload++;
// write data
// writing data in the Client buffer queue
dcb->func.write(dcb, buf);
return sizeof(mysql_packet_header) + mysql_payload_size;
@ -483,7 +485,6 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
return auth_ret;
}
/////////////////////////////////////////////////
// get the sha1(sha1(password) from repository
/////////////////////////////////////////////////
@ -645,10 +646,10 @@ int w, saved_errno = 0;
if (queue && (saved_errno != EAGAIN || saved_errno != EWOULDBLOCK))
{
/* We had a real write failure that we must deal with */
return 0;
return 1;
}
return 1;
return 0;
}
//////////////////////////////////////////
@ -669,6 +670,7 @@ int gw_read_client_event(DCB* dcb, int epfd) {
if (ioctl(dcb->fd, FIONREAD, &b)) {
fprintf(stderr, "Client Ioctl FIONREAD error %i, %s\n", errno , strerror(errno));
return 1;
} else {
//fprintf(stderr, "Client IOCTL FIONREAD bytes to read = %i\n", b);
}
@ -788,7 +790,7 @@ int gw_read_client_event(DCB* dcb, int epfd) {
}
///////////////////////////////////////////////
// client write event triggered by EPOLLOUT
// client write event to Client triggered by EPOLLOUT
//////////////////////////////////////////////
int gw_write_client_event(DCB *dcb, int epfd) {
MySQLProtocol *protocol = NULL;
@ -827,6 +829,11 @@ int gw_write_client_event(DCB *dcb, int epfd) {
//write to client mysql AUTH_OK packet, packet n. is 2
mysql_send_ok(dcb, 2, 0, NULL);
// create one backend connection
// This is not working now, as the backend dcb functions are in the mysql_protocol.c
// and it will loaded separately
//gw_create_backend_connection(dcb, epfd);
protocol->state = MYSQL_IDLE;
return 0;
@ -890,7 +897,7 @@ int gw_write_client_event(DCB *dcb, int epfd) {
///
// set listener for mysql protocol
///
void MySQLListener(int epfd, char *config_bind) {
int gw_MySQLListener(int epfd, char *config_bind) {
DCB *listener;
int l_so;
int fl;
@ -935,7 +942,8 @@ void MySQLListener(int epfd, char *config_bind) {
// socket create
if ((l_so = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
error("can't open listening socket");
fprintf(stderr, ">>> Error: can't open listening socket. Errno %i, %s\n", errno, strerror(errno));
return 1;
}
// socket options
@ -946,9 +954,9 @@ void MySQLListener(int epfd, char *config_bind) {
// bind address and port
if (bind(l_so, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
fprintf(stderr, ">>>> Bind failed !!! %i, [%s]\n", errno, strerror(errno));
error("can't bind to address and port");
exit(1);
fprintf(stderr, ">>> Bind failed !!! %i, [%s]\n", errno, strerror(errno));
fprintf(stderr, ">>> can't bind to address and port");
return 1;
}
fprintf(stderr, ">> GATEWAY bind is: %s:%i. FD is %i\n", address, port, l_so);
@ -970,13 +978,15 @@ void MySQLListener(int epfd, char *config_bind) {
// add listening socket to epoll structure
if (epoll_ctl(epfd, EPOLL_CTL_ADD, l_so, &ev) == -1) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
fprintf(stderr, ">>> epoll_ctl: can't add the listen_sock! Errno %i, %s\n", errno, strerror(errno));
return 1;
}
listener->func.accept = gw_MySQLAccept;
listener->state = DCB_STATE_LISTENING;
return 0;
}

View File

@ -24,10 +24,21 @@
* 17/06/2013 Massimiliano Pinto Common MySQL protocol routines
*/
#include "mysql_protocol.h"
#include "mysql_client_server_protocol.h"
static char *version_str = "V1.0.0";
//static int gw_create_backend_connection(DCB *client_dcb, int efd);
static MySQLProtocol *gw_mysql_init(MySQLProtocol *data);
static void gw_mysql_close(MySQLProtocol **ptr);
extern gw_read_backend_event(DCB* dcb, int epfd);
extern gw_write_backend_event(DCB *dcb, int epfd);
extern int gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue);
extern int gw_error_backend_event(DCB *dcb, int epfd, int event);
///////////////////////////////
// Initialize mysql protocol struct
///////////////////////////////////////
MySQLProtocol *gw_mysql_init(MySQLProtocol *data) {
int rv = -1;
@ -53,7 +64,7 @@ MySQLProtocol *gw_mysql_init(MySQLProtocol *data) {
//////////////////////////////////////
// close a connection if opened
// free data scructure
// free data scructure for MySQLProtocol
//////////////////////////////////////
void gw_mysql_close(MySQLProtocol **ptr) {
MySQLProtocol *conn = *ptr;
@ -85,3 +96,79 @@ void gw_mysql_close(MySQLProtocol **ptr) {
fprintf(stderr, "mysqlgw_mysql_close() free(conn) done\n");
#endif
}
/*
* Create a new MySQL backend 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 epoll set
*
* - backend dcb allocation
* - MySQL session data fetch
* - backend connection using data in MySQL session
*
* @param client_dcb The client DCB struct
* @param epfd The epoll set to add the new connection
* @return 0 on Success or 1 on Failure.
*/
/*
* This function cannot work as it will be called from mysql_client.c but it needs function pointers from mysql_backend.c
* They are modules loaded separately!!
*
int gw_create_backend_connection(DCB *client_dcb, int efd) {
struct epoll_event ee;
DCB *backend = NULL;
MySQLProtocol *ptr_proto = NULL;
MySQLProtocol *client_protocol = NULL;
SESSION *session = NULL;
MYSQL_session *s_data = NULL;
backend = (DCB *) calloc(1, sizeof(DCB));
backend->state = DCB_STATE_ALLOC;
backend->session = NULL;
backend->protocol = (MySQLProtocol *)gw_mysql_init(NULL);
ptr_proto = (MySQLProtocol *)backend->protocol;
client_protocol = (MySQLProtocol *)client_dcb->protocol;
session = DCB_SESSION(client_dcb);
s_data = (MYSQL_session *)session->data;
// this is blocking until auth done
if (gw_mysql_connect("127.0.0.1", 3306, s_data->db, s_data->user, s_data->client_sha1, backend->protocol) == 0) {
fprintf(stderr, "Connected to backend mysql server\n");
backend->fd = ptr_proto->fd;
setnonblocking(backend->fd);
} else {
fprintf(stderr, "<<<< NOT Connected to backend mysql server!!!\n");
backend->fd = -1;
}
// edge triggering flag added
ee.events = EPOLLIN | EPOLLET | EPOLLOUT;
ee.data.ptr = backend;
// if connected, add it to the epoll
if (backend->fd > 0) {
if (epoll_ctl(efd, EPOLL_CTL_ADD, backend->fd, &ee) == -1) {
perror("epoll_ctl: backend sock");
} else {
fprintf(stderr, "--> Backend conn added, bk_fd [%i], scramble [%s], is session with client_fd [%i]\n", ptr_proto->fd, ptr_proto->scramble, client_dcb->fd);
backend->state = DCB_STATE_POLLING;
backend->session = DCB_SESSION(client_dcb);
(backend->func).read = gw_read_backend_event;
(backend->func).write = gw_MySQLWrite_backend;
(backend->func).write_ready = gw_write_backend_event;
(backend->func).error = gw_error_backend_event;
// assume here one backend only.
// in session.h
// struct dcb *backends;
// instead of a list **backends;
client_dcb->session->backends = backend;
}
return 0;
}
return 1;
}
*/