log_manager.cc:
State update for filewriter was missing and that caused Maxscale to fail if opening of any log file failed. dcb.c: Added EAGAIN and EWOULDBLOCK handling to dcb_read. If dcb_close is called for freshly created dcb, dcb is only freed. gateway.c: Added file_write_footer and write_footer of which the latter is called at exit time. It simply draws a line to screen. gw_utils.c: Some macros for helping comparison between gw_read_gwbuff and dcb_read, which overlap. poll.c: Some macros to help enable/disable mutexing in poll_waitevents service.c: Check return value of listen and session_alloc and behave accordingly. mysql_client.c: If ioctl returned successfully with b==0 it earlier caused closing the client and backend dcbs. Since that doesn't reliably indicate that client has closed socket on its side, Maxscale doesn't close its sockets either. mysql_common.c: In gw_receive_backend_auth, if dcb_read returns n==0, it is not considered as an error anymore. The implemented behavior is not yet complete and correct. Result should be successful but the protocol state shouldn't change to MYSQL_IDLE before backend return is received. In gw_send_authentication_to_backend protocol state was always set to MYSQL_AUTH_RECV even if gw_rwite had failed. Now, return value is read and state is set in caller's context basen on the return value. skygw_utils.cc: Removed ss_dassert from skyge_file_init because it prevented from returning meaningful error meassage to the client.:
This commit is contained in:
@ -181,16 +181,10 @@ static int gw_read_backend_event(DCB *dcb) {
|
||||
current_session->client_sha1,
|
||||
backend_protocol) != 0)
|
||||
{
|
||||
ss_dassert(backend_protocol->state ==
|
||||
MYSQL_AUTH_FAILED);
|
||||
backend_protocol->state = MYSQL_AUTH_FAILED;
|
||||
rc = 1;
|
||||
} else {
|
||||
/**
|
||||
* next step is to wait server's response with
|
||||
* a new EPOLLIN event
|
||||
*/
|
||||
ss_dassert(backend_protocol->state ==
|
||||
MYSQL_AUTH_RECV);
|
||||
backend_protocol->state = MYSQL_AUTH_RECV;
|
||||
rc = 0;
|
||||
goto return_rc;
|
||||
}
|
||||
@ -219,23 +213,24 @@ static int gw_read_backend_event(DCB *dcb) {
|
||||
/**
|
||||
* Read backed auth reply
|
||||
*/
|
||||
if (!gw_receive_backend_auth(backend_protocol)) {
|
||||
if (gw_receive_backend_auth(backend_protocol)) {
|
||||
backend_protocol->state = MYSQL_IDLE;
|
||||
} else {
|
||||
backend_protocol->state = MYSQL_AUTH_FAILED;
|
||||
|
||||
skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"%lu [gw_read_backend_event] "
|
||||
"gw_receive_backend_auth failed. dcb "
|
||||
"%p fd %d, user %s.",
|
||||
pthread_self(),
|
||||
dcb,
|
||||
dcb->fd,
|
||||
current_session->user);
|
||||
}
|
||||
}
|
||||
|
||||
if (backend_protocol->state == MYSQL_AUTH_FAILED) {
|
||||
skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"%lu [gw_read_backend_event] "
|
||||
"gw_receive_backend_auth failed. dcb %p fd %d, "
|
||||
"user %s.",
|
||||
pthread_self(),
|
||||
dcb,
|
||||
dcb->fd,
|
||||
current_session->user);
|
||||
|
||||
|
||||
/* check the delayq before the reply */
|
||||
if (dcb->delayq) {
|
||||
/* send an error to the client */
|
||||
@ -243,7 +238,7 @@ static int gw_read_backend_event(DCB *dcb) {
|
||||
dcb->session->client,
|
||||
1,
|
||||
0,
|
||||
"Connection to backend lost right now");
|
||||
"Connection to backend lost.");
|
||||
}
|
||||
/**
|
||||
* Protect call of closeSession.
|
||||
@ -273,7 +268,7 @@ static int gw_read_backend_event(DCB *dcb) {
|
||||
rc = 1;
|
||||
goto return_rc;
|
||||
} else {
|
||||
ss_dassert(backend_protocol->state == MYSQL_AUTH_RECV);
|
||||
ss_dassert(backend_protocol->state == MYSQL_IDLE);
|
||||
skygw_log_write_flush(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [gw_read_backend_event] "
|
||||
@ -284,7 +279,6 @@ static int gw_read_backend_event(DCB *dcb) {
|
||||
current_session->user);
|
||||
|
||||
spinlock_acquire(&dcb->authlock);
|
||||
backend_protocol->state = MYSQL_IDLE;
|
||||
/* check the delay queue and flush the data */
|
||||
if(dcb->delayq) {
|
||||
backend_write_delayqueue(dcb);
|
||||
@ -312,8 +306,9 @@ static int gw_read_backend_event(DCB *dcb) {
|
||||
|
||||
if (rc < 0) {
|
||||
/**
|
||||
* Backend generated EPOLLIN event and if backend has failed, connection
|
||||
* must be closed to avoid backend dcb from getting hanged.
|
||||
* Backend generated EPOLLIN event and if backend has
|
||||
* failed, connection must be closed to avoid backend
|
||||
* dcb from getting hanged.
|
||||
*/
|
||||
(dcb->func).close(dcb);
|
||||
rc = 0;
|
||||
@ -335,7 +330,8 @@ static int gw_read_backend_event(DCB *dcb) {
|
||||
* If dcb->session->client is freed already it may be NULL.
|
||||
*/
|
||||
if (dcb->session->client != NULL) {
|
||||
client_protocol = SESSION_PROTOCOL(dcb->session, MySQLProtocol);
|
||||
client_protocol = SESSION_PROTOCOL(dcb->session,
|
||||
MySQLProtocol);
|
||||
}
|
||||
|
||||
if (client_protocol != NULL) {
|
||||
@ -448,26 +444,28 @@ gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue)
|
||||
skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"%lu [gw_MySQLWrite_backend] Write to backend failed. "
|
||||
"Backend dcb is %s.",
|
||||
"Backend dcb %p fd %d is %s.",
|
||||
pthread_self(),
|
||||
dcb,
|
||||
dcb->fd,
|
||||
STRDCBSTATE(dcb->state));
|
||||
return 0;
|
||||
}
|
||||
spinlock_acquire(&dcb->authlock);
|
||||
/**
|
||||
* Now put the incoming data to the delay queue unless backend is connected with auth ok
|
||||
* Now put the incoming data to the delay queue unless backend is
|
||||
* connected with auth ok
|
||||
*/
|
||||
if (backend_protocol->state != MYSQL_IDLE) {
|
||||
skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [gw_MySQLWrite_backend] dcb %p fd %d protocol state %s.",
|
||||
"%lu [gw_MySQLWrite_backend] dcb %p fd %d protocol "
|
||||
"state %s.",
|
||||
pthread_self(),
|
||||
dcb,
|
||||
dcb->fd,
|
||||
STRPROTOCOLSTATE(backend_protocol->state));
|
||||
|
||||
//fprintf(stderr, ">>> Writing in the backend %i delay queue: last dcb command %i, queue command %i, protocol state [%s]\n", dcb->fd, dcb->command, queue->command, gw_mysql_protocol_state2string(dcb->state));
|
||||
|
||||
backend_set_delayqueue(dcb, queue);
|
||||
spinlock_release(&dcb->authlock);
|
||||
return 1;
|
||||
@ -640,7 +638,7 @@ return_fd:
|
||||
|
||||
|
||||
/**
|
||||
* Hangup routine the backend dcb: it does nothing right now
|
||||
* Hangup routine the backend dcb: it does nothing
|
||||
*
|
||||
* @param dcb The current Backend DCB
|
||||
* @return 1 always
|
||||
@ -726,7 +724,8 @@ static int backend_write_delayqueue(DCB *dcb)
|
||||
dcb->session->client,
|
||||
1,
|
||||
0,
|
||||
"Closed backend connection.");
|
||||
"Unable to write to backend server. Connection was "
|
||||
"closed.");
|
||||
dcb_close(dcb);
|
||||
}
|
||||
return rc;
|
||||
|
@ -521,7 +521,7 @@ int gw_read_client_event(DCB* dcb) {
|
||||
/**
|
||||
* Check how many bytes are readable in dcb->fd.
|
||||
*/
|
||||
if (ioctl(dcb->fd, FIONREAD, &b)) {
|
||||
if (ioctl(dcb->fd, FIONREAD, &b) != 0) {
|
||||
int eno = errno;
|
||||
errno = 0;
|
||||
skygw_log_write(
|
||||
@ -538,9 +538,13 @@ int gw_read_client_event(DCB* dcb) {
|
||||
}
|
||||
|
||||
/**
|
||||
* The client socket was closed.
|
||||
* Note that earlier b==0 was translated to closed client socket.
|
||||
* It may, however, happen in other cases too. Besides, if socket
|
||||
* was closed, next write will tell, thus, with b==0 routine can
|
||||
* simply return.
|
||||
*/
|
||||
if (b == 0) {
|
||||
#if 0
|
||||
if (b == 0) {
|
||||
skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"%lu [gw_read_client_event] Dcb %p fd %d was closed. "
|
||||
@ -568,7 +572,12 @@ int gw_read_client_event(DCB* dcb) {
|
||||
|
||||
goto return_rc;
|
||||
}
|
||||
|
||||
#else
|
||||
if (b == 0) {
|
||||
rc = 0;
|
||||
goto return_rc;
|
||||
}
|
||||
#endif
|
||||
switch (protocol->state) {
|
||||
case MYSQL_AUTH_SENT:
|
||||
/*
|
||||
@ -594,7 +603,6 @@ int gw_read_client_event(DCB* dcb) {
|
||||
// example with consume, assuming one buffer only ...
|
||||
queue = gw_buffer;
|
||||
len = GWBUF_LENGTH(queue);
|
||||
//fprintf(stderr, "<<< Reading from Client %i bytes: [%s]\n", len, GWBUF_DATA(queue));
|
||||
auth_val = gw_mysql_do_authentication(dcb, queue);
|
||||
// Data handled withot the dcb->func.write
|
||||
// so consume it now
|
||||
@ -690,7 +698,6 @@ int gw_read_client_event(DCB* dcb) {
|
||||
"client dcb %p.",
|
||||
pthread_self(),
|
||||
dcb);
|
||||
|
||||
(dcb->func).close(dcb);
|
||||
} else {
|
||||
/* Send a custom error as MySQL command reply */
|
||||
@ -724,7 +731,9 @@ int gw_read_client_event(DCB* dcb) {
|
||||
else
|
||||
{
|
||||
/** Route other commands to backend */
|
||||
rc = router->routeQuery(router_instance, rsession, queue);
|
||||
rc = router->routeQuery(router_instance,
|
||||
rsession,
|
||||
queue);
|
||||
/** succeed */
|
||||
if (rc == 1) {
|
||||
rc = 0; /**< here '0' means success */
|
||||
@ -1068,7 +1077,6 @@ int gw_MySQLAccept(DCB *listener)
|
||||
if (protocol == NULL) {
|
||||
/** delete client_dcb */
|
||||
dcb_close(client_dcb);
|
||||
|
||||
skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"%lu [gw_MySQLAccept] Failed to create "
|
||||
@ -1093,6 +1101,13 @@ int gw_MySQLAccept(DCB *listener)
|
||||
*/
|
||||
if (poll_add_dcb(client_dcb) == -1)
|
||||
{
|
||||
/* Send a custom error as MySQL command reply */
|
||||
mysql_send_custom_error(
|
||||
client_dcb,
|
||||
1,
|
||||
0,
|
||||
"MaxScale internal error.");
|
||||
|
||||
/** delete client_dcb */
|
||||
dcb_close(client_dcb);
|
||||
|
||||
@ -1161,7 +1176,6 @@ gw_client_close(DCB *dcb)
|
||||
CHK_PROTOCOL(protocol);
|
||||
}
|
||||
#endif
|
||||
|
||||
dcb_close(dcb);
|
||||
return 1;
|
||||
}
|
||||
|
@ -166,8 +166,8 @@ int gw_read_backend_handshake(MySQLProtocol *conn) {
|
||||
|
||||
if (h_len < (packet_len + 4)) {
|
||||
/*
|
||||
* data in buffer less than expected in the packet
|
||||
* log error this exit point
|
||||
* data in buffer less than expected in the
|
||||
* packet. Log error this exit point
|
||||
*/
|
||||
conn->state = MYSQL_AUTH_FAILED;
|
||||
return 1;
|
||||
@ -177,7 +177,8 @@ int gw_read_backend_handshake(MySQLProtocol *conn) {
|
||||
payload += 4;
|
||||
|
||||
//Now decode mysql handshake
|
||||
success = gw_decode_mysql_server_handshake(conn, payload);
|
||||
success = gw_decode_mysql_server_handshake(conn,
|
||||
payload);
|
||||
|
||||
if (success < 0) {
|
||||
/* MySQL handshake has not been properly decoded
|
||||
@ -308,7 +309,10 @@ bool gw_receive_backend_auth(
|
||||
|
||||
n = dcb_read(dcb, &head);
|
||||
|
||||
if (n != -1 &&
|
||||
/**
|
||||
* Read didn't fail and there is enough data for mysql packet.
|
||||
*/
|
||||
if (n != -1 &&
|
||||
head != NULL &&
|
||||
GWBUF_LENGTH(head) >= 5)
|
||||
{
|
||||
@ -318,11 +322,52 @@ bool gw_receive_backend_auth(
|
||||
*/
|
||||
if (ptr[4] == '\x00') {
|
||||
succp = true;
|
||||
} else {
|
||||
uint8_t* tmpbuf =
|
||||
(uint8_t *)calloc(1, GWBUF_LENGTH(head)+1);
|
||||
memcpy(tmpbuf, ptr, GWBUF_LENGTH(head));
|
||||
skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"%lu [gw_receive_backend_auth] Invalid "
|
||||
"authentication message from backend dcb %p "
|
||||
"fd %d, ptr[4] = %p, msg %s.",
|
||||
pthread_self(),
|
||||
dcb,
|
||||
dcb->fd,
|
||||
tmpbuf[4],
|
||||
tmpbuf);
|
||||
|
||||
skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Invalid authentication message from "
|
||||
"backend server. Authentication failed.");
|
||||
free(tmpbuf);
|
||||
}
|
||||
/**
|
||||
* Remove data from buffer.
|
||||
*/
|
||||
head = gwbuf_consume(head, gwbuf_length(head));
|
||||
} else {
|
||||
#if 1
|
||||
/**
|
||||
* This is considered as success because call didn't fail,
|
||||
* although no bytes was read.
|
||||
*/
|
||||
if (n == 0) {
|
||||
succp = true;
|
||||
}
|
||||
#endif
|
||||
skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"%lu [gw_receive_backend_auth] Reading from backend dcb "
|
||||
"%p fd %d in state %s failed. n %d, head %p, len %d",
|
||||
pthread_self(),
|
||||
dcb,
|
||||
dcb->fd,
|
||||
STRDCBSTATE(dcb->state),
|
||||
n,
|
||||
head,
|
||||
(head == NULL) ? 0 : GWBUF_LENGTH(head));
|
||||
}
|
||||
return succp;
|
||||
}
|
||||
@ -506,27 +551,22 @@ int gw_send_authentication_to_backend(
|
||||
payload++;
|
||||
}
|
||||
|
||||
memcpy(payload, "mysql_native_password", strlen("mysql_native_password"));
|
||||
|
||||
memcpy(payload,
|
||||
"mysql_native_password",
|
||||
strlen("mysql_native_password"));
|
||||
payload += strlen("mysql_native_password");
|
||||
payload++;
|
||||
|
||||
// put here the paylod size: bytes to write - 4 bytes packet header
|
||||
gw_mysql_set_byte3(payload_start, (bytes-4));
|
||||
|
||||
// write to backend dcb
|
||||
// ToDO: handle the EAGAIN | EWOULDBLOCK
|
||||
rv = gw_write(dcb->fd, GWBUF_DATA(buffer), bytes);
|
||||
rv = dcb_write(dcb, buffer);
|
||||
|
||||
gwbuf_consume(buffer, bytes);
|
||||
|
||||
/* Set the new state, next would be MYSQL_IDLE or MYSQL_AUTH_FAILED */
|
||||
conn->state = MYSQL_AUTH_RECV;
|
||||
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
else
|
||||
return 0;
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user