First commit of SkySql Gateway with Mysql protocol implemented by Massimiliano

This commit is contained in:
Massimiliano Pinto 2012-11-21 11:50:43 +01:00
parent 5178059706
commit efcd6aaf48
9 changed files with 2451 additions and 0 deletions

0
protocol_1.0/.deps Normal file
View File

50
protocol_1.0/Makefile Normal file
View File

@ -0,0 +1,50 @@
##
## Makefile -- Build procedure for sample skysql Apache module
## Autogenerated via ``apxs -n skysql -g''.
##
builddir=.
top_srcdir=/packages/inst/apache_2.4.2
top_builddir=/packages/inst/apache_2.4.2
include /packages/inst/apache_2.4.2/build/special.mk
# the used tools
APXS=apxs
APACHECTL=apachectl
# additional defines, includes and libraries
#DEFS=-Dmy_define=my_value
#INCLUDES=
#LIBS=-lskysqlclient -lmysqlcompat
#LDFLAGS=
# the default target
all: local-shared-build
# install the shared object file into Apache
install: install-modules-yes
# cleanup
clean:
-rm -f mod_skysql.o mod_skysql.lo mod_skysql.slo mod_skysql.la skysql_utils.o skysql_utils.lo skysql_utils.slo skysql_utils.la
# simple test
test: reload
lynx -mime_header http://localhost/skysql
# install and activate shared object by reloading Apache to
# force a reload of the shared object file
reload: install restart
# the general Apache start/restart/stop
# procedures
start:
$(APACHECTL) start
restart:
$(APACHECTL) restart
stop:
$(APACHECTL) stop

1
protocol_1.0/README Normal file
View File

@ -0,0 +1 @@
Apache 2.4.2

1222
protocol_1.0/mod_skysql.c Normal file

File diff suppressed because it is too large Load Diff

4
protocol_1.0/modules.mk Normal file
View File

@ -0,0 +1,4 @@
mod_skysql.la: mod_skysql.slo skysql_utils.slo skysql_backend.slo
$(SH_LINK) -rpath $(libexecdir) -module -avoid-version mod_skysql.lo skysql_utils.lo skysql_backend.lo
DISTCLEAN_TARGETS = modules.mk
shared = mod_skysql.la

View File

@ -0,0 +1,64 @@
////////////////////////////////////////
// SKYSQL Backend
// By Massimiliano Pinto 2012
// SkySQL AB
////////////////////////////////////////
#include "skysql_gw.h"
#define SKYSQL_READ 0
#define SKYSQL_WRITE 1
int skysql_query_is_select(const char *query) {
return SKYSQL_READ;
}
int skysql_ext_file_ver(void) {
int ret = 13;
return ret;
}
int select_random_slave_server(int nslaves) {
int random_balancer = (int) ((nslaves) * (rand() / (RAND_MAX + 1.0)));
return random_balancer;
}
int get_server_from_list(char **selected_host, int *selected_port, char *server_list, int num, apr_pool_t *p) {
int ret = -1;
int curr_srv = 0;
char *next = NULL;
char *tmp = NULL;
int port;
if (num == 0) {
port = atoi(strchr(server_list, ':') + 1), sizeof(port);
memcpy(selected_port, &port, sizeof(int));
*selected_host = apr_pstrndup(p, server_list, strchr(server_list, ':') - server_list);
return 1;
}
next = server_list;
while (curr_srv < num) {
tmp = strchr(next, ',');
if (tmp != NULL) {
curr_srv++;
next = tmp+1;
} else {
return -1;
}
if (curr_srv == num) {
port = atoi(strchr(next, ':') + 1);
memcpy(selected_port, &port, sizeof(port));
*selected_host = apr_pstrndup(p, next, strchr(next, ':') - next);
ret = 0;
break;
}
}
return ret;
}

View File

