From 0352705b671f01df4d786fc55cf85e1c78ebeffa Mon Sep 17 00:00:00 2001 From: Massimiliano Pinto Date: Tue, 11 Jun 2013 13:02:26 +0200 Subject: [PATCH] New files and new versions --- compila | 6 + dcb.h | 2 + gateway.c | 4 +- gw.h | 2 +- session.h | 36 +++ utils.c | 792 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 839 insertions(+), 3 deletions(-) create mode 100644 compila create mode 100644 session.h create mode 100644 utils.c diff --git a/compila b/compila new file mode 100644 index 000000000..009d1322d --- /dev/null +++ b/compila @@ -0,0 +1,6 @@ +rm *.o +gcc -c gateway_mysql_protocol.c +gcc -c utils.c +gcc -c gw_utils.c +gcc -c gateway.c +gcc -o gateway gw_utils.o gateway.o utils.o gateway_mysql_protocol.o -lssl diff --git a/dcb.h b/dcb.h index 0af7ca906..59d3090ad 100644 --- a/dcb.h +++ b/dcb.h @@ -66,6 +66,8 @@ typedef struct dcb { #define DCB_STATE_POLLING 2 /* Waiting in the poll loop */ #define DCB_STATE_PROCESSING 4 /* Processing an event */ #define DCB_STATE_LISTENING 5 /* The DCB is for a listening socket */ +#define DCB_STATE_DISCONNECTED 6 /* The socket is now closed */ +#define DCB_STATE_FREED 7 /* Memory freed */ /* A few useful macros */ #define DCB_SESSION(x) (x)->session diff --git a/gateway.c b/gateway.c index 23f674c5e..45f63af5a 100644 --- a/gateway.c +++ b/gateway.c @@ -109,11 +109,11 @@ int handle_event_errors(DCB *dcb, int event) { } fprintf(stderr, "Return from error handling, dcb is %p\n", dcb); - free(dcb->session); + //free(dcb->session); dcb->state = DCB_STATE_FREED; fprintf(stderr, "#### Handle error function RETURN for [%i] is [%s]\n", dcb->state, gw_dcb_state2string(dcb->state)); - //myfree((void **)&dcb); + //free(dcb); return 1; } diff --git a/gw.h b/gw.h index 6a5f1355a..d288a0b18 100644 --- a/gw.h +++ b/gw.h @@ -25,7 +25,7 @@ // network buffer is 32K #define MAX_BUFFER_SIZE 32768 // socket send buffer for backend -#define GW_BACKEND_SO_SNDBUF 16 +#define GW_BACKEND_SO_SNDBUF 1024 #define GW_NOINTR_CALL(A) do { errno = 0; A; } while (errno == EINTR) #define GW_VERSION "0.1.0" diff --git a/session.h b/session.h new file mode 100644 index 000000000..70ac34fac --- /dev/null +++ b/session.h @@ -0,0 +1,36 @@ +#ifndef _SESSION_H +#define _SESSION_H +/* + * This file is distributed as part of the SkySQL Gateway. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright SkySQL Ab + */ + +struct dcb; + +/* + * The session status block + */ +typedef struct session { + int state; /* Current descriptor state */ + struct dcb *client; /* The client connection */ + struct dcb *backends; /* The set of backend servers */ +} SESSION; + +#define SESSION_STATE_ALLOC 0 +#define SESSION_STATE_READY 1 + +#define SESSION_PROTOCOL(x, type) DCB_PROTOCOL((x)->client, type) +#endif diff --git a/utils.c b/utils.c new file mode 100644 index 000000000..0f4c68e57 --- /dev/null +++ b/utils.c @@ -0,0 +1,792 @@ +/* +This file is distributed as part of the SkySQL Gateway. It is free +software: you can redistribute it and/or modify it under the terms of the +GNU General Public License as published by the Free Software Foundation, +version 2. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 51 +Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Copyright SkySQL Ab + +*/ + +#include "gw.h" +#include "dcb.h" +#include "session.h" +#include "mysql_protocol.h" +#include + +// used in the hex2bin function +#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') + +// used in the bin2hex function +char hex_upper[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +char hex_lower[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + +////////////////////////////////////////// +//backend read event triggered by EPOLLIN +////////////////////////////////////////// + +int gw_read_backend_event(DCB *dcb, int epfd) { + int n; + MySQLProtocol *client_protocol = NULL; + + if (dcb) + if(dcb->session) + client_protocol = SESSION_PROTOCOL(dcb->session, MySQLProtocol); + +#ifdef GW_DEBUG_READ_EVENT + fprintf(stderr, "Backend ready! Read from Backend %i, write to client %i, client state %i\n", dcb->fd, dcb->session->client->fd, client_protocol->state); +#endif + + if ((client_protocol->state == MYSQL_WAITING_RESULT) || (client_protocol->state == MYSQL_IDLE)) { + struct epoll_event new_event; + int w; + int count_reads = 0; + int count_writes = 0; + uint8_t buffer[MAX_BUFFER_SIZE]; + int b = -1; + int tot_b = -1; + uint8_t *ptr_buffer; + + if (ioctl(dcb->fd, FIONREAD, &b)) { + fprintf(stderr, "Backend Ioctl FIONREAD error %i, %s\n", errno , strerror(errno)); + } else { + fprintf(stderr, "Backend IOCTL FIONREAD bytes to read = %i\n", b); + } + + // detect pending data in the buffer for client write + if (dcb->session->client->buff_bytes > 0) { + + fprintf(stderr, "*********** Read backend says there are pending writes for %i bytes\n", dcb->session->client->buff_bytes); + // read data from socket, assume no error here (quick) and put it into the DCB second buffer + GW_NOINTR_CALL(n = read(dcb->fd, buffer, b < MAX_BUFFER_SIZE ? b : MAX_BUFFER_SIZE); count_reads++); + + memcpy(&dcb->session->client->second_buff_bytes, &n, sizeof(int)); + fprintf(stderr, "#### second buff_bytes set to %i\n", dcb->session->client->second_buff_bytes); + + memcpy(dcb->session->client->second_buffer, buffer, n); + fprintf(stderr, "#### filled memory second buffer!\n"); + dcb->session->client->second_buffer_ptr = dcb->session->client->second_buffer; + + return 1; + } + + // else, no pending data + tot_b = b; + + // read all the data, without using multiple buffers, only one + while (b > 0) { + + GW_NOINTR_CALL(n = read(dcb->fd, buffer, b < MAX_BUFFER_SIZE ? b : MAX_BUFFER_SIZE); count_reads++); + + fprintf (stderr, "Read %i/%i bytes done, n. reads = %i, %i. Next could be write\n", n, b, count_reads, errno); + + if (n < 0) { + if ((errno != EAGAIN) || (errno != EWOULDBLOCK)) + return 0; + else + return 1; + } else { + b = b - n; + } + } + + ptr_buffer = buffer; + + + if(n >2) + fprintf(stderr, ">>> The READ BUFFER last 2 byte [%i][%i]\n", buffer[n-2], buffer[n-1]); + + + // write all the data + // if write fails for EAGAIN or EWOULDBLOCK, copy the data into the dcb buffer + while (n >0) { + GW_NOINTR_CALL(w = write(dcb->session->client->fd, ptr_buffer, n); count_writes++); + + fprintf (stderr, "Write Cycle %i, %i of %i bytes done, errno %i\n", count_writes, w, n, errno); + if (w > 2) + fprintf(stderr, "<<< writed BUFFER last 2 byte [%i][%i]\n", ptr_buffer[w-2], ptr_buffer[w-1]); + + if (w < 0) { + if ((errno != EAGAIN) || (errno != EWOULDBLOCK)) { + break; + } else { + fprintf(stderr, "<<<< Write to client not completed for %i bytes!\n", n); + if (dcb->session->client) { + fprintf(stderr, "<<<< Try to set buff_bytes!\n"); + memcpy(&dcb->session->client->buff_bytes, &n, sizeof(int)); + fprintf(stderr, "<<<< buff_bytes set to %i\n", dcb->session->client->buff_bytes); + + fprintf(stderr, "<<<< Try to fill memory buffer!\n"); + memcpy(dcb->session->client->buffer, ptr_buffer, n); + dcb->session->client->buffer_ptr = dcb->session->client->buffer; + + fprintf(stderr, "<<<< Buffer Write to client has %i bytes\n", dcb->session->client->buff_bytes); + if (n > 1) { + fprintf(stderr, "<<<< Buffer bytes last 2 [%i]\n", ptr_buffer[0]); + fprintf(stderr, "<<<< Buffer bytes last [%i]\n", ptr_buffer[1]); + } + } else { + fprintf(stderr, "<<< do data in memory for Buffer Write to client\n"); + } + return 0; + } + } else { + n = n - w; + ptr_buffer = ptr_buffer + w; + fprintf(stderr, "<<< Write: pointer to buffer %i bytes shifted\n", w); + + memset(&dcb->session->client->buff_bytes, '\0', sizeof(int)); + } + } + return 1; + } +#ifdef GW_DEBUG_READ_EVENT + fprintf(stderr, "The backend says that Client Protocol state is %i\n", client_protocol->state); +#endif + + return 1; +} + +////////////////////////////////////////// +//backend write event triggered by EPOLLOUT +////////////////////////////////////////// + +int gw_write_backend_event(DCB *dcb, int epfd) { + + fprintf(stderr, ">>> gw_write_backend_event for %i\n", dcb->fd); + + return 0; +} + +////////////////////////////////////////// +//client read event triggered by EPOLLIN +////////////////////////////////////////// +int gw_route_read_event(DCB* dcb, int epfd) { + MySQLProtocol *protocol = DCB_PROTOCOL(dcb, MySQLProtocol); + uint8_t buffer[MAX_BUFFER_SIZE] = ""; + int n; + int b = -1; + + + if (ioctl(dcb->fd, FIONREAD, &b)) { + fprintf(stderr, "Client Ioctl FIONREAD error %i, %s\n", errno , strerror(errno)); + } else { + fprintf(stderr, "Client IOCTL FIONREAD bytes to read = %i\n", b); + } + +//#ifdef GW_DEBUG_READ_EVENT + fprintf(stderr, "Client DCB [%s], EPOLLIN Protocol state [%i] for socket %i, scramble [%s]\n", gw_dcb_state2string(dcb->state), protocol->state, dcb->fd, protocol->scramble); +//#endif + switch (protocol->state) { + case MYSQL_AUTH_SENT: + // read client auth + n = read(dcb->fd, buffer, MAX_BUFFER_SIZE); + + fprintf(stderr, "Client DCB [%s], EPOLLIN Protocol state [%i] for socket %i, bytes read %i\n", gw_dcb_state2string(dcb->state), protocol->state, dcb->fd, n); + + if (n < 0) { + if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { + fprintf(stderr, "Client connection %i: continue for %i, %s\n", dcb->fd, errno, strerror(errno)); + break; + } else { + + fprintf(stderr, "Client connection %i error: %i, %s\n", dcb->fd, errno, strerror(errno));; + + if (dcb->session->backends) { + (dcb->session->backends->func).error(dcb->session->backends, -1); + } + (dcb->func).error(dcb, -1); + + break; + } + } + + if (n == 0) { + // EOF + fprintf(stderr, "Client connection %i closed: %i, %s\n", dcb->fd, errno, strerror(errno)); + + if (dcb->session->backends) { + (dcb->session->backends->func).error(dcb->session->backends, -1); + } + (dcb->func).error(dcb, -1); + + return 1; + } + + // handle possible errors: + // 0 connection close + // -1, error: not EAGAIN or EWOULDBLOCK + + protocol->state = MYSQL_AUTH_RECV; + + +#ifdef GW_DEBUG_READ_EVENT + fprintf(stderr, "DCB [%i], EPOLLIN Protocol next state MYSQL_AUTH_RECV [%i], Packet #%i for socket %i, scramble [%s]\n", dcb->state, dcb->proto_state, 1, dcb->fd, protocol->scramble); +#endif + + // check authentication + // if OK return mysql_ok + // else return error + //protocol->state = MYSQL_AUTH_FAILED; + + break; + + case MYSQL_IDLE: + case MYSQL_WAITING_RESULT: + n = read(dcb->fd, buffer, MAX_BUFFER_SIZE); + if (n < 0) { + if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { + fprintf(stderr, "WAITING RESULT connection %i: continue for %i, %s\n", dcb->fd, errno, strerror(errno)); + break; + } else { + + fprintf(stderr, "connection %i error: %i, %s\n", dcb->fd, errno, strerror(errno));; + + (dcb->session->backends->func).error(dcb->session->backends, -1); + (dcb->func).error(dcb, -1); + + return 1; + } + } + + if (n == 0) { + fprintf(stderr, "connection %i closed: %i, %s\n", dcb->fd, errno, strerror(errno)); + if (dcb->session->backends) { + (dcb->session->backends->func).error(dcb->session->backends, -1); + } + (dcb->func).error(dcb, -1); + + return 1; + } + +#ifdef GW_DEBUG_READ_EVENT + fprintf(stderr, "Client DCB [%s], EPOLLIN Protocol state [%i] for fd %i has read %i bytes\n", gw_dcb_state2string(dcb->state), protocol->state, dcb->fd, n); +#endif + + if (buffer[4] == '\x01') { + fprintf(stderr, "COM_QUIT received\n"); + if (dcb->session->backends) { + write(dcb->session->backends->fd, buffer, n); + (dcb->session->backends->func).error(dcb->session->backends, -1); + } + (dcb->func).error(dcb, -1); + + return 1; + } +#ifdef GW_DEBUG_READ_EVENT + fprintf(stderr, "DCB [%i], EPOLLIN Protocol next state MYSQL_ROUTING [%i], Packet #%i for socket %i, scramble [%s]\n", dcb->state, protocol->state, 1, dcb->fd, protocol->scramble); +#endif + protocol->state = MYSQL_ROUTING; + + write(dcb->session->backends->fd, buffer, n); +#ifdef GW_DEBUG_READ_EVENT + fprintf(stderr, "Client %i, has written to backend %i, btytes %i [%s]\n", dcb->fd, dcb->session->backends->fd, n, buffer); +#endif + protocol->state = MYSQL_WAITING_RESULT; + + break; + + default: + // todo + break; + } + + return 0; +} + +/////////////////////////////////////////////// +// client write event triggered by EPOLLOUT +////////////////////////////////////////////// +int gw_handle_write_event(DCB *dcb, int epfd) { + MySQLProtocol *protocol = NULL; + int n; + struct epoll_event new_event; + n = dcb->buff_bytes; + + + if (dcb == NULL) { + fprintf(stderr, "DCB is NULL, return\n"); + return 1; + } + + fprintf(stderr, "DCB is ok, continue state [%i] is [%s]\n", dcb->state, gw_dcb_state2string(dcb->state)); + + if (dcb->state == DCB_STATE_DISCONNECTED) { + return 1; + } + + fprintf(stderr, "DCB is connected, continue\n"); + + if (dcb->protocol) { + fprintf(stderr, "DCB protocol is OK, continue\n"); + protocol = DCB_PROTOCOL(dcb, MySQLProtocol); + } else { + fprintf(stderr, "DCB protocol is NULL, return\n"); + return 1; + } + + if (dcb->session) { + fprintf(stderr, "DCB session is OK, continue\n"); + } else { + fprintf(stderr, "DCB session is NULL, return\n"); + return 1; + } + + if (dcb->session->backends) { + fprintf(stderr, "DCB backend is OK, continue\n"); + } else { + fprintf(stderr, "DCB backend is NULL, continue\n"); + } + + if (dcb->session->backends) { + fprintf(stderr, "CLIENT WRITE READY State [%i], FIRST bytes left to write %i from back %i to client %i\n", dcb->state, dcb->buff_bytes, dcb->session->backends->fd, dcb->fd); + fprintf(stderr, "CLIENT WRITE READY, SECOND bytes left to write %i from back %i to client %i\n", dcb->second_buff_bytes, dcb->session->backends->fd, dcb->fd); + } + +//#ifdef GW_DEBUG_WRITE_EVENT + fprintf(stderr, "$$$$$ DCB [%i], EPOLLOUT Protocol state is [%i], Packet #%i for socket %i, scramble [%s]\n", dcb->state, protocol->state, 1, dcb->fd, protocol->scramble); +//#endif + + if(protocol->state == MYSQL_AUTH_RECV) { + + //write to client mysql AUTH_OK packet, packet n. is 2 + mysql_send_ok(dcb->fd, 2, 0, NULL); + + protocol->state = MYSQL_IDLE; + +//#ifdef GW_DEBUG_WRITE_EVENT + fprintf(stderr, "DCB [%i], EPOLLIN Protocol next state MYSQL_IDLE [%i], Packet #%i for socket %i, scramble [%s]\n", dcb->state, protocol->state, 2, dcb->fd, protocol->scramble); +//#endif + + return 0; + } + + if (protocol->state == MYSQL_AUTH_FAILED) { + // still to implement + mysql_send_ok(dcb->fd, 2, 0, NULL); + + return 0; + } + + if ((protocol->state == MYSQL_IDLE) || (protocol->state == MYSQL_WAITING_RESULT)) { + int w; + int m; + + if (dcb->buff_bytes > 0) { + fprintf(stderr, "<<< Writing unsent data for state [%i], bytes %i\n", protocol->state, dcb->buff_bytes); + + if (dcb->buff_bytes > 2) + fprintf(stderr, "READ BUFFER last 2 byte [%i%i]\n", dcb->buffer[dcb->buff_bytes-2], dcb->buffer[dcb->buff_bytes-1]); + + w = write(dcb->fd, dcb->buffer_ptr, dcb->buff_bytes); + + if (w < 0) { + if ((w != EAGAIN) || (w!= EWOULDBLOCK)) { + return 1; + } else + return 0; + } + fprintf(stderr, "<<<<< Written %i bytes, left %i\n", w, dcb->buff_bytes - w); + n = n-w; + memcpy(&dcb->buff_bytes, &n, sizeof(int)); + + dcb->buffer_ptr = dcb->buffer_ptr + w; + + fprintf(stderr, "<<<<< Next time write %i bytes, buffer_ptr is %i bytes shifted\n", n, w); + } else { + fprintf(stderr, "<<<< Nothing to do with FIRST buffer left bytes\n"); + } + m = dcb->second_buff_bytes; + + if (dcb->second_buff_bytes) { + fprintf(stderr, "<<<< Now use the SECOND buffer left %i bytes\n", m); + w = write(dcb->fd, dcb->second_buffer_ptr, dcb->second_buff_bytes); + if (w < 0) { + if ((w != EAGAIN) || (w!= EWOULDBLOCK)) { + return 1; + } else + return 0; + } + fprintf(stderr, "<<<<< second Written %i bytes, left %i\n", w, dcb->second_buff_bytes - w); + m = m-w; + memcpy(&dcb->second_buff_bytes, &m, sizeof(int)); + + dcb->second_buffer_ptr = dcb->second_buffer_ptr + w; + } + + fprintf(stderr, "<<<< Nothing to do with SECOND buffer left bytes\n"); + + return 1; + } + +//#ifdef GW_DEBUG_WRITE_EVENT + fprintf(stderr, "$$$$$ DCB [%i], EPOLLOUT Protocol state is [%i] did nothing !!!!\n", dcb->state, protocol->state); +//#endif + + return 1; +} + +/// +// set listener for mysql protocol +/// +void MySQLListener(int epfd, char *config_bind) { + DCB *listener; + int l_so; + int fl; + struct sockaddr_in serv_addr; + struct sockaddr_in local; + socklen_t addrlen; + char *bind_address_and_port = NULL; + char *p; + char address[1024]=""; + int port=0; + int one; + struct epoll_event ev; + + // this gateway, as default, will bind on port 4404 for localhost only + (config_bind != NULL) ? (bind_address_and_port = config_bind) : (bind_address_and_port = "127.0.0.1:4404"); + + listener = (DCB *) calloc(1, sizeof(DCB)); + + listener->state = DCB_STATE_ALLOC; + listener->fd = -1; + + memset(&serv_addr, 0, sizeof serv_addr); + serv_addr.sin_family = AF_INET; + + p = strchr(bind_address_and_port, ':'); + if (p) { + strncpy(address, bind_address_and_port, sizeof(address)); + address[sizeof(address)-1] = '\0'; + p = strchr(address, ':'); + *p = '\0'; + port = atoi(p+1); + setipaddress(&serv_addr.sin_addr, address); + + snprintf(address, (sizeof(address) - 1), "%s", inet_ntoa(serv_addr.sin_addr)); + } else { + port = atoi(bind_address_and_port); + serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); + sprintf(address, "0.0.0.0"); + } + + serv_addr.sin_port = htons(port); + + // socket create + if ((l_so = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + error("can't open listening socket"); + } + + // socket options + setsockopt(l_so, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)); + + // set NONBLOCKING mode + setnonblocking(l_so); + + // bind address and port + if (bind(l_so, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + fprintf(stderr, ">>>> Bind failed !!! %i, [%s]\n", errno, strerror(errno)); + error("can't bind to address and port"); + exit(1); + } + + fprintf(stderr, ">> GATEWAY bind is: %s:%i. FD is %i\n", address, port, l_so); + + listen(l_so, 10 * SOMAXCONN); + + fprintf(stderr, ">> GATEWAY listen backlog queue is %i\n", 10 * SOMAXCONN); + + listener->state = DCB_STATE_IDLE; + + // assign l_so to dcb + listener->fd = l_so; + + // register events, don't add EPOLLET for now + ev.events = EPOLLIN; + + // set user data to dcb struct + ev.data.ptr = listener; + + // add listening socket to epoll structure + if (epoll_ctl(epfd, EPOLL_CTL_ADD, l_so, &ev) == -1) { + perror("epoll_ctl: listen_sock"); + exit(EXIT_FAILURE); + } + + listener->func.accept = MySQLAccept; + + listener->state = DCB_STATE_LISTENING; +} + + +int MySQLAccept(DCB *listener, int efd) { + int accept_counter = 0; + + fprintf(stderr, "MySQL Listener socket is: %i\n", listener->fd); + + while (1) { + int c_sock; + struct sockaddr_in local; + socklen_t addrlen; + addrlen = sizeof(local); + struct epoll_event ee; + DCB *client; + DCB *backend; + SESSION *session; + MySQLProtocol *protocol; + MySQLProtocol *ptr_proto; + int sendbuf = GW_BACKEND_SO_SNDBUF; + socklen_t optlen = sizeof(sendbuf); + + // new connection from client + c_sock = accept(listener->fd, (struct sockaddr *) &local, &addrlen); + + if (c_sock == -1) { + if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { + fprintf(stderr, ">>>> NO MORE conns for MySQL Listener: errno is %i for %i\n", errno, listener->fd); + /* We have processed all incoming connections. */ + break; + } else { + fprintf(stderr, "Accept error for %i, Err: %i, %s\n", listener->fd, errno, strerror(errno)); + // what else to do? + return 1; + } + } + + accept_counter++; + + fprintf(stderr, "Processing %i connection fd %i for listener %i\n", accept_counter, c_sock, listener->fd); + // set nonblocking + + setsockopt(c_sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, optlen); + setnonblocking(c_sock); + + client = (DCB *) calloc(1, sizeof(DCB)); + backend = (DCB *) calloc(1, sizeof(DCB)); + session = (SESSION *) calloc(1, sizeof(SESSION)); + protocol = (MySQLProtocol *) calloc(1, sizeof(MySQLProtocol)); + + client->fd = c_sock; + client->state = DCB_STATE_ALLOC; + client->session = session; + client->protocol = (void *)protocol; + + session->state = SESSION_STATE_ALLOC; + session->client = client; + session->backends = NULL; + + protocol->state = MYSQL_ALLOC; + protocol->descriptor = client; + protocol->fd = c_sock; + + backend->state = DCB_STATE_ALLOC; + backend->session = NULL; + backend->protocol = (MySQLProtocol *)gw_mysql_init(NULL); + + ptr_proto = (MySQLProtocol *)backend->protocol; + + // sha1(password) from client non yet handled + // this is blocking until auth done + if (gw_mysql_connect("127.0.0.1", 3306, "test", "massi", "massi", backend->protocol, 0) == 0) { + fprintf(stderr, "Connected to backend mysql server\n"); + backend->fd = ptr_proto->fd; + setnonblocking(backend->fd); + } else { + backend->fd = -1; + } + + // edge triggering flag added + ee.events = EPOLLIN | EPOLLET | EPOLLOUT; + ee.data.ptr = backend; + + // if connected, add it to the epoll + if (backend->fd > 0) { + if (epoll_ctl(efd, EPOLL_CTL_ADD, backend->fd, &ee) == -1) { + perror("epoll_ctl: backend sock"); + } else { + //fprintf(stderr, "--> Backend conn added, bk_fd [%i], scramble [%s], is session with client_fd [%i]\n", ptr_proto->fd, ptr_proto->scramble, client->fd); + backend->state = DCB_STATE_POLLING; + backend->session = session; + (backend->func).read = gw_read_backend_event; + (backend->func).write = gw_write_backend_event; + (backend->func).error = handle_event_errors_backend; + + // assume here one backend only. + // in session.h + // struct dcb *backends; + // instead of a list **backends; + session->backends = backend; + } + } + + // assign function poiters to "func" field + (client->func).error = handle_event_errors; + (client->func).read = gw_route_read_event; + (client->func).write = gw_handle_write_event; + + // edge triggering flag added + ee.events = EPOLLIN | EPOLLOUT | EPOLLET; + ee.data.ptr = client; + + client->state = DCB_STATE_IDLE; + + // event install + if (epoll_ctl(efd, EPOLL_CTL_ADD, c_sock, &ee) == -1) { + perror("epoll_ctl: conn_sock"); + exit(EXIT_FAILURE); + } else { + //fprintf(stderr, "Added fd %i to epoll, protocol state [%i]\n", c_sock , client->state); + client->state = DCB_STATE_POLLING; + } + + client->state = DCB_STATE_PROCESSING; + + //send handshake to the client + MySQLSendHandshake(client); + + // client protocol state change + protocol->state = MYSQL_AUTH_SENT; + } + + return 0; +} + + +int setnonblocking(int fd) { + int fl; + + if ((fl = fcntl(fd, F_GETFL, 0)) == -1) { + fprintf(stderr, "Can't GET fcntli for %i, errno = %d, %s", fd, errno, strerror(errno)); + return 1; + } + + if (fcntl(fd, F_SETFL, fl | O_NONBLOCK) == -1) { + fprintf(stderr, "Can't SET fcntl for %i, errno = %d, %s", fd, errno, strerror(errno)); + return 1; + } + + return 0; +} + +char *gw_strend(register const char *s) { + while (*s++); + return (char*) (s-1); +} + +/////////////////////////////// +// generate a random char +////////////////////////////// +static char gw_randomchar() { + return (char)((rand() % 78) + 30); +} + +///////////////////////////////// +// generate a random string +// output must be pre allocated +///////////////////////////////// +int gw_generate_random_str(char *output, int len) { + + int i; + srand(time()); + + for ( i = 0; i < len; ++i ) { + output[i] = gw_randomchar(); + } + + output[len]='\0'; + + return 0; +} + +///////////////////////////////// +// hex string to binary data +// output must be pre allocated +///////////////////////////////// +int gw_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; +} + +///////////////////////////////// +// binary data to hex string +// output must be pre allocated +///////////////////////////////// +char *gw_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; +} + +/////////////////////////////////////////////////////// +// 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 gw_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'; +} + +///////////////////////////////////////////////////////////// +// fill a 20 bytes preallocated with SHA1 digest (160 bits) +// for one input on in_len bytes +///////////////////////////////////////////////////////////// +void gw_sha1_str(const uint8_t *in, int in_len, uint8_t *out) { + unsigned char hash[SHA_DIGEST_LENGTH]; + + SHA1(in, in_len, hash); + memcpy(out, hash, SHA_DIGEST_LENGTH); +} + +///////////////////////////////////////////////////////////// +// fill 20 bytes preallocated with SHA1 digest (160 bits) +// for two inputs, in_len and in2_len bytes +///////////////////////////////////////////////////////////// +void gw_sha1_2_str(const uint8_t *in, int in_len, const uint8_t *in2, int in2_len, uint8_t *out) { + SHA_CTX context; + unsigned char hash[SHA_DIGEST_LENGTH]; + + SHA1_Init(&context); + SHA1_Update(&context, in, in_len); + SHA1_Update(&context, in2, in2_len); + SHA1_Final(hash, &context); + + memcpy(out, hash, SHA_DIGEST_LENGTH); +}