diff --git a/Makefile b/Makefile index d89724d45e..e7dae05864 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ DEBUG=${ALL_DEBUG} #export OPTZ #export EXTRALINK export MAKE -export CURVER?=2.4.6 +export CURVER?=2.5.0 ifneq (,$(wildcard /etc/os-release)) DISTRO := $(shell gawk -F= '/^NAME/{print $$2}' /etc/os-release) else diff --git a/deps/libhttpserver/empty_uri_log_crash.patch b/deps/libhttpserver/empty_uri_log_crash.patch new file mode 100644 index 0000000000..3970a7f885 --- /dev/null +++ b/deps/libhttpserver/empty_uri_log_crash.patch @@ -0,0 +1,13 @@ +diff --git src/webserver.cpp src/webserver.cpp +index 5ae7381..04a5a28 100644 +--- src/webserver.cpp ++++ src/webserver.cpp +@@ -443,7 +443,7 @@ int policy_callback (void *cls, const struct sockaddr* addr, socklen_t addrlen) + void* uri_log(void* cls, const char* uri) + { + struct details::modded_request* mr = new details::modded_request(); +- mr->complete_uri = new string(uri); ++ mr->complete_uri = new string(uri == NULL ? "" : uri); + mr->second = false; + return ((void*)mr); + } diff --git a/deps/mariadb-client-library/UPGRADE_NOTES.md b/deps/mariadb-client-library/UPGRADE_NOTES.md new file mode 100644 index 0000000000..32e1a7f0c5 --- /dev/null +++ b/deps/mariadb-client-library/UPGRADE_NOTES.md @@ -0,0 +1,4 @@ +Please note that upgrading `mariadb-connector-c` can require some changes +in `include/MySQL_Data_Stream.h` where we define `P_MARIADB_TLS` as a copy +of `MARIADB_TLS` . If `MARIADB_TLS` is changed, `P_MARIADB_TLS` must be +updated too. diff --git a/deps/mariadb-client-library/mariadb-connector-c-2.3.1-src.tar.gz b/deps/mariadb-client-library/mariadb-connector-c-2.3.1-src.tar.gz deleted file mode 100644 index 5d68f7402c..0000000000 Binary files a/deps/mariadb-client-library/mariadb-connector-c-2.3.1-src.tar.gz and /dev/null differ diff --git a/deps/mariadb-client-library/mariadb-connector-c-3.0.2-src.tar.gz b/deps/mariadb-client-library/mariadb-connector-c-3.0.2-src.tar.gz deleted file mode 100644 index de98af6d29..0000000000 Binary files a/deps/mariadb-client-library/mariadb-connector-c-3.0.2-src.tar.gz and /dev/null differ diff --git a/deps/mariadb-client-library/mariadb-connector-c-3.0.9-src.tar.gz b/deps/mariadb-client-library/mariadb-connector-c-3.0.9-src.tar.gz deleted file mode 100644 index d832031c91..0000000000 Binary files a/deps/mariadb-client-library/mariadb-connector-c-3.0.9-src.tar.gz and /dev/null differ diff --git a/deps/mariadb-client-library/mariadb-connector-c-3.1.4-src.tar.gz b/deps/mariadb-client-library/mariadb-connector-c-3.1.4-src.tar.gz deleted file mode 100644 index e24be31d19..0000000000 Binary files a/deps/mariadb-client-library/mariadb-connector-c-3.1.4-src.tar.gz and /dev/null differ diff --git a/include/MySQL_Data_Stream.h b/include/MySQL_Data_Stream.h index 0c71582b23..8cedfdea1d 100644 --- a/include/MySQL_Data_Stream.h +++ b/include/MySQL_Data_Stream.h @@ -6,6 +6,21 @@ #include "MySQL_Protocol.h" +#ifndef uchar +typedef unsigned char uchar; +#endif + +#include "ma_pvio.h" +// here we define P_MARIADB_TLS as a copy of MARIADB_TLS +// copied from ma_tls.h +// note that ma_pvio.h defines it as void +typedef struct P_st_ma_pvio_tls { + void *data; + MARIADB_PVIO *pvio; + void *ssl; +} P_MARIADB_TLS; + + #define QUEUE_T_DEFAULT_SIZE 32768 #define MY_SSL_BUFFER 8192 @@ -35,8 +50,12 @@ class MyDS_real_query { */ pkt.ptr=_pkt->ptr; pkt.size=_pkt->size; - QueryPtr=(char *)pkt.ptr+5; QuerySize=pkt.size-5; + if (QuerySize == 0) { + QueryPtr=const_cast(""); + } else { + QueryPtr=(char *)pkt.ptr+5; + } } void end() { l_free(pkt.size,pkt.ptr); @@ -192,6 +211,33 @@ class MySQL_Data_Stream myconn=mc; myconn->statuses.myconnpoll_get++; mc->myds=this; + encrypted = false; // this is the default + // we handle encryption for backend + // + // we have a similar code in MySQL_Connection + // in case of ASYNC_CONNECT_SUCCESSFUL + if (sess != NULL && sess->session_fast_forward == true) { + // if frontend and backend connection use SSL we will set + // encrypted = true and we will start using the SSL structure + // directly from P_MARIADB_TLS structure. + // + // For futher details: + // - without ssl: we use the file descriptor from mysql connection + // - with ssl: we use the SSL structure from mysql connection + if (myconn->mysql && myconn->ret_mysql) { + if (myconn->mysql->options.use_ssl == 1) { + encrypted = true; + if (ssl == NULL) { + // check the definition of P_MARIADB_TLS + P_MARIADB_TLS * matls = (P_MARIADB_TLS *)myconn->mysql->net.pvio->ctls; + ssl = (SSL *)matls->ssl; + rbio_ssl = BIO_new(BIO_s_mem()); + wbio_ssl = BIO_new(BIO_s_mem()); + SSL_set_bio(ssl, rbio_ssl, wbio_ssl); + } + } + } + } } // safe way to detach a MySQL Connection @@ -201,6 +247,15 @@ class MySQL_Data_Stream statuses.myconnpoll_put++; myconn->myds=NULL; myconn=NULL; + if (encrypted == true) { + if (sess != NULL && sess->session_fast_forward == true) { + // it seems we are a connection with SSL on a fast_forward session. + // See attach_connection() for more details . + // We now disable SSL metadata from the Data Stream + encrypted = false; + ssl = NULL; + } + } } void return_MySQL_Connection_To_Pool(); diff --git a/lib/ClickHouse_Server.cpp b/lib/ClickHouse_Server.cpp index 94cecdeb75..ef3b2ec0e4 100644 --- a/lib/ClickHouse_Server.cpp +++ b/lib/ClickHouse_Server.cpp @@ -567,6 +567,8 @@ void ClickHouse_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t query_no_space[query_no_space_length]=0; } + proxy_debug(PROXY_DEBUG_SQLITE, 4, "Received query on Session %p , thread_session_id %u : %s\n", sess, sess->thread_session_id, query_no_space); + if (sess->session_type == PROXYSQL_SESSION_CLICKHOUSE) { if (!strncasecmp("SET ", query_no_space, 4)) { @@ -1365,9 +1367,24 @@ static void *child_mysql(void *arg) { } } myds->revents=fds[0].revents; - myds->read_from_net(); + int rb = 0; + rb - myds->read_from_net(); if (myds->net_failure) goto __exit_child_mysql; myds->read_pkts(); + if (myds->encrypted == true) { + // PMC-10004 + // we probably should use SSL_pending() and/or SSL_has_pending() to determine + // if there is more data to be read, but it doesn't seem to be working. + // Therefore we try to call read_from_net() again as long as there is data. + // Previously we hardcoded 16KB but it seems that it can return in smaller + // chunks of 4KB. + // We finally removed the chunk size as it seems that any size is possible. + while (rb > 0) { + rb = myds->read_from_net(); + if (myds->net_failure) goto __exit_child_mysql; + myds->read_pkts(); + } + } sess->to_process=1; int rc=sess->handler(); if (rc==-1) goto __exit_child_mysql; diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index b32fa8852f..c517553aae 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -3554,7 +3554,11 @@ void MySQL_HostGroups_Manager::replication_lag_action_inner(MyHGC *myhgc, char * if ( // (current_replication_lag==-1 ) // || - (current_replication_lag>=0 && ((unsigned int)current_replication_lag > mysrvc->max_replication_lag)) + ( + current_replication_lag>=0 && + mysrvc->max_replication_lag > 0 && // see issue #4018 + ((unsigned int)current_replication_lag > mysrvc->max_replication_lag) + ) ) { // always increase the counter mysrvc->cur_replication_lag_count += 1; diff --git a/lib/MySQL_Monitor.cpp b/lib/MySQL_Monitor.cpp index 5bb98446f2..32acbf9d98 100644 --- a/lib/MySQL_Monitor.cpp +++ b/lib/MySQL_Monitor.cpp @@ -317,14 +317,16 @@ MYSQL * MySQL_Monitor_Connection_Pool::get_connection(char *hostname, int port, break; } #ifdef DEBUG - for (unsigned int j=0; jlen; j++) { - MYSQL *my1 = (MYSQL *)conns->index(j); - assert(my!=my1); - assert(my->net.fd!=my1->net.fd); - } - //proxy_info("Registering MYSQL with FD %d from mmsd %p and MYSQL %p\n", my->net.fd, mmsd, my); - if (my) + if (my) { + for (unsigned int j=0; jlen; j++) { + MYSQL *my1 = (MYSQL *)conns->index(j); + assert(my!=my1); + assert(my->net.fd!=my1->net.fd); + } + //proxy_info("Registering MYSQL with FD %d from mmsd %p and MYSQL %p\n", my->net.fd, mmsd, my); + conns->add(my); + } #endif // DEBUG } #ifdef DEBUG diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index 642a2979e8..756c28f970 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -1947,7 +1947,10 @@ bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned #endif (*myds)->sess->schema_locked=schema_locked; (*myds)->sess->transaction_persistent=transaction_persistent; - (*myds)->sess->session_fast_forward=fast_forward; + (*myds)->sess->session_fast_forward=false; // default + if ((*myds)->sess->session_type == PROXYSQL_SESSION_MYSQL) { + (*myds)->sess->session_fast_forward=fast_forward; + } (*myds)->sess->user_max_connections=max_connections; } if (password == NULL) { diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 93da82c92b..4a9a7a24aa 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -871,6 +871,7 @@ void MySQL_Session::writeout() { } bool MySQL_Session::handler_CommitRollback(PtrSize_t *pkt) { + if (pkt->size <= 5) { return false; } char c=((char *)pkt->ptr)[5]; bool ret=false; if (c=='c' || c=='C') { @@ -5398,7 +5399,7 @@ void MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE( (strcmp(client_addr,(char *)"::1")==0) ) { // we are good! - client_myds->myprot.generate_pkt_OK(true,NULL,NULL, _pid, 0,0,0,0,NULL); + client_myds->myprot.generate_pkt_OK(true, NULL, NULL, _pid, 0, 0, 2, 0, NULL); handshake_err = false; GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_OK, this, NULL); status=WAITING_CLIENT_DATA; @@ -5444,7 +5445,7 @@ void MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE( //client_myds->myprot.generate_pkt_OK(true,NULL,NULL, (is_encrypted ? 3 : 2), 0,0,0,0,NULL,false); proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION,8,"Session=%p , DS=%p . STATE_CLIENT_AUTH_OK\n", this, client_myds); GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_OK, this, NULL); - client_myds->myprot.generate_pkt_OK(true,NULL,NULL, _pid, 0,0,0,0,NULL); + client_myds->myprot.generate_pkt_OK(true, NULL, NULL, _pid, 0, 0, 2, 0, NULL); handshake_err = false; status=WAITING_CLIENT_DATA; client_myds->DSS=STATE_CLIENT_AUTH_OK; @@ -6711,7 +6712,7 @@ void MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C client_authenticated=false; if (client_myds->myprot.process_pkt_COM_CHANGE_USER((unsigned char *)pkt->ptr, pkt->size)==true) { l_free(pkt->size,pkt->ptr); - client_myds->myprot.generate_pkt_OK(true,NULL,NULL,1,0,0,0,0,NULL); + client_myds->myprot.generate_pkt_OK(true, NULL, NULL, 1, 0, 0, 2, 0, NULL); client_myds->DSS=STATE_SLEEP; status=WAITING_CLIENT_DATA; *wrong_pass=false; @@ -6801,7 +6802,7 @@ void MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C l_free(pkt->size,pkt->ptr); client_myds->setDSS_STATE_QUERY_SENT_NET(); - client_myds->myprot.generate_pkt_OK(true,NULL,NULL,1,0,0,0,0,NULL); + client_myds->myprot.generate_pkt_OK(true, NULL, NULL, 1, 0, 0, 2, 0, NULL); client_myds->DSS=STATE_SLEEP; status=WAITING_CLIENT_DATA; } else { diff --git a/lib/MySQL_Thread.cpp b/lib/MySQL_Thread.cpp index 717e78eb53..64b6f592d0 100644 --- a/lib/MySQL_Thread.cpp +++ b/lib/MySQL_Thread.cpp @@ -21,6 +21,8 @@ #include "MySQL_PreparedStatement.h" #include "MySQL_Logger.hpp" +#include + using std::vector; using std::function; @@ -2834,6 +2836,20 @@ MySQL_Session * MySQL_Thread::create_new_session_and_client_data_stream(int _fd) register_session(sess); // register session sess->client_myds = new MySQL_Data_Stream(); sess->client_myds->fd=_fd; + + // set not blocking for client connections too! + { + // PMC-10004 + // While implementing SSL and fast_forward it was noticed that all frontend connections + // are in blocking, although this was never a problem because we call poll() before reading. + // Although it became a problem with fast_forward, SSL and large packets because SSL handled + // data in chunks of 16KB and there may be data inside SSL even when there is no data + // received from the network. + // The only modules that seems to be affected by this issue are Admin, SQLite3 Server + // and Clickhouse Server + int nb = fcntl(_fd, F_SETFL, fcntl(_fd, F_GETFL, 0) | O_NONBLOCK); + assert (nb != -1); + } setsockopt(sess->client_myds->fd, IPPROTO_TCP, TCP_NODELAY, (char *) &arg_on, sizeof(arg_on)); if (mysql_thread___use_tcp_keepalive) { @@ -3578,17 +3594,42 @@ bool MySQL_Thread::process_data_on_data_stream(MySQL_Data_Stream *myds, unsigned if (rb > 0 && myds->myds_type == MYDS_BACKEND) { if (myds->sess->session_fast_forward) { - struct pollfd _fds; - nfds_t _nfds = 1; - _fds.fd = mypolls.fds[n].fd; - _fds.events = POLLIN; - _fds.revents = 0; - int _rc = poll(&_fds, _nfds, 0); - if ((_rc > 0) && _fds.revents == POLLIN) { - // there is more data - myds->revents = _fds.revents; - } else { + if (myds->encrypted == true) { // we are in fast_forward mode and encrypted == true + // PMC-10004 + // we probably should use SSL_pending() and/or SSL_has_pending() to determine + // if there is more data to be read, but it doesn't seem to be working. + // Therefore we try to call read_from_net() again as long as there is data. + // Previously we hardcoded 16KB but it seems that it can return in smaller + // chunks of 4KB. + // We finally removed the chunk size as it seems that any size is possible. +/* + int sslp = SSL_pending(myds->ssl); + int sslhp = SSL_has_pending(myds->ssl); + proxy_debug(PROXY_DEBUG_NET, 5, "Session=%p: in fast_forward mode and SSL read %d bytes , SSL_pending: %d bytes , SSL_has_pending: %d\n", myds->sess, rb, sslp, sslhp); +*/ + proxy_debug(PROXY_DEBUG_NET, 5, "Session=%p, DataStream=%p , thread_session_id=%u -- in fast_forward mode and SSL read %d bytes\n", myds->sess, myds, myds->sess->thread_session_id, rb); + while (rb > 0) { + rb = myds->read_from_net(); + if (rb > 0 && myds->myds_type == MYDS_FRONTEND) { + status_variables.stvar[st_var_queries_frontends_bytes_recv] += rb; + } + proxy_debug(PROXY_DEBUG_NET, 5, "Session=%p, DataStream=%p -- in fast_forward mode and SSL read %d bytes\n", myds->sess, myds, rb); + myds->read_pkts(); + } rb = 0; // exit loop + } else { // we are in fast_forward mode and encrypted == false + struct pollfd _fds; + nfds_t _nfds = 1; + _fds.fd = mypolls.fds[n].fd; + _fds.events = POLLIN; + _fds.revents = 0; + int _rc = poll(&_fds, _nfds, 0); + if ((_rc > 0) && _fds.revents == POLLIN) { + // there is more data + myds->revents = _fds.revents; + } else { + rb = 0; // exit loop + } } } else { rb = 0; // exit loop diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index 825ac02124..0df53b2f82 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -5459,9 +5459,24 @@ void *child_mysql(void *arg) { } mysql_thr->curtime = monotonic_time(); myds->revents=fds[0].revents; - myds->read_from_net(); + int rb = 0; + rb = myds->read_from_net(); if (myds->net_failure) goto __exit_child_mysql; myds->read_pkts(); + if (myds->encrypted == true) { + // PMC-10004 + // we probably should use SSL_pending() and/or SSL_has_pending() to determine + // if there is more data to be read, but it doesn't seem to be working. + // Therefore we try to call read_from_net() again as long as there is data. + // Previously we hardcoded 16KB but it seems that it can return in smaller + // chunks of 4KB. + // We finally removed the chunk size as it seems that any size is possible. + while (rb > 0) { + rb = myds->read_from_net(); + if (myds->net_failure) goto __exit_child_mysql; + myds->read_pkts(); + } + } sess->to_process=1; int rc=sess->handler(); if (rc==-1) goto __exit_child_mysql; diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index a02e274be8..acc06ba981 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -22,6 +22,7 @@ void ma_free_root(MA_MEM_ROOT *root, myf MyFLAGS); void *ma_alloc_root(MA_MEM_ROOT *mem_root, size_t Size); #define MAX(a,b) (((a) > (b)) ? (a) : (b)) + void * ma_alloc_root(MA_MEM_ROOT *mem_root, size_t Size) { size_t get_size; @@ -1113,6 +1114,33 @@ MDB_ASYNC_ST MySQL_Connection::handler(short event) { } break; case ASYNC_CONNECT_SUCCESSFUL: + if (mysql && ret_mysql) { + // we handle encryption for backend + // + // we have a similar code in MySQL_Data_Stream::attach_connection() + // see there for further details + if (mysql->options.use_ssl == 1) + if (myds) + if (myds->sess != NULL) + if (myds->sess->session_fast_forward == true) { + assert(myds->ssl==NULL); + if (myds->ssl == NULL) { + // check the definition of P_MARIADB_TLS + P_MARIADB_TLS * matls = (P_MARIADB_TLS *)mysql->net.pvio->ctls; + if (matls != NULL) { + myds->encrypted = true; + myds->ssl = (SSL *)matls->ssl; + myds->rbio_ssl = BIO_new(BIO_s_mem()); + myds->wbio_ssl = BIO_new(BIO_s_mem()); + SSL_set_bio(myds->ssl, myds->rbio_ssl, myds->wbio_ssl); + } else { + // if mysql->options.use_ssl == 1 but matls == NULL + // it means that ProxySQL tried to use SSL to connect to the backend + // but the backend didn't support SSL + } + } + } + } __sync_fetch_and_add(&MyHGM->status.server_connections_connected,1); __sync_fetch_and_add(&parent->connect_OK,1); options.client_flag = mysql->client_flag; @@ -1526,7 +1554,8 @@ MDB_ASYNC_ST MySQL_Connection::handler(short event) { break; case ASYNC_USE_RESULT_CONT: { - if (myds->sess && myds->sess->client_myds && myds->sess->mirror==false) { + if (myds->sess && myds->sess->client_myds && myds->sess->mirror==false && + myds->sess->status != SHOW_WARNINGS) { // see issue#4072 unsigned int buffered_data=0; buffered_data = myds->sess->client_myds->PSarrayOUT->len * RESULTSET_BUFLEN; buffered_data += myds->sess->client_myds->resultset->len * RESULTSET_BUFLEN; diff --git a/lib/mysql_data_stream.cpp b/lib/mysql_data_stream.cpp index 66d3f09142..35f8786888 100644 --- a/lib/mysql_data_stream.cpp +++ b/lib/mysql_data_stream.cpp @@ -118,7 +118,9 @@ static void __dump_pkt(const char *func, unsigned char *_ptr, unsigned int len) } #define queue_zero(_q) { \ - memcpy(_q.buffer, (unsigned char *)_q.buffer + _q.tail, _q.head - _q.tail); \ + if (_q.tail != 0) { \ + memcpy(_q.buffer, (unsigned char *)_q.buffer + _q.tail, _q.head - _q.tail); \ + } \ _q.head-=_q.tail; \ _q.tail=0; \ } @@ -385,15 +387,6 @@ MySQL_Data_Stream::~MySQL_Data_Stream() { SSL_shutdown(ssl); } if (ssl) SSL_free(ssl); -/* - SSL_free() should also take care of these - if (rbio_ssl) { - BIO_free(rbio_ssl); - } - if (wbio_ssl) { - BIO_free(wbio_ssl); - } -*/ } if (multi_pkt.ptr) { l_free(multi_pkt.size,multi_pkt.ptr); @@ -532,7 +525,7 @@ int MySQL_Data_Stream::read_from_net() { r = recv(fd, queue_w_ptr(queueIN), s, 0); } } - } else { + } else { // encrypted == true /* if (!SSL_is_init_finished(ssl)) { int ret = SSL_do_handshake(ssl); @@ -638,11 +631,21 @@ int MySQL_Data_Stream::read_from_net() { } } else { int ssl_ret=SSL_get_error(ssl, r); - if (ssl_ret!=SSL_ERROR_WANT_READ && ssl_ret!=SSL_ERROR_WANT_WRITE) shut_soft(); - if (r==0 && revents==1) { - // revents returns 1 , but recv() returns 0 , so there is no data. - // Therefore the socket is already closed - shut_soft(); + proxy_debug(PROXY_DEBUG_NET, 5, "Session=%p, Datastream=%p -- session_id: %u , SSL_get_error(): %d , errno: %d\n", sess, this, sess->thread_session_id, ssl_ret, errno); + if (ssl_ret == SSL_ERROR_SYSCALL && (errno == EINTR || errno == EAGAIN)) { + // the read was interrupted, do nothing + proxy_debug(PROXY_DEBUG_NET, 5, "Session=%p, Datastream=%p -- SSL_get_error() is SSL_ERROR_SYSCALL, errno: %d\n", sess, this, errno); + } else { + if (r==0) { // we couldn't read any data + if (revents==1) { + // revents returns 1 , but recv() returns 0 , so there is no data. + // Therefore the socket is already closed + proxy_debug(PROXY_DEBUG_NET, 5, "Session=%p, Datastream=%p -- shutdown soft\n", sess, this); + shut_soft(); + } + } + if (ssl_ret!=SSL_ERROR_WANT_READ && ssl_ret!=SSL_ERROR_WANT_WRITE) shut_soft(); + // it seems we end in shut_soft() anyway } } } else { @@ -673,11 +676,13 @@ int MySQL_Data_Stream::write_to_net() { if (encrypted) { bytes_io = SSL_write (ssl, queue_r_ptr(queueOUT), s); //proxy_info("Used SSL_write to write %d bytes\n", bytes_io); + proxy_debug(PROXY_DEBUG_NET, 7, "Session=%p, Datastream=%p: SSL_write() wrote %d bytes . queueOUT before: %u\n", sess, this, bytes_io, queue_data(queueOUT)); if (ssl_write_len || wbio_ssl->num_write > wbio_ssl->num_read) { //proxy_info("ssl_write_len = %d , num_write = %d , num_read = %d\n", ssl_write_len , wbio_ssl->num_write , wbio_ssl->num_read); char buf[MY_SSL_BUFFER]; do { n = BIO_read(wbio_ssl, buf, sizeof(buf)); + proxy_debug(PROXY_DEBUG_NET, 7, "Session=%p, Datastream=%p: BIO_read() read %d bytes\n", sess, this, n); //proxy_info("BIO read = %d\n", n); if (n > 0) { //proxy_info("Setting %d byte in queue encrypted\n", n); @@ -690,8 +695,10 @@ int MySQL_Data_Stream::write_to_net() { } } while (n>0); } + proxy_debug(PROXY_DEBUG_NET, 7, "Session=%p, Datastream=%p: current ssl_write_len is %lu bytes\n", sess, this, ssl_write_len); if (ssl_write_len) { n = write(fd, ssl_write_buf, ssl_write_len); + proxy_debug(PROXY_DEBUG_NET, 7, "Session=%p, Datastream=%p: write() wrote %d bytes in FD %d\n", sess, this, n, fd); //proxy_info("Calling write() on SSL: %d\n", n); if (n>0) { if ((size_t)n < ssl_write_len) { @@ -722,6 +729,7 @@ int MySQL_Data_Stream::write_to_net() { #else bytes_io = send(fd, queue_r_ptr(queueOUT), s, MSG_NOSIGNAL); #endif + proxy_debug(PROXY_DEBUG_NET, 7, "Session=%p, Datastream=%p: send() wrote %d bytes in FD %d\n", sess, this, bytes_io, fd); } if (encrypted) { //proxy_info("bytes_io: %d\n", bytes_io); diff --git a/src/SQLite3_Server.cpp b/src/SQLite3_Server.cpp index 8f92fc02e7..34fd09e727 100644 --- a/src/SQLite3_Server.cpp +++ b/src/SQLite3_Server.cpp @@ -372,6 +372,7 @@ void SQLite3_Server_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *p query_no_space[query_no_space_length]=0; } + proxy_debug(PROXY_DEBUG_SQLITE, 4, "Received query on Session %p , thread_session_id %u : %s\n", sess, sess->thread_session_id, query_no_space); { SQLite3_Session *sqlite_sess = (SQLite3_Session *)sess->thread->gen_args; @@ -935,11 +936,25 @@ static void *child_mysql(void *arg) { // FIXME: CI test test_sqlite3_server-t or test_sqlite3_server_and_fast_routing-t // seems to result in fds->fd = -1 // it needs investigation - myds->read_from_net(); + int rb = 0; + rb = myds->read_from_net(); if (myds->net_failure) goto __exit_child_mysql; myds->read_pkts(); + if (myds->encrypted == true) { + // PMC-10004 + // we probably should use SSL_pending() and/or SSL_has_pending() to determine + // if there is more data to be read, but it doesn't seem to be working. + // Therefore we try to call read_from_net() again as long as there is data. + // Previously we hardcoded 16KB but it seems that it can return in smaller + // chunks of 4KB. + // We finally removed the chunk size as it seems that any size is possible. + while (rb > 0) { + rb = myds->read_from_net(); + if (myds->net_failure) goto __exit_child_mysql; + myds->read_pkts(); + } + } sess->to_process=1; - // Get and set the client address before the sesion is processed. union { struct sockaddr_in in; diff --git a/test/tap/tap/utils.cpp b/test/tap/tap/utils.cpp index c4a26c24ee..ec5be5fad2 100644 --- a/test/tap/tap/utils.cpp +++ b/test/tap/tap/utils.cpp @@ -25,6 +25,18 @@ using std::string; using std::vector; +std::size_t count_matches(const string& str, const string& substr) { + std::size_t result = 0; + std::size_t pos = 0; + + while ((pos = str.find(substr, pos)) != string::npos) { + result += 1; + pos += substr.length(); + } + + return result; +} + int mysql_query_t(MYSQL* mysql, const char* query) { diag("%s: Issuing query '%s' to ('%s':%d)", get_formatted_time().c_str(), query, mysql->host, mysql->port); return mysql_query(mysql, query); diff --git a/test/tap/tap/utils.h b/test/tap/tap/utils.h index 4b286459e2..f9761bf129 100644 --- a/test/tap/tap/utils.h +++ b/test/tap/tap/utils.h @@ -431,4 +431,14 @@ int configure_endpoints( bool prevent_dups = true ); +/** + * @brief Returns the matches found of the 'substr' provided in the provided string. + * + * @param str String from which to count the matches. + * @param substr The substring which matches needs to be counted. + * + * @return Number of matches of the 'substr' in the provided string. + */ +std::size_t count_matches(const std::string& str, const std::string& substr); + #endif // #define UTILS_H diff --git a/test/tap/tests/Makefile b/test/tap/tests/Makefile index 4b5f59151c..c3e52a0394 100644 --- a/test/tap/tests/Makefile +++ b/test/tap/tests/Makefile @@ -85,7 +85,7 @@ all: tests .PHONY: clean clean: - rm -f *-t galera_1_timeout_count galera_2_timeout_no_count aurora generate_set_session_csv set_testing-240.csv clickhouse_php_conn-t || true + rm -f *-t galera_1_timeout_count galera_2_timeout_no_count aurora generate_set_session_csv set_testing-240.csv clickhouse_php_conn-t reg_test_3992_fast_forward_malformed_packet-pymysql-t || true WITHGCOVVAR := $(shell echo $(WITHGCOV)) ifeq ($(WITHGCOVVAR),1) diff --git a/test/tap/tests/reg_test_3992_fast_forward_malformed_packet-pymysql-t.py b/test/tap/tests/reg_test_3992_fast_forward_malformed_packet-pymysql-t.py new file mode 100644 index 0000000000..a0a31a08e6 --- /dev/null +++ b/test/tap/tests/reg_test_3992_fast_forward_malformed_packet-pymysql-t.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +# This is a regression test for issues #3992 and #4047. Test checks if queries +# are executed successfully with MariaDB server via PyMySQL client having fast +# forward flag set to true and false. + +import pymysql +import sys +import os + +proxy_addr = os.environ['TAP_HOST'] +proxy_port = int(os.environ['TAP_PORT']) + +users = ['mariadbuser', 'mariadbuserff'] + +db = 'information_schema' + +error_count = 0 + +for user in users: + connection = pymysql.connect(host=proxy_addr, port=proxy_port, user=user, passwd=user, db=db, + charset='utf8') + with connection.cursor() as cursor: + error = None + + try: + cursor.execute('select * from information_schema.GLOBAL_VARIABLES ' + 'where variable_name =\'sql_mode\';') + except Exception as e: + ok_msg = 'not ok' + error_count += 1 + error = e + else: + ok_msg = 'ok' + + print('{} - \'PyMySQL\' select command should be correctly executed. Error was: {}' + .format(ok_msg, error)) + +sys.exit(error_count) diff --git a/test/tap/tests/reg_test_4055_restapi-t.cpp b/test/tap/tests/reg_test_4055_restapi-t.cpp new file mode 100644 index 0000000000..73564c73be --- /dev/null +++ b/test/tap/tests/reg_test_4055_restapi-t.cpp @@ -0,0 +1,123 @@ +/** + * @file reg_test_4055_restapi-t.cpp + * @brief Simple regression test sending malformed query to RESTAPI. + * @details This test performs the following actions: + * - Issue a malformed request to the RESTAPI. + * - Checks that Admin interface is still responsive. + * - Checks that the 'metrics' endpoint from the RESTAPI is still responsive. + * - Perform minor correctness check on the 'metrics' endpoint response. + * @date 2022-12-15 + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "mysql.h" + +#include "tap.h" +#include "command_line.h" +#include "utils.h" + +using std::string; + +/* This is an estimation of the supported number of metrics as for '2022-12-15' */ +uint32_t SUPPORTED_METRICS = 148; + +int main(int argc, char** argv) { + plan(5); + + CommandLine cl; + + if (cl.getEnv()) { + diag("Failed to get the required environmental variables."); + return EXIT_FAILURE; + } + + MYSQL* admin = mysql_init(NULL); + + if (!mysql_real_connect(admin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin)); + return EXIT_FAILURE; + } + + // Enable 'RESTAPI' + MYSQL_QUERY(admin, "SET admin-restapi_enabled='true'"); + MYSQL_QUERY(admin, "SET admin-restapi_port=6070"); + MYSQL_QUERY(admin, "LOAD ADMIN VARIABLES TO RUNTIME"); + + int socket_desc; + struct sockaddr_in server; + + socket_desc = socket(AF_INET , SOCK_STREAM , 0); + if (socket_desc == -1) { + return errno; + } + + server.sin_addr.s_addr = inet_addr("127.0.0.1"); + server.sin_family = AF_INET; + server.sin_port = htons(6070); + + if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0) { + return errno; + } + + // Perform the invalid request, and add a sleep to let ProxySQL process the data + { + ssize_t n = write(socket_desc, static_cast(" \n"), strlen(" \n")); + diag("Written '%lu' bytes into socket", n); + sleep(1); + } + + int myrc = mysql_query(admin, "SELECT version()"); + ok(myrc == EXIT_SUCCESS, "ProxySQL is still up and reachable"); + + MYSQL_RES* myres = mysql_store_result(admin); + MYSQL_ROW myrow = mysql_fetch_row(myres); + + string recv_version {}; + if (myrow && myrow[0]) { recv_version = myrow[0]; } + mysql_free_result(myres); + + ok(recv_version.empty() == false, "Received non empty ProxySQL version '%s'", recv_version.c_str()); + + uint64_t curl_res_code = 0; + string curl_res_data {}; + + CURLcode code = perform_simple_get("http://localhost:6070/metrics/", curl_res_code, curl_res_data); + + ok( + code == CURLE_OK && curl_res_code == 200, + "RESTAPI still up and responding to requests - curl_code: %d, res_code: %lu", + code, curl_res_code + ); + + size_t matches = count_matches(curl_res_data, "# "); + const uint32_t min_exp_metrics = SUPPORTED_METRICS - 20; + + ok( + matches % 2 == 0, + "Response from endpoint is well-formed (even number of '# ' metrics descriptions) - matches: '%ld'", + matches + ); + + ok( + min_exp_metrics < (matches / 2), + "Response contains more than a minimum of expected metrics - min: %d, act: %lu", + min_exp_metrics, matches / 2 + ); + + if (tests_failed()) { + diag("Failed! Received GET response: \n\n%s", curl_res_data.c_str()); + } + + close(socket_desc); + mysql_close(admin); + + return exit_status(); +} diff --git a/test/tap/tests/reg_test_4072-show-warnings-t.cpp b/test/tap/tests/reg_test_4072-show-warnings-t.cpp new file mode 100644 index 0000000000..dcebe2ce08 --- /dev/null +++ b/test/tap/tests/reg_test_4072-show-warnings-t.cpp @@ -0,0 +1,126 @@ +/** + * @file reg_test_4072-show-warnings-t.cpp + * @brief This test will confirm ProxySQL does not crash if large records are fetched, having warning producing condition in the query. + */ + +#include +#include +#include +#include +#include +#include +#include "tap.h" +#include "command_line.h" +#include "utils.h" + +int main(int argc, char** argv) { + CommandLine cl; + + if (cl.getEnv()) { + diag("Failed to get the required environmental variables."); + return -1; + } + + plan(1); + + // Initialize Admin connection + MYSQL* proxysql_admin = mysql_init(NULL); + if (!proxysql_admin) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin)); + return -1; + } + // Connnect to ProxySQL Admin + if (!mysql_real_connect(proxysql_admin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin)); + return -1; + } + + MYSQL_QUERY(proxysql_admin, "SET mysql-log_mysql_warnings_enabled=1"); + MYSQL_QUERY(proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME"); + + // Initialize ProxySQL connection + MYSQL* proxysql = mysql_init(NULL); + if (!proxysql) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql)); + return -1; + } + + // Connect to ProxySQL + if (!mysql_real_connect(proxysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql)); + return exit_status(); + } + MYSQL_QUERY(proxysql, "DROP DATABASE IF EXISTS testdb"); + MYSQL_QUERY(proxysql, "CREATE DATABASE testdb"); + MYSQL_QUERY(proxysql, "CREATE TABLE testdb.`tmp` ( " \ + "`id` bigint(20) NOT NULL AUTO_INCREMENT, " \ + "`text1` varchar(200) COLLATE utf8_bin NOT NULL, " \ + "`text2` varchar(200) COLLATE utf8_bin NOT NULL, " \ + "`time` datetime NOT NULL, " \ + "PRIMARY KEY(`id`,`time`) " \ + ") ENGINE = InnoDB"); + + diag("Inserting rows..."); + MYSQL_QUERY(proxysql, "INSERT INTO testdb.tmp(text1, text2, time) values('dummy text1', 'dummy text2', now())"); + + for (int i = 0; i < 7; i++) { + MYSQL_QUERY(proxysql, "INSERT INTO testdb.tmp(text1, text2, time) SELECT text1, text2, time FROM testdb.tmp"); + } + + std::this_thread::sleep_for(std::chrono::seconds(2)); + + MYSQL_QUERY(proxysql, "SELECT COUNT(*) FROM testdb.tmp a JOIN testdb.tmp b JOIN testdb.tmp c"); + + auto mysql_result = mysql_use_result(proxysql); + + if (!mysql_result) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql)); + return exit_status(); + } + + auto row = mysql_fetch_row(mysql_result); + unsigned long add_row_count = strtoul(row[0], NULL, 0); + + if (mysql_result) { + mysql_free_result(mysql_result); + mysql_result = NULL; + } + + diag("Done... Total rows to fetch:'%lu'", add_row_count); + diag("Fetching all rows..."); + MYSQL_QUERY(proxysql, "SELECT a.* FROM testdb.tmp a JOIN testdb.tmp b JOIN testdb.tmp c WHERE 1/0 OR 1=1"); + + mysql_result = mysql_use_result(proxysql); + + if (!mysql_result) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql)); + return exit_status(); + } + + unsigned long fetched_row_count = 0; + + while (row = mysql_fetch_row(mysql_result)) { + fetched_row_count++; + usleep(10); + } + + int _errorno = mysql_errno(proxysql); + + if (_errorno) { + diag("An error occurred. Error Code:%d, Message:%s", _errorno, mysql_error(proxysql)); + return exit_status(); + } + + if (mysql_result) { + mysql_free_result(mysql_result); + mysql_result = NULL; + } + + diag("Done... Total rows fetched:'%lu'\n", fetched_row_count); + ok(add_row_count == fetched_row_count, "All rows fetched"); + + mysql_close(proxysql); + mysql_close(proxysql_admin); + + return exit_status(); +} diff --git a/test/tap/tests/test_admin_prometheus_metrics_dump-t_disabled_gh3571.cpp b/test/tap/tests/test_admin_prometheus_metrics_dump-t_disabled_gh3571.cpp index e3e269823e..4b3ba54325 100644 --- a/test/tap/tests/test_admin_prometheus_metrics_dump-t_disabled_gh3571.cpp +++ b/test/tap/tests/test_admin_prometheus_metrics_dump-t_disabled_gh3571.cpp @@ -21,18 +21,6 @@ using std::string; std::size_t supported_metrics = 121; -std::size_t count_matches(const std::string& str, const std::string& substr) { - std::size_t result = 0; - std::size_t pos = 0; - - while ((pos = str.find(substr, pos)) != std::string::npos) { - result += 1; - pos += substr.length(); - } - - return result; -} - int main(int argc, char** argv) { CommandLine cl; diff --git a/test/tap/tests/test_empty_query-t.cpp b/test/tap/tests/test_empty_query-t.cpp new file mode 100644 index 0000000000..558b08e546 --- /dev/null +++ b/test/tap/tests/test_empty_query-t.cpp @@ -0,0 +1,50 @@ +/** + * @file test_empty_query-t.cpp + * @brief Simple test checking that empty queries are properly handled by ProxySQL. + */ + +#include "mysql.h" +#include "tap.h" +#include "command_line.h" +#include "utils.h" + +uint32_t EXECUTIONS = 100; + +int main(int argc, char** argv) { + plan(1); + + CommandLine cl; + + if (cl.getEnv()) { + diag("Failed to get the required environmental variables."); + return EXIT_FAILURE; + } + + MYSQL* proxy = mysql_init(NULL); + + if (!mysql_real_connect(proxy, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy)); + return EXIT_FAILURE; + } + + int exp_myerr = 1065; + int act_myerr = 0; + + for (uint32_t i = 0; i < EXECUTIONS; i++) { + mysql_query(proxy, ""); + act_myerr = mysql_errno(proxy); + + if (exp_myerr != act_myerr) { + break; + } + } + + ok( + exp_myerr == act_myerr, "MySQL error equals expected - exp_err: '%d', act_err: '%d' error: `%s`", + exp_myerr, act_myerr, mysql_error(proxy) + ); + + mysql_close(proxy); + + return exit_status(); +} diff --git a/test/tap/tests/test_server_sess_status-t.cpp b/test/tap/tests/test_server_sess_status-t.cpp index 0764815478..cf743b2759 100644 --- a/test/tap/tests/test_server_sess_status-t.cpp +++ b/test/tap/tests/test_server_sess_status-t.cpp @@ -81,7 +81,7 @@ int main(int argc, char** argv) { CommandLine cl; // TODO: Harcoded for now, this is an initial version of the test. - plan(4); + plan(6); if (cl.getEnv()) { diag("Failed to get the required environmental variables."); @@ -133,18 +133,59 @@ int main(int argc, char** argv) { exp_mysql_srv_st, mysql->server_status ); - // TODO-FIXME: We are setting here '0' as expecting to see 'SERVER_STATUS_AUTOCOMMIT' to be false. - // This is a bug that should be addressed, and this test revisited. + uint32_t exp_proxy_srv_st = SERVER_STATUS_AUTOCOMMIT; + ok( - proxy->server_status == 0, + exp_proxy_srv_st == proxy->server_status, "ProxySQL init server status should match expected - exp: '%d', act:'%d'", - 0, proxy->server_status + exp_proxy_srv_st, proxy->server_status + ); + + int err_code = mysql_reset_connection(proxy); + if (err_code != EXIT_SUCCESS) { + diag( + "'mysql_reset_connection' failed with error: (%d,'%s') at ('%s':'%d')", + mysql_errno(proxy), mysql_error(proxy), __FILE__, __LINE__ + ); + return EXIT_FAILURE; + } + + ok( + exp_proxy_srv_st == proxy->server_status, + "ProxySQL server status after reset connection should match expected - exp: '%d', act:'%d'", + exp_proxy_srv_st, proxy->server_status + ); + + const string username = { "sbtest_reset_conn_1" }; + const string password = { "sbtest_reset_conn_1" }; + const string attributes = { "" }; + err_code = create_proxysql_user(admin, username, password, attributes); + if (err_code) { + diag("'create_proxysql_user' failed at ('%s':'%d') with error '%d'", __FILE__, __LINE__, err_code); + return EXIT_FAILURE; + } + + MYSQL_QUERY(admin, "LOAD MYSQL USERS TO RUNTIME"); + + err_code = mysql_change_user(proxy, username.c_str(), password.c_str(), NULL); + if (err_code != EXIT_SUCCESS) { + diag( + "'mysql_change_user' failed with error: (%d,'%s') at ('%s':'%d')", + mysql_errno(proxy), mysql_error(proxy), __FILE__, __LINE__ + ); + return EXIT_FAILURE; + } + + ok( + exp_proxy_srv_st == proxy->server_status, + "ProxySQL server status after change user should match expected - exp: '%d', act:'%d'", + exp_proxy_srv_st, proxy->server_status ); mysql_query(proxy, "SET SESSION session_track_transaction_info=\"CHARACTERISTICS\""); mysql_query(proxy, "START TRANSACTION"); - uint32_t exp_proxy_srv_st = SERVER_STATUS_AUTOCOMMIT | SERVER_STATUS_IN_TRANS; + exp_proxy_srv_st = SERVER_STATUS_AUTOCOMMIT | SERVER_STATUS_IN_TRANS; ok( exp_proxy_srv_st == proxy->server_status, diff --git a/test/tap/tests/test_ssl_fast_forward-1-t.cpp b/test/tap/tests/test_ssl_fast_forward-1-t.cpp new file mode 100644 index 0000000000..b075ef632f --- /dev/null +++ b/test/tap/tests/test_ssl_fast_forward-1-t.cpp @@ -0,0 +1,276 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "tap.h" +#include "command_line.h" +#include "utils.h" + + +/* +Several bugs were identified and fixed while developing this test: +- several memory corruption in SSL and fast_forward +- SQLite3 Server stops working if the user is a fast_forward user +- potential stalls with large resultset +- connections being closed because of interrupted syscall in SSL (SSL_ERROR_SYSCALL) + +*/ + + +char * username = (char *)"user1459"; +char * password = (char *)"pass1459"; + +std::vector queries_set1 = { + "SET mysql-have_ssl='false'", + "LOAD MYSQL VARIABLES TO RUNTIME", + "DELETE FROM mysql_servers WHERE hostgroup_id = 1459", + "INSERT INTO mysql_servers (hostgroup_id, hostname, port, use_ssl) VALUES (1459, '127.0.0.1', 6030, 0)", + "LOAD MYSQL SERVERS TO RUNTIME", + "DELETE FROM mysql_users WHERE username = 'user1459'", + "INSERT INTO mysql_users (username,password,fast_forward,default_hostgroup) VALUES ('" + std::string(username) + "','" + std::string(password) + "',1,1459)", + "LOAD MYSQL USERS TO RUNTIME", +}; + +std::vector queries_set2 = { + "SET mysql-have_ssl='true'", + "LOAD MYSQL VARIABLES TO RUNTIME", +}; + +std::vector queries_set3 = { + "SET mysql-have_ssl='false'", + "LOAD MYSQL VARIABLES TO RUNTIME", + "UPDATE mysql_servers SET use_ssl=1 WHERE hostgroup_id = 1459", + "LOAD MYSQL SERVERS TO RUNTIME", +}; + +std::vector queries_set4 = { + "SET mysql-have_ssl='true'", + "LOAD MYSQL VARIABLES TO RUNTIME", + "UPDATE mysql_servers SET use_ssl=1 WHERE hostgroup_id = 1459", + "LOAD MYSQL SERVERS TO RUNTIME", +}; + +std::vector queries_SQL1 = { + "DROP TABLE IF EXISTS tbl1459", + "CREATE TABLE tbl1459 (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , i1 INTEGER , i2 INTEGER)", + "INSERT INTO tbl1459 VALUES (NULL, 1, 2)", +}; + +std::vector queries_SQL2 = { + "INSERT INTO tbl1459 SELECT NULL , i1 + id, i2 + id FROM tbl1459", +}; + +std::vector queries_SQL4 = { + "DROP TABLE IF EXISTS tbl1459", + "VACUUM", +}; + +std::vector queries_limits = { + 1, 10, 20, 27, 103, 169, 320, 450, 512, 619, 915, 1022, + 1033, 1145, 1516, 1920, 2034, 5014, 9932, 10111, + 11033, 11145, 12516, 11920, 12034, 15014, 19932, + 21033, 21145, 22516, 21920, 22034, 25014, 29932 +}; + + +int run_queries_sets(std::vector& queries, MYSQL *my, const std::string& message_prefix) { + for (std::vector::iterator it = queries.begin(); it != queries.end(); it++) { + std::string q = *it; + diag("%s: %s", message_prefix.c_str(), q.c_str()); + MYSQL_QUERY(my, q.c_str()); + } + return 0; +} + +#define ITER1 10 +// ITER2 is big because we iterate many times in case SSL_ERROR_SYSCALL (interrupted syscall, that is normally a rare event) is triggered +#define ITER2 20 + +int main(int argc, char** argv) { + CommandLine cl; + + if(cl.getEnv()) + return exit_status(); + + unsigned int p = 0; + p += 5*ITER1; + p += (5-3)*ITER2*queries_limits.size(); // only on encrypted backend connections + plan(p); + diag("Testing SSL and fast_forward"); + + MYSQL* mysqladmin = mysql_init(NULL); + if (!mysqladmin) + return exit_status(); + + if (!mysql_real_connect(mysqladmin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", + __FILE__, __LINE__, mysql_error(mysqladmin)); + return exit_status(); + } + + MYSQL * mysqls[5]; + for (int i = 0 ; i<5 ; i++) { + mysqls[i] = NULL; + } + // We will loop ITER1 times. + // On each iteration we create 5 connections with different configuration and run a simple SELECT 1 + + for (int it = 0 ; itthread_id, l, rr); + mysql_free_result(result); + } + } + } + + + + // clean up + if (run_queries_sets(queries_SQL4, mysqls[0], "Running on SQLite3")) + return exit_status(); + + + for (int i=0; i<5; i++) { + diag("Connection %d has thread_id: %lu", i, mysqls[i]->thread_id); + } + + mysql_close(mysqladmin); + + return exit_status(); +} + diff --git a/test/tap/tests/test_ssl_fast_forward-2-t.cpp b/test/tap/tests/test_ssl_fast_forward-2-t.cpp new file mode 100644 index 0000000000..dd5b0b9a3b --- /dev/null +++ b/test/tap/tests/test_ssl_fast_forward-2-t.cpp @@ -0,0 +1,263 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "tap.h" +#include "command_line.h" +#include "utils.h" + + +/* +this test uses a lot of code from test_ssl_fast_forward-t.cpp +*/ + +char * username = (char *)"user1459"; +char * password = (char *)"pass1459"; + +std::vector queries_set1 = { + "SET mysql-have_ssl='false'", + "LOAD MYSQL VARIABLES TO RUNTIME", + "DELETE FROM mysql_servers WHERE hostgroup_id = 1459", + "INSERT INTO mysql_servers (hostgroup_id, hostname, port, use_ssl) VALUES (1459, '127.0.0.1', 6030, 0)", + "LOAD MYSQL SERVERS TO RUNTIME", + "DELETE FROM mysql_users WHERE username = 'user1459'", + "INSERT INTO mysql_users (username,password,fast_forward,default_hostgroup) VALUES ('" + std::string(username) + "','" + std::string(password) + "',1,1459)", + "LOAD MYSQL USERS TO RUNTIME", +}; + +std::vector queries_set2 = { + "SET mysql-have_ssl='true'", + "LOAD MYSQL VARIABLES TO RUNTIME", +}; + +std::vector queries_set3 = { + "SET mysql-have_ssl='false'", + "LOAD MYSQL VARIABLES TO RUNTIME", + "UPDATE mysql_servers SET use_ssl=1 WHERE hostgroup_id = 1459", + "LOAD MYSQL SERVERS TO RUNTIME", +}; + +std::vector queries_set4 = { + "SET mysql-have_ssl='true'", + "LOAD MYSQL VARIABLES TO RUNTIME", + "UPDATE mysql_servers SET use_ssl=1 WHERE hostgroup_id = 1459", + "LOAD MYSQL SERVERS TO RUNTIME", +}; + +std::vector queries_SQL3 = { + "DROP TABLE IF EXISTS tbl1459v", + "CREATE TABLE tbl1459v (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , t1 VARCHAR)", +}; + +std::vector queries_SQL4 = { + "DROP TABLE IF EXISTS tbl1459v", + "VACUUM", +}; + + +int run_queries_sets(std::vector& queries, MYSQL *my, const std::string& message_prefix) { + for (std::vector::iterator it = queries.begin(); it != queries.end(); it++) { + std::string q = *it; + diag("%s: %s", message_prefix.c_str(), q.c_str()); + MYSQL_QUERY(my, q.c_str()); + } + return 0; +} + +#define ITER1 1 + +#define LL 16000 // lower limit +#define UL 96000 // upper limit + +int main(int argc, char** argv) { + CommandLine cl; + + if(cl.getEnv()) + return exit_status(); + + unsigned int p = 0; + p += 5*ITER1; + //p += (5-3)*ITER2*queries_limits.size(); // only on encrypted backend connections + p += ((UL-LL)/1000*2); + plan(p); + diag("Testing SSL and fast_forward"); + + MYSQL* mysqladmin = mysql_init(NULL); + if (!mysqladmin) + return exit_status(); + + if (!mysql_real_connect(mysqladmin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", + __FILE__, __LINE__, mysql_error(mysqladmin)); + return exit_status(); + } + + MYSQL * mysqls[5]; + for (int i = 0 ; i<5 ; i++) { + mysqls[i] = NULL; + } + // We will loop ITER1 times. + // On each iteration we create 5 connections with different configuration and run a simple SELECT 1 + + for (int it = 0 ; itthread_id); + } + + // We now sends long INSERTs. This code is similar of test_ssl_large_query-t.cpp + + // We now populate a table named tbl1459v + if (run_queries_sets(queries_SQL3, mysqls[0], "Running on SQLite3")) + return exit_status(); + + + std::string s0 = "0"; + //for (int i=16001; i<=48000; i++) { + for (int i=LL; i +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "tap.h" +#include "command_line.h" +#include "utils.h" +#include + + + +char * username = (char *)"user1459"; +char * password = (char *)"pass1459"; + +const std::string lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; + +std::vector queries_set1 = { + "SET mysql-have_ssl='true'", + "LOAD MYSQL VARIABLES TO RUNTIME", + "DELETE FROM mysql_servers WHERE hostgroup_id = 1459", + "INSERT INTO mysql_servers (hostgroup_id, hostname, port, use_ssl) VALUES (1459, '127.0.0.1', 6030, 1)", + "LOAD MYSQL SERVERS TO RUNTIME", + "DELETE FROM mysql_users WHERE username = 'user1459'", + "INSERT INTO mysql_users (username,password,fast_forward,default_hostgroup) VALUES ('" + std::string(username) + "','" + std::string(password) + "',1,1459)", + "LOAD MYSQL USERS TO RUNTIME", +}; + + +int run_queries_sets(std::vector& queries, MYSQL *my, const std::string& message_prefix) { + for (std::vector::iterator it = queries.begin(); it != queries.end(); it++) { + std::string q = *it; + diag("%s: %s", message_prefix.c_str(), q.c_str()); + MYSQL_QUERY(my, q.c_str()); + } + return 0; +} + +#define ITER 400 +#define NTHR 4 +#define CPTH 100 + +CommandLine cl; + +std::mutex mtx_; + +int my_conn_thread_in(void *arg) { + MYSQL * mysqls[CPTH]; + for (int i = 0 ; i lock(mtx_); + ok(c != NULL , "Cipher in use: %s", c == NULL ? "NULL" : c); + } + } + for (int i=1; i<=ITER; i++) { + std::string s = "SELECT ''"; + for (int j=0; j lock(mtx_); + ok(s.length() == rl + strlen((const char *)"SELECT ''") + i*4 , "Line %d : on connection %ld , executed SELECT %ld bytes long. Length returned: %ld", __LINE__ , mysqls[(i+j)%CPTH]->thread_id, s.length(), rl); + } + } + } + return 0; +} + +void * my_conn_thread(void *arg) { + diag("Starting thread..."); + int rc = my_conn_thread_in(NULL); + diag("... thread ended!"); + return NULL; +} + + +int main(int argc, char** argv) { + + if(cl.getEnv()) + return exit_status(); + + plan(NTHR*(ITER+CPTH/2)); + diag("Testing SSL and fast_forward"); + + MYSQL* mysqladmin = mysql_init(NULL); + if (!mysqladmin) + return exit_status(); + + if (!mysql_real_connect(mysqladmin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", + __FILE__, __LINE__, mysql_error(mysqladmin)); + return exit_status(); + } + + // We will loop ITER1 times. + // On each iteration we create 5 connections with different configuration and run a simple SELECT 1 + + //for (int it = 0 ; it +#include +#include +#include + +#include +#include +#include +#include + +#include "tap.h" +#include "command_line.h" +#include "utils.h" + + +char * username = (char *)"user1459"; +char * password = (char *)"pass1459"; + +std::vector queries_set1 = { + "SET mysql-have_ssl='true'", + "LOAD MYSQL VARIABLES TO RUNTIME", + "DELETE FROM mysql_servers WHERE hostgroup_id = 1459", + "INSERT INTO mysql_servers (hostgroup_id, hostname, port, use_ssl) VALUES (1459, '127.0.0.1', 6030, 0)", + "LOAD MYSQL SERVERS TO RUNTIME", + "DELETE FROM mysql_users WHERE username = 'user1459'", + "INSERT INTO mysql_users (username,password,default_hostgroup) VALUES ('" + std::string(username) + "','" + std::string(password) + "',1459)", + "LOAD MYSQL USERS TO RUNTIME", +}; + +std::vector queries_SQL1 = { + "DROP TABLE IF EXISTS tbl1459", + "CREATE TABLE tbl1459 (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , t1 VARCHAR)", +}; + + +int run_queries_sets(std::vector& queries, MYSQL *my, const std::string& message_prefix) { + for (std::vector::iterator it = queries.begin(); it != queries.end(); it++) { + std::string q = *it; + diag("%s: %s", message_prefix.c_str(), q.c_str()); + MYSQL_QUERY(my, q.c_str()); + } + return 0; +} + + +int main(int argc, char** argv) { + CommandLine cl; + + if(cl.getEnv()) + return exit_status(); + + plan(33); + diag("Testing SSL and fast_forward"); + + MYSQL* mysqladmin = mysql_init(NULL); + if (!mysqladmin) + return exit_status(); + + if (!mysql_real_connect(mysqladmin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", + __FILE__, __LINE__, mysql_error(mysqladmin)); + return exit_status(); + } + + MYSQL * mysql = NULL; + + diag("We will reconfigure ProxySQL to use SQLite3 Server on hostgroup 1459, IP 127.0.0.1 and port 6030"); + if (run_queries_sets(queries_set1, mysqladmin, "Running on Admin")) + return exit_status(); + + mysql = mysql_init(NULL); + if (!mysql) + return exit_status(); + + mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, NULL); + if (!mysql_real_connect(mysql, cl.host, username, password, NULL, cl.port, NULL, CLIENT_SSL)) { + fprintf(stderr, "Failed to connect to database: Error: %s\n", + mysql_error(mysql)); + return exit_status(); + } + const char * c = mysql_get_ssl_cipher(mysql); + ok(c != NULL , "Cipher in use: %s", c == NULL ? "NULL" : c); + + // We now create a table named tbl1459 + if (run_queries_sets(queries_SQL1, mysql, "Running on SQLite3")) + return exit_status(); + + std::string s0 = "0"; + for (int i=16001; i<=48000; i++) { + std::string s = "INSERT INTO tbl1459 VALUES (" + std::to_string(i) + ",'"; + for (int j=0; j +#include +#include +#include + +#include +#include +#include +#include + +#include "tap.h" +#include "command_line.h" +#include "utils.h" + + +char * username = (char *)"user1459"; +char * password = (char *)"pass1459"; + +std::vector queries_set1 = { + "SET mysql-have_ssl='true'", + "LOAD MYSQL VARIABLES TO RUNTIME", + "DELETE FROM mysql_users WHERE username = 'user1459'", + "INSERT INTO mysql_users (username,password,default_hostgroup) VALUES ('" + std::string(username) + "','" + std::string(password) + "',1459)", + "LOAD MYSQL USERS TO RUNTIME", +}; + +std::vector queries_SQL1 = { + "DROP TABLE IF EXISTS tbl1459", + "CREATE TABLE tbl1459 (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , t1 VARCHAR)", +}; + + +int run_queries_sets(std::vector& queries, MYSQL *my, const std::string& message_prefix) { + for (std::vector::iterator it = queries.begin(); it != queries.end(); it++) { + std::string q = *it; + diag("%s: %s", message_prefix.c_str(), q.c_str()); + MYSQL_QUERY(my, q.c_str()); + } + return 0; +} + +#define ITER 400 + +const std::string lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; + +int main(int argc, char** argv) { + CommandLine cl; + + if(cl.getEnv()) + return exit_status(); + + plan(2+2*ITER); + diag("Testing SSL and fast_forward"); + + MYSQL* mysqladmin = mysql_init(NULL); + if (!mysqladmin) + return exit_status(); + + if (!mysql_real_connect(mysqladmin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", + __FILE__, __LINE__, mysql_error(mysqladmin)); + return exit_status(); + } + + + + if (run_queries_sets(queries_set1, mysqladmin, "Running on Admin")) + return exit_status(); + + + MYSQL * mysqladmin2 = mysql_init(NULL); + if (!mysqladmin2) + return exit_status(); + + mysql_ssl_set(mysqladmin2, NULL, NULL, NULL, NULL, NULL); + if (!mysql_real_connect(mysqladmin2, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", + __FILE__, __LINE__, mysql_error(mysqladmin2)); + return exit_status(); + } + + { + const char * c = mysql_get_ssl_cipher(mysqladmin2); + ok(c != NULL , "Cipher in use: %s", c == NULL ? "NULL" : c); + } + + MYSQL * mysqllite3 = mysql_init(NULL); + if (!mysqllite3) + return exit_status(); + + mysql_ssl_set(mysqllite3, NULL, NULL, NULL, NULL, NULL); + if (!mysql_real_connect(mysqllite3, cl.host, username, password, NULL, 6030, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", + __FILE__, __LINE__, mysql_error(mysqllite3)); + return exit_status(); + } + + { + const char * c = mysql_get_ssl_cipher(mysqllite3); + ok(c != NULL , "Cipher in use: %s", c == NULL ? "NULL" : c); + } + + for (int i=1; i<=ITER; i++) { + std::string s = "SELECT ''"; + for (int j=0; j