diff --git a/include/ma_common.h b/include/ma_common.h index c24d2982..c5981366 100644 --- a/include/ma_common.h +++ b/include/ma_common.h @@ -91,6 +91,7 @@ typedef struct st_connection_handler struct st_mariadb_net_extension { enum enum_multi_status multi_status; + int extended_errno; }; struct st_mariadb_session_state diff --git a/include/mysql.h b/include/mysql.h index 843489e1..dcaf316d 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -140,6 +140,7 @@ extern const char *SQLSTATE_UNKNOWN; (a)->net.last_errno= 0;\ strcpy((a)->net.sqlstate, "00000");\ (a)->net.last_error[0]= '\0';\ + (a)->net.extension->extended_errno= 0;\ } #define MYSQL_COUNT_ERROR (~(unsigned long long) 0) diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index d43b68ce..43f7f1df 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -1020,7 +1020,7 @@ mysql_init(MYSQL *mysql) mysql->charset= mysql_find_charset_name(MARIADB_DEFAULT_CHARSET); mysql->methods= &MARIADB_DEFAULT_METHODS; strcpy(mysql->net.sqlstate, "00000"); - mysql->net.last_error[0]= mysql->net.last_errno= 0; + mysql->net.last_error[0]= mysql->net.last_errno= mysql->net.extension->extended_errno= 0; if (ENABLED_LOCAL_INFILE != LOCAL_INFILE_MODE_OFF) mysql->options.client_flag|= CLIENT_LOCAL_FILES; @@ -1199,9 +1199,39 @@ mysql_real_connect(MYSQL *mysql, const char *host, const char *user, return my; } } - +#ifndef HAVE_SCHANNEL return mysql->methods->db_connect(mysql, host, user, passwd, db, port, unix_socket, client_flag); +#else +/* + With older windows versions (prior Win 10) TLS connections periodically + fail with SEC_E_INVALID_TOKEN, SEC_E_BUFFER_TOO_SMALL or SEC_E_MESSAGE_ALTERED + error (see MDEV-13492). If the connect attempt returns on of these error codes + in mysql->net.extended_errno we will try to connect again (max. 3 times) +*/ +#define MAX_SCHANNEL_CONNECT_ATTEMPTS 3 + { + int ssl_retry= (mysql->options.use_ssl) ? MAX_SCHANNEL_CONNECT_ATTEMPTS : 1; + MYSQL *my= NULL; + while (ssl_retry) + { + if ((my= mysql->methods->db_connect(mysql, host, user, passwd, + db, port, unix_socket, client_flag))) + return my; + + switch (mysql->net.extension->extended_errno) { + case SEC_E_INVALID_TOKEN: + case SEC_E_BUFFER_TOO_SMALL: + case SEC_E_MESSAGE_ALTERED: + ssl_retry--; + break; + default: + return NULL; + } + } + return my; + } +#endif } MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, diff --git a/libmariadb/secure/ma_schannel.c b/libmariadb/secure/ma_schannel.c index 06c9705f..d1998c44 100644 --- a/libmariadb/secure/ma_schannel.c +++ b/libmariadb/secure/ma_schannel.c @@ -32,6 +32,10 @@ void ma_schannel_set_win_error(MARIADB_PVIO *pvio); void ma_schannel_set_sec_error(MARIADB_PVIO *pvio, DWORD ErrorNo) { MYSQL *mysql= pvio->mysql; + + if (ErrorNo != SEC_E_OK) + mysql->net.extension->extended_errno= ErrorNo; + switch(ErrorNo) { case SEC_E_ILLEGAL_MESSAGE: pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: The message received was unexpected or badly formatted");