@ -0,0 +1,134 @@
////////////////////////////////////////
// SKYSQL header file
// By Massimiliano Pinto 2012
// SkySQL AB
////////////////////////////////////////
#include "ap_config.h"
#include "ap_mmn.h"
#include "httpd.h"
#include "http_core.h"
#include "http_main.h"
#include "http_config.h"
#include "http_connection.h"
#include "http_request.h"
#include "http_log.h"
#include "http_protocol.h"
#include "ap_config_auto.h"
#include "http_connection.h"
#include "util_filter.h"
#include "util_script.h"
#include "apr.h"
#include "apr_general.h"
#include "apr_buckets.h"
#include "apr_optional.h"
#include "apr_strings.h"
#include "apr_tables.h"
#include "apr_lib.h"
#include "apr_fnmatch.h"
#include "apr_strings.h"
#include "apr_dbm.h"
#include "apr_rmm.h"
#include "apr_shm.h"
#include "apr_global_mutex.h"
#include "apr_time.h"
#include "scoreboard.h"
// sha1
#include "apr_sha1.h"
// getpid
#include <unistd.h>
/* Protocol packing macros. */
#define skysql_set_byte2(__buffer, __int) do { \
(__buffer)[0]= (uint8_t)((__int) & 0xFF); \
(__buffer)[1]= (uint8_t)(((__int) >> 8) & 0xFF); } while (0)
#define skysql_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 skysql_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 skysql_get_byte2(__buffer) \
(uint16_t)((__buffer)[0] | \
((__buffer)[1] << 8))
#define skysql_get_byte3(__buffer) \
(uint32_t)((__buffer)[0] | \
((__buffer)[1] << 8) | \
((__buffer)[2] << 16))
#define skysql_get_byte4(__buffer) \
(uint32_t)((__buffer)[0] | \
((__buffer)[1] << 8) | \
((__buffer)[2] << 16) | \
((__buffer)[3] << 24))
#define skysql_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))
typedef enum
{
SKYSQL_CAPABILITIES_NONE= 0,
SKYSQL_CAPABILITIES_LONG_PASSWORD= (1 << 0),
SKYSQL_CAPABILITIES_FOUND_ROWS= (1 << 1),
SKYSQL_CAPABILITIES_LONG_FLAG= (1 << 2),
SKYSQL_CAPABILITIES_CONNECT_WITH_DB= (1 << 3),
SKYSQL_CAPABILITIES_NO_SCHEMA= (1 << 4),
SKYSQL_CAPABILITIES_COMPRESS= (1 << 5),
SKYSQL_CAPABILITIES_ODBC= (1 << 6),
SKYSQL_CAPABILITIES_LOCAL_FILES= (1 << 7),
SKYSQL_CAPABILITIES_IGNORE_SPACE= (1 << 8),
SKYSQL_CAPABILITIES_PROTOCOL_41= (1 << 9),
SKYSQL_CAPABILITIES_INTERACTIVE= (1 << 10),
SKYSQL_CAPABILITIES_SSL= (1 << 11),
SKYSQL_CAPABILITIES_IGNORE_SIGPIPE= (1 << 12),
SKYSQL_CAPABILITIES_TRANSACTIONS= (1 << 13),
SKYSQL_CAPABILITIES_RESERVED= (1 << 14),
SKYSQL_CAPABILITIES_SECURE_CONNECTION= (1 << 15),
SKYSQL_CAPABILITIES_MULTI_STATEMENTS= (1 << 16),
SKYSQL_CAPABILITIES_MULTI_RESULTS= (1 << 17),
SKYSQL_CAPABILITIES_PS_MULTI_RESULTS= (1 << 18),
SKYSQL_CAPABILITIES_PLUGIN_AUTH= (1 << 19),
SKYSQL_CAPABILITIES_SSL_VERIFY_SERVER_CERT= (1 << 30),
SKYSQL_CAPABILITIES_REMEMBER_OPTIONS= (1 << 31),
SKYSQL_CAPABILITIES_CLIENT= (SKYSQL_CAPABILITIES_LONG_PASSWORD |
SKYSQL_CAPABILITIES_FOUND_ROWS |
SKYSQL_CAPABILITIES_LONG_FLAG |
SKYSQL_CAPABILITIES_CONNECT_WITH_DB |
SKYSQL_CAPABILITIES_LOCAL_FILES |
SKYSQL_CAPABILITIES_PLUGIN_AUTH |
SKYSQL_CAPABILITIES_TRANSACTIONS |
SKYSQL_CAPABILITIES_PROTOCOL_41 |
SKYSQL_CAPABILITIES_MULTI_STATEMENTS |
SKYSQL_CAPABILITIES_MULTI_RESULTS |
SKYSQL_CAPABILITIES_PS_MULTI_RESULTS |
SKYSQL_CAPABILITIES_SECURE_CONNECTION)
} skysql_capabilities_t;
#define SMALL_CHUNK 1024
#define MAX_CHUNK SMALL_CHUNK * 16
#define ToHex(Y) (Y>='0'&&Y<='9'?Y-'0':Y-'A'+10)
typedef struct {
apr_socket_t *socket;
char scramble[33];
uint32_t server_capabs;
uint32_t client_capabs;
unsigned long tid;
apr_pool_t *pool;
} MYSQL_conn;

113
protocol_1.0/skysql_gw.h Normal file
View File

@ -0,0 +1,113 @@
////////////////////////////////////////
// SKYSQL header file
// By Massimiliano Pinto 2012
// SkySQL AB
////////////////////////////////////////
#include "ap_config.h"
#include "ap_mmn.h"
#include "httpd.h"
#include "http_core.h"
#include "http_main.h"
#include "http_config.h"
#include "http_connection.h"
#include "http_request.h"
#include "http_log.h"
#include "http_protocol.h"
#include "ap_config_auto.h"
#include "http_connection.h"
#include "util_filter.h"
#include "util_script.h"
#include "apr.h"
#include "apr_general.h"
#include "apr_buckets.h"
#include "apr_optional.h"
#include "apr_strings.h"
#include "apr_tables.h"
#include "apr_lib.h"
#include "apr_fnmatch.h"
#include "apr_strings.h"
#include "apr_dbm.h"
#include "apr_rmm.h"
#include "apr_shm.h"
#include "apr_global_mutex.h"
#include "apr_time.h"
#include "scoreboard.h"
// getpid
#include <unistd.h>
#include "skysql_client.h"
#define SKYSQL_GATEWAY_VERSION "0.0.1"
#define SKYSQL_VERSION "5.5.22-SKY-1.6.5"
#define HTTP_WELCOME_MESSAGE "HTTP/1.1 200 OK\r\nConnection: Keep-Alive\r\nContent-Type: text/plain\r\n\r\nSKYSQL Gateway " SKYSQL_GATEWAY_VERSION
#define SKYSQL_LISTENER_VERSION "MySQL Community Server (GPL)"
#define SKYSQL_PROTOCOL_VERSION 10 // version is 10
#define SKYSQL_THREAD_ID 11
#define SKYSQL_HANDSKAKE_FILLER 0x00
#define SKYSQL_SERVER_CAPABILITIES_BYTE1 0xff
#define SKYSQL_SERVER_CAPABILITIES_BYTE2 0xf7
#define SKYSQL_SERVER_LANGUAGE 0x08
module AP_MODULE_DECLARE_DATA skysql_module;
//const int SKY_SQL_MAX_PACKET_LEN = 0xffffffL;
typedef struct {
MYSQL_conn *conn;
unsigned long mysql_tid;
unsigned long gateway_id;
int protocol_enabled;
int pool_enabled;
char backend_servers[2][128];
apr_hash_t *resources;
int loop_timeout;
} skysql_server_conf;
typedef struct
{
char *name;
char *raw_config;
char *server_list;
int r_port;
char *dbname;
char *defaults;
int nshards;
} conn_details;
typedef struct {
char *driver_name;
char *username;
char *password;
char *database;
void *driver_details;
} skysql_client_auth;
typedef struct {
uint8_t client_flags[4];
uint8_t max_packet_size[4];
uint8_t charset;
uint8_t scramble_buff;
int connect_with_db;
} mysql_driver_details;
int skysql_ext_file_ver();
int skysql_query_is_select(const char *query);
apr_status_t skysql_read_client_autentication(conn_rec *c, apr_pool_t *pool, uint8_t *scramble, int scramble_len, skysql_client_auth *mysql_client_data, uint8_t *stage1_hash);
apr_status_t skysql_send_handshake(conn_rec *c, uint8_t *scramble, int *scramble_len);
apr_status_t skysql_send_error (conn_rec *c, uint8_t packet_number, MYSQL_conn *conn);
//apr_status_t skysql_prepare_ok(conn_rec *c, uint8_t packet_number, MYSQL_STMT *statement, MYSQL_conn *conn);
apr_status_t skysql_send_ok(conn_rec *c, apr_pool_t *p, uint8_t packet_number, uint8_t in_affected_rows, const char* skysql_message);
apr_status_t skysql_send_eof(conn_rec *c, uint8_t packet_number);
apr_status_t skysql_send_result(conn_rec *c, uint8_t *data, uint8_t len);
int select_random_slave_server(int nslaves);
apr_status_t gateway_send_error (conn_rec *c, apr_pool_t *p, uint8_t packet_number);
apr_status_t gateway_reply_data(conn_rec *c, apr_pool_t *pool, void *data, int len);
char *gateway_find_user_password_sha1(char *username, void *repository, conn_rec *c, apr_pool_t *p);
void skysql_sha1_str(const uint8_t *in, int in_len, uint8_t *out);
int skygateway_query_result(conn_rec *c, apr_pool_t *p, MYSQL_conn *conn, const char *query);
//apr_status_t skysql_change_user(conn_rec *c, apr_pool_t *p, char *username, char *database, MYSQL *conn, uint8_t *stage1_hash);

