864 lines
31 KiB
C
864 lines
31 KiB
C
////////////////////////////////////////
|
|
// 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;
|
|
}
|
|
|