You've already forked mariadb-connector-c
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:
@@ -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
2
client/CMakeLists.txt
Normal 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
166
client/ma_plugin_info.c
Normal 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
67
cmake/plugins.cmake
Normal 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)
|
@@ -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
120
include/ma_cio.h
Normal 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_ */
|
@@ -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
138
include/ma_ssl.h
Normal 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_ */
|
@@ -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 */
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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_ */
|
@@ -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}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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
469
libmariadb/ma_cio.c
Normal 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;
|
||||
}
|
||||
/* }}} */
|
@@ -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;
|
||||
}
|
||||
|
@@ -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
174
libmariadb/ma_ssl.c
Normal 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 */
|
@@ -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);
|
||||
|
||||
|
@@ -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(). */
|
||||
|
226
libmariadb/net.c
226
libmariadb/net.c
@@ -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
400
libmariadb/secure/gnutls.c
Normal 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 */
|
907
libmariadb/secure/ma_schannel.c
Normal file
907
libmariadb/secure/ma_schannel.c
Normal 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;
|
||||
}
|
||||
/* }}} */
|
||||
|
95
libmariadb/secure/ma_schannel.h
Normal file
95
libmariadb/secure/ma_schannel.h
Normal 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
532
libmariadb/secure/openssl.c
Normal 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);
|
||||
}
|
474
libmariadb/secure/schannel.c
Normal file
474
libmariadb/secure/schannel.c
Normal 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;
|
||||
}
|
@@ -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 */
|
@@ -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()
|
||||
|
@@ -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 */
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@@ -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)
|
||||
{
|
||||
/*
|
32
plugins/cio/CMakeLists.txt
Normal file
32
plugins/cio/CMakeLists.txt
Normal 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
326
plugins/cio/cio_npipe.c
Normal 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
|
2
plugins/cio/cio_plugin.def
Normal file
2
plugins/cio/cio_plugin.def
Normal file
@@ -0,0 +1,2 @@
|
||||
EXPORTS
|
||||
_mysql_client_plugin_declaration_ DATA
|
448
plugins/cio/cio_shmem.c
Normal file
448
plugins/cio/cio_shmem.c
Normal 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
905
plugins/cio/cio_socket.c
Normal 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
|
||||
}
|
||||
/* }}} */
|
19
plugins/connection/CMakeLists.txt
Normal file
19
plugins/connection/CMakeLists.txt
Normal 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()
|
332
plugins/connection/replication.c
Normal file
332
plugins/connection/replication.c
Normal 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;
|
||||
}
|
||||
}
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
BIN
plugins/trace/.trace_example.c.swo
Normal file
BIN
plugins/trace/.trace_example.c.swo
Normal file
Binary file not shown.
18
plugins/trace/CMakeLists.txt
Normal file
18
plugins/trace/CMakeLists.txt
Normal 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()
|
446
plugins/trace/trace_example.c
Normal file
446
plugins/trace/trace_example.c
Normal 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;
|
||||
}
|
@@ -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)
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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},
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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},
|
||||
|
Reference in New Issue
Block a user