1
0
mirror of https://github.com/mariadb-corporation/mariadb-connector-c.git synced 2025-08-08 14:02:17 +03:00

Merge remote-tracking branch 'origin/cio'

This commit is contained in:
Georg Richter
2015-10-18 13:53:09 +02:00
53 changed files with 6706 additions and 2652 deletions

View File

@@ -1,5 +1,6 @@
# CMakeLists.txt
# This is the LGPL libmariadb project.
PROJECT(mariadb-connector-c C)
@@ -21,9 +22,7 @@ ENDIF()
### Options ###
IF(NOT WIN32)
OPTION(WITH_MYSQLCOMPAT "creates libmysql* symbolic links" ON)
OPTION(WITH_OPENSSL "enables SSL support" ON)
ELSE()
OPTION(WITH_OPENSSL "enables SSL support" OFF)
OPTION(WITH_SIGNCODE "digitally sign files" OFF)
OPTION(WITH_RTC "enables run time checks for debug builds" OFF)
ENDIF()
@@ -36,6 +35,8 @@ IF(WITH_RTC)
SET(RTC_OPTIONS "/RTC1 /RTCc")
ENDIF()
INCLUDE(${CMAKE_SOURCE_DIR}/cmake/plugins.cmake)
IF(MSVC)
# Speedup system tests
INCLUDE(${CMAKE_SOURCE_DIR}/cmake/WindowsCache.cmake)
@@ -79,7 +80,7 @@ ENDIF()
SET(PROTOCOL_VERSION 10) # we adapted new password option from PHP's mysqlnd !
SET(MYSQL_CLIENT_VERSION_MAJOR "5")
SET(MYSQL_CLIENT_VERSION_MINOR "5")
SET(MYSQL_CLIENT_VERSION_PATCH "1")
SET(MYSQL_CLIENT_VERSION_PATCH "2")
SET(MYSQL_CLIENT_VERSION "${MYSQL_CLIENT_VERSION_MAJOR}.${MYSQL_CLIENT_VERSION_MINOR}.${MYSQL_CLIENT_VERSION_PATCH}")
MATH(EXPR MYSQL_VERSION_ID "${MYSQL_CLIENT_VERSION_MAJOR} * 10000 +
${MYSQL_CLIENT_VERSION_MINOR} * 100 +
@@ -91,12 +92,10 @@ IF(NOT MYSQL_UNIX_ADDR)
SET(MYSQL_UNIX_ADDR "/tmp/mysql.sock")
ENDIF()
IF(WITH_REMOTEIO)
ADD_DEFINITIONS(-DHAVE_REMOTEIO)
ENDIF()
INCLUDE("${CMAKE_SOURCE_DIR}/cmake/install.cmake")
# todo: we don't character sets in share - all is compiled in
SET(SHAREDIR "share")
SET(DEFAULT_CHARSET_HOME "${CMAKE_INSTALL_PREFIX}")
SET(PLUGINDIR "${CMAKE_INSTALL_PREFIX}/lib/plugin")
@@ -111,29 +110,22 @@ ENDIF()
IF(UNIX)
SEARCH_LIBRARY(DEFAULT_LIB inet_ntoa "c")
IF(NOT DEFAULT_LIB)
SEARCH_LIBRARY(LIBNSL inet_ntoa "nsl_r;nsl")
ENDIF()
SEARCH_LIBRARY(DEFAULT_LIB bind "c")
IF(NOT DEFAULT_LIB)
SEARCH_LIBRARY(LIBBIND bind "bind;socket")
ENDIF()
SEARCH_LIBRARY(DEFAULT_LIB setsockopt "c")
IF(NOT DEFAULT_LIB)
SEARCH_LIBRARY(LIBSOCKET setsockopt "socket")
ENDIF()
SEARCH_LIBRARY(LIBDL dlopen "dl")
SEARCH_LIBRARY(LIBM floor m)
# SEARCH_LIBRARY(LIBICONV iconv iconv)
SEARCH_LIBRARY(LIBPTHREAD pthread_getspecific "pthread;pthreads")
SET(EXTRA_LIBS "${LIBNSL}" "${LIBBIND}" "${LIBSOCKET}" "${LIBDL}" "${LIBM}" "${LIBPTHREAD}")
SET(EXTRA_LIBS "${LIBNSL}" "${LIBBIND}" "${LIBSOCKET}")
FIND_PACKAGE(Threads)
SET(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${LIBNSL} ${LIBBIND} ${LIBICONV}
${LIBSOCKET} ${LIBDL} ${LIBM} ${LIBPTHREAD})
#remove possible dups from required libraries
LIST(LENGTH SYS_LIBS rllength)
LIST(LENGTH CMAKE_REQUIRED_LIBRARIES rllength)
IF(${rllength} GREATER 0)
LIST(REMOVE_DUPLICATES SYS_LIBS)
LIST(REMOVE_DUPLICATES CMAKE_REQUIRED_LIBRARIES)
ENDIF()
SET(CMAKE_REQUIRED_LIBRARIES ${SYS_LIBS})
ENDIF()
@@ -159,20 +151,37 @@ INCLUDE(${CMAKE_SOURCE_DIR}/cmake/CheckFunctions.cmake)
# check for various types
INCLUDE(${CMAKE_SOURCE_DIR}/cmake/CheckTypes.cmake)
# Check for OpenSSL
IF(WITH_OPENSSL)
IF(WITH_SSL STREQUAL "OPENSSL")
FIND_PACKAGE(OpenSSL)
IF(OPENSSL_FOUND)
ADD_DEFINITIONS(-DHAVE_OPENSSL)
INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES})
IF(OPENSSL_CRYPTO_LIBRARIES)
SET(SSL_LIBRARIES ${SSL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES})
ENDIF()
ADD_DEFINITIONS(-DHAVE_OPENSSL -DHAVE_SSL)
SET(SSL_SOURCES "${CMAKE_SOURCE_DIR}/libmariadb/secure/openssl.c")
SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES})
ELSE()
MESSAGE(FATAL_ERROR "OpenSSL not found. Please install OpenSSL or disable SSL support via option -DWITH_OPENSSL=Off")
MESSAGE(FATAL "OpenSSL not found")
ENDIF()
ENDIF()
IF(WITH_SSL STREQUAL "GNUTLS")
FIND_PACKAGE(GnuTLS)
IF(GNUTLS_FOUND)
ADD_DEFINITIONS(-DHAVE_GNUTLS -DHAVE_SSL)
SET(SSL_SOURCES "${CMAKE_SOURCE_DIR}/libmariadb/secure/gnutls.c")
SET(SSL_LIBRARIES ${GNUTLS_LIBRARY})
ELSE()
MESSAGE(FATAL "GnuTLS not found")
ENDIF()
ENDIF()
IF(WIN32)
IF(WITH_SSL STREQUAL "SCHANNEL")
MESSAGE(STATUS "SSL_TYPE ${SSL_TYPE}")
ADD_DEFINITIONS(-DHAVE_SCHANNEL -DHAVE_SSL)
SET(SSL_SOURCES "${CMAKE_SOURCE_DIR}/libmariadb/secure/schannel.c" "${CMAKE_SOURCE_DIR}/libmariadb/secure/ma_schannel.c")
INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/plugins/cio/")
ENDIF()
ENDIF()
MARK_AS_ADVANCED(SSL_SOURCES)
IF(WITH_SQLITE)
ADD_DEFINITIONS(-DHAVE_SQLITE)
@@ -201,7 +210,7 @@ ELSE()
SET(SYSTEM_LIBS ${SYSTEM_LIBS} ${ICONV_LIBRARIES})
ENDIF()
ENDIF()
IF(OPENSSL_FOUND)
IF(WITH_SSL)
SET(SYSTEM_LIBS ${SYSTEM_LIBS} ${SSL_LIBRARIES})
ENDIF()
@@ -212,6 +221,8 @@ IF(NOT WIN32)
ADD_SUBDIRECTORY(mariadb_config)
ENDIF()
ADD_SUBDIRECTORY(client)
IF(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/unittest)
ADD_SUBDIRECTORY(unittest/mytap)
ADD_SUBDIRECTORY(unittest/libmariadb)
@@ -280,7 +291,7 @@ MESSAGE(STATUS "CPack generation: ${CPACK_GENERATOR}")
IF(CLIENT_DOCS)
MESSAGE(STATUS "Documentation included from ${CLIENT_DOCS}")
ENDIF()
MESSAGE(STATUS "SSL support: ${WITH_OPENSSL}")
MESSAGE(STATUS "SSL support: ${WITH_SSL} Libs: ${SSL_LIBRARIES}")
MESSAGE(STATUS "Experimental Sqlite support: ${WITH_SQLITE}")
IF(WITH_EXTERNAL_ZLIB)
MESSAGE(STATUS "Zlib support: ${WITH_EXTERNAL_ZLIB}")

2
client/CMakeLists.txt Normal file
View File

@@ -0,0 +1,2 @@
ADD_EXECUTABLE(mariadb_client_plugin_info ma_plugin_info.c)
TARGET_LINK_LIBRARIES(mariadb_client_plugin_info mariadbclient)

166
client/ma_plugin_info.c Normal file
View File

@@ -0,0 +1,166 @@
#include <my_global.h>
#include <my_sys.h>
#include <mysql.h>
#include <mysql/client_plugin.h>
#include <getopt.h>
#include <stdio.h>
#include <my_dir.h>
#include <string.h>
static struct option long_options[]=
{
{"builtin", no_argument, 0, 'b'},
{"dynamic", no_argument, 0, 'd'},
{"all", no_argument, 0, 'a'},
{"plugin", 1, 0, 'p'},
{NULL, 0, 0, 0}
};
struct st_plugin_type
{
int type;
char *typename;
};
static struct st_plugin_type plugin_types[]=
{
{MYSQL_CLIENT_AUTHENTICATION_PLUGIN, "authentication"},
{MARIADB_CLIENT_CIO_PLUGIN, "client/server protocol"},
{MARIADB_CLIENT_TRACE_PLUGIN, "trace"},
{MARIADB_CLIENT_REMOTEIO_PLUGIN, "remote file access"},
{MARIADB_CLIENT_CONNECTION_PLUGIN, "connection handler"},
{0, "unknown"}
};
static void usage(void)
{
int i=0;
puts("Copyright 2015 MariaDB Corporation AB");
puts("Show client plugin information for MariaDB Connector/C.");
printf("Usage: %s [OPTIONS] [plugin_name]\n", my_progname);
while (long_options[i].name)
{
printf(" --%-12s -%c\n", long_options[i].name, long_options[i].val);
i++;
}
}
static char *get_type_name(int type)
{
int i=0;
while (plugin_types[i].type)
{
if (type== plugin_types[i].type)
return plugin_types[i].typename;
i++;
}
return plugin_types[i].typename;
}
static void show_plugin_info(struct st_mysql_client_plugin *plugin, my_bool builtin)
{
printf("Type: %s\n", get_type_name(plugin->type));
printf("Name: %s\n", plugin->name);
printf("Desc: %s\n", plugin->desc);
printf("Author: %s\n", plugin->author);
printf("License: %s\n", plugin->license);
printf("Version: %d.%d.%d\n", plugin->version[0], plugin->version[1], plugin->version[2]);
printf("API Version: 0x%04X\n", plugin->interface_version);
printf("Build type: %s\n", builtin ? "builtin" : "dynamic");
printf("\n");
}
static void show_builtin()
{
struct st_mysql_client_plugin **builtin;
for (builtin= mysql_client_builtins; *builtin; builtin++)
show_plugin_info(*builtin, TRUE);
}
static void show_file(char *filename)
{
char dlpath[FN_REFLEN+1];
void *sym, *dlhandle;
struct st_mysql_client_plugin *plugin;
char *env_plugin_dir= getenv("MARIADB_PLUGIN_DIR");
char *has_so_ext= strstr(filename, SO_EXT);
if (!strchr(filename, FN_LIBCHAR))
strxnmov(dlpath, sizeof(dlpath) - 1,
(env_plugin_dir) ? env_plugin_dir : PLUGINDIR, "/", filename, has_so_ext ? "" : SO_EXT, NullS);
else
strcpy(dlpath, filename);
if ((dlhandle= dlopen((const char *)dlpath, RTLD_NOW)))
{
if (sym= dlsym(dlhandle, plugin_declarations_sym))
{
plugin= (struct st_mysql_client_plugin *)sym;
show_plugin_info(plugin, 0);
}
dlclose(dlhandle);
}
}
static void show_dynamic()
{
MY_DIR *dir= NULL;
int i;
char *env_plugin_dir= getenv("MARIADB_PLUGIN_DIR");
dir= my_dir(env_plugin_dir ? env_plugin_dir : PLUGINDIR, 0);
if (!dir->number_off_files)
{
printf("No plugins found in %s\n", env_plugin_dir ? env_plugin_dir : PLUGINDIR);
return;
}
for (i=0; i < dir->number_off_files; i++)
{
char *p= strstr(dir->dir_entry[i].name, SO_EXT);
if (p)
show_file(dir->dir_entry[i].name);
}
if (dir)
my_dirend(dir);
}
int main(int argc, char *argv[])
{
int option_index= 0;
int c;
my_progname= argv[0];
if (argc <= 1)
{
usage();
exit(1);
}
c= getopt_long(argc, argv, "bdap", long_options, &option_index);
switch(c) {
case 'a': /* all */
show_builtin();
show_dynamic();
break;
case 'b': /* builtin */
show_builtin();
break;
case 'd': /* dynamic */
show_dynamic();
break;
case 'p':
if (argc > 2)
{
show_file(argv[2]);
break;
}
default:
usage();
exit(1);
}
}

67
cmake/plugins.cmake Normal file
View File

@@ -0,0 +1,67 @@
# plugin configuration
MACRO(REGISTER_PLUGIN name source struct type allow)
IF(PLUGINS)
LIST(REMOVE_ITEM PLUGINS ${name})
ENDIF()
SET(${name}_PLUGIN_SOURCE ${source})
SET(${name}_PLUGIN_TYPE ${type})
SET(${name}_PLUGIN_STRUCT ${struct})
SET(${name}_PLUGIN_SOURCE ${source})
SET(${name}_PLUGIN_CHG ${allow})
SET(PLUGINS ${PLUGINS} "${name}")
ENDMACRO()
MARK_AS_ADVANCED(PLUGINS)
# CIO
REGISTER_PLUGIN("SOCKET" "${CMAKE_SOURCE_DIR}/plugins/cio/cio_socket.c" "cio_socket_plugin" "STATIC" 0)
IF(WIN32)
REGISTER_PLUGIN("NPIPE" "${CMAKE_SOURCE_DIR}/plugins/cio/cio_npipe.c" "cio_npipe_plugin" "DYNAMIC" 1)
REGISTER_PLUGIN("SHMEM" "${CMAKE_SOURCE_DIR}/plugins/cio/cio_shmem.c" "cio_shmem_plugin" "DYNAMIC" 1)
ENDIF()
# AUTHENTICATION
REGISTER_PLUGIN("AUTH_NATIVE" "${CMAKE_SOURCE_DIR}/plugins/auth/my_auth.c" "native_password_client_plugin" "STATIC" 0)
REGISTER_PLUGIN("AUTH_OLDPASSWORD" "${CMAKE_SOURCE_DIR}/plugins/auth/my_auth.c" "old_password_client_plugin" "STATIC" 0)
REGISTER_PLUGIN("AUTH_DIALOG" "${CMAKE_SOURCE_DIR}/plugins/auth/dialog.c" "auth_dialog_plugin" "DYNAMIC" 1)
REGISTER_PLUGIN("AUTH_CLEARTEXT" "${CMAKE_SOURCE_DIR}/plugins/auth/mariadb_clear_text.c" "auth_cleartext_plugin" "DYNAMIC" 1)
#Remote_IO
REGISTER_PLUGIN("REMOTEIO" "${CMAKE_SOURCE_DIR}/plugins/io/remote_io.c" "remote_io_plugin" "DYNAMIC" 1)
#Trace
REGISTER_PLUGIN("TRACE_EXAMPLE" "${CMAKE_SOURCE_DIR}/plugins/trace/trace_example.c" "trace_example_plugin" "DYNAMIC" 1)
#Connection
REGISTER_PLUGIN("REPLICATION" "${CMAKE_SOURCE_DIR}/plugins/connection/replication.c" "connection_replication_plugin" "STATIC" 1)
# Allow registration of additional plugins
IF(PLUGIN_CONF_FILE)
INCLUDE(${PLUGIN_CONF_FILE})
ENDIF()
SET(LIBMARIADB_SOURCES "")
MESSAGE(STATUS "Plugin configuration")
FOREACH(PLUGIN ${PLUGINS})
IF(WITH_${PLUGIN}_PLUGIN AND ${${PLUGIN}_PLUGIN_CHG} GREATER 0)
SET(${PLUGIN}_PLUGIN_TYPE ${WITH_${PLUGIN}_PLUGIN})
ENDIF()
IF(${PLUGIN}_PLUGIN_TYPE MATCHES "STATIC")
SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} ${${PLUGIN}_PLUGIN_SOURCE})
SET(EXTERNAL_PLUGINS "${EXTERNAL_PLUGINS}extern struct st_mysql_client_plugin ${${PLUGIN}_PLUGIN_STRUCT};\n")
SET(BUILTIN_PLUGINS "${BUILTIN_PLUGINS}(struct st_mysql_client_plugin *)&${${PLUGIN}_PLUGIN_STRUCT},\n")
ENDIF()
MESSAGE(STATUS "${PLUGIN}: ${${PLUGIN}_PLUGIN_TYPE}")
MARK_AS_ADVANCED(${PLUGIN}_PLUGIN_TYPE)
ENDFOREACH()
# since some files contain multiple plugins, remove duplicates from source files
LIST(REMOVE_DUPLICATES LIBMARIADB_SOURCES)
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/libmariadb/client_plugin.c.in
${CMAKE_BINARY_DIR}/libmariadb/client_plugin.c)
MARK_AS_ADVANCED(LIBMARIADB_SOURCES)

View File

@@ -25,6 +25,7 @@ extern "C" {
#endif
void init_client_errs(void);
extern const char *client_errors[]; /* Error messages */
extern const char *mariadb_client_errors[]; /* Error messages */
#ifdef __cplusplus
}
#endif
@@ -32,11 +33,10 @@ extern const char *client_errors[]; /* Error messages */
#define CR_MIN_ERROR 2000 /* For easier client code */
#define CR_MAX_ERROR 2999
#if defined(OS2) && defined( MYSQL_SERVER)
#define CER(X) client_errors[(X)-CR_MIN_ERROR]
#else
#define CER_MIN_ERROR 5000
#define CER_MAX_ERROR 5999
#define CER(X) mariadb_client_errors[(X)-CER_MIN_ERROR]
#define ER(X) client_errors[(X)-CR_MIN_ERROR]
#endif
#define CLIENT_ERRMAP 2 /* Errormap used by my_error() */
#define CR_UNKNOWN_ERROR 2000
@@ -66,6 +66,11 @@ extern const char *client_errors[]; /* Error messages */
#define CR_PARAMS_NOT_BOUND 2031
#define CR_INVALID_PARAMETER_NO 2034
#define CR_UNSUPPORTED_PARAM_TYPE 2036
#define CR_SHARED_MEMORY_CONNECTION 2037
#define CR_SHARED_MEMORY_CONNECT_ERROR 2038
#define CR_SECURE_AUTH 2049
#define CR_NO_DATA 2051
#define CR_NO_STMT_METADATA 2052
@@ -76,6 +81,9 @@ extern const char *client_errors[]; /* Error messages */
#define CR_ALREADY_CONNECTED 2059
#define CR_PLUGIN_FUNCTION_NOT_SUPPORTED 2060
#define CR_EVENT_CREATE_FAILED 5000
#define CR_BIND_ADDR_FAILED 5001
#define SQLSTATE_UNKNOWN "HY000"
#endif

120
include/ma_cio.h Normal file
View File

@@ -0,0 +1,120 @@
#ifndef _ma_cio_h_
#define _ma_cio_h_
#define cio_defined
#ifdef HAVE_SSL
#include <ma_ssl.h>
#endif
#define CIO_SET_ERROR if (cio->set_error) \
cio->set_error
#define CIO_READ_AHEAD_CACHE_SIZE 16384
#define CIO_READ_AHEAD_CACHE_MIN_SIZE 2048
#define CIO_EINTR_TRIES 2
struct st_ma_cio_methods;
typedef struct st_ma_cio_methods CIO_METHODS;
#ifndef ssl_defined
#define ssl_defined
struct st_ma_cio_ssl;
typedef struct st_ma_cio_ssl MARIADB_SSL;
#endif
enum enum_cio_timeout {
CIO_CONNECT_TIMEOUT= 0,
CIO_READ_TIMEOUT,
CIO_WRITE_TIMEOUT
};
enum enum_cio_io_event
{
VIO_IO_EVENT_READ,
VIO_IO_EVENT_WRITE,
VIO_IO_EVENT_CONNECT
};
enum enum_cio_type {
CIO_TYPE_UNIXSOCKET= 0,
CIO_TYPE_SOCKET,
CIO_TYPE_NAMEDPIPE
};
enum enum_cio_operation {
CIO_READ= 0,
CIO_WRITE=1
};
struct st_cio_callback;
typedef struct st_cio_callback {
void (*callback)(MYSQL *mysql, uchar *buffer, size_t size);
struct st_cio_callback *next;
} CIO_CALLBACK;
struct st_ma_cio {
void *data;
/* read ahead cache */
uchar *cache;
uchar *cache_pos;
size_t cache_size;
enum enum_cio_type type;
int timeout[3];
int ssl_type; /* todo: change to enum (ssl plugins) */
MARIADB_SSL *cssl;
MYSQL *mysql;
struct mysql_async_context *async_context; /* For non-blocking API */
CIO_METHODS *methods;
FILE *fp;
void (*set_error)(MYSQL *mysql, unsigned int error_nr, const char *sqlstate, const char *format, ...);
void (*callback)(MARIADB_CIO *cio, my_bool is_read, const char *buffer, size_t length);
};
typedef struct st_ma_cio_cinfo
{
const char *host;
const char *unix_socket;
int port;
enum enum_cio_type type;
MYSQL *mysql;
} MA_CIO_CINFO;
struct st_ma_cio_methods
{
my_bool (*set_timeout)(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout);
int (*get_timeout)(MARIADB_CIO *cio, enum enum_cio_timeout type);
size_t (*read)(MARIADB_CIO *cio, const uchar *buffer, size_t length);
size_t (*async_read)(MARIADB_CIO *cio, const uchar *buffer, size_t length);
size_t (*write)(MARIADB_CIO *cio, const uchar *buffer, size_t length);
size_t (*async_write)(MARIADB_CIO *cio, const uchar *buffer, size_t length);
int (*wait_io_or_timeout)(MARIADB_CIO *cio, my_bool is_read, int timeout);
my_bool (*blocking)(MARIADB_CIO *cio, my_bool value, my_bool *old_value);
my_bool (*connect)(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo);
my_bool (*close)(MARIADB_CIO *cio);
int (*fast_send)(MARIADB_CIO *cio);
int (*keepalive)(MARIADB_CIO *cio);
my_bool (*get_handle)(MARIADB_CIO *cio, void *handle);
my_bool (*is_blocking)(MARIADB_CIO *cio);
my_bool (*is_alive)(MARIADB_CIO *cio);
};
/* Function prototypes */
MARIADB_CIO *ma_cio_init(MA_CIO_CINFO *cinfo);
void ma_cio_close(MARIADB_CIO *cio);
size_t ma_cio_cache_read(MARIADB_CIO *cio, uchar *buffer, size_t length);
size_t ma_cio_read(MARIADB_CIO *cio, const uchar *buffer, size_t length);
size_t ma_cio_write(MARIADB_CIO *cio, const uchar *buffer, size_t length);
int ma_cio_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type);
my_bool ma_cio_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout);
int ma_cio_fast_send(MARIADB_CIO *cio);
int ma_cio_keepalive(MARIADB_CIO *cio);
my_socket ma_cio_get_socket(MARIADB_CIO *cio);
my_bool ma_cio_is_blocking(MARIADB_CIO *cio);
my_bool ma_cio_blocking(MARIADB_CIO *cio, my_bool block, my_bool *previous_mode);
my_bool ma_cio_is_blocking(MARIADB_CIO *cio);
int ma_cio_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout);
my_bool ma_cio_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo);
my_bool ma_cio_is_alive(MARIADB_CIO *cio);
#endif /* _ma_cio_h_ */

View File

@@ -1,45 +0,0 @@
/************************************************************************************
Copyright (C) 2012 Monty Program AB
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not see <http://www.gnu.org/licenses>
or write to the Free Software Foundation, Inc.,
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
Part of this code includes code from the PHP project which
is freely available from http://www.php.net
*************************************************************************************/
#ifndef _ma_secure_h_
#define _ma_secure_h_
#ifdef HAVE_OPENSSL
#include <mysql.h>
#include <openssl/ssl.h> /* SSL and SSL_CTX */
#include <openssl/err.h> /* error reporting */
#include <openssl/conf.h>
struct MYSQL;
size_t my_ssl_read(Vio *vio, uchar* buf, size_t size);
int my_ssl_close(Vio *vio);
size_t my_ssl_write(Vio *vio, const uchar* buf, size_t size);
SSL *my_ssl_init(MYSQL *mysql);
int my_ssl_connect(SSL *ssl);
int my_ssl_verify_server_cert(SSL *ssl);
int ma_ssl_verify_fingerprint(SSL *ssl);
int my_ssl_start(MYSQL *mysql);
void my_ssl_end(void);
#endif /* HAVE_OPENSSL */
#endif /* _ma_secure_h_ */

138
include/ma_ssl.h Normal file
View File

@@ -0,0 +1,138 @@
#ifndef _ma_ssl_h_
#define _ma_ssl_h_
enum enum_cio_ssl_type {
SSL_TYPE_DEFAULT=0,
#ifdef _WIN32
SSL_TYPE_SCHANNEL,
#endif
SSL_TYPE_OPENSSL,
SSL_TYPE_GNUTLS
};
typedef struct st_ma_cio_ssl {
void *data;
MARIADB_CIO *cio;
void *ssl;
} MARIADB_SSL;
/* Function prototypes */
/* ma_ssl_start
initializes the ssl library
Parameter:
errmsg pointer to error message buffer
errmsg_len length of error message buffer
Returns:
0 success
1 if an error occured
Notes:
On success the global variable ma_ssl_initialized will be set to 1
*/
int ma_ssl_start(char *errmsg, size_t errmsg_len);
/* ma_ssl_end
unloads/deinitializes ssl library and unsets global variable
ma_ssl_initialized
*/
void ma_ssl_end(void);
/* ma_ssl_init
creates a new SSL structure for a SSL connection and loads
client certificates
Parameters:
MYSQL a mysql structure
Returns:
void * a pointer to internal SSL structure
*/
void * ma_ssl_init(MYSQL *mysql);
/* ma_ssl_connect
performs SSL handshake
Parameters:
MARIADB_SSL MariaDB SSL container
Returns:
0 success
1 error
*/
my_bool ma_ssl_connect(MARIADB_SSL *cssl);
/* ma_ssl_read
reads up to length bytes from socket
Parameters:
cssl MariaDB SSL container
buffer read buffer
length buffer length
Returns:
0-n bytes read
-1 if an error occured
*/
size_t ma_ssl_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length);
/* ma_ssl_write
write buffer to socket
Parameters:
cssl MariaDB SSL container
buffer write buffer
length buffer length
Returns:
0-n bytes written
-1 if an error occured
*/
size_t ma_ssl_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length);
/* ma_ssl_close
closes SSL connection and frees SSL structure which was previously
created by ma_ssl_init call
Parameters:
MARIADB_SSL MariaDB SSL container
Returns:
0 success
1 error
*/
my_bool ma_ssl_close(MARIADB_SSL *cssl);
/* ma_ssl_verify_server_cert
validation check of server certificate
Parameter:
MARIADB_SSL MariaDB SSL container
Returns:
ß success
1 error
*/
int ma_ssl_verify_server_cert(MARIADB_SSL *cssl);
/* ma_ssl_get_cipher
returns cipher for current ssl connection
Parameter:
MARIADB_SSL MariaDB SSL container
Returns:
cipher in use or
NULL on error
*/
const char *ma_ssl_get_cipher(MARIADB_SSL *ssl);
/* ma_ssl_get_finger_print
returns SHA1 finger print of server certificate
Parameter:
MARIADB_SSL MariaDB SSL container
fp buffer for fingerprint
fp_len buffer length
Returns:
actual size of finger print
*/
unsigned int ma_ssl_get_finger_print(MARIADB_SSL *cssl, unsigned char *fp, unsigned int fp_len);
/* Function prototypes */
MARIADB_SSL *ma_cio_ssl_init(MYSQL *mysql);
my_bool ma_cio_ssl_connect(MARIADB_SSL *cssl);
size_t ma_cio_ssl_read(MARIADB_SSL *cssl, const uchar *buffer, size_t length);
size_t ma_cio_ssl_write(MARIADB_SSL *cssl, const uchar *buffer, size_t length);
my_bool ma_cio_ssl_close(MARIADB_SSL *cssl);
int ma_cio_ssl_verify_server_cert(MARIADB_SSL *cssl);
const char *ma_cio_ssl_cipher(MARIADB_SSL *cssl);
my_bool ma_cio_ssl_check_fp(MARIADB_SSL *cssl, const char *fp, const char *fp_list);
my_bool ma_cio_start_ssl(MARIADB_CIO *cio);
#endif /* _ma_ssl_h_ */

View File

@@ -212,7 +212,8 @@ extern unsigned int mariadb_deinitialize_ssl;
/* MariaDB Connector/C specific */
MYSQL_DATABASE_DRIVER=7000,
MARIADB_OPT_SSL_FP, /* single finger print for server certificate verification */
MARIADB_OPT_SSL_FP_LIST /* finger print white list for server certificate verification */
MARIADB_OPT_SSL_FP_LIST, /* finger print white list for server certificate verification */
MARIADB_OPT_CONNECTION_READ_ONLY
};
enum mysql_status { MYSQL_STATUS_READY,
@@ -231,8 +232,6 @@ extern unsigned int mariadb_deinitialize_ssl;
MYSQL_PROTOCOL_PIPE, MYSQL_PROTOCOL_MEMORY
};
struct st_mysql_options_extension;
struct st_mysql_options {
unsigned int connect_timeout, read_timeout, write_timeout;
unsigned int port, protocol;
@@ -251,7 +250,7 @@ struct st_mysql_options {
my_bool compress,named_pipe;
my_bool unused_1, unused_2, unused_3, unused_4;
enum mysql_option methods_to_use;
char *client_ip;
char *bind_address;
my_bool secure_auth;
my_bool report_data_truncation;
/* function pointers for local infile support */
@@ -627,6 +626,7 @@ struct st_mysql_methods {
int (*db_stmt_fetch)(MYSQL_STMT *stmt, unsigned char **row);
int (*db_stmt_fetch_to_bind)(MYSQL_STMT *stmt, unsigned char *row);
void (*db_stmt_flush_unbuffered)(MYSQL_STMT *stmt);
void (*set_error)(MYSQL *mysql, unsigned int error_nr, const char *sqlstate, const char *format, ...);
};
/* synonyms/aliases functions */

View File

@@ -31,25 +31,33 @@
#include <stdlib.h>
#endif
#ifndef PLUGINDIR
#define PLUGINDIR "lib/plugin"
#endif
/* known plugin types */
#define MYSQL_CLIENT_DB_PLUGIN 0
#define MYSQL_CLIENT_reserved 1
#define MYSQL_CLIENT_AUTHENTICATION_PLUGIN 2
#define MYSQL_CLIENT_reserved22 3
#define MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION 0x0100
#define MYSQL_CLIENT_DB_PLUGIN_INTERFACE_VERSION 0x0100
#define plugin_declarations_sym "_mysql_client_plugin_declaration_"
#ifdef HAVE_REMOTEIO
#define MYSQL_CLIENT_REMOTEIO_PLUGIN 4
#define MYSQL_CLIENT_MAX_PLUGINS 5
#define MYSQL_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION 0x0100
#else
#define MYSQL_CLIENT_MAX_PLUGINS 4
#endif
/* known plugin types */
#define MYSQL_CLIENT_PLUGIN_RESERVED 0
#define MYSQL_CLIENT_PLUGIN_RESERVED2 1
#define MYSQL_CLIENT_AUTHENTICATION_PLUGIN 2 /* authentication */
#define MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION 0x0100
#define MYSQL_CLIENT_MAX_PLUGINS 3
/* Connector/C specific plugin types */
#define MARIADB_CLIENT_REMOTEIO_PLUGIN 100 /* communication IO */
#define MARIADB_CLIENT_CIO_PLUGIN 101
#define MARIADB_CLIENT_TRACE_PLUGIN 102
#define MARIADB_CLIENT_CONNECTION_PLUGIN 103
#define MARIADB_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION 0x0100
#define MARIADB_CLIENT_CIO_PLUGIN_INTERFACE_VERSION 0x0100
#define MARIADB_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION 0x0100
#define MARIADB_CLIENT_CONNECTION_PLUGIN_INTERFACE_VERSION 0x0100
#define MARIADB_CLIENT_MAX_PLUGINS 4
#define mysql_declare_client_plugin(X) \
struct st_mysql_client_plugin_ ## X \
@@ -66,9 +74,9 @@
const char *author; \
const char *desc; \
unsigned int version[3]; \
const char *license; \
int (*init)(char *, size_t, int, va_list); \
int (*deinit)(void);
int (*deinit)();
struct st_mysql_client_plugin
{
MYSQL_CLIENT_PLUGIN_HEADER
@@ -76,16 +84,33 @@ struct st_mysql_client_plugin
struct st_mysql;
/********* database api plugin specific declarations **********/
typedef struct st_mariadb_client_plugin_DB
/********* connection handler plugin specific declarations **********/
typedef struct st_ma_connection_plugin
{
MYSQL_CLIENT_PLUGIN_HEADER
/* functions */
struct st_mysql_methods *methods;
} MARIADB_DB_PLUGIN;
MYSQL *(*connect)(MYSQL *mysql, const char *host, const char *user, const char *passwd,
const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag);
void (*close)(MYSQL *mysql);
int (*options)(MYSQL *mysql, enum mysql_option, void *arg);
int (*set_connection)(MYSQL *mysql,enum enum_server_command command, const char *arg,
size_t length, my_bool skipp_check, void *opt_arg);
} MARIADB_CONNECTION_PLUGIN;
#define MARIADB_DB_DRIVER(a) ((a)->ext_db)
/******************* Communication IO plugin *****************/
#include <ma_cio.h>
#ifdef HAVE_SSL
#include <ma_ssl.h>
#endif
typedef struct st_mariadb_client_plugin_CIO
{
MYSQL_CLIENT_PLUGIN_HEADER
struct st_ma_cio_methods *methods;
} MARIADB_CIO_PLUGIN;
/******** authentication plugin specific declarations *********/
#include <mysql/plugin_auth_common.h>
@@ -95,6 +120,11 @@ struct st_mysql_client_plugin_AUTHENTICATION
int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, struct st_mysql *mysql);
};
/******** trace plugin *******/
struct st_mysql_client_plugin_TRACE
{
MYSQL_CLIENT_PLUGIN_HEADER
};
/**
type of the mysql_authentication_dialog_ask function

View File

@@ -1,40 +1,37 @@
/* Copyright (C) 2012 MariaDB Services and Kristian Nielsen
2015 MariaDB Corporation
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program 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 of the License.
This library is distributed in the hope that it will be useful,
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
Library General Public License for more details.
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 Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA */
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* Common definitions for MariaDB non-blocking client library. */
#ifndef MYSQL_ASYNC_H
#define MYSQL_ASYNC_H
extern int my_connect_async(struct mysql_async_context *b, my_socket fd,
extern int my_connect_async(MARIADB_CIO *cio,
const struct sockaddr *name, uint namelen,
int vio_timeout);
extern ssize_t my_recv_async(struct mysql_async_context *b, int fd,
unsigned char *buf, size_t size, int timeout);
extern ssize_t my_send_async(struct mysql_async_context *b, int fd,
extern ssize_t my_recv_async(MARIADB_CIO *cio,
const unsigned char *buf, size_t size, int timeout);
extern ssize_t my_send_async(MARIADB_CIO *cio,
const unsigned char *buf, size_t size,
int timeout);
extern my_bool my_io_wait_async(struct mysql_async_context *b,
enum enum_vio_io_event event, int timeout);
#ifdef HAVE_OPENSSL
extern int my_ssl_read_async(struct mysql_async_context *b, SSL *ssl,
enum enum_cio_io_event event, int timeout);
#ifdef HAVE_SSL
extern int my_ssl_read_async(struct mysql_async_context *b, MARIADB_SSL *ssl,
void *buf, int size);
extern int my_ssl_write_async(struct mysql_async_context *b, SSL *ssl,
extern int my_ssl_write_async(struct mysql_async_context *b, MARIADB_SSL *ssl,
const void *buf, int size);
#endif

View File

@@ -209,14 +209,10 @@ enum enum_server_command
#define NET_WRITE_TIMEOUT 60 /* Timeout on write */
#define NET_WAIT_TIMEOUT 8*60*60 /* Wait for new query */
#ifndef Vio_defined
#define Vio_defined
#ifdef HAVE_VIO
class Vio; /* Fill Vio class in C++ */
#else
struct st_vio; /* Only C */
typedef struct st_vio Vio;
#endif
#ifndef cio_defined
#define cio_defined
struct st_ma_cio;
typedef struct st_ma_cio MARIADB_CIO;
#endif
#define MAX_CHAR_WIDTH 255 /* Max length for a CHAR colum */
@@ -231,9 +227,17 @@ typedef struct st_vio Vio;
#define MAX_INT_WIDTH 10
#define MAX_BIGINT_WIDTH 20
struct st_ma_connection_plugin;
typedef struct st_connection_handler
{
struct st_ma_connection_plugin *plugin;
void *data;
my_bool free_data;
} MA_CONNECTION_HANDLER;
typedef struct st_net {
Vio *vio;
MARIADB_CIO *cio;
unsigned char *buff;
unsigned char *buff_end,*write_pos,*read_pos;
my_socket fd; /* For Perl DBI/dbd */
@@ -249,11 +253,11 @@ typedef struct st_net {
my_bool unused_1, unused_2;
my_bool compress;
my_bool unused_3;
unsigned char *unused_4;
MA_CONNECTION_HANDLER *conn_hdlr;
unsigned int last_errno;
unsigned char error;
my_bool unused_4;
my_bool unused_5;
my_bool unused_6;
char last_error[MYSQL_ERRMSG_SIZE];
char sqlstate[SQLSTATE_LENGTH+1];
@@ -324,7 +328,7 @@ extern unsigned long net_buffer_length;
#define net_new_transaction(net) ((net)->pkt_nr=0)
int my_net_init(NET *net, Vio *vio);
int my_net_init(NET *net, MARIADB_CIO *cio);
void net_end(NET *net);
void net_clear(NET *net);
int net_flush(NET *net);

View File

@@ -1,167 +0,0 @@
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA */
/*
* Vio Lite.
* Purpose: include file for Vio that will work with C and C++
*/
#ifndef vio_violite_h_
#define vio_violite_h_
#include "my_net.h" /* needed because of struct in_addr */
#ifdef HAVE_VIO
#include <Vio.h> /* Full VIO interface */
#else
#ifdef HAVE_OPENSSL
#include <openssl/ssl.h>
#endif
enum enum_vio_io_event
{
VIO_IO_EVENT_READ,
VIO_IO_EVENT_WRITE,
VIO_IO_EVENT_CONNECT
};
/* Simple vio interface in C; The functions are implemented in violite.c */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#ifndef Vio_defined
#define Vio_defined
struct st_vio; /* Only C */
typedef struct st_vio Vio;
#endif
#ifndef _WIN32
#define HANDLE void *
#endif
/* vio read-ahead cachine */
#define VIO_CACHE_SIZE 16384
#define VIO_CACHE_MIN_SIZE 2048
enum enum_vio_type { VIO_CLOSED, VIO_TYPE_TCPIP, VIO_TYPE_SOCKET,
VIO_TYPE_NAMEDPIPE, VIO_TYPE_SSL};
Vio* vio_new(my_socket sd,
enum enum_vio_type type,
my_bool localhost);
#ifdef _WIN32
Vio* vio_new_win32pipe(HANDLE hPipe);
#endif
void vio_delete(Vio* vio);
void vio_reset(Vio* vio, enum enum_vio_type type,
my_socket sd, HANDLE hPipe,
my_bool localhost);
/*
* vio_read and vio_write should have the same semantics
* as read(2) and write(2).
*/
size_t vio_read(Vio* vio, gptr buf, size_t size);
my_bool vio_read_peek(Vio *vio, size_t *bytes);
size_t vio_write(Vio* vio, const gptr buf, size_t size);
/*
* Whenever the socket is set to blocking mode or not.
*/
int vio_blocking( Vio* vio,
my_bool onoff,
my_bool *prevmode);
my_bool vio_is_blocking( Vio* vio);
/*
* setsockopt TCP_NODELAY at IPPROTO_TCP level, when possible.
*/
int vio_fastsend( Vio* vio);
/*
* setsockopt SO_KEEPALIVE at SOL_SOCKET level, when possible.
*/
int vio_keepalive( Vio* vio,
my_bool onoff);
/*
* Whenever we should retry the last read/write operation.
*/
my_bool vio_should_retry( Vio* vio);
/*
* When the workday is over...
*/
int vio_close( Vio* vio);
/*
* Short text description of the socket for those, who are curious..
*/
const char* vio_description( Vio* vio);
/* Return the type of the connection */
enum enum_vio_type vio_type(Vio* vio);
/* set timeout */
void vio_read_timeout(Vio *vio, uint seconds);
void vio_write_timeout(Vio *vio, uint seconds);
/* Return last error number */
int vio_errno(Vio *vio);
/* Get socket number */
my_socket vio_fd(Vio *vio);
/*
* Remote peer's address and name in text form.
*/
my_bool vio_peer_addr(Vio * vio, char *buf);
/* Remotes in_addr */
void vio_in_addr(Vio *vio, struct in_addr *in);
/* Return 1 if there is data to be read */
my_bool vio_poll_read(Vio *vio,uint timeout);
int vio_wait_or_timeout(Vio *vio, my_bool is_read, int timeout);
struct st_vio
{
my_socket sd; /* my_socket - real or imaginary */
HANDLE hPipe;
my_bool localhost; /* Are we from localhost? */
int fcntl_mode; /* Buffered fcntl(sd,F_GETFL) */
struct sockaddr_in local; /* Local internet address */
struct sockaddr_in remote; /* Remote internet address */
struct mysql_async_context *async_context; /* For non-blocking API */
int write_timeout;
int read_timeout;
enum enum_vio_type type; /* Type of connection */
char desc[30]; /* String description */
#ifdef HAVE_OPENSSL
SSL *ssl;
#endif
uchar *cache; /* read-ahead cache to reduce reads (see CONC-79) */
uchar *cache_pos; /* position of read-ahead cached data */
size_t cache_size; /* <= VIO_CACHE_SIZE */
};
#ifdef __cplusplus
}
#endif
#endif /* HAVE_VIO */
#endif /* vio_violite_h_ */

View File

@@ -9,9 +9,9 @@ ADD_DEFINITIONS(-D THREAD)
SET(EXPORT_SYMBOLS
load_defaults
ma_cio_register_callback
mariadb_connection
mariadb_convert_string
mariadb_deinitialize_ssl
mariadb_dyncol_check
mariadb_dyncol_column_cmp_named
mariadb_dyncol_column_count
@@ -220,6 +220,9 @@ SET(EXPORT_SYMBOLS
mysql_thread_safe
mysql_use_result
mysql_warning_count)
IF(WITH_SSL)
SET(EXPORTS ${EXPORTS} mariadb_deinitialize_ssl)
ENDIF()
# some gcc versions fail to compile asm parts of my_context.c,
# if build type is "Release" (see CONC-133), so we need to add -g flag
@@ -245,7 +248,7 @@ ENDIF()
FILE(WRITE ${EXPORT_FILE} ${EXPORT_CONTENT})
SET(LIBMARIADB_SOURCES
SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES}
array.c
ma_dyncol.c
bchange.c
@@ -253,7 +256,6 @@ bmove.c
bmove_upp.c
my_charset.c
hash.c
violite.c
net.c
charset.c
ma_time.c
@@ -270,7 +272,8 @@ libmariadb.c
list.c
llstr.c
longlong2str.c
ma_io.c
ma_cio.c
ma_ssl.c
mf_dirname.c
mf_fn_ext.c
mf_format.c
@@ -326,11 +329,12 @@ sha1.c
my_stmt.c
my_loaddata.c
my_stmt_codec.c
client_plugin.c
my_auth.c
ma_secure.c
${CMAKE_BINARY_DIR}/libmariadb/client_plugin.c
ma_io.c
${SSL_SOURCES}
)
IF(WIN32)
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/win-iconv)
SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES}

View File

@@ -56,33 +56,13 @@ struct st_client_plugin_int {
static my_bool initialized= 0;
static MEM_ROOT mem_root;
#define plugin_declarations_sym "_mysql_client_plugin_declaration_"
static uint plugin_version[MYSQL_CLIENT_MAX_PLUGINS]=
{
MYSQL_CLIENT_DB_PLUGIN_INTERFACE_VERSION, /* these two are taken by Connector/C */
0, /* these two are taken by Connector/C */
MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION,
0,
#ifdef HAVE_REMOTEIO
MYSQL_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION
#endif
};
typedef struct st_mysql_client_plugin_AUTHENTICATION auth_plugin_t;
extern auth_plugin_t old_password_client_plugin;
extern auth_plugin_t native_password_client_plugin;
/* built in plugins:
These plugins are part of Connector/C, so no need to
load them
*/
struct st_mysql_client_plugin *mysql_client_builtins[]=
{
(struct st_mysql_client_plugin *)&old_password_client_plugin,
(struct st_mysql_client_plugin *)&native_password_client_plugin,
0
static uint valid_plugins[][2]= {
{MYSQL_CLIENT_AUTHENTICATION_PLUGIN, MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION},
{MARIADB_CLIENT_CIO_PLUGIN, MARIADB_CLIENT_CIO_PLUGIN_INTERFACE_VERSION},
{MARIADB_CLIENT_TRACE_PLUGIN, MARIADB_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION},
{MARIADB_CLIENT_REMOTEIO_PLUGIN, MARIADB_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION},
{MARIADB_CLIENT_CONNECTION_PLUGIN, MARIADB_CLIENT_CONNECTION_PLUGIN_INTERFACE_VERSION},
{0, 0}
};
/*
@@ -93,11 +73,22 @@ struct st_mysql_client_plugin *mysql_client_builtins[]=
there. The main purpose of a mutex is to prevent two threads from
loading the same plugin twice in parallel.
*/
struct st_client_plugin_int *plugin_list[MYSQL_CLIENT_MAX_PLUGINS];
struct st_client_plugin_int *plugin_list[MYSQL_CLIENT_MAX_PLUGINS + MARIADB_CLIENT_MAX_PLUGINS];
#ifdef THREAD
static pthread_mutex_t LOCK_load_client_plugin;
#endif
@EXTERNAL_PLUGINS@
struct st_mysql_client_plugin *mysql_client_builtins[]=
{
@BUILTIN_PLUGINS@
0
};
static int is_not_initialized(MYSQL *mysql, const char *name)
{
if (initialized)
@@ -105,10 +96,29 @@ static int is_not_initialized(MYSQL *mysql, const char *name)
my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD,
SQLSTATE_UNKNOWN, ER(CR_AUTH_PLUGIN_CANNOT_LOAD),
name ? name : "unknown" , "not initialized");
name, "not initialized");
return 1;
}
static int get_plugin_nr(int type)
{
uint i= 0;
for(; valid_plugins[i][1]; i++)
if (valid_plugins[i][0]== type)
return i;
return -1;
}
static my_bool check_plugin_version(struct st_mysql_client_plugin *plugin, int version, char *errmsg)
{
if (plugin->interface_version < version ||
(plugin->interface_version >> 8) > (version >> 8))
{
errmsg= "Incompatible client plugin interface";
return 1;
}
return 0;
}
/**
finds a plugin in the list
@@ -120,26 +130,27 @@ static int is_not_initialized(MYSQL *mysql, const char *name)
@retval a pointer to a found plugin or 0
*/
static struct st_mysql_client_plugin *find_plugin(const char *name, int type)
{
struct st_client_plugin_int *p;
int plugin_nr= get_plugin_nr(type);
DBUG_ASSERT(initialized);
DBUG_ASSERT(type >= 0 && type < MYSQL_CLIENT_MAX_PLUGINS);
if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS)
if (plugin_nr == -1)
return 0;
for (p= plugin_list[type]; p; p= p->next)
{
if (!name)
return p->plugin;
return plugin_list[plugin_nr]->plugin;
for (p= plugin_list[plugin_nr]; p; p= p->next)
{
if (strcmp(p->plugin->name, name) == 0)
return p->plugin;
}
return NULL;
}
/**
verifies the plugin and adds it to the list
@@ -157,28 +168,23 @@ static struct st_mysql_client_plugin *
add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle,
int argc, va_list args)
{
const char *errmsg;
char *errmsg;
struct st_client_plugin_int plugin_int, *p;
char errbuf[1024];
int plugin_nr;
DBUG_ASSERT(initialized);
plugin_int.plugin= plugin;
plugin_int.dlhandle= dlhandle;
if (plugin->type >= MYSQL_CLIENT_MAX_PLUGINS)
if ((plugin_nr= get_plugin_nr(plugin->type)) == -1)
{
errmsg= "Unknown client plugin type";
goto err1;
}
if (plugin->interface_version < plugin_version[plugin->type] ||
(plugin->interface_version >> 8) >
(plugin_version[plugin->type] >> 8))
{
errmsg= "Incompatible client plugin interface";
if (check_plugin_version(plugin, valid_plugins[plugin_nr][1], errmsg))
goto err1;
}
/* Call the plugin initialization function, if any */
if (plugin->init && plugin->init(errbuf, sizeof(errbuf), argc, args))
@@ -200,8 +206,8 @@ add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle,
safe_mutex_assert_owner(&LOCK_load_client_plugin);
#endif
p->next= plugin_list[plugin->type];
plugin_list[plugin->type]= p;
p->next= plugin_list[plugin_nr];
plugin_list[plugin_nr]= p;
return plugin;
@@ -209,10 +215,10 @@ err2:
if (plugin->deinit)
plugin->deinit();
err1:
if (dlhandle)
(void)dlclose(dlhandle);
my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, SQLSTATE_UNKNOWN,
ER(CR_AUTH_PLUGIN_CANNOT_LOAD), plugin->name, errmsg);
if (dlhandle)
(void)dlclose(dlhandle);
return NULL;
}
@@ -284,7 +290,6 @@ int mysql_client_plugin_init()
initialized= 1;
pthread_mutex_lock(&LOCK_load_client_plugin);
for (builtin= mysql_client_builtins; *builtin; builtin++)
add_plugin(&mysql, *builtin, 0, 0, unused);
@@ -405,6 +410,7 @@ mysql_load_plugin_v(MYSQL *mysql, const char *name, int type,
goto err;
}
if (!(sym= dlsym(dlhandle, plugin_declarations_sym)))
{
errmsg= "not a plugin";
@@ -458,17 +464,17 @@ mysql_load_plugin(MYSQL *mysql, const char *name, int type, int argc, ...)
return p;
}
/* see <mysql/client_plugin.h> for a full description */
struct st_mysql_client_plugin * STDCALL
mysql_client_find_plugin(MYSQL *mysql, const char *name, int type)
{
struct st_mysql_client_plugin *p;
int plugin_nr= get_plugin_nr(type);
if (is_not_initialized(mysql, name))
return NULL;
if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS)
if (plugin_nr == -1)
{
my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, SQLSTATE_UNKNOWN,
ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name, "invalid type");
@@ -478,7 +484,6 @@ mysql_client_find_plugin(MYSQL *mysql, const char *name, int type)
return p;
/* not found, load it */
if (name)
return mysql_load_plugin(mysql, name, type, 0);
}

View File

@@ -1811,7 +1811,7 @@ static int DoTrace(CODE_STATE *cs)
case INCLUDE: return DO_TRACE;
case MATCHED|SUBDIR:
case NOT_MATCHED|SUBDIR:
case MATCHED: return framep_trace_flag(cs, cs->framep) ?
case MATCHED: return (framep_trace_flag(cs, cs->framep)) ?
DO_TRACE : DONT_TRACE;
case EXCLUDE:
case NOT_MATCHED: return DONT_TRACE;

View File

@@ -55,25 +55,25 @@ const char *client_errors[]=
const char *client_errors[]=
{
"Erro desconhecido do MySQL",
"N<EFBFBD>o pode criar 'UNIX socket' (%d)",
"N<EFBFBD>o pode se conectar ao servidor MySQL local atrav<61>s do 'socket' '%-.64s' (%d)",
"N<EFBFBD>o pode se conectar ao servidor MySQL em '%-.64s' (%d)",
"N<EFBFBD>o pode criar 'socket TCP/IP' (%d)",
"N<EFBFBD>o pode criar 'UNIX socket' (%d)",
"N<EFBFBD>o pode se conectar ao servidor MySQL local atrav<61>s do 'socket' '%-.64s' (%d)",
"N<EFBFBD>o pode se conectar ao servidor MySQL em '%-.64s' (%d)",
"N<EFBFBD>o pode criar 'socket TCP/IP' (%d)",
"'Host' servidor MySQL '%-.64s' (%d) desconhecido",
"Servidor MySQL desapareceu",
"Incompatibilidade de protocolos. Vers<72>o do Servidor: %d - Vers<72>o do Cliente: %d",
"Cliente do MySQL com falta de mem<65>ria",
"Informa<EFBFBD><EFBFBD>o inv<6E>lida de 'host'",
"Incompatibilidade de protocolos. Vers<72>o do Servidor: %d - Vers<72>o do Cliente: %d",
"Cliente do MySQL com falta de mem<65>ria",
"Informa<EFBFBD><EFBFBD>o inv<6E>lida de 'host'",
"Localhost via 'UNIX socket'",
"%-.64s via 'TCP/IP'",
"Erro na negocia<69><61>o de acesso ao servidor",
"Conex<EFBFBD>o perdida com servidor MySQL durante 'query'",
"Comandos fora de sincronismo. Voc<6F> n<>o pode executar este comando agora",
"Erro na negocia<69><61>o de acesso ao servidor",
"Conex<EFBFBD>o perdida com servidor MySQL durante 'query'",
"Comandos fora de sincronismo. Voc<6F> n<>o pode executar este comando agora",
"%-.64s via 'named pipe'",
"N<EFBFBD>o pode esperar pelo 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)",
"N<EFBFBD>o pode abrir 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)",
"N<EFBFBD>o pode estabelecer o estado do 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)",
"N<EFBFBD>o pode inicializar conjunto de caracteres %-.64s (caminho %-.64s)",
"N<EFBFBD>o pode esperar pelo 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)",
"N<EFBFBD>o pode abrir 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)",
"N<EFBFBD>o pode estabelecer o estado do 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)",
"N<EFBFBD>o pode inicializar conjunto de caracteres %-.64s (caminho %-.64s)",
"Obteve pacote maior do que 'max_allowed_packet'"
};
@@ -117,8 +117,8 @@ const char *client_errors[]=
/* 2034 */ "",
/* 2035 */ "",
/* 2036 */ "Buffer type is not supported",
/* 2037 */ "",
/* 2038 */ "",
/* 2037 */ "Shared memory: %-.64s",
/* 2038 */ "Shared memory connection failed during %s. (%lu)",
/* 2039 */ "",
/* 2040 */ "",
/* 2041 */ "",
@@ -145,6 +145,12 @@ const char *client_errors[]=
};
#endif
const char *mariadb_client_errors[] =
{
/* 5000 */ "Creating an event failed (Errorcode: %d)",
/* 5001 */ "Bind to local interface '-.%64s' failed (Errorcode: %d)",
""
};
void init_client_errs(void)
{

View File

@@ -62,15 +62,12 @@
#define INADDR_NONE -1
#endif
#include <sha1.h>
#include <violite.h>
#ifdef HAVE_OPENSSL
#include <ma_secure.h>
#endif
#ifndef _WIN32
#include <poll.h>
#endif
#include <ma_cio.h>
#include <ma_dyncol.h>
#include <mysql_async.h>
#include <mysql/client_plugin.h>
#define ASYNC_CONTEXT_DEFAULT_STACK_SIZE (4096*15)
@@ -91,6 +88,8 @@ extern const CHARSET_INFO * mysql_find_charset_name(const char * const name);
extern int run_plugin_auth(MYSQL *mysql, char *data, uint data_len,
const char *data_plugin, const char *db);
extern LIST *cio_callback;
/* prepare statement methods from my_stmt.c */
extern my_bool mthd_supported_buffer_type(enum enum_field_types type);
extern my_bool mthd_stmt_read_prepare_response(MYSQL_STMT *stmt);
@@ -130,216 +129,11 @@ static void mysql_close_memory(MYSQL *mysql);
void read_user_name(char *name);
static void append_wild(char *to,char *end,const char *wild);
static my_bool mysql_reconnect(MYSQL *mysql);
static sig_handler pipe_sig_handler(int sig);
static int cli_report_progress(MYSQL *mysql, uchar *packet, uint length);
extern int mysql_client_plugin_init();
extern void mysql_client_plugin_deinit();
/*
Let the user specify that we don't want SIGPIPE; This doesn't however work
with threaded applications as we can have multiple read in progress.
*/
#if !defined(_WIN32) && defined(SIGPIPE) && !defined(THREAD)
#define init_sigpipe_variables sig_return old_signal_handler=(sig_return) 0;
#define set_sigpipe(mysql) if ((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE) old_signal_handler=signal(SIGPIPE,pipe_sig_handler)
#define reset_sigpipe(mysql) if ((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE) signal(SIGPIPE,old_signal_handler);
#else
#define init_sigpipe_variables
#define set_sigpipe(mysql)
#define reset_sigpipe(mysql)
#endif
/****************************************************************************
* A modified version of connect(). connect2() allows you to specify
* a timeout value, in seconds, that we should wait until we
* derermine we can't connect to a particular host. If timeout is 0,
* connect2() will behave exactly like connect().
*
* Base version coded by Steve Bernacki, Jr. <steve@navinet.net>
*****************************************************************************/
int socket_block(my_socket s,my_bool blocked)
{
#ifdef _WIN32
unsigned long socket_blocked= blocked ? 0 : 1;
return ioctlsocket(s, FIONBIO, &socket_blocked);
#else
int flags= fcntl(s, F_GETFL, 0);
if (blocked)
flags&= ~O_NONBLOCK;
else
flags|= O_NONBLOCK;
return fcntl(s, F_SETFL, flags);
#endif
}
static int connect2(my_socket s, const struct sockaddr *name, size_t namelen,
uint timeout)
{
int res, s_err;
socklen_t s_err_size = sizeof(uint);
#ifndef _WIN32
struct pollfd poll_fd;
#else
FD_SET sfds, efds;
struct timeval tv;
#endif
if (!timeout)
return connect(s, (struct sockaddr*) name, (int)namelen);
/* set socket to non blocking */
if (socket_block(s, 0) == SOCKET_ERROR)
return -1;
res= connect(s, (struct sockaddr*) name, (int)namelen);
if (res == 0)
return res;
#ifdef _WIN32
if (GetLastError() != WSAEWOULDBLOCK &&
GetLastError() != WSAEINPROGRESS)
#else
if (errno != EINPROGRESS)
#endif
return -1;
#ifndef _WIN32
memset(&poll_fd, 0, sizeof(struct pollfd));
poll_fd.events= POLLOUT | POLLERR;
poll_fd.fd= s;
/* connection timeout in milliseconds */
res= poll(&poll_fd, 1, timeout);
switch(res)
{
/* Error= - 1, timeout = 0 */
case -1:
break;
case 0:
errno= ETIMEDOUT;
break;
}
#else
FD_ZERO(&sfds);
FD_ZERO(&efds);
FD_SET(s, &sfds);
FD_SET(s, &efds);
memset(&tv, 0, sizeof(struct timeval));
tv.tv_sec= timeout;
res= select((int)s+1, NULL, &sfds, &efds, &tv);
if (res < 1)
return -1;
#endif
s_err=0;
if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*) &s_err, &s_err_size) != 0)
return(-1);
if (s_err)
{ /* getsockopt could succeed */
errno = s_err;
return(-1); /* but return an error... */
}
return (0); /* ok */
}
static int
connect_sync_or_async(MYSQL *mysql, NET *net, my_socket fd,
const struct sockaddr *name, uint namelen)
{
int vio_timeout= (mysql->options.connect_timeout >= 0) ?
mysql->options.connect_timeout * 1000 : -1;
if (mysql->options.extension && mysql->options.extension->async_context &&
mysql->options.extension->async_context->active)
{
my_bool old_mode;
vio_blocking(net->vio, FALSE, &old_mode);
return my_connect_async(mysql->options.extension->async_context, fd,
name, namelen, vio_timeout);
}
return connect2(fd, name, namelen, vio_timeout);
}
/*
** Create a named pipe connection
*/
#ifdef _WIN32
HANDLE create_named_pipe(NET *net, uint connect_timeout, char **arg_host,
char **arg_unix_socket)
{
HANDLE hPipe=INVALID_HANDLE_VALUE;
char szPipeName [ 257 ];
DWORD dwMode;
int i;
my_bool testing_named_pipes=0;
char *host= *arg_host, *unix_socket= *arg_unix_socket;
if ( ! unix_socket || (unix_socket)[0] == 0x00)
unix_socket = mysql_unix_port;
if (!host || !strcmp(host,LOCAL_HOST))
host=LOCAL_HOST_NAMEDPIPE;
sprintf( szPipeName, "\\\\%s\\pipe\\%s", host, unix_socket);
DBUG_PRINT("info",("Server name: '%s'. Named Pipe: %s",
host, unix_socket));
for (i=0 ; i < 100 ; i++) /* Don't retry forever */
{
if ((hPipe = CreateFile(szPipeName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL )) != INVALID_HANDLE_VALUE)
break;
if (GetLastError() != ERROR_PIPE_BUSY)
{
net->last_errno=CR_NAMEDPIPEOPEN_ERROR;
sprintf(net->last_error,ER(net->last_errno),host, unix_socket,
(ulong) GetLastError());
return INVALID_HANDLE_VALUE;
}
/* wait for for an other instance */
if (! WaitNamedPipe(szPipeName, connect_timeout) )
{
net->last_errno=CR_NAMEDPIPEWAIT_ERROR;
sprintf(net->last_error,ER(net->last_errno),host, unix_socket,
(ulong) GetLastError());
return INVALID_HANDLE_VALUE;
}
}
if (hPipe == INVALID_HANDLE_VALUE)
{
net->last_errno=CR_NAMEDPIPEOPEN_ERROR;
sprintf(net->last_error,ER(net->last_errno),host, unix_socket,
(ulong) GetLastError());
return INVALID_HANDLE_VALUE;
}
dwMode = PIPE_READMODE_BYTE | PIPE_WAIT;
if ( !SetNamedPipeHandleState(hPipe, &dwMode, NULL, NULL) )
{
CloseHandle( hPipe );
net->last_errno=CR_NAMEDPIPESETSTATE_ERROR;
sprintf(net->last_error,ER(net->last_errno),host, unix_socket,
(ulong) GetLastError());
return INVALID_HANDLE_VALUE;
}
*arg_host=host ; *arg_unix_socket=unix_socket; /* connect arg */
return (hPipe);
}
#endif
/* net_get_error */
void net_get_error(char *buf, size_t buf_len,
char *error, size_t error_len,
@@ -382,14 +176,10 @@ net_safe_read(MYSQL *mysql)
{
NET *net= &mysql->net;
ulong len=0;
init_sigpipe_variables
restart:
/* Don't give sigpipe errors if the client doesn't want them */
set_sigpipe(mysql);
if (net->vio != 0)
if (net->cio != 0)
len=my_net_read(net);
reset_sigpipe(mysql);
if (len == packet_error || len == 0)
{
@@ -553,22 +343,25 @@ void free_rows(MYSQL_DATA *cur)
}
}
int
mthd_my_send_cmd(MYSQL *mysql,enum enum_server_command command, const char *arg,
size_t length, my_bool skipp_check, void *opt_arg)
{
NET *net= &mysql->net;
int result= -1;
init_sigpipe_variables
DBUG_ENTER("mthd_my_send_command");
DBUG_PRINT("info", ("server_command: %d packet_size: %u", command, length));
/* Don't give sigpipe errors if the client doesn't want them */
set_sigpipe(mysql);
if (mysql->net.vio == 0)
if (mysql->net.conn_hdlr && mysql->net.conn_hdlr->data)
{
result= mysql->net.conn_hdlr->plugin->set_connection(mysql, command, arg, length, skipp_check, opt_arg);
if (result== -1)
DBUG_RETURN(result);
}
if (mysql->net.cio == 0)
{ /* Do reconnect if possible */
if (mysql_reconnect(mysql))
{
@@ -617,7 +410,6 @@ mthd_my_send_cmd(MYSQL *mysql,enum enum_server_command command, const char *arg,
DBUG_PRINT("info", ("packet_length=%llu", mysql->packet_length));
}
end:
reset_sigpipe(mysql);
DBUG_RETURN(result);
}
@@ -759,21 +551,6 @@ mysql_debug(const char *debug __attribute__((unused)))
}
/**************************************************************************
** Close the server connection if we get a SIGPIPE
ARGSUSED
**************************************************************************/
static sig_handler
pipe_sig_handler(int sig __attribute__((unused)))
{
DBUG_PRINT("info",("Hit by signal %d",sig));
#ifdef DONT_REMEMBER_SIGNAL
(void) signal(SIGPIPE,pipe_sig_handler);
#endif
}
/**************************************************************************
** Shut down connection
**************************************************************************/
@@ -782,13 +559,13 @@ static void
end_server(MYSQL *mysql)
{
DBUG_ENTER("end_server");
if (mysql->net.vio != 0)
/* if net->error 2 and reconnect is activated, we need to inforn
connection handler */
if (mysql->net.cio != 0)
{
init_sigpipe_variables
set_sigpipe(mysql);
vio_delete(mysql->net.vio);
reset_sigpipe(mysql);
mysql->net.vio= 0; /* Marker */
ma_cio_close(mysql->net.cio);
mysql->net.cio= 0; /* Marker */
}
net_end(&mysql->net);
free_old_query(mysql);
@@ -845,7 +622,7 @@ static const char *default_options[]=
"ssl-cipher", "max-allowed-packet", "protocol", "shared-memory-base-name",
"multi-results", "multi-statements", "multi-queries", "secure-auth",
"report-data-truncation", "plugin-dir", "default-auth", "database-type",
"ssl-fp", "ssl-fp-list",
"ssl-fp", "ssl-fp-list", "bind-address",
NULL
};
@@ -860,7 +637,7 @@ enum option_val
OPT_ssl_cipher, OPT_max_allowed_packet, OPT_protocol, OPT_shared_memory_base_name,
OPT_multi_results, OPT_multi_statements, OPT_multi_queries, OPT_secure_auth,
OPT_report_data_truncation, OPT_plugin_dir, OPT_default_auth, OPT_db_type,
OPT_ssl_fp, OPT_ssl_fp_list
OPT_ssl_fp, OPT_ssl_fp_list, OPT_bind_address
};
#define CHECK_OPT_EXTENSION_SET(OPTS)\
@@ -869,13 +646,15 @@ enum option_val
my_malloc(sizeof(struct st_mysql_options_extension), \
MYF(MY_WME | MY_ZEROFILL)); \
#define OPT_SET_EXTENDED_VALUE(OPTS, KEY, VAL, IS_STRING) \
#define OPT_SET_EXTENDED_VALUE_STR(OPTS, KEY, VAL) \
CHECK_OPT_EXTENSION_SET(OPTS) \
if (IS_STRING) { \
my_free((OPTS)->extension->KEY); \
(OPTS)->extension->KEY= my_strdup((char *)(VAL), MYF(MY_WME)); \
} else \
(OPTS)->extension->KEY= (VAL);
my_free((gptr)(OPTS)->extension->KEY);\
(OPTS)->extension->KEY= my_strdup((char *)(VAL), MYF(MY_WME))
#define OPT_SET_EXTENDED_VALUE_INT(OPTS, KEY, VAL) \
CHECK_OPT_EXTENSION_SET(OPTS) \
+ (OPTS)->extension->KEY= (VAL)
static TYPELIB option_types={array_elements(default_options)-1,
"options",default_options};
@@ -993,7 +772,7 @@ static void mysql_read_default_options(struct st_mysql_options *options,
case OPT_return_found_rows:
options->client_flag|=CLIENT_FOUND_ROWS;
break;
#ifdef HAVE_OPENSSL
#ifdef HAVE_SSL
case OPT_ssl_key:
my_free(options->ssl_key);
options->ssl_key = my_strdup(opt_arg, MYF(MY_WME));
@@ -1013,10 +792,10 @@ static void mysql_read_default_options(struct st_mysql_options *options,
case OPT_ssl_cipher:
break;
case OPT_ssl_fp:
OPT_SET_EXTENDED_VALUE(options, ssl_fp, opt_arg, 1);
OPT_SET_EXTENDED_VALUE_STR(options, ssl_fp, opt_arg);
break;
case OPT_ssl_fp_list:
OPT_SET_EXTENDED_VALUE(options, ssl_fp_list, opt_arg, 1);
OPT_SET_EXTENDED_VALUE_STR(options, ssl_fp_list, opt_arg);
break;
#else
case OPT_ssl_key:
@@ -1027,7 +806,7 @@ static void mysql_read_default_options(struct st_mysql_options *options,
case OPT_ssl_fp:
case OPT_ssl_fp_list:
break;
#endif /* HAVE_OPENSSL */
#endif /* HAVE_SSL */
case OPT_charset_dir:
my_free(options->charset_dir);
options->charset_dir = my_strdup(opt_arg, MYF(MY_WME));
@@ -1088,11 +867,15 @@ static void mysql_read_default_options(struct st_mysql_options *options,
if (strlen(opt_arg) >= FN_REFLEN)
opt_arg[FN_REFLEN]= 0;
if (!my_realpath(directory, opt_arg, 0))
OPT_SET_EXTENDED_VALUE(options, plugin_dir, convert_dirname(directory), 1);
OPT_SET_EXTENDED_VALUE_STR(options, plugin_dir, convert_dirname(directory));
}
break;
case OPT_default_auth:
OPT_SET_EXTENDED_VALUE(options, default_auth, opt_arg, 1);
OPT_SET_EXTENDED_VALUE_STR(options, default_auth, opt_arg);
break;
case OPT_bind_address:
my_free(options->bind_address);
options->bind_address= my_strdup(opt_arg, MYF(MY_WME));
break;
default:
DBUG_PRINT("warning",("unknown option: %s",option[0]));
@@ -1346,7 +1129,7 @@ mysql_init(MYSQL *mysql)
if (!(mysql=(MYSQL*) my_malloc(sizeof(*mysql),MYF(MY_WME | MY_ZEROFILL))))
return 0;
mysql->free_me=1;
mysql->net.vio= 0;
mysql->net.cio= 0;
}
else
bzero((char*) (mysql),sizeof(*(mysql)));
@@ -1354,10 +1137,6 @@ mysql_init(MYSQL *mysql)
mysql->charset= default_charset_info;
strmov(mysql->net.sqlstate, "00000");
mysql->net.last_error[0]= mysql->net.last_errno= 0;
#if defined(SIGPIPE) && defined(THREAD) && !defined(_WIN32)
if (!((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE))
(void) signal(SIGPIPE,pipe_sig_handler);
#endif
/*
Only enable LOAD DATA INFILE by default if configured with
@@ -1385,7 +1164,6 @@ mysql_ssl_set(MYSQL *mysql, const char *key, const char *cert,
#else
return 0;
#endif
}
/**************************************************************************
@@ -1394,10 +1172,10 @@ mysql_ssl_set(MYSQL *mysql, const char *key, const char *cert,
const char * STDCALL
mysql_get_ssl_cipher(MYSQL *mysql)
{
#ifdef HAVE_OPENSSL
if (mysql->net.vio && mysql->net.vio->ssl)
#ifdef HAVE_SSL
if (mysql->net.cio && mysql->net.cio->cssl)
{
return SSL_get_cipher_name(mysql->net.vio->ssl);
return ma_cio_ssl_cipher(mysql->net.cio->cssl);
}
#endif
return(NULL);
@@ -1519,18 +1297,12 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
char buff[NAME_LEN+USERNAME_LENGTH+100];
char *end, *end_pkt, *host_info,
*charset_name= NULL;
my_socket sock;
MA_CIO_CINFO cinfo= {NULL, NULL, 0, -1, NULL};
MARIADB_CIO *cio= NULL;
char *scramble_data;
const char *scramble_plugin;
uint pkt_length, scramble_len, pkt_scramble_len= 0;
NET *net= &mysql->net;
#ifdef _WIN32
HANDLE hPipe=INVALID_HANDLE_VALUE;
#endif
#ifdef HAVE_SYS_UN_H
struct sockaddr_un UNIXaddr;
#endif
init_sigpipe_variables
DBUG_ENTER("mysql_real_connect");
DBUG_PRINT("enter",("host: %s db: %s user: %s",
@@ -1538,17 +1310,43 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
db ? db : "(Null)",
user ? user : "(Null)"));
if (!mysql->methods)
mysql->methods= &MARIADB_DEFAULT_METHODS;
/* special case:
* If hostname contains "://", e.g. "repl://localhost", we need to process connection
* by connection plugin
*/
if (host && (end= strstr(host, "://")))
{
MARIADB_CONNECTION_PLUGIN *plugin;
char plugin_name[64];
bzero(plugin_name, 64);
strncpy(plugin_name, host, MIN(end - host, 63));
end+= 3;
if (!(plugin= (MARIADB_CONNECTION_PLUGIN *)mysql_client_find_plugin(mysql, plugin_name, MARIADB_CLIENT_CONNECTION_PLUGIN)))
DBUG_RETURN(NULL);
if (!(mysql->net.conn_hdlr= (MA_CONNECTION_HANDLER *)my_malloc(sizeof(MA_CONNECTION_HANDLER), MYF(MY_ZEROFILL))))
{
SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
DBUG_RETURN(NULL);
}
mysql->net.conn_hdlr->plugin= plugin;
if (plugin->connect)
return plugin->connect(mysql, end, user, passwd, db, port, unix_socket, client_flag);
}
ma_set_connect_attrs(mysql);
if (net->vio) /* check if we are already connected */
if (net->cio) /* check if we are already connected */
{
SET_CLIENT_ERROR(mysql, CR_ALREADY_CONNECTED, SQLSTATE_UNKNOWN, 0);
DBUG_RETURN(NULL);
}
/* Don't give sigpipe errors if the client doesn't want them */
set_sigpipe(mysql);
/* use default options */
if (mysql->options.my_cnf_file || mysql->options.my_cnf_group)
{
@@ -1582,48 +1380,33 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
unix_socket=mysql->options.unix_socket;
/* Since 5.0.3 reconnect is not enabled by default!!
mysql->reconnect=1; */
mysql->server_status=SERVER_STATUS_AUTOCOMMIT;
/* try to connect via cio_init */
cinfo.host= host;
cinfo.unix_socket= unix_socket;
cinfo.port= port;
cinfo.mysql= mysql;
/*
** Grab a socket and connect it to the server
*/
#ifndef _WIN32
#if defined(HAVE_SYS_UN_H)
if ((!host || strcmp(host,LOCAL_HOST) == 0) &&
mysql->options.protocol != MYSQL_PROTOCOL_TCP &&
(unix_socket || mysql_unix_port))
{
host=LOCAL_HOST;
if (!unix_socket)
unix_socket=mysql_unix_port;
host_info=(char*) ER(CR_LOCALHOST_CONNECTION);
DBUG_PRINT("info",("Using UNIX sock '%s'",unix_socket));
if ((sock = socket(AF_UNIX,SOCK_STREAM,0)) == SOCKET_ERROR)
{
net->last_errno=CR_SOCKET_CREATE_ERROR;
sprintf(net->last_error,ER(net->last_errno),socket_errno);
goto error;
}
net->vio = vio_new(sock, VIO_TYPE_SOCKET, TRUE);
bzero((char*) &UNIXaddr,sizeof(UNIXaddr));
UNIXaddr.sun_family = AF_UNIX;
strmov(UNIXaddr.sun_path, unix_socket);
if (connect_sync_or_async(mysql, net, sock,
(struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr)))
{
DBUG_PRINT("error",("Got error %d on connect to local server",socket_errno));
my_set_error(mysql, CR_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ER(CR_CONNECTION_ERROR),
unix_socket, socket_errno);
goto error;
}
if (socket_block(sock, 1) == SOCKET_ERROR)
goto error;
cinfo.host= LOCAL_HOST;
cinfo.unix_socket= (unix_socket) ? unix_socket : mysql_unix_port;
cinfo.type= CIO_TYPE_UNIXSOCKET;
sprintf(host_info=buff,ER(CR_LOCALHOST_CONNECTION),cinfo.host);
}
else
#elif defined(_WIN32)
{
#endif
#else
/* named pipe */
if ((unix_socket ||
(!host && is_NT()) ||
(host && strcmp(host,LOCAL_HOST_NAMEDPIPE) == 0) ||
@@ -1631,146 +1414,43 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
!have_tcpip) &&
mysql->options.protocol != MYSQL_PROTOCOL_TCP)
{
sock=0;
if ((hPipe=create_named_pipe(net, mysql->options.connect_timeout,
(char**) &host, (char**) &unix_socket)) ==
INVALID_HANDLE_VALUE)
{
DBUG_PRINT("error",
("host: '%s' socket: '%s' named_pipe: %d have_tcpip: %d",
host ? host : "<null>",
unix_socket ? unix_socket : "<null>",
(int) mysql->options.named_pipe,
(int) have_tcpip));
if (mysql->options.named_pipe ||
(host && !strcmp(host,LOCAL_HOST_NAMEDPIPE)) ||
(unix_socket && !strcmp(unix_socket,MYSQL_NAMEDPIPE)))
goto error; /* User only requested named pipes */
/* Try also with TCP/IP */
cinfo.type= CIO_TYPE_NAMEDPIPE;
sprintf(host_info=buff,ER(CR_NAMEDPIPE_CONNECTION),cinfo.host);
}
else
{
net->vio=vio_new_win32pipe(hPipe);
sprintf(host_info=buff, ER(CR_NAMEDPIPE_CONNECTION), host,
unix_socket);
}
}
}
if (hPipe == INVALID_HANDLE_VALUE)
#endif
{
struct addrinfo hints, *save_res= 0, *res= 0;
char server_port[NI_MAXSERV];
int gai_rc;
int rc;
unix_socket=0; /* This is not used */
cinfo.unix_socket=0; /* This is not used */
if (!port)
port=mysql_port;
if (!host)
host=LOCAL_HOST;
sprintf(host_info=buff,ER(CR_TCP_CONNECTION),host);
bzero(&server_port, NI_MAXSERV);
cinfo.host= host;
cinfo.port= port;
cinfo.type= CIO_TYPE_SOCKET;
sprintf(host_info=buff,ER(CR_TCP_CONNECTION), cinfo.host);
}
/* Initialize and load cio plugin */
if (!(cio= ma_cio_init(&cinfo)))
goto error;
DBUG_PRINT("info",("Server name: '%s'. TCP sock: %d", host,port));
my_snprintf(server_port, NI_MAXSERV, "%d", port);
/* set hints for getaddrinfo */
bzero(&hints, sizeof(hints));
hints.ai_protocol= IPPROTO_TCP; /* TCP connections only */
hints.ai_family= AF_UNSPEC; /* includes: IPv4, IPv6 or hostname */
hints.ai_socktype= SOCK_STREAM;
/* Get the address information for the server using getaddrinfo() */
gai_rc= getaddrinfo(host, server_port, &hints, &res);
if (gai_rc != 0)
/* try to connect */
if (ma_cio_connect(cio, &cinfo) != 0)
{
my_set_error(mysql, CR_UNKNOWN_HOST, SQLSTATE_UNKNOWN,
ER(CR_UNKNOWN_HOST), host, gai_rc);
ma_cio_close(cio);
goto error;
}
/* res is a linked list of addresses. If connect to an address fails we will not return
an error, instead we will try the next address */
for (save_res= res; save_res; save_res= save_res->ai_next)
{
sock= socket(save_res->ai_family, save_res->ai_socktype,
save_res->ai_protocol);
if (sock == SOCKET_ERROR)
/* we do error handling after for loop only for last call */
continue;
if (!(net->vio= vio_new(sock, VIO_TYPE_TCPIP, FALSE)))
{
my_set_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0);
closesocket(sock);
freeaddrinfo(res);
if (my_net_init(net, cio))
goto error;
}
rc= connect_sync_or_async(mysql, net, sock,
save_res->ai_addr, save_res->ai_addrlen);
if (!rc)
{
if (mysql->options.extension && mysql->options.extension->async_context &&
mysql->options.extension->async_context->active)
break;
else if (socket_block(sock, 1) == SOCKET_ERROR)
{
closesocket(sock);
continue;
}
break; /* success! */
}
vio_delete(mysql->net.vio);
mysql->net.vio= NULL;
}
freeaddrinfo(res);
if (sock == SOCKET_ERROR)
{
my_set_error(mysql, CR_IPSOCK_ERROR, SQLSTATE_UNKNOWN, ER(CR_IPSOCK_ERROR),
socket_errno);
goto error;
}
/* last call to connect 2 failed */
if (rc)
{
my_set_error(mysql, CR_CONN_HOST_ERROR, SQLSTATE_UNKNOWN, ER(CR_CONN_HOST_ERROR),
host, socket_errno);
goto error;
}
}
/* set timeouts */
net->vio->read_timeout= mysql->options.read_timeout;
net->vio->write_timeout= mysql->options.write_timeout;
if (mysql->options.extension && mysql->options.extension->async_context)
net->vio->async_context= mysql->options.extension->async_context;
if (!net->vio || my_net_init(net, net->vio))
{
vio_delete(net->vio);
net->vio = 0;
SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0);
goto error;
}
vio_keepalive(net->vio,TRUE);
ma_cio_keepalive(net->cio);
strmov(mysql->net.sqlstate, "00000");
/* set read timeout */
if (mysql->options.read_timeout >= 0)
vio_read_timeout(net->vio, mysql->options.read_timeout);
/* set write timeout */
if (mysql->options.write_timeout >= 0)
vio_write_timeout(net->vio, mysql->options.read_timeout);
/* Get version info */
mysql->protocol_version= PROTOCOL_VERSION; /* Assume this */
if (mysql->options.connect_timeout >= 0 &&
vio_wait_or_timeout(net->vio, FALSE, mysql->options.connect_timeout * 1000) < 1)
/*
if (ma_cio_wait_io_or_timeout(net->cio, FALSE, 0) < 1)
{
my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN,
ER(CR_SERVER_LOST_EXTENDED),
@@ -1778,6 +1458,7 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
errno);
goto error;
}
*/
if ((pkt_length=net_safe_read(mysql)) == packet_error)
{
if (mysql->net.last_errno == CR_SERVER_LOST)
@@ -1823,9 +1504,9 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
if (!my_multi_malloc(MYF(0),
&mysql->host_info, (uint) strlen(host_info)+1,
&mysql->host, (uint) strlen(host)+1,
&mysql->unix_socket,unix_socket ?
(uint) strlen(unix_socket)+1 : (uint) 1,
&mysql->host, (uint) strlen(cinfo.host)+1,
&mysql->unix_socket, cinfo.unix_socket ?
(uint) strlen(cinfo.unix_socket)+1 : (uint) 1,
&mysql->server_version, (uint) (end - (char*) net->read_pos),
NullS) ||
!(mysql->user=my_strdup(user,MYF(0))) ||
@@ -1835,9 +1516,9 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
goto error;
}
strmov(mysql->host_info,host_info);
strmov(mysql->host,host);
if (unix_socket)
strmov(mysql->unix_socket,unix_socket);
strmov(mysql->host, cinfo.host);
if (cinfo.unix_socket)
strmov(mysql->unix_socket, cinfo.unix_socket);
else
mysql->unix_socket=0;
mysql->port=port;
@@ -1987,11 +1668,9 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
strmov(mysql->net.sqlstate, "00000");
DBUG_PRINT("exit",("Mysql handler: %lx",mysql));
reset_sigpipe(mysql);
DBUG_RETURN(mysql);
error:
reset_sigpipe(mysql);
DBUG_PRINT("error",("message: %u (%s)",net->last_errno,net->last_error));
{
/* Free alloced memory */
@@ -2004,7 +1683,6 @@ error:
DBUG_RETURN(0);
}
static my_bool mysql_reconnect(MYSQL *mysql)
{
MYSQL tmp_mysql;
@@ -2230,8 +1908,6 @@ static void mysql_close_memory(MYSQL *mysql)
mysql->host_info= mysql->server_version=mysql->user=mysql->passwd=mysql->db=0;
}
void my_set_error(MYSQL *mysql,
unsigned int error_nr,
const char *sqlstate,
@@ -2254,7 +1930,7 @@ void my_set_error(MYSQL *mysql,
void mysql_close_slow_part(MYSQL *mysql)
{
if (mysql->net.vio)
if (mysql->net.cio)
{
free_old_query(mysql);
mysql->status=MYSQL_STATUS_READY; /* Force command */
@@ -2273,6 +1949,14 @@ mysql_close(MYSQL *mysql)
{
LIST *li_stmt= mysql->stmts;
if (mysql->net.conn_hdlr && mysql->net.conn_hdlr->data)
{
void *p= (void *)mysql->net.conn_hdlr;
mysql->net.conn_hdlr->plugin->close(mysql);
my_free(p);
DBUG_VOID_RETURN;
}
if (mysql->methods)
mysql->methods->db_close(mysql);
@@ -2290,7 +1974,11 @@ mysql_close(MYSQL *mysql)
/* Clear pointers for better safety */
bzero((char*) &mysql->options,sizeof(mysql->options));
mysql->net.vio= 0;
if (mysql->extension)
my_free(mysql->extension);
mysql->net.cio= 0;
if (mysql->free_me)
my_free(mysql);
}
@@ -2933,7 +2621,7 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
#else
if (*(uint *)arg1 > MYSQL_PROTOCOL_SOCKET)
#endif
DBUG_RETURN(-1);
goto end;
mysql->options.protocol= *(uint *)arg1;
break;
case MYSQL_OPT_READ_TIMEOUT:
@@ -2955,11 +2643,12 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
(void (*)(const MYSQL *, uint, uint, double, const char *, uint)) arg1;
break;
case MYSQL_PLUGIN_DIR:
OPT_SET_EXTENDED_VALUE(&mysql->options, plugin_dir, (char *)arg1, 1);
OPT_SET_EXTENDED_VALUE_STR(&mysql->options, plugin_dir, (char *)arg1);
break;
case MYSQL_DEFAULT_AUTH:
OPT_SET_EXTENDED_VALUE(&mysql->options, default_auth, (char *)arg1, 1);
OPT_SET_EXTENDED_VALUE_STR(&mysql->options, default_auth, (char *)arg1);
break;
/*
case MYSQL_DATABASE_DRIVER:
{
MARIADB_DB_PLUGIN *db_plugin;
@@ -2983,6 +2672,7 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
}
}
break;
*/
case MYSQL_OPT_NONBLOCK:
if (mysql->options.extension &&
(ctxt = mysql->options.extension->async_context) != 0)
@@ -2992,7 +2682,7 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
suspended (as the stack is then in use).
*/
if (ctxt->suspended)
DBUG_RETURN(1);
goto end;
my_context_destroy(&ctxt->async_context);
my_free(ctxt);
}
@@ -3000,7 +2690,7 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
my_malloc(sizeof(*ctxt), MYF(MY_ZEROFILL))))
{
SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0);
DBUG_RETURN(1);
goto end;
}
stacksize= 0;
if (arg1)
@@ -3010,7 +2700,7 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
if (my_context_init(&ctxt->async_context, stacksize))
{
my_free(ctxt);
DBUG_RETURN(1);
goto end;
}
if (!mysql->options.extension)
if(!(mysql->options.extension= (struct st_mysql_options_extension *)
@@ -3018,11 +2708,11 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
MYF(MY_WME | MY_ZEROFILL))))
{
SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0);
DBUG_RETURN(1);
goto end;
}
mysql->options.extension->async_context= ctxt;
if (mysql->net.vio)
mysql->net.vio->async_context= ctxt;
if (mysql->net.cio)
mysql->net.cio->async_context= ctxt;
break;
case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
@@ -3052,16 +2742,10 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
mysql->options.ssl_cipher=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR));
break;
case MYSQL_OPT_SSL_CRL:
OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_crl, (char *)arg1, 1);
OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_crl, (char *)arg1);
break;
case MYSQL_OPT_SSL_CRLPATH:
OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_crlpath, (char *)arg1, 1);
break;
case MARIADB_OPT_SSL_FP:
OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_fp, (char *)arg1, 1);
break;
case MARIADB_OPT_SSL_FP_LIST:
OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_fp_list, (char *)arg1, 1);
OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_crlpath, (char *)arg1);
break;
case MYSQL_OPT_CONNECT_ATTR_DELETE:
{
@@ -3150,6 +2834,21 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
case MYSQL_SECURE_AUTH:
mysql->options.secure_auth= *(my_bool *)arg1;
break;
case MYSQL_OPT_BIND:
my_free(mysql->options.bind_address);
mysql->options.bind_address= my_strdup(arg1, MYF(MY_WME));
break;
case MARIADB_OPT_SSL_FP:
OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_fp, (char *)arg1);
break;
case MARIADB_OPT_SSL_FP_LIST:
OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_fp_list, (char *)arg1);
break;
case MARIADB_OPT_CONNECTION_READ_ONLY:
if (mysql->net.conn_hdlr)
DBUG_RETURN(mysql->net.conn_hdlr->plugin->options(mysql, MARIADB_OPT_CONNECTION_READ_ONLY, arg1));
else
return -1;
default:
va_end(ap);
DBUG_RETURN(-1);
@@ -3454,9 +3153,6 @@ int STDCALL mysql_server_init(int argc __attribute__((unused)),
mysql_unix_port = env;
}
mysql_debug(NullS);
#if defined(SIGPIPE) && !defined(THREAD) && !defined(_WIN32)
(void) signal(SIGPIPE,SIG_IGN);
#endif
}
#ifdef THREAD
else
@@ -3471,12 +3167,10 @@ void STDCALL mysql_server_end()
{
if (!mysql_client_init)
return;
#ifdef HAVE_OPENSSL
my_ssl_end();
#endif
mysql_client_plugin_deinit();
list_free(cio_callback, 0);
if (my_init_done)
my_end(0);
mysql_client_init= 0;
@@ -3552,9 +3246,13 @@ mysql_get_parameters(void)
my_socket STDCALL
mysql_get_socket(const MYSQL *mysql)
{
if (mysql->net.vio)
return vio_fd(mysql->net.vio);
return MARIADB_INVALID_SOCKET;
my_socket sock;
if (mysql->net.cio)
{
ma_cio_get_handle(mysql->net.cio, &sock);
return sock;
}
return INVALID_SOCKET;
}
/*

469
libmariadb/ma_cio.c Normal file
View File

@@ -0,0 +1,469 @@
/************************************************************************************
Copyright (C) 2015 MariaDB Corporation AB,
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not see <http://www.gnu.org/licenses>
or write to the Free Software Foundation, Inc.,
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
*************************************************************************************/
/* MariaDB Communication IO (CIO) interface
CIO is the interface for client server communication and replaces former vio
component of the client library.
CIO support various protcols like sockets, pipes and shared memory, which are
implemented as plugins and can be extended therfore easily.
Interface function description:
ma_cio_init allocates a new CIO object which will be used
for the current connection
ma_cio_close frees all resources of previously allocated CIO object
and closes open connections
ma_cio_read reads data from server
ma_cio_write sends data to server
ma_cio_set_timeout sets timeout for connection, read and write
ma_cio_register_callback
register callback functions for read and write
*/
#include <my_global.h>
#include <my_sys.h>
#include <mysql.h>
#include <errmsg.h>
#include <mysql/client_plugin.h>
#include <string.h>
#include <ma_common.h>
#include <ma_cio.h>
#include <mysql_async.h>
#include <my_context.h>
extern pthread_mutex_t THR_LOCK_lock;
/* callback functions for read/write */
LIST *cio_callback= NULL;
/* {{{ MARIADB_CIO *ma_cio_init */
MARIADB_CIO *ma_cio_init(MA_CIO_CINFO *cinfo)
{
/* check connection type and load the required plugin.
* Currently we support the following cio types:
* cio_socket
* cio_namedpipe
*/
char *cio_plugins[] = {"cio_socket", "cio_npipe"};
int type;
MARIADB_CIO_PLUGIN *cio_plugin;
MARIADB_CIO *cio= NULL;
switch (cinfo->type)
{
case CIO_TYPE_UNIXSOCKET:
case CIO_TYPE_SOCKET:
type= 0;
break;
#ifdef _WIN32
case CIO_TYPE_NAMEDPIPE:
type= 1;
break;
#endif
default:
return NULL;
}
if (!(cio_plugin= (MARIADB_CIO_PLUGIN *)
mysql_client_find_plugin(cinfo->mysql,
cio_plugins[type],
MARIADB_CLIENT_CIO_PLUGIN)))
{
/* error handling */
return NULL;
}
if (!(cio= (MARIADB_CIO *)my_malloc(sizeof(MARIADB_CIO),
MYF(MY_WME | MY_ZEROFILL))))
{
CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0);
return NULL;
}
/* register error routine and methods */
cio->methods= cio_plugin->methods;
cio->set_error= my_set_error;
/* set tineouts */
if (cio->methods->set_timeout)
{
cio->methods->set_timeout(cio, CIO_CONNECT_TIMEOUT, cinfo->mysql->options.connect_timeout);
cio->methods->set_timeout(cio, CIO_READ_TIMEOUT, cinfo->mysql->options.read_timeout);
cio->methods->set_timeout(cio, CIO_WRITE_TIMEOUT, cinfo->mysql->options.write_timeout);
}
if (!(cio->cache= my_malloc(CIO_READ_AHEAD_CACHE_SIZE, MYF(MY_ZEROFILL))))
{
CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0);
return NULL;
}
cio->cache_size= 0;
cio->cache_pos= cio->cache;
return cio;
}
/* }}} */
/* {{{ my_bool ma_cio_is_alive */
my_bool ma_cio_is_alive(MARIADB_CIO *cio)
{
if (cio->methods->is_alive)
return cio->methods->is_alive(cio);
}
/* }}} */
/* {{{ int ma_cio_fast_send */
int ma_cio_fast_send(MARIADB_CIO *cio)
{
if (!cio || !cio->methods->fast_send)
return 1;
return cio->methods->fast_send(cio);
}
/* }}} */
/* {{{ int ma_cio_keepalive */
int ma_cio_keepalive(MARIADB_CIO *cio)
{
if (!cio || !cio->methods->keepalive)
return 1;
return cio->methods->keepalive(cio);
}
/* }}} */
/* {{{ my_bool ma_cio_set_timeout */
my_bool ma_cio_set_timeout(MARIADB_CIO *cio,
enum enum_cio_timeout type,
int timeout)
{
if (!cio)
return 1;
if (cio->methods->set_timeout)
return cio->methods->set_timeout(cio, type, timeout);
return 1;
}
/* }}} */
/* {{{ size_t ma_cio_read_async */
static size_t ma_cio_read_async(MARIADB_CIO *cio, const uchar *buffer, size_t length)
{
ssize_t res;
struct mysql_async_context *b= cio->async_context;
int timeout= cio->timeout[CIO_READ_TIMEOUT];
for (;;)
{
/* todo: async */
if (cio->methods->async_read)
res= cio->methods->async_read(cio, buffer, length);
if (res >= 0 /* || IS_BLOCKING_ERROR()*/)
return res;
b->events_to_wait_for= MYSQL_WAIT_READ;
if (timeout >= 0)
{
b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT;
b->timeout_value= timeout;
}
if (b->suspend_resume_hook)
(*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
my_context_yield(&b->async_context);
if (b->suspend_resume_hook)
(*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
if (b->events_occured & MYSQL_WAIT_TIMEOUT)
return -1;
}
}
/* }}} */
/* {{{ size_t ma_cio_read */
size_t ma_cio_read(MARIADB_CIO *cio, const uchar *buffer, size_t length)
{
size_t r= -1;
if (!cio)
return -1;
if (cio && cio->async_context && cio->async_context->active)
{
goto end;
r= ma_cio_read_async(cio, buffer, length);
}
else
{
if (cio->async_context)
{
/*
If switching from non-blocking to blocking API usage, set the socket
back to blocking mode.
*/
my_bool old_mode;
ma_cio_blocking(cio, TRUE, &old_mode);
}
}
/* secure connection */
#ifdef HAVE_SSL
if (cio->cssl)
r= ma_cio_ssl_read(cio->cssl, buffer, length);
else
#endif
if (cio->methods->read)
r= cio->methods->read(cio, buffer, length);
end:
if (cio_callback)
{
void (*callback)(int mode, MYSQL *mysql, const uchar *buffer, size_t length);
LIST *p= cio_callback;
while (p)
{
callback= p->data;
callback(0, cio->mysql, buffer, r);
p= p->next;
}
}
return r;
}
/* }}} */
/* {{{ size_t ma_cio_cache_read */
size_t ma_cio_cache_read(MARIADB_CIO *cio, uchar *buffer, size_t length)
{
size_t r;
if (!cio)
return -1;
if (!cio->cache)
return ma_cio_read(cio, buffer, length);
if (cio->cache + cio->cache_size > cio->cache_pos)
{
r= MIN(length, cio->cache + cio->cache_size - cio->cache_pos);
memcpy(buffer, cio->cache_pos, r);
cio->cache_pos+= r;
}
else if (length >= CIO_READ_AHEAD_CACHE_MIN_SIZE)
{
r= ma_cio_read(cio, buffer, length);
}
else
{
r= ma_cio_read(cio, cio->cache, CIO_READ_AHEAD_CACHE_SIZE);
if ((ssize_t)r > 0)
{
if (length < r)
{
cio->cache_size= r;
cio->cache_pos= cio->cache + length;
r= length;
}
memcpy(buffer, cio->cache, r);
}
}
return r;
}
/* }}} */
/* {{{ size_t ma_cio_write */
size_t ma_cio_write(MARIADB_CIO *cio, const uchar *buffer, size_t length)
{
size_t r;
if (!cio)
return -1;
if (cio_callback)
{
void (*callback)(int mode, MYSQL *mysql, const uchar *buffer, size_t length);
LIST *p= cio_callback;
while (p)
{
callback= p->data;
callback(1, cio->mysql, buffer, length);
p= p->next;
}
}
/* secure connection */
#ifdef HAVE_SSL
if (cio->cssl)
r= ma_cio_ssl_write(cio->cssl, buffer, length);
else
#endif
if (cio->methods->write)
r= cio->methods->write(cio, buffer, length);
if (cio->callback)
cio->callback(cio, 0, buffer, r);
return r;
}
/* }}} */
/* {{{ void ma_cio_close */
void ma_cio_close(MARIADB_CIO *cio)
{
/* free internal structures and close connection */
#ifdef HAVE_SSL
if (cio && cio->cssl)
{
ma_cio_ssl_close(cio->cssl);
my_free((gptr)cio->cssl);
}
#endif
if (cio && cio->methods->close)
cio->methods->close(cio);
if (cio->cache)
my_free((gptr)cio->cache);
if (cio->fp)
my_fclose(cio->fp, MYF(0));
my_free((gptr)cio);
}
/* }}} */
/* {{{ my_bool ma_cio_get_handle */
my_bool ma_cio_get_handle(MARIADB_CIO *cio, void *handle)
{
if (cio && cio->methods->get_handle)
return cio->methods->get_handle(cio, handle);
return 1;
}
/* }}} */
/* {{{ ma_cio_wait_io_or_timeout */
int ma_cio_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout)
{
if (cio && cio->async_context && cio->async_context->active)
return my_io_wait_async(cio->async_context,
(is_read) ? VIO_IO_EVENT_READ : VIO_IO_EVENT_WRITE,
timeout);
if (cio && cio->methods->wait_io_or_timeout)
return cio->methods->wait_io_or_timeout(cio, is_read, timeout);
return 1;
}
/* }}} */
/* {{{ my_bool ma_cio_connect */
my_bool ma_cio_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo)
{
if (cio && cio->methods->connect)
return cio->methods->connect(cio, cinfo);
return 1;
}
/* }}} */
/* {{{ my_bool ma_cio_blocking */
my_bool ma_cio_blocking(MARIADB_CIO *cio, my_bool block, my_bool *previous_mode)
{
if (cio && cio->methods->blocking)
return cio->methods->blocking(cio, block, previous_mode);
return 1;
}
/* }}} */
/* {{{ my_bool ma_cio_is_blocking */
my_bool ma_cio_is_blocking(MARIADB_CIO *cio)
{
if (cio && cio->methods->is_blocking)
return cio->methods->is_blocking(cio);
return 1;
}
/* }}} */
#ifdef HAVE_SSL
/* {{{ my_bool ma_cio_start_ssl */
my_bool ma_cio_start_ssl(MARIADB_CIO *cio)
{
if (!cio || !cio->mysql)
return 1;
CLEAR_CLIENT_ERROR(cio->mysql);
if (!(cio->cssl= ma_cio_ssl_init(cio->mysql)))
{
return 1;
}
if (ma_cio_ssl_connect(cio->cssl))
{
my_free((gptr)cio->cssl);
cio->cssl= NULL;
return 1;
}
if ((cio->mysql->options.ssl_ca || cio->mysql->options.ssl_capath) &&
(cio->mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) &&
ma_cio_ssl_verify_server_cert(cio->cssl))
return 1;
if (cio->mysql->options.extension &&
(cio->mysql->options.extension->ssl_fp || cio->mysql->options.extension->ssl_fp_list))
{
if (ma_cio_ssl_check_fp(cio->cssl,
cio->mysql->options.extension->ssl_fp,
cio->mysql->options.extension->ssl_fp_list))
return 1;
}
return 0;
}
/* }}} */
#endif
/* {{{ ma_cio_register_callback */
int ma_cio_register_callback(my_bool register_callback,
void (*callback_function)(int mode, MYSQL *mysql, const uchar *buffer, size_t length))
{
LIST *list;
if (!callback_function)
return 1;
/* plugin will unregister in it's deinit function */
if (register_callback)
{
list= (LIST *)malloc(sizeof(LIST));
list->data= (void *)callback_function;
cio_callback= list_add(cio_callback, list);
}
else /* unregister callback function */
{
LIST *p= cio_callback;
while (p)
{
if (p->data == callback_function)
{
list_delete(cio_callback, p);
break;
}
p= p->next;
}
}
return 0;
}
/* }}} */

View File

@@ -129,7 +129,7 @@ remote:
{
MYSQL mysql;
if (rio_plugin ||(rio_plugin= (struct st_mysql_client_plugin_REMOTEIO *)
mysql_client_find_plugin(&mysql, NULL, MYSQL_CLIENT_REMOTEIO_PLUGIN)))
mysql_client_find_plugin(&mysql, NULL, MARIADB_CLIENT_REMOTEIO_PLUGIN)))
return rio_plugin->methods->open(location, mode);
return NULL;
}

View File

@@ -1,687 +0,0 @@
/************************************************************************************
Copyright (C) 2012 Monty Program AB
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not see <http://www.gnu.org/licenses>
or write to the Free Software Foundation, Inc.,
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
*************************************************************************************/
unsigned int mariadb_deinitialize_ssl= 1;
#ifdef HAVE_OPENSSL
#include <my_global.h>
#include <my_sys.h>
#include <ma_common.h>
#include <ma_secure.h>
#include <errmsg.h>
#include <violite.h>
#include <mysql_async.h>
#include <my_context.h>
static my_bool my_ssl_initialized= FALSE;
static SSL_CTX *SSL_context= NULL;
#define MAX_SSL_ERR_LEN 100
extern pthread_mutex_t LOCK_ssl_config;
static pthread_mutex_t *LOCK_crypto= NULL;
/*
SSL error handling
*/
static void my_SSL_error(MYSQL *mysql)
{
ulong ssl_errno= ERR_get_error();
char ssl_error[MAX_SSL_ERR_LEN];
const char *ssl_error_reason;
DBUG_ENTER("my_SSL_error");
if (mysql_errno(mysql))
DBUG_VOID_RETURN;
if (!ssl_errno)
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error");
DBUG_VOID_RETURN;
}
if ((ssl_error_reason= ERR_reason_error_string(ssl_errno)))
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR), ssl_error_reason);
DBUG_VOID_RETURN;
}
my_snprintf(ssl_error, MAX_SSL_ERR_LEN, "SSL errno=%lu", ssl_errno, mysql->charset);
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR), ssl_error);
DBUG_VOID_RETURN;
}
/*
thread safe callbacks for OpenSSL
Crypto call back functions will be
set during ssl_initialization
*/
#if (OPENSSL_VERSION_NUMBER < 0x10000000)
static unsigned long my_cb_threadid(void)
{
/* cast pthread_t to unsigned long */
return (unsigned long) pthread_self();
}
#else
static void my_cb_threadid(CRYPTO_THREADID *id)
{
CRYPTO_THREADID_set_numeric(id, (unsigned long)pthread_self());
}
#endif
static void my_cb_locking(int mode, int n, const char *file, int line)
{
if (mode & CRYPTO_LOCK)
pthread_mutex_lock(&LOCK_crypto[n]);
else
pthread_mutex_unlock(&LOCK_crypto[n]);
}
static int ssl_crypto_init()
{
int i, rc= 1, max= CRYPTO_num_locks();
#if (OPENSSL_VERSION_NUMBER < 0x10000000)
CRYPTO_set_id_callback(my_cb_threadid);
#else
rc= CRYPTO_THREADID_set_callback(my_cb_threadid);
#endif
/* if someone else already set callbacks
* there is nothing do */
if (!rc)
return 0;
if (LOCK_crypto == NULL)
{
if (!(LOCK_crypto=
(pthread_mutex_t *)my_malloc(sizeof(pthread_mutex_t) * max, MYF(0))))
return 1;
for (i=0; i < max; i++)
pthread_mutex_init(&LOCK_crypto[i], NULL);
}
CRYPTO_set_locking_callback(my_cb_locking);
return 0;
}
/*
Initializes SSL and allocate global
context SSL_context
SYNOPSIS
my_ssl_start
mysql connection handle
RETURN VALUES
0 success
1 error
*/
int my_ssl_start(MYSQL *mysql)
{
int rc= 0;
DBUG_ENTER("my_ssl_start");
/* lock mutex to prevent multiple initialization */
pthread_mutex_lock(&LOCK_ssl_config);
if (!my_ssl_initialized)
{
if (ssl_crypto_init())
goto end;
SSL_library_init();
#if SSLEAY_VERSION_NUMBER >= 0x00907000L
OPENSSL_config(NULL);
#endif
/* load errors */
SSL_load_error_strings();
/* digests and ciphers */
OpenSSL_add_all_algorithms();
if (!(SSL_context= SSL_CTX_new(TLSv1_client_method())))
{
my_SSL_error(mysql);
rc= 1;
goto end;
}
my_ssl_initialized= TRUE;
}
end:
pthread_mutex_unlock(&LOCK_ssl_config);
DBUG_RETURN(rc);
}
/*
Release SSL and free resources
Will be automatically executed by
mysql_server_end() function
SYNOPSIS
my_ssl_end()
void
RETURN VALUES
void
*/
void my_ssl_end()
{
DBUG_ENTER("my_ssl_end");
pthread_mutex_lock(&LOCK_ssl_config);
if (my_ssl_initialized)
{
int i;
if (LOCK_crypto)
{
CRYPTO_set_locking_callback(NULL);
CRYPTO_set_id_callback(NULL);
for (i=0; i < CRYPTO_num_locks(); i++)
pthread_mutex_destroy(&LOCK_crypto[i]);
my_free(LOCK_crypto);
LOCK_crypto= NULL;
}
if (SSL_context)
{
SSL_CTX_free(SSL_context);
SSL_context= FALSE;
}
if (mariadb_deinitialize_ssl)
{
ERR_remove_state(0);
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
ERR_free_strings();
CONF_modules_free();
CONF_modules_unload(1);
sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
}
my_ssl_initialized= FALSE;
}
pthread_mutex_unlock(&LOCK_ssl_config);
pthread_mutex_destroy(&LOCK_ssl_config);
DBUG_VOID_RETURN;
}
/*
Set certification stuff.
*/
static int my_ssl_set_certs(MYSQL *mysql)
{
char *certfile= mysql->options.ssl_cert,
*keyfile= mysql->options.ssl_key;
DBUG_ENTER("my_ssl_set_certs");
/* Make sure that ssl was allocated and
ssl_system was initialized */
DBUG_ASSERT(my_ssl_initialized == TRUE);
/* add cipher */
if ((mysql->options.ssl_cipher &&
mysql->options.ssl_cipher[0] != 0) &&
SSL_CTX_set_cipher_list(SSL_context, mysql->options.ssl_cipher) == 0)
goto error;
/* ca_file and ca_path */
if (SSL_CTX_load_verify_locations(SSL_context,
mysql->options.ssl_ca,
mysql->options.ssl_capath) == 0)
{
if (mysql->options.ssl_ca || mysql->options.ssl_capath)
goto error;
if (SSL_CTX_set_default_verify_paths(SSL_context) == 0)
goto error;
}
if (keyfile && !certfile)
certfile= keyfile;
if (certfile && !keyfile)
keyfile= certfile;
/* set cert */
if (certfile && certfile[0] != 0)
if (SSL_CTX_use_certificate_file(SSL_context, certfile, SSL_FILETYPE_PEM) != 1)
goto error;
/* set key */
if (keyfile && keyfile[0])
{
if (SSL_CTX_use_PrivateKey_file(SSL_context, keyfile, SSL_FILETYPE_PEM) != 1)
goto error;
}
/* verify key */
if (certfile && !SSL_CTX_check_private_key(SSL_context))
goto error;
if (mysql->options.extension &&
(mysql->options.extension->ssl_crl || mysql->options.extension->ssl_crlpath))
{
X509_STORE *certstore;
if ((certstore= SSL_CTX_get_cert_store(SSL_context)))
{
if (X509_STORE_load_locations(certstore, mysql->options.extension->ssl_crl,
mysql->options.extension->ssl_crlpath) == 0 ||
X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL) == 0)
goto error;
}
}
DBUG_RETURN(0);
error:
my_SSL_error(mysql);
DBUG_RETURN(1);
}
static unsigned int ma_get_cert_fingerprint(X509 *cert, EVP_MD *digest,
unsigned char *fingerprint, unsigned int *fp_length)
{
if (*fp_length < EVP_MD_size(digest))
return 0;
if (!X509_digest(cert, digest, fingerprint, fp_length))
return 0;
return *fp_length;
}
static my_bool ma_check_fingerprint(char *fp1, unsigned int fp1_len,
char *fp2, unsigned int fp2_len)
{
char hexstr[fp1_len * 2 + 1];
fp1_len= (unsigned int)mysql_hex_string(hexstr, fp1, fp1_len);
if (strncasecmp(hexstr, fp2, fp1_len) != 0)
return 1;
return 0;
}
int my_verify_callback(int ok, X509_STORE_CTX *ctx)
{
X509 *check_cert;
SSL *ssl;
MYSQL *mysql;
DBUG_ENTER("my_verify_callback");
ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
DBUG_ASSERT(ssl != NULL);
mysql= (MYSQL *)SSL_get_app_data(ssl);
DBUG_ASSERT(mysql != NULL);
/* skip verification if no ca_file/path was specified */
if (!mysql->options.ssl_ca && !mysql->options.ssl_capath)
{
ok= 1;
DBUG_RETURN(1);
}
if (!ok)
{
uint depth;
if (!(check_cert= X509_STORE_CTX_get_current_cert(ctx)))
DBUG_RETURN(0);
depth= X509_STORE_CTX_get_error_depth(ctx);
if (depth == 0)
ok= 1;
}
/*
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
X509_verify_cert_error_string(ctx->error));
*/
DBUG_RETURN(ok);
}
/*
allocates a new ssl object
SYNOPSIS
my_ssl_init
mysql connection object
RETURN VALUES
NULL on error
SSL new SSL object
*/
SSL *my_ssl_init(MYSQL *mysql)
{
int verify;
SSL *ssl= NULL;
DBUG_ENTER("my_ssl_init");
DBUG_ASSERT(mysql->net.vio->ssl == NULL);
if (!my_ssl_initialized)
my_ssl_start(mysql);
pthread_mutex_lock(&LOCK_ssl_config);
if (my_ssl_set_certs(mysql))
goto error;
if (!(ssl= SSL_new(SSL_context)))
goto error;
if (!SSL_set_app_data(ssl, mysql))
goto error;
verify= (!mysql->options.ssl_ca && !mysql->options.ssl_capath) ?
SSL_VERIFY_NONE : SSL_VERIFY_PEER;
SSL_CTX_set_verify(SSL_context, verify, my_verify_callback);
SSL_CTX_set_verify_depth(SSL_context, 1);
pthread_mutex_unlock(&LOCK_ssl_config);
DBUG_RETURN(ssl);
error:
pthread_mutex_unlock(&LOCK_ssl_config);
if (ssl)
SSL_free(ssl);
DBUG_RETURN(NULL);
}
/*
establish SSL connection between client
and server
SYNOPSIS
my_ssl_connect
ssl ssl object
RETURN VALUES
0 success
1 error
*/
int my_ssl_connect(SSL *ssl)
{
my_bool blocking;
MYSQL *mysql;
long rc;
DBUG_ENTER("my_ssl_connect");
DBUG_ASSERT(ssl != NULL);
mysql= (MYSQL *)SSL_get_app_data(ssl);
CLEAR_CLIENT_ERROR(mysql);
/* Set socket to blocking if not already set */
if (!(blocking= vio_is_blocking(mysql->net.vio)))
vio_blocking(mysql->net.vio, TRUE, 0);
SSL_clear(ssl);
SSL_SESSION_set_timeout(SSL_get_session(ssl),
mysql->options.connect_timeout);
SSL_set_fd(ssl, mysql->net.vio->sd);
if (SSL_connect(ssl) != 1)
{
my_SSL_error(mysql);
/* restore blocking mode */
if (!blocking)
vio_blocking(mysql->net.vio, FALSE, 0);
DBUG_RETURN(1);
}
rc= SSL_get_verify_result(ssl);
if (rc != X509_V_OK)
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR), X509_verify_cert_error_string(rc));
/* restore blocking mode */
if (!blocking)
vio_blocking(mysql->net.vio, FALSE, 0);
DBUG_RETURN(1);
}
vio_reset(mysql->net.vio, VIO_TYPE_SSL, mysql->net.vio->sd, 0, 0);
mysql->net.vio->ssl= ssl;
DBUG_RETURN(0);
}
int ma_ssl_verify_fingerprint(SSL *ssl)
{
X509 *cert= SSL_get_peer_certificate(ssl);
MYSQL *mysql= (MYSQL *)SSL_get_app_data(ssl);
unsigned char fingerprint[EVP_MAX_MD_SIZE];
EVP_MD *digest;
unsigned int fp_length;
DBUG_ENTER("ma_ssl_verify_fingerprint");
if (!cert)
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"Unable to get server certificate");
DBUG_RETURN(1);
}
digest= (EVP_MD *)EVP_sha1();
fp_length= sizeof(fingerprint);
if (!ma_get_cert_fingerprint(cert, digest, fingerprint, &fp_length))
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"Unable to get finger print of server certificate");
DBUG_RETURN(1);
}
/* single finger print was specified */
if (mysql->options.extension->ssl_fp)
{
if (ma_check_fingerprint(fingerprint, fp_length, mysql->options.extension->ssl_fp,
strlen(mysql->options.extension->ssl_fp)))
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"invalid finger print of server certificate");
DBUG_RETURN(1);
}
}
/* white list of finger prints was specified */
if (mysql->options.extension->ssl_fp_list)
{
FILE *fp;
char buff[255];
if (!(fp = my_fopen(mysql->options.extension->ssl_fp_list ,O_RDONLY, MYF(0))))
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"Can't open finger print list");
DBUG_RETURN(1);
}
while (fgets(buff, sizeof(buff)-1, fp))
{
/* remove trailing new line character */
char *pos= strchr(buff, '\r');
if (!pos)
pos= strchr(buff, '\n');
if (pos)
*pos= '\0';
if (!ma_check_fingerprint(fingerprint, fp_length, buff, strlen(buff)))
{
/* finger print is valid: close file and exit */
my_fclose(fp, MYF(0));
DBUG_RETURN(0);
}
}
/* No finger print matched - close file and return error */
my_fclose(fp, MYF(0));
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"invalid finger print of server certificate");
DBUG_RETURN(1);
}
DBUG_RETURN(0);
}
/*
verify server certificate
SYNOPSIS
my_ssl_verify_server_cert()
MYSQL mysql
mybool verify_server_cert;
RETURN VALUES
1 Error
0 OK
*/
int my_ssl_verify_server_cert(SSL *ssl)
{
X509 *cert;
MYSQL *mysql;
char *p1, *p2, buf[256];
DBUG_ENTER("my_ssl_verify_server_cert");
mysql= (MYSQL *)SSL_get_app_data(ssl);
if (!mysql->host)
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"Invalid (empty) hostname");
DBUG_RETURN(1);
}
if (!(cert= SSL_get_peer_certificate(ssl)))
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"Unable to get server certificate");
DBUG_RETURN(1);
}
X509_NAME_oneline(X509_get_subject_name(cert), buf, 256);
X509_free(cert);
/* Extract the server name from buffer:
Format: ....CN=/hostname/.... */
if ((p1= strstr(buf, "/CN=")))
{
p1+= 4;
if ((p2= strchr(p1, '/')))
*p2= 0;
if (!strcmp(mysql->host, p1))
DBUG_RETURN(0);
}
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"Validation of SSL server certificate failed");
DBUG_RETURN(1);
}
/*
write to ssl socket
SYNOPSIS
my_ssl_write()
vio vio
buf write buffer
size size of buffer
RETURN VALUES
bytes written
*/
size_t my_ssl_write(Vio *vio, const uchar* buf, size_t size)
{
size_t written;
DBUG_ENTER("my_ssl_write");
if (vio->async_context && vio->async_context->active)
written= my_ssl_write_async(vio->async_context, (SSL *)vio->ssl, buf,
size);
else
written= SSL_write((SSL*) vio->ssl, buf, size);
DBUG_RETURN(written);
}
/*
read from ssl socket
SYNOPSIS
my_ssl_read()
vio vio
buf read buffer
size_t max number of bytes to read
RETURN VALUES
number of bytes read
*/
size_t my_ssl_read(Vio *vio, uchar* buf, size_t size)
{
size_t read;
DBUG_ENTER("my_ssl_read");
if (vio->async_context && vio->async_context->active)
read= my_ssl_read_async(vio->async_context, (SSL *)vio->ssl, buf, size);
else
read= SSL_read((SSL*) vio->ssl, buf, size);
DBUG_RETURN(read);
}
/*
close ssl connection and free
ssl object
SYNOPSIS
my_ssl_close()
vio vio
RETURN VALUES
1 ok
0 or -1 on error
*/
int my_ssl_close(Vio *vio)
{
int i, rc;
DBUG_ENTER("my_ssl_close");
if (!vio || !vio->ssl)
DBUG_RETURN(1);
SSL_set_quiet_shutdown(vio->ssl, 1);
/* 2 x pending + 2 * data = 4 */
for (i=0; i < 4; i++)
if ((rc= SSL_shutdown(vio->ssl)))
break;
SSL_free(vio->ssl);
vio->ssl= NULL;
DBUG_RETURN(rc);
}
#endif /* HAVE_OPENSSL */

174
libmariadb/ma_ssl.c Normal file
View File

@@ -0,0 +1,174 @@
/************************************************************************************
Copyright (C) 2014 MariaDB Corporation AB
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not see <http://www.gnu.org/licenses>
or write to the Free Software Foundation, Inc.,
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
*************************************************************************************/
/*
* this is the abstraction layer for communication via SSL.
* The following SSL libraries/variants are currently supported:
* - openssl
* - gnutls
* - schannel (windows only)
*
* Different SSL variants are implemented as plugins
* On Windows schannel is implemented as (standard)
* built-in plugin.
*/
#ifdef HAVE_SSL
#include <my_global.h>
#include <my_sys.h>
#include <ma_common.h>
#include <string.h>
//#include <ma_secure.h>
#include <errmsg.h>
#include <ma_cio.h>
#include <ma_ssl.h>
#include <mysql/client_plugin.h>
/*
#include <mysql_async.h>
#include <my_context.h>
*/
/* Errors should be handled via cio callback function */
my_bool ma_ssl_initialized= FALSE;
unsigned int mariadb_deinitialize_ssl= 1;
MARIADB_SSL *ma_cio_ssl_init(MYSQL *mysql)
{
MARIADB_SSL *cssl= NULL;
if (!ma_ssl_initialized)
ma_ssl_start(mysql->net.last_error, MYSQL_ERRMSG_SIZE);
if (!(cssl= (MARIADB_SSL *)my_malloc(sizeof(MARIADB_CIO),
MYF(MY_WME | MY_ZEROFILL))))
{
return NULL;
}
/* register error routine and methods */
cssl->cio= mysql->net.cio;
if (!(cssl->ssl= ma_ssl_init(mysql)))
{
my_free(cssl);
cssl= NULL;
}
return cssl;
}
my_bool ma_cio_ssl_connect(MARIADB_SSL *cssl)
{
return ma_ssl_connect(cssl);
}
size_t ma_cio_ssl_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length)
{
return ma_ssl_read(cssl, buffer, length);
}
size_t ma_cio_ssl_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length)
{
return ma_ssl_write(cssl, buffer, length);
}
my_bool ma_cio_ssl_close(MARIADB_SSL *cssl)
{
return ma_ssl_close(cssl);
}
int ma_cio_ssl_verify_server_cert(MARIADB_SSL *cssl)
{
return ma_ssl_verify_server_cert(cssl);
}
const char *ma_cio_ssl_cipher(MARIADB_SSL *cssl)
{
return ma_ssl_get_cipher(cssl);
}
static my_bool ma_cio_ssl_compare_fp(char *fp1, unsigned int fp1_len,
char *fp2, unsigned int fp2_len)
{
char hexstr[64];
fp1_len= (unsigned int)mysql_hex_string(hexstr, fp1, fp1_len);
#ifdef WIN32
if (strnicmp(hexstr, fp2, fp1_len) != 0)
#else
if (strncasecmp(hexstr, fp2, fp1_len) != 0)
#endif
return 1;
return 0;
}
my_bool ma_cio_ssl_check_fp(MARIADB_SSL *cssl, const char *fp, const char *fp_list)
{
unsigned int cert_fp_len= 64;
unsigned char cert_fp[64];
MYSQL *mysql;
my_bool rc=1;
if ((cert_fp_len= ma_ssl_get_finger_print(cssl, cert_fp, cert_fp_len)) < 1)
goto end;
if (fp)
rc= ma_cio_ssl_compare_fp(cert_fp, cert_fp_len, fp, strlen(fp));
else if (fp_list)
{
FILE *fp;
char buff[255];
if (!(fp = fopen(fp_list, "r")))
{
/*
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"Can't open finger print list");
*/
goto end;
}
while (fgets(buff, sizeof(buff)-1, fp))
{
/* remove trailing new line character */
char *pos= strchr(buff, '\r');
if (!pos)
pos= strchr(buff, '\n');
if (pos)
*pos= '\0';
if (!ma_cio_ssl_compare_fp(cert_fp, cert_fp_len, buff, strlen(buff)))
{
/* finger print is valid: close file and exit */
fclose(fp);
rc= 0;
goto end;
}
}
/* No finger print matched - close file and return error */
fclose(fp);
}
end:
return rc;
}
#endif /* HAVE_SSL */

View File

@@ -53,7 +53,7 @@
#include "mysql_version.h"
#include "mysqld_error.h"
#include "errmsg.h"
#include <violite.h>
#include <ma_cio.h>
#include <sys/stat.h>
#include <signal.h>
#include <time.h>
@@ -988,7 +988,7 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt)
{
DBUG_ENTER("mysql_stmt_close");
if (stmt && stmt->mysql && stmt->mysql->net.vio)
if (stmt && stmt->mysql && stmt->mysql->net.cio)
mysql_stmt_reset(stmt);
net_stmt_close(stmt, 1);

View File

@@ -30,8 +30,9 @@
#include "ma_common.h"
#endif
#include "my_context.h"
#include "violite.h"
#include "ma_cio.h"
#include "mysql_async.h"
#include <string.h>
#ifdef _WIN32
@@ -41,7 +42,7 @@
*/
#define WIN_SET_NONBLOCKING(mysql) { \
my_bool old_mode; \
if ((mysql)->net.vio) vio_blocking((mysql)->net.vio, FALSE, &old_mode); \
if ((mysql)->net.cio) ma_cio_blocking((mysql)->net.cio, FALSE, &old_mode); \
}
#else
#define WIN_SET_NONBLOCKING(mysql)
@@ -62,19 +63,18 @@ my_context_install_suspend_resume_hook(struct mysql_async_context *b,
/* Asynchronous connect(); socket must already be set non-blocking. */
int
my_connect_async(struct mysql_async_context *b, my_socket fd,
my_connect_async(MARIADB_CIO *cio,
const struct sockaddr *name, uint namelen, int vio_timeout)
{
int res;
size_socket s_err_size;
struct mysql_async_context *b= cio->mysql->options.extension->async_context;
my_socket sock;
ma_cio_get_handle(cio, &sock);
/* Make the socket non-blocking. */
#ifdef _WIN32
ulong arg= 1;
ioctlsocket(fd, FIONBIO, (void *)&arg);
#else
fcntl(fd, F_SETFL, O_NONBLOCK);
#endif
ma_cio_blocking(cio, 0, 0);
b->events_to_wait_for= 0;
/*
@@ -83,7 +83,7 @@ my_connect_async(struct mysql_async_context *b, my_socket fd,
application context. The application will then resume us when the socket
polls ready for write, indicating that the connection attempt completed.
*/
res= connect(fd, name, namelen);
res= connect(sock, name, namelen);
if (res != 0)
{
#ifdef _WIN32
@@ -113,7 +113,7 @@ my_connect_async(struct mysql_async_context *b, my_socket fd,
return -1;
s_err_size= sizeof(res);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*) &res, &s_err_size) != 0)
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*) &res, &s_err_size) != 0)
return -1;
if (res)
{
@@ -135,14 +135,15 @@ my_connect_async(struct mysql_async_context *b, my_socket fd,
#endif
ssize_t
my_recv_async(struct mysql_async_context *b, int fd,
unsigned char *buf, size_t size, int timeout)
my_recv_async(MARIADB_CIO *cio, const unsigned char *buf, size_t size, int timeout)
{
ssize_t res;
struct mysql_async_context *b= cio->async_context;
for (;;)
{
res= recv(fd, buf, size, IF_WIN(0, MSG_DONTWAIT));
/* todo: async */
if (cio->methods->async_read)
res= cio->methods->async_read(cio, buf, size);
if (res >= 0 || IS_BLOCKING_ERROR())
return res;
b->events_to_wait_for= MYSQL_WAIT_READ;
@@ -163,14 +164,15 @@ my_recv_async(struct mysql_async_context *b, int fd,
ssize_t
my_send_async(struct mysql_async_context *b, int fd,
const unsigned char *buf, size_t size, int timeout)
my_send_async(MARIADB_CIO *cio, const unsigned char *buf, size_t size, int timeout)
{
ssize_t res;
struct mysql_async_context *b= cio->async_context;
for (;;)
{
res= send(fd, buf, size, IF_WIN(0, MSG_DONTWAIT));
if (cio->methods->async_write)
res= cio->methods->async_write(cio, buf, size);
if (res >= 0 || IS_BLOCKING_ERROR())
return res;
b->events_to_wait_for= MYSQL_WAIT_WRITE;
@@ -191,7 +193,7 @@ my_send_async(struct mysql_async_context *b, int fd,
my_bool
my_io_wait_async(struct mysql_async_context *b, enum enum_vio_io_event event,
my_io_wait_async(struct mysql_async_context *b, enum enum_cio_io_event event,
int timeout)
{
switch (event)
@@ -221,9 +223,9 @@ my_io_wait_async(struct mysql_async_context *b, enum enum_vio_io_event event,
}
#ifdef HAVE_OPENSSL
#ifdef HAVE_SSL_FIXME
static my_bool
my_ssl_async_check_result(int res, struct mysql_async_context *b, SSL *ssl)
my_ssl_async_check_result(int res, struct mysql_async_context *b, MARIADB_SSL *cssl)
{
int ssl_err;
b->events_to_wait_for= 0;
@@ -496,18 +498,36 @@ MK_ASYNC_INTERNAL_BODY(
int STDCALL
mysql_real_query_start(int *ret, MYSQL *mysql, const char *stmt_str, unsigned long length)
{
MK_ASYNC_START_BODY(
mysql_real_query,
mysql,
int res;
struct mysql_async_context *b;
struct mysql_real_query_params parms;
b= mysql->options.extension->async_context;
{
WIN_SET_NONBLOCKING(mysql)
parms.mysql= mysql;
parms.stmt_str= stmt_str;
parms.length= length;
},
1,
r_int,
/* Nothing */)
}
b->active= 1;
res= my_context_spawn(&b->async_context, mysql_real_query_start_internal, &parms);
b->active= b->suspended= 0;
if (res > 0)
{
/* Suspended. */
b->suspended= 1;
return b->events_to_wait_for;
}
if (res < 0)
{
set_mysql_error((mysql), CR_OUT_OF_MEMORY, unknown_sqlstate);
*ret= 1;
}
else
*ret= b->ret_result.r_int;
return 0;
}
int STDCALL
mysql_real_query_cont(int *ret, MYSQL *mysql, int ready_status)
@@ -805,7 +825,7 @@ mysql_close_start(MYSQL *sock)
int res;
/* It is legitimate to have NULL sock argument, which will do nothing. */
if (sock && sock->net.vio)
if (sock && sock->net.cio)
{
res= mysql_close_slow_part_start(sock);
/* If we need to block, return now and do the rest in mysql_close_cont(). */

View File

@@ -22,8 +22,10 @@
** 3 byte length & 1 byte package-number.
*/
#include <my_global.h>
#include <violite.h>
#include <mysql.h>
#include <ma_cio.h>
#include <my_sys.h>
#include <m_string.h>
#include "mysql.h"
@@ -31,6 +33,7 @@
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <ma_cio.h>
#ifndef _WIN32
#include <poll.h>
#endif
@@ -110,13 +113,13 @@ static int net_write_buff(NET *net,const char *packet, size_t len);
/* Init with packet info */
int my_net_init(NET *net, Vio* vio)
int my_net_init(NET *net, MARIADB_CIO* cio)
{
if (!(net->buff=(uchar*) my_malloc(net_buffer_length,MYF(MY_WME | MY_ZEROFILL))))
return 1;
max_allowed_packet= net->max_packet_size= MAX(net_buffer_length, max_allowed_packet);
net->buff_end=net->buff+(net->max_packet=net_buffer_length);
net->vio = vio;
net->cio = cio;
net->error=0; net->return_status=0;
net->read_timeout=(uint) net_read_timeout; /* Timeout for read */
net->compress_pkt_nr= net->pkt_nr= 0;
@@ -127,21 +130,18 @@ int my_net_init(NET *net, Vio* vio)
net->where_b = net->remain_in_buf=0;
net->last_errno=0;
if (vio != 0) /* If real connection */
if (cio != 0) /* If real connection */
{
net->fd = vio_fd(vio); /* For perl DBI/DBD */
#if defined(MYSQL_SERVER) && !defined(__WIN32) && !defined(__EMX__) && !defined(OS2)
if (!(test_flags & TEST_BLOCKING))
vio_blocking(vio, FALSE, 0);
#endif
vio_fastsend(vio);
ma_cio_get_handle(cio, &net->fd);
ma_cio_blocking(cio, 1, 0);
ma_cio_fast_send(cio);
}
return 0;
}
void net_end(NET *net)
{
my_free(net->buff);
my_free((gptr) net->buff);
net->buff=0;
}
@@ -223,15 +223,17 @@ static my_bool net_check_socket_status(my_socket sock)
void net_clear(NET *net)
{
my_socket sock;
DBUG_ENTER("net_clear");
ma_cio_get_handle(net->cio, &sock);
/* see conc-71: we need to check the socket status first:
if the socket is dead we set net->error, so net_flush
will report an error */
while (net_check_socket_status(net->vio->sd))
while (net_check_socket_status(sock))
{
/* vio_read returns size_t. so casting to long is required to check for -1 */
if ((long)vio_read(net->vio, (gptr)net->buff, (size_t) net->max_packet) <= 0)
if ((ssize_t)ma_cio_cache_read(net->cio, (gptr)net->buff, (size_t) net->max_packet) <= 0)
{
net->error= 2;
DBUG_PRINT("info", ("socket disconnected"));
@@ -388,12 +390,6 @@ net_real_write(NET *net,const char *packet,size_t len)
{
size_t length;
char *pos,*end;
thr_alarm_t alarmed;
#if !defined(_WIN32) && !defined(__EMX__) && !defined(OS2)
ALARM alarm_buff;
#endif
uint retry_count=0;
my_bool net_blocking = vio_is_blocking(net->vio);
DBUG_ENTER("net_real_write");
if (net->error == 2)
@@ -430,79 +426,23 @@ net_real_write(NET *net,const char *packet,size_t len)
}
#endif /* HAVE_COMPRESS */
alarmed=0;
pos=(char*) packet; end=pos+len;
while (pos != end)
{
if ((long) (length=vio_write(net->vio,pos,(size_t) (end-pos))) <= 0)
if ((ssize_t) (length=ma_cio_write(net->cio,pos,(size_t) (end-pos))) <= 0)
{
my_bool interrupted = vio_should_retry(net->vio);
#if (!defined(_WIN32) && !defined(__EMX__) && !defined(OS2))
if ((interrupted || length==0) && !thr_alarm_in_use(&alarmed))
{
if (!thr_alarm(&alarmed,(uint) net_write_timeout,&alarm_buff))
{ /* Always true for client */
if (!vio_is_blocking(net->vio))
{
while (vio_blocking(net->vio, TRUE, 0) < 0)
{
if (vio_should_retry(net->vio) && retry_count++ < RETRY_COUNT)
continue;
#ifdef EXTRA_DEBUG
fprintf(stderr,
"%s: my_net_write: fcntl returned error %d, aborting thread\n",
my_progname,vio_errno(net->vio));
#endif /* EXTRA_DEBUG */
net->error=2; /* Close socket */
net->last_errno= (interrupted ?
ER_NET_WRITE_INTERRUPTED : ER_NET_ERROR_ON_WRITE);
goto end;
}
}
retry_count=0;
continue;
}
}
else
#endif /* (!defined(_WIN32) && !defined(__EMX__)) */
if (thr_alarm_in_use(&alarmed) && !thr_got_alarm(&alarmed) &&
interrupted)
{
if (retry_count++ < RETRY_COUNT)
continue;
#ifdef EXTRA_DEBUG
fprintf(stderr, "%s: write looped, aborting thread\n",
my_progname);
#endif /* EXTRA_DEBUG */
}
#if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER)
if (vio_errno(net->vio) == SOCKET_EINTR)
{
DBUG_PRINT("warning",("Interrupted write. Retrying..."));
continue;
}
#endif /* defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER) */
net->error=2; /* Close socket */
net->last_errno= (interrupted ? ER_NET_WRITE_INTERRUPTED :
ER_NET_ERROR_ON_WRITE);
break;
net->last_errno= ER_NET_ERROR_ON_WRITE;
net->reading_or_writing=0;
DBUG_RETURN(1);
}
pos+=length;
statistic_add(bytes_sent,length,&LOCK_bytes_sent);
}
#ifndef _WIN32
end:
#endif
#ifdef HAVE_COMPRESS
if (net->compress)
my_free((void *)packet);
my_free((char*) packet);
#endif
if (thr_alarm_in_use(&alarmed))
{
thr_end_alarm(&alarmed);
vio_blocking(net->vio, net_blocking, 0);
}
net->reading_or_writing=0;
DBUG_RETURN(((int) (pos != end)));
}
@@ -511,66 +451,18 @@ net_real_write(NET *net,const char *packet,size_t len)
/*****************************************************************************
** Read something from server/clinet
*****************************************************************************/
#ifdef MYSQL_SERVER
/*
Help function to clear the commuication buffer when we get a too
big packet
*/
static void my_net_skip_rest(NET *net, ulong remain, thr_alarm_t *alarmed,
ALARM *alarm_buff)
{
uint retry_count=0;
if (!thr_alarm_in_use(alarmed))
{
if (thr_alarm(alarmed,net->timeout,alarm_buff) ||
(!vio_is_blocking(net->vio) && vio_blocking(net->vio,TRUE, 0) < 0))
return; /* Can't setup, abort */
}
while (remain > 0)
{
ulong length;
if ((int) (length=vio_read(net->vio,(char*) net->buff,remain)) <= 0L)
{
my_bool interrupted = vio_should_retry(net->vio);
if (!thr_got_alarm(alarmed) && interrupted)
{ /* Probably in MIT threads */
if (retry_count++ < RETRY_COUNT)
continue;
}
return;
}
remain -=(ulong) length;
statistic_add(bytes_received,(ulong) length,&LOCK_bytes_received);
}
}
#endif /* MYSQL_SERVER */
static ulong
my_real_read(NET *net, size_t *complen)
{
uchar *pos;
size_t length;
uint i,retry_count=0;
uint i;
ulong len=packet_error;
thr_alarm_t alarmed;
#if (!defined(_WIN32) && !defined(__EMX__) && !defined(OS2)) || defined(MYSQL_SERVER)
ALARM alarm_buff;
#endif
my_bool net_blocking=vio_is_blocking(net->vio);
size_t remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE :
NET_HEADER_SIZE);
*complen = 0;
net->reading_or_writing=1;
thr_alarm_init(&alarmed);
#ifdef MYSQL_SERVER
if (net_blocking)
thr_alarm(&alarmed,net->timeout,&alarm_buff);
#endif /* MYSQL_SERVER */
pos = net->buff + net->where_b; /* net->packet -4 */
for (i=0 ; i < 2 ; i++)
@@ -578,68 +470,8 @@ my_real_read(NET *net, size_t *complen)
while (remain > 0)
{
/* First read is done with non blocking mode */
if ((long) (length=vio_read(net->vio,(char*) pos,remain)) <= 0L)
if ((ssize_t) (length=ma_cio_cache_read(net->cio,(char*) pos,remain)) <= 0L)
{
my_bool interrupted = vio_should_retry(net->vio);
DBUG_PRINT("info",("vio_read returned %d, errno: %d",
length, vio_errno(net->vio)));
#if (!defined(_WIN32) && !defined(__EMX__) && !defined(OS2)) || defined(MYSQL_SERVER)
/*
We got an error that there was no data on the socket. We now set up
an alarm to not 'read forever', change the socket to non blocking
mode and try again
*/
if ((interrupted || length == 0) && !thr_alarm_in_use(&alarmed))
{
if (!thr_alarm(&alarmed,net->read_timeout,&alarm_buff)) /* Don't wait too long */
{
if (!vio_is_blocking(net->vio))
{
while (vio_blocking(net->vio,TRUE, 0) < 0)
{
if (vio_should_retry(net->vio) &&
retry_count++ < RETRY_COUNT)
continue;
DBUG_PRINT("error",
("fcntl returned error %d, aborting thread",
vio_errno(net->vio)));
#ifdef EXTRA_DEBUG
fprintf(stderr,
"%s: read: fcntl returned error %d, aborting thread\n",
my_progname,vio_errno(net->vio));
#endif /* EXTRA_DEBUG */
len= packet_error;
net->error=2; /* Close socket */
#ifdef MYSQL_SERVER
net->last_errno=ER_NET_FCNTL_ERROR;
#endif
goto end;
}
}
retry_count=0;
continue;
}
}
#endif /* (!defined(_WIN32) && !defined(__EMX__)) || defined(MYSQL_SERVER) */
if (thr_alarm_in_use(&alarmed) && !thr_got_alarm(&alarmed) &&
interrupted)
{ /* Probably in MIT threads */
if (retry_count++ < RETRY_COUNT)
continue;
#ifdef EXTRA_DEBUG
fprintf(stderr, "%s: read looped with error %d, aborting thread\n",
my_progname,vio_errno(net->vio));
#endif /* EXTRA_DEBUG */
}
#if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER)
if (vio_should_retry(net->vio))
{
DBUG_PRINT("warning",("Interrupted read. Retrying..."));
continue;
}
#endif
DBUG_PRINT("error",("Couldn't read packet: remain: %d errno: %d length: %d alarmed: %d", remain,vio_errno(net->vio),length,alarmed));
len= packet_error;
net->error=2; /* Close socket */
goto end;
@@ -666,9 +498,6 @@ my_real_read(NET *net, size_t *complen)
#endif
}
len= packet_error;
#ifdef MYSQL_SERVER
net->last_errno=ER_NET_PACKETS_OUT_OF_ORDER;
#endif
goto end;
}
net->compress_pkt_nr= ++net->pkt_nr;
@@ -689,10 +518,6 @@ my_real_read(NET *net, size_t *complen)
{
if (net_realloc(net,helping))
{
#ifdef MYSQL_SERVER
if (i == 1)
my_net_skip_rest(net, len, &alarmed, &alarm_buff);
#endif
len= packet_error; /* Return error */
goto end;
}
@@ -703,11 +528,6 @@ my_real_read(NET *net, size_t *complen)
}
end:
if (thr_alarm_in_use(&alarmed))
{
thr_end_alarm(&alarmed);
vio_blocking(net->vio, net_blocking, 0);
}
net->reading_or_writing=0;
return(len);
}

400
libmariadb/secure/gnutls.c Normal file
View File

@@ -0,0 +1,400 @@
/************************************************************************************
Copyright (C) 2014 MariaDB Corporation AB
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not see <http://www.gnu.org/licenses>
or write to the Free Software Foundation, Inc.,
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
*************************************************************************************/
#ifdef HAVE_GNUTLS
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#include <my_global.h>
#include <my_sys.h>
#include <ma_common.h>
#include <ma_cio.h>
#include <errmsg.h>
#include <my_pthread.h>
#include <mysql/client_plugin.h>
#include <string.h>
#include <ma_ssl.h>
pthread_mutex_t LOCK_gnutls_config;
static gnutls_certificate_credentials_t GNUTLS_xcred;
extern my_bool ma_ssl_initialized;
static int my_verify_callback(gnutls_session_t ssl);
#define MAX_SSL_ERR_LEN 100
static void ma_ssl_set_error(MYSQL *mysql, int ssl_errno)
{
char ssl_error[MAX_SSL_ERR_LEN];
const char *ssl_error_reason;
MARIADB_CIO *cio= mysql->net.cio;
if (!ssl_errno)
{
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error");
return;
}
if ((ssl_error_reason= gnutls_strerror(ssl_errno)))
{
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0,
ssl_error_reason);
return;
}
my_snprintf(ssl_error, MAX_SSL_ERR_LEN, "SSL errno=%lu", ssl_errno, mysql->charset);
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ssl_error);
}
static void ma_ssl_get_error(char *errmsg, size_t length, int ssl_errno)
{
const char *ssl_error_reason;
if (!ssl_errno)
{
strncpy(errmsg, "Unknown SSL error", length);
return;
}
if ((ssl_error_reason= gnutls_strerror(ssl_errno)))
{
strncpy(errmsg, ssl_error_reason, length);
return;
}
snprintf(errmsg, length, "SSL errno=%lu", ssl_errno);
}
/*
Initializes SSL and allocate global
context SSL_context
SYNOPSIS
my_gnutls_start
mysql connection handle
RETURN VALUES
0 success
1 error
*/
int ma_ssl_start(char *errmsg, size_t errmsg_len)
{
int rc= 0;
pthread_mutex_init(&LOCK_gnutls_config,MY_MUTEX_INIT_FAST);
pthread_mutex_lock(&LOCK_gnutls_config);
if (!ma_ssl_initialized)
{
if ((rc= gnutls_global_init()) != GNUTLS_E_SUCCESS)
{
ma_ssl_get_error(errmsg, errmsg_len, rc);
goto end;
}
ma_ssl_initialized= TRUE;
}
/* Allocate a global context for credentials */
rc= gnutls_certificate_allocate_credentials(&GNUTLS_xcred);
end:
pthread_mutex_unlock(&LOCK_gnutls_config);
return rc;
}
/*
Release SSL and free resources
Will be automatically executed by
mysql_server_end() function
SYNOPSIS
my_gnutls_end()
void
RETURN VALUES
void
*/
void ma_ssl_end()
{
pthread_mutex_lock(&LOCK_gnutls_config);
if (ma_ssl_initialized)
{
gnutls_certificate_free_keys(GNUTLS_xcred);
gnutls_certificate_free_cas(GNUTLS_xcred);
gnutls_certificate_free_crls(GNUTLS_xcred);
gnutls_certificate_free_ca_names(GNUTLS_xcred);
gnutls_certificate_free_credentials(GNUTLS_xcred);
gnutls_global_deinit();
ma_ssl_initialized= FALSE;
}
pthread_mutex_unlock(&LOCK_gnutls_config);
pthread_mutex_destroy(&LOCK_gnutls_config);
return;
}
static int ma_ssl_set_certs(MYSQL *mysql)
{
char *certfile= mysql->options.ssl_cert,
*keyfile= mysql->options.ssl_key;
char *cipher= NULL;
int ssl_error= 0;
if (mysql->options.ssl_ca)
{
ssl_error= gnutls_certificate_set_x509_trust_file(GNUTLS_xcred,
mysql->options.ssl_ca,
GNUTLS_X509_FMT_PEM);
if (ssl_error < 0)
goto error;
}
gnutls_certificate_set_verify_function(GNUTLS_xcred,
my_verify_callback);
/* GNUTLS doesn't support ca_path */
if (keyfile && !certfile)
certfile= keyfile;
if (certfile && !keyfile)
keyfile= certfile;
/* set key */
if (certfile || keyfile)
{
if ((ssl_error= gnutls_certificate_set_x509_key_file(GNUTLS_xcred,
certfile, keyfile,
GNUTLS_X509_FMT_PEM)) < 0)
goto error;
}
return 1;
error:
if (cipher)
my_free(cipher);
return ssl_error;
}
void *ma_ssl_init(MYSQL *mysql)
{
gnutls_session_t ssl= NULL;
int ssl_error= 0;
const char *err;
pthread_mutex_lock(&LOCK_gnutls_config);
if ((ssl_error= ma_ssl_set_certs(mysql)) < 0)
goto error;
if ((ssl_error = gnutls_init(&ssl, GNUTLS_CLIENT & GNUTLS_NONBLOCK)) < 0)
goto error;
gnutls_session_set_ptr(ssl, (void *)mysql);
ssl_error= gnutls_priority_set_direct(ssl, "NORMAL:-DHE-RSA", &err);
if (ssl_error < 0)
goto error;
if ((ssl_error= gnutls_credentials_set(ssl, GNUTLS_CRD_CERTIFICATE, GNUTLS_xcred)) < 0)
goto error;
pthread_mutex_unlock(&LOCK_gnutls_config);
return (void *)ssl;
error:
ma_ssl_set_error(mysql, ssl_error);
if (ssl)
gnutls_deinit(ssl);
pthread_mutex_unlock(&LOCK_gnutls_config);
return NULL;
}
my_bool ma_ssl_connect(MARIADB_SSL *cssl)
{
gnutls_session_t ssl = (gnutls_session_t)cssl->ssl;
my_bool blocking;
MYSQL *mysql;
MARIADB_CIO *cio;
int ret;
mysql= (MYSQL *)gnutls_session_get_ptr(ssl);
if (!mysql)
return 1;
cio= mysql->net.cio;
/* Set socket to blocking if not already set */
if (!(blocking= cio->methods->is_blocking(cio)))
cio->methods->blocking(cio, TRUE, 0);
gnutls_transport_set_int(ssl, mysql_get_socket(mysql));
gnutls_handshake_set_timeout(ssl, mysql->options.connect_timeout);
do {
ret = gnutls_handshake(ssl);
} while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
if (ret < 0)
{
ma_ssl_set_error(mysql, ret);
/* restore blocking mode */
if (!blocking)
cio->methods->blocking(cio, FALSE, 0);
return 1;
}
cssl->ssl= (void *)ssl;
return 0;
}
size_t ma_ssl_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length)
{
return gnutls_record_recv((gnutls_session_t )cssl->ssl, (void *)buffer, length);
}
size_t ma_ssl_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length)
{
return gnutls_record_send((gnutls_session_t )cssl->ssl, (void *)buffer, length);
}
my_bool ma_ssl_close(MARIADB_SSL *cssl)
{
gnutls_bye((gnutls_session_t )cssl->ssl, GNUTLS_SHUT_WR);
gnutls_deinit((gnutls_session_t )cssl->ssl);
cssl->ssl= NULL;
return 0;
}
int ma_ssl_verify_server_cert(MARIADB_SSL *cssl)
{
/* server verification is already handled before */
return 0;
}
const char *ma_ssl_get_cipher(MARIADB_SSL *cssl)
{
if (!cssl || !cssl->ssl)
return NULL;
return gnutls_cipher_get_name (gnutls_cipher_get((gnutls_session_t )cssl->ssl));
}
static int my_verify_callback(gnutls_session_t ssl)
{
unsigned int status;
const gnutls_datum_t *cert_list;
unsigned int cert_list_size;
int ret;
MYSQL *mysql= (MYSQL *)gnutls_session_get_ptr(ssl);
MARIADB_CIO *cio= mysql->net.cio;
gnutls_x509_crt_t cert;
const char *hostname;
/* read hostname */
hostname = mysql->host;
/* skip verification if no ca_file/path was specified */
if (!mysql->options.ssl_ca)
return 0;
/* This verification function uses the trusted CAs in the credentials
* structure. So you must have installed one or more CA certificates.
*/
ret = gnutls_certificate_verify_peers2 (ssl, &status);
if (ret < 0)
{
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "CA verification failed");
return GNUTLS_E_CERTIFICATE_ERROR;
}
// mysql->net.vio->status= status;
if (status & GNUTLS_CERT_INVALID)
{
return GNUTLS_E_CERTIFICATE_ERROR;
}
/* Up to here the process is the same for X.509 certificates and
* OpenPGP keys. From now on X.509 certificates are assumed. This can
* be easily extended to work with openpgp keys as well.
*/
if (gnutls_certificate_type_get (ssl) != GNUTLS_CRT_X509)
{
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Expected X509 certificate");
return GNUTLS_E_CERTIFICATE_ERROR;
}
if (gnutls_x509_crt_init (&cert) < 0)
{
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Error during certificate initialization");
return GNUTLS_E_CERTIFICATE_ERROR;
}
cert_list = gnutls_certificate_get_peers (ssl, &cert_list_size);
if (cert_list == NULL)
{
gnutls_x509_crt_deinit (cert);
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "No certificate found");
return GNUTLS_E_CERTIFICATE_ERROR;
}
if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
{
gnutls_x509_crt_deinit (cert);
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error");
return GNUTLS_E_CERTIFICATE_ERROR;
}
if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) &&
gnutls_x509_crt_check_hostname (cert, hostname) < 0)
{
gnutls_x509_crt_deinit (cert);
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Hostname in certificate doesn't match");
return GNUTLS_E_CERTIFICATE_ERROR;
}
gnutls_x509_crt_deinit (cert);
/* notify gnutls to continue handshake normally */
CLEAR_CLIENT_ERROR(mysql);
return 0;
}
unsigned int ma_ssl_get_finger_print(MARIADB_SSL *cssl, unsigned char *fp, unsigned int len)
{
MYSQL *mysql;
size_t fp_len= len;
const gnutls_datum_t *cert_list;
unsigned int cert_list_size;
if (!cssl || !cssl->ssl)
return 0;
mysql= (MYSQL *)gnutls_session_get_ptr(cssl->ssl);
cert_list = gnutls_certificate_get_peers (cssl->ssl, &cert_list_size);
if (cert_list == NULL)
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"Unable to get server certificate");
return 0;
}
if (gnutls_fingerprint(GNUTLS_DIG_SHA1, &cert_list[0], fp, &fp_len) == 0)
return fp_len;
else
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"Finger print buffer too small");
return 0;
}
}
#endif /* HAVE_GNUTLS */

View File

@@ -0,0 +1,907 @@
/************************************************************************************
Copyright (C) 2014 MariaDB Corporation Ab
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not see <http://www.gnu.org/licenses>
or write to the Free Software Foundation, Inc.,
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
Author: Georg Richter
*************************************************************************************/
#include "ma_schannel.h"
#include <assert.h>
#define SC_IO_BUFFER_SIZE 0x4000
#define MAX_SSL_ERR_LEN 100
#define SCHANNEL_PAYLOAD(A) (A).cbMaximumMessage - (A).cbHeader - (A).cbTrailer
/* {{{ void ma_schannel_set_sec_error */
void ma_schannel_set_sec_error(MARIADB_CIO *cio, DWORD ErrorNo)
{
MYSQL *mysql= cio->mysql;
switch(ErrorNo) {
case SEC_E_UNTRUSTED_ROOT:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Untrusted root certificate");
break;
case SEC_E_BUFFER_TOO_SMALL:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Buffer too small");
break;
case SEC_E_CRYPTO_SYSTEM_INVALID:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Cipher is not supported");
break;
case SEC_E_INSUFFICIENT_MEMORY:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Out of memory");
break;
case SEC_E_OUT_OF_SEQUENCE:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Invalid message sequence");
break;
case SEC_E_DECRYPT_FAILURE:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "An error occured during decrypting data");
break;
case SEC_I_INCOMPLETE_CREDENTIALS:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Incomplete credentials");
break;
case SEC_E_ENCRYPT_FAILURE:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "An error occured during encrypting data");
break;
case SEC_I_CONTEXT_EXPIRED:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Context expired: ");
case SEC_E_OK:
break;
default:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error (%d)", ErrorNo);
}
}
/* }}} */
/* {{{ void ma_schnnel_set_win_error */
void ma_schannel_set_win_error(MARIADB_CIO *cio)
{
ulong ssl_errno= GetLastError();
char ssl_error[MAX_SSL_ERR_LEN];
char *ssl_error_reason= NULL;
if (!ssl_errno)
{
cio->set_error(cio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error");
return;
}
/* todo: obtain error messge */
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, ssl_errno, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &ssl_error_reason, 0, NULL );
cio->set_error(cio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ssl_error_reason);
if (ssl_error_reason)
LocalFree(ssl_error_reason);
return;
}
/* }}} */
/* {{{ LPBYTE ma_schannel_load_pem(const char *PemFileName, DWORD *buffer_len) */
/*
Load a pem or clr file and convert it to a binary DER object
SYNOPSIS
ma_schannel_load_pem()
PemFileName name of the pem file (in)
buffer_len length of the converted DER binary
DESCRIPTION
Loads a X509 file (ca, certification, key or clr) into memory and converts
it to a DER binary object. This object can be decoded and loaded into
a schannel crypto context.
If the function failed, error can be retrieved by GetLastError()
The returned binary object must be freed by caller.
RETURN VALUE
NULL if the conversion failed or file was not found
LPBYTE * a pointer to a binary der object
buffer_len will contain the length of binary der object
*/
static LPBYTE ma_schannel_load_pem(MARIADB_CIO *cio, const char *PemFileName, DWORD *buffer_len)
{
HANDLE hfile;
char *buffer= NULL;
DWORD dwBytesRead= 0;
LPBYTE der_buffer= NULL;
DWORD der_buffer_length;
DWORD x;
if (buffer_len == NULL)
return NULL;
if ((hfile= CreateFile(PemFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL )) == INVALID_HANDLE_VALUE)
{
ma_schannel_set_win_error(cio);
return NULL;
}
if (!(*buffer_len = GetFileSize(hfile, NULL)))
{
cio->set_error(cio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Invalid pem format");
goto end;
}
if (!(buffer= LocalAlloc(0, *buffer_len + 1)))
{
cio->set_error(cio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL);
goto end;
}
if (!ReadFile(hfile, buffer, *buffer_len, &dwBytesRead, NULL))
{
ma_schannel_set_win_error(cio);
goto end;
}
CloseHandle(hfile);
/* calculate the length of DER binary */
if (!CryptStringToBinaryA(buffer, *buffer_len, CRYPT_STRING_BASE64HEADER,
NULL, &der_buffer_length, NULL, NULL))
{
ma_schannel_set_win_error(cio);
goto end;
}
/* allocate DER binary buffer */
if (!(der_buffer= (LPBYTE)LocalAlloc(0, der_buffer_length)))
{
cio->set_error(cio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL);
goto end;
}
/* convert to DER binary */
if (!CryptStringToBinaryA(buffer, *buffer_len, CRYPT_STRING_BASE64HEADER,
der_buffer, &der_buffer_length, NULL, NULL))
{
ma_schannel_set_win_error(cio);
goto end;
}
*buffer_len= der_buffer_length;
LocalFree(buffer);
return der_buffer;
end:
if (hfile != INVALID_HANDLE_VALUE)
CloseHandle(hfile);
if (buffer)
LocalFree(buffer);
if (der_buffer)
LocalFree(der_buffer);
*buffer_len= 0;
return NULL;
}
/* }}} */
/* {{{ CERT_CONTEXT *ma_schannel_create_cert_context(MARIADB_CIO *cio, const char *pem_file) */
/*
Create a certification context from ca or cert file
SYNOPSIS
ma_schannel_create_cert_context()
cio cio object
pem_file name of certificate or ca file
DESCRIPTION
Loads a PEM file (certificate authority or certificate) creates a certification
context and loads the binary representation into context.
The returned context must be freed by caller.
If the function failed, error can be retrieved by GetLastError().
RETURNS
NULL If loading of the file or creating context failed
CERT_CONTEXT * A pointer to a certification context structure
*/
CERT_CONTEXT *ma_schannel_create_cert_context(MARIADB_CIO *cio, const char *pem_file)
{
DWORD der_buffer_length;
LPBYTE der_buffer= NULL;
CERT_CONTEXT *ctx= NULL;
/* create DER binary object from ca/certification file */
if (!(der_buffer= ma_schannel_load_pem(cio, pem_file, (DWORD *)&der_buffer_length)))
goto end;
if (!(ctx= CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
der_buffer, der_buffer_length)))
ma_schannel_set_win_error(cio);
end:
if (der_buffer)
LocalFree(der_buffer);
return ctx;
}
/* }}} */
/* {{{ PCCRL_CONTEXT ma_schannel_create_crl_context(MARIADB_CIO *cio, const char *pem_file) */
/*
Create a crl context from crlfile
SYNOPSIS
ma_schannel_create_crl_context()
pem_file name of certificate or ca file
DESCRIPTION
Loads a certification revocation list file, creates a certification
context and loads the binary representation into crl context.
The returned context must be freed by caller.
If the function failed, error can be retrieved by GetLastError().
RETURNS
NULL If loading of the file or creating context failed
PCCRL_CONTEXT A pointer to a certification context structure
*/
PCCRL_CONTEXT ma_schannel_create_crl_context(MARIADB_CIO *cio, const char *pem_file)
{
DWORD der_buffer_length;
LPBYTE der_buffer= NULL;
PCCRL_CONTEXT ctx= NULL;
/* load ca pem file into memory */
if (!(der_buffer= ma_schannel_load_pem(cio, pem_file, (DWORD *)&der_buffer_length)))
goto end;
if (!(ctx= CertCreateCRLContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
der_buffer, der_buffer_length)))
ma_schannel_set_win_error(cio);
end:
if (der_buffer)
LocalFree(der_buffer);
return ctx;
}
/* }}} */
/* {{{ my_bool ma_schannel_load_private_key(MARIADB_CIO *cio, CERT_CONTEXT *ctx, char *key_file) */
/*
Load privte key into context
SYNOPSIS
ma_schannel_load_private_key()
ctx pointer to a certification context
pem_file name of certificate or ca file
DESCRIPTION
Loads a certification revocation list file, creates a certification
context and loads the binary representation into crl context.
The returned context must be freed by caller.
If the function failed, error can be retrieved by GetLastError().
RETURNS
NULL If loading of the file or creating context failed
PCCRL_CONTEXT A pointer to a certification context structure
*/
my_bool ma_schannel_load_private_key(MARIADB_CIO *cio, CERT_CONTEXT *ctx, char *key_file)
{
DWORD der_buffer_len= 0;
LPBYTE der_buffer= NULL;
DWORD priv_key_len= 0;
LPBYTE priv_key= NULL;
HCRYPTPROV crypt_prov= NULL;
HCRYPTKEY crypt_key= NULL;
CERT_KEY_CONTEXT kpi;
my_bool rc= 0;
/* load private key into der binary object */
if (!(der_buffer= ma_schannel_load_pem(cio, key_file, &der_buffer_len)))
return 0;
/* determine required buffer size for decoded private key */
if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
PKCS_RSA_PRIVATE_KEY,
der_buffer, der_buffer_len,
0, NULL,
NULL, &priv_key_len))
{
ma_schannel_set_win_error(cio);
goto end;
}
/* allocate buffer for decoded private key */
if (!(priv_key= LocalAlloc(0, priv_key_len)))
{
cio->set_error(cio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL);
goto end;
}
/* decode */
if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
PKCS_RSA_PRIVATE_KEY,
der_buffer, der_buffer_len,
0, NULL,
priv_key, &priv_key_len))
{
ma_schannel_set_win_error(cio);
goto end;
}
/* Acquire context:
If cio_schannel context doesn't exist, create a new one */
if (!CryptAcquireContext(&crypt_prov, "cio_schannel", MS_ENHANCED_PROV, PROV_RSA_FULL, 0))
if (!CryptAcquireContext(&crypt_prov, "cio_schannel", MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET))
{
ma_schannel_set_win_error(cio);
goto end;
}
/* ... and import the private key */
if (!CryptImportKey(crypt_prov, priv_key, priv_key_len, NULL, 0, &crypt_key))
{
ma_schannel_set_win_error(cio);
goto end;
}
SecureZeroMemory(&kpi, sizeof(kpi));
kpi.hCryptProv= crypt_prov;
kpi.dwKeySpec = AT_KEYEXCHANGE;
kpi.cbSize= sizeof(kpi);
/* assign private key to certificate context */
if (CertSetCertificateContextProperty(ctx, CERT_KEY_CONTEXT_PROP_ID, 0, &kpi))
rc= 1;
else
ma_schannel_set_win_error(cio);
end:
if (der_buffer)
LocalFree(der_buffer);
if (priv_key)
{
if (crypt_key)
CryptDestroyKey(crypt_key);
LocalFree(priv_key);
if (!rc)
if (crypt_prov)
CryptReleaseContext(crypt_prov, 0);
}
return rc;
}
/* }}} */
/* {{{ SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_CIO *cio, my_bool InitialRead, SecBuffer *pExtraData) */
/*
perform handshake loop
SYNOPSIS
ma_schannel_handshake_loop()
cio Pointer to an Communication/IO structure
InitialRead TRUE if it's the very first read
ExtraData Pointer to an SecBuffer which contains extra data (sent by application)
*/
SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_CIO *cio, my_bool InitialRead, SecBuffer *pExtraData)
{
SecBufferDesc OutBuffer, InBuffer;
SecBuffer InBuffers[2], OutBuffers[1];
DWORD dwSSPIFlags, dwSSPIOutFlags, cbData, cbIoBuffer;
TimeStamp tsExpiry;
SECURITY_STATUS rc;
PUCHAR IoBuffer;
BOOL fDoRead;
MARIADB_SSL *cssl= cio->cssl;
SC_CTX *sctx= (SC_CTX *)cssl->ssl;
dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
ISC_REQ_REPLAY_DETECT |
ISC_REQ_CONFIDENTIALITY |
ISC_RET_EXTENDED_ERROR |
ISC_REQ_ALLOCATE_MEMORY |
ISC_REQ_STREAM;
/* Allocate data buffer */
if (!(IoBuffer = LocalAlloc(LMEM_FIXED, SC_IO_BUFFER_SIZE)))
return SEC_E_INSUFFICIENT_MEMORY;
cbIoBuffer = 0;
fDoRead = InitialRead;
/* handshake loop: We will leave a handshake is finished
or an error occurs */
rc = SEC_I_CONTINUE_NEEDED;
while (rc == SEC_I_CONTINUE_NEEDED ||
rc == SEC_E_INCOMPLETE_MESSAGE ||
rc == SEC_I_INCOMPLETE_CREDENTIALS )
{
/* Read data */
if (rc == SEC_E_INCOMPLETE_MESSAGE ||
!cbIoBuffer)
{
if(fDoRead)
{
cbData = cio->methods->read(cio, IoBuffer + cbIoBuffer, SC_IO_BUFFER_SIZE - cbIoBuffer, 0);
if (cbData == SOCKET_ERROR || cbData == 0)
{
rc = SEC_E_INTERNAL_ERROR;
break;
}
cbIoBuffer += cbData;
}
else
fDoRead = TRUE;
}
/* input buffers
First buffer stores data received from server. leftover data
will be stored in second buffer with BufferType SECBUFFER_EXTRA */
InBuffers[0].pvBuffer = IoBuffer;
InBuffers[0].cbBuffer = cbIoBuffer;
InBuffers[0].BufferType = SECBUFFER_TOKEN;
InBuffers[1].pvBuffer = NULL;
InBuffers[1].cbBuffer = 0;
InBuffers[1].BufferType = SECBUFFER_EMPTY;
InBuffer.cBuffers = 2;
InBuffer.pBuffers = InBuffers;
InBuffer.ulVersion = SECBUFFER_VERSION;
/* output buffer */
OutBuffers[0].pvBuffer = NULL;
OutBuffers[0].BufferType= SECBUFFER_TOKEN;
OutBuffers[0].cbBuffer = 0;
OutBuffer.cBuffers = 1;
OutBuffer.pBuffers = OutBuffers;
OutBuffer.ulVersion = SECBUFFER_VERSION;
rc = InitializeSecurityContextA(&sctx->CredHdl,
&sctx->ctxt,
NULL,
dwSSPIFlags,
0,
SECURITY_NATIVE_DREP,
&InBuffer,
0,
NULL,
&OutBuffer,
&dwSSPIOutFlags,
&tsExpiry );
if (rc == SEC_E_OK ||
rc == SEC_I_CONTINUE_NEEDED ||
FAILED(rc) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))
{
if(OutBuffers[0].cbBuffer && OutBuffers[0].pvBuffer)
{
cbData= cio->methods->write(cio, (uchar *)OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer);
if(cbData == SOCKET_ERROR || cbData == 0)
{
FreeContextBuffer(OutBuffers[0].pvBuffer);
DeleteSecurityContext(&sctx->ctxt);
return SEC_E_INTERNAL_ERROR;
}
/* Free output context buffer */
FreeContextBuffer(OutBuffers[0].pvBuffer);
OutBuffers[0].pvBuffer = NULL;
}
}
/* check if we need to read more data */
switch (rc) {
case SEC_E_INCOMPLETE_MESSAGE:
/* we didn't receive all data, so just continue loop */
continue;
break;
case SEC_E_OK:
/* handshake completed, but we need to check if extra
data was sent (which contains encrypted application data) */
if (InBuffers[1].BufferType == SECBUFFER_EXTRA)
{
if (!(pExtraData->pvBuffer= LocalAlloc(0, InBuffers[1].cbBuffer)))
return SEC_E_INSUFFICIENT_MEMORY;
MoveMemory(pExtraData->pvBuffer, IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer );
pExtraData->BufferType = SECBUFFER_TOKEN;
pExtraData->cbBuffer = InBuffers[1].cbBuffer;
}
else
{
pExtraData->BufferType= SECBUFFER_EMPTY;
pExtraData->pvBuffer= NULL;
pExtraData->cbBuffer= 0;
}
break;
case SEC_I_INCOMPLETE_CREDENTIALS:
/* Provided credentials didn't contain a valid client certificate.
We will try to connect anonymously, using current credentials */
fDoRead= FALSE;
rc= SEC_I_CONTINUE_NEEDED;
continue;
break;
default:
if (FAILED(rc))
{
ma_schannel_set_sec_error(cio, rc);
goto loopend;
}
break;
}
if ( InBuffers[1].BufferType == SECBUFFER_EXTRA )
{
MoveMemory( IoBuffer, IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer );
cbIoBuffer = InBuffers[1].cbBuffer;
}
cbIoBuffer = 0;
}
loopend:
if (FAILED(rc))
DeleteSecurityContext(&sctx->ctxt);
LocalFree(IoBuffer);
return rc;
}
/* }}} */
/* {{{ SECURITY_STATUS ma_schannel_client_handshake(MARIADB_SSL *cssl) */
/*
performs client side handshake
SYNOPSIS
ma_schannel_client_handshake()
cssl Pointer to a MARIADB_SSL structure
DESCRIPTION
initiates a client/server handshake. This function can be used
by clients only
RETURN
SEC_E_OK on success
*/
SECURITY_STATUS ma_schannel_client_handshake(MARIADB_SSL *cssl)
{
MARIADB_CIO *cio;
SECURITY_STATUS sRet;
DWORD OutFlags;
DWORD r;
SC_CTX *sctx;
SecBuffer ExtraData;
DWORD SFlags= ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR |
ISC_REQ_USE_SUPPLIED_CREDS |
ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
SecBufferDesc BufferIn, BufferOut;
SecBuffer BuffersOut[1], BuffersIn[2];
if (!cssl || !cssl->cio)
return 1;
cio= cssl->cio;
sctx= (SC_CTX *)cssl->ssl;
/* Initialie securifty context */
BuffersOut[0].BufferType= SECBUFFER_TOKEN;
BuffersOut[0].cbBuffer= 0;
BuffersOut[0].pvBuffer= NULL;
BufferOut.cBuffers= 1;
BufferOut.pBuffers= BuffersOut;
BufferOut.ulVersion= SECBUFFER_VERSION;
sRet = InitializeSecurityContext(&sctx->CredHdl,
NULL,
cio->mysql->host,
SFlags,
0,
SECURITY_NATIVE_DREP,
NULL,
0,
&sctx->ctxt,
&BufferOut,
&OutFlags,
NULL);
if(sRet != SEC_I_CONTINUE_NEEDED)
{
ma_schannel_set_sec_error(cio, sRet);
return sRet;
}
/* Allocate IO-Buffer */
sctx->IoBufferSize= 2 * net_buffer_length;
if (!(sctx->IoBuffer= (PUCHAR)LocalAlloc(LMEM_ZEROINIT, sctx->IoBufferSize)))
{
sRet= SEC_E_INSUFFICIENT_MEMORY;
goto end;
}
/* send client hello packaet */
if(BuffersOut[0].cbBuffer != 0 && BuffersOut[0].pvBuffer != NULL)
{
r= cio->methods->write(cio, (uchar *)BuffersOut[0].pvBuffer, BuffersOut[0].cbBuffer);
if (r <= 0)
{
sRet= SEC_E_INTERNAL_ERROR;
goto end;
}
}
sRet= ma_schannel_handshake_loop(cio, TRUE, &ExtraData);
/* Reallocate IO-Buffer for write operations: After handshake
was successfull, we are able now to calculate payload */
QueryContextAttributes( &sctx->ctxt, SECPKG_ATTR_STREAM_SIZES, &sctx->Sizes );
sctx->IoBufferSize= SCHANNEL_PAYLOAD(sctx->Sizes);
sctx->IoBuffer= LocalReAlloc(sctx->IoBuffer, sctx->IoBufferSize, LMEM_ZEROINIT);
return sRet;
end:
LocalFree(sctx->IoBuffer);
sctx->IoBufferSize= 0;
FreeContextBuffer(BuffersOut[0].pvBuffer);
DeleteSecurityContext(&sctx->ctxt);
return sRet;
}
/* }}} */
/* {{{ SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_CIO *cio, PCredHandle phCreds, CtxtHandle * phContext,
DWORD DecryptLength, uchar *ReadBuffer, DWORD ReadBufferSize) */
/*
Reads encrypted data from a SSL stream and decrypts it.
SYNOPSIS
ma_schannel_read
cio pointer to Communication IO structure
phContext a context handle
DecryptLength size of decrypted buffer
ReadBuffer Buffer for decrypted data
ReadBufferSize size of ReadBuffer
DESCRIPTION
Reads decrypted data from a SSL stream and encrypts it.
RETURN
SEC_E_OK on success
SEC_E_* if an error occured
*/
SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_CIO *cio,
PCredHandle phCreds,
CtxtHandle * phContext,
DWORD *DecryptLength,
uchar *ReadBuffer,
DWORD ReadBufferSize)
{
DWORD dwBytesRead= 0;
DWORD dwOffset= 0;
SC_CTX *sctx;
SECURITY_STATUS sRet= 0;
SecBufferDesc Msg;
SecBuffer Buffers[4],
ExtraBuffer,
*pData, *pExtra;
int i;
if (!cio || !cio->methods || !cio->methods->read || !cio->cssl || !DecryptLength)
return SEC_E_INTERNAL_ERROR;
sctx= (SC_CTX *)cio->cssl->ssl;
*DecryptLength= 0;
while (1)
{
if (!dwBytesRead || sRet == SEC_E_INCOMPLETE_MESSAGE)
{
dwBytesRead= cio->methods->read(cio, sctx->IoBuffer + dwOffset, sctx->IoBufferSize - dwOffset);
if (dwBytesRead == 0)
{
/* server closed connection */
// todo: error
return NULL;
}
if (dwBytesRead < 0)
{
/* socket error */
// todo: error
return NULL;
}
dwOffset+= dwBytesRead;
}
ZeroMemory(Buffers, sizeof(SecBuffer) * 4);
Buffers[0].pvBuffer= sctx->IoBuffer;
Buffers[0].cbBuffer= dwOffset;
Buffers[0].BufferType= SECBUFFER_DATA;
Buffers[1].BufferType=
Buffers[2].BufferType=
Buffers[3].BufferType= SECBUFFER_EMPTY;
Msg.ulVersion= SECBUFFER_VERSION; // Version number
Msg.cBuffers= 4;
Msg.pBuffers= Buffers;
sRet = DecryptMessage(phContext, &Msg, 0, NULL);
/* Check for possible errors: we continue in case context has
expired or renogitiation is required */
if (sRet != SEC_E_OK && sRet != SEC_I_CONTEXT_EXPIRED &&
sRet != SEC_I_RENEGOTIATE && sRet != SEC_E_INCOMPLETE_MESSAGE)
{
ma_schannel_set_sec_error(cio, sRet);
return sRet;
}
pData= pExtra= NULL;
for (i=0; i < 4; i++)
{
if (!pData && Buffers[i].BufferType == SECBUFFER_DATA)
pData= &Buffers[i];
if (!pExtra && Buffers[i].BufferType == SECBUFFER_EXTRA)
pExtra= &Buffers[i];
if (pData && pExtra)
break;
}
if (pData && pData->cbBuffer)
{
memcpy(ReadBuffer + *DecryptLength, pData->pvBuffer, pData->cbBuffer);
*DecryptLength+= pData->cbBuffer;
return sRet;
}
if (pExtra)
{
MoveMemory(sctx->IoBuffer, pExtra->pvBuffer, pExtra->cbBuffer);
dwOffset= pExtra->cbBuffer;
}
else
dwOffset= 0;
}
}
my_bool ma_schannel_verify_certs(SC_CTX *sctx, DWORD dwCertFlags)
{
SECURITY_STATUS sRet;
DWORD flags;
MARIADB_CIO *cio= sctx->mysql->net.cio;
PCCERT_CONTEXT pServerCert= NULL;
if ((sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert)) != SEC_E_OK)
{
ma_schannel_set_sec_error(cio, sRet);
return 0;
}
flags= CERT_STORE_SIGNATURE_FLAG |
CERT_STORE_TIME_VALIDITY_FLAG;
if (sctx->client_ca_ctx)
{
if (!(sRet= CertVerifySubjectCertificateContext(pServerCert,
sctx->client_ca_ctx,
&flags)))
{
ma_schannel_set_win_error(cio);
return 0;
}
if (flags)
{
if ((flags & CERT_STORE_SIGNATURE_FLAG) != 0)
cio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Client certificate signature check failed");
else if ((flags & CERT_STORE_REVOCATION_FLAG) != 0)
cio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Client certificate was revoked");
else if ((flags & CERT_STORE_TIME_VALIDITY_FLAG) != 0)
cio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Client certificate has expired");
else
cio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown error during client certificate validation");
return 0;
}
}
/* Check if none of the certificates in the certificate chain have been revoked. */
if (sctx->client_crl_ctx)
{
PCRL_INFO Info[1];
Info[0]= sctx->client_crl_ctx->pCrlInfo;
if (!(CertVerifyCRLRevocation(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
pServerCert->pCertInfo,
1, Info)) )
{
cio->set_error(cio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "CRL Revocation failed");
return 0;
}
}
return 1;
}
/* {{{ size_t ma_schannel_write_encrypt(MARIADB_CIO *cio, PCredHandle phCreds, CtxtHandle * phContext) */
/*
Decrypts data and write to SSL stream
SYNOPSIS
ma_schannel_write_decrypt
cio pointer to Communication IO structure
phContext a context handle
DecryptLength size of decrypted buffer
ReadBuffer Buffer for decrypted data
ReadBufferSize size of ReadBuffer
DESCRIPTION
Write encrypted data to SSL stream.
RETURN
SEC_E_OK on success
SEC_E_* if an error occured
*/
size_t ma_schannel_write_encrypt(MARIADB_CIO *cio,
uchar *WriteBuffer,
size_t WriteBufferSize)
{
SECURITY_STATUS scRet;
SecBufferDesc Message;
SecBuffer Buffers[4];
DWORD cbMessage, cbData;
PBYTE pbMessage;
SC_CTX *sctx= (SC_CTX *)cio->cssl->ssl;
size_t payload;
payload= MIN(WriteBufferSize, sctx->IoBufferSize);
memcpy(&sctx->IoBuffer[sctx->Sizes.cbHeader], WriteBuffer, payload);
pbMessage = sctx->IoBuffer + sctx->Sizes.cbHeader;
cbMessage = payload;
Buffers[0].pvBuffer = sctx->IoBuffer;
Buffers[0].cbBuffer = sctx->Sizes.cbHeader;
Buffers[0].BufferType = SECBUFFER_STREAM_HEADER; // Type of the buffer
Buffers[1].pvBuffer = &sctx->IoBuffer[sctx->Sizes.cbHeader];
Buffers[1].cbBuffer = payload;
Buffers[1].BufferType = SECBUFFER_DATA;
Buffers[2].pvBuffer = &sctx->IoBuffer[sctx->Sizes.cbHeader] + payload;
Buffers[2].cbBuffer = sctx->Sizes.cbTrailer;
Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
Buffers[3].pvBuffer = SECBUFFER_EMPTY; // Pointer to buffer 4
Buffers[3].cbBuffer = SECBUFFER_EMPTY; // length of buffer 4
Buffers[3].BufferType = SECBUFFER_EMPTY; // Type of the buffer 4
Message.ulVersion = SECBUFFER_VERSION;
Message.cBuffers = 4;
Message.pBuffers = Buffers;
if ((scRet = EncryptMessage(&sctx->ctxt, 0, &Message, 0))!= SEC_E_OK)
return -1;
if (cio->methods->write(cio, sctx->IoBuffer, Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer))
return payload;
}
/* }}} */

View File

@@ -0,0 +1,95 @@
/************************************************************************************
Copyright (C) 2014 MariaDB Corporation Ab
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not see <http://www.gnu.org/licenses>
or write to the Free Software Foundation, Inc.,
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
Author: Georg Richter
*************************************************************************************/
#ifndef _ma_schannel_h_
#define _ma_schannel_h_
#define SECURITY_WIN32
#include <my_global.h>
#include <my_sys.h>
#include <ma_common.h>
#include <ma_cio.h>
#include <errmsg.h>
typedef void VOID;
#include <wincrypt.h>
#include <wintrust.h>
#include <security.h>
#include <schnlsp.h>
#undef SECURITY_WIN32
#include <Windows.h>
#include <sspi.h>
#define SC_IO_BUFFER_SIZE 0x4000
#ifndef HAVE_SCHANNEL_DEFAULT
#define my_snprintf snprintf
#define my_vsnprintf vsnprintf
#undef SAFE_MUTEX
#endif
#include <my_pthread.h>
struct st_schannel {
HCERTSTORE cert_store;
CERT_CONTEXT *client_cert_ctx;
CERT_CONTEXT *client_ca_ctx;
CRL_CONTEXT *client_crl_ctx;
CredHandle CredHdl;
PUCHAR IoBuffer;
DWORD IoBufferSize;
/* PUCHAR EncryptBuffer;
DWORD EncryptBufferSize;
DWORD EncryptBufferLength;
PUCHAR DecryptBuffer;
DWORD DecryptBufferSize;
DWORD DecryptBufferLength;
uchar thumbprint[21]; */
SecPkgContext_StreamSizes Sizes;
CtxtHandle ctxt;
MYSQL *mysql;
};
typedef struct st_schannel SC_CTX;
CERT_CONTEXT *ma_schannel_create_cert_context(MARIADB_CIO *cio, const char *pem_file);
SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_CIO *cio, my_bool InitialRead, SecBuffer *pExtraData);
my_bool ma_schannel_load_private_key(MARIADB_CIO *cio, CERT_CONTEXT *ctx, char *key_file);
PCCRL_CONTEXT ma_schannel_create_crl_context(MARIADB_CIO *cio, const char *pem_file);
my_bool ma_schannel_verify_certs(SC_CTX *sctx, DWORD dwCertFlags);
size_t ma_schannel_write_encrypt(MARIADB_CIO *cio,
uchar *WriteBuffer,
size_t WriteBufferSize);
size_t ma_schannel_read_decrypt(MARIADB_CIO *cio,
PCredHandle phCreds,
CtxtHandle * phContext,
DWORD *DecryptLength,
uchar *ReadBuffer,
DWORD ReadBufferSize);
#endif /* _ma_schannel_h_ */

532
libmariadb/secure/openssl.c Normal file
View File

@@ -0,0 +1,532 @@
/************************************************************************************
Copyright (C) 2012 Monty Program AB
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not see <http://www.gnu.org/licenses>
or write to the Free Software Foundation, Inc.,
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
*************************************************************************************/
#include <my_global.h>
#include <my_sys.h>
#include <ma_common.h>
#include <ma_cio.h>
#include <errmsg.h>
#include <string.h>
#include <mysql/client_plugin.h>
#include <string.h>
#include <openssl/ssl.h> /* SSL and SSL_CTX */
#include <openssl/err.h> /* error reporting */
#include <openssl/conf.h>
#ifndef HAVE_OPENSSL_DEFAULT
#include <memory.h>
#define my_malloc(A,B) malloc((A))
#undef my_free
#define my_free(A) free((A))
#define my_snprintf snprintf
#define my_vsnprintf vsnprintf
#undef SAFE_MUTEX
#endif
#include <my_pthread.h>
extern my_bool ma_ssl_initialized;
static SSL_CTX *SSL_context= NULL;
#define MAX_SSL_ERR_LEN 100
static pthread_mutex_t LOCK_openssl_config;
static pthread_mutex_t *LOCK_crypto= NULL;
static void ma_ssl_set_error(MYSQL *mysql)
{
ulong ssl_errno= ERR_get_error();
char ssl_error[MAX_SSL_ERR_LEN];
const char *ssl_error_reason;
MARIADB_CIO *cio= mysql->net.cio;
if (!ssl_errno)
{
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error");
return;
}
if ((ssl_error_reason= ERR_reason_error_string(ssl_errno)))
{
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
0, ssl_error_reason);
return;
}
snprintf(ssl_error, MAX_SSL_ERR_LEN, "SSL errno=%lu", ssl_errno, mysql->charset);
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, ssl_error);
return;
}
static void ma_ssl_get_error(char *errmsg, size_t length)
{
ulong ssl_errno= ERR_get_error();
const char *ssl_error_reason;
if (!ssl_errno)
{
strncpy(errmsg, "Unknown SSL error", length);
return;
}
if ((ssl_error_reason= ERR_reason_error_string(ssl_errno)))
{
strncpy(errmsg, ssl_error_reason, length);
return;
}
snprintf(errmsg, length, "SSL errno=%lu", ssl_errno);
}
/*
thread safe callbacks for OpenSSL
Crypto call back functions will be
set during ssl_initialization
*/
#if (OPENSSL_VERSION_NUMBER < 0x10000000)
static unsigned long my_cb_threadid(void)
{
/* cast pthread_t to unsigned long */
return (unsigned long) pthread_self();
}
#else
static void my_cb_threadid(CRYPTO_THREADID *id)
{
CRYPTO_THREADID_set_numeric(id, (unsigned long)pthread_self());
}
#endif
static void my_cb_locking(int mode, int n, const char *file, int line)
{
if (mode & CRYPTO_LOCK)
pthread_mutex_lock(&LOCK_crypto[n]);
else
pthread_mutex_unlock(&LOCK_crypto[n]);
}
static int ssl_thread_init()
{
int i, max= CRYPTO_num_locks();
if (LOCK_crypto == NULL)
{
if (!(LOCK_crypto=
(pthread_mutex_t *)my_malloc(sizeof(pthread_mutex_t) * max, MYF(0))))
return 1;
for (i=0; i < max; i++)
pthread_mutex_init(&LOCK_crypto[i], NULL);
}
#if (OPENSSL_VERSION_NUMBER < 0x10000000)
CRYPTO_set_id_callback(my_cb_threadid);
#else
CRYPTO_THREADID_set_callback(my_cb_threadid);
#endif
CRYPTO_set_locking_callback(my_cb_locking);
return 0;
}
/*
Initializes SSL and allocate global
context SSL_context
SYNOPSIS
my_ssl_start
mysql connection handle
RETURN VALUES
0 success
1 error
*/
int ma_ssl_start(char *errmsg, size_t errmsg_len)
{
int rc= 1;
/* lock mutex to prevent multiple initialization */
pthread_mutex_init(&LOCK_openssl_config,MY_MUTEX_INIT_FAST);
pthread_mutex_lock(&LOCK_openssl_config);
if (!ma_ssl_initialized)
{
if (ssl_thread_init())
{
strncpy(errmsg, "Not enough memory", errmsg_len);
goto end;
}
SSL_library_init();
#if SSLEAY_VERSION_NUMBER >= 0x00907000L
OPENSSL_config(NULL);
#endif
/* load errors */
SSL_load_error_strings();
/* digests and ciphers */
OpenSSL_add_all_algorithms();
if (!(SSL_context= SSL_CTX_new(TLSv1_client_method())))
{
ma_ssl_get_error(errmsg, errmsg_len);
goto end;
}
rc= 0;
ma_ssl_initialized= TRUE;
}
end:
pthread_mutex_unlock(&LOCK_openssl_config);
return rc;
}
/*
Release SSL and free resources
Will be automatically executed by
mysql_server_end() function
SYNOPSIS
my_ssl_end()
void
RETURN VALUES
void
*/
void ma_ssl_end()
{
pthread_mutex_lock(&LOCK_openssl_config);
if (ma_ssl_initialized)
{
int i;
CRYPTO_set_locking_callback(NULL);
CRYPTO_set_id_callback(NULL);
for (i=0; i < CRYPTO_num_locks(); i++)
pthread_mutex_destroy(&LOCK_crypto[i]);
my_free((gptr)LOCK_crypto);
LOCK_crypto= NULL;
if (SSL_context)
{
SSL_CTX_free(SSL_context);
SSL_context= NULL;
}
ERR_remove_state(0);
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
ERR_free_strings();
//ENGINE_cleanup();
CONF_modules_free();
CONF_modules_unload(1);
sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
ma_ssl_initialized= FALSE;
}
pthread_mutex_unlock(&LOCK_openssl_config);
pthread_mutex_destroy(&LOCK_openssl_config);
return;
}
static int ma_ssl_set_certs(MYSQL *mysql)
{
char *certfile= mysql->options.ssl_cert,
*keyfile= mysql->options.ssl_key;
/* add cipher */
if ((mysql->options.ssl_cipher &&
mysql->options.ssl_cipher[0] != 0) &&
SSL_CTX_set_cipher_list(SSL_context, mysql->options.ssl_cipher) == 0)
goto error;
/* ca_file and ca_path */
if (SSL_CTX_load_verify_locations(SSL_context,
mysql->options.ssl_ca,
mysql->options.ssl_capath) == 0)
{
if (mysql->options.ssl_ca || mysql->options.ssl_capath)
goto error;
if (SSL_CTX_set_default_verify_paths(SSL_context) == 0)
goto error;
}
if (keyfile && !certfile)
certfile= keyfile;
if (certfile && !keyfile)
keyfile= certfile;
/* set cert */
if (certfile && certfile[0] != 0)
if (SSL_CTX_use_certificate_file(SSL_context, certfile, SSL_FILETYPE_PEM) != 1)
goto error;
/* set key */
if (keyfile && keyfile[0])
{
if (SSL_CTX_use_PrivateKey_file(SSL_context, keyfile, SSL_FILETYPE_PEM) != 1)
goto error;
}
/* verify key */
if (certfile && !SSL_CTX_check_private_key(SSL_context))
goto error;
if (mysql->options.extension &&
(mysql->options.extension->ssl_crl || mysql->options.extension->ssl_crlpath))
{
X509_STORE *certstore;
if ((certstore= SSL_CTX_get_cert_store(SSL_context)))
{
if (X509_STORE_load_locations(certstore, mysql->options.extension->ssl_crl,
mysql->options.extension->ssl_crlpath) == 0 ||
X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL) == 0)
goto error;
}
}
return 0;
error:
ma_ssl_set_error(mysql);
return 1;
}
static int my_verify_callback(int ok, X509_STORE_CTX *ctx)
{
X509 *check_cert;
SSL *ssl;
MYSQL *mysql;
ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
mysql= (MYSQL *)SSL_get_app_data(ssl);
/* skip verification if no ca_file/path was specified */
if (!mysql->options.ssl_ca && !mysql->options.ssl_capath)
{
ok= 1;
return 1;
}
if (!ok)
{
uint depth;
if (!(check_cert= X509_STORE_CTX_get_current_cert(ctx)))
return 0;
depth= X509_STORE_CTX_get_error_depth(ctx);
if (depth == 0)
ok= 1;
}
return ok;
}
void *ma_ssl_init(MYSQL *mysql)
{
int verify;
SSL *ssl= NULL;
pthread_mutex_lock(&LOCK_openssl_config);
if (ma_ssl_set_certs(mysql))
{
goto error;
}
if (!(ssl= SSL_new(SSL_context)))
goto error;
if (!SSL_set_app_data(ssl, mysql))
goto error;
verify= (!mysql->options.ssl_ca && !mysql->options.ssl_capath) ?
SSL_VERIFY_NONE : SSL_VERIFY_PEER;
SSL_CTX_set_verify(SSL_context, verify, my_verify_callback);
SSL_CTX_set_verify_depth(SSL_context, 1);
pthread_mutex_unlock(&LOCK_openssl_config);
return (void *)ssl;
error:
pthread_mutex_unlock(&LOCK_openssl_config);
if (ssl)
SSL_free(ssl);
return NULL;
}
my_bool ma_ssl_connect(MARIADB_SSL *cssl)
{
SSL *ssl = (SSL *)cssl->ssl;
my_bool blocking;
MYSQL *mysql;
MARIADB_CIO *cio;
int rc;
mysql= (MYSQL *)SSL_get_app_data(ssl);
cio= mysql->net.cio;
/* Set socket to blocking if not already set */
if (!(blocking= cio->methods->is_blocking(cio)))
cio->methods->blocking(cio, TRUE, 0);
SSL_clear(ssl);
SSL_SESSION_set_timeout(SSL_get_session(ssl),
mysql->options.connect_timeout);
SSL_set_fd(ssl, mysql_get_socket(mysql));
if (SSL_connect(ssl) != 1)
{
ma_ssl_set_error(mysql);
/* restore blocking mode */
if (!blocking)
cio->methods->blocking(cio, FALSE, 0);
return 1;
}
rc= SSL_get_verify_result(ssl);
if (rc != X509_V_OK)
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR), X509_verify_cert_error_string(rc));
/* restore blocking mode */
if (!blocking)
cio->methods->blocking(cio, FALSE, 0);
return 1;
}
cio->cssl->ssl= cssl->ssl= (void *)ssl;
return 0;
}
size_t ma_ssl_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length)
{
return SSL_read((SSL *)cssl->ssl, (void *)buffer, (int)length);
}
size_t ma_ssl_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length)
{
return SSL_write((SSL *)cssl->ssl, (void *)buffer, (int)length);
}
my_bool ma_ssl_close(MARIADB_SSL *cssl)
{
int i, rc;
SSL *ssl;
if (!cssl || !cssl->ssl)
return 1;
ssl= (SSL *)cssl->ssl;
SSL_set_quiet_shutdown(ssl, 1);
/* 2 x pending + 2 * data = 4 */
for (i=0; i < 4; i++)
if ((rc= SSL_shutdown(ssl)))
break;
SSL_free(ssl);
cssl->ssl= NULL;
return rc;
}
int ma_ssl_verify_server_cert(MARIADB_SSL *cssl)
{
X509 *cert;
MYSQL *mysql;
MARIADB_CIO *cio;
SSL *ssl;
char *p1, *p2, buf[256];
if (!cssl || !cssl->ssl)
return 1;
ssl= (SSL *)cssl->ssl;
mysql= (MYSQL *)SSL_get_app_data(ssl);
cio= mysql->net.cio;
if (!mysql->host)
{
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0,
"Invalid (empty) hostname");
return 1;
}
if (!(cert= SSL_get_peer_certificate(ssl)))
{
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0,
"Unable to get server certificate");
return 1;
}
X509_NAME_oneline(X509_get_subject_name(cert), buf, 256);
X509_free(cert);
/* Extract the server name from buffer:
Format: ....CN=/hostname/.... */
if ((p1= strstr(buf, "/CN=")))
{
p1+= 4;
if ((p2= strchr(p1, '/')))
*p2= 0;
if (!strcmp(mysql->host, p1))
return(0);
}
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0,
"Validation of SSL server certificate failed");
return 1;
}
const char *ma_ssl_get_cipher(MARIADB_SSL *cssl)
{
if (!cssl || !cssl->ssl)
return NULL;
return SSL_get_cipher_name(cssl->ssl);
}
unsigned int ma_ssl_get_finger_print(MARIADB_SSL *cssl, unsigned char *fp, unsigned int len)
{
EVP_MD *digest= (EVP_MD *)EVP_sha1();
X509 *cert;
MYSQL *mysql;
unsigned int *fp_len;
if (!cssl || !cssl->ssl)
return NULL;
mysql= SSL_get_app_data(cssl->ssl);
if (!(cert= SSL_get_peer_certificate(cssl->ssl)))
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"Unable to get server certificate");
return 0;
}
if (len < EVP_MAX_MD_SIZE)
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"Finger print buffer too small");
return 0;
}
*fp_len= len;
if (!X509_digest(cert, digest, fp, fp_len))
{
my_free(fp);
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
"invalid finger print of server certificate");
return 0;
}
return (*fp_len);
}

View File

@@ -0,0 +1,474 @@
/************************************************************************************
Copyright (C) 2014 MariaDB Corporation Ab
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not see <http://www.gnu.org/licenses>
or write to the Free Software Foundation, Inc.,
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
*************************************************************************************/
#include "ma_schannel.h"
#pragma comment (lib, "crypt32.lib")
#pragma comment (lib, "secur32.lib")
#define VOID void
extern my_bool ma_ssl_initialized;
static pthread_mutex_t LOCK_schannel_config;
static pthread_mutex_t *LOCK_crypto= NULL;
struct st_cipher_suite {
DWORD aid;
CHAR *cipher;
};
void ma_schannel_set_sec_error(MARIADB_CIO *cio, DWORD ErrorNo);
void ma_schannel_set_win_error(MYSQL *mysql);
const struct st_cipher_suite sc_ciphers[]=
{
{CALG_3DES, "CALG_3DES"},
{CALG_3DES_112, "CALG_3DES_112"},
{CALG_AES, "CALG_AES"},
{CALG_AES_128, "CALG_AES_128"},
{CALG_AES_192, "CALG_AES_192"},
{CALG_AES_256, "CALG_AES_256"},
{CALG_AGREEDKEY_ANY, "CALG_AGREEDKEY_ANY"},
{CALG_CYLINK_MEK, "CALG_CYLINK_MEK"},
{CALG_DES, "CALG_DES"},
{CALG_DESX, "CALG_DESX"},
{CALG_DH_EPHEM, "CALG_DH_EPHEM"},
{CALG_DH_SF, "CALG_DH_SF"},
{CALG_DSS_SIGN, "CALG_DSS_SIGN"},
{CALG_ECDH, "CALG_ECDH"},
{CALG_ECDSA, "CALG_ECDSA"},
{CALG_ECMQV, "CALG_ECMQV"},
{CALG_HASH_REPLACE_OWF, "CALG_HASH_REPLACE_OWF"},
{CALG_HUGHES_MD5, "CALG_HUGHES_MD5"},
{CALG_HMAC, "CALG_HMAC"},
{CALG_KEA_KEYX, "CALG_KEA_KEYX"},
{CALG_MAC, "CALG_MAC"},
{CALG_MD2, "CALG_MD2"},
{CALG_MD4, "CALG_MD4"},
{CALG_MD4, "CALG_MD5"},
{CALG_NO_SIGN, "CALG_NO_SIGN"},
{CALG_OID_INFO_CNG_ONLY, "CALG_OID_INFO_CNG_ONLY"},
{CALG_OID_INFO_PARAMETERS, "CALG_OID_INFO_PARAMETERS"},
{CALG_PCT1_MASTER, "CALG_PCT1_MASTER"},
{CALG_RC2, "CALG_RC2"},
{CALG_RC4, "CALG_RC4"},
{CALG_RC5, "CALG_RC5"},
{CALG_RSA_KEYX, "CALG_RSA_KEYX"},
{CALG_RSA_SIGN, "CALG_RSA_SIGN"},
{CALG_SCHANNEL_MAC_KEY, "CALG_SCHANNEL_MAC_KEY"},
{CALG_SCHANNEL_MASTER_HASH, "CALG_SCHANNEL_MASTER_HASH"},
{CALG_SEAL, "CALG_SEAL"},
{CALG_SHA, "CALG_SHA"},
{CALG_SHA1, "CALG_SHA1"},
{CALG_SHA_256, "CALG_SHA_256"},
{CALG_SHA_384, "CALG_SHA_384"},
{CALG_SHA_512, "CALG_SHA_512"},
{CALG_SKIPJACK, "CALG_SKIPJACK"},
{CALG_SSL2_MASTER, "CALG_SSL2_MASTER"},
{CALG_SSL3_MASTER, "CALG_SSL3_MASTER"},
{CALG_SSL3_SHAMD5, "CALG_SSL3_SHAMD5"},
{CALG_TEK, "CALG_TEK"},
{CALG_TLS1_MASTER, "CALG_TLS1_MASTER"},
{CALG_TLS1PRF, "CALG_TLS1PRF"},
{0, NULL}
};
static int ssl_thread_init()
{
return 0;
}
/*
Initializes SSL and allocate global
context SSL_context
SYNOPSIS
ma_ssl_start
RETURN VALUES
0 success
1 error
*/
int ma_ssl_start(char *errmsg, size_t errmsg_len, int count, va_list list)
{
if (!ma_ssl_initialized)
{
pthread_mutex_init(&LOCK_schannel_config,MY_MUTEX_INIT_FAST);
pthread_mutex_lock(&LOCK_schannel_config);
ma_ssl_initialized= TRUE;
}
pthread_mutex_unlock(&LOCK_schannel_config);
return 0;
}
/*
Release SSL and free resources
Will be automatically executed by
mysql_server_end() function
SYNOPSIS
ma_ssl_end()
void
RETURN VALUES
void
*/
void ma_ssl_end()
{
pthread_mutex_lock(&LOCK_schannel_config);
if (ma_ssl_initialized)
{
ma_ssl_initialized= FALSE;
}
pthread_mutex_unlock(&LOCK_schannel_config);
pthread_mutex_destroy(&LOCK_schannel_config);
return;
}
/* {{{ static int ma_ssl_set_client_certs(MARIADB_SSL *cssl) */
static int ma_ssl_set_client_certs(MARIADB_SSL *cssl)
{
MYSQL *mysql= cssl->cio->mysql;
char *certfile= mysql->options.ssl_cert,
*keyfile= mysql->options.ssl_key,
*cafile= mysql->options.ssl_ca;
SC_CTX *sctx= (SC_CTX *)cssl->ssl;
MARIADB_CIO *cio= cssl->cio;
if (cafile)
{
HCERTSTORE myCS= NULL;
char szName[64];
if (!(sctx->client_ca_ctx = ma_schannel_create_cert_context(cio, cafile)))
goto end;
/* For X509 authentication we need to add ca certificate to local MY store.
Schannel doesn't provide a callback to send ca to server during handshake */
if ((myCS= CertOpenStore(CERT_STORE_PROV_SYSTEM,
0, //X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
0,
CERT_SYSTEM_STORE_CURRENT_USER,
L"CA")))
{
CertAddCertificateContextToStore(myCS, sctx->client_ca_ctx, CERT_STORE_ADD_NEWER, NULL);
CertCloseStore(myCS, 0);
}
else {
ma_schannel_set_win_error(sctx->mysql);
goto end;
}
}
if (!certfile && keyfile)
certfile= keyfile;
if (!keyfile && certfile)
keyfile= certfile;
if (certfile && certfile[0])
if (!(sctx->client_cert_ctx = ma_schannel_create_cert_context(cssl->cio, certfile)))
goto end;
if (sctx->client_cert_ctx && keyfile[0])
if (!ma_schannel_load_private_key(cio, sctx->client_cert_ctx, keyfile))
goto end;
if (mysql->options.extension && mysql->options.extension->ssl_crl)
{
if (!(sctx->client_crl_ctx= ma_schannel_create_crl_context(cio, mysql->options.extension->ssl_crl)))
goto end;
}
return 0;
end:
if (sctx->client_ca_ctx)
CertFreeCertificateContext(sctx->client_ca_ctx);
if (sctx->client_cert_ctx)
CertFreeCertificateContext(sctx->client_cert_ctx);
if (sctx->client_crl_ctx)
CertFreeCRLContext(sctx->client_crl_ctx);
sctx->client_ca_ctx= sctx->client_cert_ctx= NULL;
sctx->client_crl_ctx= NULL;
return 1;
}
/* }}} */
/* {{{ void *ma_ssl_init(MARIADB_SSL *cssl, MYSQL *mysql) */
void *ma_ssl_init(MYSQL *mysql)
{
int verify;
SC_CTX *sctx= NULL;
pthread_mutex_lock(&LOCK_schannel_config);
if ((sctx= LocalAlloc(0, sizeof(SC_CTX))))
{
ZeroMemory(sctx, sizeof(SC_CTX));
sctx->mysql= mysql;
}
pthread_mutex_unlock(&LOCK_schannel_config);
return sctx;
}
/* }}} */
my_bool ma_ssl_connect(MARIADB_SSL *cssl)
{
my_bool blocking;
MYSQL *mysql;
SCHANNEL_CRED Cred;
MARIADB_CIO *cio;
SC_CTX *sctx;
SECURITY_STATUS sRet;
PCCERT_CONTEXT pRemoteCertContext = NULL,
pLocalCertContext= NULL;
ALG_ID AlgId[2]= {0, 0};
if (!cssl || !cssl->cio)
return 1;;
cio= cssl->cio;
sctx= (SC_CTX *)cssl->ssl;
/* Set socket to blocking if not already set */
if (!(blocking= cio->methods->is_blocking(cio)))
cio->methods->blocking(cio, TRUE, 0);
mysql= cio->mysql;
if (ma_ssl_set_client_certs(cssl))
goto end;
/* Set cipher */
if (mysql->options.ssl_cipher)
{
DWORD i= 0;
while (sc_ciphers[i].cipher) {
if (!strcmp(sc_ciphers[i].cipher, mysql->options.ssl_cipher))
{
AlgId[0]= sc_ciphers[i].aid;
break;
}
}
Cred.palgSupportedAlgs= &AlgId;
}
ZeroMemory(&Cred, sizeof(SCHANNEL_CRED));
Cred.dwVersion= SCHANNEL_CRED_VERSION;
Cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK | SCH_SEND_ROOT_CERT |
SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION;
if (sctx->client_cert_ctx)
{
Cred.cCreds = 1;
Cred.paCred = &sctx->client_cert_ctx;
}
Cred.grbitEnabledProtocols= SP_PROT_TLS1;
if ((sRet= AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND,
NULL, &Cred, NULL, NULL, &sctx->CredHdl, NULL)) != SEC_E_OK)
{
ma_schannel_set_sec_error(cio, sRet);
goto end;
}
if (ma_schannel_client_handshake(cssl))
goto end;
sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext);
if (sRet != SEC_E_OK)
{
ma_schannel_set_sec_error(cio, sRet);
goto end;
}
if (!ma_schannel_verify_certs(sctx, 0))
goto end;
return 0;
end:
/* todo: cleanup */
if (sctx->IoBufferSize)
LocalFree(sctx->IoBuffer);
if (sctx->client_ca_ctx)
CertFreeCertificateContext(sctx->client_ca_ctx);
if (sctx->client_cert_ctx)
CertFreeCertificateContext(sctx->client_cert_ctx);
if (sctx->client_crl_ctx)
CertFreeCRLContext(sctx->client_crl_ctx);
return 1;
}
size_t ma_ssl_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length)
{
SC_CTX *sctx= (SC_CTX *)cssl->ssl;
MARIADB_CIO *cio= sctx->mysql->net.cio;
size_t dlength= -1;
ma_schannel_read_decrypt(cio, &sctx->CredHdl, &sctx->ctxt, &dlength, buffer, length);
return dlength;
}
size_t ma_ssl_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length)
{
SC_CTX *sctx= (SC_CTX *)cssl->ssl;
MARIADB_CIO *cio= sctx->mysql->net.cio;
size_t rc, wlength= 0;
size_t remain= length;
while (remain)
{
if ((rc= ma_schannel_write_encrypt(cio, (uchar *)buffer + wlength, remain)) <= 0)
return rc;
wlength+= rc;
remain-= rc;
}
return length;
}
/* {{{ void ma_ssl_close(MARIADB_CIO *cio) */
void ma_ssl_close(MARIADB_CIO *cio)
{
SC_CTX *sctx;
if (!cio || !cio->cssl)
return;
if ((sctx= (SC_CTX *)cio->cssl))
{
if (sctx->IoBufferSize)
LocalFree(sctx->IoBuffer);
if (sctx->client_ca_ctx)
CertFreeCertificateContext(sctx->client_ca_ctx);
if (sctx->client_cert_ctx)
CertFreeCertificateContext(sctx->client_cert_ctx);
if (sctx->client_crl_ctx)
CertFreeCRLContext(sctx->client_crl_ctx);
FreeCredentialHandle(&sctx->CredHdl);
DeleteSecurityContext(&sctx->ctxt);
}
LocalFree(sctx);
}
/* }}} */
int ma_ssl_verify_server_cert(MARIADB_SSL *cssl)
{
SC_CTX *sctx= (SC_CTX *)cssl->ssl;
MARIADB_CIO *cio= cssl->cio;
int rc= 1;
char *szName= NULL;
char *pszServerName= cio->mysql->host;
/* check server name */
if (pszServerName && (sctx->mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT))
{
PCCERT_CONTEXT pServerCert;
DWORD NameSize= 0;
char *p1, *p2;
SECURITY_STATUS sRet;
if ((sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert)) != SEC_E_OK)
{
ma_schannel_set_sec_error(cio, sRet);
return 1;
}
if (!(NameSize= CertNameToStr(pServerCert->dwCertEncodingType,
&pServerCert->pCertInfo->Subject,
CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
NULL, 0)))
{
cio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Can't retrieve name of server certificate");
return 1;
}
if (!(szName= LocalAlloc(0, NameSize + 1)))
{
cio->set_error(sctx->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL);
goto end;
}
if (!CertNameToStr(pServerCert->dwCertEncodingType,
&pServerCert->pCertInfo->Subject,
CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
szName, NameSize))
{
cio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Can't retrieve name of server certificate");
goto end;
}
if ((p1 = strstr(szName, "CN=")))
{
p1+= 3;
if ((p2= strstr(p1, ", ")))
*p2= 0;
if (!strcmp(pszServerName, p1))
{
rc= 0;
goto end;
}
cio->set_error(cio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
"Name of server certificate didn't match");
}
}
end:
if (szName)
LocalFree(szName);
return rc;
}
const char *ma_ssl_get_cipher(MARIADB_SSL *cssl)
{
SecPkgContext_ConnectionInfo cinfo;
SECURITY_STATUS sRet;
SC_CTX *sctx;
DWORD i= 0;
if (!cssl || !cssl->ssl)
return NULL;
sctx= (SC_CTX *)cssl->ssl;
sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_CONNECTION_INFO, (PVOID)&cinfo);
if (sRet != SEC_E_OK)
return NULL;
while (sc_ciphers[i].cipher)
{
if (sc_ciphers[i].aid == cinfo.aiCipher)
return sc_ciphers[i].cipher;
i++;
}
return NULL;
}
unsigned int ma_ssl_get_finger_print(MARIADB_SSL *cssl, unsigned char *fp, unsigned int len)
{
SC_CTX *sctx= (SC_CTX *)cssl->ssl;
PCCERT_CONTEXT pRemoteCertContext = NULL;
if (QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext) != SEC_E_OK)
return NULL;
CertGetCertificateContextProperty(pRemoteCertContext, CERT_HASH_PROP_ID, fp, &len);
return len;
}

View File

@@ -1,713 +0,0 @@
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA */
/*
Note that we can't have assertion on file descriptors; The reason for
this is that during mysql shutdown, another thread can close a file
we are working on. In this case we should just return read errors from
the file descriptior.
*/
#ifndef HAVE_VIO /* is Vio suppored by the Vio lib ? */
#include <my_global.h>
#include <errno.h>
#include <assert.h>
#include <violite.h>
#include <my_sys.h>
#include <my_net.h>
#include <m_string.h>
#ifdef HAVE_POLL
#include <sys/poll.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_OPENSSL
#include <ma_secure.h>
#endif
#ifdef _WIN32
#define socklen_t int
#pragma comment (lib, "ws2_32")
#endif
#if !defined(MSDOS) && !defined(_WIN32) && !defined(HAVE_BROKEN_NETINET_INCLUDES) && !defined(__BEOS__) && !defined(__FreeBSD__)
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#if !defined(alpha_linux_port)
#include <netinet/tcp.h>
#endif
#endif
#if defined(__EMX__) || defined(OS2)
#define ioctlsocket ioctl
#endif /* defined(__EMX__) */
#if defined(MSDOS) || defined(_WIN32)
#define O_NONBLOCK 1 /* For emulation of fcntl() */
#endif
#ifndef EWOULDBLOCK
#define SOCKET_EWOULDBLOCK SOCKET_EAGAIN
#endif
#include <mysql_async.h>
#include <my_context.h>
#ifdef _WIN32
#define ma_get_error() WSAGetLastError()
#else
#define ma_get_error() errno
#endif
typedef void *vio_ptr;
typedef char *vio_cstring;
/*
* Helper to fill most of the Vio* with defaults.
*/
void vio_reset(Vio* vio, enum enum_vio_type type,
my_socket sd, HANDLE hPipe,
my_bool localhost)
{
uchar *save_cache= vio->cache;
int save_timeouts[2]= {vio->read_timeout, vio->write_timeout};
bzero((char*) vio, sizeof(*vio));
vio->type= type;
vio->sd= sd;
vio->hPipe= hPipe;
vio->localhost= localhost;
/* do not clear cache */
vio->cache= vio->cache_pos= save_cache;
vio->cache_size= 0;
vio->read_timeout= save_timeouts[0];
vio->write_timeout= save_timeouts[1];
}
void vio_timeout(Vio *vio, int type, uint timeval)
{
#ifdef _WIN32
uint timeout= timeval; /* milli secs */
#else
struct timeval timeout;
timeout.tv_sec= timeval;
timeout.tv_usec= (timeval % 1000) * 1000;
#endif
if (setsockopt(vio->sd, SOL_SOCKET, type,
#ifdef _WIN32
(const char *)&timeout,
#else
(const void *)&timeout,
#endif
sizeof(timeout)))
{
DBUG_PRINT("error", ("setsockopt failed. Errno: %d", errno));
}
}
void vio_read_timeout(Vio *vio, uint timeout)
{
vio->read_timeout= (timeout >= 0) ? timeout * 1000 : -1;
vio_timeout(vio, SO_RCVTIMEO, vio->read_timeout);
}
void vio_write_timeout(Vio *vio, uint timeout)
{
vio->write_timeout= (timeout >= 0) ? timeout * 1000 : -1;
vio_timeout(vio, SO_SNDTIMEO, vio->write_timeout);
}
/* Open the socket or TCP/IP connection and read the fnctl() status */
Vio *vio_new(my_socket sd, enum enum_vio_type type, my_bool localhost)
{
Vio *vio;
DBUG_ENTER("vio_new");
DBUG_PRINT("enter", ("sd=%d", sd));
if ((vio = (Vio*) my_malloc(sizeof(*vio),MYF(MY_WME))))
{
vio_reset(vio, type, sd, 0, localhost);
sprintf(vio->desc,
(vio->type == VIO_TYPE_SOCKET ? "socket (%d)" : "TCP/IP (%d)"),
vio->sd);
#if !defined(__WIN32) && !defined(__EMX__) && !defined(OS2)
#if !defined(NO_FCNTL_NONBLOCK)
vio->fcntl_mode = fcntl(sd, F_GETFL);
#elif defined(HAVE_SYS_IOCTL_H) /* hpux */
/* Non blocking sockets doesn't work good on HPUX 11.0 */
(void) ioctl(sd,FIOSNBIO,0);
#endif
#else /* !defined(_WIN32) && !defined(__EMX__) */
{
/* set to blocking mode by default */
ulong arg=0, r;
r = ioctlsocket(vio->sd,FIONBIO,(void*) &arg/*, sizeof(arg)*/);
}
#endif
}
if (!(vio->cache= my_malloc(VIO_CACHE_SIZE, MYF(MY_WME))))
{
my_free(vio);
vio= NULL;
}
vio->cache_size= 0;
vio->cache_pos= vio->cache;
DBUG_RETURN(vio);
}
#ifdef _WIN32
Vio *vio_new_win32pipe(HANDLE hPipe)
{
Vio *vio;
DBUG_ENTER("vio_new_handle");
if ((vio = (Vio*) my_malloc(sizeof(Vio),MYF(MY_ZEROFILL))))
{
vio_reset(vio, VIO_TYPE_NAMEDPIPE, 0, hPipe, TRUE);
strmov(vio->desc, "named pipe");
}
DBUG_RETURN(vio);
}
#endif
void vio_delete(Vio * vio)
{
/* It must be safe to delete null pointers. */
/* This matches the semantics of C++'s delete operator. */
if (vio)
{
if (vio->type != VIO_CLOSED)
vio_close(vio);
my_free(vio->cache);
my_free(vio);
}
}
int vio_errno(Vio *vio __attribute__((unused)))
{
return socket_errno; /* On Win32 this mapped to WSAGetLastError() */
}
int vio_wait_or_timeout(Vio *vio, my_bool is_read, int timeout)
{
int rc;
#ifndef _WIN32
struct pollfd p_fd;
#else
struct timeval tv= {0,0};
fd_set fds, exc_fds;
#endif
/* we don't support it via named pipes yet.
* maybe this could be handled via PeekNamedPipe somehow !? */
if (vio->type == VIO_TYPE_NAMEDPIPE)
return 1;
/*
Note that if zero timeout, then we will not block, so we do not need to
yield to calling application in the async case.
*/
if (timeout != 0 && vio->async_context && vio->async_context->active)
{
rc= my_io_wait_async(vio->async_context,
(is_read) ? VIO_IO_EVENT_READ : VIO_IO_EVENT_WRITE,
timeout);
return(rc);
}
else
{
#ifndef _WIN32
p_fd.fd= vio->sd;
p_fd.events= (is_read) ? POLLIN : POLLOUT;
do {
rc= poll(&p_fd, 1, timeout);
} while (rc == -1 || errno == EINTR);
if (rc == 0)
errno= ETIMEDOUT;
#else
FD_ZERO(&fds);
FD_ZERO(&exc_fds);
FD_SET(vio->sd, &fds);
FD_SET(vio->sd, &exc_fds);
if (timeout >= 0)
{
tv.tv_sec= timeout / 1000;
tv.tv_usec= (timeout % 1000) * 1000;
}
rc= select(0, (is_read) ? &fds : NULL,
(is_read) ? NULL : &fds,
&exc_fds,
(timeout >= 0) ? &tv : NULL);
if (rc == SOCKET_ERROR)
errno= WSAGetLastError();
if (rc == 0)
errno= ETIMEDOUT;
#endif
}
return rc;
}
size_t vio_real_read(Vio *vio, gptr buf, size_t size)
{
size_t r;
switch(vio->type) {
#ifdef HAVE_OPENSSL
case VIO_TYPE_SSL:
return my_ssl_read(vio, (char *)buf, size);
break;
#endif
#ifdef _WIN32
case VIO_TYPE_NAMEDPIPE:
{
DWORD length= 0;
if (!ReadFile(vio->hPipe, buf, (DWORD)size, &length, NULL))
return -1;
return length;
}
break;
#endif
default:
if (vio->async_context && vio->async_context->active)
r= my_recv_async(vio->async_context,
vio->sd,
buf, size, vio->read_timeout);
else
{
if (vio->async_context)
{
/*
If switching from non-blocking to blocking API usage, set the socket
back to blocking mode.
*/
my_bool old_mode;
vio_blocking(vio, TRUE, &old_mode);
}
#ifndef _WIN32
do {
r= read(vio->sd, buf, size);
} while (r == -1 && errno == EINTR);
while (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)
&& vio->read_timeout > 0)
{
if (vio_wait_or_timeout(vio, TRUE, vio->write_timeout) < 1)
return 0;
do {
r= read(vio->sd, buf, size);
} while (r == -1 && errno == EINTR);
}
#else
{
WSABUF wsaData;
DWORD dwBytes = 0;
DWORD flags = 0;
wsaData.len= size;
wsaData.buf= buf;
if (WSARecv(vio->sd, &wsaData, 1, &dwBytes, &flags, NULL, NULL) == SOCKET_ERROR)
{
errno= WSAGetLastError();
return 0;
}
r= (size_t)dwBytes;
}
#endif
}
break;
}
return r;
}
size_t vio_read(Vio * vio, gptr buf, size_t size)
{
size_t r;
DBUG_ENTER("vio_read");
DBUG_PRINT("enter", ("sd=%d size=%d", vio->sd, size));
if (!vio->cache)
DBUG_RETURN(vio_real_read(vio, buf, size));
if (vio->cache + vio->cache_size > vio->cache_pos)
{
r= MIN(size, vio->cache + vio->cache_size - vio->cache_pos);
memcpy(buf, vio->cache_pos, r);
vio->cache_pos+= r;
}
else if (size >= VIO_CACHE_MIN_SIZE)
{
r= vio_real_read(vio, buf, size);
}
else
{
r= vio_real_read(vio, vio->cache, VIO_CACHE_SIZE);
if (r > 0)
{
if (size < r)
{
vio->cache_size= r; /* might be < VIO_CACHE_SIZE */
vio->cache_pos= vio->cache + size;
r= size;
}
memcpy(buf, vio->cache, r);
}
}
#ifndef DBUG_OFF
if ((size_t)r == -1)
{
DBUG_PRINT("vio_error", ("Got error %d during read",socket_errno));
}
#endif /* DBUG_OFF */
DBUG_PRINT("exit", ("%u", (uint)r));
DBUG_RETURN(r);
}
/*
Return data from the beginning of the receive queue without removing
that data from the queue. A subsequent receive call will return the same data.
*/
my_bool vio_read_peek(Vio *vio, size_t *bytes)
{
#ifdef _WIN32
if (ioctlsocket(vio->sd, FIONREAD, (unsigned long*)bytes))
return TRUE;
#else
char buffer[1024];
ssize_t length;
vio_blocking(vio, 0, 0);
length= recv(vio->sd, &buffer, sizeof(buffer), MSG_PEEK);
if (length < 0)
return TRUE;
*bytes= length;
#endif
return FALSE;
}
size_t vio_write(Vio * vio, const gptr buf, size_t size)
{
size_t r;
DBUG_ENTER("vio_write");
DBUG_PRINT("enter", ("sd=%d size=%d", vio->sd, size));
#ifdef HAVE_OPENSSL
if (vio->type == VIO_TYPE_SSL)
{
r= my_ssl_write(vio, (uchar *)buf, size);
DBUG_RETURN(r);
}
#endif
#ifdef _WIN32
if ( vio->type == VIO_TYPE_NAMEDPIPE)
{
DWORD length;
if (!WriteFile(vio->hPipe, (char*) buf, (DWORD)size, &length, NULL))
DBUG_RETURN(-1);
DBUG_RETURN(length);
}
#endif
if (vio->async_context && vio->async_context->active)
r= my_send_async(vio->async_context, vio->sd, buf, size,
vio->write_timeout);
else
{
if (vio->async_context)
{
/*
If switching from non-blocking to blocking API usage, set the socket
back to blocking mode.
*/
my_bool old_mode;
vio_blocking(vio, TRUE, &old_mode);
}
#ifndef _WIN32
do {
r= send(vio->sd, buf, size, vio->write_timeout ? MSG_DONTWAIT : MSG_WAITALL);
} while (r == -1 && errno == EINTR);
while (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK) &&
vio->write_timeout > 0)
{
if (vio_wait_or_timeout(vio, FALSE, vio->write_timeout) < 1)
return 0;
do {
r= send(vio->sd, buf, size, vio->write_timeout ? MSG_DONTWAIT : MSG_WAITALL);
} while (r == -1 && errno == EINTR);
}
#else
{
WSABUF wsaData;
DWORD dwBytes = 0;
wsaData.len= size;
wsaData.buf= (char *)buf;
if (WSASend(vio->sd, &wsaData, 1, &dwBytes, 0, NULL, NULL) == SOCKET_ERROR)
{
errno= WSAGetLastError();
DBUG_RETURN(0);
}
r= (size_t)dwBytes;
}
#endif
}
#ifndef DBUG_OFF
if ((size_t)r == -1)
{
DBUG_PRINT("vio_error", ("Got error on write: %d",socket_errno));
}
#endif /* DBUG_OFF */
DBUG_PRINT("exit", ("%u", (uint)r));
DBUG_RETURN(r);
}
int vio_blocking(Vio *vio, my_bool block, my_bool *previous_mode)
{
int *sd_flags= &vio->fcntl_mode;
int save_flags= vio->fcntl_mode;
my_bool tmp;
my_socket sock= vio->sd;
if (vio->type == VIO_TYPE_NAMEDPIPE)
return 0;
if (!previous_mode)
previous_mode= &tmp;
#ifdef _WIN32
*previous_mode= (*sd_flags & O_NONBLOCK) != 0;
*sd_flags = (block) ? *sd_flags & ~O_NONBLOCK : *sd_flags | O_NONBLOCK;
{
ulong arg= 1 - block;
if (ioctlsocket(sock, FIONBIO, (void *)&arg))
{
vio->fcntl_mode= save_flags;
return(WSAGetLastError());
}
}
#else
#if defined(O_NONBLOCK)
*previous_mode= (*sd_flags & O_NONBLOCK) != 0;
*sd_flags = (block) ? *sd_flags & ~O_NONBLOCK : *sd_flags | O_NONBLOCK;
#elif defined(O_NDELAY)
*previous_mode= (*sd_flags & O_NODELAY) != 0;
*sd_flags = (block) ? *sd_flags & ~O_NODELAY : *sd_flags | O_NODELAY;
#elif defined(FNDELAY)
*previous_mode= (*sd_flags & O_FNDELAY) != 0;
*sd_flags = (block) ? *sd_flags & ~O_FNDELAY : *sd_flags | O_FNDELAY;
#else
#error socket blocking is not supported on this platform
#endif
if (fcntl(sock, F_SETFL, *sd_flags) == -1)
{
vio->fcntl_mode= save_flags;
return errno;
}
#endif
return 0;
}
my_bool
vio_is_blocking(Vio * vio)
{
my_bool r;
DBUG_ENTER("vio_is_blocking");
r = !(vio->fcntl_mode & O_NONBLOCK);
DBUG_PRINT("exit", ("%d", (int) r));
DBUG_RETURN(r);
}
int vio_fastsend(Vio * vio __attribute__((unused)))
{
int r=0;
DBUG_ENTER("vio_fastsend");
#ifdef IPTOS_THROUGHPUT
{
#ifndef __EMX__
int tos = IPTOS_THROUGHPUT;
if (!setsockopt(vio->sd, IPPROTO_IP, IP_TOS, (void *) &tos, sizeof(tos)))
#endif /* !__EMX__ */
{
int nodelay = 1;
if (setsockopt(vio->sd, IPPROTO_TCP, TCP_NODELAY, (void *) &nodelay,
sizeof(nodelay))) {
DBUG_PRINT("warning",
("Couldn't set socket option for fast send"));
r= -1;
}
}
}
#endif /* IPTOS_THROUGHPUT */
DBUG_PRINT("exit", ("%d", r));
DBUG_RETURN(r);
}
int vio_keepalive(Vio* vio, my_bool set_keep_alive)
{
int r=0;
uint opt = 0;
DBUG_ENTER("vio_keepalive");
DBUG_PRINT("enter", ("sd=%d set_keep_alive=%d", vio->sd, (int)
set_keep_alive));
if (vio->type != VIO_TYPE_NAMEDPIPE)
{
if (set_keep_alive)
opt = 1;
r = setsockopt(vio->sd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt,
sizeof(opt));
}
DBUG_RETURN(r);
}
my_bool
vio_should_retry(Vio * vio __attribute__((unused)))
{
int en = socket_errno;
return en == SOCKET_EAGAIN || en == SOCKET_EINTR || en == SOCKET_EWOULDBLOCK;
}
int vio_close(Vio * vio)
{
int r;
DBUG_ENTER("vio_close");
#ifdef HAVE_OPENSSL
if (vio->type == VIO_TYPE_SSL)
{
r = my_ssl_close(vio);
}
#endif
#ifdef _WIN32
if (vio->type == VIO_TYPE_NAMEDPIPE)
{
r=CloseHandle(vio->hPipe);
}
else if (vio->type != VIO_CLOSED)
#endif /* _WIN32 */
{
r=0;
if (shutdown(vio->sd,2))
r= -1;
if (closesocket(vio->sd))
r= -1;
}
if (r)
{
DBUG_PRINT("vio_error", ("close() failed, error: %d",socket_errno));
/* FIXME: error handling (not critical for MySQL) */
}
vio->type= VIO_CLOSED;
vio->sd= -1;
DBUG_RETURN(r);
}
const char *vio_description(Vio * vio)
{
return vio->desc;
}
enum enum_vio_type vio_type(Vio* vio)
{
return vio->type;
}
my_socket vio_fd(Vio* vio)
{
return vio->sd;
}
my_bool vio_peer_addr(Vio * vio, char *buf)
{
DBUG_ENTER("vio_peer_addr");
DBUG_PRINT("enter", ("sd=%d", vio->sd));
if (vio->localhost)
{
strmov(buf,"127.0.0.1");
}
else
{
socklen_t addrLen = sizeof(struct sockaddr);
if (getpeername(vio->sd, (struct sockaddr *) (& (vio->remote)),
&addrLen) != 0)
{
DBUG_PRINT("exit", ("getpeername, error: %d", socket_errno));
DBUG_RETURN(1);
}
my_inet_ntoa(vio->remote.sin_addr,buf);
}
DBUG_PRINT("exit", ("addr=%s", buf));
DBUG_RETURN(0);
}
void vio_in_addr(Vio *vio, struct in_addr *in)
{
DBUG_ENTER("vio_in_addr");
if (vio->localhost)
bzero((char*) in, sizeof(*in)); /* This should never be executed */
else
*in=vio->remote.sin_addr;
DBUG_VOID_RETURN;
}
/* Return 0 if there is data to be read */
/*
my_bool vio_poll_read(Vio *vio,uint timeout)
{
#ifndef HAVE_POLL
return 0;
#else
struct pollfd fds;
int res;
DBUG_ENTER("vio_poll");
fds.fd=vio->sd;
fds.events=POLLIN;
fds.revents=0;
if ((res=poll(&fds,1,(int) timeout*1000)) <= 0)
{
DBUG_RETURN(res < 0 ? 0 : 1);
}
DBUG_RETURN(fds.revents & POLLIN ? 0 : 1);
#endif
}
*/
#endif /* HAVE_VIO */

View File

@@ -1,6 +1,8 @@
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include)
# Dialog plugin
IF(AUTH_DIALOG_PLUGIN_TYPE MATCHES "DYNAMIC")
ADD_DEFINITIONS(-DHAVE_DIALOG_DYNAMIC=1)
SET(DIALOG_SOURCES dialog.c ${CMAKE_SOURCE_DIR}/libmariadb/get_password.c)
IF(WIN32)
SET(DIALOG_SOURCES ${DIALOG_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def)
@@ -13,9 +15,12 @@ INSTALL(TARGETS
RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}"
LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}"
ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}")
ENDIF()
# Cleartext
# Dialog plugin
IF(AUTH_CLEARTEXT_PLUGIN_TYPE MATCHES "DYNAMIC")
ADD_DEFINITIONS(-DHAVE_CLEARTEXT_DYNAMIC=1)
SET(CTEXT_SOURCES mariadb_cleartext.c)
IF(WIN32)
SET(CTEXT_SOURCES ${CTEXT_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def)
@@ -28,5 +33,4 @@ INSTALL(TARGETS
RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}"
LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}"
ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}")
# Cleartext
ENDIF()

View File

@@ -41,15 +41,23 @@ static int auth_dialog_init(char *unused1,
mysql_authentication_dialog_ask_t auth_dialog_func;
mysql_declare_client_plugin(AUTHENTICATION)
#ifndef HAVE_DIALOG_DYNAMIC
struct st_mysql_client_plugin_AUTHENTICATION auth_dialog_plugin=
#else
struct st_mysql_client_plugin_AUTHENTICATION _mysql_client_plugin_declaration_ =
#endif
{
MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION,
"dialog",
"Sergei Golubchik, Georg Richter",
"Dialog Client Authentication Plugin",
{0,1,0},
"LGPL",
auth_dialog_init,
NULL,
auth_dialog_open
mysql_end_client_plugin;
};
/* {{{ static char *auth_dialog_native_prompt */

View File

@@ -53,14 +53,22 @@ static int clear_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
}
/* }}} */
mysql_declare_client_plugin(AUTHENTICATION)
#ifndef HAVE_DIALOG_DYNAMIC
struct st_mysql_client_plugin_AUTHENTICATION auth_cleartext_plugin=
#else
struct st_mysql_client_plugin_AUTHENTICATION _mysql_client_plugin_declaration_ =
#endif
{
MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION,
"mysql_clear_password",
"Georg Richter",
"MariaDB clear password authentication plugin",
{0,1,0},
"LGPL",
NULL,
NULL,
clear_password_auth_client
mysql_end_client_plugin;
};

View File

@@ -1,36 +1,10 @@
/************************************************************************************
Copyright (C) 2012-2015 Monty Program AB, MariaDB Corporation AB,
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not see <http://www.gnu.org/licenses>
or write to the Free Software Foundation, Inc.,
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
Part of this code includes code from the PHP project which
is freely available from http://www.php.net
Originally written by Sergei Golubchik
*************************************************************************************/
#include <my_global.h>
#include <my_sys.h>
#include <m_string.h>
#include <errmsg.h>
#include <ma_common.h>
#include <mysql/client_plugin.h>
#include <violite.h>
#ifdef HAVE_OPENSSL
#include <ma_secure.h>
#endif
#include <ma_cio.h>
typedef struct st_mysql_client_plugin_AUTHENTICATION auth_plugin_t;
static int client_mpvio_write_packet(struct st_plugin_vio*, const uchar*, size_t);
@@ -39,10 +13,12 @@ static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql);
extern void read_user_name(char *name);
extern uchar *ma_send_connect_attr(MYSQL *mysql, uchar *buffer);
/*
#define compile_time_assert(A) \
do {\
typedef char constraint[(A) ? 1 : -1];\
} while (0);
*/
auth_plugin_t native_password_client_plugin=
{
@@ -52,6 +28,7 @@ auth_plugin_t native_password_client_plugin=
"R.J.Silk, Sergei Golubchik",
"Native MySQL authentication",
{1, 0, 0},
"LGPL",
NULL,
NULL,
native_password_auth_client
@@ -65,6 +42,7 @@ auth_plugin_t old_password_client_plugin=
"R.J.Silk, Sergei Golubchik",
"Old MySQL-3.23 authentication",
{1, 0, 0},
"LGPL",
NULL,
NULL,
old_password_auth_client
@@ -254,13 +232,16 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
if (mysql->client_flag & CLIENT_MULTI_STATEMENTS)
mysql->client_flag|= CLIENT_MULTI_RESULTS;
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
#if defined(HAVE_SSL) && !defined(EMBEDDED_LIBRARY)
if (mysql->options.ssl_key || mysql->options.ssl_cert ||
mysql->options.ssl_ca || mysql->options.ssl_capath ||
mysql->options.ssl_cipher)
mysql->options.use_ssl= 1;
if (mysql->options.use_ssl)
mysql->client_flag|= CLIENT_SSL;
#endif /* HAVE_SSL && !EMBEDDED_LIBRARY*/
if (mpvio->db)
mysql->client_flag|= CLIENT_CONNECT_WITH_DB;
/* if server doesn't support SSL and verification of server certificate
was set to mandatory, we need to return an error */
@@ -277,9 +258,6 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
}
}
#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY*/
if (mpvio->db)
mysql->client_flag|= CLIENT_CONNECT_WITH_DB;
/* Remove options that server doesn't support */
mysql->client_flag= mysql->client_flag &
@@ -305,7 +283,7 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
int3store(buff+2, net->max_packet_size);
end= buff+5;
}
#ifdef HAVE_OPENSSL
#ifdef HAVE_SSL
if (mysql->options.ssl_key ||
mysql->options.ssl_cert ||
mysql->options.ssl_ca ||
@@ -318,11 +296,9 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
#endif
)
mysql->options.use_ssl= 1;
if (mysql->options.use_ssl &&
(mysql->client_flag & CLIENT_SSL))
{
SSL *ssl;
/*
Send mysql->client_flag, max_packet_size - unencrypted otherwise
the server does not know we want to do SSL
@@ -335,38 +311,15 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
errno);
goto error;
}
/* Create SSL */
if (!(ssl= my_ssl_init(mysql)))
goto error;
/* Connect to the server */
if (my_ssl_connect(ssl))
{
SSL_free(ssl);
if (ma_cio_start_ssl(mysql->net.cio))
goto error;
}
#endif /* HAVE_SSL */
if (mysql->options.extension &&
(mysql->options.extension->ssl_fp || mysql->options.extension->ssl_fp_list))
{
if (ma_ssl_verify_fingerprint(ssl))
goto error;
}
if ((mysql->options.ssl_ca || mysql->options.ssl_capath) &&
(mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) &&
my_ssl_verify_server_cert(ssl))
goto error;
}
#endif /* HAVE_OPENSSL */
DBUG_PRINT("info",("Server version = '%s' capabilities: %lu status: %u client_flag: %lu",
DBUG_PRINT("info",("Server version = '%s' capabilites: %lu status: %u client_flag: %lu",
mysql->server_version, mysql->server_capabilities,
mysql->server_status, mysql->client_flag));
compile_time_assert(MYSQL_USERNAME_LENGTH == USERNAME_LENGTH);
/* This needs to be changed as it's not useful with big packets */
if (mysql->user[0])
strmake(end, mysql->user, USERNAME_LENGTH);
@@ -527,18 +480,19 @@ static int client_mpvio_write_packet(struct st_plugin_vio *mpv,
connection
*/
void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info)
void mpvio_info(MARIADB_CIO *cio, MYSQL_PLUGIN_VIO_INFO *info)
{
bzero(info, sizeof(*info));
switch (vio->type) {
case VIO_TYPE_TCPIP:
switch (cio->type) {
case CIO_TYPE_SOCKET:
info->protocol= MYSQL_VIO_TCP;
info->socket= vio->sd;
ma_cio_get_handle(cio, &info->socket);
return;
case VIO_TYPE_SOCKET:
case CIO_TYPE_UNIXSOCKET:
info->protocol= MYSQL_VIO_SOCKET;
info->socket= vio->sd;
ma_cio_get_handle(cio, &info->socket);
return;
/*
case VIO_TYPE_SSL:
{
struct sockaddr addr;
@@ -550,11 +504,14 @@ void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info)
info->socket= vio->sd;
return;
}
*/
#ifdef _WIN32
/*
case VIO_TYPE_NAMEDPIPE:
info->protocol= MYSQL_VIO_PIPE;
info->handle= vio->hPipe;
return;
*/
/* not supported yet
case VIO_TYPE_SHARED_MEMORY:
info->protocol= MYSQL_VIO_MEMORY;
@@ -570,7 +527,7 @@ static void client_mpvio_info(MYSQL_PLUGIN_VIO *vio,
MYSQL_PLUGIN_VIO_INFO *info)
{
MCPVIO_EXT *mpvio= (MCPVIO_EXT*)vio;
mpvio_info(mpvio->mysql->net.vio, info);
mpvio_info(mpvio->mysql->net.cio, info);
}
/**
@@ -637,8 +594,6 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len,
res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql);
compile_time_assert(CR_OK == -1);
compile_time_assert(CR_ERROR == 0);
if (res > CR_OK && mysql->net.read_pos[0] != 254)
{
/*

View File

@@ -0,0 +1,32 @@
IF(WIN32)
SET(EXPORT_FILE "cio_plugin.def")
ENDIF()
SET(CMAKE_SHARED_LIBRARY_PREFIX "")
IF(SOCKET_PLUGIN_TYPE MATCHES "DYNAMIC")
ADD_DEFINITIONS(-DHAVE_SOCKET_DYNAMIC=1)
ADD_LIBRARY(cio_socket SHARED cio_socket.c ${EXPORT_FILE})
SET(INSTALL_LIBS cio_socket)
ENDIF()
IF(WIN32)
IF(NPIPE_PLUGIN_TYPE MATCHES "DYNAMIC")
ADD_DEFINITIONS(-DHAVE_NPIPE_DYNAMIC=1)
ADD_LIBRARY(cio_npipe SHARED cio_npipe.c ${EXPORT_FILE})
SET(INSTALL_LIBS ${INSTALL_LIBS} cio_npipe)
ENDIF()
IF(SHMEM_PLUGIN_TYPE MATCHES "DYNAMIC")
ADD_DEFINITIONS(-DHAVE_SHMEM_DYNAMIC=1)
ADD_LIBRARY(cio_shmem SHARED cio_shmem.c ${EXPORT_FILE})
SET(INSTALL_LIBS ${INSTALL_LIBS} cio_shmem)
ENDIF()
ENDIF()
IF(INSTALL_LIBS)
INSTALL(TARGETS
${INSTALL_LIBS}
RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}"
LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}"
ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}")
ENDIF()

326
plugins/cio/cio_npipe.c Normal file
View File

@@ -0,0 +1,326 @@
/************************************************************************************
Copyright (C) 2015 MariaDB Corporation AB
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not see <http://www.gnu.org/licenses>
or write to the Free Software Foundation, Inc.,
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
Part of this code includes code from the PHP project which
is freely available from http://www.php.net
*************************************************************************************/
/* MariaDB Communication IO (CIO) plugin for named pipe communication */
#ifdef _WIN32
#include <my_global.h>
#include <my_sys.h>
#include <errmsg.h>
#include <mysql.h>
#include <mysql/client_plugin.h>
#include <string.h>
#include <m_string.h>
#ifdef HAVE_NPIPE_DYNAMIC
#define my_malloc(A, B) malloc((A))
#undef my_free
#define my_free(A,B) free(((A)))
#endif
/* Function prototypes */
my_bool cio_npipe_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout);
int cio_npipe_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type);
size_t cio_npipe_read(MARIADB_CIO *cio, uchar *buffer, size_t length);
size_t cio_npipe_write(MARIADB_CIO *cio, uchar *buffer, size_t length);
int cio_npipe_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout);
my_bool cio_npipe_blocking(MARIADB_CIO *cio, my_bool value, my_bool *old_value);
my_bool cio_npipe_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo);
my_bool cio_npipe_close(MARIADB_CIO *cio);
int cio_npipe_fast_send(MARIADB_CIO *cio);
int cio_npipe_keepalive(MARIADB_CIO *cio);
my_socket cio_npipe_get_socket(MARIADB_CIO *cio);
my_bool cio_npipe_is_blocking(MARIADB_CIO *cio);
struct st_ma_cio_methods cio_npipe_methods= {
cio_npipe_set_timeout,
cio_npipe_get_timeout,
cio_npipe_read,
cio_npipe_write,
cio_npipe_wait_io_or_timeout,
cio_npipe_blocking,
cio_npipe_connect,
cio_npipe_close,
cio_npipe_fast_send,
cio_npipe_keepalive,
cio_npipe_get_socket,
cio_npipe_is_blocking
};
#ifndef HAVE_NPIPE_DYNAMIC
MARIADB_CIO_PLUGIN cio_npipe_plugin =
#else
MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_ =
#endif
{
MARIADB_CLIENT_CIO_PLUGIN,
MARIADB_CLIENT_CIO_PLUGIN_INTERFACE_VERSION,
"cio_npipe",
"Georg Richter",
"MariaDB communication IO plugin for named pipe communication",
{1, 0, 0},
"LGPL",
NULL,
NULL,
&cio_npipe_methods
};
struct st_cio_npipe {
HANDLE pipe;
OVERLAPPED overlapped;
size_t rw_size;
int fcntl_mode;
MYSQL *mysql;
};
my_bool cio_npipe_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout)
{
if (!cio)
return 1;
cio->timeout[type]= (timeout > 0) ? timeout * 1000 : -1;
return 0;
}
int cio_npipe_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type)
{
if (!cio)
return -1;
return cio->timeout[type] / 1000;
}
size_t cio_npipe_read(MARIADB_CIO *cio, uchar *buffer, size_t length)
{
DWORD dwRead= 0;
size_t r= -1;
struct st_cio_npipe *cpipe= NULL;
if (!cio || !cio->data)
return -1;
cpipe= (struct st_cio_npipe *)cio->data;
if (ReadFile(cpipe->pipe, buffer, length, &dwRead, &cpipe->overlapped))
{
r= (size_t)dwRead;
goto end;
}
if (GetLastError() == ERROR_IO_PENDING)
r= cio_npipe_wait_io_or_timeout(cio, 1, 0);
if (!r)
r= cpipe->rw_size;
end:
return r;
}
size_t cio_npipe_write(MARIADB_CIO *cio, uchar *buffer, size_t length)
{
DWORD dwWrite= 0;
size_t r= -1;
struct st_cio_npipe *cpipe= NULL;
if (!cio || !cio->data)
return -1;
cpipe= (struct st_cio_npipe *)cio->data;
if (WriteFile(cpipe->pipe, buffer, length, &dwWrite, &cpipe->overlapped))
{
r= (size_t)dwWrite;
goto end;
}
if (GetLastError() == ERROR_IO_PENDING)
r= cio_npipe_wait_io_or_timeout(cio, 1, 0);
if (!r)
r= cpipe->rw_size;
end:
return r;
}
int cio_npipe_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout)
{
int r= -1;
DWORD status;
int save_error;
struct st_cio_npipe *cpipe= NULL;
cpipe= (struct st_cio_npipe *)cio->data;
if (!timeout)
timeout= (is_read) ? cio->timeout[CIO_READ_TIMEOUT] : cio->timeout[CIO_WRITE_TIMEOUT];
status= WaitForSingleObject(cpipe->overlapped.hEvent, timeout);
if (status == WAIT_OBJECT_0)
{
if (GetOverlappedResult(cpipe->pipe, &cpipe->overlapped, &cpipe->rw_size, FALSE))
return 0;
}
/* other status codes are: WAIT_ABANDONED, WAIT_TIMEOUT and WAIT_FAILED */
save_error= GetLastError();
CancelIo(cpipe->pipe);
SetLastError(save_error);
return -1;
}
my_bool cio_npipe_blocking(MARIADB_CIO *cio, my_bool block, my_bool *previous_mode)
{
/* not supported */
return 0;
}
int cio_npipe_keepalive(MARIADB_CIO *cio)
{
/* not supported */
return 0;
}
int cio_npipe_fast_send(MARIADB_CIO *cio)
{
/* not supported */
return 0;
}
my_bool cio_npipe_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo)
{
struct st_cio_npipe *cpipe= NULL;
if (!cio || !cinfo)
return 1;
if (!(cpipe= (struct st_cio_npipe *)my_malloc(sizeof(struct st_cio_npipe), MYF(0))))
{
CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0, "");
return 1;
}
bzero(cpipe, sizeof(struct st_cio_npipe));
cio->data= (void *)cpipe;
cpipe->pipe= INVALID_HANDLE_VALUE;
cio->mysql= cinfo->mysql;
cio->type= cinfo->type;
if (cinfo->type == CIO_TYPE_NAMEDPIPE)
{
my_bool has_timedout= 0;
char szPipeName[MAX_PATH];
DWORD dwMode;
if ( ! cinfo->unix_socket || (cinfo->unix_socket)[0] == 0x00)
cinfo->unix_socket = MYSQL_NAMEDPIPE;
if (!cinfo->host || !strcmp(cinfo->host,LOCAL_HOST))
cinfo->host=LOCAL_HOST_NAMEDPIPE;
szPipeName[MAX_PATH - 1]= 0;
snprintf(szPipeName, MAX_PATH - 1, "\\\\%s\\pipe\\%s", cinfo->host, cinfo->unix_socket);
while (1)
{
if ((cpipe->pipe = CreateFile(szPipeName,
GENERIC_READ |
GENERIC_WRITE,
0, /* no sharing */
NULL, /* default security attributes */
OPEN_EXISTING,
0, /* default attributes */
NULL)) != INVALID_HANDLE_VALUE)
break;
if (GetLastError() != ERROR_PIPE_BUSY)
{
cio->set_error(cio, CR_NAMEDPIPEOPEN_ERROR, SQLSTATE_UNKNOWN, 0,
cinfo->host, cinfo->unix_socket, GetLastError());
goto end;
}
if (has_timedout || !WaitNamedPipe(szPipeName, cio->timeout[CIO_CONNECT_TIMEOUT]))
{
cio->set_error(cio, CR_NAMEDPIPEWAIT_ERROR, SQLSTATE_UNKNOWN, 0,
cinfo->host, cinfo->unix_socket, GetLastError());
goto end;
}
has_timedout= 1;
}
dwMode = PIPE_READMODE_BYTE | PIPE_WAIT;
if (!SetNamedPipeHandleState(cpipe->pipe, &dwMode, NULL, NULL))
{
cio->set_error(cio, CR_NAMEDPIPESETSTATE_ERROR, SQLSTATE_UNKNOWN, 0,
cinfo->host, cinfo->unix_socket, (ulong) GetLastError());
goto end;
}
/* Register event handler for overlapped IO */
if (!(cpipe->overlapped.hEvent= CreateEvent(NULL, FALSE, FALSE, NULL)))
{
cio->set_error(cio, CR_EVENT_CREATE_FAILED, SQLSTATE_UNKNOWN, 0,
GetLastError());
goto end;
}
return 0;
}
end:
if (cpipe)
{
if (cpipe->pipe != INVALID_HANDLE_VALUE)
CloseHandle(cpipe->pipe);
my_free((gptr)cpipe, MYF(0));
cio->data= NULL;
}
return 1;
}
my_bool cio_npipe_close(MARIADB_CIO *cio)
{
struct st_cio_npipe *cpipe= NULL;
int r= 0;
if (!cio)
return 1;
if (cio->data)
{
cpipe= (struct st_cio_npipe *)cio->data;
CloseHandle(cpipe->overlapped.hEvent);
if (cpipe->pipe != INVALID_HANDLE_VALUE)
{
CloseHandle(cpipe->pipe);
cpipe->pipe= INVALID_HANDLE_VALUE;
}
my_free((gptr)cio->data, MYF(0));
cio->data= NULL;
}
return r;
}
my_socket cio_npipe_get_socket(MARIADB_CIO *cio)
{
if (cio && cio->data)
return (my_socket)((struct st_cio_npipe *)cio->data)->pipe;
return INVALID_SOCKET;
}
my_bool cio_npipe_is_blocking(MARIADB_CIO *cio)
{
return 1;
}
#endif

View File

@@ -0,0 +1,2 @@
EXPORTS
_mysql_client_plugin_declaration_ DATA

448
plugins/cio/cio_shmem.c Normal file
View File

@@ -0,0 +1,448 @@
/************************************************************************************
Copyright (C) 2015 MariaDB Corporation AB
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not see <http://www.gnu.org/licenses>
or write to the Free Software Foundation, Inc.,
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
Part of this code includes code from the PHP project which
is freely available from http://www.php.net
*************************************************************************************/
/* MariaDB Communication IO (CIO) plugin for named pipe communication */
#ifdef _WIN32
#include <my_global.h>
#include <my_sys.h>
#include <errmsg.h>
#include <mysql.h>
#include <mysql/client_plugin.h>
#include <string.h>
#include <m_string.h>
#ifdef HAVE_SHMEM_DYNAMIC
#define my_malloc(A, B) malloc((A))
#undef my_free
#define my_free(A,B) free(((A)))
#endif
#define SHM_DEFAULT_NAME "MYSQL"
#define CIO_SHM_BUFFER_SIZE 16000 + 4
my_bool cio_shm_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout);
int cio_shm_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type);
size_t cio_shm_read(MARIADB_CIO *cio, const uchar *buffer, size_t length);
size_t cio_shm_write(MARIADB_CIO *cio, uchar *buffer, size_t length);
int cio_shm_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout);
my_bool cio_shm_blocking(MARIADB_CIO *cio, my_bool value, my_bool *old_value);
my_bool cio_shm_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo);
my_bool cio_shm_close(MARIADB_CIO *cio);
struct st_ma_cio_methods cio_shm_methods= {
cio_shm_set_timeout,
cio_shm_get_timeout,
cio_shm_read,
NULL,
cio_shm_write,
NULL,
cio_shm_wait_io_or_timeout,
cio_shm_blocking,
cio_shm_connect,
cio_shm_close,
NULL,
NULL,
NULL,
NULL,
NULL
};
#ifndef HAVE_SHMEM_DYNAMIC
MARIADB_CIO_PLUGIN cio_shmem_plugin=
#else
MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_=
#endif
{
MARIADB_CLIENT_CIO_PLUGIN,
MARIADB_CLIENT_CIO_PLUGIN_INTERFACE_VERSION,
"cio_shmem",
"Georg Richter",
"MariaDB communication IO plugin for Windows shared memory communication",
{1, 0, 0},
"LGPPL",
NULL,
NULL,
&cio_shm_methods,
};
enum enum_shm_events
{
CIO_SHM_SERVER_WROTE= 0,
CIO_SHM_SERVER_READ,
CIO_SHM_CLIENT_WROTE,
CIO_SHM_CLIENT_READ,
CIO_SHM_CONNECTION_CLOSED
};
typedef struct {
HANDLE event[5];
HANDLE file_map;
LPVOID *map;
char *read_pos;
size_t buffer_size;
} CIO_SHM;
char *StrEvent[]= {"SERVER_WROTE", "SERVER_READ", "CLIENT_WROTE", "CLIENT_READ", "CONNECTION_CLOSED"};
struct st_cio_shm {
char *shm_name;
};
my_bool cio_shm_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout)
{
if (!cio)
return 1;
cio->timeout[type]= (timeout > 0) ? timeout * 1000 : INFINITE;
return 0;
}
int cio_shm_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type)
{
if (!cio)
return -1;
return cio->timeout[type] / 1000;
}
size_t cio_shm_read(MARIADB_CIO *cio, const uchar *buffer, size_t length)
{
CIO_SHM *cio_shm= (CIO_SHM *)cio->data;
size_t copy_size= length;
HANDLE events[2];
if (!cio_shm)
return -1;
/* we need to wait for write and close events */
if (!cio_shm->buffer_size)
{
events[0]= cio_shm->event[CIO_SHM_CONNECTION_CLOSED];
events[1]= cio_shm->event[CIO_SHM_SERVER_WROTE];
switch(WaitForMultipleObjects(2, events, 0, cio->timeout[CIO_READ_TIMEOUT]))
{
case WAIT_OBJECT_0: /* server closed connection */
SetLastError(ERROR_GRACEFUL_DISCONNECT);
return -1;
case WAIT_OBJECT_0 +1: /* server_wrote event */
break;
case WAIT_TIMEOUT:
SetLastError(ETIMEDOUT);
default:
return -1;
}
/* server sent data */
cio_shm->read_pos= cio_shm->map;
cio_shm->buffer_size= uint4korr(cio_shm->read_pos);
cio_shm->read_pos+= 4;
}
if (cio_shm->buffer_size < copy_size)
copy_size= cio_shm->buffer_size;
if (copy_size)
{
memcpy(buffer, cio_shm->read_pos, cio_shm->buffer_size);
cio_shm->read_pos+= copy_size;
cio_shm->buffer_size-= copy_size;
}
/* we need to read again */
if (!cio_shm->buffer_size)
if (!SetEvent(cio_shm->event[CIO_SHM_CLIENT_READ]))
return -1;
return copy_size;
}
size_t cio_shm_write(MARIADB_CIO *cio, uchar *buffer, size_t length)
{
HANDLE events[2];
CIO_SHM *cio_shm= (CIO_SHM *)cio->data;
size_t bytes_to_write= length;
uchar *buffer_pos= buffer;
if (!cio_shm)
return -1;
events[0]= cio_shm->event[CIO_SHM_CONNECTION_CLOSED];
events[1]= cio_shm->event[CIO_SHM_SERVER_READ];
while (bytes_to_write)
{
size_t pkt_length;
switch (WaitForMultipleObjects(2, events, 0, cio->timeout[CIO_WRITE_TIMEOUT])) {
case WAIT_OBJECT_0: /* connection closed */
SetLastError(ERROR_GRACEFUL_DISCONNECT);
return -1;
case WAIT_OBJECT_0 + 1: /* server_read */
break;
case WAIT_TIMEOUT:
SetLastError(ETIMEDOUT);
default:
return -1;
}
pkt_length= MIN(CIO_SHM_BUFFER_SIZE, length);
int4store(cio_shm->map, pkt_length);
memcpy((uchar *)cio_shm->map + 4, buffer_pos, length);
buffer_pos+= length;
bytes_to_write-= length;
if (!SetEvent(cio_shm->event[CIO_SHM_CLIENT_WROTE]))
return -1;
}
return length;
}
int cio_shm_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout)
{
}
my_bool cio_shm_blocking(MARIADB_CIO *cio, my_bool block, my_bool *previous_mode)
{
/* not supported */
return 0;
}
int cio_shm_keepalive(MARIADB_CIO *cio)
{
/* not supported */
return 0;
}
int cio_shm_fast_send(MARIADB_CIO *cio)
{
/* not supported */
return 0;
}
my_bool cio_shm_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo)
{
char *base_memory_name;
char *prefixes[]= {"", "Global\\", NULL};
char *shm_name, *shm_suffix, *shm_prefix;
uchar i= 0;
int len;
DWORD cid;
char connection_id[28];
char *connection_id_str;
DWORD dwDesiredAccess= EVENT_MODIFY_STATE | SYNCHRONIZE;
HANDLE hdlConnectRequest= NULL,
hdlConnectRequestAnswer= NULL,
file_map= NULL;
LPVOID map= NULL;
CIO_SHM *cio_shm= (CIO_SHM*)LocalAlloc(LMEM_ZEROINIT, sizeof(CIO_SHM));
if (!cio_shm)
{
CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0, "");
return 0;
}
/* MariaDB server constructs the event name as follows:
"Global\\base_memory_name" or
"\\base_memory_name"
*/
base_memory_name= (cinfo->mysql->options.shared_memory_base_name) ?
cinfo->mysql->options.shared_memory_base_name : SHM_DEFAULT_NAME;
if (!(shm_name= LocalAlloc(LMEM_ZEROINIT, strlen(base_memory_name) + 40)))
{
CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0, "");
goto error;
}
/* iterate through prefixes */
while (prefixes[i])
{
len= sprintf(shm_name, "%s%s_", prefixes[i], base_memory_name);
shm_suffix= shm_name + len;
strcpy(shm_suffix, "CONNECT_REQUEST");
if ((hdlConnectRequest= OpenEvent(dwDesiredAccess, 0, shm_name)))
{
/* save prefix to prevent further loop */
shm_prefix= prefixes[i];
break;
}
i++;
}
if (!hdlConnectRequest)
{
CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Opening CONNECT_REQUEST event failed", GetLastError());
goto error;
}
strcpy(shm_suffix, "CONNECT_ANSWER");
if (!(hdlConnectRequestAnswer= OpenEvent(dwDesiredAccess, 0, shm_name)))
{
CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Opening CONNECT_ANSWER event failed", GetLastError());
goto error;
}
/* get connection id, so we can build the filename used for connection */
strcpy(shm_suffix, "CONNECT_DATA");
if (!(file_map= OpenFileMapping(FILE_MAP_WRITE, 0, shm_name)))
{
CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "OpenFileMapping failed", GetLastError());
goto error;
}
/* try to get first 4 bytes, which represents connection_id */
if (!(map= MapViewOfFile(file_map, FILE_MAP_WRITE, 0, 0, sizeof(cid))))
{
CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Reading connection_id failed", GetLastError());
goto error;
}
/* notify server */
if (!SetEvent(hdlConnectRequest))
{
CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Failed sending connection request", GetLastError());
goto error;
}
/* Wait for server answer */
switch(WaitForSingleObject(hdlConnectRequestAnswer, cio->timeout[CIO_CONNECT_TIMEOUT])) {
case WAIT_ABANDONED:
CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Mutex was not released in time", GetLastError());
goto error;
break;
case WAIT_FAILED:
CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Operation wait failed", GetLastError());
goto error;
break;
case WAIT_TIMEOUT:
CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Operation timed out", GetLastError());
goto error;
break;
case WAIT_OBJECT_0:
break;
default:
CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Wait for server failed", GetLastError());
break;
}
cid= uint4korr(map);
len= sprintf(shm_name, "%s%s_%d_", shm_prefix, base_memory_name, cid);
shm_suffix= shm_name + len;
strcpy(shm_suffix, "DATA");
cio_shm->file_map= OpenFileMapping(FILE_MAP_WRITE, 0, shm_name);
if (cio_shm->file_map == NULL)
{
CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "OpenFileMapping failed", GetLastError());
goto error;
}
if (!(cio_shm->map= MapViewOfFile(cio_shm->file_map, FILE_MAP_WRITE, 0, 0, CIO_SHM_BUFFER_SIZE)))
{
CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "MapViewOfFile failed", GetLastError());
goto error;
}
for (i=0; i < 5; i++)
{
strcpy(shm_suffix, StrEvent[i]);
if (!(cio_shm->event[i]= OpenEvent(dwDesiredAccess, 0, shm_name)))
{
CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Couldn't create event", GetLastError());
goto error;
}
}
/* we will first read from server */
SetEvent(cio_shm->event[CIO_SHM_SERVER_READ]);
error:
if (hdlConnectRequest)
CloseHandle(hdlConnectRequest);
if (hdlConnectRequestAnswer)
CloseHandle(hdlConnectRequestAnswer);
if (shm_name)
LocalFree(shm_name);
if (map)
UnmapViewOfFile(map);
if (file_map)
CloseHandle(file_map);
if (cio_shm)
{
/* check if all events are set */
if (cio_shm->event[4])
{
cio->data= (void *)cio_shm;
cio->mysql= cinfo->mysql;
cio->type= cinfo->type;
cio_shm->read_pos= cio_shm->map;
cio->mysql->net.cio= cio;
return 0;
}
for (i=0;i < 5; i++)
if (cio_shm->event[i])
CloseHandle(cio_shm->event[i]);
if (cio_shm->map)
UnmapViewOfFile(cio_shm->map);
if (cio_shm->file_map)
CloseHandle(cio_shm->file_map);
LocalFree(cio_shm);
}
return 1;
}
my_bool cio_shm_close(MARIADB_CIO *cio)
{
CIO_SHM *cio_shm= (CIO_SHM *)cio->data;
int i;
if (!cio_shm)
return 1;
/* notify server */
SetEvent(cio_shm->event[CIO_SHM_CONNECTION_CLOSED]);
UnmapViewOfFile(cio_shm->map);
CloseHandle(cio_shm->file_map);
for (i=0; i < 5; i++)
CloseHandle(cio_shm->event[i]);
LocalFree(cio_shm);
cio->data= NULL;
return 0;
}
my_socket cio_shm_get_socket(MARIADB_CIO *cio)
{
}
my_bool cio_shm_is_blocking(MARIADB_CIO *cio)
{
return 1;
}
#endif

905
plugins/cio/cio_socket.c Normal file
View File

@@ -0,0 +1,905 @@
/************************************************************************************
Copyright (C) 2015 MariaDB Corporation AB,
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not see <http://www.gnu.org/licenses>
or write to the Free Software Foundation, Inc.,
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
*************************************************************************************/
/*
MariaDB Communication IO (CIO) plugin for socket communication:
The plugin handles connections via unix and network sockets. it is enabled by
default and compiled into Connector/C.
*/
#include <my_global.h>
#include <my_sys.h>
#include <errmsg.h>
#include <mysql.h>
#include <mysql/client_plugin.h>
#include <my_context.h>
#include <mysql_async.h>
#include <ma_common.h>
#include <string.h>
#include <m_string.h>
#ifndef _WIN32
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#ifdef HAVE_POLL
#include <sys/poll.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netdb.h>
#include <netinet/tcp.h>
#else
#define O_NONBLOCK 1
#endif
#ifndef SOCKET_ERROR
#define SOCKET_ERROR -1
#endif
/* Function prototypes */
my_bool cio_socket_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout);
int cio_socket_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type);
size_t cio_socket_read(MARIADB_CIO *cio, const uchar *buffer, size_t length);
size_t cio_socket_async_read(MARIADB_CIO *cio, const uchar *buffer, size_t length);
size_t cio_socket_write(MARIADB_CIO *cio, const uchar *buffer, size_t length);
size_t cio_socket_async_write(MARIADB_CIO *cio, const uchar *buffer, size_t length);
int cio_socket_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout);
my_bool cio_socket_blocking(MARIADB_CIO *cio, my_bool value, my_bool *old_value);
my_bool cio_socket_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo);
my_bool cio_socket_close(MARIADB_CIO *cio);
int cio_socket_fast_send(MARIADB_CIO *cio);
int cio_socket_keepalive(MARIADB_CIO *cio);
my_bool cio_socket_get_handle(MARIADB_CIO *cio, void *handle);
my_bool cio_socket_is_blocking(MARIADB_CIO *cio);
my_bool cio_socket_is_alive(MARIADB_CIO *cio);
static int cio_socket_init(char *unused1,
size_t unused2,
int unused3,
va_list);
static int cio_socket_end(void);
struct st_ma_cio_methods cio_socket_methods= {
cio_socket_set_timeout,
cio_socket_get_timeout,
cio_socket_read,
cio_socket_async_read,
cio_socket_write,
cio_socket_async_write,
cio_socket_wait_io_or_timeout,
cio_socket_blocking,
cio_socket_connect,
cio_socket_close,
cio_socket_fast_send,
cio_socket_keepalive,
cio_socket_get_handle,
cio_socket_is_blocking,
cio_socket_is_alive
};
#ifndef HAVE_SOCKET_DYNAMIC
MARIADB_CIO_PLUGIN cio_socket_plugin=
#else
MARIADB_CIO_PLUGIN _mysql_client_plugin_declare_
#endif
{
MARIADB_CLIENT_CIO_PLUGIN,
MARIADB_CLIENT_CIO_PLUGIN_INTERFACE_VERSION,
"cio_socket",
"Georg Richter",
"MariaDB communication IO plugin for socket communication",
{1, 0, 0},
"LGPL",
&cio_socket_init,
&cio_socket_end,
&cio_socket_methods
};
struct st_cio_socket {
my_socket socket;
int fcntl_mode;
MYSQL *mysql;
};
static my_bool cio_socket_initialized= FALSE;
static int cio_socket_init(char *errmsg,
size_t errmsg_length,
int unused,
va_list va)
{
cio_socket_initialized= TRUE;
return 0;
}
static int cio_socket_end(void)
{
if (!cio_socket_initialized)
return 1;
return 0;
}
/* {{{ cio_socket_set_timeout */
/*
set timeout value
SYNOPSIS
cio_socket_set_timeout
cio CIO
type timeout type (connect, read, write)
timeout timeout in seconds
DESCRIPTION
Sets timeout values for connection-, read or write time out.
CIO internally stores all timeout values in milliseconds, but
accepts and returns all time values in seconds (like api does).
RETURNS
0 Success
1 Error
*/
my_bool cio_socket_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout)
{
if (!cio)
return 1;
cio->timeout[type]= (timeout > 0) ? timeout * 1000 : -1;
return 0;
}
/* }}} */
/* {{{ cio_socket_get_timeout */
/*
get timeout value
SYNOPSIS
cio_socket_get_timeout
cio CIO
type timeout type (connect, read, write)
DESCRIPTION
Returns timeout values for connection-, read or write time out.
CIO internally stores all timeout values in milliseconds, but
accepts and returns all time values in seconds (like api does).
RETURNS
0...n time out value
-1 error
*/
int cio_socket_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type)
{
if (!cio)
return -1;
return cio->timeout[type] / 1000;
}
/* }}} */
/* {{{ cio_socket_read */
/*
read from socket
SYNOPSIS
cio_socket_read()
cio CIO
buffer read buffer
length buffer length
DESCRIPTION
reads up to length bytes into specified buffer. In the event of an
error erno is set to indicate it.
RETURNS
1..n number of bytes read
0 peer has performed shutdown
-1 on error
*/
size_t cio_socket_read(MARIADB_CIO *cio, const uchar *buffer, size_t length)
{
ssize_t r= -1;
#ifndef _WIN32
/* don't ignore SIGPIPE globally like in libmysql!! */
int read_flags= MSG_NOSIGNAL;
#endif
struct st_cio_socket *csock= NULL;
if (!cio || !cio->data)
return -1;
csock= (struct st_cio_socket *)cio->data;
#ifndef _WIN32
do {
r= recv(csock->socket, (void *)buffer, length, read_flags);
} while (r == -1 && errno == EINTR);
while (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)
&& cio->timeout[CIO_READ_TIMEOUT] > 0)
{
if (cio_socket_wait_io_or_timeout(cio, TRUE, cio->timeout[CIO_READ_TIMEOUT]) < 1)
return -1;
do {
r= recv(csock->socket, (void *)buffer, length, read_flags);
} while (r == -1 && errno == EINTR);
}
#else
{
WSABUF wsaData;
DWORD flags= 0,
dwBytes= 0;
/* clear error */
errno= 0;
wsaData.len = (u_long)length;
wsaData.buf = (char*) buffer;
r = WSARecv(csock->socket, &wsaData, 1, &dwBytes, &flags, NULL, NULL);
if (r == SOCKET_ERROR)
{
errno= WSAGetLastError();
return -1;
}
r= dwBytes;
}
#endif
return r;
}
/* }}} */
/* {{{ cio_socket_async_read */
/*
read from socket
SYNOPSIS
cio_socket_async_read()
cio CIO
buffer read buffer
length buffer length
DESCRIPTION
reads up to length bytes into specified buffer. In the event of an
error erno is set to indicate it.
RETURNS
1..n number of bytes read
0 peer has performed shutdown
-1 on error
*/
size_t cio_socket_async_read(MARIADB_CIO *cio, const uchar *buffer, size_t length)
{
ssize_t r= -1;
#ifndef _WIN32
int read_flags= MSG_NOSIGNAL | MSG_DONTWAIT;
#endif
struct st_cio_socket *csock= NULL;
if (!cio || !cio->data)
return -1;
csock= (struct st_cio_socket *)cio->data;
#ifndef _WIN32
r= recv(csock->socket,(void *)buffer, length, read_flags);
#else
r= recv(csock->socket, buffer, length, 0);
#endif
return r;
}
/* }}} */
/* {{{ cio_socket_async_write */
/*
write to socket
SYNOPSIS
cio_socket_async_write()
cio CIO
buffer read buffer
length buffer length
DESCRIPTION
writes up to length bytes to socket. In the event of an
error erno is set to indicate it.
RETURNS
1..n number of bytes read
0 peer has performed shutdown
-1 on error
*/
size_t cio_socket_async_write(MARIADB_CIO *cio, const uchar *buffer, size_t length)
{
ssize_t r= -1;
#ifndef WIN32
int write_flags= MSG_NOSIGNAL | MSG_DONTWAIT;
#endif
struct st_cio_socket *csock= NULL;
if (!cio || !cio->data)
return -1;
csock= (struct st_cio_socket *)cio->data;
#ifndef WIN32
r= send(csock->socket, buffer, length, write_flags);
#else
r= send(csock->socket, buffer, length, 0);
#endif
return r;
}
/* }}} */
/* {{{ cio_socket_write */
/*
write to socket
SYNOPSIS
cio_socket_write()
cio CIO
buffer read buffer
length buffer length
DESCRIPTION
writes up to length bytes to socket. In the event of an
error erno is set to indicate it.
RETURNS
1..n number of bytes read
0 peer has performed shutdown
-1 on error
*/
size_t cio_socket_write(MARIADB_CIO *cio, const uchar *buffer, size_t length)
{
ssize_t r= -1;
#ifndef _WIN32
int send_flags= MSG_NOSIGNAL;
#endif
struct st_cio_socket *csock= NULL;
if (!cio || !cio->data)
return -1;
csock= (struct st_cio_socket *)cio->data;
#ifndef _WIN32
do {
r= send(csock->socket, buffer, length, send_flags);
} while (r == -1 && errno == EINTR);
while (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK) &&
cio->timeout[CIO_WRITE_TIMEOUT] != 0)
{
if (cio_socket_wait_io_or_timeout(cio, FALSE, cio->timeout[CIO_WRITE_TIMEOUT]) < 1)
return -1;
do {
r= send(csock->socket, buffer, length, send_flags);
} while (r == -1 && errno == EINTR);
}
#else
{
WSABUF wsaData;
DWORD dwBytes= 0;
wsaData.len = (u_long)length;
wsaData.buf = (char*) buffer;
r = WSASend(csock->socket, &wsaData, 1, &dwBytes, 0, NULL, NULL);
if (r == SOCKET_ERROR) {
errno= WSAGetLastError();
return -1;
}
r= dwBytes;
}
#endif
return r;
}
/* }}} */
int cio_socket_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout)
{
int rc;
struct st_cio_socket *csock= NULL;
#ifndef _WIN32
struct pollfd p_fd;
#else
struct timeval tv= {0,0};
fd_set fds, exc_fds;
#endif
if (!cio || !cio->data)
return 0;
csock= (struct st_cio_socket *)cio->data;
{
#ifndef _WIN32
memset(&p_fd, 0, sizeof(p_fd));
p_fd.fd= csock->socket;
p_fd.events= (is_read) ? POLLIN : POLLOUT;
do {
rc= poll(&p_fd, 1, timeout);
} while (rc == -1 || errno == EINTR);
if (rc == 0)
errno= ETIMEDOUT;
#else
FD_ZERO(&fds);
FD_ZERO(&exc_fds);
FD_SET(csock->socket, &fds);
FD_SET(csock->socket, &exc_fds);
if (timeout >= 0)
{
tv.tv_sec= timeout / 1000;
tv.tv_usec= (timeout % 1000) * 1000;
}
rc= select(0, (is_read) ? &fds : NULL,
(is_read) ? NULL : &fds,
&exc_fds,
(timeout >= 0) ? &tv : NULL);
if (rc == SOCKET_ERROR)
errno= WSAGetLastError();
if (rc == 0)
errno= ETIMEDOUT;
#endif
}
return rc;
}
my_bool cio_socket_blocking(MARIADB_CIO *cio, my_bool block, my_bool *previous_mode)
{
int *sd_flags, save_flags;
my_bool tmp;
struct st_cio_socket *csock= NULL;
if (!cio || !cio->data)
return 1;
csock= (struct st_cio_socket *)cio->data;
sd_flags= &csock->fcntl_mode;
save_flags= csock->fcntl_mode;
if (!previous_mode)
previous_mode= &tmp;
#ifdef _WIN32
*previous_mode= (*sd_flags & O_NONBLOCK) != 0;
*sd_flags = (block) ? *sd_flags & ~O_NONBLOCK : *sd_flags | O_NONBLOCK;
{
ulong arg= 1 - block;
if (ioctlsocket(csock->socket, FIONBIO, (void *)&arg))
{
csock->fcntl_mode= save_flags;
return(WSAGetLastError());
}
}
#else
#if defined(O_NONBLOCK)
*previous_mode= (*sd_flags & O_NONBLOCK) != 0;
*sd_flags = (block) ? *sd_flags & ~O_NONBLOCK : *sd_flags | O_NONBLOCK;
#elif defined(O_NDELAY)
*previous_mode= (*sd_flags & O_NODELAY) != 0;
*sd_flags = (block) ? *sd_flags & ~O_NODELAY : *sd_flags | O_NODELAY;
#elif defined(FNDELAY)
*previous_mode= (*sd_flags & O_FNDELAY) != 0;
*sd_flags = (block) ? *sd_flags & ~O_FNDELAY : *sd_flags | O_FNDELAY;
#else
#error socket blocking is not supported on this platform
#endif
if (fcntl(csock->socket, F_SETFL, *sd_flags) == -1)
{
csock->fcntl_mode= save_flags;
return errno;
}
#endif
return 0;
}
static int cio_socket_internal_connect(MARIADB_CIO *cio,
const struct sockaddr *name,
size_t namelen)
{
int rc= 0;
struct st_cio_socket *csock= NULL;
int timeout;
if (!cio || !cio->data)
return 1;
csock= (struct st_cio_socket *)cio->data;
timeout= cio->timeout[CIO_CONNECT_TIMEOUT];
/* set non blocking */
cio_socket_blocking(cio, 0, 0);
#ifndef _WIN32
do {
rc= connect(csock->socket, (struct sockaddr*) name, (int)namelen);
} while (rc == -1 && errno == EINTR);
/* in case a timeout values was set we need to check error values
EINPROGRESS and EAGAIN */
if (timeout != 0 && rc == -1 &&
(errno == EINPROGRESS || errno == EAGAIN))
{
rc= cio_socket_wait_io_or_timeout(cio, FALSE, timeout);
if (rc < 1)
return -1;
{
int error;
socklen_t error_len= sizeof(error);
if ((rc = getsockopt(csock->socket, SOL_SOCKET, SO_ERROR,
(char *)&error, &error_len)) < 0)
return errno;
else if (error)
return error;
}
}
#else
rc= connect(csock->socket, (struct sockaddr*) name, (int)namelen);
if (rc == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
if (cio_socket_wait_io_or_timeout(cio, FALSE, timeout) < 0)
return -1;
rc= 0;
}
}
#endif
return rc;
}
int cio_socket_keepalive(MARIADB_CIO *cio)
{
int opt= 1;
struct st_cio_socket *csock= NULL;
if (!cio || !cio->data)
return 1;
csock= (struct st_cio_socket *)cio->data;
return setsockopt(csock->socket, SOL_SOCKET, SO_KEEPALIVE,
#ifndef _WIN32
(const void *)&opt, sizeof(opt));
#else
(char *)&opt, (int)sizeof(opt));
#endif
}
int cio_socket_fast_send(MARIADB_CIO *cio)
{
int r= 0;
struct st_cio_socket *csock= NULL;
if (!cio || !cio->data)
return 1;
csock= (struct st_cio_socket *)cio->data;
/* 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
{
int tos = IPTOS_THROUGHPUT;
r= setsockopt(csock->socket, IPPROTO_IP, IP_TOS,
(const void *)&tos, sizeof(tos));
}
#endif /* IPTOS_THROUGHPUT */
#endif
if (!r)
{
int opt = 1;
/* turn off nagle algorithm */
r= setsockopt(csock->socket, IPPROTO_TCP, TCP_NODELAY,
#ifdef _WIN32
(const char *)&opt, (int)sizeof(opt));
#else
(const void *)&opt, sizeof(opt));
#endif
}
return r;
}
static int
cio_socket_connect_sync_or_async(MARIADB_CIO *cio,
const struct sockaddr *name, uint namelen)
{
MYSQL *mysql= cio->mysql;
if (mysql->options.extension && mysql->options.extension->async_context &&
mysql->options.extension->async_context->active)
{
cio_socket_blocking(cio,0, 0);
return my_connect_async(cio, name, namelen, cio->timeout[CIO_CONNECT_TIMEOUT]);
}
return cio_socket_internal_connect(cio, name, namelen);
}
my_bool cio_socket_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo)
{
struct st_cio_socket *csock= NULL;
if (!cio || !cinfo)
return 1;
if (!(csock= (struct st_cio_socket *)my_malloc(sizeof(struct st_cio_socket),
MYF(MY_WME | MY_ZEROFILL))))
{
CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0, "");
return 1;
}
cio->data= (void *)csock;
csock->socket= -1;
cio->mysql= cinfo->mysql;
cio->type= cinfo->type;
if (cinfo->type == CIO_TYPE_UNIXSOCKET)
{
#ifndef _WIN32
#ifdef HAVE_SYS_UN_H
struct sockaddr_un UNIXaddr;
if ((csock->socket = socket(AF_UNIX,SOCK_STREAM,0)) == SOCKET_ERROR)
{
CIO_SET_ERROR(cinfo->mysql, CR_SOCKET_CREATE_ERROR, unknown_sqlstate, 0, errno);
goto error;
}
bzero((char*) &UNIXaddr,sizeof(UNIXaddr));
UNIXaddr.sun_family = AF_UNIX;
strmov(UNIXaddr.sun_path, cinfo->unix_socket);
if (cio_socket_connect_sync_or_async(cio, (struct sockaddr *) &UNIXaddr,
sizeof(UNIXaddr)))
{
CIO_SET_ERROR(cinfo->mysql, CR_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_CONNECTION_ERROR), cinfo->unix_socket, socket_errno);
goto error;
}
if (cio_socket_blocking(cio, 1, 0) == SOCKET_ERROR)
{
goto error;
}
#else
/* todo: error, not supported */
#endif
#endif
} else if (cinfo->type == CIO_TYPE_SOCKET)
{
struct addrinfo hints, *save_res= 0, *bind_res= 0, *res= 0, *bres= 0;
char server_port[NI_MAXSERV];
int gai_rc;
int rc= 0;
bzero(&server_port, NI_MAXSERV);
my_snprintf(server_port, NI_MAXSERV, "%d", cinfo->port);
/* set hints for getaddrinfo */
bzero(&hints, sizeof(hints));
hints.ai_protocol= IPPROTO_TCP; /* TCP connections only */
hints.ai_family= AF_UNSPEC; /* includes: IPv4, IPv6 or hostname */
hints.ai_socktype= SOCK_STREAM;
/* if client has multiple interfaces, we will bind socket to given
* bind_address */
if (cinfo->mysql->options.bind_address)
{
gai_rc= getaddrinfo(cinfo->mysql->options.bind_address, 0,
&hints, &res);
if (gai_rc != 0)
{
CIO_SET_ERROR(cinfo->mysql, CR_BIND_ADDR_FAILED, SQLSTATE_UNKNOWN,
CER(CR_BIND_ADDR_FAILED), cinfo->mysql->options.bind_address, gai_rc);
goto error;
}
}
/* Get the address information for the server using getaddrinfo() */
gai_rc= getaddrinfo(cinfo->host, server_port, &hints, &res);
if (gai_rc != 0)
{
CIO_SET_ERROR(cinfo->mysql, CR_UNKNOWN_HOST, SQLSTATE_UNKNOWN,
ER(CR_UNKNOWN_HOST), cinfo->host, gai_rc);
if (bres)
freeaddrinfo(bres);
goto error;
}
/* res is a linked list of addresses for the given hostname. We loop until
we are able to connect to one address or all connect attempts failed */
for (save_res= res; save_res; save_res= save_res->ai_next)
{
csock->socket= socket(save_res->ai_family, save_res->ai_socktype,
save_res->ai_protocol);
if (csock->socket == SOCKET_ERROR)
/* Errors will be handled after loop finished */
continue;
if (bind_res)
{
for (bind_res= bres; bind_res; bind_res= bind_res->ai_next)
{
if (!(rc= bind(csock->socket, bind_res->ai_addr, bind_res->ai_addrlen)))
break;
}
if (rc)
{
closesocket(csock->socket);
continue;
}
}
rc= cio_socket_connect_sync_or_async(cio, save_res->ai_addr, save_res->ai_addrlen);
if (!rc)
{
/* if (mysql->options.extension && mysql->options.extension->async_context &&
mysql->options.extension->async_context->active)
break; */
if (cio_socket_blocking(cio, 1, 0) == SOCKET_ERROR)
{
closesocket(csock->socket);
continue;
}
break; /* success! */
}
}
freeaddrinfo(res);
freeaddrinfo(bres);
if (csock->socket == SOCKET_ERROR)
{
CIO_SET_ERROR(cinfo->mysql, CR_IPSOCK_ERROR, SQLSTATE_UNKNOWN, ER(CR_IPSOCK_ERROR),
socket_errno);
goto error;
}
/* last call to connect 2 failed */
if (rc)
{
CIO_SET_ERROR(cinfo->mysql, CR_CONN_HOST_ERROR, SQLSTATE_UNKNOWN, ER(CR_CONN_HOST_ERROR),
cinfo->host, socket_errno);
goto error;
}
}
#ifdef _WIN32
/* apply timeouts */
if (cio->timeout[CIO_WRITE_TIMEOUT] > 0)
setsockopt(csock->socket, SOL_SOCKET, SO_SNDTIMEO, (const char *)&cio->timeout[CIO_WRITE_TIMEOUT], sizeof(int));
if (cio->timeout[CIO_READ_TIMEOUT] > 0)
setsockopt(csock->socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&cio->timeout[CIO_READ_TIMEOUT], sizeof(int));
#endif
return 0;
error:
if (cio->data)
{
my_free((gptr)cio->data);
cio->data= NULL;
}
return 1;
}
/* {{{ my_bool cio_socket_close() */
my_bool cio_socket_close(MARIADB_CIO *cio)
{
struct st_cio_socket *csock= NULL;
int r= 0;
if (!cio)
return 1;
if (cio->data)
{
csock= (struct st_cio_socket *)cio->data;
if (csock && csock->socket != -1)
{
r= shutdown(csock->socket ,2);
r= closesocket(csock->socket);
csock->socket= -1;
}
my_free((gptr)cio->data);
cio->data= NULL;
}
return r;
}
/* }}} */
/* {{{ my_socket cio_socket_get_handle */
my_bool cio_socket_get_handle(MARIADB_CIO *cio, void *handle)
{
if (cio && cio->data && handle)
{
*(my_socket *)handle= ((struct st_cio_socket *)cio->data)->socket;
return 0;
}
return 1;
}
/* }}} */
/* {{{ my_bool cio_socket_is_blocking(MARIADB_CIO *cio) */
my_bool cio_socket_is_blocking(MARIADB_CIO *cio)
{
struct st_cio_socket *csock= NULL;
my_bool r;
if (!cio || !cio->data)
return 0;
csock= (struct st_cio_socket *)cio->data;
r = !(csock->fcntl_mode & O_NONBLOCK);
return r;
}
/* }}} */
/* {{{ my_bool cio_socket_is_alive(MARIADB_CIO *cio) */
my_bool cio_socket_is_alive(MARIADB_CIO *cio)
{
struct st_cio_socket *csock= NULL;
#ifndef _WIN32
struct pollfd poll_fd;
#else
FD_SET sfds;
struct timeval tv= {0,0};
#endif
int res;
if (!cio || !cio->data)
return 0;
csock= (struct st_cio_socket *)cio->data;
#ifndef _WIN32
memset(&poll_fd, 0, sizeof(struct pollfd));
poll_fd.events= POLLPRI | POLLIN;
poll_fd.fd= csock->socket;
res= poll(&poll_fd, 1, 0);
if (res <= 0) /* timeout or error */
return FALSE;
if (!(poll_fd.revents & (POLLIN | POLLPRI)))
return FALSE;
return TRUE;
#else
/* We can't use the WSAPoll function, it's broken :-(
(see Windows 8 Bugs 309411 - WSAPoll does not report failed connections)
Instead we need to use select function:
If TIMEVAL is initialized to {0, 0}, select will return immediately;
this is used to poll the state of the selected sockets.
*/
FD_ZERO(&sfds);
FD_SET(csock->socket, &sfds);
res= select(csock->socket + 1, &sfds, NULL, NULL, &tv);
if (res > 0 && FD_ISSET(csock->socket, &sfds))
return TRUE;
return FALSE;
#endif
}
/* }}} */

View File

@@ -0,0 +1,19 @@
IF(WIN32)
SET(EXPORT_FILE "../plugin.def")
ENDIF()
SET(CMAKE_SHARED_LIBRARY_PREFIX "")
IF(REPLICATION_PLUGIN_TYPE MATCHES "DYNAMIC")
ADD_DEFINITIONS(-DHAVE_REPLICATION_DYNAMIC=1)
ADD_LIBRARY(replication SHARED replication.c ${EXPORT_FILE})
SET(INSTALL_LIBS replication)
ENDIF()
IF(INSTALL_LIBS)
INSTALL(TARGETS
${INSTALL_LIBS}
RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}"
LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}"
ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}")
ENDIF()

View File

@@ -0,0 +1,332 @@
/************************************************************************************
Copyright (C) 2015 MariaDB Corporation AB
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not see <http://www.gnu.org/licenses>
or write to the Free Software Foundation, Inc.,
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
Part of this code includes code from the PHP project which
is freely available from http://www.php.net
*************************************************************************************/
/* MariaDB Connection plugin for load balancing */
#include <my_global.h>
#include <my_sys.h>
#include <errmsg.h>
#include <mysql.h>
#include <mysql/client_plugin.h>
#include <string.h>
#include <m_string.h>
#ifndef WIN32
#include <sys/time.h>
#endif
/* function prototypes */
MYSQL *repl_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd,
const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag);
void repl_close(MYSQL *mysql);
int repl_command(MYSQL *mysql,enum enum_server_command command, const char *arg,
size_t length, my_bool skipp_check, void *opt_arg);
int repl_set_options(MYSQL *msql, enum mysql_option option, void *arg);
#ifdef HAVE_REPLICATION_DYNAMIC
#undef my_free
#define my_malloc(a,b) malloc(a)
#define my_free(a) free(a)
#endif
#define MARIADB_MASTER 0
#define MARIADB_SLAVE 1
#ifndef HAVE_REPLICATION_DYNAMIC
MARIADB_CONNECTION_PLUGIN connection_replication_plugin =
#else
MARIADB_CONNECTION_PLUGIN _mysql_client_plugin_declaration_ =
#endif
{
MARIADB_CLIENT_CONNECTION_PLUGIN,
MARIADB_CLIENT_CONNECTION_PLUGIN_INTERFACE_VERSION,
"replication",
"Georg Richter",
"MariaDB connection plugin for load balancing",
{1, 0, 0},
"LGPL",
NULL,
NULL,
repl_connect,
repl_close,
repl_set_options,
repl_command
};
typedef struct st_conn_repl {
MARIADB_CIO *cio[2];
MYSQL *slave_mysql;
my_bool read_only;
my_bool round_robin;
char *url;
char *host[2];
int port[2];
} REPL_DATA;
#define SET_SLAVE(mysql, data) mysql->net.cio= data->cio[MARIADB_SLAVE]
#define SET_MASTER(mysql, data) mysql->net.cio= data->cio[MARIADB_MASTER]
/* parse url
* Url has the following format:
* master[:port],slave1[:port],slave2[:port],..,slaven[:port]
*
*/
my_bool repl_parse_url(const char *url, REPL_DATA *data)
{
char *p;
char *slaves[64];
int port[64], i,num_slaves= 0;
if (!url || url[0] == 0)
return 1;
bzero(slaves, 64 * sizeof(char *));
bzero(&port, 64 * sizeof(int));
memset(data->host, 0, 2 * sizeof(char *));
memset(data->port, 0, 2 * sizeof(int));
if (data->url)
my_free(data->url);
data->url= my_strdup(url, MYF(0));
data->host[MARIADB_MASTER]= p= data->url;
/* get slaves */
while((p && (p= strchr(p, ','))))
{
*p= '\0';
p++;
if (*p)
{
slaves[num_slaves]= p;
num_slaves++;
}
}
if (!num_slaves)
return 0;
if (num_slaves == 1)
data->host[MARIADB_SLAVE]= slaves[0];
else
{
int random_nr;
#ifndef WIN32
struct timeval tp;
gettimeofday(&tp,NULL);
srand(tp.tv_usec / 1000 + tp.tv_sec * 1000);
#else
srand(GetTickCount());
#endif
random_nr= rand() % num_slaves;
data->host[MARIADB_SLAVE]= slaves[random_nr];
}
/* check ports */
for (i=0; i < 2 && data->host[i]; i++)
{
/* We need to be aware of IPv6 addresses: According to RFC3986 sect. 3.2.2
hostnames have to be enclosed in square brackets if a port is given */
if (data->host[i][0]= '[' && strchr(data->host[i], ':') && (p= strchr(data->host[i],']')))
{
/* ignore first square bracket */
data->host[i]++;
*p= 0;
p++;
}
else
p= data->host[i];
if (p && (p= strchr(p, ':')))
{
*p= '\0';
p++;
data->port[i]= atoi(p);
}
}
return 0;
}
MYSQL *repl_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd,
const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag)
{
REPL_DATA *data= NULL;
MA_CONNECTION_HANDLER *hdlr= mysql->net.conn_hdlr;
if (!(data= calloc(1, sizeof(REPL_DATA))))
{
mysql->methods->set_error(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
return NULL;
}
memset(data->cio, 0, 2 * sizeof(MARIADB_CIO *));
if (repl_parse_url(host, data))
goto error;
/* try to connect to master */
if (!(mysql->methods->db_connect(mysql, data->host[MARIADB_MASTER], user, passwd, db,
data->port[MARIADB_MASTER] ? data->port[MARIADB_MASTER] : port, unix_socket, clientflag)))
goto error;
data->cio[MARIADB_MASTER]= mysql->net.cio;
hdlr->data= data;
/* to allow immediate access without connection delay, we will start
* connecting to slave(s) in background */
/* if slave connection will fail, we will not return error but use master instead */
if (!(data->slave_mysql= mysql_init(NULL)) ||
!(mysql->methods->db_connect(data->slave_mysql, data->host[MARIADB_SLAVE], user, passwd, db,
data->port[MARIADB_SLAVE] ? data->port[MARIADB_SLAVE] : port, unix_socket, clientflag)))
{
if (data->slave_mysql)
mysql_close(data->slave_mysql);
data->cio[MARIADB_SLAVE]= NULL;
}
else
{
data->cio[MARIADB_SLAVE]= data->slave_mysql->net.cio;
data->slave_mysql->net.cio->mysql= mysql;
}
return mysql;
error:
if (data)
{
if (data->url)
my_free(data->url);
my_free(data);
}
return NULL;
}
void repl_close(MYSQL *mysql)
{
MA_CONNECTION_HANDLER *hdlr= mysql->net.conn_hdlr;
REPL_DATA *data= (REPL_DATA *)hdlr->data;
/* restore master */
SET_MASTER(mysql, data);
/* free slave information and close connection */
if (data->cio[MARIADB_SLAVE])
{
/* restore mysql */
data->cio[MARIADB_SLAVE]->mysql= data->slave_mysql;
mysql_close(data->slave_mysql);
data->cio[MARIADB_SLAVE]= NULL;
data->slave_mysql= NULL;
}
/* free masrwe information and close connection */
my_free(data->url);
my_free(data);
mysql->net.conn_hdlr->data= NULL;
return mysql_close(mysql);
}
static my_bool is_slave_command(const char *buffer, size_t buffer_len)
{
const char *buffer_end= buffer + buffer_len;
for (; buffer < buffer_end; ++buffer)
{
char c;
if (isalpha(c=*buffer))
{
if (tolower(c) == 's')
return 1;
return 0;
}
}
return 0;
}
static my_bool is_slave_stmt(MYSQL *mysql, const char *buffer)
{
unsigned long stmt_id= uint4korr(buffer);
LIST *stmt_list= mysql->stmts;
for (; stmt_list; stmt_list= stmt_list->next)
{
MYSQL_STMT *stmt= (MYSQL_STMT *)stmt_list->data;
if (stmt->stmt_id == stmt_id)
return 1;
}
return 0;
}
int repl_command(MYSQL *mysql,enum enum_server_command command, const char *arg,
size_t length, my_bool skipp_check, void *opt_arg)
{
REPL_DATA *data= (REPL_DATA *)mysql->net.conn_hdlr->data;
/* if we don't have slave or slave became unavailable root traffic to master */
if (!data->cio[MARIADB_SLAVE] || !data->read_only)
{
SET_MASTER(mysql, data);
return 0;
}
switch(command) {
case MYSQL_COM_QUERY:
case MYSQL_COM_STMT_PREPARE:
if (is_slave_command(arg, length))
SET_SLAVE(mysql, data);
else
SET_MASTER(mysql,data);
break;
case MYSQL_COM_STMT_EXECUTE:
case MYSQL_COM_STMT_FETCH:
if (data->cio[MARIADB_SLAVE]->mysql->stmts && is_slave_stmt(data->cio[MARIADB_SLAVE]->mysql, arg))
SET_SLAVE(mysql, data);
else
SET_MASTER(mysql,data);
break;
default:
SET_MASTER(mysql,data);
break;
}
return 0;
}
int repl_set_options(MYSQL *mysql, enum mysql_option option, void *arg)
{
REPL_DATA *data= (REPL_DATA *)mysql->net.conn_hdlr->data;
switch(option) {
case MARIADB_OPT_CONNECTION_READ_ONLY:
data->read_only= *(my_bool *)arg;
return 0;
/*
case MARIADB_OPT_CONNECTION_ROUND_ROBIN:
data->round_robin= *(my_bool *)arg;
return 0; */
default:
return -1;
}
}

View File

@@ -1,9 +1,7 @@
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include)
IF(WITH_REMOTEIO)
FIND_PACKAGE(CURL)
IF(REMOTEIO_PLUGIN_TYPE MATCHES "DYNAMIC")
IF(CURL_FOUND)
ADD_DEFINITIONS(-DHAVE_CURL=1)
# remote file plugin
INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR})
SET(REMOTE_IO_SOURCES remote_io.c)

View File

@@ -89,11 +89,20 @@ typedef struct
CURLM *multi_handle= NULL;
mysql_declare_client_plugin(REMOTEIO)
#ifndef HAVE_REMOTE_IO_DYNAMIC
struct st_mysql_client_plugin remote_io_plugin=
{
#else
struct st_mysql_client_plugin _mysql_client_plugin_declare_ =
#endif
{
MARIADB_CLIENT_REMOTEIO_PLUGIN,
MARIADB_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION,
"remote_io",
"Georg Richter",
"Remote IO plugin",
{0,1,0},
"LGPL",
ma_rio_init,
ma_rio_deinit,
&ma_rio_methods

Binary file not shown.

View File

@@ -0,0 +1,18 @@
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include)
# Trace example plugin
IF(TRACE_EXAMPLE_PLUGIN_TYPE MATCHES "DYNAMIC")
ADD_DEFINITIONS(-DHAVE_TRACE_EXAMPLE_PLUGIN_DYNAMIC=1)
SET(TRACE_EXAMPLE_SOURCES trace_example.c)
IF(WIN32)
SET(TRACE_EXAMPLE_SOURCES ${TRACE_EXAMPLE_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def)
ENDIF()
ADD_LIBRARY(trace_example SHARED ${TRACE_EXAMPLE_SOURCES})
SET_TARGET_PROPERTIES(trace_example PROPERTIES PREFIX "")
INSTALL(TARGETS
trace_example
RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}"
LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}"
ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}")
ENDIF()

View File

@@ -0,0 +1,446 @@
/************************************************************************************
Copyright (C) 2015 MariaDB Corporation AB
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not see <http://www.gnu.org/licenses>
or write to the Free Software Foundation, Inc.,
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
*************************************************************************************/
#ifndef _WIN32
#define _GNU_SOURCE 1
#endif
#include <my_global.h>
#include <mysql.h>
#include <mysql/client_plugin.h>
#include <string.h>
#include <memory.h>
#ifndef WIN32
#include <dlfcn.h>
#endif
#define READ 0
#define WRITE 1
/* function prototypes */
static int trace_init(char *errormsg,
size_t errormsg_size,
int unused __attribute__((unused)),
va_list unused1 __attribute__((unused)));
static int trace_deinit(void);
int (*register_callback)(my_bool register_callback,
void (*callback_function)(int mode, MYSQL *mysql, const uchar *buffer, size_t length));
void trace_callback(int mode, MYSQL *mysql, const uchar *buffer, size_t length);
#ifndef HAVE_TRACE_EXAMPLE_PLUGIN_DYNAMIC
struct st_mysql_client_plugin trace_example_plugin=
#else
struct st_mysql_client_plugin _mysql_client_plugin_declaration_ =
#endif
{
MARIADB_CLIENT_TRACE_PLUGIN,
MARIADB_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION,
"trace_example",
"Georg Richter",
"Trace example plugin",
{1,0,0},
"LGPL",
&trace_init,
&trace_deinit
};
static char *commands[]= {
"MYSQL_COM_SLEEP",
"MYSQL_COM_QUIT",
"MYSQL_COM_INIT_DB",
"MYSQL_COM_QUERY",
"MYSQL_COM_FIELD_LIST",
"MYSQL_COM_CREATE_DB",
"MYSQL_COM_DROP_DB",
"MYSQL_COM_REFRESH",
"MYSQL_COM_SHUTDOWN",
"MYSQL_COM_STATISTICS",
"MYSQL_COM_PROCESS_INFO",
"MYSQL_COM_CONNECT",
"MYSQL_COM_PROCESS_KILL",
"MYSQL_COM_DEBUG",
"MYSQL_COM_PING",
"MYSQL_COM_TIME",
"MYSQL_COM_DELAYED_INSERT",
"MYSQL_COM_CHANGE_USER",
"MYSQL_COM_BINLOG_DUMP",
"MYSQL_COM_TABLE_DUMP",
"MYSQL_COM_CONNECT_OUT",
"MYSQL_COM_REGISTER_SLAVE",
"MYSQL_COM_STMT_PREPARE",
"MYSQL_COM_STMT_EXECUTE",
"MYSQL_COM_STMT_SEND_LONG_DATA",
"MYSQL_COM_STMT_CLOSE",
"MYSQL_COM_STMT_RESET",
"MYSQL_COM_SET_OPTION",
"MYSQL_COM_STMT_FETCH",
"MYSQL_COM_DAEMON",
"MYSQL_COM_END"
};
typedef struct {
unsigned long thread_id;
int last_command; /* MYSQL_COM_* values, -1 for handshake */
unsigned int max_packet_size;
size_t total_size[2];
unsigned int client_flags;
char *username;
char *db;
char *command;
char *filename;
unsigned long refid; /* stmt_id, thread_id for kill */
uchar charset;
void *next;
int local_infile;
} TRACE_INFO;
#define TRACE_STATUS(a) (!a) ? "ok" : "error"
TRACE_INFO *trace_info= NULL;
static TRACE_INFO *get_trace_info(unsigned long thread_id)
{
TRACE_INFO *info= trace_info;
while (info)
{
if (info->thread_id == thread_id)
return info;
else
info= info->next;
}
if (!(info= (TRACE_INFO *)calloc(sizeof(TRACE_INFO), 1)))
return NULL;
info->thread_id= thread_id;
info->next= trace_info;
trace_info= info;
return info;
}
static void delete_trace_info(unsigned long thread_id)
{
TRACE_INFO *last= NULL, *current;
current= trace_info;
while (current)
{
if (current->thread_id == thread_id)
{
printf("deleting thread %d\n", thread_id);
if (last)
last->next= current->next;
else
trace_info= current->next;
if (current->command)
free(current->command);
if (current->db)
free(current->db);
if (current->username)
free(current->username);
if (current->filename)
free(current->filename);
free(current);
}
last= current;
current= current->next;
}
}
/* {{{ static int trace_init */
/*
Initialization routine
SYNOPSIS
trace_init
unused1
unused2
unused3
unused4
DESCRIPTION
Init function registers a callback handler for CIO interface.
RETURN
0 success
*/
static int trace_init(char *errormsg,
size_t errormsg_size,
int unused1 __attribute__((unused)),
va_list unused2 __attribute__((unused)))
{
void *func;
#ifdef WIN32
if (!(func= GetProcAddress(GetModuleHandle(NULL), "ma_cio_register_callback")))
#else
if (!(func= dlsym(RTLD_DEFAULT, "ma_cio_register_callback")))
#endif
{
strncpy(errormsg, "Can't find ma_cio_register_callback function", errormsg_size);
return 1;
}
register_callback= func;
register_callback(TRUE, trace_callback);
return 0;
}
/* }}} */
static int trace_deinit()
{
/* unregister plugin */
while(trace_info)
{
printf("Warning: Connection for thread %d not properly closed\n", trace_info->thread_id);
trace_info= trace_info->next;
}
register_callback(FALSE, trace_callback);
}
static void trace_set_command(TRACE_INFO *info, char *buffer, size_t size)
{
if (info->command)
free(info->command);
info->command= (char *)malloc(size);
strncpy(info->command, buffer, size);
}
void dump_buffer(uchar *buffer, size_t len)
{
uchar *p= buffer;
while (p < buffer + len)
{
printf("%02x ", *p);
p++;
}
printf("\n");
}
static void dump_simple(TRACE_INFO *info, my_bool is_error)
{
printf("%8d: %s %s\n", info->thread_id, commands[info->last_command], TRACE_STATUS(is_error));
}
static void dump_reference(TRACE_INFO *info, my_bool is_error)
{
printf("%8d: %s(%d) %s\n", info->thread_id, commands[info->last_command], info->refid, TRACE_STATUS(is_error));
}
static void dump_command(TRACE_INFO *info, my_bool is_error)
{
int i;
printf("%8d: %s(", info->thread_id, commands[info->last_command]);
for (i= 0; info->command && i < strlen(info->command); i++)
if (info->command[i] == '\n')
printf("\\n");
else if (info->command[i] == '\r')
printf("\\r");
else if (info->command[i] == '\t')
printf("\\t");
else
printf("%c", info->command[i]);
printf(") %s\n", TRACE_STATUS(is_error));
}
void trace_callback(int mode, MYSQL *mysql, const uchar *buffer, size_t length)
{
unsigned long thread_id= mysql->thread_id;
TRACE_INFO *info;
/* check if package is server greeting package,
* and set thread_id */
if (!thread_id && mode == READ)
{
char *p= (char *)buffer;
p+= 4; /* packet length */
if (*p != 0xFF) /* protocol version 0xFF indicates error */
{
p+= strlen(p + 1) + 2;
thread_id= uint4korr(p);
}
info= get_trace_info(thread_id);
info->last_command= -1;
}
else
{
char *p= (char *)buffer;
info= get_trace_info(thread_id);
if (info->last_command == -1)
{
if (mode == WRITE)
{
/* client authentication reply packet:
*
* ofs description length
* ------------------------
* 0 length 3
* 3 packet_no 1
* 4 client capab. 4
* 8 max_packet_size 4
* 12 character set 1
* 13 reserved 23
* ------------------------
* 36 username (zero terminated)
* len (1 byte) + password or
*/
p+= 4;
info->client_flags= uint4korr(p);
p+= 4;
info->max_packet_size= uint4korr(p);
p+= 4;
info->charset= *p;
p+= 24;
info->username= strdup(p);
p+= strlen(p) + 1;
if (*p) /* we are not interested in authentication data */
p+= *p;
p++;
if (info->client_flags & CLIENT_CONNECT_WITH_DB)
info->db= strdup(p);
}
else
{
p++;
if (*p == 0xFF)
printf("%8d: CONNECT_ERROR(%d)\n", info->thread_id, uint4korr(p+1));
else
printf("%8d: CONNECT_SUCCESS(host=%s,user=%s,db=%s)\n", info->thread_id,
mysql->host, info->username, info->db ? info->db : "'none'");
info->last_command= MYSQL_COM_SLEEP;
}
}
else {
char *p= (char *)buffer;
int len;
if (mode == WRITE)
{
len= uint3korr(p);
p+= 4;
info->last_command= *p;
p++;
switch (info->last_command) {
case MYSQL_COM_INIT_DB:
case MYSQL_COM_DROP_DB:
case MYSQL_COM_CREATE_DB:
case MYSQL_COM_DEBUG:
case MYSQL_COM_QUERY:
case MYSQL_COM_STMT_PREPARE:
trace_set_command(info, p, len - 1);
break;
case MYSQL_COM_PROCESS_KILL:
info->refid= uint4korr(p);
break;
case MYSQL_COM_QUIT:
printf("%8d: MYSQL_COM_QUIT\n", info->thread_id);
delete_trace_info(info->thread_id);
break;
case MYSQL_COM_PING:
printf("%8d: MYSQL_COM_PING\n", info->thread_id);
break;
case MYSQL_COM_STMT_EXECUTE:
case MYSQL_COM_STMT_RESET:
case MYSQL_COM_STMT_CLOSE:
info->refid= uint4korr(p);
break;
case MYSQL_COM_CHANGE_USER:
break;
default:
if (info->local_infile == 1)
{
printf("%8d: SEND_LOCAL_INFILE(%s) ", info->thread_id, info->filename);
if (len)
printf("sent %d bytes\n", len);
else
printf("- error\n");
info->local_infile= 2;
}
else
printf("%8d: UNKNOWN_COMMAND: %d\n", info->thread_id, info->last_command);
break;
}
}
else
{
my_bool is_error;
len= uint3korr(p);
p+= 4;
is_error= ((unsigned int)len == -1);
switch(info->last_command) {
case MYSQL_COM_STMT_EXECUTE:
case MYSQL_COM_STMT_RESET:
case MYSQL_COM_STMT_CLOSE:
case MYSQL_COM_PROCESS_KILL:
dump_reference(info, is_error);
info->refid= 0;
info->last_command= 0;
break;
case MYSQL_COM_QUIT:
dump_simple(info, is_error);
break;
case MYSQL_COM_QUERY:
case MYSQL_COM_INIT_DB:
case MYSQL_COM_DROP_DB:
case MYSQL_COM_CREATE_DB:
case MYSQL_COM_DEBUG:
case MYSQL_COM_CHANGE_USER:
if (info->last_command == MYSQL_COM_QUERY && (uchar)*p == 251)
{
info->local_infile= 1;
p++;
info->filename= (char *)malloc(len);
strncpy(info->filename, (char *)p, len);
dump_command(info, is_error);
break;
}
dump_command(info, is_error);
if (info->local_infile != 1)
{
free(info->command);
info->command= NULL;
}
break;
case MYSQL_COM_STMT_PREPARE:
printf("%8d: MYSQL_COM_STMT_PREPARE(%s) ", info->thread_id, info->command);
if (!*p)
{
unsigned long stmt_id= uint4korr(p+1);
printf("-> stmt_id(%d)\n", stmt_id);
}
else
printf("error\n");
break;
}
}
}
}
info->total_size[mode]+= length;
}

View File

@@ -25,7 +25,7 @@ SET(API_TESTS "async" "basic-t" "fetch" "charset" "logs" "cursor" "errors" "view
"sp" "result" "connection" "misc" "ps_new" "sqlite3" "thread" "dyncol")
# Get finger print from server certificate
IF(WITH_OPENSSL)
IF(WITH_SSL)
#create certificates
IF(EXISTS "${CMAKE_SOURCE_DIR}/unittest/libmariadb/certs/server-cert.pem")
@@ -62,7 +62,7 @@ ENDIF()
FOREACH(API_TEST ${API_TESTS})
ADD_EXECUTABLE(${API_TEST} ${API_TEST}.c)
TARGET_LINK_LIBRARIES(${API_TEST} mytap mariadbclient ${EXTRA_LIBS})
TARGET_LINK_LIBRARIES(${API_TEST} mytap mariadbclient )
ADD_TEST(${API_TEST} ${EXECUTABLE_OUTPUT_PATH}/${API_TEST})
SET_TESTS_PROPERTIES(${API_TEST} PROPERTIES TIMEOUT 120)
ENDFOREACH(API_TEST)

View File

@@ -61,6 +61,7 @@ static int test_conc75(MYSQL *my)
rc= mysql_query(mysql, "load data local infile './nonexistingfile.csv' into table a (`a`)");
FAIL_IF(!test(mysql->options.client_flag | CLIENT_LOCAL_FILES), "client_flags not correct");
FAIL_IF(thread_id == mysql_thread_id(mysql), "new thread id expected");
diag("cs: %s", mysql->charset->csname);
FAIL_IF(strcmp(mysql->charset->csname, "utf8"), "wrong character set");
}
mysql_close(mysql);

View File

@@ -143,7 +143,6 @@ static int create_dyncol_num(MYSQL *mysql)
rc= mariadb_dyncol_unpack(&dyncol, &my_count, &my_keys, &my_vals);
diag("unpack: %d %d", rc, my_count);
diag("---------------__");
for(i=0; i < 5; i++)
{
diag("%s %lu", my_keys[i].str, (unsigned long)my_keys[i].length);
@@ -200,7 +199,7 @@ static int mdev_x1(MYSQL *mysql)
for (i=0; i < unpack_columns; i++)
if (memcmp(unpack_vals[i].x.string.value.str, vals[i].x.string.value.str, vals[i].x.string.value.length))
printf("Error1: key: %1s val: %s %s\n", unpack_keys[i].str, unpack_vals[i].x.string.value.str, vals[i].x.string.value.str);
diag("Error1: key: %1s val: %s %s", unpack_keys[i].str, unpack_vals[i].x.string.value.str, vals[i].x.string.value.str);
free(unpack_keys);
free(unpack_vals);

View File

@@ -176,35 +176,6 @@ static int test_bug31418(MYSQL *mysql)
return OK;
}
/*
Altough mysql_create_db(), mysql_rm_db() are deprecated since 4.0 they
should not crash server and should not hang in case of errors.
Since those functions can't be seen in modern API (unless client library
was compiled with USE_OLD_FUNCTIONS define) we use simple_command() macro.
*/
static int test_bug6081(MYSQL *mysql)
{
int rc;
if (mysql_get_server_version(mysql) < 50100) {
diag("Test requires MySQL Server version 5.1 or above");
return SKIP;
}
rc= simple_command(mysql, MYSQL_COM_DROP_DB, (char*) schema,
(ulong)strlen(schema), 0U, NULL);
FAIL_IF(!rc, "Error expected");
rc= simple_command(mysql, MYSQL_COM_CREATE_DB, (char*) schema,
(ulong)strlen(schema), 0U, NULL);
FAIL_IF(!rc, "Error expected");
rc= mysql_select_db(mysql, schema);
check_mysql_rc(rc, mysql);
return OK;
}
/* Query processing */
static int test_debug_example(MYSQL *mysql)
@@ -1008,7 +979,7 @@ static int test_remote1(MYSQL *mysql)
{
int rc;
remote_plugin= (void *)mysql_client_find_plugin(mysql, "remote_io", MYSQL_CLIENT_REMOTEIO_PLUGIN);
remote_plugin= (void *)mysql_client_find_plugin(mysql, "remote_io", MARIADB_CLIENT_REMOTEIO_PLUGIN);
if (!remote_plugin)
{
diag("skip - no remote io plugin available");
@@ -1065,7 +1036,6 @@ struct my_tests_st my_tests[] = {
{"test_debug_example", test_debug_example, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
{"test_bug29692", test_bug29692, TEST_CONNECTION_NEW, CLIENT_FOUND_ROWS, NULL, NULL},
{"test_bug31418", test_bug31418, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
{"test_bug6081", test_bug6081, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
{"test_frm_bug", test_frm_bug, TEST_CONNECTION_NEW, 0, NULL, NULL},
{"test_wl4166_1", test_wl4166_1, TEST_CONNECTION_NEW, 0, NULL, NULL},
{"test_wl4166_2", test_wl4166_2, TEST_CONNECTION_NEW, 0, NULL, NULL},

View File

@@ -507,7 +507,9 @@ void run_tests(struct my_tests_st *test) {
return; /* default doesn't work anymore */
}
else if (mysql && !(test[i].connection & TEST_CONNECTION_DONT_CLOSE))
{
mysql_close(mysql);
}
} else {
skip(1, "%s", test[i].skipmsg);
}

View File

@@ -33,8 +33,8 @@ pthread_mutex_t LOCK_test;
int check_skip_ssl()
{
#ifndef HAVE_OPENSSL
diag("client library built without OpenSSL support -> skip");
#ifndef HAVE_SSL
diag("client library built without SSL support -> skip");
return 1;
#endif
if (skip_ssl)
@@ -67,7 +67,8 @@ static int test_ssl(MYSQL *mysql)
if (!skip_ssl)
{
rc= mysql_query(mysql, "DROP USER 'ssluser'@'localhost'");
rc= mysql_query(mysql, "DROP USER 'ssluser'@'%'");
rc= mysql_query(mysql, "GRANT ALL ON test.* TO 'ssluser'@'%' IDENTIFIED BY 'sslpw' REQUIRE SSL");
rc= mysql_query(mysql, "GRANT ALL ON test.* TO 'ssluser'@'localhost' IDENTIFIED BY 'sslpw' REQUIRE SSL");
rc= mysql_query(mysql, "FLUSH PRVILEGES");
}
@@ -92,7 +93,7 @@ static int test_ssl_cipher(MYSQL *unused)
port, socketname, 0), mysql_error(my));
cipher= (char *)mysql_get_ssl_cipher(my);
FAIL_IF(strcmp(cipher, "DHE-RSA-AES256-SHA") != 0, "Cipher != DHE-RSA-AES256-SHA");
FAIL_IF(cipher == NULL, "used cipher is NULL");
mysql_close(my);
return OK;
}
@@ -105,7 +106,9 @@ static int test_conc95(MYSQL *my)
if (check_skip_ssl())
return SKIP;
rc= mysql_query(my, "DROP USER 'ssluser1'@'%'");
rc= mysql_query(my, "DROP USER 'ssluser1'@'localhost'");
rc= mysql_query(my, "GRANT ALL ON test.* TO 'ssluser1'@'%' IDENTIFIED BY 'sslpw' REQUIRE X509");
rc= mysql_query(my, "GRANT ALL ON test.* TO 'ssluser1'@'localhost' IDENTIFIED BY 'sslpw' REQUIRE X509");
check_mysql_rc(rc, my);
rc= mysql_query(my, "FLUSH PRIVILEGES");
@@ -113,8 +116,8 @@ static int test_conc95(MYSQL *my)
mysql= mysql_init(NULL);
mysql_ssl_set(mysql,
"@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/server-key.pem",
"@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/server-cert.pem",
"@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-key.pem",
"@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-cert.pem",
"@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/ca-cert.pem",
NULL,
NULL);
@@ -122,6 +125,7 @@ static int test_conc95(MYSQL *my)
if (!mysql_real_connect(mysql, hostname, "ssluser1", sslpw, schema,
port, socketname, 0))
{
diag("Error: %s", mysql_error(mysql));
mysql_close(mysql);
diag("could not establish x509 connection");
return FAIL;
@@ -174,7 +178,7 @@ static int test_multi_ssl_connections(MYSQL *unused)
}
cipher= (char *)mysql_get_ssl_cipher(mysql[i]);
FAIL_IF(strcmp(cipher, "DHE-RSA-AES256-SHA") != 0, "Cipher != DHE-RSA-AES256-SHA");
FAIL_IF(cipher == NULL, "used cipher is NULL");
}
for (i=0; i < 50; i++)
mysql_close(mysql[i]);
@@ -396,8 +400,11 @@ static int test_conc50_3(MYSQL *my)
if (check_skip_ssl())
return SKIP;
mysql_query(my, "DROP USER 'ssltest'@'localhost'");
mysql_query(my, "DROP USER 'ssltest'@'%'");
sprintf(query, "GRANT ALL ON %s.* TO 'ssltest'@'%' REQUIRE SSL", schema ? schema : "*");
rc= mysql_query(my, query);
check_mysql_rc(rc, my);
sprintf(query, "GRANT ALL ON %s.* TO 'ssltest'@'localhost' REQUIRE SSL", schema ? schema : "*");
rc= mysql_query(my, query);
check_mysql_rc(rc, my);
@@ -481,7 +488,7 @@ static int test_bug62743(MYSQL *my)
mysql= mysql_init(NULL);
FAIL_IF(!mysql, "Can't allocate memory");
mysql_ssl_set(mysql, "dummykey", NULL, NULL, NULL, NULL);
mysql_ssl_set(mysql, "dummykey", "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-cert.pem", NULL, NULL, NULL);
mysql_real_connect(mysql, hostname, ssluser, sslpw, schema,
port, socketname, 0);
@@ -588,6 +595,8 @@ static int test_conc_102(MYSQL *mysql)
rc= mysql_query(mysql, "INSERT INTO t_conc102 VALUES (0)");
check_mysql_rc(rc, mysql);
pthread_mutex_init(&LOCK_test, 0);
for (i=0; i < 50; i++)
{
#ifndef WIN32
@@ -606,6 +615,7 @@ static int test_conc_102(MYSQL *mysql)
WaitForSingleObject(hthreads[i], INFINITE);
#endif
}
pthread_mutex_destroy(&LOCK_test);
rc= mysql_query(mysql, "SELECT a FROM t_conc102");
check_mysql_rc(rc, mysql);
res= mysql_store_result(mysql);
@@ -637,7 +647,7 @@ static int test_ssl_fp(MYSQL *unused)
port, socketname, 0), mysql_error(my));
cipher= (char *)mysql_get_ssl_cipher(my);
FAIL_IF(strcmp(cipher, "DHE-RSA-AES256-SHA") != 0, "Cipher != DHE-RSA-AES256-SHA");
FAIL_IF(cipher == NULL, "used cipher is NULL");
mysql_close(my);
return OK;
}
@@ -660,16 +670,43 @@ static int test_ssl_fp_list(MYSQL *unused)
FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema,
port, socketname, 0), mysql_error(my));
cipher= (char *)mysql_get_ssl_cipher(my);
FAIL_IF(strcmp(cipher, "DHE-RSA-AES256-SHA") != 0, "Cipher != DHE-RSA-AES256-SHA");
cipher= mysql_get_ssl_cipher(my);
FAIL_IF(cipher == NULL, "used cipher is NULL");
mysql_close(my);
return OK;
}
static int test_ssl_long_msg(MYSQL *unused)
{
MYSQL *my;
char buffer[20000];
int rc;
if (check_skip_ssl())
return SKIP;
my= mysql_init(NULL);
FAIL_IF(!my, "mysql_init() failed");
mysql_ssl_set(my,0, 0, "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/ca-cert.pem", 0, 0);
mysql_options(my, MARIADB_OPT_SSL_FP, ssl_cert_finger_print);
FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema,
port, socketname, 0), mysql_error(my));
memset(buffer, 0, 20000);
strcpy(buffer, "SET @a:=");
memset(buffer + strlen(buffer), '0', 19000);
rc= mysql_query(my, buffer);
check_mysql_rc(rc, my);
mysql_close(my);
}
struct my_tests_st my_tests[] = {
{"test_ssl", test_ssl, TEST_CONNECTION_NEW, 0, NULL, NULL},
{"test_ssl_long_msg", test_ssl_long_msg, TEST_CONNECTION_NEW, 0, NULL, NULL},
{"test_conc127", test_conc127, TEST_CONNECTION_NEW, 0, NULL, NULL},
{"test_ssl_fp", test_ssl_fp, TEST_CONNECTION_NEW, 0, NULL, NULL},
{"test_ssl_fp_list", test_ssl_fp_list, TEST_CONNECTION_NEW, 0, NULL, NULL},