863
protocol_1.0/skysql_utils.c Normal file
View File

@ -0,0 +1,863 @@
////////////////////////////////////////
// SKYSQL Utils
// By Massimiliano Pinto 2012
// SkySQL AB
////////////////////////////////////////
#include "skysql_gw.h"
#include "apr_sha1.h"
#define MYSQL_PROTOCOL_VERSION41_CHAR '*'
#define char_val(X) (X >= '0' && X <= '9' ? X-'0' :\
X >= 'A' && X <= 'Z' ? X-'A'+10 :\
X >= 'a' && X <= 'z' ? X-'a'+10 :\
'\177')
char hex_upper[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char hex_lower[] = "0123456789abcdefghijklmnopqrstuvwxyz";
/////////////////////////////////
// binary data to hex string
// output must be pre allocated
/////////////////////////////////
char *bin2hex(char *out, const uint8_t *in, unsigned int len) {
const uint8_t *in_end= in + len;
if (len == 0 || in == NULL) {
return NULL;
}
for (; in != in_end; ++in) {
*out++= hex_upper[((uint8_t) *in) >> 4];
*out++= hex_upper[((uint8_t) *in) & 0x0F];
}
*out= '\0';
return out;
}
/////////////////////////////////
// hex string to binary data
// output must be pre allocated
/////////////////////////////////
int hex2bin(uint8_t *out, const char *in, unsigned int len) {
const char *in_end= in + len;
if (len == 0 || in == NULL) {
return 1;
}
while (in < in_end) {
register char tmp_ptr = char_val(*in++);
*out++= (tmp_ptr << 4) | char_val(*in++);
}
return 0;
}
/////////////////////////////////
// general random string
// output must be pre allocated
/////////////////////////////////
void skysql_set_random_str(uint8_t *output, unsigned int length) {
uint8_t *ptr = output;
apr_status_t rv = apr_generate_random_bytes(output, length);
// this is for debug, the same scramble for every handshake
//strcpy(output, "12345678abcdefjhilmn");
}
/////////////////////////////////////////////////////////////
// fill a 20 bytes preallocated with SHA1 digest (160 bits)
// for one input on in_len bytes
/////////////////////////////////////////////////////////////
void skysql_sha1_str(const uint8_t *in, int in_len, uint8_t *out) {
int l;
apr_sha1_ctx_t context;
apr_byte_t digest[APR_SHA1_DIGESTSIZE];
apr_sha1_init(&context);
apr_sha1_update(&context, in, in_len);
apr_sha1_final(digest, &context);
memcpy(out, digest, APR_SHA1_DIGESTSIZE);
}
/////////////////////////////////////////////////////////////
// fill 20 bytes preallocated with SHA1 digest (160 bits)
// for two inputs, in_len and in2_len bytes
/////////////////////////////////////////////////////////////
void skysql_sha1_2_str(const uint8_t *in, int in_len, const uint8_t *in2, int in2_len, uint8_t *out) {
int l;
apr_sha1_ctx_t context;
apr_byte_t digest[APR_SHA1_DIGESTSIZE];
apr_sha1_init(&context);
apr_sha1_update(&context, in, in_len);
apr_sha1_update(&context, in2, in2_len);
apr_sha1_final(digest, &context);
memcpy(out, digest, APR_SHA1_DIGESTSIZE);
}
///////////////////////////////////////////////////////
// fill a preallocated buffer with XOR(str1, str2)
// XOR between 2 equal len strings
// note that XOR(str1, XOR(str1 CONCAT str2)) == str2
// and that XOR(str1, str2) == XOR(str2, str1)
///////////////////////////////////////////////////////
void skysql_str_xor(char *output, const uint8_t *input1, const uint8_t *input2, unsigned int len) {
const uint8_t *input1_end = NULL;
input1_end = input1 + len;
while (input1 < input1_end)
*output++= *input1++ ^ *input2++;
*output = '\0';
}
//////////////////////////////////////////
// get skygateway password from username
// output is SHA1(SHA1(password))
//////////////////////////////////////////
char *gateway_find_user_password_sha1(char *username, void *repository, conn_rec *c, apr_pool_t *p) {
uint8_t hash1[APR_SHA1_DIGESTSIZE];
uint8_t hash2[APR_SHA1_DIGESTSIZE];
skysql_sha1_str(username, strlen(username), hash1);
skysql_sha1_str(hash1, APR_SHA1_DIGESTSIZE, hash2);
return apr_pstrmemdup(p, hash2, APR_SHA1_DIGESTSIZE);
}
/////////////////////////////////////////////
// get the SHA1(SHA1(password)) from client
/////////////////////////////////////////////
int skysql_check_scramble(conn_rec *c, apr_pool_t *p, uint8_t *token, unsigned int token_len, uint8_t *scramble, unsigned int scramble_len, char *username, uint8_t *stage1_hash) {
uint8_t step1[APR_SHA1_DIGESTSIZE];
uint8_t step2[APR_SHA1_DIGESTSIZE +1];
uint8_t check_hash[APR_SHA1_DIGESTSIZE];
char hex_double_sha1[2 * APR_SHA1_DIGESTSIZE + 1]="";
uint8_t *password = gateway_find_user_password_sha1("pippo", NULL, c, p);
bin2hex(hex_double_sha1, password, APR_SHA1_DIGESTSIZE);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "stored hex(SHA1(SHA1(password))) [%s]", hex_double_sha1);
// possible, now skipped
/*
if (password == NULL) {
??????
}
*/
// step 1
skysql_sha1_2_str(scramble, scramble_len, password, APR_SHA1_DIGESTSIZE, step1);
//step2
skysql_str_xor(step2, token, step1, token_len);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "skygateway SHA1(password) [%s]", step2);
memcpy(stage1_hash, step2, 20);
skysql_sha1_str(step2, APR_SHA1_DIGESTSIZE, check_hash);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "SHA1 di SHA1(client password) [%s]", check_hash);
return memcmp(password, check_hash, APR_SHA1_DIGESTSIZE);
}
apr_status_t gateway_reply_data(conn_rec *c, apr_pool_t *pool, void *data, int len) {
apr_status_t rv = APR_SUCCESS;
apr_bucket_brigade *bb;
apr_bucket_brigade *r_bb;
// create brigade
bb = apr_brigade_create(pool, c->bucket_alloc);
apr_brigade_write(bb, ap_filter_flush, c->output_filters, data, len);
ap_fflush(c->output_filters, bb);
apr_brigade_destroy(bb);
return 1;
}
apr_status_t skysql_change_user(conn_rec *c, apr_pool_t *p, char *username, char *database, MYSQL_conn *conn, uint8_t *stage1_hash) {
uint8_t skysql_payload_size = 0;
uint8_t skysql_packet_header[4];
uint8_t skysql_packet_id = 0;
uint8_t change_user_command = 0x11;
uint8_t *outbuf = NULL;
uint8_t token[20 + 1]="";
uint8_t charset[2]="";
uint8_t backend_scramble[20 +1]="";
int fd = 0;
int user_len = strlen(username);
int database_len = strlen(database);
uint8_t *password = NULL;
uint8_t temp_token[20 +1] ="";
uint8_t stage1_password[20 +1] ="";
//get password from repository
password = gateway_find_user_password_sha1("pippo", NULL, c, p);
memcpy(backend_scramble, conn->scramble, 20);
skysql_sha1_2_str(backend_scramble, 20, password, 20, temp_token);
*token = '\x14';
charset[0]='\x08';
charset[1]='\x00';
skysql_str_xor(token+1, temp_token, stage1_hash, 20);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "skygateway TO backend scramble [%s]", backend_scramble);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "skygateway SHA1(password) [%s]", stage1_hash);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "skygateway SHA1(scramble + SHA1(stage1_hash)) [%s]", temp_token);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "skygateway TO backend token [%s]", token+1);
skysql_payload_size = 1 + user_len + 1 + sizeof(token) + database_len + 1 + sizeof(charset);
// allocate memory for packet header + payload
outbuf = (uint8_t *) apr_pcalloc(p, sizeof(skysql_packet_header) + skysql_payload_size);
// write packet header with packet number
skysql_set_byte3(skysql_packet_header, skysql_payload_size);
skysql_packet_header[3] = skysql_packet_id;
memcpy(outbuf, skysql_packet_header, sizeof(skysql_packet_header));
memcpy(outbuf + sizeof(skysql_packet_header), &change_user_command, 1);
memcpy(outbuf + sizeof(skysql_packet_header) + 1, username, user_len);
memcpy(outbuf + sizeof(skysql_packet_header) + 1 + strlen(username) + 1, token, 21);
memcpy(outbuf + sizeof(skysql_packet_header) + 1 + strlen(username) + 1 + 21, database, database_len);
memcpy(outbuf + sizeof(skysql_packet_header) + 1 + strlen(username) + 1 + 21 + database_len + 1, charset, sizeof(charset));
write(fd, outbuf, sizeof(skysql_packet_header) + skysql_payload_size);
}
apr_status_t skysql_read_client_autentication(conn_rec *c, apr_pool_t *pool, uint8_t *scramble, int scramble_len, skysql_client_auth *mysql_client_data, uint8_t *stage1_hash) {
apr_bucket_brigade *r_bb;
apr_bucket *r_b;
apr_status_t rv;
int seen_eos = 0;
int child_stopped_reading = 0;
int i;
apr_bucket *auth_bucket;
apr_bucket *bucket;
const char *client_auth_packet = NULL;
unsigned int query_ret = 0;
int return_data = 0;
int input_read = 0;
uint8_t client_flags[4];
char *current_slave_server_host = NULL;
int current_slave_server_port = 3306;
apr_pool_t *p = NULL;
mysql_driver_details *mysql_driver = NULL;
uint8_t *token = NULL;
unsigned int token_len = 0;
int auth_ret = 0;
// use the passed pool?
p = pool == NULL ? c->pool : pool;
// now read the client authentication
r_bb = apr_brigade_create(p, c->bucket_alloc);
if (((rv = ap_get_brigade(c->input_filters, r_bb, AP_MODE_READBYTES, APR_BLOCK_READ, 8192)) != APR_SUCCESS) || APR_BRIGADE_EMPTY(r_bb)) {
apr_brigade_destroy(r_bb);
return input_read;
}
for (auth_bucket = APR_BRIGADE_FIRST(r_bb); bucket != APR_BRIGADE_SENTINEL(r_bb); bucket = APR_BUCKET_NEXT(auth_bucket)) {
apr_size_t len;
const char *data;
if (APR_BUCKET_IS_EOS(auth_bucket)) {
seen_eos = 1;
break;
}
if (APR_BUCKET_IS_FLUSH(auth_bucket)) {
continue;
}
if (child_stopped_reading) {
break;
}
rv = apr_bucket_read(auth_bucket, &data, &len, APR_BLOCK_READ);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "Auth Data len [%i]", len);
if (rv != APR_SUCCESS) {
child_stopped_reading = 1;
}
client_auth_packet = apr_pstrmemdup(p, data, len);
input_read = 1;
}
// this brigade is useless
apr_brigade_destroy(r_bb);
if (input_read && client_auth_packet) {
// now fill data structure for client data in driver MYSQL5
if (mysql_client_data != NULL) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "Now decode MYSQL client auth packet");
mysql_driver = (mysql_driver_details *)mysql_client_data->driver_details;
if (mysql_driver != NULL) {
uint8_t hash_stage1[20 +1];
/*
uint8_t hash_stage1[20];
uint8_t hash_stage2[20];
uint8_t temp_token[20];
uint8_t client_token[20];
uint8_t check_auth[20];
uint8_t final_hash[20];
*/
// todo: insert constant values instead of numbers
memcpy(mysql_driver->client_flags, client_auth_packet + 4, 4);
mysql_driver->connect_with_db = SKYSQL_CAPABILITIES_CONNECT_WITH_DB & skysql_get_byte4(mysql_driver->client_flags);
mysql_client_data->username = apr_pstrndup(p, client_auth_packet + 4 + 4 + 4 + 1 + 23, 128);
memcpy(&token_len, client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(mysql_client_data->username) + 1, 1);
token = apr_pstrmemdup(p, client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(mysql_client_data->username) + 1 + 1, token_len);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "[client TO gateway] current username is [%s], token is [%s] len %i", mysql_client_data->username, token, token_len);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "[gateway TO client] server scramble was [%s], len %i", scramble, scramble_len);
/*
skysql_sha1_str(mysql_client_data->username, strlen(mysql_client_data->username), hash_stage1);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "SHA1 di '%s' [%s]", mysql_client_data->username, hash_stage1);
skysql_sha1_str(hash_stage1, 20, hash_stage2);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "SHA1 di SHA1('%s') [%s]", mysql_client_data->username, hash_stage2);
skysql_sha1_2_str(scramble, scramble_len, hash_stage2, 20, temp_token);
skysql_str_xor(check_auth, hash_stage1, temp_token, 20);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "This is the client input?? [%s]", check_auth);
memset(temp_token, '\0', sizeof(temp_token));
memcpy(client_token, scramble, scramble_len);
memcpy(client_token + scramble_len, hash_stage2, 20);
skysql_sha1_str(client_token, scramble_len, temp_token);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "This is the client input?? [%s]", temp_token);
skysql_str_xor(check_auth, hash_stage2, hash_stage1, scramble_len);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "XOR( client token, stage2_hash) [%s]", check_auth);
skysql_sha1_str(check_auth, 20, final_hash);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "SHA1 di check_auth [%s]", final_hash);
*/
// decode the token and check the password
auth_ret = skysql_check_scramble(c, p, token, token_len, scramble, scramble_len, mysql_client_data->username, stage1_hash);
if (auth_ret == 0) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "SkySQL Gateway Authentication OK for [%s]", mysql_client_data->username);
} else {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "**** SkySQL Gateway Authentication ERROR [%s], retcode = [%i]", mysql_client_data->username, auth_ret);
}
if (mysql_driver->connect_with_db) {
mysql_client_data->database = apr_pstrndup(p, client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(mysql_client_data->username) + 1 + 1 + token_len, 128);
}
}
}
}
return input_read;
}
apr_status_t gateway_send_error (conn_rec *c, apr_pool_t *p, uint8_t packet_number) {
apr_status_t rv;
rv = APR_SUCCESS;
apr_bucket_brigade *bb;
apr_bucket *b;
uint8_t *outbuf = NULL;
uint8_t skysql_payload_size = 0;
uint8_t skysql_packet_header[4];
uint8_t *skysql_payload = NULL;
uint8_t field_count = 0;
uint8_t affected_rows = 0;
uint8_t insert_id = 0;
uint8_t skysql_err[2];
uint8_t skysql_statemsg[6];
unsigned int skysql_errno = 0;
const char *skysql_error_msg = NULL;
const char *skysql_state = NULL;
skysql_errno = 6969;
skysql_error_msg = "Too many queries in one connection";
skysql_state = "FA5D3";
field_count = 0xff;
skysql_set_byte2(skysql_err, skysql_errno);
skysql_statemsg[0]='#';
memcpy(skysql_statemsg+1, skysql_state, 5);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "SKYSQL_Error: Errno [%u], ErrorMessage [%s], State [%s]", skysql_errno, skysql_error_msg, skysql_state);
skysql_payload_size = sizeof(field_count) + sizeof(skysql_err) + sizeof(skysql_statemsg) + strlen(skysql_error_msg);
// allocate memory for packet header + payload
outbuf = (uint8_t *) apr_pcalloc(p, sizeof(skysql_packet_header) + skysql_payload_size);
// write packet header with packet number
skysql_set_byte3(skysql_packet_header, skysql_payload_size);
skysql_packet_header[3] = packet_number;
// write header
memcpy(outbuf, skysql_packet_header, sizeof(skysql_packet_header));
skysql_payload = outbuf + sizeof(skysql_packet_header);
// write field
memcpy(skysql_payload, &field_count, sizeof(field_count));
skysql_payload = skysql_payload + sizeof(field_count);
// write errno
memcpy(skysql_payload, skysql_err, sizeof(skysql_err));
skysql_payload = skysql_payload + sizeof(skysql_err);
// write sqlstate
memcpy(skysql_payload, skysql_statemsg, sizeof(skysql_statemsg));
skysql_payload = skysql_payload + sizeof(skysql_statemsg);
// write err messg
memcpy(skysql_payload, skysql_error_msg, strlen(skysql_error_msg));
// create brigade
bb = apr_brigade_create(p, c->bucket_alloc);
b = apr_bucket_pool_create(outbuf, sizeof(skysql_packet_header) + skysql_payload_size, p, c->bucket_alloc);
APR_BRIGADE_INSERT_HEAD(bb, b);
b = apr_bucket_flush_create(c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, b);
return ap_pass_brigade(c->output_filters, bb);
}
apr_status_t skysql_send_error (conn_rec *c, uint8_t packet_number, MYSQL_conn *conn) {
apr_status_t rv;
rv = APR_SUCCESS;
apr_bucket_brigade *bb;
apr_bucket *b;
uint8_t *outbuf = NULL;
uint8_t skysql_payload_size = 0;
uint8_t skysql_packet_header[4];
uint8_t *skysql_payload = NULL;
uint8_t field_count = 0;
uint8_t affected_rows = 0;
uint8_t insert_id = 0;
uint8_t skysql_err[2];
uint8_t skysql_statemsg[6];
unsigned int skysql_errno = 0;
const char *skysql_error_msg = NULL;
const char *skysql_state = NULL;
skysql_errno = mysql_errno(conn);
skysql_error_msg = mysql_error(conn);
skysql_state = mysql_sqlstate(conn);
field_count = 0xff;
skysql_set_byte2(skysql_err, skysql_errno);
skysql_statemsg[0]='#';
memcpy(skysql_statemsg+1, skysql_state, 5);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "SKYSQL_Error: Errno [%u], ErrorMessage [%s], State [%s]", skysql_errno, skysql_error_msg, skysql_state);
skysql_payload_size = sizeof(field_count) + sizeof(skysql_err) + sizeof(skysql_statemsg) + strlen(skysql_error_msg);
// allocate memory for packet header + payload
outbuf = (uint8_t *) apr_pcalloc(c->pool, sizeof(skysql_packet_header) + skysql_payload_size);
// write packet header with packet number
skysql_set_byte3(skysql_packet_header, skysql_payload_size);
skysql_packet_header[3] = packet_number;
// write header
memcpy(outbuf, skysql_packet_header, sizeof(skysql_packet_header));
skysql_payload = outbuf + sizeof(skysql_packet_header);
// write field
memcpy(skysql_payload, &field_count, sizeof(field_count));
skysql_payload = skysql_payload + sizeof(field_count);
// write errno
memcpy(skysql_payload, skysql_err, sizeof(skysql_err));
skysql_payload = skysql_payload + sizeof(skysql_err);
// write sqlstate
memcpy(skysql_payload, skysql_statemsg, sizeof(skysql_statemsg));
skysql_payload = skysql_payload + sizeof(skysql_statemsg);
// write err messg
memcpy(skysql_payload, skysql_error_msg, strlen(skysql_error_msg));
// create brigade
bb = apr_brigade_create(c->pool, c->bucket_alloc);
b = apr_bucket_pool_create(outbuf, sizeof(skysql_packet_header) + skysql_payload_size, c->pool, c->bucket_alloc);
APR_BRIGADE_INSERT_HEAD(bb, b);
b = apr_bucket_flush_create(c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, b);
return ap_pass_brigade(c->output_filters, bb);
}
apr_status_t skysql_send_result(conn_rec *c, uint8_t *data, uint8_t len) {
apr_status_t rv;
rv = APR_SUCCESS;
apr_bucket_brigade *bb;
apr_bucket *b;
// create brigade
bb = apr_brigade_create(c->pool, c->bucket_alloc);
// write
apr_brigade_write(bb, ap_filter_flush, c->output_filters, data, len);
//send & flush
return ap_fflush(c->output_filters, bb);
}
apr_status_t skysql_send_eof(conn_rec *c, uint8_t packet_number) {
apr_status_t rv;
rv = APR_SUCCESS;
apr_bucket_brigade *bb;
apr_bucket *b;
uint8_t *outbuf = NULL;
uint8_t skysql_payload_size = 0;
uint8_t skysql_packet_header[4];
uint8_t *skysql_payload = NULL;
uint8_t field_count = 0;
uint8_t skysql_server_status[2];
uint8_t skysql_warning_count[2];
field_count = 0xfe;
skysql_payload_size = sizeof(field_count) + sizeof(skysql_server_status) + sizeof(skysql_warning_count);
// allocate memory for packet header + payload
outbuf = (uint8_t *) apr_pcalloc(c->pool, sizeof(skysql_packet_header) + skysql_payload_size);
// write packet header with packet number
skysql_set_byte3(skysql_packet_header, skysql_payload_size);
skysql_packet_header[3] = packet_number;
// write header
memcpy(outbuf, skysql_packet_header, sizeof(skysql_packet_header));
skysql_payload = outbuf + sizeof(skysql_packet_header);
skysql_server_status[0] = 2;
skysql_server_status[1] = 0;
skysql_warning_count[0] = 0;
skysql_warning_count[1] = 0;
// write data
memcpy(skysql_payload, &field_count, sizeof(field_count));
skysql_payload = skysql_payload + sizeof(field_count);
memcpy(skysql_payload, skysql_server_status, sizeof(skysql_server_status));
skysql_payload = skysql_payload + sizeof(skysql_server_status);
memcpy(skysql_payload, skysql_warning_count, sizeof(skysql_warning_count));
skysql_payload = skysql_payload + sizeof(skysql_warning_count);
// create brigade
bb = apr_brigade_create(c->pool, c->bucket_alloc);
// write
apr_brigade_write(bb, ap_filter_flush, c->output_filters, outbuf, sizeof(skysql_packet_header) + skysql_payload_size);
//send & flush
return ap_fflush(c->output_filters, bb);
}
apr_status_t skysql_send_ok(conn_rec *c, apr_pool_t *p, uint8_t packet_number, uint8_t in_affected_rows, const char* skysql_message) {
apr_status_t rv;
rv = APR_SUCCESS;
apr_bucket_brigade *bb;
apr_bucket *b;
uint8_t *outbuf = NULL;
uint8_t skysql_payload_size = 0;
uint8_t skysql_packet_header[4];
uint8_t *skysql_payload = NULL;
uint8_t field_count = 0;
uint8_t affected_rows = 0;
uint8_t insert_id = 0;
uint8_t skysql_server_status[2];
uint8_t skysql_warning_count[2];
affected_rows = in_affected_rows;
skysql_payload_size = sizeof(field_count) + sizeof(affected_rows) + sizeof(insert_id) + sizeof(skysql_server_status) + sizeof(skysql_warning_count);
if (skysql_message != NULL) {
skysql_payload_size += strlen(skysql_message);
}
// allocate memory for packet header + payload
outbuf = (uint8_t *) apr_pcalloc(p, sizeof(skysql_packet_header) + skysql_payload_size);
// write packet header with packet number
skysql_set_byte3(skysql_packet_header, skysql_payload_size);
skysql_packet_header[3] = packet_number;
// write header
memcpy(outbuf, skysql_packet_header, sizeof(skysql_packet_header));
skysql_payload = outbuf + sizeof(skysql_packet_header);
skysql_server_status[0] = 2;
skysql_server_status[1] = 0;
skysql_warning_count[0] = 0;
skysql_warning_count[1] = 0;
// write data
memcpy(skysql_payload, &field_count, sizeof(field_count));
skysql_payload = skysql_payload + sizeof(field_count);
memcpy(skysql_payload, &affected_rows, sizeof(affected_rows));
skysql_payload = skysql_payload + sizeof(affected_rows);
memcpy(skysql_payload, &insert_id, sizeof(insert_id));
skysql_payload = skysql_payload + sizeof(insert_id);
memcpy(skysql_payload, skysql_server_status, sizeof(skysql_server_status));
skysql_payload = skysql_payload + sizeof(skysql_server_status);
memcpy(skysql_payload, skysql_warning_count, sizeof(skysql_warning_count));
skysql_payload = skysql_payload + sizeof(skysql_warning_count);
if (skysql_message != NULL) {
memcpy(skysql_payload, skysql_message, strlen(skysql_message));
}
// create brigade
bb = apr_brigade_create(p, c->bucket_alloc);
/*
b = apr_bucket_pool_create(outbuf, sizeof(skysql_packet_header) + skysql_payload_size, c->pool, c->bucket_alloc);
APR_BRIGADE_INSERT_HEAD(bb, b);
b = apr_bucket_flush_create(c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, b);
return ap_pass_brigade(c->output_filters, bb);
*/
apr_brigade_write(bb, ap_filter_flush, c->output_filters, outbuf, sizeof(skysql_packet_header) + skysql_payload_size);
ap_fflush(c->output_filters, bb);
apr_brigade_destroy(bb);
return 1;
}
///////////////////////////
// scramble is 20 bytes and must be pre allocated
apr_status_t skysql_send_handshake(conn_rec *c, uint8_t *scramble, int *scramble_len) {
apr_status_t rv;
rv = APR_SUCCESS;
apr_bucket_brigade *bb;
apr_bucket *b;
apr_pool_t *p = c->pool;
uint8_t *outbuf = NULL;
uint8_t skysql_payload_size = 0;
uint8_t skysql_packet_header[4];
uint8_t skysql_packet_id = 0;
uint8_t skysql_filler = SKYSQL_HANDSKAKE_FILLER;
uint8_t skysql_protocol_version = SKYSQL_PROTOCOL_VERSION;
uint8_t *skysql_handshake_payload = NULL;
uint8_t skysql_thread_id[4];
uint8_t skysql_scramble_buf[9] = "";
uint8_t skysql_plugin_data[13] = "";
uint8_t skysql_server_capabilities_one[2];
uint8_t skysql_server_capabilities_two[2];
uint8_t skysql_server_language = 8;
uint8_t skysql_server_status[2];
uint8_t skysql_scramble_len = 21;
uint8_t skysql_filler_ten[10];
uint8_t skysql_last_byte = 0x00;
uint8_t scramble_buffer[20]="";
skysql_set_random_str(scramble_buffer, 20);
// set len to the caller
memset(scramble_len, 20, 1);
// copy back to the caller
memcpy(scramble, scramble_buffer, 20);
memset(&skysql_filler_ten, 0x00, sizeof(skysql_filler_ten));
// thread id, now put the apache child PID, then a conversion map in memory is needed!
skysql_set_byte4(skysql_thread_id, getpid());
memcpy(skysql_scramble_buf, scramble_buffer, 8);
memcpy(skysql_plugin_data, scramble_buffer + 8, 12);
skysql_payload_size = sizeof(skysql_protocol_version) + (strlen(SKYSQL_VERSION) + 1) + sizeof(skysql_thread_id) + 8 + sizeof(skysql_filler) + sizeof(skysql_server_capabilities_one) + sizeof(skysql_server_language) + sizeof(skysql_server_status) + sizeof(skysql_server_capabilities_two) + sizeof(skysql_scramble_len) + sizeof(skysql_filler_ten) + 12 + sizeof(skysql_last_byte) + strlen("mysql_native_password") + sizeof(skysql_last_byte);
// allocate memory for packet header + payload
outbuf = (uint8_t *) apr_pcalloc(p, sizeof(skysql_packet_header) + skysql_payload_size);
// write packet heder with skysql_payload_size
skysql_set_byte3(skysql_packet_header, skysql_payload_size);
//skysql_packet_header[0] = skysql_payload_size;
// write packent number, now is 0
skysql_packet_header[3]= skysql_packet_id;
memcpy(outbuf, skysql_packet_header, sizeof(skysql_packet_header));
// current buffer pointer
skysql_handshake_payload = outbuf + sizeof(skysql_packet_header);
// write protocol version
memcpy(skysql_handshake_payload, &skysql_protocol_version, sizeof(skysql_protocol_version));
skysql_handshake_payload = skysql_handshake_payload + sizeof(skysql_protocol_version);
// write server version plus 0 filler
strcpy(skysql_handshake_payload, SKYSQL_VERSION);
skysql_handshake_payload = skysql_handshake_payload + strlen(SKYSQL_VERSION);
*skysql_handshake_payload = 0x00;
skysql_handshake_payload++;
// write thread id
memcpy(skysql_handshake_payload, skysql_thread_id, sizeof(skysql_thread_id));
skysql_handshake_payload = skysql_handshake_payload + sizeof(skysql_thread_id);
// write scramble buf
memcpy(skysql_handshake_payload, skysql_scramble_buf, 8);
skysql_handshake_payload = skysql_handshake_payload + 8;
*skysql_handshake_payload = SKYSQL_HANDSKAKE_FILLER;
skysql_handshake_payload++;
// write server capabilities part one
skysql_server_capabilities_one[0] = SKYSQL_SERVER_CAPABILITIES_BYTE1;
skysql_server_capabilities_one[1] = SKYSQL_SERVER_CAPABILITIES_BYTE2;
memcpy(skysql_handshake_payload, skysql_server_capabilities_one, sizeof(skysql_server_capabilities_one));
skysql_handshake_payload = skysql_handshake_payload + sizeof(skysql_server_capabilities_one);
// write server language
memcpy(skysql_handshake_payload, &skysql_server_language, sizeof(skysql_server_language));
skysql_handshake_payload = skysql_handshake_payload + sizeof(skysql_server_language);
//write server status
skysql_server_status[0] = 2;
skysql_server_status[1] = 0;
memcpy(skysql_handshake_payload, skysql_server_status, sizeof(skysql_server_status));
skysql_handshake_payload = skysql_handshake_payload + sizeof(skysql_server_status);
//write server capabilities part two
skysql_server_capabilities_two[0] = 15;
skysql_server_capabilities_two[1] = 128;
memcpy(skysql_handshake_payload, skysql_server_capabilities_two, sizeof(skysql_server_capabilities_two));
skysql_handshake_payload = skysql_handshake_payload + sizeof(skysql_server_capabilities_two);
// write scramble_len
memcpy(skysql_handshake_payload, &skysql_scramble_len, sizeof(skysql_scramble_len));
skysql_handshake_payload = skysql_handshake_payload + sizeof(skysql_scramble_len);
//write 10 filler
memcpy(skysql_handshake_payload, skysql_filler_ten, sizeof(skysql_filler_ten));
skysql_handshake_payload = skysql_handshake_payload + sizeof(skysql_filler_ten);
// write plugin data
memcpy(skysql_handshake_payload, skysql_plugin_data, 12);
skysql_handshake_payload = skysql_handshake_payload + 12;
//write last byte, 0
*skysql_handshake_payload = 0x00;
skysql_handshake_payload++;
// to be understanded ????
memcpy(skysql_handshake_payload, "mysql_native_password", strlen("mysql_native_password"));
skysql_handshake_payload = skysql_handshake_payload + strlen("mysql_native_password");
//write last byte, 0
*skysql_handshake_payload = 0x00;
skysql_handshake_payload++;
// create brigade
bb = apr_brigade_create(p, c->bucket_alloc);
/*
b = apr_bucket_pool_create(outbuf, sizeof(skysql_packet_header) + skysql_payload_size, p, c->bucket_alloc);
APR_BRIGADE_INSERT_HEAD(bb, b);
b = apr_bucket_flush_create(c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, b);
ap_pass_brigade(c->output_filters, bb);
apr_brigade_destroy(bb);
*/
apr_brigade_write(bb, ap_filter_flush, c->output_filters, outbuf, sizeof(skysql_packet_header) + skysql_payload_size);
ap_fflush(c->output_filters, bb);
apr_brigade_destroy(bb);
return 1;
}
int skygateway_query_result(conn_rec *c, apr_pool_t *p, MYSQL_conn *conn, const char *query) {
int query_ret = 0;
int num_fields = 0;
int return_data = 0;
uint8_t result_column_count = 0;
uint8_t header_result_packet[4];
apr_bucket_brigade *bb1;
apr_bucket *b1;
uint8_t *outbuf = NULL;
apr_status_t rv;
uint8_t buffer[MAX_CHUNK];
unsigned long bytes = MAX_CHUNK;
query_ret = mysql_query(conn, query);
fprintf(stderr, "HERE SEND QUERY\n");
fflush(stderr);
if (query_ret) {
// send error, packet #1
skysql_send_error(c, 1, conn);
return 1;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "SKYSQLGW is sending result set ...");
rv = apr_socket_recv(conn->socket, buffer, &bytes);
if (rv != APR_SUCCESS) {
fprintf(stderr, "Errore in recv\n");
fflush(stderr);
return 1;
}
bb1 = apr_brigade_create(p, c->bucket_alloc);
apr_brigade_write(bb1, ap_filter_flush, c->output_filters, buffer, bytes);
ap_fflush(c->output_filters, bb1);
apr_brigade_destroy(bb1);
return 0;
}