develop branch merged

develop branch merged
This commit is contained in:
MassimilianoPinto
2014-10-10 15:30:56 +02:00
63 changed files with 6064 additions and 329 deletions

View File

@ -199,6 +199,7 @@ static int gw_read_backend_event(DCB *dcb) {
if (backend_protocol->protocol_auth_state == MYSQL_CONNECTED)
{
/** Read cached backend handshake */
if (gw_read_backend_handshake(backend_protocol) != 0)
{
backend_protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
@ -209,11 +210,13 @@ static int gw_read_backend_event(DCB *dcb) {
"state = MYSQL_AUTH_FAILED.",
pthread_self(),
backend_protocol->owner_dcb->fd)));
}
else
else
{
/* handshake decoded, send the auth credentials */
/**
* Decode password and send the auth credentials
* to backend.
*/
if (gw_send_authentication_to_backend(
current_session->db,
current_session->user,
@ -227,16 +230,16 @@ static int gw_read_backend_event(DCB *dcb) {
"gw_send_authentication_to_backend "
"fd %d, state = MYSQL_AUTH_FAILED.",
pthread_self(),
backend_protocol->owner_dcb->fd)));
}
else
backend_protocol->owner_dcb->fd)));
}
else
{
backend_protocol->protocol_auth_state = MYSQL_AUTH_RECV;
}
}
}
spinlock_release(&dcb->authlock);
}
} /*< backend_protocol->protocol_auth_state == MYSQL_CONNECTED */
/*
* Now:
* -- check the authentication reply from backend
@ -266,9 +269,10 @@ static int gw_read_backend_event(DCB *dcb) {
router_instance = session->service->router_instance;
rsession = session->router_session;
if (backend_protocol->protocol_auth_state == MYSQL_AUTH_RECV) {
/*<
* Read backed auth reply
if (backend_protocol->protocol_auth_state == MYSQL_AUTH_RECV)
{
/**
* Read backed's reply to authentication message
*/
receive_rc =
gw_receive_backend_auth(backend_protocol);
@ -283,7 +287,6 @@ static int gw_read_backend_event(DCB *dcb) {
"fd %d, state = MYSQL_AUTH_FAILED.",
pthread_self(),
backend_protocol->owner_dcb->fd)));
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
@ -362,8 +365,8 @@ static int gw_read_backend_event(DCB *dcb) {
0,
"Authentication with backend failed. "
"Session will be closed.");
router->handleError(router_instance,
router->handleError(router_instance,
rsession,
errbuf,
dcb,
@ -371,7 +374,6 @@ static int gw_read_backend_event(DCB *dcb) {
&succp);
ss_dassert(!succp);
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_backend_event] "
@ -412,7 +414,7 @@ static int gw_read_backend_event(DCB *dcb) {
}
}
} /* MYSQL_AUTH_RECV || MYSQL_AUTH_FAILED */
spinlock_release(&dcb->authlock);
} /* MYSQL_AUTH_RECV || MYSQL_AUTH_FAILED */
@ -878,6 +880,10 @@ static int gw_create_backend_connection(
goto return_fd;
}
/** Copy client flags to backend protocol */
protocol->client_capabilities =
((MySQLProtocol *)(backend_dcb->session->client->protocol))->client_capabilities;
/*< if succeed, fd > 0, -1 otherwise */
rv = gw_do_connect_to_backend(server->name, server->port, &fd);
/*< Assign protocol with backend_dcb */
@ -1048,6 +1054,10 @@ gw_backend_close(DCB *dcb)
mysql_protocol_done(dcb);
/**
* If session->state is set to STOPPING the client and the session must
* be closed too.
*/
if (session != NULL && session->state == SESSION_STATE_STOPPING)
{
client_dcb = session->client;

View File

@ -68,6 +68,8 @@ int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char*
int MySQLSendHandshake(DCB* dcb);
static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue);
static int route_by_statement(SESSION *, GWBUF **);
static char* create_auth_fail_str(GWBUF* readbuf, char* hostaddr, char* sha1);
static char* get_username_from_auth(char* ptr, uint8_t* data);
/*
* The "module object" for the mysqld client protocol module.
@ -372,9 +374,9 @@ MySQLSendHandshake(DCB* dcb)
* The useful data: user, db, client_sha1 are copied into the MYSQL_session * dcb->session->data
* client_capabilitiesa are copied into the dcb->protocol
*
* @param dcb Descriptor Control Block of the client
* @param queue The GWBUF with data from client
* @return 0 for Authentication ok, !=0 for failed autht
* @param dcb Descriptor Control Block of the client
* @param queue The GWBUF with data from client
* @return 0 If succeed, otherwise non-zero value
*
*/
@ -432,12 +434,10 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
&protocol->client_capabilities);
*/
/* now get the user */
strncpy(username, (char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23), MYSQL_USER_MAXLEN);
/* the empty username field is not allowed */
if (!strlen(username)) {
username = get_username_from_auth(username, client_auth_packet);
if (username == NULL)
{
return 1;
}
@ -448,9 +448,9 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
if (connect_with_db) {
database = client_data->db;
strncpy(database,
(char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) +
1 + 1 + auth_token_len), MYSQL_DATABASE_MAXLEN);
strncpy(database,
(char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) +
1 + 1 + auth_token_len), MYSQL_DATABASE_MAXLEN);
}
/* allocate memory for token only if auth_token_len > 0 */
@ -489,10 +489,80 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
{
dcb->user = strdup(client_data->user);
}
return auth_ret;
}
/**
* Read username from MySQL authentication packet.
*
* @param ptr address where to write the result or NULL if memory
* is allocated here.
* @param data Address of MySQL packet.
*
* @return Pointer to a copy of the username. NULL if memory allocation
* failed or if username was empty.
*/
static char* get_username_from_auth(
char* ptr,
uint8_t* data)
{
char* first_letter;
char* rval;
first_letter = (char *)(data + 4 + 4 + 4 + 1 + 23);
if (first_letter == '\0')
{
rval = NULL;
goto retblock;
}
if (ptr == NULL)
{
if ((rval = (char *)malloc(MYSQL_USER_MAXLEN+1)) == NULL)
{
goto retblock;
}
}
else
{
rval = ptr;
}
snprintf(rval, MYSQL_USER_MAXLEN+1, "%s", first_letter);
retblock:
return rval;
}
static char* create_auth_fail_str(
GWBUF* readbuf,
char* hostaddr,
char* sha1)
{
char* errstr;
char* uname;
const char* ferrstr = "Access denied for user '%s'@'%s' (using password: %s)";
if ( (uname = get_username_from_auth(NULL, (uint8_t *)GWBUF_DATA(readbuf))) == NULL)
{
errstr = NULL;
goto retblock;
}
/** -4 comes from 2X'%s' minus terminating char */
errstr = (char *)malloc(strlen(uname)+strlen(ferrstr)+strlen(hostaddr)+strlen("YES")-6+1);
if (errstr != NULL)
{
sprintf(errstr, ferrstr, uname, hostaddr, (*sha1 == '\0' ? "NO" : "YES"));
}
retblock:
return errstr;
}
/**
* Write function for client DCB: writes data from MaxScale to Client
*
@ -593,77 +663,83 @@ int gw_read_client_event(
case MYSQL_AUTH_SENT:
{
int auth_val = -1;
int auth_val;
auth_val = gw_mysql_do_authentication(dcb, read_buffer);
read_buffer = gwbuf_consume(read_buffer, nbytes_read);
ss_dassert(read_buffer == NULL || GWBUF_EMPTY(read_buffer));
if (auth_val == 0)
{
SESSION *session = NULL;
protocol->protocol_auth_state = MYSQL_AUTH_RECV;
/**
* Create session, and a router session for it.
* If successful, there will be backend connection(s)
* after this point.
*/
session = session_alloc(dcb->service, dcb);
if (session != NULL)
{
CHK_SESSION(session);
ss_dassert(session->state != SESSION_STATE_ALLOC);
if (auth_val == 0)
{
SESSION *session;
protocol->protocol_auth_state = MYSQL_AUTH_RECV;
/**
* Create session, and a router session for it.
* If successful, there will be backend connection(s)
* after this point.
*/
session = session_alloc(dcb->service, dcb);
if (session != NULL)
{
CHK_SESSION(session);
ss_dassert(session->state != SESSION_STATE_ALLOC);
protocol->protocol_auth_state = MYSQL_IDLE;
/**
* Send an AUTH_OK packet to the client,
* packet sequence is # 2
*/
mysql_send_ok(dcb, 2, 0, NULL);
}
else
{
protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_client_event] session "
"creation failed. fd %d, "
"state = MYSQL_AUTH_FAILED.",
protocol->owner_dcb->fd,
pthread_self())));
/** Send ERR 1045 to client */
mysql_send_auth_error(
dcb,
2,
0,
"failed to create new session");
dcb_close(dcb);
}
}
else
{
char* fail_str;
protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
fail_str = create_auth_fail_str(read_buffer,
dcb->remote,
(char*)((MYSQL_session *)dcb->data)->client_sha1);
/** Send error 1045 to client */
mysql_send_auth_error(
dcb,
2,
0,
fail_str);
protocol->protocol_auth_state = MYSQL_IDLE;
/**
* Send an AUTH_OK packet to the client,
* packet sequence is # 2
*/
mysql_send_ok(dcb, 2, 0, NULL);
}
else
{
protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_client_event] session "
"creation failed. fd %d, "
"state = MYSQL_AUTH_FAILED.",
protocol->owner_dcb->fd,
pthread_self())));
/** Send ERR 1045 to client */
mysql_send_auth_error(
dcb,
2,
0,
"failed to create new session");
dcb_close(dcb);
}
}
else
{
protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_client_event] after "
"gw_mysql_do_authentication, fd %d, "
"state = MYSQL_AUTH_FAILED.",
protocol->owner_dcb->fd,
pthread_self())));
/** Send ERR 1045 to client */
mysql_send_auth_error(
dcb,
2,
0,
"Authorization failed");
dcb_close(dcb);
}
}
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_client_event] after "
"gw_mysql_do_authentication, fd %d, "
"state = MYSQL_AUTH_FAILED.",
protocol->owner_dcb->fd,
pthread_self())));
free(fail_str);
dcb_close(dcb);
}
read_buffer = gwbuf_consume(read_buffer, nbytes_read);
}
break;
case MYSQL_IDLE:
@ -801,9 +877,12 @@ int gw_read_client_event(
}
/** succeed */
if (rc) {
if (rc)
{
rc = 0; /**< here '0' means success */
} else {
}
else
{
GWBUF* errbuf;
bool succp;
@ -1360,20 +1439,12 @@ gw_client_close(DCB *dcb)
CHK_SESSION(session);
spinlock_acquire(&session->ses_lock);
if (session->state == SESSION_STATE_STOPPING)
if (session->state != SESSION_STATE_STOPPING)
{
/**
* Session is already getting closed so avoid
* redundant calls
*/
spinlock_release(&session->ses_lock);
return 1;
}
else
{
session->state = SESSION_STATE_STOPPING;
spinlock_release(&session->ses_lock);
}
session->state = SESSION_STATE_STOPPING;
}
spinlock_release(&session->ses_lock);
router = session->service->router;
router_instance = session->service->router_instance;
rsession = session->router_session;

View File

@ -466,6 +466,7 @@ int gw_receive_backend_auth(
bufstr)));
free(bufstr);
free(err);
rc = -1;
}
else
@ -540,9 +541,9 @@ int gw_receive_backend_auth(
* @return 0 on success, 1 on failure
*/
int gw_send_authentication_to_backend(
char *dbname,
char *user,
uint8_t *passwd,
char *dbname,
char *user,
uint8_t *passwd,
MySQLProtocol *conn)
{
int compress = 0;
@ -552,8 +553,8 @@ int gw_send_authentication_to_backend(
long bytes;
uint8_t client_scramble[GW_MYSQL_SCRAMBLE_SIZE];
uint8_t client_capabilities[4];
uint32_t server_capabilities;
uint32_t final_capabilities;
uint32_t server_capabilities = 0;
uint32_t final_capabilities = 0;
char dbpass[MYSQL_USER_MAXLEN + 1]="";
GWBUF *buffer;
DCB *dcb;
@ -568,17 +569,12 @@ int gw_send_authentication_to_backend(
curr_passwd = passwd;
dcb = conn->owner_dcb;
// 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;
/** Copy client's flags to backend */
final_capabilities |= conn->client_capabilities;;
if (compress) {
if (compress) {
final_capabilities |= GW_MYSQL_CAPABILITIES_COMPRESS;
#ifdef DEBUG_MYSQL_CONN
fprintf(stderr, ">>>> Backend Connection with compression\n");
@ -1033,19 +1029,24 @@ int mysql_send_custom_error (
* @param passwd The SHA1(real_password): Note real_password is unknown
* @return 1 on success, 0 on failure
*/
int gw_send_change_user_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[MYSQL_USER_MAXLEN + 1]="";
GWBUF *buffer;
DCB *dcb;
int gw_send_change_user_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 = 0;
uint32_t final_capabilities = 0;
char dbpass[MYSQL_USER_MAXLEN + 1]="";
GWBUF *buffer;
DCB *dcb;
char *curr_db = NULL;
uint8_t *curr_passwd = NULL;
@ -1058,14 +1059,10 @@ int gw_send_change_user_to_backend(char *dbname, char *user, uint8_t *passwd, My
dcb = conn->owner_dcb;
// 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_get_byte4((uint8_t *)&server_capabilities);
final_capabilities |= GW_MYSQL_CAPABILITIES_PROTOCOL_41;
final_capabilities |= GW_MYSQL_CAPABILITIES_CLIENT;
/** Copy client's flags to backend */
final_capabilities |= conn->client_capabilities;;
if (compress) {
final_capabilities |= GW_MYSQL_CAPABILITIES_COMPRESS;
@ -1366,8 +1363,10 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password,
LOGIF(LE,
(skygw_log_write_flush(
LOGFILE_ERROR,
"%lu [MySQL Client Auth], user [%s@%s] not found, please try with 'localhost_match_wildcard_host=1' in service definition",
pthread_self(),
"Error : user %s@%s not found, try set "
"'localhost_match_wildcard_host=1' in "
"service definition of the configuration "
"file.",
key.user,
dcb->remote)));
@ -1506,7 +1505,7 @@ mysql_send_auth_error (
}
mysql_errno = 1045;
mysql_error_msg = "Access denied!";
mysql_state = "2800";
mysql_state = "28000";
field_count = 0xff;
gw_mysql_set_byte2(mysql_err, mysql_errno);