From cc0c34554d5e81c83432cc0100b29dfb801e26a8 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Tue, 8 Mar 2016 17:08:01 +0100 Subject: [PATCH] - Fixes for 10.2-integration - As requested by Wlad we use connect timeout for read/write unless the connection was established. - Added experimental session cache support for OpenSSL. It's currently disabled --- libmariadb/ma_password.c | 8 ---- libmariadb/ma_pvio.c | 12 ++++-- libmariadb/mariadb_lib.c | 9 ++-- libmariadb/secure/openssl.c | 20 +++++---- plugins/pvio/pvio_socket.c | 67 +++++++++++++++++++---------- unittest/libmariadb/connection.c | 72 +++++++++++++++++++++++++++++++- unittest/libmariadb/ssl.c.in | 2 +- 7 files changed, 143 insertions(+), 47 deletions(-) diff --git a/libmariadb/ma_password.c b/libmariadb/ma_password.c index 6af89846..4bd14a70 100644 --- a/libmariadb/ma_password.c +++ b/libmariadb/ma_password.c @@ -54,14 +54,6 @@ void ma_randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2) rand_st->seed2=seed2%rand_st->max_value; } -static void ma_old_randominit(struct rand_struct *rand_st,ulong seed1) -{ /* For mysql 3.20.# */ - rand_st->max_value= 0x01FFFFFFL; - rand_st->max_value_dbl=(double) rand_st->max_value; - seed1%=rand_st->max_value; - rand_st->seed1=seed1 ; rand_st->seed2=seed1/2; -} - double rnd(struct rand_struct *rand_st) { rand_st->seed1=(rand_st->seed1*3+rand_st->seed2) % rand_st->max_value; diff --git a/libmariadb/ma_pvio.c b/libmariadb/ma_pvio.c index 370223a4..9b3178a5 100644 --- a/libmariadb/ma_pvio.c +++ b/libmariadb/ma_pvio.c @@ -100,6 +100,7 @@ MARIADB_PVIO *ma_pvio_init(MA_PVIO_CINFO *cinfo) pvio_plugins[type], MARIADB_CLIENT_PVIO_PLUGIN))) { + /* error already set in mysql_client_find_plugin */ return NULL; } @@ -115,12 +116,11 @@ MARIADB_PVIO *ma_pvio_init(MA_PVIO_CINFO *cinfo) pvio->set_error= my_set_error; pvio->type= cinfo->type; - /* set tineouts */ + /* set timeout to connect timeout - after successfull connect we will set + * correct values for read and write */ if (pvio->methods->set_timeout) { pvio->methods->set_timeout(pvio, PVIO_CONNECT_TIMEOUT, cinfo->mysql->options.connect_timeout); - pvio->methods->set_timeout(pvio, PVIO_READ_TIMEOUT, cinfo->mysql->options.read_timeout); - pvio->methods->set_timeout(pvio, PVIO_WRITE_TIMEOUT, cinfo->mysql->options.write_timeout); } if (!(pvio->cache= calloc(1, PVIO_READ_AHEAD_CACHE_SIZE))) @@ -521,6 +521,12 @@ my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio) pvio->cssl= NULL; return 1; } + + /* default behaviour: + 1. peer certificate verification + 2. verify CN (requires option ssl_verify_check) + 3. verrify finger print + */ if ((pvio->mysql->options.ssl_ca || pvio->mysql->options.ssl_capath) && (pvio->mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) && ma_pvio_ssl_verify_server_cert(pvio->cssl)) diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index 201d2be3..b312181d 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -1349,8 +1349,6 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); goto error; } - strcpy(mysql->host_info,host_info); - strcpy(mysql->host, cinfo.host); if (cinfo.unix_socket) mysql->unix_socket= strdup(cinfo.unix_socket); else @@ -1512,6 +1510,9 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, strcpy(mysql->net.sqlstate, "00000"); + /* connection established, apply timeouts */ + ma_pvio_set_timeout(mysql->net.pvio, PVIO_READ_TIMEOUT, mysql->options.read_timeout); + ma_pvio_set_timeout(mysql->net.pvio, PVIO_WRITE_TIMEOUT, mysql->options.write_timeout); return(mysql); error: @@ -1557,6 +1558,7 @@ my_bool STDCALL mariadb_reconnect(MYSQL *mysql) struct mysql_async_context *ctxt= NULL; #endif LIST *li_stmt= mysql->stmts; + mysql_init(&tmp_mysql); /* check if connection handler is active */ if (IS_CONNHDLR_ACTIVE(mysql)) @@ -1835,11 +1837,12 @@ static void mysql_close_options(MYSQL *mysql) static void mysql_close_memory(MYSQL *mysql) { free(mysql->host_info); + free(mysql->host); free(mysql->user); free(mysql->passwd); free(mysql->db); free(mysql->server_version); - mysql->host_info= mysql->server_version=mysql->user=mysql->passwd=mysql->db=0; + mysql->host_info= mysql->host= mysql->server_version=mysql->user=mysql->passwd=mysql->db=0; } void my_set_error(MYSQL *mysql, diff --git a/libmariadb/secure/openssl.c b/libmariadb/secure/openssl.c index 6c774766..56d61b40 100644 --- a/libmariadb/secure/openssl.c +++ b/libmariadb/secure/openssl.c @@ -30,6 +30,7 @@ #include #include +#define HAVE_SSL_SESSION_CACHE 1 #ifndef HAVE_OPENSSL_DEFAULT #include #define ma_malloc(A,B) malloc((A)) @@ -111,7 +112,7 @@ static void my_cb_threadid(CRYPTO_THREADID *id) } #endif -#ifdef HAVE_OPENSSL_SESSION_TICKET +#ifdef HAVE_SSL_SESSION_CACHE typedef struct st_ma_ssl_session { char md4_hash[17]; SSL_SESSION *session; @@ -120,10 +121,11 @@ typedef struct st_ma_ssl_session { MA_SSL_SESSION *ma_ssl_sessions= NULL; unsigned int ma_ssl_session_cache_size= 128; -static char *ma_md4_hash(const char *host, unsigned int port, char *md4) +static char *ma_md4_hash(const char *host, const char *user, unsigned int port, char *md4) { - char buffer[260]; - snprintf(buffer, 258, "%s:%d", host, port); + char buffer[195]; /* MAX_USERNAME_LEN + MAX_HOST_NAME_LEN + 2 + 5 */ + snprintf(buffer, 194, "%s@%s:%d", user ? user : "", host, port); + buffer[194]= 0; MD4(buffer, strlen(buffer), md4); return md4; } @@ -137,7 +139,7 @@ MA_SSL_SESSION *ma_ssl_get_session(MYSQL *mysql) return NULL; memset(md4, 0, 16); - ma_md4_hash(mysql->host, mysql->port, md4); + ma_md4_hash(mysql->host, mysql->user, mysql->port, md4); for (i=0; i < ma_ssl_session_cache_size; i++) { if (ma_ssl_sessions[i].session && @@ -169,7 +171,7 @@ static int ma_ssl_session_cb(SSL *ssl, SSL_SESSION *session) { if (!ma_ssl_sessions[i].session) { - ma_md4_hash(mysql->host, mysql->port, ma_ssl_sessions[i].md4_hash); + ma_md4_hash(mysql->host, mysql->user, mysql->port, ma_ssl_sessions[i].md4_hash); ma_ssl_sessions[i].session= session; } return 1; @@ -268,7 +270,7 @@ int ma_ssl_start(char *errmsg, size_t errmsg_len) ma_ssl_get_error(errmsg, errmsg_len); goto end; } -#ifdef HAVE_OPENSSL_SESSION_TICKET +#ifdef HAVE_SSL_SESSION_CACHE SSL_CTX_set_session_cache_mode(SSL_context, SSL_SESS_CACHE_CLIENT); ma_ssl_sessions= (MA_SSL_SESSION *)calloc(1, sizeof(struct st_ma_ssl_session) * ma_ssl_session_cache_size); SSL_CTX_sess_set_new_cb(SSL_context, ma_ssl_session_cb); @@ -452,7 +454,7 @@ void *ma_ssl_init(MYSQL *mysql) { int verify; SSL *ssl= NULL; -#ifdef HAVE_OPENSSL_SESSION_TICKET +#ifdef HAVE_SSL_SESSION_CACHE MA_SSL_SESSION *session= ma_ssl_get_session(mysql); #endif pthread_mutex_lock(&LOCK_openssl_config); @@ -468,7 +470,7 @@ void *ma_ssl_init(MYSQL *mysql) if (!SSL_set_app_data(ssl, mysql)) goto error; -#ifdef HAVE_OPENSSL_SESSION_TICKET +#ifdef HAVE_SSL_SESSION_CACHE if (session) SSL_set_session(ssl, session->session); #endif diff --git a/plugins/pvio/pvio_socket.c b/plugins/pvio/pvio_socket.c index 9eff580c..71022f27 100644 --- a/plugins/pvio/pvio_socket.c +++ b/plugins/pvio/pvio_socket.c @@ -158,6 +158,38 @@ static int pvio_socket_end(void) return 0; } +my_bool pvio_socket_change_timeout(MARIADB_PVIO *pvio, enum enum_pvio_timeout type, int timeout) +{ + struct timeval tm; + struct st_pvio_socket *csock= NULL; + if (!pvio) + return 1; + if (!(csock= (struct st_pvio_socket *)pvio->data)) + return 1; + tm.tv_sec= timeout / 1000; + tm.tv_usec= (timeout % 1000) * 1000; + switch(type) + { + case PVIO_WRITE_TIMEOUT: +#ifndef _WIN32 + setsockopt(csock->socket, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tm, sizeof(tm)); +#else + setsockopt(csock->socket, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout, sizeof(int)); +#endif + break; + case PVIO_READ_TIMEOUT: +#ifndef _WIN32 + setsockopt(csock->socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tm, sizeof(tm)); +#else + setsockopt(csock->socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(int)); +#endif + break; + default: + break; + } + return 0; +} + /* {{{ pvio_socket_set_timeout */ /* set timeout value @@ -179,9 +211,13 @@ static int pvio_socket_end(void) */ my_bool pvio_socket_set_timeout(MARIADB_PVIO *pvio, enum enum_pvio_timeout type, int timeout) { + struct st_pvio_socket *csock= NULL; if (!pvio) return 1; + csock= (struct st_pvio_socket *)pvio->data; pvio->timeout[type]= (timeout > 0) ? timeout * 1000 : -1; + if (csock) + return pvio_socket_change_timeout(pvio, type, timeout); return 0; } /* }}} */ @@ -677,15 +713,13 @@ int pvio_socket_fast_send(MARIADB_PVIO *pvio) /* Setting IP_TOS is not recommended on Windows. See http://msdn.microsoft.com/en-us/library/windows/desktop/ms738586(v=vs.85).aspx */ -#ifndef _WIN32 -#ifdef IPTOS_THROUGHPUT +#if !defined(_WIN32) && defined(IPTOS_THROUGHPUT) { int tos = IPTOS_THROUGHPUT; r= setsockopt(csock->socket, IPPROTO_IP, IP_TOS, (const void *)&tos, sizeof(tos)); } -#endif /* IPTOS_THROUGHPUT */ -#endif +#endif /* !_WIN32 && IPTOS_THROUGHPUT */ if (!r) { int opt = 1; @@ -723,7 +757,6 @@ pvio_socket_connect_sync_or_async(MARIADB_PVIO *pvio, my_bool pvio_socket_connect(MARIADB_PVIO *pvio, MA_PVIO_CINFO *cinfo) { struct st_pvio_socket *csock= NULL; - struct timeval tm; if (!pvio || !cinfo) return 1; @@ -870,25 +903,17 @@ my_bool pvio_socket_connect(MARIADB_PVIO *pvio, MA_PVIO_CINFO *cinfo) goto error; } /* apply timeouts */ - if (pvio->timeout[PVIO_WRITE_TIMEOUT] > 0) + if (pvio->timeout[PVIO_CONNECT_TIMEOUT] > 0) { -#ifndef _WIN32 - tm.tv_sec= pvio->timeout[PVIO_WRITE_TIMEOUT] / 1000; - tm.tv_usec= pvio->timeout[PVIO_WRITE_TIMEOUT] % 1000; - setsockopt(csock->socket, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tm, sizeof(tm)); -#else - setsockopt(csock->socket, SOL_SOCKET, SO_SNDTIMEO, (const char *)&pvio->timeout[PVIO_WRITE_TIMEOUT], sizeof(int)); -#endif + pvio_socket_change_timeout(pvio, PVIO_READ_TIMEOUT, pvio->timeout[PVIO_CONNECT_TIMEOUT]); + pvio_socket_change_timeout(pvio, PVIO_WRITE_TIMEOUT, pvio->timeout[PVIO_CONNECT_TIMEOUT]); } - if (pvio->timeout[PVIO_READ_TIMEOUT] > 0) + else { -#ifndef _WIN32 - tm.tv_sec= pvio->timeout[PVIO_READ_TIMEOUT] / 1000; - tm.tv_usec= pvio->timeout[PVIO_READ_TIMEOUT] % 1000; - setsockopt(csock->socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tm, sizeof(tm)); -#else - setsockopt(csock->socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&pvio->timeout[PVIO_WRITE_TIMEOUT], sizeof(int)); -#endif + if (pvio->timeout[PVIO_WRITE_TIMEOUT] > 0) + pvio_socket_change_timeout(pvio, PVIO_WRITE_TIMEOUT, pvio->timeout[PVIO_WRITE_TIMEOUT]); + if (pvio->timeout[PVIO_READ_TIMEOUT] > 0) + pvio_socket_change_timeout(pvio, PVIO_READ_TIMEOUT, pvio->timeout[PVIO_READ_TIMEOUT]); } return 0; error: diff --git a/unittest/libmariadb/connection.c b/unittest/libmariadb/connection.c index a4e36341..2b88305e 100644 --- a/unittest/libmariadb/connection.c +++ b/unittest/libmariadb/connection.c @@ -49,8 +49,8 @@ static int test_conc66(MYSQL *my) rc= mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, "conc-66"); check_mysql_rc(rc, mysql); -// rc= mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, "./my.cnf"); -// check_mysql_rc(rc, mysql); + rc= mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, "./my.cnf"); + check_mysql_rc(rc, mysql); sprintf(query, "GRANT ALL ON %s.* TO 'conc66'@'%s' IDENTIFIED BY 'test\";#test'", schema, hostname); rc= mysql_query(my, query); @@ -63,6 +63,7 @@ static int test_conc66(MYSQL *my) diag("Error: %s", mysql_error(mysql)); return FAIL; } + sprintf(query, "DROP user conc66@%s", hostname); rc= mysql_query(my, query); @@ -660,6 +661,71 @@ int test_connection_timeout(MYSQL *my) return OK; } +int test_connection_timeout2(MYSQL *my) +{ + unsigned int timeout= 5; + time_t start, elapsed; + MYSQL *mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (unsigned int *)&timeout); + mysql_options(mysql, MYSQL_INIT_COMMAND, "set @a:=SLEEP(6)"); + start= time(NULL); + if (mysql_real_connect(mysql, hostname, username, password, schema, port, NULL, CLIENT_REMEMBER_OPTIONS)) + { + diag("timeout error expected"); + return FAIL; + } + elapsed= time(NULL) - start; + diag("elapsed: %lu", (unsigned long)elapsed); + mysql_close(mysql); + FAIL_IF(elapsed > 2 * timeout, "timeout ignored") + return OK; +} + +int test_connection_timeout3(MYSQL *my) +{ + unsigned int timeout= 5; + unsigned int read_write_timeout= 10; + int rc; + time_t start, elapsed; + MYSQL *mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (unsigned int *)&timeout); + mysql_options(mysql, MYSQL_OPT_READ_TIMEOUT, (unsigned int *)&read_write_timeout); + mysql_options(mysql, MYSQL_OPT_WRITE_TIMEOUT, (unsigned int *)&read_write_timeout); + mysql_options(mysql, MYSQL_INIT_COMMAND, "set @a:=SLEEP(6)"); + start= time(NULL); + if (mysql_real_connect(mysql, hostname, username, password, schema, port, NULL, CLIENT_REMEMBER_OPTIONS)) + { + diag("timeout error expected"); + elapsed= time(NULL) - start; + diag("elapsed: %lu", (unsigned long)elapsed); + return FAIL; + } + elapsed= time(NULL) - start; + diag("elapsed: %lu", (unsigned long)elapsed); + FAIL_IF(elapsed > timeout + 1, "timeout ignored") + + mysql_close(mysql); + mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (unsigned int *)&timeout); + mysql_options(mysql, MYSQL_OPT_READ_TIMEOUT, (unsigned int *)&read_write_timeout); + mysql_options(mysql, MYSQL_OPT_WRITE_TIMEOUT, (unsigned int *)&read_write_timeout); + + if (!mysql_real_connect(mysql, hostname, username, password, schema, port, NULL, CLIENT_REMEMBER_OPTIONS)) + { + diag("Error: %s", mysql_error(mysql)); + return FAIL; + } + + start= time(NULL); + rc= mysql_query(mysql, "SET @a:=SLEEP(12)"); + elapsed= time(NULL) - start; + diag("elapsed: %lu", (unsigned long)elapsed); + FAIL_IF(!rc, "timeout expected"); + mysql_close(mysql); + return OK; +} + + /* test should run with valgrind */ static int test_conc118(MYSQL *mysql) { @@ -862,6 +928,8 @@ struct my_tests_st my_tests[] = { {"test_conc21", test_conc21, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc26", test_conc26, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_connection_timeout", test_connection_timeout, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_connection_timeout2", test_connection_timeout2, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_connection_timeout3", test_connection_timeout3, TEST_CONNECTION_NONE, 0, NULL, NULL}, {NULL, NULL, 0, 0, NULL, NULL} }; diff --git a/unittest/libmariadb/ssl.c.in b/unittest/libmariadb/ssl.c.in index 579cfcf9..11a51a4c 100644 --- a/unittest/libmariadb/ssl.c.in +++ b/unittest/libmariadb/ssl.c.in @@ -182,8 +182,8 @@ static int test_conc95(MYSQL *my) if (!mysql_real_connect(mysql, hostname, "ssluser1", sslpw, schema, port, socketname, 0)) { + diag("could not establish x509 connection. Error: %s", mysql_error(mysql)); mysql_close(mysql); - diag("could not establish x509 connection"); return FAIL; } mysql_close(mysql);