From 6f326373159ccab2dfd03780dc346fbcf7382803 Mon Sep 17 00:00:00 2001 From: Massimiliano Pinto Date: Mon, 1 Jul 2013 14:34:47 +0200 Subject: [PATCH] New functions added in mysql_common for full asysncronous mysql connect The new funcs are not yet used in the public repo --- .../include/mysql_client_server_protocol.h | 25 +- modules/protocol/mysql_common.c | 268 +++++++++++++++++- 2 files changed, 273 insertions(+), 20 deletions(-) diff --git a/modules/include/mysql_client_server_protocol.h b/modules/include/mysql_client_server_protocol.h index 03c0e21c7..167511ff3 100644 --- a/modules/include/mysql_client_server_protocol.h +++ b/modules/include/mysql_client_server_protocol.h @@ -103,14 +103,14 @@ typedef struct mysql_session { } 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 backends */ -#define MYSQL_WAITING_RESULT 6 /* Waiting for result set */ -#define MYSQL_CONNECTED 7 /* Backend socket Connected */ +#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 */ +#define MYSQL_CONNECTED 7 /* Backend socket Connected */ /* Protocol packing macros. */ #define gw_mysql_set_byte2(__buffer, __int) do { \ @@ -203,6 +203,14 @@ typedef enum } gw_mysql_capabilities_t; #endif +void gw_mysql_close(MySQLProtocol **ptr); +MySQLProtocol *gw_mysql_init(MySQLProtocol *data); +void gw_mysql_close(MySQLProtocol **ptr); +int gw_receive_backend_auth(MySQLProtocol *conn); +int gw_decode_mysql_server_handshake(MySQLProtocol *conn, uint8_t *payload); +int gw_read_backend_handshake(MySQLProtocol *conn); +int gw_send_authentication_to_backend(char *dbname, char *user, uint8_t *passwd, MySQLProtocol *conn); + extern void gw_sha1_str(const uint8_t *in, int in_len, uint8_t *out); extern void gw_sha1_2_str(const uint8_t *in, int in_len, const uint8_t *in2, int in2_len, uint8_t *out); extern void gw_str_xor(uint8_t *output, const uint8_t *input1, const uint8_t *input2, unsigned int len); @@ -213,4 +221,3 @@ extern char *gw_strend(register const char *s); extern int setnonblocking(int fd); extern void setipaddress(struct in_addr *a, char *p); extern int gw_read_gwbuff(DCB *dcb, GWBUF **head, int b); -void gw_mysql_close(MySQLProtocol **ptr); diff --git a/modules/protocol/mysql_common.c b/modules/protocol/mysql_common.c index d9f5d45b5..cab110b9e 100644 --- a/modules/protocol/mysql_common.c +++ b/modules/protocol/mysql_common.c @@ -26,9 +26,6 @@ #include "mysql_client_server_protocol.h" -MySQLProtocol *gw_mysql_init(MySQLProtocol *data); -void gw_mysql_close(MySQLProtocol **ptr); - extern int gw_read_backend_event(DCB* dcb); extern int gw_write_backend_event(DCB *dcb); extern int gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue); @@ -92,7 +89,43 @@ void gw_mysql_close(MySQLProtocol **ptr) { #endif } -// Decode mysql handshake +/* + * Read the backend server handshake + */ +int gw_read_backend_handshake(MySQLProtocol *conn) { + GWBUF *head = NULL; + DCB *dcb = conn->descriptor; + int n = -1; + uint8_t *payload = NULL; + + if ((n = dcb_read(dcb, &head)) != -1) { + dcb->state = DCB_STATE_PROCESSING; + if (head) { + payload = GWBUF_DATA(head); + + // skip the 4 bytes header + payload += 4; + + //Now decode mysql handshake + gw_decode_mysql_server_handshake(conn, payload); + + conn->state = MYSQL_AUTH_SENT; + + // consume all the data here + head = gwbuf_consume(head, gwbuf_length(head)); + + dcb->state = DCB_STATE_POLLING; + + return 0; + } + } + + return 1; +} + +/* + * Decode mysql server handshake + */ int gw_decode_mysql_server_handshake(MySQLProtocol *conn, uint8_t *payload) { int server_protocol; uint8_t *server_version_end = NULL; @@ -104,12 +137,6 @@ int gw_decode_mysql_server_handshake(MySQLProtocol *conn, uint8_t *payload) { uint8_t capab_ptr[4]; int scramble_len; uint8_t scramble[GW_MYSQL_SCRAMBLE_SIZE]; - uint32_t server_capabilities; - uint32_t final_capabilities; - - // zero the vars - memset(&server_capabilities, '\0', sizeof(server_capabilities)); - memset(&final_capabilities, '\0', sizeof(final_capabilities)); // Get server protocol server_protocol= payload[0]; @@ -151,6 +178,7 @@ int gw_decode_mysql_server_handshake(MySQLProtocol *conn, uint8_t *payload) { // get scramble len scramble_len = payload[0] -1; + // skip 10 zero bytes payload += 11; // copy the second part of the scramble @@ -164,4 +192,222 @@ int gw_decode_mysql_server_handshake(MySQLProtocol *conn, uint8_t *payload) { return 0; } -/// + +/* + Receive the MySQL authentication packet from backend, packet # is 2 +*/ +int gw_receive_backend_auth(MySQLProtocol *conn) { + int rv = 1; + int n = -1; + GWBUF *head = NULL; + DCB *dcb = conn->descriptor; + + if ((n = dcb_read(dcb, &head)) != -1) { + dcb->state = DCB_STATE_PROCESSING; + if (head) { + uint8_t *ptr = GWBUF_DATA(head); + // check if the auth is SUCCESFUL + if (ptr[4] == '\x00') { + // Auth is OK + conn->state = MYSQL_IDLE; + + rv = 0; + } else { + conn->state = MYSQL_AUTH_FAILED; + + rv = 1; + } + + // consume all the data here + head = gwbuf_consume(head, gwbuf_length(head)); + } + } + + dcb->state = DCB_STATE_POLLING; + + return rv; +} + +/* + * send authentication to backend + */ + +int gw_send_authentication_to_backend(char *dbname, char *user, uint8_t *passwd, MySQLProtocol *conn) { + int compress = 0; + int rv; + uint8_t *payload = NULL; + uint8_t *payload_start = NULL; + long bytes; + uint8_t client_scramble[GW_MYSQL_SCRAMBLE_SIZE]; + uint8_t client_capabilities[4]; + uint32_t server_capabilities; + uint32_t final_capabilities; + char dbpass[129]=""; + GWBUF *buffer; + DCB *dcb; + + char *curr_db = NULL; + uint8_t *curr_passwd = NULL; + + if (strlen(dbname)) + curr_db = dbname; + + if (strlen((char *)passwd)) + curr_passwd = passwd; + + dcb = conn->descriptor; + + fprintf(stderr, ">> Sending credentials %s, %s, db %s\n", user, passwd, dbname); + + // Zero the vars + memset(&server_capabilities, '\0', sizeof(server_capabilities)); + memset(&final_capabilities, '\0', sizeof(final_capabilities)); + + final_capabilities = gw_mysql_get_byte4((uint8_t *)&server_capabilities); + + 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]=""; + + // hash1 is the function input, SHA1(real_password) + memcpy(hash1, passwd, GW_MYSQL_SCRAMBLE_SIZE); + + // hash2 is the SHA1(input data), where input_data = SHA1(real_password) + gw_sha1_str(hash1, GW_MYSQL_SCRAMBLE_SIZE, hash2); + + // dbpass is the HEX form of SHA1(SHA1(real_password)) + gw_bin2hex(dbpass, hash2, GW_MYSQL_SCRAMBLE_SIZE); + + // new_sha is the SHA1(CONCAT(scramble, hash2) + gw_sha1_2_str(conn->scramble, GW_MYSQL_SCRAMBLE_SIZE, hash2, GW_MYSQL_SCRAMBLE_SIZE, new_sha); + + // compute the xor in client_scramble + gw_str_xor(client_scramble, new_sha, hash1, GW_MYSQL_SCRAMBLE_SIZE); + + } + + if (curr_db == NULL) { + // without db + final_capabilities &= ~GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB; + } else { + final_capabilities |= GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB; + } + + final_capabilities |= GW_MYSQL_CAPABILITIES_PLUGIN_AUTH; + + gw_mysql_set_byte4(client_capabilities, final_capabilities); + + + // 4 + 4 + 1 + 23 = 32 + bytes = 32; + + bytes += strlen(user); + // the NULL + bytes++; + + // next will be + 1 (scramble_len) + 20 (fixed_scramble) + 1 (user NULL term) + 1 (db NULL term) + + if (curr_passwd != NULL) { + bytes++; + bytes += GW_MYSQL_SCRAMBLE_SIZE; + } else { + bytes++; + } + + if (curr_db != NULL) { + bytes += strlen(curr_db); + bytes++; + } + + bytes +=strlen("mysql_native_password"); + bytes++; + + // the packet header + bytes += 4; + + // allocating the GWBUF + buffer = gwbuf_alloc(bytes); + payload = GWBUF_DATA(buffer); + + // clearing data + memset(payload, '\0', bytes); + + // save the start pointer + payload_start = payload; + + // set packet # = 1 + payload[3] = '\x01'; + payload += 4; + + // set client capabilities + memcpy(payload, client_capabilities, 4); + + // set now the max-packet size + payload += 4; + gw_mysql_set_byte4(payload, 16777216); + + // set the charset + payload += 4; + *payload = '\x08'; + + payload++; + + // 23 bytes of 0 + payload += 23; + + // 4 + 4 + 4 + 1 + 23 = 36 + + memcpy(payload, user, strlen(user)); + payload += strlen(user); + payload++; + + if (curr_passwd != NULL) { + // set the auth-length + *payload = GW_MYSQL_SCRAMBLE_SIZE; + payload++; + + //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; + + } else { + // skip the auth-length and write a NULL + payload++; + } + + // if the db is not NULL append it + if (curr_db != NULL) { + memcpy(payload, curr_db, strlen(curr_db)); + payload += strlen(curr_db); + payload++; + } + + memcpy(payload, "mysql_native_password", strlen("mysql_native_password")); + + payload += strlen("mysql_native_password"); + payload++; + + gw_mysql_set_byte3(payload_start, (bytes-4)); + + // write to backend dcb + rv = dcb->func.write(dcb, buffer); + + conn->state = MYSQL_AUTH_RECV; + + if (rv < 0) + return rv; + else + return 0; +} +/////