mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2025-06-22 01:57:14 +02:00
Add MariaDB Connector/C as a built-in alternative (v3.2.3).
This commit is contained in:
470
vendor/MDBC/libmariadb/CMakeLists.txt
vendored
Normal file
470
vendor/MDBC/libmariadb/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,470 @@
|
||||
INCLUDE_DIRECTORIES(${CC_SOURCE_DIR}/include
|
||||
${CC_SOURCE_DIR}/libmariadb)
|
||||
|
||||
ADD_DEFINITIONS(-D HAVE_COMPRESS)
|
||||
ADD_DEFINITIONS(-D LIBMARIADB)
|
||||
ADD_DEFINITIONS(-D THREAD)
|
||||
|
||||
INCLUDE(${CC_SOURCE_DIR}/cmake/sign.cmake)
|
||||
|
||||
SET(MARIADB_LIB_SYMBOLS
|
||||
mariadb_cancel
|
||||
mariadb_connection
|
||||
mariadb_convert_string
|
||||
ma_pvio_register_callback
|
||||
mariadb_get_charset_by_name
|
||||
mariadb_stmt_execute_direct
|
||||
mariadb_get_charset_by_nr
|
||||
mariadb_get_info
|
||||
mariadb_get_infov
|
||||
mysql_get_timeout_value
|
||||
mysql_get_timeout_value_ms
|
||||
mysql_optionsv
|
||||
mysql_ps_fetch_functions
|
||||
mariadb_reconnect
|
||||
mysql_stmt_warning_count
|
||||
mariadb_stmt_fetch_fields
|
||||
mariadb_rpl_open
|
||||
mariadb_rpl_close
|
||||
mariadb_rpl_fetch
|
||||
mariadb_rpl_optionsv
|
||||
mariadb_rpl_get_optionsv
|
||||
mariadb_rpl_init_ex
|
||||
mariadb_free_rpl_event
|
||||
mariadb_field_attr
|
||||
)
|
||||
IF(WITH_SSL)
|
||||
SET(MARIADB_LIB_SYMBOLS ${MARIADB_LIB_SYMBOLS} mariadb_deinitialize_ssl)
|
||||
ENDIF()
|
||||
|
||||
SET(MYSQL_LIB_SYMBOLS
|
||||
mysql_affected_rows
|
||||
mysql_autocommit
|
||||
mysql_change_user
|
||||
mysql_character_set_name
|
||||
mysql_client_find_plugin
|
||||
mysql_client_register_plugin
|
||||
mysql_close
|
||||
mysql_commit
|
||||
mysql_data_seek
|
||||
mysql_debug
|
||||
mysql_dump_debug_info
|
||||
mysql_embedded
|
||||
mysql_eof
|
||||
mysql_errno
|
||||
mysql_error
|
||||
mysql_escape_string
|
||||
mysql_fetch_field
|
||||
mysql_fetch_field_direct
|
||||
mysql_fetch_fields
|
||||
mysql_fetch_lengths
|
||||
mysql_fetch_row
|
||||
mysql_field_count
|
||||
mysql_field_seek
|
||||
mysql_field_tell
|
||||
mysql_free_result
|
||||
mysql_get_character_set_info
|
||||
mysql_get_charset_by_name
|
||||
mysql_get_charset_by_nr
|
||||
mysql_get_client_info
|
||||
mysql_get_client_version
|
||||
mysql_get_host_info
|
||||
mysql_get_option
|
||||
mysql_get_optionv
|
||||
mysql_get_parameters
|
||||
mysql_get_proto_info
|
||||
mysql_get_server_info
|
||||
mysql_get_server_name
|
||||
mysql_get_server_version
|
||||
mysql_get_socket
|
||||
mysql_get_ssl_cipher
|
||||
mysql_hex_string
|
||||
mysql_info
|
||||
mysql_init
|
||||
mysql_insert_id
|
||||
mysql_kill
|
||||
mysql_list_dbs
|
||||
mysql_list_fields
|
||||
mysql_list_processes
|
||||
mysql_list_tables
|
||||
mysql_load_plugin
|
||||
mysql_load_plugin_v
|
||||
mysql_more_results
|
||||
mysql_net_field_length
|
||||
mysql_net_read_packet
|
||||
mysql_next_result
|
||||
mysql_num_fields
|
||||
mysql_num_rows
|
||||
mysql_options
|
||||
mysql_options4
|
||||
mysql_ping
|
||||
mysql_query
|
||||
mysql_read_query_result
|
||||
mysql_real_connect
|
||||
mysql_real_escape_string
|
||||
mysql_real_query
|
||||
mysql_refresh
|
||||
mysql_reset_connection
|
||||
mysql_rollback
|
||||
mysql_row_seek
|
||||
mysql_row_tell
|
||||
mysql_select_db
|
||||
mysql_send_query
|
||||
mysql_server_end
|
||||
mysql_server_init
|
||||
mysql_session_track_get_next
|
||||
mysql_session_track_get_first
|
||||
mysql_set_character_set
|
||||
mysql_set_local_infile_default
|
||||
mysql_set_local_infile_handler
|
||||
mysql_set_server_option
|
||||
mysql_shutdown
|
||||
mysql_sqlstate
|
||||
mysql_ssl_set
|
||||
mysql_stat
|
||||
mysql_stmt_affected_rows
|
||||
mysql_stmt_attr_get
|
||||
mysql_stmt_attr_set
|
||||
mysql_stmt_bind_param
|
||||
mysql_stmt_bind_result
|
||||
mysql_stmt_close
|
||||
mysql_stmt_data_seek
|
||||
mysql_stmt_errno
|
||||
mysql_stmt_error
|
||||
mysql_stmt_execute
|
||||
mysql_stmt_fetch
|
||||
mysql_stmt_fetch_column
|
||||
mysql_stmt_field_count
|
||||
mysql_stmt_free_result
|
||||
mysql_stmt_init
|
||||
mysql_stmt_insert_id
|
||||
mysql_stmt_more_results
|
||||
mysql_stmt_next_result
|
||||
mysql_stmt_num_rows
|
||||
mysql_stmt_param_count
|
||||
mysql_stmt_param_metadata
|
||||
mysql_stmt_prepare
|
||||
mysql_stmt_reset
|
||||
mysql_stmt_result_metadata
|
||||
mysql_stmt_row_seek
|
||||
mysql_stmt_row_tell
|
||||
mysql_stmt_send_long_data
|
||||
mysql_stmt_sqlstate
|
||||
mysql_stmt_store_result
|
||||
mysql_store_result
|
||||
mysql_thread_end
|
||||
mysql_thread_id
|
||||
mysql_thread_init
|
||||
mysql_thread_safe
|
||||
mysql_use_result
|
||||
mysql_warning_count)
|
||||
|
||||
# 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
|
||||
IF(CMAKE_COMPILER_IS_GNUCC AND CMAKE_BUILD_TYPE MATCHES "Release")
|
||||
SET_SOURCE_FILES_PROPERTIES(my_context.c PROPERTIES COMPILE_FLAGS -g)
|
||||
ENDIF()
|
||||
|
||||
SET(MARIADB_DYNCOL_SYMBOLS
|
||||
mariadb_dyncol_check
|
||||
mariadb_dyncol_column_cmp_named
|
||||
mariadb_dyncol_column_count
|
||||
mariadb_dyncol_create_many_named
|
||||
mariadb_dyncol_create_many_num
|
||||
mariadb_dyncol_exists_named
|
||||
mariadb_dyncol_exists_num
|
||||
mariadb_dyncol_free
|
||||
mariadb_dyncol_get_named
|
||||
mariadb_dyncol_get_num
|
||||
mariadb_dyncol_has_names
|
||||
mariadb_dyncol_json
|
||||
mariadb_dyncol_list_named
|
||||
mariadb_dyncol_list_num
|
||||
mariadb_dyncol_unpack
|
||||
mariadb_dyncol_update_many_named
|
||||
mariadb_dyncol_update_many_num
|
||||
mariadb_dyncol_val_double
|
||||
mariadb_dyncol_val_long
|
||||
mariadb_dyncol_val_str)
|
||||
|
||||
SET(MARIADB_NONBLOCK_SYMBOLS
|
||||
mysql_autocommit_cont
|
||||
mysql_autocommit_start
|
||||
mysql_change_user_cont
|
||||
mysql_change_user_start
|
||||
mysql_close_cont
|
||||
mysql_close_start
|
||||
mysql_commit_cont
|
||||
mysql_commit_start
|
||||
mysql_dump_debug_info_cont
|
||||
mysql_dump_debug_info_start
|
||||
mysql_fetch_row_cont
|
||||
mysql_fetch_row_start
|
||||
mysql_free_result_cont
|
||||
mysql_free_result_start
|
||||
mysql_kill_cont
|
||||
mysql_kill_start
|
||||
mysql_list_fields_cont
|
||||
mysql_list_fields_start
|
||||
mysql_next_result_cont
|
||||
mysql_next_result_start
|
||||
mysql_ping_cont
|
||||
mysql_ping_start
|
||||
mysql_reset_connection_start
|
||||
mysql_reset_connection_cont
|
||||
mysql_query_cont
|
||||
mysql_query_start
|
||||
mysql_read_query_result_cont
|
||||
mysql_read_query_result_start
|
||||
mysql_real_connect_cont
|
||||
mysql_real_connect_start
|
||||
mysql_real_query_cont
|
||||
mysql_real_query_start
|
||||
mysql_refresh_cont
|
||||
mysql_refresh_start
|
||||
mysql_rollback_cont
|
||||
mysql_rollback_start
|
||||
mysql_select_db_cont
|
||||
mysql_select_db_start
|
||||
mysql_send_query_cont
|
||||
mysql_send_query_start
|
||||
mysql_set_character_set_cont
|
||||
mysql_set_character_set_start
|
||||
mysql_set_server_option_cont
|
||||
mysql_set_server_option_start
|
||||
mysql_shutdown_cont
|
||||
mysql_shutdown_start
|
||||
mysql_stat_cont
|
||||
mysql_stat_start
|
||||
mysql_stmt_close_cont
|
||||
mysql_stmt_close_start
|
||||
mysql_stmt_execute_cont
|
||||
mysql_stmt_execute_start
|
||||
mysql_stmt_fetch_cont
|
||||
mysql_stmt_fetch_start
|
||||
mysql_stmt_free_result_cont
|
||||
mysql_stmt_free_result_start
|
||||
mysql_stmt_next_result_cont
|
||||
mysql_stmt_next_result_start
|
||||
mysql_stmt_prepare_cont
|
||||
mysql_stmt_prepare_start
|
||||
mysql_stmt_reset_cont
|
||||
mysql_stmt_reset_start
|
||||
mysql_stmt_send_long_data_cont
|
||||
mysql_stmt_send_long_data_start
|
||||
mysql_stmt_store_result_cont
|
||||
mysql_stmt_store_result_start
|
||||
mysql_store_result_cont
|
||||
mysql_store_result_start
|
||||
)
|
||||
|
||||
# handle static plugins
|
||||
SET(LIBMARIADB_SOURCES ${LIBMARIADB_PLUGIN_SOURCES})
|
||||
SET(SYSTEM_LIBS ${SYSTEM_LIBS} ${LIBMARIADB_PLUGIN_LIBS} ${INTERNAL_ZLIB_LIBRARY})
|
||||
ADD_DEFINITIONS(${LIBMARIADB_PLUGIN_DEFS})
|
||||
FOREACH(plugin ${PLUGINS_STATIC})
|
||||
SET(EXTERNAL_PLUGINS "${EXTERNAL_PLUGINS} extern struct st_mysql_client_plugin ${plugin}_client_plugin;\n")
|
||||
SET(BUILTIN_PLUGINS "${BUILTIN_PLUGINS} (struct st_mysql_client_plugin *)&${plugin}_client_plugin,\n")
|
||||
ENDFOREACH()
|
||||
CONFIGURE_FILE(${CC_SOURCE_DIR}/libmariadb/ma_client_plugin.c.in
|
||||
${CC_BINARY_DIR}/libmariadb/ma_client_plugin.c)
|
||||
|
||||
SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES}
|
||||
${CC_SOURCE_DIR}/plugins/auth/my_auth.c
|
||||
ma_array.c
|
||||
ma_charset.c
|
||||
ma_hashtbl.c
|
||||
ma_net.c
|
||||
mariadb_charset.c
|
||||
ma_time.c
|
||||
ma_default.c
|
||||
ma_errmsg.c
|
||||
mariadb_lib.c
|
||||
ma_list.c
|
||||
ma_pvio.c
|
||||
ma_tls.c
|
||||
ma_alloc.c
|
||||
ma_compress.c
|
||||
ma_init.c
|
||||
ma_password.c
|
||||
ma_ll2str.c
|
||||
ma_sha1.c
|
||||
mariadb_stmt.c
|
||||
ma_loaddata.c
|
||||
ma_stmt_codec.c
|
||||
ma_string.c
|
||||
ma_dtoa.c
|
||||
mariadb_rpl.c
|
||||
${CC_BINARY_DIR}/libmariadb/ma_client_plugin.c
|
||||
ma_io.c
|
||||
${SSL_SOURCES}
|
||||
)
|
||||
|
||||
IF(WIN32)
|
||||
ADD_DEFINITIONS(-DSIZEOF_CHARP=${CMAKE_SIZEOF_VOID_P})
|
||||
INCLUDE_DIRECTORIES(${CC_SOURCE_DIR}/win-iconv)
|
||||
SET(LIBMARIADB_SOURCES
|
||||
${LIBMARIADB_SOURCES}
|
||||
${CC_SOURCE_DIR}/win-iconv/win_iconv.c
|
||||
win32_errmsg.c
|
||||
win32_errmsg.h)
|
||||
ELSE()
|
||||
IF(ICONV_INCLUDE_DIR)
|
||||
INCLUDE_DIRECTORIES(BEFORE ${ICONV_INCLUDE_DIR})
|
||||
ENDIF()
|
||||
IF(NOT CMAKE_SYSTEM_NAME MATCHES AIX)
|
||||
ADD_DEFINITIONS(-DLIBICONV_PLUG)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
IF(ZLIB_FOUND AND WITH_EXTERNAL_ZLIB)
|
||||
INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR})
|
||||
ELSE()
|
||||
SET(ZLIB_SOURCES
|
||||
../zlib/adler32.c
|
||||
../zlib/compress.c
|
||||
../zlib/crc32.c
|
||||
../zlib/deflate.c
|
||||
../zlib/gzclose.c
|
||||
../zlib/gzlib.c
|
||||
../zlib/gzread.c
|
||||
../zlib/gzwrite.c
|
||||
../zlib/infback.c
|
||||
../zlib/inffast.c
|
||||
../zlib/inflate.c
|
||||
../zlib/inftrees.c
|
||||
../zlib/trees.c
|
||||
../zlib/uncompr.c
|
||||
../zlib/zutil.c
|
||||
)
|
||||
SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} ${ZLIB_SOURCES})
|
||||
INCLUDE_DIRECTORIES(${CC_SOURCE_DIR}/zlib)
|
||||
ENDIF()
|
||||
|
||||
IF(WITH_DYNCOL)
|
||||
MESSAGE1(WITH_DYNCOL "Dynamic column API support: ON")
|
||||
SET(MARIADB_LIB_SYMBOLS ${MARIADB_LIB_SYMBOLS} ${MARIADB_DYNCOL_SYMBOLS})
|
||||
SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} mariadb_dyncol.c)
|
||||
ENDIF()
|
||||
|
||||
SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} mariadb_async.c ma_context.c)
|
||||
SET(MARIADB_LIB_SYMBOLS ${MARIADB_LIB_SYMBOLS} ${MARIADB_NONBLOCK_SYMBOLS})
|
||||
|
||||
INCLUDE(${CC_SOURCE_DIR}/cmake/export.cmake)
|
||||
IF(NOT WIN32)
|
||||
CREATE_EXPORT_FILE(WRITE mariadbclient.def
|
||||
"libmysqlclient_18"
|
||||
"${MYSQL_LIB_SYMBOLS}"
|
||||
"libmariadbclient_18")
|
||||
CREATE_EXPORT_FILE(APPEND mariadbclient.def
|
||||
"libmariadb_3"
|
||||
"${MARIADB_LIB_SYMBOLS}"
|
||||
"")
|
||||
ELSE()
|
||||
CREATE_EXPORT_FILE(WRITE mariadbclient.def
|
||||
"libmariadb_3"
|
||||
"${MARIADB_LIB_SYMBOLS};${MYSQL_LIB_SYMBOLS}"
|
||||
"")
|
||||
ENDIF()
|
||||
|
||||
|
||||
IF(CMAKE_VERSION VERSION_GREATER 2.8.7)
|
||||
# CREATE OBJECT LIBRARY
|
||||
ADD_LIBRARY(mariadb_obj OBJECT ${LIBMARIADB_SOURCES})
|
||||
IF(UNIX)
|
||||
SET_TARGET_PROPERTIES(mariadb_obj PROPERTIES COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_C_FLAGS}")
|
||||
ENDIF()
|
||||
SET (MARIADB_OBJECTS $<TARGET_OBJECTS:mariadb_obj>)
|
||||
ELSE()
|
||||
SET (MARIADB_OBJECTS ${LIBMARIADB_SOURCES})
|
||||
ENDIF()
|
||||
|
||||
# Xcode doesn't support targets that have only object files,
|
||||
# so let's add an empty file to keep Xcode happy
|
||||
IF(CMAKE_GENERATOR MATCHES Xcode)
|
||||
FILE(WRITE ${CC_BINARY_DIR}/libmariadb/empty.c "")
|
||||
SET(EMPTY_FILE ${CC_BINARY_DIR}/libmariadb/empty.c)
|
||||
ENDIF()
|
||||
|
||||
#* create file with list of functions */
|
||||
FILE(WRITE ${CC_BINARY_DIR}/manpages.list "${MARIADB_LIB_SYMBOLS};${MYSQL_LIB_SYMBOLS}")
|
||||
|
||||
IF(WIN32)
|
||||
SET_VERSION_INFO("TARGET:libmariadb"
|
||||
"FILE_TYPE:VFT_DLL"
|
||||
"SOURCE_FILE:libmariadb/libmariadb.c"
|
||||
"ORIGINAL_FILE_NAME:libmariadb.dll"
|
||||
"FILE_DESCRIPTION:Dynamic lib for client/server communication")
|
||||
ENDIF()
|
||||
|
||||
|
||||
ADD_LIBRARY(mariadbclient STATIC ${MARIADB_OBJECTS} ${EMPTY_FILE})
|
||||
TARGET_LINK_LIBRARIES(mariadbclient ${SYSTEM_LIBS})
|
||||
|
||||
IF(UNIX)
|
||||
ADD_LIBRARY(libmariadb SHARED ${libmariadb_RC} ${MARIADB_OBJECTS} ${EMPTY_FILE})
|
||||
SET_TARGET_PROPERTIES(libmariadb PROPERTIES COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_C_FLAGS}")
|
||||
ELSE()
|
||||
ADD_LIBRARY(libmariadb SHARED ${libmariadb_RC} ${MARIADB_OBJECTS} mariadbclient.def)
|
||||
SET_TARGET_PROPERTIES(libmariadb PROPERTIES LINKER_LANGUAGE C)
|
||||
ENDIF()
|
||||
|
||||
TARGET_LINK_LIBRARIES(libmariadb LINK_PRIVATE ${SYSTEM_LIBS})
|
||||
|
||||
SIGN_TARGET(libmariadb)
|
||||
|
||||
IF(CMAKE_SYSTEM_NAME MATCHES "Linux" OR
|
||||
CMAKE_SYSTEM_NAME MATCHES "kFreeBSD" OR
|
||||
CMAKE_SYSTEM_NAME MATCHES "GNU")
|
||||
IF (NOT WITH_ASAN AND NOT WITH_TSAN AND NOT WITH_UBSAN AND NOT WITH_MSAN)
|
||||
TARGET_LINK_LIBRARIES (libmariadb LINK_PRIVATE "-Wl,--no-undefined")
|
||||
ENDIF()
|
||||
SET_TARGET_PROPERTIES(libmariadb PROPERTIES LINK_FLAGS "${CC_BINARY_DIR}/libmariadb/mariadbclient.def")
|
||||
ENDIF()
|
||||
|
||||
SET_TARGET_PROPERTIES(mariadbclient PROPERTIES IMPORTED_INTERFACE_LINK_LIBRARIES "${SYSTEM_LIBS}")
|
||||
SET_TARGET_PROPERTIES(libmariadb PROPERTIES IMPORTED_INTERFACE_LINK_LIBRARIES "${SYSTEM_LIBS}")
|
||||
|
||||
SET_TARGET_PROPERTIES(libmariadb PROPERTIES PREFIX "")
|
||||
|
||||
#
|
||||
# Installation
|
||||
#
|
||||
INCLUDE(${CC_SOURCE_DIR}/cmake/symlink.cmake)
|
||||
|
||||
# There are still several projects which don't make use
|
||||
# of the config program. To make sure these programs can
|
||||
# use mariadb client library we provide libmysql symlinks
|
||||
|
||||
IF(WITH_MYSQLCOMPAT)
|
||||
create_symlink(libmysqlclient${CMAKE_SHARED_LIBRARY_SUFFIX} libmariadb ${INSTALL_LIBDIR})
|
||||
create_symlink(libmysqlclient_r${CMAKE_SHARED_LIBRARY_SUFFIX} libmariadb ${INSTALL_LIBDIR})
|
||||
IF(NOT CMAKE_SYSTEM_NAME MATCHES AIX)
|
||||
create_symlink(libmysqlclient${CMAKE_STATIC_LIBRARY_SUFFIX} mariadbclient ${INSTALL_LIBDIR})
|
||||
create_symlink(libmysqlclient_r${CMAKE_STATIC_LIBRARY_SUFFIX} mariadbclient ${INSTALL_LIBDIR})
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
|
||||
SET_TARGET_PROPERTIES(libmariadb PROPERTIES VERSION
|
||||
${CPACK_PACKAGE_VERSION_MAJOR}
|
||||
SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR})
|
||||
|
||||
IF(NOT WIN32)
|
||||
SET_TARGET_PROPERTIES(mariadbclient PROPERTIES OUTPUT_NAME "${LIBMARIADB_STATIC_NAME}")
|
||||
ENDIF()
|
||||
|
||||
INSTALL(TARGETS mariadbclient
|
||||
COMPONENT Development
|
||||
DESTINATION ${INSTALL_LIBDIR})
|
||||
INSTALL(TARGETS libmariadb
|
||||
COMPONENT SharedLibraries
|
||||
DESTINATION ${INSTALL_LIBDIR})
|
||||
|
||||
|
||||
IF(MSVC)
|
||||
# On Windows, install PDB
|
||||
INSTALL(FILES $<TARGET_PDB_FILE:libmariadb> DESTINATION "${INSTALL_LIBDIR}"
|
||||
CONFIGURATIONS Debug RelWithDebInfo
|
||||
COMPONENT Development)
|
||||
ENDIF()
|
33
vendor/MDBC/libmariadb/bmove_upp.c
vendored
Normal file
33
vendor/MDBC/libmariadb/bmove_upp.c
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
/* 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., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02111-1301, USA */
|
||||
|
||||
/* File : bmove.c
|
||||
Author : Michael widenius
|
||||
Updated: 1987-03-20
|
||||
Defines: bmove_upp()
|
||||
|
||||
bmove_upp(dst, src, len) moves exactly "len" bytes from the source
|
||||
"src-len" to the destination "dst-len" counting downwards.
|
||||
*/
|
||||
|
||||
#include <ma_global.h>
|
||||
#include "ma_string.h"
|
||||
|
||||
void ma_bmove_upp(register char *dst, register const char *src, register size_t len)
|
||||
{
|
||||
while (len-- != 0) *--dst = *--src;
|
||||
}
|
172
vendor/MDBC/libmariadb/get_password.c
vendored
Normal file
172
vendor/MDBC/libmariadb/get_password.c
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
/************************************************************************************
|
||||
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_global.h>
|
||||
#include <ma_sys.h>
|
||||
#include "mysql.h"
|
||||
#include <ma_string.h>
|
||||
#include <mariadb_ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <termios.h>
|
||||
#else
|
||||
#include <conio.h>
|
||||
#endif /* _WIN32 */
|
||||
|
||||
/* {{{ static char *get_password() */
|
||||
/*
|
||||
read password from device
|
||||
|
||||
SYNOPSIS
|
||||
get_password
|
||||
Hdl/file file handle
|
||||
buffer input buffer
|
||||
length buffer length
|
||||
|
||||
RETURN
|
||||
buffer zero terminated input buffer
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
static char *get_password(HANDLE Hdl, char *buffer, DWORD length)
|
||||
#else
|
||||
static char *get_password(FILE *file, char *buffer, int length)
|
||||
#endif
|
||||
{
|
||||
char inChar;
|
||||
int CharsProcessed= 1;
|
||||
#ifdef _WIN32
|
||||
DWORD Offset= 0;
|
||||
#else
|
||||
int Offset= 0;
|
||||
#endif
|
||||
memset(buffer, 0, length);
|
||||
|
||||
do
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (!ReadConsole(Hdl, &inChar, 1, (DWORD *)&CharsProcessed, NULL) ||
|
||||
!CharsProcessed)
|
||||
break;
|
||||
#else
|
||||
inChar= (char)fgetc(file);
|
||||
#endif
|
||||
|
||||
switch(inChar) {
|
||||
case '\b': /* backslash */
|
||||
if (Offset)
|
||||
{
|
||||
/* cursor is always at the end */
|
||||
Offset--;
|
||||
buffer[Offset]= 0;
|
||||
#ifdef _WIN32
|
||||
_cputs("\b \b");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case '\n':
|
||||
case '\r':
|
||||
break;
|
||||
default:
|
||||
buffer[Offset]= inChar;
|
||||
if (Offset < length - 2)
|
||||
Offset++;
|
||||
#ifdef _WIN32
|
||||
_cputs("*");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
} while (CharsProcessed && inChar != '\n' && inChar != '\r');
|
||||
return buffer;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ static char* get_tty_password */
|
||||
/*
|
||||
reads password from tty/console
|
||||
|
||||
SYNOPSIS
|
||||
get_tty_password()
|
||||
buffer input buffer
|
||||
length length of input buffer
|
||||
|
||||
DESCRIPTION
|
||||
reads a password from console (Windows) or tty without echoing
|
||||
it's characters. Input buffer must be allocated by calling function.
|
||||
|
||||
RETURNS
|
||||
buffer pointer to input buffer
|
||||
*/
|
||||
char* get_tty_password(char *prompt, char *buffer, int length)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD SaveState;
|
||||
HANDLE Hdl;
|
||||
|
||||
if (prompt)
|
||||
fprintf(stderr, "%s", prompt);
|
||||
|
||||
if (!(Hdl= CreateFile("CONIN$",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING, 0, NULL)))
|
||||
{
|
||||
/* todo: provide a graphical dialog */
|
||||
return buffer;
|
||||
}
|
||||
/* Save ConsoleMode and set ENABLE_PROCESSED_INPUT:
|
||||
CTRL+C is processed by the system and is not placed in the input buffer */
|
||||
GetConsoleMode(Hdl, &SaveState);
|
||||
SetConsoleMode(Hdl, ENABLE_PROCESSED_INPUT);
|
||||
|
||||
buffer= get_password(Hdl, buffer, length);
|
||||
SetConsoleMode(Hdl, SaveState);
|
||||
CloseHandle(Hdl);
|
||||
return buffer;
|
||||
#else
|
||||
struct termios term_old,
|
||||
term_new;
|
||||
FILE *readfrom;
|
||||
|
||||
if (prompt && isatty(fileno(stderr)))
|
||||
fputs(prompt, stderr);
|
||||
|
||||
if (!(readfrom= fopen("/dev/tty", "r")))
|
||||
readfrom= stdin;
|
||||
|
||||
/* try to disable echo */
|
||||
tcgetattr(fileno(readfrom), &term_old);
|
||||
term_new= term_old;
|
||||
term_new.c_cc[VMIN] = 1;
|
||||
term_new.c_cc[VTIME]= 0;
|
||||
term_new.c_lflag&= ~(ECHO | ISIG | ICANON | ECHONL);
|
||||
tcsetattr(fileno(readfrom), TCSADRAIN, &term_new);
|
||||
|
||||
buffer= get_password(readfrom, buffer, length);
|
||||
|
||||
if (isatty(fileno(readfrom)))
|
||||
tcsetattr(fileno(readfrom), TCSADRAIN, &term_old);
|
||||
|
||||
fclose(readfrom);
|
||||
|
||||
return buffer;
|
||||
#endif
|
||||
}
|
||||
/* }}} */
|
193
vendor/MDBC/libmariadb/ma_alloc.c
vendored
Normal file
193
vendor/MDBC/libmariadb/ma_alloc.c
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
/* 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., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02111-1301, USA */
|
||||
|
||||
/* Routines to handle mallocing of results which will be freed the same time */
|
||||
|
||||
#include <ma_global.h>
|
||||
#include <ma_sys.h>
|
||||
#include <ma_string.h>
|
||||
|
||||
void ma_init_alloc_root(MA_MEM_ROOT *mem_root, size_t block_size, size_t pre_alloc_size)
|
||||
{
|
||||
mem_root->free= mem_root->used= mem_root->pre_alloc= 0;
|
||||
mem_root->min_malloc=32;
|
||||
mem_root->block_size= (block_size-MALLOC_OVERHEAD-sizeof(MA_USED_MEM)+8);
|
||||
mem_root->error_handler=0;
|
||||
mem_root->block_num= 4;
|
||||
mem_root->first_block_usage= 0;
|
||||
#if !(defined(HAVE_purify) && defined(EXTRA_DEBUG))
|
||||
if (pre_alloc_size)
|
||||
{
|
||||
if ((mem_root->free = mem_root->pre_alloc=
|
||||
(MA_USED_MEM*) malloc(pre_alloc_size+ ALIGN_SIZE(sizeof(MA_USED_MEM)))))
|
||||
{
|
||||
mem_root->free->size=pre_alloc_size+ALIGN_SIZE(sizeof(MA_USED_MEM));
|
||||
mem_root->free->left=pre_alloc_size;
|
||||
mem_root->free->next=0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void * ma_alloc_root(MA_MEM_ROOT *mem_root, size_t Size)
|
||||
{
|
||||
#if defined(HAVE_purify) && defined(EXTRA_DEBUG)
|
||||
reg1 MA_USED_MEM *next;
|
||||
Size+=ALIGN_SIZE(sizeof(MA_USED_MEM));
|
||||
|
||||
if (!(next = (MA_USED_MEM*) malloc(Size)))
|
||||
{
|
||||
if (mem_root->error_handler)
|
||||
(*mem_root->error_handler)();
|
||||
return((void *) 0); /* purecov: inspected */
|
||||
}
|
||||
next->next=mem_root->used;
|
||||
mem_root->used=next;
|
||||
return (void *) (((char*) next)+ALIGN_SIZE(sizeof(MA_USED_MEM)));
|
||||
#else
|
||||
size_t get_size;
|
||||
void * point;
|
||||
reg1 MA_USED_MEM *next= 0;
|
||||
reg2 MA_USED_MEM **prev;
|
||||
|
||||
Size= ALIGN_SIZE(Size);
|
||||
|
||||
if ((*(prev= &mem_root->free)))
|
||||
{
|
||||
if ((*prev)->left < Size &&
|
||||
mem_root->first_block_usage++ >= 16 &&
|
||||
(*prev)->left < 4096)
|
||||
{
|
||||
next= *prev;
|
||||
*prev= next->next;
|
||||
next->next= mem_root->used;
|
||||
mem_root->used= next;
|
||||
mem_root->first_block_usage= 0;
|
||||
}
|
||||
for (next= *prev; next && next->left < Size; next= next->next)
|
||||
prev= &next->next;
|
||||
}
|
||||
|
||||
if (! next)
|
||||
{ /* Time to alloc new block */
|
||||
get_size= MAX(Size+ALIGN_SIZE(sizeof(MA_USED_MEM)),
|
||||
(mem_root->block_size & ~1) * (mem_root->block_num >> 2));
|
||||
|
||||
if (!(next = (MA_USED_MEM*) malloc(get_size)))
|
||||
{
|
||||
if (mem_root->error_handler)
|
||||
(*mem_root->error_handler)();
|
||||
return((void *) 0); /* purecov: inspected */
|
||||
}
|
||||
mem_root->block_num++;
|
||||
next->next= *prev;
|
||||
next->size= get_size;
|
||||
next->left= get_size-ALIGN_SIZE(sizeof(MA_USED_MEM));
|
||||
*prev=next;
|
||||
}
|
||||
point= (void *) ((char*) next+ (next->size-next->left));
|
||||
if ((next->left-= Size) < mem_root->min_malloc)
|
||||
{ /* Full block */
|
||||
*prev=next->next; /* Remove block from list */
|
||||
next->next=mem_root->used;
|
||||
mem_root->used=next;
|
||||
mem_root->first_block_usage= 0;
|
||||
}
|
||||
return(point);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* deallocate everything used by alloc_root */
|
||||
|
||||
void ma_free_root(MA_MEM_ROOT *root, myf MyFlags)
|
||||
{
|
||||
reg1 MA_USED_MEM *next,*old;
|
||||
|
||||
if (!root)
|
||||
return; /* purecov: inspected */
|
||||
if (!(MyFlags & MY_KEEP_PREALLOC))
|
||||
root->pre_alloc=0;
|
||||
|
||||
for ( next=root->used; next ;)
|
||||
{
|
||||
old=next; next= next->next ;
|
||||
if (old != root->pre_alloc)
|
||||
free(old);
|
||||
}
|
||||
for (next= root->free ; next ; )
|
||||
{
|
||||
old=next; next= next->next ;
|
||||
if (old != root->pre_alloc)
|
||||
free(old);
|
||||
}
|
||||
root->used=root->free=0;
|
||||
if (root->pre_alloc)
|
||||
{
|
||||
root->free=root->pre_alloc;
|
||||
root->free->left=root->pre_alloc->size-ALIGN_SIZE(sizeof(MA_USED_MEM));
|
||||
root->free->next=0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char *ma_strdup_root(MA_MEM_ROOT *root,const char *str)
|
||||
{
|
||||
size_t len= strlen(str)+1;
|
||||
char *pos;
|
||||
if ((pos=ma_alloc_root(root,len)))
|
||||
memcpy(pos,str,len);
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
char *ma_memdup_root(MA_MEM_ROOT *root, const char *str, size_t len)
|
||||
{
|
||||
char *pos;
|
||||
if ((pos= ma_alloc_root(root,len)))
|
||||
memcpy(pos,str,len);
|
||||
return pos;
|
||||
}
|
||||
|
||||
void *ma_multi_malloc(myf myFlags, ...)
|
||||
{
|
||||
va_list args;
|
||||
char **ptr,*start,*res;
|
||||
size_t tot_length,length;
|
||||
|
||||
va_start(args,myFlags);
|
||||
tot_length=0;
|
||||
while ((ptr=va_arg(args, char **)))
|
||||
{
|
||||
length=va_arg(args, size_t);
|
||||
tot_length+=ALIGN_SIZE(length);
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
if (!(start=(char *)malloc(tot_length)))
|
||||
return 0;
|
||||
|
||||
va_start(args,myFlags);
|
||||
res=start;
|
||||
while ((ptr=va_arg(args, char **)))
|
||||
{
|
||||
*ptr=res;
|
||||
length=va_arg(args,size_t);
|
||||
res+=ALIGN_SIZE(length);
|
||||
}
|
||||
va_end(args);
|
||||
return start;
|
||||
}
|
172
vendor/MDBC/libmariadb/ma_array.c
vendored
Normal file
172
vendor/MDBC/libmariadb/ma_array.c
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
2016 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, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02111-1301, USA */
|
||||
|
||||
/* Handling of arrays that can grow dynamically. */
|
||||
|
||||
#undef SAFEMALLOC /* Problems with threads */
|
||||
|
||||
#include <ma_global.h>
|
||||
#include <ma_sys.h>
|
||||
#include "ma_string.h"
|
||||
#include <memory.h>
|
||||
|
||||
/*
|
||||
Initiate array and alloc space for init_alloc elements. Array is usable
|
||||
even if space allocation failed
|
||||
*/
|
||||
|
||||
my_bool ma_init_dynamic_array(DYNAMIC_ARRAY *array, uint element_size,
|
||||
uint init_alloc, uint alloc_increment CALLER_INFO_PROTO)
|
||||
{
|
||||
if (!alloc_increment)
|
||||
{
|
||||
alloc_increment=max((8192-MALLOC_OVERHEAD)/element_size,16);
|
||||
if (init_alloc > 8 && alloc_increment > init_alloc * 2)
|
||||
alloc_increment=init_alloc*2;
|
||||
}
|
||||
|
||||
if (!init_alloc)
|
||||
init_alloc=alloc_increment;
|
||||
array->elements=0;
|
||||
array->max_element=init_alloc;
|
||||
array->alloc_increment=alloc_increment;
|
||||
array->size_of_element=element_size;
|
||||
if (!(array->buffer=(char*) malloc(element_size*init_alloc)))
|
||||
{
|
||||
array->max_element=0;
|
||||
return(TRUE);
|
||||
}
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
|
||||
my_bool ma_insert_dynamic(DYNAMIC_ARRAY *array, void *element)
|
||||
{
|
||||
void *buffer;
|
||||
if (array->elements == array->max_element)
|
||||
{ /* Call only when necessary */
|
||||
if (!(buffer=ma_alloc_dynamic(array)))
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer=array->buffer+(array->elements * array->size_of_element);
|
||||
array->elements++;
|
||||
}
|
||||
memcpy(buffer,element,(size_t) array->size_of_element);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/* Alloc room for one element */
|
||||
|
||||
unsigned char *ma_alloc_dynamic(DYNAMIC_ARRAY *array)
|
||||
{
|
||||
if (array->elements == array->max_element)
|
||||
{
|
||||
char *new_ptr;
|
||||
if (!(new_ptr=(char*) realloc(array->buffer,(array->max_element+
|
||||
array->alloc_increment)*
|
||||
array->size_of_element)))
|
||||
return 0;
|
||||
array->buffer=new_ptr;
|
||||
array->max_element+=array->alloc_increment;
|
||||
}
|
||||
return (unsigned char *)array->buffer+(array->elements++ * array->size_of_element);
|
||||
}
|
||||
|
||||
|
||||
/* remove last element from array and return it */
|
||||
|
||||
unsigned char *ma_pop_dynamic(DYNAMIC_ARRAY *array)
|
||||
{
|
||||
if (array->elements)
|
||||
return (unsigned char *)array->buffer+(--array->elements * array->size_of_element);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
my_bool ma_set_dynamic(DYNAMIC_ARRAY *array, void * element, uint idx)
|
||||
{
|
||||
if (idx >= array->elements)
|
||||
{
|
||||
if (idx >= array->max_element)
|
||||
{
|
||||
uint size;
|
||||
char *new_ptr;
|
||||
size=(idx+array->alloc_increment)/array->alloc_increment;
|
||||
size*= array->alloc_increment;
|
||||
if (!(new_ptr=(char*) realloc(array->buffer,size*
|
||||
array->size_of_element)))
|
||||
return TRUE;
|
||||
array->buffer=new_ptr;
|
||||
array->max_element=size;
|
||||
}
|
||||
memset((array->buffer+array->elements*array->size_of_element), 0,
|
||||
(idx - array->elements)*array->size_of_element);
|
||||
array->elements=idx+1;
|
||||
}
|
||||
memcpy(array->buffer+(idx * array->size_of_element),element,
|
||||
(size_t) array->size_of_element);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
void ma_get_dynamic(DYNAMIC_ARRAY *array, void * element, uint idx)
|
||||
{
|
||||
if (idx >= array->elements)
|
||||
{
|
||||
memset(element, 0, array->size_of_element);
|
||||
return;
|
||||
}
|
||||
memcpy(element,array->buffer+idx*array->size_of_element,
|
||||
(size_t) array->size_of_element);
|
||||
}
|
||||
|
||||
|
||||
void ma_delete_dynamic(DYNAMIC_ARRAY *array)
|
||||
{
|
||||
if (array->buffer)
|
||||
{
|
||||
free(array->buffer);
|
||||
array->buffer=0;
|
||||
array->elements=array->max_element=0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ma_delete_dynamic_element(DYNAMIC_ARRAY *array, uint idx)
|
||||
{
|
||||
char *ptr=array->buffer+array->size_of_element*idx;
|
||||
array->elements--;
|
||||
memmove(ptr,ptr+array->size_of_element,
|
||||
(array->elements-idx)*array->size_of_element);
|
||||
}
|
||||
|
||||
|
||||
void ma_freeze_size(DYNAMIC_ARRAY *array)
|
||||
{
|
||||
uint elements=max(array->elements,1);
|
||||
|
||||
if (array->buffer && array->max_element != elements)
|
||||
{
|
||||
array->buffer=(char*) realloc(array->buffer,
|
||||
elements*array->size_of_element);
|
||||
array->max_element=elements;
|
||||
}
|
||||
}
|
1482
vendor/MDBC/libmariadb/ma_charset.c
vendored
Normal file
1482
vendor/MDBC/libmariadb/ma_charset.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
508
vendor/MDBC/libmariadb/ma_client_plugin.c.in
vendored
Normal file
508
vendor/MDBC/libmariadb/ma_client_plugin.c.in
vendored
Normal file
@ -0,0 +1,508 @@
|
||||
/* Copyright (C) 2010 - 2012 Sergei Golubchik and Monty Program Ab
|
||||
2015-2016 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 */
|
||||
|
||||
/**
|
||||
@file
|
||||
|
||||
Support code for the client side (libmariadb) plugins
|
||||
|
||||
Client plugins are somewhat different from server plugins, they are simpler.
|
||||
|
||||
They do not need to be installed or in any way explicitly loaded on the
|
||||
client, they are loaded automatically on demand.
|
||||
One client plugin per shared object, soname *must* match the plugin name.
|
||||
|
||||
There is no reference counting and no unloading either.
|
||||
*/
|
||||
|
||||
/* Silence warnings about variable 'unused' being used. */
|
||||
#define FORCE_INIT_OF_VARS 1
|
||||
|
||||
#include <ma_global.h>
|
||||
#include <ma_sys.h>
|
||||
#include <ma_common.h>
|
||||
#include <ma_string.h>
|
||||
#include <ma_pthread.h>
|
||||
|
||||
#include "errmsg.h"
|
||||
#include <mysql/client_plugin.h>
|
||||
|
||||
#ifndef WIN32
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
struct st_client_plugin_int {
|
||||
struct st_client_plugin_int *next;
|
||||
void *dlhandle;
|
||||
struct st_mysql_client_plugin *plugin;
|
||||
};
|
||||
|
||||
static my_bool initialized= 0;
|
||||
static MA_MEM_ROOT mem_root;
|
||||
|
||||
static uint valid_plugins[][2]= {
|
||||
{MYSQL_CLIENT_AUTHENTICATION_PLUGIN, MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION},
|
||||
{MARIADB_CLIENT_PVIO_PLUGIN, MARIADB_CLIENT_PVIO_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}
|
||||
};
|
||||
|
||||
/*
|
||||
Loaded plugins are stored in a linked list.
|
||||
The list is append-only, the elements are added to the head (like in a stack).
|
||||
The elements are added under a mutex, but the list can be read and traversed
|
||||
without any mutex because once an element is added to the list, it stays
|
||||
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 + 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)
|
||||
return 0;
|
||||
|
||||
my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD,
|
||||
SQLSTATE_UNKNOWN, ER(CR_AUTH_PLUGIN_CANNOT_LOAD),
|
||||
name, "not initialized");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int get_plugin_nr(uint type)
|
||||
{
|
||||
uint i= 0;
|
||||
for(; valid_plugins[i][1]; i++)
|
||||
if (valid_plugins[i][0] == type)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const char *check_plugin_version(struct st_mysql_client_plugin *plugin, unsigned int version)
|
||||
{
|
||||
if (plugin->interface_version < version ||
|
||||
(plugin->interface_version >> 8) > (version >> 8))
|
||||
return "Incompatible client plugin interface";
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
finds a plugin in the list
|
||||
|
||||
@param name plugin name to search for
|
||||
@param type plugin type
|
||||
|
||||
@note this does NOT necessarily need a mutex, take care!
|
||||
|
||||
@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);
|
||||
if (plugin_nr == -1)
|
||||
return 0;
|
||||
|
||||
if (!name)
|
||||
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
|
||||
|
||||
@param mysql MYSQL structure (for error reporting)
|
||||
@param plugin plugin to install
|
||||
@param dlhandle a handle to the shared object (returned by dlopen)
|
||||
or 0 if the plugin was not dynamically loaded
|
||||
@param argc number of arguments in the 'va_list args'
|
||||
@param args arguments passed to the plugin initialization function
|
||||
|
||||
@retval a pointer to an installed plugin or 0
|
||||
*/
|
||||
|
||||
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;
|
||||
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_nr= get_plugin_nr(plugin->type)) == -1)
|
||||
{
|
||||
errmsg= "Unknown client plugin type";
|
||||
goto err1;
|
||||
}
|
||||
if ((errmsg= check_plugin_version(plugin, valid_plugins[plugin_nr][1])))
|
||||
goto err1;
|
||||
|
||||
/* Call the plugin initialization function, if any */
|
||||
if (plugin->init && plugin->init(errbuf, sizeof(errbuf), argc, args))
|
||||
{
|
||||
errmsg= errbuf;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
p= (struct st_client_plugin_int *)
|
||||
ma_memdup_root(&mem_root, (char *)&plugin_int, sizeof(plugin_int));
|
||||
|
||||
if (!p)
|
||||
{
|
||||
errmsg= "Out of memory";
|
||||
goto err2;
|
||||
}
|
||||
|
||||
|
||||
p->next= plugin_list[plugin_nr];
|
||||
plugin_list[plugin_nr]= p;
|
||||
|
||||
return plugin;
|
||||
|
||||
err2:
|
||||
if (plugin->deinit)
|
||||
plugin->deinit();
|
||||
err1:
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Loads plugins which are specified in the environment variable
|
||||
LIBMYSQL_PLUGINS.
|
||||
|
||||
Multiple plugins must be separated by semicolon. This function doesn't
|
||||
return or log an error.
|
||||
|
||||
The function is be called by mysql_client_plugin_init
|
||||
|
||||
@todo
|
||||
Support extended syntax, passing parameters to plugins, for example
|
||||
LIBMYSQL_PLUGINS="plugin1(param1,param2);plugin2;..."
|
||||
or
|
||||
LIBMYSQL_PLUGINS="plugin1=int:param1,str:param2;plugin2;..."
|
||||
*/
|
||||
|
||||
static void load_env_plugins(MYSQL *mysql)
|
||||
{
|
||||
char *plugs, *free_env, *s= getenv("LIBMYSQL_PLUGINS");
|
||||
|
||||
if (ma_check_env_str(s))
|
||||
return;
|
||||
|
||||
free_env= strdup(s);
|
||||
plugs= s= free_env;
|
||||
|
||||
do {
|
||||
if ((s= strchr(plugs, ';')))
|
||||
*s= '\0';
|
||||
mysql_load_plugin(mysql, plugs, -1, 0);
|
||||
plugs= s + 1;
|
||||
} while (s);
|
||||
|
||||
free(free_env);
|
||||
}
|
||||
|
||||
/********** extern functions to be used by libmariadb *********************/
|
||||
|
||||
/**
|
||||
Initializes the client plugin layer.
|
||||
|
||||
This function must be called before any other client plugin function.
|
||||
|
||||
@retval 0 successful
|
||||
@retval != 0 error occurred
|
||||
*/
|
||||
|
||||
int mysql_client_plugin_init()
|
||||
{
|
||||
MYSQL mysql;
|
||||
struct st_mysql_client_plugin **builtin;
|
||||
va_list unused;
|
||||
LINT_INIT_STRUCT(unused);
|
||||
|
||||
if (initialized)
|
||||
return 0;
|
||||
|
||||
memset(&mysql, 0, sizeof(mysql)); /* dummy mysql for set_mysql_extended_error */
|
||||
|
||||
pthread_mutex_init(&LOCK_load_client_plugin, NULL);
|
||||
ma_init_alloc_root(&mem_root, 128, 128);
|
||||
|
||||
memset(&plugin_list, 0, sizeof(plugin_list));
|
||||
|
||||
initialized= 1;
|
||||
|
||||
pthread_mutex_lock(&LOCK_load_client_plugin);
|
||||
for (builtin= mysql_client_builtins; *builtin; builtin++)
|
||||
add_plugin(&mysql, *builtin, 0, 0, unused);
|
||||
|
||||
pthread_mutex_unlock(&LOCK_load_client_plugin);
|
||||
|
||||
load_env_plugins(&mysql);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Deinitializes the client plugin layer.
|
||||
|
||||
Unloades all client plugins and frees any associated resources.
|
||||
*/
|
||||
|
||||
void mysql_client_plugin_deinit()
|
||||
{
|
||||
int i;
|
||||
struct st_client_plugin_int *p;
|
||||
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
for (i=0; i < MYSQL_CLIENT_MAX_PLUGINS; i++)
|
||||
for (p= plugin_list[i]; p; p= p->next)
|
||||
{
|
||||
if (p->plugin->deinit)
|
||||
p->plugin->deinit();
|
||||
if (p->dlhandle)
|
||||
(void)dlclose(p->dlhandle);
|
||||
}
|
||||
|
||||
memset(&plugin_list, 0, sizeof(plugin_list));
|
||||
initialized= 0;
|
||||
ma_free_root(&mem_root, MYF(0));
|
||||
pthread_mutex_destroy(&LOCK_load_client_plugin);
|
||||
}
|
||||
|
||||
/************* public facing functions, for client consumption *********/
|
||||
|
||||
/* see <mysql/client_plugin.h> for a full description */
|
||||
struct st_mysql_client_plugin * STDCALL
|
||||
mysql_client_register_plugin(MYSQL *mysql,
|
||||
struct st_mysql_client_plugin *plugin)
|
||||
{
|
||||
va_list unused;
|
||||
LINT_INIT_STRUCT(unused);
|
||||
|
||||
if (is_not_initialized(mysql, plugin->name))
|
||||
return NULL;
|
||||
|
||||
pthread_mutex_lock(&LOCK_load_client_plugin);
|
||||
|
||||
/* make sure the plugin wasn't loaded meanwhile */
|
||||
if (find_plugin(plugin->name, plugin->type))
|
||||
{
|
||||
my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD,
|
||||
SQLSTATE_UNKNOWN, ER(CR_AUTH_PLUGIN_CANNOT_LOAD),
|
||||
plugin->name, "it is already loaded");
|
||||
plugin= NULL;
|
||||
}
|
||||
else
|
||||
plugin= add_plugin(mysql, plugin, 0, 0, unused);
|
||||
|
||||
pthread_mutex_unlock(&LOCK_load_client_plugin);
|
||||
return plugin;
|
||||
}
|
||||
|
||||
|
||||
/* see <mysql/client_plugin.h> for a full description */
|
||||
struct st_mysql_client_plugin * STDCALL
|
||||
mysql_load_plugin_v(MYSQL *mysql, const char *name, int type,
|
||||
int argc, va_list args)
|
||||
{
|
||||
const char *errmsg;
|
||||
#ifdef _WIN32
|
||||
char errbuf[1024];
|
||||
#endif
|
||||
char dlpath[FN_REFLEN+1];
|
||||
void *sym, *dlhandle = NULL;
|
||||
struct st_mysql_client_plugin *plugin;
|
||||
char *env_plugin_dir= getenv("MARIADB_PLUGIN_DIR");
|
||||
|
||||
CLEAR_CLIENT_ERROR(mysql);
|
||||
if (is_not_initialized(mysql, name))
|
||||
return NULL;
|
||||
|
||||
pthread_mutex_lock(&LOCK_load_client_plugin);
|
||||
|
||||
/* make sure the plugin wasn't loaded meanwhile */
|
||||
if (type >= 0 && find_plugin(name, type))
|
||||
{
|
||||
errmsg= "it is already loaded";
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Compile dll path */
|
||||
#ifndef WIN32
|
||||
snprintf(dlpath, sizeof(dlpath) - 1, "%s/%s%s",
|
||||
mysql->options.extension && mysql->options.extension->plugin_dir ?
|
||||
mysql->options.extension->plugin_dir : (env_plugin_dir) ? env_plugin_dir :
|
||||
MARIADB_PLUGINDIR, name, SO_EXT);
|
||||
#else
|
||||
{
|
||||
char *p= (mysql->options.extension && mysql->options.extension->plugin_dir) ?
|
||||
mysql->options.extension->plugin_dir : env_plugin_dir;
|
||||
snprintf(dlpath, sizeof(dlpath), "%s%s%s%s", p ? p : "", p ? "\\" : "", name, SO_EXT);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (strpbrk(name, "()[]!@#$%^&/*;.,'?\\"))
|
||||
{
|
||||
errmsg= "invalid plugin name";
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
||||
/* Open new dll handle */
|
||||
if (!(dlhandle= dlopen((const char *)dlpath, RTLD_NOW)))
|
||||
{
|
||||
#ifdef _WIN32
|
||||
char winmsg[255];
|
||||
size_t len;
|
||||
winmsg[0] = 0;
|
||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
|
||||
NULL,
|
||||
GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
winmsg, 255, NULL);
|
||||
len= strlen(winmsg);
|
||||
while (len > 0 && (winmsg[len - 1] == '\n' || winmsg[len - 1] == '\r'))
|
||||
len--;
|
||||
if (len)
|
||||
winmsg[len] = 0;
|
||||
snprintf(errbuf, sizeof(errbuf), "%s Library path is '%s'", winmsg, dlpath);
|
||||
errmsg= errbuf;
|
||||
#else
|
||||
errmsg= dlerror();
|
||||
#endif
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
||||
if (!(sym= dlsym(dlhandle, plugin_declarations_sym)))
|
||||
{
|
||||
errmsg= "not a plugin";
|
||||
(void)dlclose(dlhandle);
|
||||
goto err;
|
||||
}
|
||||
|
||||
plugin= (struct st_mysql_client_plugin*)sym;
|
||||
|
||||
if (type >=0 && type != plugin->type)
|
||||
{
|
||||
errmsg= "type mismatch";
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (strcmp(name, plugin->name))
|
||||
{
|
||||
errmsg= "name mismatch";
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (type < 0 && find_plugin(name, plugin->type))
|
||||
{
|
||||
errmsg= "it is already loaded";
|
||||
goto err;
|
||||
}
|
||||
|
||||
plugin= add_plugin(mysql, plugin, dlhandle, argc, args);
|
||||
|
||||
pthread_mutex_unlock(&LOCK_load_client_plugin);
|
||||
|
||||
return plugin;
|
||||
|
||||
err:
|
||||
if (dlhandle)
|
||||
dlclose(dlhandle);
|
||||
pthread_mutex_unlock(&LOCK_load_client_plugin);
|
||||
my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, SQLSTATE_UNKNOWN,
|
||||
ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name, errmsg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* see <mysql/client_plugin.h> for a full description */
|
||||
struct st_mysql_client_plugin * STDCALL
|
||||
mysql_load_plugin(MYSQL *mysql, const char *name, int type, int argc, ...)
|
||||
{
|
||||
struct st_mysql_client_plugin *p;
|
||||
va_list args;
|
||||
va_start(args, argc);
|
||||
p= mysql_load_plugin_v(mysql, name, type, argc, args);
|
||||
va_end(args);
|
||||
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 (plugin_nr == -1)
|
||||
{
|
||||
my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, SQLSTATE_UNKNOWN,
|
||||
ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name, "invalid type");
|
||||
}
|
||||
|
||||
if ((p= find_plugin(name, type)))
|
||||
return p;
|
||||
|
||||
/* not found, load it */
|
||||
return mysql_load_plugin(mysql, name, type, 0);
|
||||
}
|
||||
|
90
vendor/MDBC/libmariadb/ma_compress.c
vendored
Normal file
90
vendor/MDBC/libmariadb/ma_compress.c
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
2016 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, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02111-1301, USA */
|
||||
|
||||
/* Written by Sinisa Milivojevic <sinisa@coresinc.com> */
|
||||
|
||||
#include <ma_global.h>
|
||||
#ifdef HAVE_COMPRESS
|
||||
#include <ma_sys.h>
|
||||
#include <ma_string.h>
|
||||
#include <zlib.h>
|
||||
|
||||
/*
|
||||
** This replaces the packet with a compressed packet
|
||||
** Returns 1 on error
|
||||
** *complen is 0 if the packet wasn't compressed
|
||||
*/
|
||||
|
||||
my_bool _mariadb_compress(unsigned char *packet, size_t *len, size_t *complen)
|
||||
{
|
||||
if (*len < MIN_COMPRESS_LENGTH)
|
||||
*complen=0;
|
||||
else
|
||||
{
|
||||
unsigned char *compbuf=_mariadb_compress_alloc(packet,len,complen);
|
||||
if (!compbuf)
|
||||
return *complen ? 0 : 1;
|
||||
memcpy(packet,compbuf,*len);
|
||||
free(compbuf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
unsigned char *_mariadb_compress_alloc(const unsigned char *packet, size_t *len, size_t *complen)
|
||||
{
|
||||
unsigned char *compbuf;
|
||||
*complen = *len * 120 / 100 + 12;
|
||||
if (!(compbuf = (unsigned char *) malloc(*complen)))
|
||||
return 0; /* Not enough memory */
|
||||
if (compress((Bytef*) compbuf,(ulong *) complen, (Bytef*) packet,
|
||||
(uLong) *len ) != Z_OK)
|
||||
{
|
||||
free(compbuf);
|
||||
return 0;
|
||||
}
|
||||
if (*complen >= *len)
|
||||
{
|
||||
*complen=0;
|
||||
free(compbuf);
|
||||
return 0;
|
||||
}
|
||||
swap(size_t,*len,*complen); /* *len is now packet length */
|
||||
return compbuf;
|
||||
}
|
||||
|
||||
my_bool _mariadb_uncompress (unsigned char *packet, size_t *len, size_t *complen)
|
||||
{
|
||||
if (*complen) /* If compressed */
|
||||
{
|
||||
unsigned char *compbuf = (unsigned char *) malloc (*complen);
|
||||
if (!compbuf)
|
||||
return 1; /* Not enough memory */
|
||||
if (uncompress((Bytef*) compbuf, (uLongf *)complen, (Bytef*) packet, (uLongf)*len) != Z_OK)
|
||||
{ /* Probably wrong packet */
|
||||
free(compbuf);
|
||||
return 1;
|
||||
}
|
||||
*len = *complen;
|
||||
memcpy(packet,compbuf,*len);
|
||||
free(compbuf);
|
||||
}
|
||||
else *complen= *len;
|
||||
return 0;
|
||||
}
|
||||
#endif /* HAVE_COMPRESS */
|
726
vendor/MDBC/libmariadb/ma_context.c
vendored
Normal file
726
vendor/MDBC/libmariadb/ma_context.c
vendored
Normal file
@ -0,0 +1,726 @@
|
||||
/*
|
||||
Copyright 2011, 2012 Kristian Nielsen and Monty Program Ab
|
||||
2016 MariaDB Corporation AB
|
||||
|
||||
This file is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
Implementation of async context spawning using Posix ucontext and
|
||||
swapcontext().
|
||||
*/
|
||||
|
||||
#include "ma_global.h"
|
||||
#include "ma_string.h"
|
||||
#include "ma_context.h"
|
||||
|
||||
#ifdef HAVE_VALGRIND
|
||||
#include <valgrind/valgrind.h>
|
||||
#endif
|
||||
|
||||
#ifdef MY_CONTEXT_USE_UCONTEXT
|
||||
/*
|
||||
The makecontext() only allows to pass integers into the created context :-(
|
||||
We want to pass pointers, so we do it this kinda hackish way.
|
||||
Anyway, it should work everywhere, and at least it does not break strict
|
||||
aliasing.
|
||||
*/
|
||||
union pass_void_ptr_as_2_int {
|
||||
int a[2];
|
||||
void *p;
|
||||
};
|
||||
|
||||
/*
|
||||
We use old-style function definition here, as this is passed to
|
||||
makecontext(). And the type of the makecontext() argument does not match
|
||||
the actual type (as the actual type can differ from call to call).
|
||||
*/
|
||||
static void
|
||||
my_context_spawn_internal(i0, i1)
|
||||
int i0, i1;
|
||||
{
|
||||
int err;
|
||||
struct my_context *c;
|
||||
union pass_void_ptr_as_2_int u;
|
||||
|
||||
u.a[0]= i0;
|
||||
u.a[1]= i1;
|
||||
c= (struct my_context *)u.p;
|
||||
|
||||
(*c->user_func)(c->user_data);
|
||||
c->active= 0;
|
||||
err= setcontext(&c->base_context);
|
||||
fprintf(stderr, "Aieie, setcontext() failed: %d (errno=%d)\n", err, errno);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
my_context_continue(struct my_context *c)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!c->active)
|
||||
return 0;
|
||||
|
||||
err= swapcontext(&c->base_context, &c->spawned_context);
|
||||
if (err)
|
||||
{
|
||||
fprintf(stderr, "Aieie, swapcontext() failed: %d (errno=%d)\n",
|
||||
err, errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return c->active;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
|
||||
{
|
||||
int err;
|
||||
union pass_void_ptr_as_2_int u;
|
||||
|
||||
err= getcontext(&c->spawned_context);
|
||||
if (err)
|
||||
return -1;
|
||||
c->spawned_context.uc_stack.ss_sp= c->stack;
|
||||
c->spawned_context.uc_stack.ss_size= c->stack_size;
|
||||
c->spawned_context.uc_link= NULL;
|
||||
c->user_func= f;
|
||||
c->user_data= d;
|
||||
c->active= 1;
|
||||
u.p= c;
|
||||
makecontext(&c->spawned_context, my_context_spawn_internal, 2,
|
||||
u.a[0], u.a[1]);
|
||||
|
||||
return my_context_continue(c);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
my_context_yield(struct my_context *c)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!c->active)
|
||||
return -1;
|
||||
|
||||
err= swapcontext(&c->spawned_context, &c->base_context);
|
||||
if (err)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
my_context_init(struct my_context *c, size_t stack_size)
|
||||
{
|
||||
#if SIZEOF_CHARP > SIZEOF_INT*2
|
||||
#error Error: Unable to store pointer in 2 ints on this architecture
|
||||
#endif
|
||||
|
||||
memset(c, 0, sizeof(*c));
|
||||
if (!(c->stack= malloc(stack_size)))
|
||||
return -1; /* Out of memory */
|
||||
c->stack_size= stack_size;
|
||||
#ifdef HAVE_VALGRIND
|
||||
c->valgrind_stack_id=
|
||||
VALGRIND_STACK_REGISTER(c->stack, ((unsigned char *)(c->stack))+stack_size);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
my_context_destroy(struct my_context *c)
|
||||
{
|
||||
if (c->stack)
|
||||
{
|
||||
#ifdef HAVE_VALGRIND
|
||||
VALGRIND_STACK_DEREGISTER(c->valgrind_stack_id);
|
||||
#endif
|
||||
free(c->stack);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MY_CONTEXT_USE_UCONTEXT */
|
||||
|
||||
|
||||
#ifdef MY_CONTEXT_USE_X86_64_GCC_ASM
|
||||
/*
|
||||
GCC-amd64 implementation of my_context.
|
||||
|
||||
This is slightly optimized in the common case where we never yield
|
||||
(eg. fetch next row and it is already fully received in buffer). In this
|
||||
case we do not need to restore registers at return (though we still need to
|
||||
save them as we cannot know if we will yield or not in advance).
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
Layout of saved registers etc.
|
||||
Since this is accessed through gcc inline assembler, it is simpler to just
|
||||
use numbers than to try to define nice constants or structs.
|
||||
|
||||
0 0 %rsp
|
||||
1 8 %rbp
|
||||
2 16 %rbx
|
||||
3 24 %r12
|
||||
4 32 %r13
|
||||
5 40 %r14
|
||||
6 48 %r15
|
||||
7 56 %rip for done
|
||||
8 64 %rip for yield/continue
|
||||
*/
|
||||
|
||||
int
|
||||
my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
There are 6 callee-save registers we need to save and restore when
|
||||
suspending and continuing, plus stack pointer %rsp and instruction pointer
|
||||
%rip.
|
||||
|
||||
However, if we never suspend, the user-supplied function will in any case
|
||||
restore the 6 callee-save registers, so we can avoid restoring them in
|
||||
this case.
|
||||
*/
|
||||
__asm__ __volatile__
|
||||
(
|
||||
"movq %%rsp, (%[save])\n\t"
|
||||
"movq %[stack], %%rsp\n\t"
|
||||
#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 4 && !defined(__INTEL_COMPILER)
|
||||
/*
|
||||
This emits a DWARF DW_CFA_undefined directive to make the return address
|
||||
undefined. This indicates that this is the top of the stack frame, and
|
||||
helps tools that use DWARF stack unwinding to obtain stack traces.
|
||||
(I use numeric constant to avoid a dependency on libdwarf includes).
|
||||
*/
|
||||
".cfi_escape 0x07, 16\n\t"
|
||||
#endif
|
||||
"movq %%rbp, 8(%[save])\n\t"
|
||||
"movq %%rbx, 16(%[save])\n\t"
|
||||
"movq %%r12, 24(%[save])\n\t"
|
||||
"movq %%r13, 32(%[save])\n\t"
|
||||
"movq %%r14, 40(%[save])\n\t"
|
||||
"movq %%r15, 48(%[save])\n\t"
|
||||
"leaq 1f(%%rip), %%rax\n\t"
|
||||
"leaq 2f(%%rip), %%rcx\n\t"
|
||||
"movq %%rax, 56(%[save])\n\t"
|
||||
"movq %%rcx, 64(%[save])\n\t"
|
||||
/*
|
||||
Constraint below puts the argument to the user function into %rdi, as
|
||||
needed for the calling convention.
|
||||
*/
|
||||
"callq *%[f]\n\t"
|
||||
"jmpq *56(%[save])\n"
|
||||
/*
|
||||
Come here when operation is done.
|
||||
We do not need to restore callee-save registers, as the called function
|
||||
will do this for us if needed.
|
||||
*/
|
||||
"1:\n\t"
|
||||
"movq (%[save]), %%rsp\n\t"
|
||||
"xorl %[ret], %[ret]\n\t"
|
||||
"jmp 3f\n"
|
||||
/* Come here when operation was suspended. */
|
||||
"2:\n\t"
|
||||
"movl $1, %[ret]\n"
|
||||
"3:\n"
|
||||
: [ret] "=a" (ret),
|
||||
[f] "+S" (f),
|
||||
/* Need this in %rdi to follow calling convention. */
|
||||
[d] "+D" (d)
|
||||
: [stack] "a" (c->stack_top),
|
||||
/* Need this in callee-save register to preserve in function call. */
|
||||
[save] "b" (&c->save[0])
|
||||
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc"
|
||||
);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
my_context_continue(struct my_context *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
__asm__ __volatile__
|
||||
(
|
||||
"movq (%[save]), %%rax\n\t"
|
||||
"movq %%rsp, (%[save])\n\t"
|
||||
"movq %%rax, %%rsp\n\t"
|
||||
"movq 8(%[save]), %%rax\n\t"
|
||||
"movq %%rbp, 8(%[save])\n\t"
|
||||
"movq %%rax, %%rbp\n\t"
|
||||
"movq 24(%[save]), %%rax\n\t"
|
||||
"movq %%r12, 24(%[save])\n\t"
|
||||
"movq %%rax, %%r12\n\t"
|
||||
"movq 32(%[save]), %%rax\n\t"
|
||||
"movq %%r13, 32(%[save])\n\t"
|
||||
"movq %%rax, %%r13\n\t"
|
||||
"movq 40(%[save]), %%rax\n\t"
|
||||
"movq %%r14, 40(%[save])\n\t"
|
||||
"movq %%rax, %%r14\n\t"
|
||||
"movq 48(%[save]), %%rax\n\t"
|
||||
"movq %%r15, 48(%[save])\n\t"
|
||||
"movq %%rax, %%r15\n\t"
|
||||
|
||||
"leaq 1f(%%rip), %%rax\n\t"
|
||||
"leaq 2f(%%rip), %%rcx\n\t"
|
||||
"movq %%rax, 56(%[save])\n\t"
|
||||
"movq 64(%[save]), %%rax\n\t"
|
||||
"movq %%rcx, 64(%[save])\n\t"
|
||||
|
||||
"movq 16(%[save]), %%rcx\n\t"
|
||||
"movq %%rbx, 16(%[save])\n\t"
|
||||
"movq %%rcx, %%rbx\n\t"
|
||||
|
||||
"jmpq *%%rax\n"
|
||||
/*
|
||||
Come here when operation is done.
|
||||
Be sure to use the same callee-save register for %[save] here and in
|
||||
my_context_spawn(), so we preserve the value correctly at this point.
|
||||
*/
|
||||
"1:\n\t"
|
||||
"movq (%[save]), %%rsp\n\t"
|
||||
"movq 8(%[save]), %%rbp\n\t"
|
||||
/* %rbx is preserved from my_context_spawn() in this case. */
|
||||
"movq 24(%[save]), %%r12\n\t"
|
||||
"movq 32(%[save]), %%r13\n\t"
|
||||
"movq 40(%[save]), %%r14\n\t"
|
||||
"movq 48(%[save]), %%r15\n\t"
|
||||
"xorl %[ret], %[ret]\n\t"
|
||||
"jmp 3f\n"
|
||||
/* Come here when operation is suspended. */
|
||||
"2:\n\t"
|
||||
"movl $1, %[ret]\n"
|
||||
"3:\n"
|
||||
: [ret] "=a" (ret)
|
||||
: /* Need this in callee-save register to preserve in function call. */
|
||||
[save] "b" (&c->save[0])
|
||||
: "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", "memory", "cc"
|
||||
);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
my_context_yield(struct my_context *c)
|
||||
{
|
||||
uint64_t *save= &c->save[0];
|
||||
__asm__ __volatile__
|
||||
(
|
||||
"movq (%[save]), %%rax\n\t"
|
||||
"movq %%rsp, (%[save])\n\t"
|
||||
"movq %%rax, %%rsp\n\t"
|
||||
"movq 8(%[save]), %%rax\n\t"
|
||||
"movq %%rbp, 8(%[save])\n\t"
|
||||
"movq %%rax, %%rbp\n\t"
|
||||
"movq 16(%[save]), %%rax\n\t"
|
||||
"movq %%rbx, 16(%[save])\n\t"
|
||||
"movq %%rax, %%rbx\n\t"
|
||||
"movq 24(%[save]), %%rax\n\t"
|
||||
"movq %%r12, 24(%[save])\n\t"
|
||||
"movq %%rax, %%r12\n\t"
|
||||
"movq 32(%[save]), %%rax\n\t"
|
||||
"movq %%r13, 32(%[save])\n\t"
|
||||
"movq %%rax, %%r13\n\t"
|
||||
"movq 40(%[save]), %%rax\n\t"
|
||||
"movq %%r14, 40(%[save])\n\t"
|
||||
"movq %%rax, %%r14\n\t"
|
||||
"movq 48(%[save]), %%rax\n\t"
|
||||
"movq %%r15, 48(%[save])\n\t"
|
||||
"movq %%rax, %%r15\n\t"
|
||||
"movq 64(%[save]), %%rax\n\t"
|
||||
"leaq 1f(%%rip), %%rcx\n\t"
|
||||
"movq %%rcx, 64(%[save])\n\t"
|
||||
|
||||
"jmpq *%%rax\n"
|
||||
|
||||
"1:\n"
|
||||
: [save] "+D" (save)
|
||||
:
|
||||
: "rax", "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory", "cc"
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
my_context_init(struct my_context *c, size_t stack_size)
|
||||
{
|
||||
memset(c, 0, sizeof(*c));
|
||||
|
||||
if (!(c->stack_bot= malloc(stack_size)))
|
||||
return -1; /* Out of memory */
|
||||
/*
|
||||
The x86_64 ABI specifies 16-byte stack alignment.
|
||||
Also put two zero words at the top of the stack.
|
||||
*/
|
||||
c->stack_top= (void *)
|
||||
(( ((intptr)c->stack_bot + stack_size) & ~(intptr)0xf) - 16);
|
||||
memset(c->stack_top, 0, 16);
|
||||
|
||||
#ifdef HAVE_VALGRIND
|
||||
c->valgrind_stack_id=
|
||||
VALGRIND_STACK_REGISTER(c->stack_bot, c->stack_top);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
my_context_destroy(struct my_context *c)
|
||||
{
|
||||
if (c->stack_bot)
|
||||
{
|
||||
free(c->stack_bot);
|
||||
#ifdef HAVE_VALGRIND
|
||||
VALGRIND_STACK_DEREGISTER(c->valgrind_stack_id);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MY_CONTEXT_USE_X86_64_GCC_ASM */
|
||||
|
||||
|
||||
#ifdef MY_CONTEXT_USE_I386_GCC_ASM
|
||||
/*
|
||||
GCC-i386 implementation of my_context.
|
||||
|
||||
This is slightly optimized in the common case where we never yield
|
||||
(eg. fetch next row and it is already fully received in buffer). In this
|
||||
case we do not need to restore registers at return (though we still need to
|
||||
save them as we cannot know if we will yield or not in advance).
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
Layout of saved registers etc.
|
||||
Since this is accessed through gcc inline assembler, it is simpler to just
|
||||
use numbers than to try to define nice constants or structs.
|
||||
|
||||
0 0 %esp
|
||||
1 4 %ebp
|
||||
2 8 %ebx
|
||||
3 12 %esi
|
||||
4 16 %edi
|
||||
5 20 %eip for done
|
||||
6 24 %eip for yield/continue
|
||||
*/
|
||||
|
||||
int
|
||||
my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
There are 4 callee-save registers we need to save and restore when
|
||||
suspending and continuing, plus stack pointer %esp and instruction pointer
|
||||
%eip.
|
||||
|
||||
However, if we never suspend, the user-supplied function will in any case
|
||||
restore the 4 callee-save registers, so we can avoid restoring them in
|
||||
this case.
|
||||
*/
|
||||
__asm__ __volatile__
|
||||
(
|
||||
"movl %%esp, (%[save])\n\t"
|
||||
"movl %[stack], %%esp\n\t"
|
||||
#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 4 && !defined(__INTEL_COMPILER)
|
||||
/*
|
||||
This emits a DWARF DW_CFA_undefined directive to make the return address
|
||||
undefined. This indicates that this is the top of the stack frame, and
|
||||
helps tools that use DWARF stack unwinding to obtain stack traces.
|
||||
(I use numeric constant to avoid a dependency on libdwarf includes).
|
||||
*/
|
||||
".cfi_escape 0x07, 8\n\t"
|
||||
#endif
|
||||
/* Push the parameter on the stack. */
|
||||
"pushl %[d]\n\t"
|
||||
"movl %%ebp, 4(%[save])\n\t"
|
||||
"movl %%ebx, 8(%[save])\n\t"
|
||||
"movl %%esi, 12(%[save])\n\t"
|
||||
"movl %%edi, 16(%[save])\n\t"
|
||||
/* Get label addresses in -fPIC-compatible way (no pc-relative on 32bit) */
|
||||
"call 1f\n"
|
||||
"1:\n\t"
|
||||
"popl %%eax\n\t"
|
||||
"addl $(2f-1b), %%eax\n\t"
|
||||
"movl %%eax, 20(%[save])\n\t"
|
||||
"addl $(3f-2f), %%eax\n\t"
|
||||
"movl %%eax, 24(%[save])\n\t"
|
||||
"call *%[f]\n\t"
|
||||
"jmp *20(%[save])\n"
|
||||
/*
|
||||
Come here when operation is done.
|
||||
We do not need to restore callee-save registers, as the called function
|
||||
will do this for us if needed.
|
||||
*/
|
||||
"2:\n\t"
|
||||
"movl (%[save]), %%esp\n\t"
|
||||
"xorl %[ret], %[ret]\n\t"
|
||||
"jmp 4f\n"
|
||||
/* Come here when operation was suspended. */
|
||||
"3:\n\t"
|
||||
"movl $1, %[ret]\n"
|
||||
"4:\n"
|
||||
: [ret] "=a" (ret),
|
||||
[f] "+c" (f),
|
||||
[d] "+d" (d)
|
||||
: [stack] "a" (c->stack_top),
|
||||
/* Need this in callee-save register to preserve across function call. */
|
||||
[save] "D" (&c->save[0])
|
||||
: "memory", "cc"
|
||||
);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
my_context_continue(struct my_context *c)
|
||||
{
|
||||
int ret;
|
||||
|
||||
__asm__ __volatile__
|
||||
(
|
||||
"movl (%[save]), %%eax\n\t"
|
||||
"movl %%esp, (%[save])\n\t"
|
||||
"movl %%eax, %%esp\n\t"
|
||||
"movl 4(%[save]), %%eax\n\t"
|
||||
"movl %%ebp, 4(%[save])\n\t"
|
||||
"movl %%eax, %%ebp\n\t"
|
||||
"movl 8(%[save]), %%eax\n\t"
|
||||
"movl %%ebx, 8(%[save])\n\t"
|
||||
"movl %%eax, %%ebx\n\t"
|
||||
"movl 12(%[save]), %%eax\n\t"
|
||||
"movl %%esi, 12(%[save])\n\t"
|
||||
"movl %%eax, %%esi\n\t"
|
||||
|
||||
"movl 24(%[save]), %%eax\n\t"
|
||||
"call 1f\n"
|
||||
"1:\n\t"
|
||||
"popl %%ecx\n\t"
|
||||
"addl $(2f-1b), %%ecx\n\t"
|
||||
"movl %%ecx, 20(%[save])\n\t"
|
||||
"addl $(3f-2f), %%ecx\n\t"
|
||||
"movl %%ecx, 24(%[save])\n\t"
|
||||
|
||||
/* Must restore %edi last as it is also our %[save] register. */
|
||||
"movl 16(%[save]), %%ecx\n\t"
|
||||
"movl %%edi, 16(%[save])\n\t"
|
||||
"movl %%ecx, %%edi\n\t"
|
||||
|
||||
"jmp *%%eax\n"
|
||||
/*
|
||||
Come here when operation is done.
|
||||
Be sure to use the same callee-save register for %[save] here and in
|
||||
my_context_spawn(), so we preserve the value correctly at this point.
|
||||
*/
|
||||
"2:\n\t"
|
||||
"movl (%[save]), %%esp\n\t"
|
||||
"movl 4(%[save]), %%ebp\n\t"
|
||||
"movl 8(%[save]), %%ebx\n\t"
|
||||
"movl 12(%[save]), %%esi\n\t"
|
||||
"movl 16(%[save]), %%edi\n\t"
|
||||
"xorl %[ret], %[ret]\n\t"
|
||||
"jmp 4f\n"
|
||||
/* Come here when operation is suspended. */
|
||||
"3:\n\t"
|
||||
"movl $1, %[ret]\n"
|
||||
"4:\n"
|
||||
: [ret] "=a" (ret)
|
||||
: /* Need this in callee-save register to preserve in function call. */
|
||||
[save] "D" (&c->save[0])
|
||||
: "ecx", "edx", "memory", "cc"
|
||||
);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
my_context_yield(struct my_context *c)
|
||||
{
|
||||
uint64_t *save= &c->save[0];
|
||||
__asm__ __volatile__
|
||||
(
|
||||
"movl (%[save]), %%eax\n\t"
|
||||
"movl %%esp, (%[save])\n\t"
|
||||
"movl %%eax, %%esp\n\t"
|
||||
"movl 4(%[save]), %%eax\n\t"
|
||||
"movl %%ebp, 4(%[save])\n\t"
|
||||
"movl %%eax, %%ebp\n\t"
|
||||
"movl 8(%[save]), %%eax\n\t"
|
||||
"movl %%ebx, 8(%[save])\n\t"
|
||||
"movl %%eax, %%ebx\n\t"
|
||||
"movl 12(%[save]), %%eax\n\t"
|
||||
"movl %%esi, 12(%[save])\n\t"
|
||||
"movl %%eax, %%esi\n\t"
|
||||
"movl 16(%[save]), %%eax\n\t"
|
||||
"movl %%edi, 16(%[save])\n\t"
|
||||
"movl %%eax, %%edi\n\t"
|
||||
|
||||
"movl 24(%[save]), %%eax\n\t"
|
||||
"call 1f\n"
|
||||
"1:\n\t"
|
||||
"popl %%ecx\n\t"
|
||||
"addl $(2f-1b), %%ecx\n\t"
|
||||
"movl %%ecx, 24(%[save])\n\t"
|
||||
|
||||
"jmp *%%eax\n"
|
||||
|
||||
"2:\n"
|
||||
: [save] "+d" (save)
|
||||
:
|
||||
: "eax", "ecx", "memory", "cc"
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
my_context_init(struct my_context *c, size_t stack_size)
|
||||
{
|
||||
memset(c, 0, sizeof(*c));
|
||||
if (!(c->stack_bot= malloc(stack_size)))
|
||||
return -1; /* Out of memory */
|
||||
c->stack_top= (void *)
|
||||
(( ((intptr)c->stack_bot + stack_size) & ~(intptr)0xf) - 16);
|
||||
memset(c->stack_top, 0, 16);
|
||||
|
||||
#ifdef HAVE_VALGRIND
|
||||
c->valgrind_stack_id=
|
||||
VALGRIND_STACK_REGISTER(c->stack_bot, c->stack_top);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
my_context_destroy(struct my_context *c)
|
||||
{
|
||||
if (c->stack_bot)
|
||||
{
|
||||
free(c->stack_bot);
|
||||
#ifdef HAVE_VALGRIND
|
||||
VALGRIND_STACK_DEREGISTER(c->valgrind_stack_id);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MY_CONTEXT_USE_I386_GCC_ASM */
|
||||
|
||||
|
||||
#ifdef MY_CONTEXT_USE_WIN32_FIBERS
|
||||
int
|
||||
my_context_yield(struct my_context *c)
|
||||
{
|
||||
c->return_value= 1;
|
||||
SwitchToFiber(c->app_fiber);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void WINAPI
|
||||
my_context_trampoline(void *p)
|
||||
{
|
||||
struct my_context *c= (struct my_context *)p;
|
||||
/*
|
||||
Reuse the Fiber by looping infinitely, each time we are scheduled we
|
||||
spawn the appropriate function and switch back when it is done.
|
||||
|
||||
This way we avoid the overhead of CreateFiber() for every asynchroneous
|
||||
operation.
|
||||
*/
|
||||
for(;;)
|
||||
{
|
||||
(*(c->user_func))(c->user_arg);
|
||||
c->return_value= 0;
|
||||
SwitchToFiber(c->app_fiber);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
my_context_init(struct my_context *c, size_t stack_size)
|
||||
{
|
||||
memset(c, 0, sizeof(*c));
|
||||
c->lib_fiber= CreateFiber(stack_size, my_context_trampoline, c);
|
||||
if (c->lib_fiber)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
my_context_destroy(struct my_context *c)
|
||||
{
|
||||
if (c->lib_fiber)
|
||||
{
|
||||
DeleteFiber(c->lib_fiber);
|
||||
c->lib_fiber= NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
|
||||
{
|
||||
c->user_func= f;
|
||||
c->user_arg= d;
|
||||
return my_context_continue(c);
|
||||
}
|
||||
|
||||
int
|
||||
my_context_continue(struct my_context *c)
|
||||
{
|
||||
void *current_fiber= IsThreadAFiber() ? GetCurrentFiber() : ConvertThreadToFiber(c);
|
||||
c->app_fiber= current_fiber;
|
||||
SwitchToFiber(c->lib_fiber);
|
||||
return c->return_value;
|
||||
}
|
||||
|
||||
#endif /* MY_CONTEXT_USE_WIN32_FIBERS */
|
||||
|
||||
#ifdef MY_CONTEXT_DISABLE
|
||||
int
|
||||
my_context_continue(struct my_context *c)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
my_context_yield(struct my_context *c)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
my_context_init(struct my_context *c, size_t stack_size)
|
||||
{
|
||||
return -1; /* Out of memory */
|
||||
}
|
||||
|
||||
void
|
||||
my_context_destroy(struct my_context *c)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
370
vendor/MDBC/libmariadb/ma_default.c
vendored
Normal file
370
vendor/MDBC/libmariadb/ma_default.c
vendored
Normal file
@ -0,0 +1,370 @@
|
||||
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
2016 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, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02111-1301, USA */
|
||||
|
||||
#include <ma_global.h>
|
||||
#include <ma_sys.h>
|
||||
#include "ma_string.h"
|
||||
#include <ctype.h>
|
||||
#include "mariadb_ctype.h"
|
||||
#include <mysql.h>
|
||||
#include <ma_common.h>
|
||||
#include <mariadb/ma_io.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#include "shlwapi.h"
|
||||
|
||||
static const char *ini_exts[]= {"ini", "cnf", 0};
|
||||
#define R_OK 4
|
||||
#else
|
||||
#include <unistd.h>
|
||||
static const char *ini_exts[]= {"cnf", 0};
|
||||
#endif
|
||||
|
||||
char **configuration_dirs= NULL;
|
||||
#define MAX_CONFIG_DIRS 6
|
||||
|
||||
my_bool _mariadb_read_options(MYSQL *mysql,
|
||||
const char *config_dir,
|
||||
const char *config_file,
|
||||
const char *group,
|
||||
unsigned int recursion);
|
||||
|
||||
static int add_cfg_dir(char **cfg_dirs, const char *directory)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_CONFIG_DIRS && cfg_dirs[i]; i++)
|
||||
if (!strcmp(cfg_dirs[i], directory)) /* already present */
|
||||
return 0;
|
||||
|
||||
if (i < MAX_CONFIG_DIRS) {
|
||||
cfg_dirs[i]= strdup(directory);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void release_configuration_dirs()
|
||||
{
|
||||
if (configuration_dirs)
|
||||
{
|
||||
int i= 0;
|
||||
while (configuration_dirs[i])
|
||||
free(configuration_dirs[i++]);
|
||||
free(configuration_dirs);
|
||||
}
|
||||
}
|
||||
|
||||
char **get_default_configuration_dirs()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
char dirname[FN_REFLEN];
|
||||
#endif
|
||||
char *env;
|
||||
|
||||
configuration_dirs= (char **)calloc(1, (MAX_CONFIG_DIRS + 1) * sizeof(char *));
|
||||
if (!configuration_dirs)
|
||||
goto end;
|
||||
|
||||
#ifdef _WIN32
|
||||
/* On Windows operating systems configuration files are stored in
|
||||
1. System Windows directory
|
||||
2. System directory
|
||||
3. Windows directory
|
||||
4. C:\
|
||||
*/
|
||||
|
||||
if (!GetSystemWindowsDirectory(dirname, FN_REFLEN) ||
|
||||
add_cfg_dir(configuration_dirs, dirname))
|
||||
goto error;
|
||||
|
||||
if (!GetWindowsDirectory(dirname, FN_REFLEN) ||
|
||||
add_cfg_dir(configuration_dirs, dirname))
|
||||
goto error;
|
||||
|
||||
if (add_cfg_dir(configuration_dirs, "C:"))
|
||||
goto error;
|
||||
|
||||
if (GetModuleFileName(NULL, dirname, FN_REFLEN))
|
||||
{
|
||||
PathRemoveFileSpec(dirname);
|
||||
if (add_cfg_dir(configuration_dirs, dirname))
|
||||
goto error;
|
||||
}
|
||||
#else
|
||||
/* on *nix platforms configuration files are stored in
|
||||
1. SYSCONFDIR (if build happens inside server package, or
|
||||
-DDEFAULT_SYSCONFDIR was specified
|
||||
2. /etc
|
||||
3. /etc/mysql
|
||||
*/
|
||||
#ifdef DEFAULT_SYSCONFDIR
|
||||
if (add_cfg_dir(configuration_dirs, DEFAULT_SYSCONFDIR))
|
||||
goto error;
|
||||
#else
|
||||
if (add_cfg_dir(configuration_dirs, "/etc"))
|
||||
goto error;
|
||||
if (add_cfg_dir(configuration_dirs, "/etc/mysql"))
|
||||
goto error;
|
||||
#endif
|
||||
#endif
|
||||
/* CONC-537: Read configuration files from MYSQL_HOME directory only if
|
||||
MARIADB_HOME was not set */
|
||||
if (!(env= getenv("MARIADB_HOME")))
|
||||
env= getenv("MYSQL_HOME");
|
||||
if (env && add_cfg_dir(configuration_dirs, env))
|
||||
goto error;
|
||||
end:
|
||||
return configuration_dirs;
|
||||
error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
extern my_bool _mariadb_set_conf_option(MYSQL *mysql, const char *config_option, const char *config_value);
|
||||
|
||||
static my_bool is_group(char *ptr, const char **groups)
|
||||
{
|
||||
while (*groups)
|
||||
{
|
||||
if (!strcmp(ptr, *groups))
|
||||
return 1;
|
||||
groups++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static my_bool _mariadb_read_options_from_file(MYSQL *mysql,
|
||||
const char *config_file,
|
||||
const char *group,
|
||||
unsigned int recursion)
|
||||
{
|
||||
uint line=0;
|
||||
my_bool read_values= 0, found_group= 0, is_escaped= 0, is_quoted= 0;
|
||||
char buff[4096],*ptr,*end,*value, *key= 0, *optval;
|
||||
MA_FILE *file= NULL;
|
||||
my_bool rc= 1;
|
||||
const char *groups[5]= {"client",
|
||||
"client-server",
|
||||
"client-mariadb",
|
||||
group,
|
||||
NULL};
|
||||
my_bool (*set_option)(MYSQL *mysql, const char *config_option, const char *config_value);
|
||||
|
||||
|
||||
/* if a plugin registered a hook we will call this hook, otherwise
|
||||
* default (_mariadb_set_conf_option) will be called */
|
||||
if (mysql->options.extension && mysql->options.extension->set_option)
|
||||
set_option= mysql->options.extension->set_option;
|
||||
else
|
||||
set_option= _mariadb_set_conf_option;
|
||||
|
||||
if (!(file = ma_open(config_file, "r", NULL)))
|
||||
goto err;
|
||||
|
||||
while (ma_gets(buff,sizeof(buff)-1,file))
|
||||
{
|
||||
line++;
|
||||
key= 0;
|
||||
/* Ignore comment and empty lines */
|
||||
for (ptr=buff ; isspace(*ptr) ; ptr++ );
|
||||
if (!is_escaped && (*ptr == '\"' || *ptr== '\''))
|
||||
{
|
||||
is_quoted= !is_quoted;
|
||||
continue;
|
||||
}
|
||||
/* CONC- 327: !includedir and !include */
|
||||
if (*ptr == '!')
|
||||
{
|
||||
char *val;
|
||||
ptr++;
|
||||
if (!(val= strchr(ptr, ' ')))
|
||||
continue;
|
||||
*val++= 0;
|
||||
end= strchr(val, 0);
|
||||
for ( ; isspace(end[-1]) ; end--) ; /* Remove end space */
|
||||
*end= 0;
|
||||
if (!strcmp(ptr, "includedir"))
|
||||
_mariadb_read_options(mysql, (const char *)val, NULL, group, recursion + 1);
|
||||
else if (!strcmp(ptr, "include"))
|
||||
_mariadb_read_options(mysql, NULL, (const char *)val, group, recursion + 1);
|
||||
continue;
|
||||
}
|
||||
if (*ptr == '#' || *ptr == ';' || !*ptr)
|
||||
continue;
|
||||
is_escaped= (*ptr == '\\');
|
||||
if (*ptr == '[') /* Group name */
|
||||
{
|
||||
found_group=1;
|
||||
if (!(end=(char *) strchr(++ptr,']')))
|
||||
{
|
||||
/* todo: set error */
|
||||
goto err;
|
||||
}
|
||||
for ( ; isspace(end[-1]) ; end--) ; /* Remove end space */
|
||||
end[0]=0;
|
||||
read_values= is_group(ptr, groups);
|
||||
continue;
|
||||
}
|
||||
if (!found_group)
|
||||
{
|
||||
/* todo: set error */
|
||||
goto err;
|
||||
}
|
||||
if (!read_values)
|
||||
continue;
|
||||
if (!(end=value=strchr(ptr,'=')))
|
||||
{
|
||||
end=strchr(ptr, '\0'); /* Option without argument */
|
||||
set_option(mysql, ptr, NULL);
|
||||
}
|
||||
if (!key)
|
||||
key= ptr;
|
||||
for ( ; isspace(end[-1]) ; end--) ;
|
||||
*end= 0;
|
||||
if (value)
|
||||
{
|
||||
/* Remove pre- and end space */
|
||||
char *value_end;
|
||||
*value= 0;
|
||||
value++;
|
||||
ptr= value;
|
||||
for ( ; isspace(*value); value++) ;
|
||||
value_end=strchr(value, '\0');
|
||||
*value_end= 0;
|
||||
optval= ptr;
|
||||
for ( ; isspace(value_end[-1]) ; value_end--) ;
|
||||
/* remove possible quotes */
|
||||
if (*value == '\'' || *value == '\"')
|
||||
{
|
||||
value++;
|
||||
if (value_end[-1] == '\'' || value_end[-1] == '\"')
|
||||
value_end--;
|
||||
}
|
||||
if (value_end < value) /* Empty string */
|
||||
value_end=value;
|
||||
for ( ; value != value_end; value++)
|
||||
{
|
||||
if (*value == '\\' && value != value_end-1)
|
||||
{
|
||||
switch(*++value) {
|
||||
case 'n':
|
||||
*ptr++='\n';
|
||||
break;
|
||||
case 't':
|
||||
*ptr++= '\t';
|
||||
break;
|
||||
case 'r':
|
||||
*ptr++ = '\r';
|
||||
break;
|
||||
case 'b':
|
||||
*ptr++ = '\b';
|
||||
break;
|
||||
case 's':
|
||||
*ptr++= ' '; /* space */
|
||||
break;
|
||||
case '\"':
|
||||
*ptr++= '\"';
|
||||
break;
|
||||
case '\'':
|
||||
*ptr++= '\'';
|
||||
break;
|
||||
case '\\':
|
||||
*ptr++= '\\';
|
||||
break;
|
||||
default: /* Unknown; Keep '\' */
|
||||
*ptr++= '\\';
|
||||
*ptr++= *value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
*ptr++= *value;
|
||||
}
|
||||
*ptr=0;
|
||||
set_option(mysql, key, optval);
|
||||
key= optval= 0;
|
||||
}
|
||||
}
|
||||
rc= 0;
|
||||
|
||||
err:
|
||||
if (file)
|
||||
ma_close(file);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
my_bool _mariadb_read_options(MYSQL *mysql,
|
||||
const char *config_dir,
|
||||
const char *config_file,
|
||||
const char *group,
|
||||
unsigned int recursion)
|
||||
{
|
||||
int i= 0,
|
||||
exts,
|
||||
errors= 0;
|
||||
char filename[FN_REFLEN + 1];
|
||||
unsigned int recursion_stop= 64;
|
||||
#ifndef _WIN32
|
||||
char *env;
|
||||
#endif
|
||||
|
||||
if (recursion >= recursion_stop)
|
||||
return 1;
|
||||
|
||||
if (config_file && config_file[0])
|
||||
return _mariadb_read_options_from_file(mysql, config_file, group, recursion);
|
||||
|
||||
if (config_dir && config_dir[0])
|
||||
{
|
||||
for (exts= 0; ini_exts[exts]; exts++)
|
||||
{
|
||||
snprintf(filename, FN_REFLEN,
|
||||
"%s%cmy.%s", config_dir, FN_LIBCHAR, ini_exts[exts]);
|
||||
if (!access(filename, R_OK))
|
||||
errors+= _mariadb_read_options_from_file(mysql, filename, group, recursion);
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
for (i=0; i < MAX_CONFIG_DIRS && configuration_dirs[i]; i++)
|
||||
{
|
||||
for (exts= 0; ini_exts[exts]; exts++)
|
||||
{
|
||||
snprintf(filename, FN_REFLEN,
|
||||
"%s%cmy.%s", configuration_dirs[i], FN_LIBCHAR, ini_exts[exts]);
|
||||
if (!access(filename, R_OK))
|
||||
errors+= _mariadb_read_options_from_file(mysql, filename, group, recursion);
|
||||
}
|
||||
}
|
||||
#ifndef _WIN32
|
||||
/* special case: .my.cnf in Home directory */
|
||||
if ((env= getenv("HOME")))
|
||||
{
|
||||
for (exts= 0; ini_exts[exts]; exts++)
|
||||
{
|
||||
snprintf(filename, FN_REFLEN,
|
||||
"%s%c.my.%s", env, FN_LIBCHAR, ini_exts[exts]);
|
||||
if (!access(filename, R_OK))
|
||||
errors+= _mariadb_read_options_from_file(mysql, filename, group, recursion);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return errors;
|
||||
}
|
1924
vendor/MDBC/libmariadb/ma_dtoa.c
vendored
Normal file
1924
vendor/MDBC/libmariadb/ma_dtoa.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
116
vendor/MDBC/libmariadb/ma_errmsg.c
vendored
Normal file
116
vendor/MDBC/libmariadb/ma_errmsg.c
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
/* 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., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02111-1301, USA */
|
||||
|
||||
/* Error messages for clients */
|
||||
/* error messages for the demon is in share/language/errmsg.sys */
|
||||
|
||||
#include <ma_global.h>
|
||||
#include <ma_sys.h>
|
||||
#include "errmsg.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
const char *SQLSTATE_UNKNOWN= "HY000";
|
||||
|
||||
const char *client_errors[]=
|
||||
{
|
||||
/* 2000 */ "Unknown error",
|
||||
/* 2001 */ "Can't create UNIX socket (%d)",
|
||||
/* 2002 */ "Can't connect to local server through socket '%-.64s' (%d)",
|
||||
/* 2003 */ "Can't connect to server on '%-.64s' (%d)",
|
||||
/* 2004 */ "Can't create TCP/IP socket (%d)",
|
||||
/* 2005 */ "Unknown server host '%-.100s' (%d)",
|
||||
/* 2006 */ "Server has gone away",
|
||||
/* 2007 */ "Protocol mismatch. Server Version = %d Client Version = %d",
|
||||
/* 2008 */ "Client run out of memory",
|
||||
/* 2009 */ "Wrong host info",
|
||||
/* 2010 */ "Localhost via UNIX socket",
|
||||
/* 2011 */ "%-.64s via TCP/IP",
|
||||
/* 2012 */ "Error in server handshake",
|
||||
/* 2013 */ "Lost connection to server during query",
|
||||
/* 2014 */ "Commands out of sync; you can't run this command now",
|
||||
/* 2015 */ "%-.64s via named pipe",
|
||||
/* 2016 */ "Can't wait for named pipe to host: %-.64s pipe: %-.32s (%lu)",
|
||||
/* 2017 */ "Can't open named pipe to host: %-.64s pipe: %-.32s (%lu)",
|
||||
/* 2018 */ "Can't set state of named pipe to host: %-.64s pipe: %-.32s (%lu)",
|
||||
/* 2019 */ "Can't initialize character set %-.64s (path: %-.64s)",
|
||||
/* 2020 */ "Got packet bigger than 'max_allowed_packet'",
|
||||
/* 2021 */ "",
|
||||
/* 2022 */ "",
|
||||
/* 2023 */ "",
|
||||
/* 2024 */ "",
|
||||
/* 2025 */ "",
|
||||
/* 2026 */ "SSL connection error: %-.100s",
|
||||
/* 2027 */ "Received malformed packet",
|
||||
/* 2028 */ "",
|
||||
/* 2029 */ "",
|
||||
/* 2030 */ "Statement is not prepared",
|
||||
/* 2031 */ "No data supplied for parameters in prepared statement",
|
||||
/* 2032 */ "Data truncated",
|
||||
/* 2033 */ "",
|
||||
/* 2034 */ "Invalid parameter number",
|
||||
/* 2035 */ "Invalid buffer type: %d (parameter: %d)",
|
||||
/* 2036 */ "Buffer type is not supported",
|
||||
/* 2037 */ "Shared memory: %-.64s",
|
||||
/* 2038 */ "Shared memory connection failed during %s. (%lu)",
|
||||
/* 2039 */ "",
|
||||
/* 2040 */ "",
|
||||
/* 2041 */ "",
|
||||
/* 2042 */ "",
|
||||
/* 2043 */ "",
|
||||
/* 2044 */ "",
|
||||
/* 2045 */ "",
|
||||
/* 2046 */ "",
|
||||
/* 2047 */ "Wrong or unknown protocol",
|
||||
/* 2048 */ "",
|
||||
/* 2049 */ "Connection with old authentication protocol refused.",
|
||||
/* 2050 */ "",
|
||||
/* 2051 */ "",
|
||||
/* 2052 */ "Prepared statement contains no metadata",
|
||||
/* 2053 */ "",
|
||||
/* 2054 */ "This feature is not implemented or disabled",
|
||||
/* 2055 */ "Lost connection to server at '%s', system error: %d",
|
||||
/* 2056 */ "Server closed statement due to a prior %s function call",
|
||||
/* 2057 */ "The number of parameters in bound buffers differs from number of columns in resultset",
|
||||
/* 2059 */ "Can't connect twice. Already connected",
|
||||
/* 2058 */ "Plugin %s could not be loaded: %s",
|
||||
/* 2059 */ "An attribute with same name already exists",
|
||||
/* 2060 */ "Plugin doesn't support this function",
|
||||
""
|
||||
};
|
||||
|
||||
const char *mariadb_client_errors[] =
|
||||
{
|
||||
/* 5000 */ "Creating an event failed (Errorcode: %d)",
|
||||
/* 5001 */ "Bind to local interface '-.%64s' failed (Errorcode: %d)",
|
||||
/* 5002 */ "Connection type doesn't support asynchronous IO operations",
|
||||
/* 5003 */ "Server doesn't support function '%s'",
|
||||
/* 5004 */ "File '%s' not found (Errcode: %d)",
|
||||
/* 5005 */ "Error reading file '%s' (Errcode: %d)",
|
||||
/* 5006 */ "Bulk operation without parameters is not supported",
|
||||
/* 5007 */ "Invalid statement handle",
|
||||
/* 5008 */ "Unsupported version %d. Supported versions are in the range %d - %d",
|
||||
""
|
||||
};
|
||||
|
||||
const char ** NEAR my_errmsg[MAXMAPS]={0,0,0,0};
|
||||
char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE];
|
||||
|
||||
void init_client_errs(void)
|
||||
{
|
||||
my_errmsg[CLIENT_ERRMAP] = &client_errors[0];
|
||||
}
|
||||
|
580
vendor/MDBC/libmariadb/ma_hashtbl.c
vendored
Normal file
580
vendor/MDBC/libmariadb/ma_hashtbl.c
vendored
Normal file
@ -0,0 +1,580 @@
|
||||
/************************************************************************************
|
||||
Copyright (C) 2000, 2012 MySQL AB & MySQL Finland AB & TCX DataKonsult AB,
|
||||
Monty Program AB, 2016 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
|
||||
*************************************************************************************/
|
||||
|
||||
/* The hash functions used for saving keys */
|
||||
/* One of key_length or key_length_offset must be given */
|
||||
/* Key length of 0 isn't allowed */
|
||||
|
||||
#include <ma_global.h>
|
||||
#include <ma_sys.h>
|
||||
#include <ma_string.h>
|
||||
#include <mariadb_ctype.h>
|
||||
#include "ma_hashtbl.h"
|
||||
|
||||
#define NO_RECORD ((uint) -1)
|
||||
#define LOWFIND 1
|
||||
#define LOWUSED 2
|
||||
#define HIGHFIND 4
|
||||
#define HIGHUSED 8
|
||||
|
||||
static uint hash_mask(uint hashnr,uint buffmax,uint maxlength);
|
||||
static void movelink(MA_HASHTBL_LINK *array,uint pos,uint next_link,uint newlink);
|
||||
static uint calc_hashnr(const uchar *key,uint length);
|
||||
static uint calc_hashnr_caseup(const uchar *key,uint length);
|
||||
static int hashcmp(MA_HASHTBL *hash,MA_HASHTBL_LINK *pos,const uchar *key,uint length);
|
||||
|
||||
|
||||
my_bool _ma_hashtbl_init(MA_HASHTBL *hash,uint size,uint key_offset,uint key_length,
|
||||
hash_get_key get_key,
|
||||
void (*free_element)(void*),uint flags CALLER_INFO_PROTO)
|
||||
{
|
||||
hash->records=0;
|
||||
if (ma_init_dynamic_array_ci(&hash->array,sizeof(MA_HASHTBL_LINK),size,0))
|
||||
{
|
||||
hash->free=0; /* Allow call to hash_free */
|
||||
return(TRUE);
|
||||
}
|
||||
hash->key_offset=key_offset;
|
||||
hash->key_length=key_length;
|
||||
hash->blength=1;
|
||||
hash->current_record= NO_RECORD; /* For the future */
|
||||
hash->get_key=get_key;
|
||||
hash->free=free_element;
|
||||
hash->flags=flags;
|
||||
if (flags & MA_HASHTBL_CASE_INSENSITIVE)
|
||||
hash->calc_hashnr=calc_hashnr_caseup;
|
||||
else
|
||||
hash->calc_hashnr=calc_hashnr;
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
void ma_hashtbl_free(MA_HASHTBL *hash)
|
||||
{
|
||||
if (hash->free)
|
||||
{
|
||||
uint i,records;
|
||||
MA_HASHTBL_LINK *data=dynamic_element(&hash->array,0,MA_HASHTBL_LINK*);
|
||||
for (i=0,records=hash->records ; i < records ; i++)
|
||||
(*hash->free)(data[i].data);
|
||||
hash->free=0;
|
||||
}
|
||||
ma_delete_dynamic(&hash->array);
|
||||
hash->records=0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* some helper functions */
|
||||
|
||||
/*
|
||||
This function is char* instead of uchar* as HPUX11 compiler can't
|
||||
handle inline functions that are not defined as native types
|
||||
*/
|
||||
|
||||
static inline char*
|
||||
hash_key(MA_HASHTBL *hash,const uchar *record,uint *length,my_bool first)
|
||||
{
|
||||
if (hash->get_key)
|
||||
return (char *)(*hash->get_key)(record,(uint *)length,first);
|
||||
*length=hash->key_length;
|
||||
return (char*) record+hash->key_offset;
|
||||
}
|
||||
|
||||
/* Calculate pos according to keys */
|
||||
|
||||
static uint hash_mask(uint hashnr,uint buffmax,uint maxlength)
|
||||
{
|
||||
if ((hashnr & (buffmax-1)) < maxlength) return (hashnr & (buffmax-1));
|
||||
return (hashnr & ((buffmax >> 1) -1));
|
||||
}
|
||||
|
||||
static uint hash_rec_mask(MA_HASHTBL *hash,MA_HASHTBL_LINK *pos,uint buffmax,
|
||||
uint maxlength)
|
||||
{
|
||||
uint length;
|
||||
uchar *key= (uchar*) hash_key(hash,pos->data,&length,0);
|
||||
return hash_mask((*hash->calc_hashnr)(key,length),buffmax,maxlength);
|
||||
}
|
||||
|
||||
#ifndef NEW_MA_HASHTBL_FUNCTION
|
||||
|
||||
/* Calc hashvalue for a key */
|
||||
|
||||
static uint calc_hashnr(const uchar *key,uint length)
|
||||
{
|
||||
register uint nr=1, nr2=4;
|
||||
while (length--)
|
||||
{
|
||||
nr^= (((nr & 63)+nr2)*((uint) (uchar) *key++))+ (nr << 8);
|
||||
nr2+=3;
|
||||
}
|
||||
return((uint) nr);
|
||||
}
|
||||
|
||||
/* Calc hashvalue for a key, case independently */
|
||||
|
||||
static uint calc_hashnr_caseup(const uchar *key,uint length)
|
||||
{
|
||||
register uint nr=1, nr2=4;
|
||||
while (length--)
|
||||
{
|
||||
nr^= (((nr & 63)+nr2)*((uint) (uchar) toupper(*key++)))+ (nr << 8);
|
||||
nr2+=3;
|
||||
}
|
||||
return((uint) nr);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Fowler/Noll/Vo hash
|
||||
*
|
||||
* The basis of the hash algorithm was taken from an idea sent by email to the
|
||||
* IEEE Posix P1003.2 mailing list from Phong Vo (kpv@research.att.com) and
|
||||
* Glenn Fowler (gsf@research.att.com). Landon Curt Noll (chongo@toad.com)
|
||||
* later improved on their algorithm.
|
||||
*
|
||||
* The magic is in the interesting relationship between the special prime
|
||||
* 16777619 (2^24 + 403) and 2^32 and 2^8.
|
||||
*
|
||||
* This hash produces the fewest collisions of any function that we've seen so
|
||||
* far, and works well on both numbers and strings.
|
||||
*/
|
||||
|
||||
uint calc_hashnr(const uchar *key, uint len)
|
||||
{
|
||||
const uchar *end=key+len;
|
||||
uint hash;
|
||||
for (hash = 0; key < end; key++)
|
||||
{
|
||||
hash *= 16777619;
|
||||
hash ^= (uint) *(uchar*) key;
|
||||
}
|
||||
return (hash);
|
||||
}
|
||||
|
||||
uint calc_hashnr_caseup(const uchar *key, uint len)
|
||||
{
|
||||
const uchar *end=key+len;
|
||||
uint hash;
|
||||
for (hash = 0; key < end; key++)
|
||||
{
|
||||
hash *= 16777619;
|
||||
hash ^= (uint) (uchar) toupper(*key);
|
||||
}
|
||||
return (hash);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef __SUNPRO_C /* SUNPRO can't handle this */
|
||||
static inline
|
||||
#endif
|
||||
unsigned int rec_hashnr(MA_HASHTBL *hash,const uchar *record)
|
||||
{
|
||||
uint length;
|
||||
uchar *key= (uchar*) hash_key(hash,record,&length,0);
|
||||
return (*hash->calc_hashnr)(key,length);
|
||||
}
|
||||
|
||||
|
||||
/* Search after a record based on a key */
|
||||
/* Sets info->current_ptr to found record */
|
||||
|
||||
void* ma_hashtbl_search(MA_HASHTBL *hash,const uchar *key,uint length)
|
||||
{
|
||||
MA_HASHTBL_LINK *pos;
|
||||
uint flag,idx;
|
||||
|
||||
flag=1;
|
||||
if (hash->records)
|
||||
{
|
||||
idx=hash_mask((*hash->calc_hashnr)(key,length ? length :
|
||||
hash->key_length),
|
||||
hash->blength,hash->records);
|
||||
do
|
||||
{
|
||||
pos= dynamic_element(&hash->array,idx,MA_HASHTBL_LINK*);
|
||||
if (!hashcmp(hash,pos,key,length))
|
||||
{
|
||||
hash->current_record= idx;
|
||||
return (pos->data);
|
||||
}
|
||||
if (flag)
|
||||
{
|
||||
flag=0; /* Reset flag */
|
||||
if (hash_rec_mask(hash,pos,hash->blength,hash->records) != idx)
|
||||
break; /* Wrong link */
|
||||
}
|
||||
}
|
||||
while ((idx=pos->next) != NO_RECORD);
|
||||
}
|
||||
hash->current_record= NO_RECORD;
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* Get next record with identical key */
|
||||
/* Can only be called if previous calls was hash_search */
|
||||
|
||||
void *ma_hashtbl_next(MA_HASHTBL *hash,const uchar *key,uint length)
|
||||
{
|
||||
MA_HASHTBL_LINK *pos;
|
||||
uint idx;
|
||||
|
||||
if (hash->current_record != NO_RECORD)
|
||||
{
|
||||
MA_HASHTBL_LINK *data=dynamic_element(&hash->array,0,MA_HASHTBL_LINK*);
|
||||
for (idx=data[hash->current_record].next; idx != NO_RECORD ; idx=pos->next)
|
||||
{
|
||||
pos=data+idx;
|
||||
if (!hashcmp(hash,pos,key,length))
|
||||
{
|
||||
hash->current_record= idx;
|
||||
return pos->data;
|
||||
}
|
||||
}
|
||||
hash->current_record=NO_RECORD;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Change link from pos to new_link */
|
||||
|
||||
static void movelink(MA_HASHTBL_LINK *array,uint find,uint next_link,uint newlink)
|
||||
{
|
||||
MA_HASHTBL_LINK *old_link;
|
||||
do
|
||||
{
|
||||
old_link=array+next_link;
|
||||
}
|
||||
while ((next_link=old_link->next) != find);
|
||||
old_link->next= newlink;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Compare a key in a record to a whole key. Return 0 if identical */
|
||||
|
||||
static int hashcmp(MA_HASHTBL *hash,MA_HASHTBL_LINK *pos,const uchar *key,uint length)
|
||||
{
|
||||
uint rec_keylength;
|
||||
uchar *rec_key= (uchar*) hash_key(hash,pos->data,&rec_keylength,1);
|
||||
return (length && length != rec_keylength) ||
|
||||
memcmp(rec_key,key,rec_keylength);
|
||||
}
|
||||
|
||||
|
||||
/* Write a hash-key to the hash-index */
|
||||
|
||||
my_bool ma_hashtbl_insert(MA_HASHTBL *info,const uchar *record)
|
||||
{
|
||||
int flag;
|
||||
uint halfbuff,hash_nr,first_index,idx;
|
||||
uchar *ptr_to_rec= NULL,*ptr_to_rec2= NULL;
|
||||
MA_HASHTBL_LINK *data,*empty,*gpos= NULL,*gpos2 = NULL,*pos;
|
||||
|
||||
LINT_INIT(gpos); LINT_INIT(gpos2);
|
||||
LINT_INIT(ptr_to_rec); LINT_INIT(ptr_to_rec2);
|
||||
|
||||
flag=0;
|
||||
if (!(empty=(MA_HASHTBL_LINK*) ma_alloc_dynamic(&info->array)))
|
||||
return(TRUE); /* No more memory */
|
||||
|
||||
info->current_record= NO_RECORD;
|
||||
data=dynamic_element(&info->array,0,MA_HASHTBL_LINK*);
|
||||
halfbuff= info->blength >> 1;
|
||||
|
||||
idx=first_index=info->records-halfbuff;
|
||||
if (idx != info->records) /* If some records */
|
||||
{
|
||||
do
|
||||
{
|
||||
pos=data+idx;
|
||||
hash_nr=rec_hashnr(info,pos->data);
|
||||
if (flag == 0) /* First loop; Check if ok */
|
||||
if (hash_mask(hash_nr,info->blength,info->records) != first_index)
|
||||
break;
|
||||
if (!(hash_nr & halfbuff))
|
||||
{ /* Key will not move */
|
||||
if (!(flag & LOWFIND))
|
||||
{
|
||||
if (flag & HIGHFIND)
|
||||
{
|
||||
flag=LOWFIND | HIGHFIND;
|
||||
/* key shall be moved to the current empty position */
|
||||
gpos=empty;
|
||||
ptr_to_rec=pos->data;
|
||||
empty=pos; /* This place is now free */
|
||||
}
|
||||
else
|
||||
{
|
||||
flag=LOWFIND | LOWUSED; /* key isn't changed */
|
||||
gpos=pos;
|
||||
ptr_to_rec=pos->data;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(flag & LOWUSED))
|
||||
{
|
||||
/* Change link of previous LOW-key */
|
||||
gpos->data=ptr_to_rec;
|
||||
gpos->next=(uint) (pos-data);
|
||||
flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
|
||||
}
|
||||
gpos=pos;
|
||||
ptr_to_rec=pos->data;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ /* key will be moved */
|
||||
if (!(flag & HIGHFIND))
|
||||
{
|
||||
flag= (flag & LOWFIND) | HIGHFIND;
|
||||
/* key shall be moved to the last (empty) position */
|
||||
gpos2 = empty; empty=pos;
|
||||
ptr_to_rec2=pos->data;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(flag & HIGHUSED))
|
||||
{
|
||||
/* Change link of previous hash-key and save */
|
||||
gpos2->data=ptr_to_rec2;
|
||||
gpos2->next=(uint) (pos-data);
|
||||
flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
|
||||
}
|
||||
gpos2=pos;
|
||||
ptr_to_rec2=pos->data;
|
||||
}
|
||||
}
|
||||
}
|
||||
while ((idx=pos->next) != NO_RECORD);
|
||||
|
||||
if ((flag & (LOWFIND | LOWUSED)) == LOWFIND)
|
||||
{
|
||||
gpos->data=ptr_to_rec;
|
||||
gpos->next=NO_RECORD;
|
||||
}
|
||||
if ((flag & (HIGHFIND | HIGHUSED)) == HIGHFIND)
|
||||
{
|
||||
gpos2->data=ptr_to_rec2;
|
||||
gpos2->next=NO_RECORD;
|
||||
}
|
||||
}
|
||||
/* Check if we are at the empty position */
|
||||
|
||||
idx=hash_mask(rec_hashnr(info,record),info->blength,info->records+1);
|
||||
pos=data+idx;
|
||||
if (pos == empty)
|
||||
{
|
||||
pos->data=(uchar*) record;
|
||||
pos->next=NO_RECORD;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Check if more records in same hash-nr family */
|
||||
empty[0]=pos[0];
|
||||
gpos=data+hash_rec_mask(info,pos,info->blength,info->records+1);
|
||||
if (pos == gpos)
|
||||
{
|
||||
pos->data=(uchar*) record;
|
||||
pos->next=(uint) (empty - data);
|
||||
}
|
||||
else
|
||||
{
|
||||
pos->data=(uchar*) record;
|
||||
pos->next=NO_RECORD;
|
||||
movelink(data,(uint) (pos-data),(uint) (gpos-data),(uint) (empty-data));
|
||||
}
|
||||
}
|
||||
if (++info->records == info->blength)
|
||||
info->blength+= info->blength;
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
** Remove one record from hash-table. The record with the same record
|
||||
** ptr is removed.
|
||||
** if there is a free-function it's called for record if found
|
||||
******************************************************************************/
|
||||
|
||||
my_bool ma_hashtbl_delete(MA_HASHTBL *hash,uchar *record)
|
||||
{
|
||||
uint blength,pos2,pos_hashnr,lastpos_hashnr,idx,empty_index;
|
||||
MA_HASHTBL_LINK *data,*lastpos,*gpos,*pos,*pos3,*empty;
|
||||
if (!hash->records)
|
||||
return(1);
|
||||
|
||||
blength=hash->blength;
|
||||
data=dynamic_element(&hash->array,0,MA_HASHTBL_LINK*);
|
||||
/* Search after record with key */
|
||||
pos=data+ hash_mask(rec_hashnr(hash,record),blength,hash->records);
|
||||
gpos = 0;
|
||||
|
||||
while (pos->data != record)
|
||||
{
|
||||
gpos=pos;
|
||||
if (pos->next == NO_RECORD)
|
||||
return(1); /* Key not found */
|
||||
pos=data+pos->next;
|
||||
}
|
||||
|
||||
if ( --(hash->records) < hash->blength >> 1) hash->blength>>=1;
|
||||
hash->current_record= NO_RECORD;
|
||||
lastpos=data+hash->records;
|
||||
|
||||
/* Remove link to record */
|
||||
empty=pos; empty_index=(uint) (empty-data);
|
||||
if (gpos)
|
||||
gpos->next=pos->next; /* unlink current ptr */
|
||||
else if (pos->next != NO_RECORD)
|
||||
{
|
||||
empty=data+(empty_index=pos->next);
|
||||
pos->data=empty->data;
|
||||
pos->next=empty->next;
|
||||
}
|
||||
|
||||
if (empty == lastpos) /* last key at wrong pos or no next link */
|
||||
goto exit;
|
||||
|
||||
/* Move the last key (lastpos) */
|
||||
lastpos_hashnr=rec_hashnr(hash,lastpos->data);
|
||||
/* pos is where lastpos should be */
|
||||
pos=data+hash_mask(lastpos_hashnr,hash->blength,hash->records);
|
||||
if (pos == empty) /* Move to empty position. */
|
||||
{
|
||||
empty[0]=lastpos[0];
|
||||
goto exit;
|
||||
}
|
||||
pos_hashnr=rec_hashnr(hash,pos->data);
|
||||
/* pos3 is where the pos should be */
|
||||
pos3= data+hash_mask(pos_hashnr,hash->blength,hash->records);
|
||||
if (pos != pos3)
|
||||
{ /* pos is on wrong posit */
|
||||
empty[0]=pos[0]; /* Save it here */
|
||||
pos[0]=lastpos[0]; /* This should be here */
|
||||
movelink(data,(uint) (pos-data),(uint) (pos3-data),empty_index);
|
||||
goto exit;
|
||||
}
|
||||
pos2= hash_mask(lastpos_hashnr,blength,hash->records+1);
|
||||
if (pos2 == hash_mask(pos_hashnr,blength,hash->records+1))
|
||||
{ /* Identical key-positions */
|
||||
if (pos2 != hash->records)
|
||||
{
|
||||
empty[0]=lastpos[0];
|
||||
movelink(data,(uint) (lastpos-data),(uint) (pos-data),empty_index);
|
||||
goto exit;
|
||||
}
|
||||
idx= (uint) (pos-data); /* Link pos->next after lastpos */
|
||||
}
|
||||
else idx= NO_RECORD; /* Different positions merge */
|
||||
|
||||
empty[0]=lastpos[0];
|
||||
movelink(data,idx,empty_index,pos->next);
|
||||
pos->next=empty_index;
|
||||
|
||||
exit:
|
||||
ma_pop_dynamic(&hash->array);
|
||||
if (hash->free)
|
||||
(*hash->free)((uchar*) record);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
Update keys when record has changed.
|
||||
This is much more efficient than using a delete & insert.
|
||||
*/
|
||||
|
||||
my_bool ma_hashtbl_update(MA_HASHTBL *hash,uchar *record,uchar *old_key,uint old_key_length)
|
||||
{
|
||||
uint idx,new_index,new_pos_index,blength,records,empty;
|
||||
MA_HASHTBL_LINK org_link,*data,*previous,*pos;
|
||||
|
||||
data=dynamic_element(&hash->array,0,MA_HASHTBL_LINK*);
|
||||
blength=hash->blength; records=hash->records;
|
||||
|
||||
/* Search after record with key */
|
||||
|
||||
idx=hash_mask((*hash->calc_hashnr)(old_key,(old_key_length ?
|
||||
old_key_length :
|
||||
hash->key_length)),
|
||||
blength,records);
|
||||
new_index=hash_mask(rec_hashnr(hash,record),blength,records);
|
||||
if (idx == new_index)
|
||||
return(0); /* Nothing to do (No record check) */
|
||||
previous=0;
|
||||
for (;;)
|
||||
{
|
||||
|
||||
if ((pos= data+idx)->data == record)
|
||||
break;
|
||||
previous=pos;
|
||||
if ((idx=pos->next) == NO_RECORD)
|
||||
return(1); /* Not found in links */
|
||||
}
|
||||
hash->current_record= NO_RECORD;
|
||||
org_link= *pos;
|
||||
empty=idx;
|
||||
|
||||
/* Relink record from current chain */
|
||||
|
||||
if (!previous)
|
||||
{
|
||||
if (pos->next != NO_RECORD)
|
||||
{
|
||||
empty=pos->next;
|
||||
*pos= data[pos->next];
|
||||
}
|
||||
}
|
||||
else
|
||||
previous->next=pos->next; /* unlink pos */
|
||||
|
||||
/* Move data to correct position */
|
||||
pos=data+new_index;
|
||||
new_pos_index=hash_rec_mask(hash,pos,blength,records);
|
||||
if (new_index != new_pos_index)
|
||||
{ /* Other record in wrong position */
|
||||
data[empty] = *pos;
|
||||
movelink(data,new_index,new_pos_index,empty);
|
||||
org_link.next=NO_RECORD;
|
||||
data[new_index]= org_link;
|
||||
}
|
||||
else
|
||||
{ /* Link in chain at right position */
|
||||
org_link.next=data[new_index].next;
|
||||
data[empty]=org_link;
|
||||
data[new_index].next=empty;
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
uchar *ma_hashtbl_element(MA_HASHTBL *hash,uint idx)
|
||||
{
|
||||
if (idx < hash->records)
|
||||
return dynamic_element(&hash->array,idx,MA_HASHTBL_LINK*)->data;
|
||||
return 0;
|
||||
}
|
87
vendor/MDBC/libmariadb/ma_init.c
vendored
Normal file
87
vendor/MDBC/libmariadb/ma_init.c
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
2016 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, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02111-1301, USA */
|
||||
|
||||
#include <ma_global.h>
|
||||
#include <ma_sys.h>
|
||||
#include "mariadb_ctype.h"
|
||||
#include <ma_string.h>
|
||||
#include <mariadb_ctype.h>
|
||||
#include <signal.h>
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
#include <locale.h>
|
||||
#include <crtdbg.h>
|
||||
#endif
|
||||
static my_bool my_win_init(void);
|
||||
#else
|
||||
#define my_win_init()
|
||||
#endif
|
||||
|
||||
my_bool ma_init_done=0;
|
||||
|
||||
|
||||
|
||||
/* Init ma_sys functions and ma_sys variabels */
|
||||
|
||||
void ma_init(void)
|
||||
{
|
||||
if (ma_init_done)
|
||||
return;
|
||||
ma_init_done=1;
|
||||
{
|
||||
#ifdef _WIN32
|
||||
my_win_init();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
} /* ma_init */
|
||||
|
||||
|
||||
|
||||
void ma_end(int infoflag __attribute__((unused)))
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSACleanup( );
|
||||
#endif /* _WIN32 */
|
||||
ma_init_done=0;
|
||||
} /* ma_end */
|
||||
|
||||
#ifdef _WIN32
|
||||
static my_bool my_win_init()
|
||||
{
|
||||
WORD VersionRequested;
|
||||
int err;
|
||||
WSADATA WsaData;
|
||||
const unsigned int MajorVersion=2,
|
||||
MinorVersion=2;
|
||||
VersionRequested= MAKEWORD(MajorVersion, MinorVersion);
|
||||
/* Load WinSock library */
|
||||
if ((err= WSAStartup(VersionRequested, &WsaData)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
/* make sure 2.2 or higher is supported */
|
||||
if ((LOBYTE(WsaData.wVersion) * 10 + HIBYTE(WsaData.wVersion)) < 22)
|
||||
{
|
||||
WSACleanup();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
224
vendor/MDBC/libmariadb/ma_io.c
vendored
Normal file
224
vendor/MDBC/libmariadb/ma_io.c
vendored
Normal file
@ -0,0 +1,224 @@
|
||||
/*
|
||||
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, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02111-1301, USA
|
||||
*/
|
||||
|
||||
#include <ma_global.h>
|
||||
#include <ma_sys.h>
|
||||
#include <errmsg.h>
|
||||
#include <mysql.h>
|
||||
#include <mysql/client_plugin.h>
|
||||
#include <mariadb/ma_io.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef HAVE_REMOTEIO
|
||||
struct st_mysql_client_plugin_REMOTEIO *rio_plugin= NULL;
|
||||
#endif
|
||||
|
||||
/* {{{ ma_open */
|
||||
MA_FILE *ma_open(const char *location, const char *mode, MYSQL *mysql)
|
||||
{
|
||||
int CodePage= -1;
|
||||
FILE *fp= NULL;
|
||||
MA_FILE *ma_file= NULL;
|
||||
|
||||
if (!location || !location[0])
|
||||
return NULL;
|
||||
#ifdef HAVE_REMOTEIO
|
||||
if (strstr(location, "://"))
|
||||
goto remote;
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
if (mysql && mysql->charset)
|
||||
CodePage= madb_get_windows_cp(mysql->charset->csname);
|
||||
#endif
|
||||
if (CodePage == -1)
|
||||
{
|
||||
if (!(fp= fopen(location, mode)))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#ifdef _WIN32
|
||||
/* See CONC-44: we need to support non ascii filenames too, so we convert
|
||||
current character set to wchar_t and try to open the file via _wsopen */
|
||||
else
|
||||
{
|
||||
wchar_t *w_filename= NULL;
|
||||
wchar_t *w_mode= NULL;
|
||||
int len;
|
||||
DWORD Length;
|
||||
|
||||
len= MultiByteToWideChar(CodePage, 0, location, (int)strlen(location), NULL, 0);
|
||||
if (!len)
|
||||
return NULL;
|
||||
if (!(w_filename= (wchar_t *)calloc(1, (len + 1) * sizeof(wchar_t))))
|
||||
{
|
||||
my_set_error(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
|
||||
return NULL;
|
||||
}
|
||||
Length= len;
|
||||
len= MultiByteToWideChar(CodePage, 0, location, (int)strlen(location), w_filename, (int)Length);
|
||||
if (!len)
|
||||
{
|
||||
/* todo: error handling */
|
||||
free(w_filename);
|
||||
return NULL;
|
||||
}
|
||||
len= (int)strlen(mode);
|
||||
if (!(w_mode= (wchar_t *)calloc(1, (len + 1) * sizeof(wchar_t))))
|
||||
{
|
||||
my_set_error(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
|
||||
free(w_filename);
|
||||
return NULL;
|
||||
}
|
||||
Length= len;
|
||||
len= MultiByteToWideChar(CodePage, 0, mode, (int)strlen(mode), w_mode, (int)Length);
|
||||
if (!len)
|
||||
{
|
||||
/* todo: error handling */
|
||||
free(w_filename);
|
||||
free(w_mode);
|
||||
return NULL;
|
||||
}
|
||||
fp= _wfopen(w_filename, w_mode);
|
||||
free(w_filename);
|
||||
free(w_mode);
|
||||
}
|
||||
|
||||
#endif
|
||||
if (fp)
|
||||
{
|
||||
ma_file= (MA_FILE *)malloc(sizeof(MA_FILE));
|
||||
if (!ma_file)
|
||||
{
|
||||
fclose(fp);
|
||||
my_set_error(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
|
||||
return NULL;
|
||||
}
|
||||
ma_file->type= MA_FILE_LOCAL;
|
||||
ma_file->ptr= (void *)fp;
|
||||
}
|
||||
return ma_file;
|
||||
#ifdef HAVE_REMOTEIO
|
||||
remote:
|
||||
/* check if plugin for remote io is available and try
|
||||
* to open location */
|
||||
{
|
||||
MYSQL mysql;
|
||||
if (rio_plugin ||(rio_plugin= (struct st_mysql_client_plugin_REMOTEIO *)
|
||||
mysql_client_find_plugin(&mysql, NULL, MARIADB_CLIENT_REMOTEIO_PLUGIN)))
|
||||
return rio_plugin->methods->mopen(location, mode);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ ma_close */
|
||||
int ma_close(MA_FILE *file)
|
||||
{
|
||||
int rc;
|
||||
if (!file)
|
||||
return -1;
|
||||
|
||||
switch (file->type) {
|
||||
case MA_FILE_LOCAL:
|
||||
rc= fclose((FILE *)file->ptr);
|
||||
free(file);
|
||||
break;
|
||||
#ifdef HAVE_REMOTEIO
|
||||
case MA_FILE_REMOTE:
|
||||
rc= rio_plugin->methods->mclose(file);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
/* {{{ ma_feof */
|
||||
int ma_feof(MA_FILE *file)
|
||||
{
|
||||
if (!file)
|
||||
return -1;
|
||||
|
||||
switch (file->type) {
|
||||
case MA_FILE_LOCAL:
|
||||
return feof((FILE *)file->ptr);
|
||||
break;
|
||||
#ifdef HAVE_REMOTEIO
|
||||
case MA_FILE_REMOTE:
|
||||
return rio_plugin->methods->mfeof(file);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ ma_read */
|
||||
size_t ma_read(void *ptr, size_t size, size_t nmemb, MA_FILE *file)
|
||||
{
|
||||
size_t s= 0;
|
||||
if (!file)
|
||||
return -1;
|
||||
|
||||
switch (file->type) {
|
||||
case MA_FILE_LOCAL:
|
||||
s= fread(ptr, size, nmemb, (FILE *)file->ptr);
|
||||
return s;
|
||||
break;
|
||||
#ifdef HAVE_REMOTEIO
|
||||
case MA_FILE_REMOTE:
|
||||
return rio_plugin->methods->mread(ptr, size, nmemb, file);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ ma_gets */
|
||||
char *ma_gets(char *ptr, size_t size, MA_FILE *file)
|
||||
{
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
switch (file->type) {
|
||||
case MA_FILE_LOCAL:
|
||||
return fgets(ptr, (int)size, (FILE *)file->ptr);
|
||||
break;
|
||||
#ifdef HAVE_REMOTEIO
|
||||
case MA_FILE_REMOTE:
|
||||
return rio_plugin->methods->mgets(ptr, size, file);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
114
vendor/MDBC/libmariadb/ma_list.c
vendored
Normal file
114
vendor/MDBC/libmariadb/ma_list.c
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
2016 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, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02111-1301, USA */
|
||||
|
||||
/*
|
||||
Code for handling dubble-linked lists in C
|
||||
*/
|
||||
|
||||
#include <ma_global.h>
|
||||
#include <ma_sys.h>
|
||||
#include <ma_list.h>
|
||||
|
||||
/* Add a element to start of list */
|
||||
|
||||
LIST *list_add(LIST *root, LIST *element)
|
||||
{
|
||||
if (root)
|
||||
{
|
||||
if (root->prev) /* If add in mid of list */
|
||||
root->prev->next= element;
|
||||
element->prev=root->prev;
|
||||
root->prev=element;
|
||||
}
|
||||
else
|
||||
element->prev=0;
|
||||
element->next=root;
|
||||
return(element); /* New root */
|
||||
}
|
||||
|
||||
|
||||
LIST *list_delete(LIST *root, LIST *element)
|
||||
{
|
||||
if (element->prev)
|
||||
element->prev->next=element->next;
|
||||
else
|
||||
root=element->next;
|
||||
if (element->next)
|
||||
element->next->prev=element->prev;
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
void list_free(LIST *root, unsigned int free_data)
|
||||
{
|
||||
LIST *next;
|
||||
while (root)
|
||||
{
|
||||
next=root->next;
|
||||
if (free_data)
|
||||
free(root->data);
|
||||
free(root);
|
||||
root=next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LIST *list_cons(void *data, LIST *list)
|
||||
{
|
||||
LIST *new_charset=(LIST*) malloc(sizeof(LIST));
|
||||
if (!new_charset)
|
||||
return 0;
|
||||
new_charset->data=data;
|
||||
return list_add(list,new_charset);
|
||||
}
|
||||
|
||||
|
||||
LIST *list_reverse(LIST *root)
|
||||
{
|
||||
LIST *last;
|
||||
|
||||
last=root;
|
||||
while (root)
|
||||
{
|
||||
last=root;
|
||||
root=root->next;
|
||||
last->next=last->prev;
|
||||
last->prev=root;
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
uint list_length(LIST *list)
|
||||
{
|
||||
uint count;
|
||||
for (count=0 ; list ; list=list->next, count++) ;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int list_walk(LIST *list, list_walk_action action, gptr argument)
|
||||
{
|
||||
int error=0;
|
||||
while (list)
|
||||
{
|
||||
if ((error = (*action)(list->data,argument)))
|
||||
return error;
|
||||
list= list_rest(list);
|
||||
}
|
||||
return 0;
|
||||
}
|
70
vendor/MDBC/libmariadb/ma_ll2str.c
vendored
Normal file
70
vendor/MDBC/libmariadb/ma_ll2str.c
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
2016,2018 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, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02111-1301, USA */
|
||||
|
||||
#include <ma_global.h>
|
||||
#include "ma_string.h"
|
||||
#include <ctype.h>
|
||||
|
||||
static char NEAR _dig_vec[] =
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
char *ma_ll2str(long long val,char *dst,int radix)
|
||||
{
|
||||
char buffer[65];
|
||||
register char *p;
|
||||
long long_val;
|
||||
|
||||
if (radix < 0)
|
||||
{
|
||||
if (radix < -36 || radix > -2) return (char*) 0;
|
||||
if (val < 0) {
|
||||
*dst++ = '-';
|
||||
val = 0ULL - val;
|
||||
}
|
||||
radix = -radix;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (radix > 36 || radix < 2) return (char*) 0;
|
||||
}
|
||||
if (val == 0)
|
||||
{
|
||||
*dst++='0';
|
||||
*dst='\0';
|
||||
return dst;
|
||||
}
|
||||
p = &buffer[sizeof(buffer)-1];
|
||||
*p = '\0';
|
||||
|
||||
while ((ulonglong) val > (ulonglong) LONG_MAX)
|
||||
{
|
||||
ulonglong quo=(ulonglong) val/(uint) radix;
|
||||
uint rem= (uint) (val- quo* (uint) radix);
|
||||
*--p = _dig_vec[rem];
|
||||
val= quo;
|
||||
}
|
||||
long_val= (long) val;
|
||||
while (long_val != 0)
|
||||
{
|
||||
long quo= long_val/radix;
|
||||
*--p = _dig_vec[(uchar) (long_val - quo*radix)];
|
||||
long_val= quo;
|
||||
}
|
||||
while ((*dst++ = *p++) != 0) ;
|
||||
return dst-1;
|
||||
}
|
265
vendor/MDBC/libmariadb/ma_loaddata.c
vendored
Normal file
265
vendor/MDBC/libmariadb/ma_loaddata.c
vendored
Normal file
@ -0,0 +1,265 @@
|
||||
/************************************************************************************
|
||||
Copyright (C) 2000, 2011 MySQL AB & MySQL Finland AB & TCX DataKonsult AB,
|
||||
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
|
||||
*************************************************************************************/
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| PHP Version 5 |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 2006-2011 The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Georg Richter <georg@mysql.com> |
|
||||
| Andrey Hristov <andrey@mysql.com> |
|
||||
| Ulf Wendel <uwendel@mysql.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "ma_global.h"
|
||||
#include <ma_sys.h>
|
||||
#include <ma_string.h>
|
||||
#include "errmsg.h"
|
||||
#include "mysql.h"
|
||||
#include <mariadb/ma_io.h>
|
||||
#include <string.h>
|
||||
#ifdef _WIN32
|
||||
#include <share.h>
|
||||
#endif
|
||||
#include <ma_common.h>
|
||||
|
||||
typedef struct st_mysql_infile_info
|
||||
{
|
||||
MA_FILE *fp;
|
||||
int error_no;
|
||||
char error_msg[MYSQL_ERRMSG_SIZE + 1];
|
||||
const char *filename;
|
||||
} MYSQL_INFILE_INFO;
|
||||
|
||||
/* {{{ mysql_local_infile_init */
|
||||
static
|
||||
int mysql_local_infile_init(void **ptr, const char *filename, void *userdata)
|
||||
{
|
||||
MYSQL_INFILE_INFO *info;
|
||||
MYSQL *mysql= (MYSQL *)userdata;
|
||||
|
||||
info = (MYSQL_INFILE_INFO *)malloc(sizeof(MYSQL_INFILE_INFO));
|
||||
if (!info) {
|
||||
return(1);
|
||||
}
|
||||
memset(info, 0, sizeof(MYSQL_INFILE_INFO));
|
||||
*ptr = info;
|
||||
|
||||
info->filename = filename;
|
||||
|
||||
info->fp= ma_open(filename, "rb", mysql);
|
||||
|
||||
if (!info->fp)
|
||||
{
|
||||
/* error handling is done via mysql_local_infile_error function, so we
|
||||
need to copy error to info */
|
||||
if (mysql_errno(mysql) && !info->error_no)
|
||||
{
|
||||
info->error_no= mysql_errno(mysql);
|
||||
ma_strmake(info->error_msg, mysql_error(mysql), MYSQL_ERRMSG_SIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
info->error_no = errno;
|
||||
snprintf((char *)info->error_msg, sizeof(info->error_msg),
|
||||
CER(CR_FILE_NOT_FOUND), filename, info->error_no);
|
||||
}
|
||||
return(1);
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
/* {{{ mysql_local_infile_read */
|
||||
static
|
||||
int mysql_local_infile_read(void *ptr, char * buf, unsigned int buf_len)
|
||||
{
|
||||
MYSQL_INFILE_INFO *info = (MYSQL_INFILE_INFO *)ptr;
|
||||
size_t count;
|
||||
|
||||
count= ma_read((void *)buf, 1, (size_t)buf_len, info->fp);
|
||||
|
||||
if (count == (size_t)-1)
|
||||
{
|
||||
info->error_no = errno;
|
||||
snprintf((char *)info->error_msg, sizeof(info->error_msg),
|
||||
CER(CR_FILE_READ), info->filename, info->error_no);
|
||||
}
|
||||
return((int)count);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
/* {{{ mysql_local_infile_error */
|
||||
static
|
||||
int mysql_local_infile_error(void *ptr, char *error_buf, unsigned int error_buf_len)
|
||||
{
|
||||
MYSQL_INFILE_INFO *info = (MYSQL_INFILE_INFO *)ptr;
|
||||
|
||||
if (info) {
|
||||
ma_strmake(error_buf, info->error_msg, error_buf_len);
|
||||
return(info->error_no);
|
||||
}
|
||||
|
||||
ma_strmake(error_buf, "Unknown error", error_buf_len);
|
||||
return(CR_UNKNOWN_ERROR);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
/* {{{ mysql_local_infile_end */
|
||||
static
|
||||
void mysql_local_infile_end(void *ptr)
|
||||
{
|
||||
MYSQL_INFILE_INFO *info = (MYSQL_INFILE_INFO *)ptr;
|
||||
|
||||
if (info)
|
||||
{
|
||||
if (info->fp)
|
||||
ma_close(info->fp);
|
||||
free(ptr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
/* {{{ mysql_local_infile_default */
|
||||
void mysql_set_local_infile_default(MYSQL *conn)
|
||||
{
|
||||
conn->options.local_infile_init = mysql_local_infile_init;
|
||||
conn->options.local_infile_read = mysql_local_infile_read;
|
||||
conn->options.local_infile_error = mysql_local_infile_error;
|
||||
conn->options.local_infile_end = mysql_local_infile_end;
|
||||
return;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ mysql_set_local_infile_handler */
|
||||
void STDCALL mysql_set_local_infile_handler(MYSQL *conn,
|
||||
int (*local_infile_init)(void **, const char *, void *),
|
||||
int (*local_infile_read)(void *, char *, uint),
|
||||
void (*local_infile_end)(void *),
|
||||
int (*local_infile_error)(void *, char *, uint),
|
||||
void *userdata)
|
||||
{
|
||||
conn->options.local_infile_init= local_infile_init;
|
||||
conn->options.local_infile_read= local_infile_read;
|
||||
conn->options.local_infile_end= local_infile_end;
|
||||
conn->options.local_infile_error= local_infile_error;
|
||||
conn->options.local_infile_userdata = userdata;
|
||||
return;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ mysql_handle_local_infile */
|
||||
my_bool mysql_handle_local_infile(MYSQL *conn, const char *filename, my_bool can_local_infile)
|
||||
{
|
||||
unsigned int buflen= 4096;
|
||||
int bufread;
|
||||
unsigned char *buf= NULL;
|
||||
void *info= NULL;
|
||||
my_bool result= 1;
|
||||
|
||||
/* check if all callback functions exist */
|
||||
if (!conn->options.local_infile_init || !conn->options.local_infile_end ||
|
||||
!conn->options.local_infile_read || !conn->options.local_infile_error)
|
||||
{
|
||||
conn->options.local_infile_userdata= conn;
|
||||
mysql_set_local_infile_default(conn);
|
||||
}
|
||||
|
||||
if (!(conn->options.client_flag & CLIENT_LOCAL_FILES) ||
|
||||
!can_local_infile)
|
||||
{
|
||||
my_set_error(conn, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, "Load data local infile forbidden");
|
||||
/* write empty packet to server */
|
||||
ma_net_write(&conn->net, (unsigned char *)"", 0);
|
||||
ma_net_flush(&conn->net);
|
||||
goto infile_error;
|
||||
}
|
||||
|
||||
/* allocate buffer for reading data */
|
||||
buf = (uchar *)malloc(buflen);
|
||||
|
||||
/* init handler: allocate read buffer and open file */
|
||||
if (conn->options.local_infile_init(&info, filename,
|
||||
conn->options.local_infile_userdata))
|
||||
{
|
||||
char tmp_buf[MYSQL_ERRMSG_SIZE];
|
||||
int tmp_errno;
|
||||
|
||||
tmp_errno= conn->options.local_infile_error(info, tmp_buf, sizeof(tmp_buf));
|
||||
my_set_error(conn, tmp_errno, SQLSTATE_UNKNOWN, tmp_buf);
|
||||
ma_net_write(&conn->net, (unsigned char *)"", 0);
|
||||
ma_net_flush(&conn->net);
|
||||
goto infile_error;
|
||||
}
|
||||
|
||||
/* read data */
|
||||
while ((bufread= conn->options.local_infile_read(info, (char *)buf, buflen)) > 0)
|
||||
{
|
||||
if (ma_net_write(&conn->net, (unsigned char *)buf, bufread))
|
||||
{
|
||||
my_set_error(conn, CR_SERVER_LOST, SQLSTATE_UNKNOWN, NULL);
|
||||
goto infile_error;
|
||||
}
|
||||
}
|
||||
|
||||
/* send empty packet for eof */
|
||||
if (ma_net_write(&conn->net, (unsigned char *)"", 0) ||
|
||||
ma_net_flush(&conn->net))
|
||||
{
|
||||
my_set_error(conn, CR_SERVER_LOST, SQLSTATE_UNKNOWN, NULL);
|
||||
goto infile_error;
|
||||
}
|
||||
|
||||
/* error during read occurred */
|
||||
if (bufread < 0)
|
||||
{
|
||||
char tmp_buf[MYSQL_ERRMSG_SIZE];
|
||||
int tmp_errno= conn->options.local_infile_error(info, tmp_buf, sizeof(tmp_buf));
|
||||
my_set_error(conn, tmp_errno, SQLSTATE_UNKNOWN, tmp_buf);
|
||||
goto infile_error;
|
||||
}
|
||||
|
||||
result = 0;
|
||||
|
||||
infile_error:
|
||||
conn->options.local_infile_end(info);
|
||||
free(buf);
|
||||
return(result);
|
||||
}
|
||||
/* }}} */
|
||||
|
591
vendor/MDBC/libmariadb/ma_net.c
vendored
Normal file
591
vendor/MDBC/libmariadb/ma_net.c
vendored
Normal file
@ -0,0 +1,591 @@
|
||||
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
2012-2016 SkySQL 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, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02111-1301, USA */
|
||||
|
||||
/* Write and read of logical packets to/from socket
|
||||
** Writes are cached into net_buffer_length big packets.
|
||||
** Read packets are reallocated dynamically when reading big packets.
|
||||
** Each logical packet has the following pre-info:
|
||||
** 3 byte length & 1 byte package-number.
|
||||
*/
|
||||
|
||||
|
||||
#include <ma_global.h>
|
||||
#include <mysql.h>
|
||||
#include <ma_pvio.h>
|
||||
#include <ma_sys.h>
|
||||
#include <ma_string.h>
|
||||
#include "mysql.h"
|
||||
#include "ma_server_error.h"
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <ma_pvio.h>
|
||||
#include <ma_common.h>
|
||||
#ifndef _WIN32
|
||||
#include <poll.h>
|
||||
#endif
|
||||
|
||||
#define MAX_PACKET_LENGTH (256L*256L*256L-1)
|
||||
|
||||
/* net_buffer_length and max_allowed_packet are defined in mysql.h
|
||||
See bug conc-57
|
||||
*/
|
||||
#undef net_buffer_length
|
||||
|
||||
#undef max_allowed_packet
|
||||
ulong max_allowed_packet=1024L * 1024L * 1024L;
|
||||
ulong net_read_timeout= NET_READ_TIMEOUT;
|
||||
ulong net_write_timeout= NET_WRITE_TIMEOUT;
|
||||
ulong net_buffer_length= 8192; /* Default length. Enlarged if necessary */
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <sys/socket.h>
|
||||
#else
|
||||
#undef MYSQL_SERVER /* Win32 can't handle interrupts */
|
||||
#endif
|
||||
#if !defined(_WIN32) && !defined(HAVE_BROKEN_NETINET_INCLUDES) && !defined(__BEOS__)
|
||||
#include <netinet/in_systm.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#if !defined(alpha_linux_port)
|
||||
#include <netinet/tcp.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Give error if a too big packet is found
|
||||
** The server can change this with the -O switch, but because the client
|
||||
** can't normally do this the client should have a bigger max-buffer.
|
||||
*/
|
||||
|
||||
static int ma_net_write_buff(NET *net,const char *packet, size_t len);
|
||||
|
||||
|
||||
/* Init with packet info */
|
||||
|
||||
int ma_net_init(NET *net, MARIADB_PVIO* pvio)
|
||||
{
|
||||
if (!(net->buff=(uchar*) malloc(net_buffer_length)))
|
||||
return 1;
|
||||
if (!net->extension)
|
||||
return 1;
|
||||
|
||||
memset(net->buff, 0, net_buffer_length);
|
||||
|
||||
net->max_packet_size= MAX(net_buffer_length, max_allowed_packet);
|
||||
net->buff_end=net->buff+(net->max_packet=net_buffer_length);
|
||||
net->pvio = pvio;
|
||||
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;
|
||||
net->write_pos=net->read_pos = net->buff;
|
||||
net->last_error[0]= net->sqlstate[0] =0;
|
||||
|
||||
net->compress=0; net->reading_or_writing=0;
|
||||
net->where_b = net->remain_in_buf=0;
|
||||
net->last_errno=0;
|
||||
|
||||
if (pvio != 0) /* If real connection */
|
||||
{
|
||||
ma_pvio_get_handle(pvio, &net->fd);
|
||||
ma_pvio_blocking(pvio, 1, 0);
|
||||
ma_pvio_fast_send(pvio);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ma_net_end(NET *net)
|
||||
{
|
||||
free(net->buff);
|
||||
net->buff=0;
|
||||
}
|
||||
|
||||
/* Realloc the packet buffer */
|
||||
|
||||
static my_bool net_realloc(NET *net, size_t length)
|
||||
{
|
||||
uchar *buff;
|
||||
size_t pkt_length;
|
||||
|
||||
if (length >= net->max_packet_size)
|
||||
{
|
||||
net->error=1;
|
||||
net->last_errno=ER_NET_PACKET_TOO_LARGE;
|
||||
return(1);
|
||||
}
|
||||
pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1);
|
||||
/* reallocate buffer:
|
||||
size= pkt_length + NET_HEADER_SIZE + COMP_HEADER_SIZE */
|
||||
if (!(buff=(uchar*) realloc(net->buff,
|
||||
pkt_length + NET_HEADER_SIZE + COMP_HEADER_SIZE)))
|
||||
{
|
||||
net->error=1;
|
||||
return(1);
|
||||
}
|
||||
net->buff=net->write_pos=buff;
|
||||
net->buff_end=buff+(net->max_packet=(unsigned long)pkt_length);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* Remove unwanted characters from connection */
|
||||
void ma_net_clear(NET *net)
|
||||
{
|
||||
if (net->extension->multi_status > COM_MULTI_OFF)
|
||||
return;
|
||||
net->compress_pkt_nr= net->pkt_nr=0; /* Ready for new command */
|
||||
net->write_pos=net->buff;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Flush write_buffer if not empty. */
|
||||
int ma_net_flush(NET *net)
|
||||
{
|
||||
int error=0;
|
||||
|
||||
/* don't flush if pipelined query is in progress */
|
||||
if (net->extension->multi_status > COM_MULTI_OFF)
|
||||
return 0;
|
||||
|
||||
if (net->buff != net->write_pos)
|
||||
{
|
||||
error=ma_net_real_write(net,(char*) net->buff,
|
||||
(size_t) (net->write_pos - net->buff));
|
||||
net->write_pos=net->buff;
|
||||
}
|
||||
if (net->compress)
|
||||
net->pkt_nr= net->compress_pkt_nr;
|
||||
return(error);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
** Write something to server/client buffer
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
** Write a logical packet with packet header
|
||||
** Format: Packet length (3 bytes), packet number(1 byte)
|
||||
** When compression is used a 3 byte compression length is added
|
||||
** NOTE: If compression is used the original package is destroyed!
|
||||
*/
|
||||
|
||||
int ma_net_write(NET *net, const uchar *packet, size_t len)
|
||||
{
|
||||
uchar buff[NET_HEADER_SIZE];
|
||||
while (len >= MAX_PACKET_LENGTH)
|
||||
{
|
||||
const ulong max_len= MAX_PACKET_LENGTH;
|
||||
int3store(buff,max_len);
|
||||
buff[3]= (uchar)net->pkt_nr++;
|
||||
if (ma_net_write_buff(net,(char*) buff,NET_HEADER_SIZE) ||
|
||||
ma_net_write_buff(net, (char *)packet, max_len))
|
||||
return 1;
|
||||
packet+= max_len;
|
||||
len-= max_len;
|
||||
}
|
||||
/* write last remaining packet, size can be zero */
|
||||
int3store(buff, len);
|
||||
buff[3]= (uchar)net->pkt_nr++;
|
||||
if (ma_net_write_buff(net,(char*) buff,NET_HEADER_SIZE) ||
|
||||
ma_net_write_buff(net, (char *)packet, len))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ma_net_write_command(NET *net, uchar command,
|
||||
const char *packet, size_t len,
|
||||
my_bool disable_flush)
|
||||
{
|
||||
uchar buff[NET_HEADER_SIZE+1];
|
||||
size_t buff_size= NET_HEADER_SIZE + 1;
|
||||
size_t length= 1 + len; /* 1 extra byte for command */
|
||||
int rc;
|
||||
|
||||
buff[NET_HEADER_SIZE]= 0;
|
||||
buff[4]=command;
|
||||
|
||||
if (length >= MAX_PACKET_LENGTH)
|
||||
{
|
||||
len= MAX_PACKET_LENGTH - 1;
|
||||
do
|
||||
{
|
||||
int3store(buff, MAX_PACKET_LENGTH);
|
||||
buff[3]= (net->compress) ? 0 : (uchar) (net->pkt_nr++);
|
||||
|
||||
if (ma_net_write_buff(net, (char *)buff, buff_size) ||
|
||||
ma_net_write_buff(net, packet, len))
|
||||
return(1);
|
||||
packet+= len;
|
||||
length-= MAX_PACKET_LENGTH;
|
||||
len= MAX_PACKET_LENGTH;
|
||||
buff_size= NET_HEADER_SIZE; /* don't send command for further packets */
|
||||
} while (length >= MAX_PACKET_LENGTH);
|
||||
len= length;
|
||||
}
|
||||
int3store(buff,length);
|
||||
buff[3]= (net->compress) ? 0 :(uchar) (net->pkt_nr++);
|
||||
rc= test (ma_net_write_buff(net,(char *)buff, buff_size) ||
|
||||
ma_net_write_buff(net,packet,len));
|
||||
if (!rc && !disable_flush)
|
||||
return test(ma_net_flush(net));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int ma_net_write_buff(NET *net,const char *packet, size_t len)
|
||||
{
|
||||
size_t left_length;
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
if (net->max_packet > MAX_PACKET_LENGTH &&
|
||||
net->compress)
|
||||
left_length= (size_t)(MAX_PACKET_LENGTH - (net->write_pos - net->buff));
|
||||
else
|
||||
left_length=(size_t) (net->buff_end - net->write_pos);
|
||||
|
||||
if (len > left_length)
|
||||
{
|
||||
if (net->write_pos != net->buff)
|
||||
{
|
||||
memcpy((char*) net->write_pos,packet,left_length);
|
||||
if (ma_net_real_write(net,(char*) net->buff,
|
||||
(size_t)(net->write_pos - net->buff) + left_length))
|
||||
return 1;
|
||||
packet+=left_length;
|
||||
len-=left_length;
|
||||
net->write_pos= net->buff;
|
||||
}
|
||||
if (net->compress)
|
||||
{
|
||||
/* uncompressed length is stored in 3 bytes,so
|
||||
packet can't be > 0xFFFFFF */
|
||||
left_length= MAX_PACKET_LENGTH;
|
||||
while (len > left_length)
|
||||
{
|
||||
if (ma_net_real_write(net, packet, left_length))
|
||||
return 1;
|
||||
packet+= left_length;
|
||||
len-= left_length;
|
||||
}
|
||||
}
|
||||
if (len > net->max_packet)
|
||||
return(test(ma_net_real_write(net, packet, len)));
|
||||
}
|
||||
memcpy((char*) net->write_pos,packet,len);
|
||||
net->write_pos+=len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned char *mysql_net_store_length(unsigned char *packet, size_t length);
|
||||
|
||||
/* Read and write using timeouts */
|
||||
|
||||
int ma_net_real_write(NET *net, const char *packet, size_t len)
|
||||
{
|
||||
ssize_t length;
|
||||
char *pos,*end;
|
||||
|
||||
if (net->error == 2)
|
||||
return(-1); /* socket can't be used */
|
||||
|
||||
net->reading_or_writing=2;
|
||||
#ifdef HAVE_COMPRESS
|
||||
if (net->compress)
|
||||
{
|
||||
size_t complen;
|
||||
uchar *b;
|
||||
uint header_length=NET_HEADER_SIZE+COMP_HEADER_SIZE;
|
||||
if (!(b=(uchar*) malloc(len + NET_HEADER_SIZE + COMP_HEADER_SIZE + 1)))
|
||||
{
|
||||
net->last_errno=ER_OUT_OF_RESOURCES;
|
||||
net->error=2;
|
||||
net->reading_or_writing=0;
|
||||
return(1);
|
||||
}
|
||||
memcpy(b+header_length,packet,len);
|
||||
|
||||
if (_mariadb_compress((unsigned char*) b+header_length,&len,&complen))
|
||||
{
|
||||
complen=0;
|
||||
}
|
||||
int3store(&b[NET_HEADER_SIZE],complen);
|
||||
int3store(b,len);
|
||||
b[3]=(uchar) (net->compress_pkt_nr++);
|
||||
len+= header_length;
|
||||
packet= (char*) b;
|
||||
}
|
||||
#endif /* HAVE_COMPRESS */
|
||||
|
||||
pos=(char*) packet; end=pos+len;
|
||||
while (pos != end)
|
||||
{
|
||||
if ((length=ma_pvio_write(net->pvio,(uchar *)pos,(size_t) (end-pos))) <= 0)
|
||||
{
|
||||
net->error=2; /* Close socket */
|
||||
net->last_errno= ER_NET_ERROR_ON_WRITE;
|
||||
net->reading_or_writing=0;
|
||||
#ifdef HAVE_COMPRESS
|
||||
if (net->compress)
|
||||
free((char*) packet);
|
||||
#endif
|
||||
return(1);
|
||||
}
|
||||
pos+=length;
|
||||
}
|
||||
#ifdef HAVE_COMPRESS
|
||||
if (net->compress)
|
||||
free((char*) packet);
|
||||
#endif
|
||||
net->reading_or_writing=0;
|
||||
return(((int) (pos != end)));
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
** Read something from server/clinet
|
||||
*****************************************************************************/
|
||||
static ulong ma_real_read(NET *net, size_t *complen)
|
||||
{
|
||||
uchar *pos;
|
||||
ssize_t length;
|
||||
uint i;
|
||||
ulong len=packet_error;
|
||||
size_t remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE :
|
||||
NET_HEADER_SIZE);
|
||||
*complen = 0;
|
||||
|
||||
net->reading_or_writing=1;
|
||||
|
||||
pos = net->buff + net->where_b; /* net->packet -4 */
|
||||
for (i=0 ; i < 2 ; i++)
|
||||
{
|
||||
while (remain > 0)
|
||||
{
|
||||
/* First read is done with non blocking mode */
|
||||
if ((length=ma_pvio_cache_read(net->pvio, pos,remain)) <= 0L)
|
||||
{
|
||||
len= packet_error;
|
||||
net->error=2; /* Close socket */
|
||||
goto end;
|
||||
}
|
||||
remain -= (ulong) length;
|
||||
pos+= (ulong) length;
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
{ /* First parts is packet length */
|
||||
ulong helping;
|
||||
net->pkt_nr= net->buff[net->where_b + 3];
|
||||
net->compress_pkt_nr= ++net->pkt_nr;
|
||||
#ifdef HAVE_COMPRESS
|
||||
if (net->compress)
|
||||
{
|
||||
/* complen is > 0 if package is really compressed */
|
||||
*complen=uint3korr(&(net->buff[net->where_b + NET_HEADER_SIZE]));
|
||||
}
|
||||
#endif
|
||||
|
||||
len=uint3korr(net->buff+net->where_b);
|
||||
if (!len)
|
||||
goto end;
|
||||
helping = max(len,(ulong)*complen) + net->where_b;
|
||||
/* The necessary size of net->buff */
|
||||
if (helping >= net->max_packet)
|
||||
{
|
||||
if (net_realloc(net, helping))
|
||||
{
|
||||
len= packet_error; /* Return error */
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
pos=net->buff + net->where_b;
|
||||
remain = len;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
net->reading_or_writing=0;
|
||||
return(len);
|
||||
}
|
||||
|
||||
ulong ma_net_read(NET *net)
|
||||
{
|
||||
size_t len,complen;
|
||||
|
||||
#ifdef HAVE_COMPRESS
|
||||
if (!net->compress)
|
||||
{
|
||||
#endif
|
||||
len = ma_real_read (net,(size_t *)&complen);
|
||||
if (len == MAX_PACKET_LENGTH)
|
||||
{
|
||||
/* multi packet read */
|
||||
size_t length= 0;
|
||||
ulong last_pos= net->where_b;
|
||||
|
||||
do
|
||||
{
|
||||
length+= len;
|
||||
net->where_b+= (unsigned long)len;
|
||||
len= ma_real_read(net, &complen);
|
||||
} while (len == MAX_PACKET_LENGTH);
|
||||
net->where_b= last_pos;
|
||||
if (len != packet_error)
|
||||
len+= length;
|
||||
}
|
||||
net->read_pos = net->buff + net->where_b;
|
||||
if (len != packet_error)
|
||||
net->read_pos[len]=0; /* Safeguard for mysql_use_result */
|
||||
return (ulong)len;
|
||||
#ifdef HAVE_COMPRESS
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
compressed protocol:
|
||||
|
||||
--------------------------------------
|
||||
packet_length 3
|
||||
sequence_id 1
|
||||
uncompressed_length 3
|
||||
--------------------------------------
|
||||
compressed data packet_length - 7
|
||||
--------------------------------------
|
||||
|
||||
Another packet will follow if:
|
||||
packet_length == MAX_PACKET_LENGTH
|
||||
|
||||
Last package will be identified by
|
||||
- packet_length is zero (special case)
|
||||
- packet_length < MAX_PACKET_LENGTH
|
||||
*/
|
||||
|
||||
size_t packet_length,
|
||||
buffer_length;
|
||||
size_t current= 0, start= 0;
|
||||
my_bool is_multi_packet= 0;
|
||||
|
||||
/* check if buffer is empty */
|
||||
if (!net->remain_in_buf)
|
||||
{
|
||||
buffer_length= 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* save position and restore \0 character */
|
||||
buffer_length= net->buf_length;
|
||||
current= net->buf_length - net->remain_in_buf;
|
||||
start= current;
|
||||
net->buff[net->buf_length - net->remain_in_buf]=net->save_char;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
if (buffer_length - current >= 4)
|
||||
{
|
||||
uchar *pos= net->buff + current;
|
||||
packet_length= uint3korr(pos);
|
||||
|
||||
/* check if we have last package (special case: zero length) */
|
||||
if (!packet_length)
|
||||
{
|
||||
current+= 4; /* length + sequence_id,
|
||||
no more data will follow */
|
||||
break;
|
||||
}
|
||||
if (packet_length + 4 <= buffer_length - current)
|
||||
{
|
||||
if (!is_multi_packet)
|
||||
{
|
||||
current= current + packet_length + 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* remove packet_header */
|
||||
memmove(net->buff + current,
|
||||
net->buff + current + 4,
|
||||
buffer_length - current);
|
||||
buffer_length-= 4;
|
||||
current+= packet_length;
|
||||
}
|
||||
/* do we have last packet ? */
|
||||
if (packet_length != MAX_PACKET_LENGTH)
|
||||
{
|
||||
is_multi_packet= 0;
|
||||
break;
|
||||
}
|
||||
else
|
||||
is_multi_packet= 1;
|
||||
if (start)
|
||||
{
|
||||
memmove(net->buff, net->buff + start,
|
||||
buffer_length - start);
|
||||
/* decrease buflen*/
|
||||
buffer_length-= start;
|
||||
start= 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (start)
|
||||
{
|
||||
memmove(net->buff, net->buff + start, buffer_length - start);
|
||||
/* decrease buflen and current */
|
||||
current -= start;
|
||||
buffer_length-= start;
|
||||
start= 0;
|
||||
}
|
||||
|
||||
net->where_b=(unsigned long)buffer_length;
|
||||
|
||||
if ((packet_length = ma_real_read(net,(size_t *)&complen)) == packet_error)
|
||||
return packet_error;
|
||||
if (_mariadb_uncompress((unsigned char*) net->buff + net->where_b, &packet_length, &complen))
|
||||
{
|
||||
net->error=2; /* caller will close socket */
|
||||
net->last_errno=ER_NET_UNCOMPRESS_ERROR;
|
||||
break;
|
||||
return packet_error;
|
||||
}
|
||||
buffer_length+= complen;
|
||||
}
|
||||
/* set values */
|
||||
net->buf_length= (unsigned long)buffer_length;
|
||||
net->remain_in_buf= (unsigned long)(buffer_length - current);
|
||||
net->read_pos= net->buff + start + 4;
|
||||
len= current - start - 4;
|
||||
if (is_multi_packet)
|
||||
len-= 4;
|
||||
net->save_char= net->read_pos[len]; /* Must be saved */
|
||||
net->read_pos[len]=0; /* Safeguard for mysql_use_result */
|
||||
}
|
||||
#endif
|
||||
return (ulong)len;
|
||||
}
|
||||
|
||||
int net_add_multi_command(NET *net, uchar command, const uchar *packet,
|
||||
size_t length)
|
||||
{
|
||||
if (net->extension->multi_status == COM_MULTI_OFF)
|
||||
{
|
||||
return(1);
|
||||
}
|
||||
/* don't increase packet number */
|
||||
net->compress_pkt_nr= net->pkt_nr= 0;
|
||||
return ma_net_write_command(net, command, (const char *)packet, length, 1);
|
||||
}
|
||||
|
162
vendor/MDBC/libmariadb/ma_password.c
vendored
Normal file
162
vendor/MDBC/libmariadb/ma_password.c
vendored
Normal file
@ -0,0 +1,162 @@
|
||||
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
2016,2018 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, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02111-1301, USA */
|
||||
|
||||
/* password checking routines */
|
||||
/*****************************************************************************
|
||||
The main idea is that no password are sent between client & server on
|
||||
connection and that no password are saved in mysql in a decodable form.
|
||||
|
||||
On connection a random string is generated and sent to the client.
|
||||
The client generates a new string with a random generator inited with
|
||||
the hash values from the password and the sent string.
|
||||
This 'check' string is sent to the server where it is compared with
|
||||
a string generated from the stored hash_value of the password and the
|
||||
random string.
|
||||
|
||||
The password is saved (in user.password) by using the PASSWORD() function in
|
||||
mysql.
|
||||
|
||||
Example:
|
||||
update user set password=PASSWORD("hello") where user="test"
|
||||
This saves a hashed number as a string in the password field.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <ma_global.h>
|
||||
#include <ma_sys.h>
|
||||
#include <ma_string.h>
|
||||
#include <ma_sha1.h>
|
||||
#include "mysql.h"
|
||||
|
||||
|
||||
void ma_randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2)
|
||||
{ /* For mysql 3.21.# */
|
||||
#ifdef HAVE_purify
|
||||
memset((char*) rand_st, 0m sizeof(*rand_st)); /* Avoid UMC warnings */
|
||||
#endif
|
||||
rand_st->max_value= 0x3FFFFFFFL;
|
||||
rand_st->max_value_dbl=(double) rand_st->max_value;
|
||||
rand_st->seed1=seed1%rand_st->max_value ;
|
||||
rand_st->seed2=seed2%rand_st->max_value;
|
||||
}
|
||||
|
||||
double rnd(struct rand_struct *rand_st)
|
||||
{
|
||||
rand_st->seed1=(rand_st->seed1*3+rand_st->seed2) % rand_st->max_value;
|
||||
rand_st->seed2=(rand_st->seed1+rand_st->seed2+33) % rand_st->max_value;
|
||||
return (((double) rand_st->seed1)/rand_st->max_value_dbl);
|
||||
}
|
||||
|
||||
void ma_hash_password(ulong *result, const char *password, size_t len)
|
||||
{
|
||||
register ulong nr=1345345333L, add=7, nr2=0x12345671L;
|
||||
ulong tmp;
|
||||
const char *password_end= password + len;
|
||||
for (; password < password_end; password++)
|
||||
{
|
||||
if (*password == ' ' || *password == '\t')
|
||||
continue; /* skip space in password */
|
||||
tmp= (ulong) (uchar) *password;
|
||||
nr^= (((nr & 63)+add)*tmp)+ (nr << 8);
|
||||
nr2+=(nr2 << 8) ^ nr;
|
||||
add+=tmp;
|
||||
}
|
||||
result[0]=nr & (((ulong) 1L << 31) -1L); /* Don't use sign bit (str2int) */;
|
||||
result[1]=nr2 & (((ulong) 1L << 31) -1L);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a new message based on message and password
|
||||
* The same thing is done in client and server and the results are checked.
|
||||
*/
|
||||
|
||||
/* scramble for 4.1 servers
|
||||
* Code based on php_nysqlnd_scramble function from PHP's mysqlnd extension,
|
||||
* written by Andrey Hristov (andrey@php.net)
|
||||
* License: PHP License 3.0
|
||||
*/
|
||||
void my_crypt(unsigned char *buffer, const unsigned char *s1, const unsigned char *s2, size_t len)
|
||||
{
|
||||
const unsigned char *s1_end= s1 + len;
|
||||
while (s1 < s1_end) {
|
||||
*buffer++= *s1++ ^ *s2++;
|
||||
}
|
||||
}
|
||||
|
||||
void ma_scramble_41(const unsigned char *buffer, const char *scramble, const char *password)
|
||||
{
|
||||
_MA_SHA1_CTX context;
|
||||
unsigned char sha1[SHA1_MAX_LENGTH];
|
||||
unsigned char sha2[SHA1_MAX_LENGTH];
|
||||
|
||||
|
||||
/* Phase 1: hash password */
|
||||
ma_SHA1Init(&context);
|
||||
ma_SHA1Update(&context, (unsigned char *)password, strlen((char *)password));
|
||||
ma_SHA1Final(sha1, &context);
|
||||
|
||||
/* Phase 2: hash sha1 */
|
||||
ma_SHA1Init(&context);
|
||||
ma_SHA1Update(&context, (unsigned char*)sha1, SHA1_MAX_LENGTH);
|
||||
ma_SHA1Final(sha2, &context);
|
||||
|
||||
/* Phase 3: hash scramble + sha2 */
|
||||
ma_SHA1Init(&context);
|
||||
ma_SHA1Update(&context, (unsigned char *)scramble, SCRAMBLE_LENGTH);
|
||||
ma_SHA1Update(&context, (unsigned char*)sha2, SHA1_MAX_LENGTH);
|
||||
ma_SHA1Final((unsigned char *)buffer, &context);
|
||||
|
||||
/* let's crypt buffer now */
|
||||
my_crypt((uchar *)buffer, (const unsigned char *)buffer, (const unsigned char *)sha1, SHA1_MAX_LENGTH);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void ma_make_scrambled_password(char *to,const char *password)
|
||||
{
|
||||
ulong hash_res[2];
|
||||
ma_hash_password(hash_res,password, strlen(password));
|
||||
sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a new message based on message and password
|
||||
* The same thing is done in client and server and the results are checked.
|
||||
*/
|
||||
char *ma_scramble_323(char *to, const char *message, const char *password)
|
||||
{
|
||||
struct rand_struct rand_st;
|
||||
ulong hash_pass[2], hash_message[2];
|
||||
|
||||
if (password && password[0])
|
||||
{
|
||||
char extra, *to_start=to;
|
||||
const char *end_scramble323= message + SCRAMBLE_LENGTH_323;
|
||||
ma_hash_password(hash_pass,password, (uint) strlen(password));
|
||||
/* Don't use strlen, could be > SCRAMBLE_LENGTH_323 ! */
|
||||
ma_hash_password(hash_message, message, SCRAMBLE_LENGTH_323);
|
||||
ma_randominit(&rand_st, hash_pass[0] ^ hash_message[0],
|
||||
hash_pass[1] ^ hash_message[1]);
|
||||
for (; message < end_scramble323; message++)
|
||||
*to++= (char) (floor(rnd(&rand_st) * 31) + 64);
|
||||
extra=(char) (floor(rnd(&rand_st) * 31));
|
||||
while (to_start != to)
|
||||
*(to_start++)^= extra;
|
||||
}
|
||||
*to= 0;
|
||||
return to;
|
||||
}
|
594
vendor/MDBC/libmariadb/ma_pvio.c
vendored
Normal file
594
vendor/MDBC/libmariadb/ma_pvio.c
vendored
Normal file
@ -0,0 +1,594 @@
|
||||
/************************************************************************************
|
||||
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 (PVIO) interface
|
||||
|
||||
PVIO is the interface for client server communication and replaces former vio
|
||||
component of the client library.
|
||||
|
||||
PVIO support various protcols like sockets, pipes and shared memory, which are
|
||||
implemented as plugins and can be extended therefore easily.
|
||||
|
||||
Interface function description:
|
||||
|
||||
ma_pvio_init allocates a new PVIO object which will be used
|
||||
for the current connection
|
||||
|
||||
ma_pvio_close frees all resources of previously allocated PVIO object
|
||||
and closes open connections
|
||||
|
||||
ma_pvio_read reads data from server
|
||||
|
||||
ma_pvio_write sends data to server
|
||||
|
||||
ma_pvio_set_timeout sets timeout for connection, read and write
|
||||
|
||||
ma_pvio_register_callback
|
||||
register callback functions for read and write
|
||||
*/
|
||||
|
||||
#include <ma_global.h>
|
||||
#include <ma_sys.h>
|
||||
#include <mysql.h>
|
||||
#include <errmsg.h>
|
||||
#include <mysql/client_plugin.h>
|
||||
#include <string.h>
|
||||
#include <ma_common.h>
|
||||
#include <ma_pvio.h>
|
||||
#include <mariadb_async.h>
|
||||
#include <ma_context.h>
|
||||
|
||||
/* callback functions for read/write */
|
||||
LIST *pvio_callback= NULL;
|
||||
|
||||
#define IS_BLOCKING_ERROR() \
|
||||
IF_WIN(WSAGetLastError() != WSAEWOULDBLOCK, \
|
||||
(errno != EAGAIN && errno != EINTR))
|
||||
|
||||
/* {{{ MARIADB_PVIO *ma_pvio_init */
|
||||
MARIADB_PVIO *ma_pvio_init(MA_PVIO_CINFO *cinfo)
|
||||
{
|
||||
/* check connection type and load the required plugin.
|
||||
* Currently we support the following pvio types:
|
||||
* pvio_socket
|
||||
* pvio_namedpipe
|
||||
* pvio_sharedmed
|
||||
*/
|
||||
const char *pvio_plugins[] = {"pvio_socket", "pvio_npipe", "pvio_shmem"};
|
||||
int type;
|
||||
MARIADB_PVIO_PLUGIN *pvio_plugin;
|
||||
MARIADB_PVIO *pvio= NULL;
|
||||
|
||||
switch (cinfo->type)
|
||||
{
|
||||
case PVIO_TYPE_UNIXSOCKET:
|
||||
case PVIO_TYPE_SOCKET:
|
||||
type= 0;
|
||||
break;
|
||||
#ifdef _WIN32
|
||||
case PVIO_TYPE_NAMEDPIPE:
|
||||
type= 1;
|
||||
break;
|
||||
case PVIO_TYPE_SHAREDMEM:
|
||||
type= 2;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(pvio_plugin= (MARIADB_PVIO_PLUGIN *)
|
||||
mysql_client_find_plugin(cinfo->mysql,
|
||||
pvio_plugins[type],
|
||||
MARIADB_CLIENT_PVIO_PLUGIN)))
|
||||
{
|
||||
/* error already set in mysql_client_find_plugin */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* coverity[var_deref_op] */
|
||||
if (!(pvio= (MARIADB_PVIO *)calloc(1, sizeof(MARIADB_PVIO))))
|
||||
{
|
||||
my_set_error(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* register error routine and methods */
|
||||
pvio->methods= pvio_plugin->methods;
|
||||
pvio->set_error= my_set_error;
|
||||
pvio->type= cinfo->type;
|
||||
|
||||
/* set timeout to connect timeout - after successful connect we will set
|
||||
* correct values for read and write */
|
||||
if (pvio->methods->set_timeout)
|
||||
{
|
||||
pvio->methods->set_timeout(pvio, PVIO_CONNECT_TIMEOUT, cinfo->mysql->options.connect_timeout);
|
||||
pvio->methods->set_timeout(pvio, PVIO_READ_TIMEOUT, cinfo->mysql->options.connect_timeout);
|
||||
pvio->methods->set_timeout(pvio, PVIO_WRITE_TIMEOUT, cinfo->mysql->options.connect_timeout);
|
||||
}
|
||||
|
||||
if (!(pvio->cache= calloc(1, PVIO_READ_AHEAD_CACHE_SIZE)))
|
||||
{
|
||||
PVIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0);
|
||||
free(pvio);
|
||||
return NULL;
|
||||
}
|
||||
pvio->cache_size= 0;
|
||||
pvio->cache_pos= pvio->cache;
|
||||
|
||||
return pvio;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ my_bool ma_pvio_is_alive */
|
||||
my_bool ma_pvio_is_alive(MARIADB_PVIO *pvio)
|
||||
{
|
||||
if (!pvio)
|
||||
return FALSE;
|
||||
if (pvio->methods->is_alive)
|
||||
return pvio->methods->is_alive(pvio);
|
||||
return TRUE;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ int ma_pvio_fast_send */
|
||||
int ma_pvio_fast_send(MARIADB_PVIO *pvio)
|
||||
{
|
||||
if (!pvio || !pvio->methods->fast_send)
|
||||
return 1;
|
||||
return pvio->methods->fast_send(pvio);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ int ma_pvio_keepalive */
|
||||
int ma_pvio_keepalive(MARIADB_PVIO *pvio)
|
||||
{
|
||||
if (!pvio || !pvio->methods->keepalive)
|
||||
return 1;
|
||||
return pvio->methods->keepalive(pvio);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ my_bool ma_pvio_set_timeout */
|
||||
my_bool ma_pvio_set_timeout(MARIADB_PVIO *pvio,
|
||||
enum enum_pvio_timeout type,
|
||||
int timeout)
|
||||
{
|
||||
if (!pvio)
|
||||
return 1;
|
||||
|
||||
if (pvio->methods->set_timeout)
|
||||
return pvio->methods->set_timeout(pvio, type, timeout);
|
||||
return 1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ size_t ma_pvio_read_async */
|
||||
static size_t ma_pvio_read_async(MARIADB_PVIO *pvio, uchar *buffer, size_t length)
|
||||
{
|
||||
ssize_t res= 0;
|
||||
struct mysql_async_context *b= pvio->mysql->options.extension->async_context;
|
||||
int timeout= pvio->timeout[PVIO_READ_TIMEOUT];
|
||||
|
||||
if (!pvio->methods->async_read)
|
||||
{
|
||||
PVIO_SET_ERROR(pvio->mysql, CR_ASYNC_NOT_SUPPORTED, unknown_sqlstate, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (pvio->methods->async_read)
|
||||
res= pvio->methods->async_read(pvio, 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_pvio_read */
|
||||
ssize_t ma_pvio_read(MARIADB_PVIO *pvio, uchar *buffer, size_t length)
|
||||
{
|
||||
ssize_t r= -1;
|
||||
if (!pvio)
|
||||
return -1;
|
||||
if (IS_PVIO_ASYNC_ACTIVE(pvio))
|
||||
{
|
||||
r=
|
||||
#if defined(HAVE_TLS) && !defined(HAVE_SCHANNEL)
|
||||
(pvio->ctls) ? ma_tls_read_async(pvio, buffer, length) :
|
||||
#endif
|
||||
(ssize_t)ma_pvio_read_async(pvio, buffer, length);
|
||||
goto end;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IS_PVIO_ASYNC(pvio))
|
||||
{
|
||||
/*
|
||||
If switching from non-blocking to blocking API usage, set the socket
|
||||
back to blocking mode.
|
||||
*/
|
||||
my_bool old_mode;
|
||||
ma_pvio_blocking(pvio, TRUE, &old_mode);
|
||||
}
|
||||
}
|
||||
|
||||
/* secure connection */
|
||||
#ifdef HAVE_TLS
|
||||
if (pvio->ctls)
|
||||
{
|
||||
r= ma_pvio_tls_read(pvio->ctls, buffer, length);
|
||||
goto end;
|
||||
}
|
||||
#endif
|
||||
if (pvio->methods->read)
|
||||
r= pvio->methods->read(pvio, buffer, length);
|
||||
end:
|
||||
if (pvio_callback)
|
||||
{
|
||||
void (*callback)(int mode, MYSQL *mysql, const uchar *buffer, size_t length);
|
||||
LIST *p= pvio_callback;
|
||||
while (p)
|
||||
{
|
||||
callback= p->data;
|
||||
callback(0, pvio->mysql, buffer, r);
|
||||
p= p->next;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ size_t ma_pvio_cache_read */
|
||||
ssize_t ma_pvio_cache_read(MARIADB_PVIO *pvio, uchar *buffer, size_t length)
|
||||
{
|
||||
ssize_t r;
|
||||
|
||||
if (!pvio)
|
||||
return -1;
|
||||
|
||||
if (!pvio->cache)
|
||||
return ma_pvio_read(pvio, buffer, length);
|
||||
|
||||
if (pvio->cache + pvio->cache_size > pvio->cache_pos)
|
||||
{
|
||||
ssize_t remaining = pvio->cache + pvio->cache_size - pvio->cache_pos;
|
||||
assert(remaining > 0);
|
||||
r= MIN((ssize_t)length, remaining);
|
||||
memcpy(buffer, pvio->cache_pos, r);
|
||||
pvio->cache_pos+= r;
|
||||
}
|
||||
else if (length >= PVIO_READ_AHEAD_CACHE_MIN_SIZE)
|
||||
{
|
||||
r= ma_pvio_read(pvio, buffer, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
r= ma_pvio_read(pvio, pvio->cache, PVIO_READ_AHEAD_CACHE_SIZE);
|
||||
if (r > 0)
|
||||
{
|
||||
if (length < (size_t)r)
|
||||
{
|
||||
pvio->cache_size= r;
|
||||
pvio->cache_pos= pvio->cache + length;
|
||||
r= length;
|
||||
}
|
||||
memcpy(buffer, pvio->cache, r);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ size_t ma_pvio_write_async */
|
||||
static ssize_t ma_pvio_write_async(MARIADB_PVIO *pvio, const uchar *buffer, size_t length)
|
||||
{
|
||||
ssize_t res;
|
||||
struct mysql_async_context *b= pvio->mysql->options.extension->async_context;
|
||||
int timeout= pvio->timeout[PVIO_WRITE_TIMEOUT];
|
||||
|
||||
for (;;)
|
||||
{
|
||||
res= pvio->methods->async_write(pvio, buffer, length);
|
||||
if (res >= 0 || IS_BLOCKING_ERROR())
|
||||
return res;
|
||||
b->events_to_wait_for= MYSQL_WAIT_WRITE;
|
||||
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_pvio_write */
|
||||
ssize_t ma_pvio_write(MARIADB_PVIO *pvio, const uchar *buffer, size_t length)
|
||||
{
|
||||
ssize_t r= 0;
|
||||
|
||||
if (!pvio)
|
||||
return -1;
|
||||
|
||||
if (IS_PVIO_ASYNC_ACTIVE(pvio))
|
||||
{
|
||||
r=
|
||||
#if defined(HAVE_TLS) && !defined(HAVE_SCHANNEL)
|
||||
(pvio->ctls) ? ma_tls_write_async(pvio, buffer, length) :
|
||||
#endif
|
||||
ma_pvio_write_async(pvio, buffer, length);
|
||||
goto end;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IS_PVIO_ASYNC(pvio))
|
||||
{
|
||||
/*
|
||||
If switching from non-blocking to blocking API usage, set the socket
|
||||
back to blocking mode.
|
||||
*/
|
||||
my_bool old_mode;
|
||||
ma_pvio_blocking(pvio, TRUE, &old_mode);
|
||||
}
|
||||
}
|
||||
/* secure connection */
|
||||
#ifdef HAVE_TLS
|
||||
if (pvio->ctls)
|
||||
{
|
||||
r= ma_pvio_tls_write(pvio->ctls, buffer, length);
|
||||
goto end;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (pvio->methods->write)
|
||||
r= pvio->methods->write(pvio, buffer, length);
|
||||
end:
|
||||
if (pvio_callback)
|
||||
{
|
||||
void (*callback)(int mode, MYSQL *mysql, const uchar *buffer, size_t length);
|
||||
LIST *p= pvio_callback;
|
||||
while (p)
|
||||
{
|
||||
callback= p->data;
|
||||
callback(1, pvio->mysql, buffer, r);
|
||||
p= p->next;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ void ma_pvio_close */
|
||||
void ma_pvio_close(MARIADB_PVIO *pvio)
|
||||
{
|
||||
/* free internal structures and close connection */
|
||||
if (pvio)
|
||||
{
|
||||
#ifdef HAVE_TLS
|
||||
if (pvio->ctls)
|
||||
{
|
||||
ma_pvio_tls_close(pvio->ctls);
|
||||
free(pvio->ctls);
|
||||
}
|
||||
#endif
|
||||
if (pvio && pvio->methods->close)
|
||||
pvio->methods->close(pvio);
|
||||
|
||||
if (pvio->cache)
|
||||
free(pvio->cache);
|
||||
|
||||
free(pvio);
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ my_bool ma_pvio_get_handle */
|
||||
my_bool ma_pvio_get_handle(MARIADB_PVIO *pvio, void *handle)
|
||||
{
|
||||
if (pvio && pvio->methods->get_handle)
|
||||
return pvio->methods->get_handle(pvio, handle);
|
||||
return 1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ ma_pvio_wait_async */
|
||||
static my_bool
|
||||
ma_pvio_wait_async(struct mysql_async_context *b, enum enum_pvio_io_event event,
|
||||
int timeout)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case VIO_IO_EVENT_READ:
|
||||
b->events_to_wait_for = MYSQL_WAIT_READ;
|
||||
break;
|
||||
case VIO_IO_EVENT_WRITE:
|
||||
b->events_to_wait_for = MYSQL_WAIT_WRITE;
|
||||
break;
|
||||
case VIO_IO_EVENT_CONNECT:
|
||||
b->events_to_wait_for = MYSQL_WAIT_WRITE | IF_WIN(0, MYSQL_WAIT_EXCEPT);
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
return (b->events_occured & MYSQL_WAIT_TIMEOUT) ? 0 : 1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ ma_pvio_wait_io_or_timeout */
|
||||
int ma_pvio_wait_io_or_timeout(MARIADB_PVIO *pvio, my_bool is_read, int timeout)
|
||||
{
|
||||
if (pvio)
|
||||
{
|
||||
if (IS_PVIO_ASYNC_ACTIVE(pvio))
|
||||
return ma_pvio_wait_async(pvio->mysql->options.extension->async_context,
|
||||
(is_read) ? VIO_IO_EVENT_READ : VIO_IO_EVENT_WRITE,
|
||||
timeout);
|
||||
|
||||
if (pvio && pvio->methods->wait_io_or_timeout)
|
||||
return pvio->methods->wait_io_or_timeout(pvio, is_read, timeout);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ my_bool ma_pvio_connect */
|
||||
my_bool ma_pvio_connect(MARIADB_PVIO *pvio, MA_PVIO_CINFO *cinfo)
|
||||
{
|
||||
if (pvio && pvio->methods->connect)
|
||||
return pvio->methods->connect(pvio, cinfo);
|
||||
return 1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ my_bool ma_pvio_blocking */
|
||||
my_bool ma_pvio_blocking(MARIADB_PVIO *pvio, my_bool block, my_bool *previous_mode)
|
||||
{
|
||||
if (pvio && pvio->methods->blocking)
|
||||
return pvio->methods->blocking(pvio, block, previous_mode) != 0;
|
||||
return 1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ my_bool ma_pvio_is_blocking */
|
||||
my_bool ma_pvio_is_blocking(MARIADB_PVIO *pvio)
|
||||
{
|
||||
if (pvio && pvio->methods->is_blocking)
|
||||
return pvio->methods->is_blocking(pvio);
|
||||
return 1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ ma_pvio_has_data */
|
||||
my_bool ma_pvio_has_data(MARIADB_PVIO *pvio, ssize_t *data_len)
|
||||
{
|
||||
/* check if we still have unread data in cache */
|
||||
if (pvio && pvio->cache)
|
||||
if (pvio->cache_pos > pvio->cache)
|
||||
return test(pvio->cache_pos - pvio->cache);
|
||||
if (pvio && pvio->methods->has_data)
|
||||
return pvio->methods->has_data(pvio, data_len);
|
||||
return 1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
#ifdef HAVE_TLS
|
||||
/* {{{ my_bool ma_pvio_start_ssl */
|
||||
my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio)
|
||||
{
|
||||
if (!pvio || !pvio->mysql)
|
||||
return 1;
|
||||
CLEAR_CLIENT_ERROR(pvio->mysql);
|
||||
if (!(pvio->ctls= ma_pvio_tls_init(pvio->mysql)))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (ma_pvio_tls_connect(pvio->ctls))
|
||||
{
|
||||
free(pvio->ctls);
|
||||
pvio->ctls= NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* default behaviour:
|
||||
1. peer certificate verification
|
||||
2. verify CN (requires option ssl_verify_check)
|
||||
3. verrify finger print
|
||||
*/
|
||||
if ((pvio->mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) &&
|
||||
ma_pvio_tls_verify_server_cert(pvio->ctls))
|
||||
return 1;
|
||||
|
||||
if (pvio->mysql->options.extension &&
|
||||
((pvio->mysql->options.extension->tls_fp && pvio->mysql->options.extension->tls_fp[0]) ||
|
||||
(pvio->mysql->options.extension->tls_fp_list && pvio->mysql->options.extension->tls_fp_list[0])))
|
||||
{
|
||||
if (ma_pvio_tls_check_fp(pvio->ctls,
|
||||
pvio->mysql->options.extension->tls_fp,
|
||||
pvio->mysql->options.extension->tls_fp_list))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
#endif
|
||||
|
||||
/* {{{ ma_pvio_register_callback */
|
||||
int ma_pvio_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;
|
||||
pvio_callback= list_add(pvio_callback, list);
|
||||
}
|
||||
else /* unregister callback function */
|
||||
{
|
||||
LIST *p= pvio_callback;
|
||||
while (p)
|
||||
{
|
||||
if (p->data == callback_function)
|
||||
{
|
||||
list_delete(pvio_callback, p);
|
||||
break;
|
||||
}
|
||||
p= p->next;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
326
vendor/MDBC/libmariadb/ma_sha1.c
vendored
Normal file
326
vendor/MDBC/libmariadb/ma_sha1.c
vendored
Normal file
@ -0,0 +1,326 @@
|
||||
/****************************************************************************
|
||||
Copyright (C) 2012 Monty Program AB
|
||||
2016 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 code came from the PHP project, initially written by
|
||||
Stefan Esser */
|
||||
|
||||
|
||||
#include "ma_global.h"
|
||||
#include "string.h"
|
||||
|
||||
/* This code is heavily based on the PHP md5 implementation */
|
||||
|
||||
#include "ma_sha1.h"
|
||||
|
||||
|
||||
static void ma_SHA1Transform(uint32[5], const unsigned char[64]);
|
||||
static void ma_SHA1Encode(unsigned char *, uint32 *, unsigned int);
|
||||
static void ma_SHA1Decode(uint32 *, const unsigned char *, unsigned int);
|
||||
|
||||
static unsigned char PADDING[64] =
|
||||
{
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
/* F, G, H and I are basic SHA1 functions.
|
||||
*/
|
||||
#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
|
||||
#define G(x, y, z) ((x) ^ (y) ^ (z))
|
||||
#define H(x, y, z) (((x) & (y)) | ((z) & ((x) | (y))))
|
||||
#define I(x, y, z) ((x) ^ (y) ^ (z))
|
||||
|
||||
/* ROTATE_LEFT rotates x left n bits.
|
||||
*/
|
||||
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
|
||||
|
||||
/* W[i]
|
||||
*/
|
||||
#define W(i) ( tmp=x[(i-3)&15]^x[(i-8)&15]^x[(i-14)&15]^x[i&15], \
|
||||
(x[i&15]=ROTATE_LEFT(tmp, 1)) )
|
||||
|
||||
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
|
||||
*/
|
||||
#define FF(a, b, c, d, e, w) { \
|
||||
(e) += F ((b), (c), (d)) + (w) + (uint32)(0x5A827999); \
|
||||
(e) += ROTATE_LEFT ((a), 5); \
|
||||
(b) = ROTATE_LEFT((b), 30); \
|
||||
}
|
||||
#define GG(a, b, c, d, e, w) { \
|
||||
(e) += G ((b), (c), (d)) + (w) + (uint32)(0x6ED9EBA1); \
|
||||
(e) += ROTATE_LEFT ((a), 5); \
|
||||
(b) = ROTATE_LEFT((b), 30); \
|
||||
}
|
||||
#define HH(a, b, c, d, e, w) { \
|
||||
(e) += H ((b), (c), (d)) + (w) + (uint32)(0x8F1BBCDC); \
|
||||
(e) += ROTATE_LEFT ((a), 5); \
|
||||
(b) = ROTATE_LEFT((b), 30); \
|
||||
}
|
||||
#define II(a, b, c, d, e, w) { \
|
||||
(e) += I ((b), (c), (d)) + (w) + (uint32)(0xCA62C1D6); \
|
||||
(e) += ROTATE_LEFT ((a), 5); \
|
||||
(b) = ROTATE_LEFT((b), 30); \
|
||||
}
|
||||
|
||||
|
||||
/* {{{ ma_SHA1Init
|
||||
* SHA1 initialization. Begins an SHA1 operation, writing a new context.
|
||||
*/
|
||||
void ma_SHA1Init(_MA_SHA1_CTX * context)
|
||||
{
|
||||
context->count[0] = context->count[1] = 0;
|
||||
/* Load magic initialization constants.
|
||||
*/
|
||||
context->state[0] = 0x67452301;
|
||||
context->state[1] = 0xefcdab89;
|
||||
context->state[2] = 0x98badcfe;
|
||||
context->state[3] = 0x10325476;
|
||||
context->state[4] = 0xc3d2e1f0;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ ma_SHA1Update
|
||||
SHA1 block update operation. Continues an SHA1 message-digest
|
||||
operation, processing another message block, and updating the
|
||||
context.
|
||||
*/
|
||||
void ma_SHA1Update(_MA_SHA1_CTX * context, const unsigned char *input,
|
||||
size_t inputLen)
|
||||
{
|
||||
unsigned int i, index, partLen;
|
||||
|
||||
/* Compute number of bytes mod 64 */
|
||||
index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
|
||||
|
||||
/* Update number of bits */
|
||||
if ((context->count[0] += ((uint32) inputLen << 3))
|
||||
< ((uint32) inputLen << 3))
|
||||
context->count[1]++;
|
||||
context->count[1] += ((uint32) inputLen >> 29);
|
||||
|
||||
partLen = 64 - index;
|
||||
|
||||
/* Transform as many times as possible.
|
||||
*/
|
||||
if (inputLen >= partLen) {
|
||||
memcpy
|
||||
((unsigned char*) & context->buffer[index], (unsigned char*) input, partLen);
|
||||
ma_SHA1Transform(context->state, context->buffer);
|
||||
|
||||
for (i = partLen; i + 63 < inputLen; i += 64)
|
||||
ma_SHA1Transform(context->state, &input[i]);
|
||||
|
||||
index = 0;
|
||||
} else
|
||||
i = 0;
|
||||
|
||||
/* Buffer remaining input */
|
||||
memcpy
|
||||
((unsigned char*) & context->buffer[index], (unsigned char*) & input[i],
|
||||
inputLen - i);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ ma_SHA1Final
|
||||
SHA1 finalization. Ends an SHA1 message-digest operation, writing the
|
||||
the message digest and zeroizing the context.
|
||||
*/
|
||||
void ma_SHA1Final(unsigned char digest[20], _MA_SHA1_CTX * context)
|
||||
{
|
||||
unsigned char bits[8];
|
||||
unsigned int index, padLen;
|
||||
|
||||
/* Save number of bits */
|
||||
bits[7] = context->count[0] & 0xFF;
|
||||
bits[6] = (context->count[0] >> 8) & 0xFF;
|
||||
bits[5] = (context->count[0] >> 16) & 0xFF;
|
||||
bits[4] = (context->count[0] >> 24) & 0xFF;
|
||||
bits[3] = context->count[1] & 0xFF;
|
||||
bits[2] = (context->count[1] >> 8) & 0xFF;
|
||||
bits[1] = (context->count[1] >> 16) & 0xFF;
|
||||
bits[0] = (context->count[1] >> 24) & 0xFF;
|
||||
|
||||
/* Pad out to 56 mod 64.
|
||||
*/
|
||||
index = (unsigned int) ((context->count[0] >> 3) & 0x3f);
|
||||
padLen = (index < 56) ? (56 - index) : (120 - index);
|
||||
ma_SHA1Update(context, PADDING, padLen);
|
||||
|
||||
/* Append length (before padding) */
|
||||
ma_SHA1Update(context, bits, 8);
|
||||
|
||||
/* Store state in digest */
|
||||
ma_SHA1Encode(digest, context->state, 20);
|
||||
|
||||
/* Zeroize sensitive information.
|
||||
*/
|
||||
memset((unsigned char*) context, 0, sizeof(*context));
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ ma_SHA1Transform
|
||||
* SHA1 basic transformation. Transforms state based on block.
|
||||
*/
|
||||
static void ma_SHA1Transform(uint32 state[5], const unsigned char block[64])
|
||||
{
|
||||
uint32 a = state[0], b = state[1], c = state[2];
|
||||
uint32 d = state[3], e = state[4], x[16], tmp;
|
||||
|
||||
ma_SHA1Decode(x, block, 64);
|
||||
|
||||
/* Round 1 */
|
||||
FF(a, b, c, d, e, x[0]); /* 1 */
|
||||
FF(e, a, b, c, d, x[1]); /* 2 */
|
||||
FF(d, e, a, b, c, x[2]); /* 3 */
|
||||
FF(c, d, e, a, b, x[3]); /* 4 */
|
||||
FF(b, c, d, e, a, x[4]); /* 5 */
|
||||
FF(a, b, c, d, e, x[5]); /* 6 */
|
||||
FF(e, a, b, c, d, x[6]); /* 7 */
|
||||
FF(d, e, a, b, c, x[7]); /* 8 */
|
||||
FF(c, d, e, a, b, x[8]); /* 9 */
|
||||
FF(b, c, d, e, a, x[9]); /* 10 */
|
||||
FF(a, b, c, d, e, x[10]); /* 11 */
|
||||
FF(e, a, b, c, d, x[11]); /* 12 */
|
||||
FF(d, e, a, b, c, x[12]); /* 13 */
|
||||
FF(c, d, e, a, b, x[13]); /* 14 */
|
||||
FF(b, c, d, e, a, x[14]); /* 15 */
|
||||
FF(a, b, c, d, e, x[15]); /* 16 */
|
||||
FF(e, a, b, c, d, W(16)); /* 17 */
|
||||
FF(d, e, a, b, c, W(17)); /* 18 */
|
||||
FF(c, d, e, a, b, W(18)); /* 19 */
|
||||
FF(b, c, d, e, a, W(19)); /* 20 */
|
||||
|
||||
/* Round 2 */
|
||||
GG(a, b, c, d, e, W(20)); /* 21 */
|
||||
GG(e, a, b, c, d, W(21)); /* 22 */
|
||||
GG(d, e, a, b, c, W(22)); /* 23 */
|
||||
GG(c, d, e, a, b, W(23)); /* 24 */
|
||||
GG(b, c, d, e, a, W(24)); /* 25 */
|
||||
GG(a, b, c, d, e, W(25)); /* 26 */
|
||||
GG(e, a, b, c, d, W(26)); /* 27 */
|
||||
GG(d, e, a, b, c, W(27)); /* 28 */
|
||||
GG(c, d, e, a, b, W(28)); /* 29 */
|
||||
GG(b, c, d, e, a, W(29)); /* 30 */
|
||||
GG(a, b, c, d, e, W(30)); /* 31 */
|
||||
GG(e, a, b, c, d, W(31)); /* 32 */
|
||||
GG(d, e, a, b, c, W(32)); /* 33 */
|
||||
GG(c, d, e, a, b, W(33)); /* 34 */
|
||||
GG(b, c, d, e, a, W(34)); /* 35 */
|
||||
GG(a, b, c, d, e, W(35)); /* 36 */
|
||||
GG(e, a, b, c, d, W(36)); /* 37 */
|
||||
GG(d, e, a, b, c, W(37)); /* 38 */
|
||||
GG(c, d, e, a, b, W(38)); /* 39 */
|
||||
GG(b, c, d, e, a, W(39)); /* 40 */
|
||||
|
||||
/* Round 3 */
|
||||
HH(a, b, c, d, e, W(40)); /* 41 */
|
||||
HH(e, a, b, c, d, W(41)); /* 42 */
|
||||
HH(d, e, a, b, c, W(42)); /* 43 */
|
||||
HH(c, d, e, a, b, W(43)); /* 44 */
|
||||
HH(b, c, d, e, a, W(44)); /* 45 */
|
||||
HH(a, b, c, d, e, W(45)); /* 46 */
|
||||
HH(e, a, b, c, d, W(46)); /* 47 */
|
||||
HH(d, e, a, b, c, W(47)); /* 48 */
|
||||
HH(c, d, e, a, b, W(48)); /* 49 */
|
||||
HH(b, c, d, e, a, W(49)); /* 50 */
|
||||
HH(a, b, c, d, e, W(50)); /* 51 */
|
||||
HH(e, a, b, c, d, W(51)); /* 52 */
|
||||
HH(d, e, a, b, c, W(52)); /* 53 */
|
||||
HH(c, d, e, a, b, W(53)); /* 54 */
|
||||
HH(b, c, d, e, a, W(54)); /* 55 */
|
||||
HH(a, b, c, d, e, W(55)); /* 56 */
|
||||
HH(e, a, b, c, d, W(56)); /* 57 */
|
||||
HH(d, e, a, b, c, W(57)); /* 58 */
|
||||
HH(c, d, e, a, b, W(58)); /* 59 */
|
||||
HH(b, c, d, e, a, W(59)); /* 60 */
|
||||
|
||||
/* Round 4 */
|
||||
II(a, b, c, d, e, W(60)); /* 61 */
|
||||
II(e, a, b, c, d, W(61)); /* 62 */
|
||||
II(d, e, a, b, c, W(62)); /* 63 */
|
||||
II(c, d, e, a, b, W(63)); /* 64 */
|
||||
II(b, c, d, e, a, W(64)); /* 65 */
|
||||
II(a, b, c, d, e, W(65)); /* 66 */
|
||||
II(e, a, b, c, d, W(66)); /* 67 */
|
||||
II(d, e, a, b, c, W(67)); /* 68 */
|
||||
II(c, d, e, a, b, W(68)); /* 69 */
|
||||
II(b, c, d, e, a, W(69)); /* 70 */
|
||||
II(a, b, c, d, e, W(70)); /* 71 */
|
||||
II(e, a, b, c, d, W(71)); /* 72 */
|
||||
II(d, e, a, b, c, W(72)); /* 73 */
|
||||
II(c, d, e, a, b, W(73)); /* 74 */
|
||||
II(b, c, d, e, a, W(74)); /* 75 */
|
||||
II(a, b, c, d, e, W(75)); /* 76 */
|
||||
II(e, a, b, c, d, W(76)); /* 77 */
|
||||
II(d, e, a, b, c, W(77)); /* 78 */
|
||||
II(c, d, e, a, b, W(78)); /* 79 */
|
||||
II(b, c, d, e, a, W(79)); /* 80 */
|
||||
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
|
||||
/* Zeroize sensitive information. */
|
||||
memset((unsigned char*) x, 0, sizeof(x));
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ ma_SHA1Encode
|
||||
Encodes input (uint32) into output (unsigned char). Assumes len is
|
||||
a multiple of 4.
|
||||
*/
|
||||
static void ma_SHA1Encode(unsigned char *output, uint32 *input, unsigned int len)
|
||||
{
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0, j = 0; j < len; i++, j += 4) {
|
||||
output[j] = (unsigned char) ((input[i] >> 24) & 0xff);
|
||||
output[j + 1] = (unsigned char) ((input[i] >> 16) & 0xff);
|
||||
output[j + 2] = (unsigned char) ((input[i] >> 8) & 0xff);
|
||||
output[j + 3] = (unsigned char) (input[i] & 0xff);
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ ma_SHA1Decode
|
||||
Decodes input (unsigned char) into output (uint32). Assumes len is
|
||||
a multiple of 4.
|
||||
*/
|
||||
static void ma_SHA1Decode(uint32 *output, const unsigned char * input, unsigned int len)
|
||||
{
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0, j = 0; j < len; i++, j += 4)
|
||||
output[i] = ((uint32) input[j + 3]) | (((uint32) input[j + 2]) << 8) |
|
||||
(((uint32) input[j + 1]) << 16) | (((uint32) input[j]) << 24);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
* c-basic-offset: 4
|
||||
* End:
|
||||
* vim600: sw=4 ts=4 fdm=marker
|
||||
* vim<600: sw=4 ts=4
|
||||
*/
|
1362
vendor/MDBC/libmariadb/ma_stmt_codec.c
vendored
Normal file
1362
vendor/MDBC/libmariadb/ma_stmt_codec.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
163
vendor/MDBC/libmariadb/ma_string.c
vendored
Normal file
163
vendor/MDBC/libmariadb/ma_string.c
vendored
Normal file
@ -0,0 +1,163 @@
|
||||
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
2016 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, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02111-1301, USA */
|
||||
|
||||
/*
|
||||
Code for handling strings which can grow dynamically.
|
||||
Copyright Monty Program KB.
|
||||
By monty.
|
||||
*/
|
||||
|
||||
#include <ma_global.h>
|
||||
#include <ma_sys.h>
|
||||
#include <ma_string.h>
|
||||
|
||||
my_bool ma_init_dynamic_string(DYNAMIC_STRING *str, const char *init_str,
|
||||
size_t init_alloc, size_t alloc_increment)
|
||||
{
|
||||
uint length;
|
||||
|
||||
if (!alloc_increment)
|
||||
alloc_increment=128;
|
||||
length=1;
|
||||
if (init_str && (length= (uint) strlen(init_str)+1) < init_alloc)
|
||||
init_alloc=((length+alloc_increment-1)/alloc_increment)*alloc_increment;
|
||||
if (!init_alloc)
|
||||
init_alloc=alloc_increment;
|
||||
|
||||
if (!(str->str=(char*) malloc(init_alloc)))
|
||||
return(TRUE);
|
||||
str->length=length-1;
|
||||
if (init_str)
|
||||
memcpy(str->str,init_str,length);
|
||||
str->max_length=init_alloc;
|
||||
str->alloc_increment=alloc_increment;
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
my_bool ma_dynstr_set(DYNAMIC_STRING *str, const char *init_str)
|
||||
{
|
||||
uint length;
|
||||
|
||||
if (init_str && (length= (uint) strlen(init_str)+1) > str->max_length)
|
||||
{
|
||||
str->max_length=((length+str->alloc_increment-1)/str->alloc_increment)*
|
||||
str->alloc_increment;
|
||||
if (!str->max_length)
|
||||
str->max_length=str->alloc_increment;
|
||||
if (!(str->str=(char*) realloc(str->str,str->max_length)))
|
||||
return(TRUE);
|
||||
}
|
||||
if (init_str)
|
||||
{
|
||||
str->length=length-1;
|
||||
memcpy(str->str,init_str,length);
|
||||
}
|
||||
else
|
||||
str->length=0;
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
|
||||
my_bool ma_dynstr_realloc(DYNAMIC_STRING *str, size_t additional_size)
|
||||
{
|
||||
if (!additional_size) return(FALSE);
|
||||
if (str->length + additional_size > str->max_length)
|
||||
{
|
||||
str->max_length=((str->length + additional_size+str->alloc_increment-1)/
|
||||
str->alloc_increment)*str->alloc_increment;
|
||||
if (!(str->str=(char*) realloc(str->str,str->max_length)))
|
||||
return(TRUE);
|
||||
}
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
|
||||
my_bool ma_dynstr_append(DYNAMIC_STRING *str, const char *append)
|
||||
{
|
||||
return ma_dynstr_append_mem(str,append,strlen(append));
|
||||
}
|
||||
|
||||
my_bool ma_dynstr_append_quoted(DYNAMIC_STRING *str,
|
||||
const char *append, size_t len,
|
||||
char quote)
|
||||
{
|
||||
size_t additional= str->alloc_increment;
|
||||
size_t lim= additional;
|
||||
uint i;
|
||||
|
||||
if (ma_dynstr_realloc(str, len + additional + 2))
|
||||
return TRUE;
|
||||
str->str[str->length++]= quote;
|
||||
for (i= 0; i < len; i++)
|
||||
{
|
||||
register char c= append[i];
|
||||
if (c == quote || c == '\\')
|
||||
{
|
||||
if (!lim)
|
||||
{
|
||||
if (ma_dynstr_realloc(str, additional))
|
||||
return TRUE;
|
||||
lim= additional;
|
||||
}
|
||||
lim--;
|
||||
str->str[str->length++]= '\\';
|
||||
}
|
||||
str->str[str->length++]= c;
|
||||
}
|
||||
str->str[str->length++]= quote;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
my_bool ma_dynstr_append_mem(DYNAMIC_STRING *str, const char *append,
|
||||
size_t length)
|
||||
{
|
||||
char *new_ptr;
|
||||
if (str->length+length >= str->max_length)
|
||||
{
|
||||
size_t new_length=(str->length+length+str->alloc_increment)/
|
||||
str->alloc_increment;
|
||||
new_length*=str->alloc_increment;
|
||||
if (!(new_ptr=(char*) realloc(str->str,new_length)))
|
||||
return TRUE;
|
||||
str->str=new_ptr;
|
||||
str->max_length=new_length;
|
||||
}
|
||||
memcpy(str->str + str->length,append,length);
|
||||
str->length+=length;
|
||||
str->str[str->length]=0; /* Safety for C programs */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
void ma_dynstr_free(DYNAMIC_STRING *str)
|
||||
{
|
||||
if (str->str)
|
||||
{
|
||||
free(str->str);
|
||||
str->str=0;
|
||||
}
|
||||
}
|
||||
|
||||
char *ma_strmake(register char *dst, register const char *src, size_t length)
|
||||
{
|
||||
while (length--)
|
||||
if (! (*dst++ = *src++))
|
||||
return dst-1;
|
||||
*dst=0;
|
||||
return dst;
|
||||
}
|
65
vendor/MDBC/libmariadb/ma_time.c
vendored
Normal file
65
vendor/MDBC/libmariadb/ma_time.c
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
/****************************************************************************
|
||||
Copyright (C) 2013 Monty Program AB
|
||||
2016 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
|
||||
*****************************************************************************/
|
||||
#include <ma_global.h>
|
||||
#include <mysql.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
size_t mariadb_time_to_string(const MYSQL_TIME *tm, char *time_str, size_t len,
|
||||
unsigned int digits)
|
||||
{
|
||||
size_t length;
|
||||
|
||||
if (!time_str || !len)
|
||||
return 0;
|
||||
|
||||
if (digits == AUTO_SEC_PART_DIGITS)
|
||||
digits= (tm->second_part) ? SEC_PART_DIGITS : 0;
|
||||
|
||||
switch(tm->time_type) {
|
||||
case MYSQL_TIMESTAMP_DATE:
|
||||
length= snprintf(time_str, len, "%04u-%02u-%02u", tm->year, tm->month, tm->day);
|
||||
digits= 0;
|
||||
break;
|
||||
case MYSQL_TIMESTAMP_DATETIME:
|
||||
length= snprintf(time_str, len, "%04u-%02u-%02u %02u:%02u:%02u",
|
||||
tm->year, tm->month, tm->day, tm->hour, tm->minute, tm->second);
|
||||
break;
|
||||
case MYSQL_TIMESTAMP_TIME:
|
||||
length= snprintf(time_str, len, "%s%02u:%02u:%02u",
|
||||
(tm->neg ? "-" : ""), tm->hour, tm->minute, tm->second);
|
||||
break;
|
||||
default:
|
||||
time_str[0]= '\0';
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
if (digits && (len < length))
|
||||
{
|
||||
char helper[16];
|
||||
snprintf(helper, 16, ".%%0%du", digits);
|
||||
length+= snprintf(time_str + length, len - length, helper, digits);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
232
vendor/MDBC/libmariadb/ma_tls.c
vendored
Normal file
232
vendor/MDBC/libmariadb/ma_tls.c
vendored
Normal file
@ -0,0 +1,232 @@
|
||||
/************************************************************************************
|
||||
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_TLS
|
||||
|
||||
#include <ma_global.h>
|
||||
#include <ma_sys.h>
|
||||
#include <ma_common.h>
|
||||
#include <string.h>
|
||||
#include <errmsg.h>
|
||||
#include <ma_pvio.h>
|
||||
#include <ma_tls.h>
|
||||
#include <mysql/client_plugin.h>
|
||||
#include <mariadb/ma_io.h>
|
||||
|
||||
#ifdef HAVE_NONBLOCK
|
||||
#include <mariadb_async.h>
|
||||
#include <ma_context.h>
|
||||
#endif
|
||||
|
||||
/* Errors should be handled via pvio callback function */
|
||||
my_bool ma_tls_initialized= FALSE;
|
||||
unsigned int mariadb_deinitialize_ssl= 1;
|
||||
|
||||
const char *tls_protocol_version[]=
|
||||
{"SSLv3", "TLSv1.0", "TLSv1.1", "TLSv1.2", "TLSv1.3", "Unknown"};
|
||||
|
||||
MARIADB_TLS *ma_pvio_tls_init(MYSQL *mysql)
|
||||
{
|
||||
MARIADB_TLS *ctls= NULL;
|
||||
|
||||
if (!ma_tls_initialized)
|
||||
ma_tls_start(mysql->net.last_error, MYSQL_ERRMSG_SIZE);
|
||||
|
||||
if (!(ctls= (MARIADB_TLS *)calloc(1, sizeof(MARIADB_TLS))))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* register error routine and methods */
|
||||
ctls->pvio= mysql->net.pvio;
|
||||
if (!(ctls->ssl= ma_tls_init(mysql)))
|
||||
{
|
||||
free(ctls);
|
||||
ctls= NULL;
|
||||
}
|
||||
return ctls;
|
||||
}
|
||||
|
||||
my_bool ma_pvio_tls_connect(MARIADB_TLS *ctls)
|
||||
{
|
||||
my_bool rc;
|
||||
|
||||
if ((rc= ma_tls_connect(ctls)))
|
||||
ma_tls_close(ctls);
|
||||
return rc;
|
||||
}
|
||||
|
||||
ssize_t ma_pvio_tls_read(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
|
||||
{
|
||||
return ma_tls_read(ctls, buffer, length);
|
||||
}
|
||||
|
||||
ssize_t ma_pvio_tls_write(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
|
||||
{
|
||||
return ma_tls_write(ctls, buffer, length);
|
||||
}
|
||||
|
||||
my_bool ma_pvio_tls_close(MARIADB_TLS *ctls)
|
||||
{
|
||||
return ma_tls_close(ctls);
|
||||
}
|
||||
|
||||
int ma_pvio_tls_verify_server_cert(MARIADB_TLS *ctls)
|
||||
{
|
||||
return ma_tls_verify_server_cert(ctls);
|
||||
}
|
||||
|
||||
const char *ma_pvio_tls_cipher(MARIADB_TLS *ctls)
|
||||
{
|
||||
return ma_tls_get_cipher(ctls);
|
||||
}
|
||||
|
||||
void ma_pvio_tls_end()
|
||||
{
|
||||
ma_tls_end();
|
||||
}
|
||||
|
||||
int ma_pvio_tls_get_protocol_version_id(MARIADB_TLS *ctls)
|
||||
{
|
||||
return ma_tls_get_protocol_version(ctls);
|
||||
}
|
||||
|
||||
const char *ma_pvio_tls_get_protocol_version(MARIADB_TLS *ctls)
|
||||
{
|
||||
int version;
|
||||
|
||||
version= ma_tls_get_protocol_version(ctls);
|
||||
if (version < 0 || version > PROTOCOL_MAX)
|
||||
return tls_protocol_version[PROTOCOL_UNKNOWN];
|
||||
return tls_protocol_version[version];
|
||||
}
|
||||
|
||||
static signed char ma_hex2int(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if (c >= 'A' && c <= 'F')
|
||||
return 10 + c - 'A';
|
||||
if (c >= 'a' && c <= 'f')
|
||||
return 10 + c - 'a';
|
||||
return -1;
|
||||
}
|
||||
|
||||
static my_bool ma_pvio_tls_compare_fp(const char *cert_fp,
|
||||
unsigned int cert_fp_len,
|
||||
const char *fp, unsigned int fp_len)
|
||||
{
|
||||
char *p= (char *)fp,
|
||||
*c;
|
||||
|
||||
/* check length */
|
||||
if (cert_fp_len != 20)
|
||||
return 1;
|
||||
|
||||
/* We support two formats:
|
||||
2 digits hex numbers, separated by colons (length=59)
|
||||
20 * 2 digits hex numbers without separators (length = 40)
|
||||
*/
|
||||
if (fp_len != (strchr(fp, ':') ? 59 : 40))
|
||||
return 1;
|
||||
|
||||
for(c= (char *)cert_fp; c < cert_fp + cert_fp_len; c++)
|
||||
{
|
||||
signed char d1, d2;
|
||||
if (*p == ':')
|
||||
p++;
|
||||
if (p - fp > (int)fp_len -1)
|
||||
return 1;
|
||||
if ((d1 = ma_hex2int(*p)) == - 1 ||
|
||||
(d2 = ma_hex2int(*(p+1))) == -1 ||
|
||||
(char)(d1 * 16 + d2) != *c)
|
||||
return 1;
|
||||
p+= 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
my_bool ma_pvio_tls_check_fp(MARIADB_TLS *ctls, const char *fp, const char *fp_list)
|
||||
{
|
||||
unsigned int cert_fp_len= 64;
|
||||
char *cert_fp= NULL;
|
||||
my_bool rc=1;
|
||||
MYSQL *mysql= ctls->pvio->mysql;
|
||||
|
||||
cert_fp= (char *)malloc(cert_fp_len);
|
||||
|
||||
if ((cert_fp_len= ma_tls_get_finger_print(ctls, cert_fp, cert_fp_len)) < 1)
|
||||
goto end;
|
||||
if (fp)
|
||||
rc= ma_pvio_tls_compare_fp(cert_fp, cert_fp_len, fp, (unsigned int)strlen(fp));
|
||||
else if (fp_list)
|
||||
{
|
||||
MA_FILE *fp;
|
||||
char buff[255];
|
||||
|
||||
if (!(fp = ma_open(fp_list, "r", mysql)))
|
||||
goto end;
|
||||
|
||||
while (ma_gets(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_pvio_tls_compare_fp(cert_fp, cert_fp_len, buff, (unsigned int)strlen(buff)))
|
||||
{
|
||||
/* finger print is valid: close file and exit */
|
||||
ma_close(fp);
|
||||
rc= 0;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/* No finger print matched - close file and return error */
|
||||
ma_close(fp);
|
||||
}
|
||||
|
||||
end:
|
||||
if (cert_fp)
|
||||
free(cert_fp);
|
||||
if (rc)
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR),
|
||||
"Fingerprint verification of server certificate failed");
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
#endif /* HAVE_TLS */
|
1946
vendor/MDBC/libmariadb/mariadb_async.c
vendored
Normal file
1946
vendor/MDBC/libmariadb/mariadb_async.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
74
vendor/MDBC/libmariadb/mariadb_charset.c
vendored
Normal file
74
vendor/MDBC/libmariadb/mariadb_charset.c
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
2016 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, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02111-1301, USA */
|
||||
|
||||
#include <ma_global.h>
|
||||
#include <ma_sys.h>
|
||||
// #include "mysys_err.h"
|
||||
#include <mariadb_ctype.h>
|
||||
#include <ma_string.h>
|
||||
|
||||
MARIADB_CHARSET_INFO *ma_default_charset_info; /* will be set in mysql_server_init */
|
||||
MARIADB_CHARSET_INFO *ma_charset_bin= (MARIADB_CHARSET_INFO *)&mariadb_compiled_charsets[32];
|
||||
MARIADB_CHARSET_INFO *ma_charset_latin1= (MARIADB_CHARSET_INFO *)&mariadb_compiled_charsets[5];
|
||||
MARIADB_CHARSET_INFO *ma_charset_utf8_general_ci= (MARIADB_CHARSET_INFO *)&mariadb_compiled_charsets[21];
|
||||
MARIADB_CHARSET_INFO *ma_charset_utf16le_general_ci= (MARIADB_CHARSET_INFO *)&mariadb_compiled_charsets[68];
|
||||
|
||||
MARIADB_CHARSET_INFO * STDCALL mysql_get_charset_by_nr(uint cs_number)
|
||||
{
|
||||
int i= 0;
|
||||
|
||||
while (mariadb_compiled_charsets[i].nr && cs_number != mariadb_compiled_charsets[i].nr)
|
||||
i++;
|
||||
|
||||
return (mariadb_compiled_charsets[i].nr) ? (MARIADB_CHARSET_INFO *)&mariadb_compiled_charsets[i] : NULL;
|
||||
}
|
||||
|
||||
my_bool set_default_charset(uint cs, myf flags __attribute__((unused)))
|
||||
{
|
||||
MARIADB_CHARSET_INFO *new_charset;
|
||||
new_charset = mysql_get_charset_by_nr(cs);
|
||||
if (!new_charset)
|
||||
{
|
||||
return(TRUE); /* error */
|
||||
}
|
||||
ma_default_charset_info = new_charset;
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
MARIADB_CHARSET_INFO * STDCALL mysql_get_charset_by_name(const char *cs_name)
|
||||
{
|
||||
int i= 0;
|
||||
|
||||
while (mariadb_compiled_charsets[i].nr && strcmp(cs_name, mariadb_compiled_charsets[i].csname) != 0)
|
||||
i++;
|
||||
|
||||
return (mariadb_compiled_charsets[i].nr) ? (MARIADB_CHARSET_INFO *)&mariadb_compiled_charsets[i] : NULL;
|
||||
}
|
||||
|
||||
my_bool set_default_charset_by_name(const char *cs_name, myf flags __attribute__((unused)))
|
||||
{
|
||||
MARIADB_CHARSET_INFO *new_charset;
|
||||
new_charset = mysql_get_charset_by_name(cs_name);
|
||||
if (!new_charset)
|
||||
{
|
||||
return(TRUE); /* error */
|
||||
}
|
||||
|
||||
ma_default_charset_info = new_charset;
|
||||
return(FALSE);
|
||||
}
|
4368
vendor/MDBC/libmariadb/mariadb_dyncol.c
vendored
Normal file
4368
vendor/MDBC/libmariadb/mariadb_dyncol.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
4522
vendor/MDBC/libmariadb/mariadb_lib.c
vendored
Normal file
4522
vendor/MDBC/libmariadb/mariadb_lib.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
513
vendor/MDBC/libmariadb/mariadb_rpl.c
vendored
Normal file
513
vendor/MDBC/libmariadb/mariadb_rpl.c
vendored
Normal file
@ -0,0 +1,513 @@
|
||||
/************************************************************************************
|
||||
Copyright (C) 2018 MariaDB Corpoeation 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_global.h>
|
||||
#include <ma_sys.h>
|
||||
#include <mysql.h>
|
||||
#include <errmsg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <zlib.h>
|
||||
#include <mariadb_rpl.h>
|
||||
|
||||
static int rpl_alloc_string(MARIADB_RPL_EVENT *event,
|
||||
MARIADB_STRING *s,
|
||||
unsigned char *buffer,
|
||||
size_t len)
|
||||
{
|
||||
if (!(s->str= ma_alloc_root(&event->memroot, len)))
|
||||
return 1;
|
||||
memcpy(s->str, buffer, len);
|
||||
s->length= len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
MARIADB_RPL * STDCALL mariadb_rpl_init_ex(MYSQL *mysql, unsigned int version)
|
||||
{
|
||||
MARIADB_RPL *rpl;
|
||||
|
||||
if (version < MARIADB_RPL_REQUIRED_VERSION ||
|
||||
version > MARIADB_RPL_VERSION)
|
||||
{
|
||||
my_set_error(mysql, CR_VERSION_MISMATCH, SQLSTATE_UNKNOWN, 0, version,
|
||||
MARIADB_RPL_VERSION, MARIADB_RPL_REQUIRED_VERSION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!mysql)
|
||||
return NULL;
|
||||
|
||||
if (!(rpl= (MARIADB_RPL *)calloc(1, sizeof(MARIADB_RPL))))
|
||||
{
|
||||
SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
|
||||
return 0;
|
||||
}
|
||||
rpl->version= version;
|
||||
rpl->mysql= mysql;
|
||||
return rpl;
|
||||
}
|
||||
|
||||
void STDCALL mariadb_free_rpl_event(MARIADB_RPL_EVENT *event)
|
||||
{
|
||||
if (event)
|
||||
{
|
||||
ma_free_root(&event->memroot, MYF(0));
|
||||
free(event);
|
||||
}
|
||||
}
|
||||
|
||||
int STDCALL mariadb_rpl_open(MARIADB_RPL *rpl)
|
||||
{
|
||||
unsigned char *ptr, *buf;
|
||||
if (!rpl || !rpl->mysql)
|
||||
return 1;
|
||||
|
||||
/* COM_BINLOG_DUMP:
|
||||
Ofs Len Data
|
||||
0 1 COM_BINLOG_DUMP
|
||||
1 4 position
|
||||
5 2 flags
|
||||
7 4 server id
|
||||
11 * filename
|
||||
|
||||
* = filename length
|
||||
|
||||
*/
|
||||
ptr= buf=
|
||||
#ifdef WIN32
|
||||
(unsigned char *)_alloca(rpl->filename_length + 11);
|
||||
#else
|
||||
(unsigned char *)alloca(rpl->filename_length + 11);
|
||||
#endif
|
||||
|
||||
int4store(ptr, (unsigned int)rpl->start_position);
|
||||
ptr+= 4;
|
||||
int2store(ptr, rpl->flags);
|
||||
ptr+= 2;
|
||||
int4store(ptr, rpl->server_id);
|
||||
ptr+= 4;
|
||||
memcpy(ptr, rpl->filename, rpl->filename_length);
|
||||
ptr+= rpl->filename_length;
|
||||
|
||||
if (ma_simple_command(rpl->mysql, COM_BINLOG_DUMP, (const char *)buf, ptr - buf, 1, 0))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
MARIADB_RPL_EVENT * STDCALL mariadb_rpl_fetch(MARIADB_RPL *rpl, MARIADB_RPL_EVENT *event)
|
||||
{
|
||||
unsigned char *ev;
|
||||
size_t len;
|
||||
MARIADB_RPL_EVENT *rpl_event= 0;
|
||||
|
||||
if (!rpl || !rpl->mysql)
|
||||
return 0;
|
||||
|
||||
while (1) {
|
||||
unsigned long pkt_len= ma_net_safe_read(rpl->mysql);
|
||||
|
||||
if (pkt_len == packet_error)
|
||||
{
|
||||
rpl->buffer_size= 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* EOF packet:
|
||||
see https://mariadb.com/kb/en/library/eof_packet/
|
||||
Packet length must be less than 9 bytes, EOF header
|
||||
is 0xFE.
|
||||
*/
|
||||
if (pkt_len < 9 && rpl->mysql->net.read_pos[0] == 0xFE)
|
||||
{
|
||||
rpl->buffer_size= 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if ignore heartbeat flag was set, we ignore this
|
||||
record and continue to fetch next record.
|
||||
The first byte is always status byte (0x00)
|
||||
For event header description see
|
||||
https://mariadb.com/kb/en/library/2-binlog-event-header/ */
|
||||
if (rpl->flags & MARIADB_RPL_IGNORE_HEARTBEAT)
|
||||
{
|
||||
if (rpl->mysql->net.read_pos[1 + 4] == HEARTBEAT_LOG_EVENT)
|
||||
continue;
|
||||
}
|
||||
|
||||
rpl->buffer_size= pkt_len;
|
||||
rpl->buffer= rpl->mysql->net.read_pos;
|
||||
|
||||
if (event)
|
||||
{
|
||||
MA_MEM_ROOT memroot= event->memroot;
|
||||
rpl_event= event;
|
||||
ma_free_root(&memroot, MYF(MY_KEEP_PREALLOC));
|
||||
memset(rpl_event, 0, sizeof(MARIADB_RPL_EVENT));
|
||||
rpl_event->memroot= memroot;
|
||||
} else {
|
||||
if (!(rpl_event = (MARIADB_RPL_EVENT *)malloc(sizeof(MARIADB_RPL_EVENT))))
|
||||
goto mem_error;
|
||||
memset(rpl_event, 0, sizeof(MARIADB_RPL_EVENT));
|
||||
ma_init_alloc_root(&rpl_event->memroot, 8192, 0);
|
||||
}
|
||||
rpl_event->checksum= uint4korr(rpl->buffer + rpl->buffer_size - 4);
|
||||
|
||||
rpl_event->ok= rpl->buffer[0];
|
||||
rpl_event->timestamp= uint4korr(rpl->buffer + 1);
|
||||
rpl_event->event_type= (unsigned char)*(rpl->buffer + 5);
|
||||
rpl_event->server_id= uint4korr(rpl->buffer + 6);
|
||||
rpl_event->event_length= uint4korr(rpl->buffer + 10);
|
||||
rpl_event->next_event_pos= uint4korr(rpl->buffer + 14);
|
||||
rpl_event->flags= uint2korr(rpl->buffer + 18);
|
||||
|
||||
ev= rpl->buffer + EVENT_HEADER_OFS;
|
||||
|
||||
if (rpl->use_checksum)
|
||||
{
|
||||
rpl_event->checksum= *(ev + rpl_event->event_length - 4);
|
||||
rpl_event->event_length-= 4;
|
||||
}
|
||||
|
||||
switch(rpl_event->event_type) {
|
||||
case HEARTBEAT_LOG_EVENT:
|
||||
rpl_event->event.heartbeat.timestamp= uint4korr(ev);
|
||||
ev+= 4;
|
||||
rpl_event->event.heartbeat.next_position= uint4korr(ev);
|
||||
ev+= 4;
|
||||
rpl_event->event.heartbeat.type= (uint8_t)*ev;
|
||||
ev+= 1;
|
||||
rpl_event->event.heartbeat.flags= uint2korr(ev);
|
||||
break;
|
||||
case BINLOG_CHECKPOINT_EVENT:
|
||||
len= uint4korr(ev);
|
||||
ev+= 4;
|
||||
if (rpl_alloc_string(rpl_event, &rpl_event->event.checkpoint.filename, ev, len))
|
||||
goto mem_error;
|
||||
break;
|
||||
case FORMAT_DESCRIPTION_EVENT:
|
||||
rpl_event->event.format_description.format = uint2korr(ev);
|
||||
ev+= 2;
|
||||
rpl_event->event.format_description.server_version = (char *)(ev);
|
||||
ev+= 50;
|
||||
rpl_event->event.format_description.timestamp= uint4korr(ev);
|
||||
ev+= 4;
|
||||
rpl->fd_header_len= rpl_event->event.format_description.header_len= (uint8_t)*ev;
|
||||
ev= rpl->buffer + rpl->buffer_size - 5;
|
||||
rpl->use_checksum= *ev;
|
||||
break;
|
||||
case QUERY_EVENT:
|
||||
{
|
||||
size_t db_len, status_len;
|
||||
rpl_event->event.query.thread_id= uint4korr(ev);
|
||||
ev+= 4;
|
||||
rpl_event->event.query.seconds= uint4korr(ev);
|
||||
ev+= 4;
|
||||
db_len= *ev;
|
||||
ev++;
|
||||
rpl_event->event.query.errornr= uint2korr(ev);
|
||||
ev+= 2;
|
||||
status_len= uint2korr(ev);
|
||||
ev+= 2;
|
||||
if (rpl_alloc_string(rpl_event, &rpl_event->event.query.status, ev, status_len))
|
||||
goto mem_error;
|
||||
ev+= status_len;
|
||||
|
||||
if (rpl_alloc_string(rpl_event, &rpl_event->event.query.database, ev, db_len))
|
||||
goto mem_error;
|
||||
ev+= db_len + 1; /* zero terminated */
|
||||
|
||||
/* calculate statement size: buffer + buffer_size - current_ofs (ev) - crc_size */
|
||||
len= (size_t)(rpl->buffer + rpl->buffer_size - ev - (rpl->use_checksum ? 4 : 0));
|
||||
if (rpl_alloc_string(rpl_event, &rpl_event->event.query.statement, ev, len))
|
||||
goto mem_error;
|
||||
break;
|
||||
}
|
||||
case TABLE_MAP_EVENT:
|
||||
rpl_event->event.table_map.table_id= uint6korr(ev);
|
||||
ev+= 8;
|
||||
len= *ev;
|
||||
ev++;
|
||||
if (rpl_alloc_string(rpl_event, &rpl_event->event.table_map.database, ev, len))
|
||||
goto mem_error;
|
||||
ev+= len + 1;
|
||||
len= *ev;
|
||||
ev++;
|
||||
if (rpl_alloc_string(rpl_event, &rpl_event->event.table_map.table, ev, len))
|
||||
goto mem_error;
|
||||
ev+= len + 1;
|
||||
rpl_event->event.table_map.column_count= mysql_net_field_length(&ev);
|
||||
len= rpl_event->event.table_map.column_count;
|
||||
if (rpl_alloc_string(rpl_event, &rpl_event->event.table_map.column_types, ev, len))
|
||||
goto mem_error;
|
||||
ev+= len;
|
||||
len= mysql_net_field_length(&ev);
|
||||
if (rpl_alloc_string(rpl_event, &rpl_event->event.table_map.metadata, ev, len))
|
||||
goto mem_error;
|
||||
break;
|
||||
case RAND_EVENT:
|
||||
rpl_event->event.rand.first_seed= uint8korr(ev);
|
||||
ev+= 8;
|
||||
rpl_event->event.rand.second_seed= uint8korr(ev);
|
||||
break;
|
||||
case INTVAR_EVENT:
|
||||
rpl_event->event.intvar.type= *ev;
|
||||
ev++;
|
||||
rpl_event->event.intvar.value= uint8korr(ev);
|
||||
break;
|
||||
case USER_VAR_EVENT:
|
||||
len= uint4korr(ev);
|
||||
ev+= 4;
|
||||
if (rpl_alloc_string(rpl_event, &rpl_event->event.uservar.name, ev, len))
|
||||
goto mem_error;
|
||||
ev+= len;
|
||||
if (!(rpl_event->event.uservar.is_null= (uint8)*ev))
|
||||
{
|
||||
ev++;
|
||||
rpl_event->event.uservar.type= *ev;
|
||||
ev++;
|
||||
rpl_event->event.uservar.charset_nr= uint4korr(ev);
|
||||
ev+= 4;
|
||||
len= uint4korr(ev);
|
||||
ev+= 4;
|
||||
if (rpl_alloc_string(rpl_event, &rpl_event->event.uservar.value, ev, len))
|
||||
goto mem_error;
|
||||
ev+= len;
|
||||
if ((unsigned long)(ev - rpl->buffer) < rpl->buffer_size)
|
||||
rpl_event->event.uservar.flags= *ev;
|
||||
}
|
||||
break;
|
||||
case START_ENCRYPTION_EVENT:
|
||||
rpl_event->event.encryption.scheme= *ev;
|
||||
ev++;
|
||||
rpl_event->event.encryption.key_version= uint4korr(ev);
|
||||
ev+= 4;
|
||||
rpl_event->event.encryption.nonce= (char *)ev;
|
||||
break;
|
||||
case ANNOTATE_ROWS_EVENT:
|
||||
len= (uint32)(rpl->buffer + rpl->buffer_size - (unsigned char *)ev - (rpl->use_checksum ? 4 : 0));
|
||||
if (rpl_alloc_string(rpl_event, &rpl_event->event.annotate_rows.statement, ev, len))
|
||||
goto mem_error;
|
||||
break;
|
||||
case ROTATE_EVENT:
|
||||
rpl_event->event.rotate.position= uint8korr(ev);
|
||||
ev+= 8;
|
||||
len= rpl_event->event_length - rpl->fd_header_len - 8;
|
||||
if (rpl_alloc_string(rpl_event, &rpl_event->event.rotate.filename, ev, len))
|
||||
goto mem_error;
|
||||
break;
|
||||
case XID_EVENT:
|
||||
rpl_event->event.xid.transaction_nr= uint8korr(ev);
|
||||
break;
|
||||
case STOP_EVENT:
|
||||
/* nothing to do here */
|
||||
break;
|
||||
case GTID_EVENT:
|
||||
rpl_event->event.gtid.sequence_nr= uint8korr(ev);
|
||||
ev+= 8;
|
||||
rpl_event->event.gtid.domain_id= uint4korr(ev);
|
||||
ev+= 4;
|
||||
rpl_event->event.gtid.flags= *ev;
|
||||
ev++;
|
||||
if (rpl_event->event.gtid.flags & FL_GROUP_COMMIT_ID)
|
||||
rpl_event->event.gtid.commit_id= uint8korr(ev);
|
||||
break;
|
||||
case GTID_LIST_EVENT:
|
||||
{
|
||||
uint32 i;
|
||||
rpl_event->event.gtid_list.gtid_cnt= uint4korr(ev);
|
||||
ev++;
|
||||
if (!(rpl_event->event.gtid_list.gtid= (MARIADB_GTID *)ma_alloc_root(&rpl_event->memroot, sizeof(MARIADB_GTID) * rpl_event->event.gtid_list.gtid_cnt)))
|
||||
goto mem_error;
|
||||
for (i=0; i < rpl_event->event.gtid_list.gtid_cnt; i++)
|
||||
{
|
||||
rpl_event->event.gtid_list.gtid[i].domain_id= uint4korr(ev);
|
||||
ev+= 4;
|
||||
rpl_event->event.gtid_list.gtid[i].server_id= uint4korr(ev);
|
||||
ev+= 4;
|
||||
rpl_event->event.gtid_list.gtid[i].sequence_nr= uint8korr(ev);
|
||||
ev+= 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WRITE_ROWS_EVENT_V1:
|
||||
case UPDATE_ROWS_EVENT_V1:
|
||||
case DELETE_ROWS_EVENT_V1:
|
||||
rpl_event->event.rows.type= rpl_event->event_type - WRITE_ROWS_EVENT_V1;
|
||||
if (rpl->fd_header_len == 6)
|
||||
{
|
||||
rpl_event->event.rows.table_id= uint4korr(ev);
|
||||
ev+= 4;
|
||||
} else {
|
||||
rpl_event->event.rows.table_id= uint6korr(ev);
|
||||
ev+= 6;
|
||||
}
|
||||
rpl_event->event.rows.flags= uint2korr(ev);
|
||||
ev+= 2;
|
||||
len= rpl_event->event.rows.column_count= mysql_net_field_length(&ev);
|
||||
if (!len)
|
||||
break;
|
||||
if (!(rpl_event->event.rows.column_bitmap =
|
||||
(char *)ma_alloc_root(&rpl_event->memroot, (len + 7) / 8)))
|
||||
goto mem_error;
|
||||
memcpy(rpl_event->event.rows.column_bitmap, ev, (len + 7) / 8);
|
||||
ev+= (len + 7) / 8;
|
||||
if (rpl_event->event_type == UPDATE_ROWS_EVENT_V1)
|
||||
{
|
||||
if (!(rpl_event->event.rows.column_update_bitmap =
|
||||
(char *)ma_alloc_root(&rpl_event->memroot, (len + 7) / 8)))
|
||||
goto mem_error;
|
||||
memcpy(rpl_event->event.rows.column_update_bitmap, ev, (len + 7) / 8);
|
||||
ev+= (len + 7) / 8;
|
||||
}
|
||||
len= (rpl->buffer + rpl_event->event_length + EVENT_HEADER_OFS - rpl->fd_header_len) - ev;
|
||||
if ((rpl_event->event.rows.row_data_size= len))
|
||||
{
|
||||
if (!(rpl_event->event.rows.row_data =
|
||||
(char *)ma_alloc_root(&rpl_event->memroot, rpl_event->event.rows.row_data_size)))
|
||||
goto mem_error;
|
||||
memcpy(rpl_event->event.rows.row_data, ev, rpl_event->event.rows.row_data_size);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
free(rpl_event);
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
return rpl_event;
|
||||
}
|
||||
mem_error:
|
||||
free(rpl_event);
|
||||
SET_CLIENT_ERROR(rpl->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void STDCALL mariadb_rpl_close(MARIADB_RPL *rpl)
|
||||
{
|
||||
if (!rpl)
|
||||
return;
|
||||
if (rpl->filename)
|
||||
free((void *)rpl->filename);
|
||||
free(rpl);
|
||||
return;
|
||||
}
|
||||
|
||||
int mariadb_rpl_optionsv(MARIADB_RPL *rpl,
|
||||
enum mariadb_rpl_option option,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
int rc= 0;
|
||||
|
||||
if (!rpl)
|
||||
return 1;
|
||||
|
||||
va_start(ap, option);
|
||||
|
||||
switch (option) {
|
||||
case MARIADB_RPL_FILENAME:
|
||||
{
|
||||
char *arg1= va_arg(ap, char *);
|
||||
rpl->filename_length= (uint32_t)va_arg(ap, size_t);
|
||||
free((void *)rpl->filename);
|
||||
rpl->filename= NULL;
|
||||
if (rpl->filename_length)
|
||||
{
|
||||
rpl->filename= (char *)malloc(rpl->filename_length);
|
||||
memcpy((void *)rpl->filename, arg1, rpl->filename_length);
|
||||
}
|
||||
else if (arg1)
|
||||
{
|
||||
rpl->filename= strdup((const char *)arg1);
|
||||
rpl->filename_length= (uint32_t)strlen(rpl->filename);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MARIADB_RPL_SERVER_ID:
|
||||
{
|
||||
rpl->server_id= va_arg(ap, unsigned int);
|
||||
break;
|
||||
}
|
||||
case MARIADB_RPL_FLAGS:
|
||||
{
|
||||
rpl->flags= va_arg(ap, unsigned int);
|
||||
break;
|
||||
}
|
||||
case MARIADB_RPL_START:
|
||||
{
|
||||
rpl->start_position= va_arg(ap, unsigned long);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rc= -1;
|
||||
goto end;
|
||||
}
|
||||
end:
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int mariadb_rpl_get_optionsv(MARIADB_RPL *rpl,
|
||||
enum mariadb_rpl_option option,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (!rpl)
|
||||
return 1;
|
||||
|
||||
va_start(ap, option);
|
||||
|
||||
switch (option) {
|
||||
case MARIADB_RPL_FILENAME:
|
||||
{
|
||||
const char **name= (const char **)va_arg(ap, char **);
|
||||
size_t *len= (size_t*)va_arg(ap, size_t *);
|
||||
|
||||
*name= rpl->filename;
|
||||
*len= rpl->filename_length;
|
||||
break;
|
||||
}
|
||||
case MARIADB_RPL_SERVER_ID:
|
||||
{
|
||||
unsigned int *id= va_arg(ap, unsigned int *);
|
||||
*id= rpl->server_id;
|
||||
break;
|
||||
}
|
||||
case MARIADB_RPL_FLAGS:
|
||||
{
|
||||
unsigned int *flags= va_arg(ap, unsigned int *);
|
||||
*flags= rpl->flags;
|
||||
break;
|
||||
}
|
||||
case MARIADB_RPL_START:
|
||||
{
|
||||
unsigned long *start= va_arg(ap, unsigned long *);
|
||||
*start= rpl->start_position;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
va_end(ap);
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
va_end(ap);
|
||||
return 0;
|
||||
}
|
2515
vendor/MDBC/libmariadb/mariadb_stmt.c
vendored
Normal file
2515
vendor/MDBC/libmariadb/mariadb_stmt.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1463
vendor/MDBC/libmariadb/secure/gnutls.c
vendored
Normal file
1463
vendor/MDBC/libmariadb/secure/gnutls.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
77
vendor/MDBC/libmariadb/secure/gnutls_crypt.c
vendored
Normal file
77
vendor/MDBC/libmariadb/secure/gnutls_crypt.c
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright (C) 2018 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_crypt.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/crypto.h>
|
||||
|
||||
static gnutls_digest_algorithm_t ma_hash_get_algorithm(unsigned int alg)
|
||||
{
|
||||
switch(alg)
|
||||
{
|
||||
case MA_HASH_MD5:
|
||||
return GNUTLS_DIG_MD5;
|
||||
case MA_HASH_SHA1:
|
||||
return GNUTLS_DIG_SHA1;
|
||||
case MA_HASH_SHA256:
|
||||
return GNUTLS_DIG_SHA256;
|
||||
case MA_HASH_SHA384:
|
||||
return GNUTLS_DIG_SHA384;
|
||||
case MA_HASH_SHA512:
|
||||
return GNUTLS_DIG_SHA512;
|
||||
case MA_HASH_RIPEMD160:
|
||||
return GNUTLS_DIG_RMD160;
|
||||
default:
|
||||
return GNUTLS_DIG_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
MA_HASH_CTX *ma_hash_new(unsigned int algorithm, MA_HASH_CTX *unused_ctx __attribute__((unused)))
|
||||
{
|
||||
gnutls_hash_hd_t ctx= NULL;
|
||||
gnutls_digest_algorithm_t hash_alg= ma_hash_get_algorithm(algorithm);
|
||||
|
||||
/* unknown or unsupported hash algorithm */
|
||||
if (hash_alg == GNUTLS_DIG_UNKNOWN)
|
||||
return NULL;
|
||||
|
||||
if (gnutls_hash_init(&ctx, hash_alg) < 0)
|
||||
return NULL;
|
||||
|
||||
return (MA_HASH_CTX *)ctx;
|
||||
}
|
||||
|
||||
void ma_hash_free(MA_HASH_CTX *ctx)
|
||||
{
|
||||
if (ctx)
|
||||
gnutls_hash_deinit((gnutls_hash_hd_t)ctx, NULL);
|
||||
}
|
||||
|
||||
void ma_hash_input(MA_HASH_CTX *ctx,
|
||||
const unsigned char *buffer,
|
||||
size_t len)
|
||||
{
|
||||
gnutls_hash((gnutls_hash_hd_t)ctx, (const void *)buffer, len);
|
||||
}
|
||||
|
||||
void ma_hash_result(MA_HASH_CTX *ctx, unsigned char *digest)
|
||||
{
|
||||
gnutls_hash_output((gnutls_hash_hd_t)ctx, digest);
|
||||
}
|
||||
|
||||
|
637
vendor/MDBC/libmariadb/secure/ma_schannel.c
vendored
Normal file
637
vendor/MDBC/libmariadb/secure/ma_schannel.c
vendored
Normal file
@ -0,0 +1,637 @@
|
||||
/************************************************************************************
|
||||
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 "schannel_certs.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_win_error(MARIADB_PVIO *pvio, DWORD ErrorNo);
|
||||
|
||||
|
||||
|
||||
|
||||
/* {{{ void ma_schannel_set_sec_error */
|
||||
void ma_schannel_set_sec_error(MARIADB_PVIO* pvio, DWORD ErrorNo)
|
||||
{
|
||||
MYSQL* mysql = pvio->mysql;
|
||||
if (ErrorNo != SEC_E_OK)
|
||||
mysql->net.extension->extended_errno = ErrorNo;
|
||||
if (ErrorNo == SEC_E_INTERNAL_ERROR && GetLastError())
|
||||
{
|
||||
ma_schannel_set_win_error(pvio, GetLastError());
|
||||
return;
|
||||
}
|
||||
ma_schannel_set_win_error(pvio, ErrorNo);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
#include "win32_errmsg.h"
|
||||
/* {{{ void ma_schnnel_set_win_error */
|
||||
void ma_schannel_set_win_error(MARIADB_PVIO *pvio, DWORD ErrorNo)
|
||||
{
|
||||
char buffer[256];
|
||||
ma_format_win32_error(buffer, sizeof(buffer), ErrorNo, "SSL connection error: ");
|
||||
pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, buffer);
|
||||
return;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* {{{ SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRead, SecBuffer *pExtraData) */
|
||||
/*
|
||||
perform handshake loop
|
||||
|
||||
SYNOPSIS
|
||||
ma_schannel_handshake_loop()
|
||||
pvio 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_PVIO *pvio, my_bool InitialRead, SecBuffer *pExtraData)
|
||||
{
|
||||
SecBufferDesc OutBuffer, InBuffer;
|
||||
SecBuffer InBuffers[2], OutBuffers;
|
||||
DWORD dwSSPIFlags, dwSSPIOutFlags, cbData, cbIoBuffer;
|
||||
TimeStamp tsExpiry;
|
||||
SECURITY_STATUS rc;
|
||||
PUCHAR IoBuffer;
|
||||
BOOL fDoRead;
|
||||
MARIADB_TLS *ctls= pvio->ctls;
|
||||
SC_CTX *sctx= (SC_CTX *)ctls->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 if 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)
|
||||
{
|
||||
ssize_t nbytes = pvio->methods->read(pvio, IoBuffer + cbIoBuffer, (size_t)(SC_IO_BUFFER_SIZE - cbIoBuffer));
|
||||
if (nbytes <= 0)
|
||||
{
|
||||
rc = SEC_E_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
cbData = (DWORD)nbytes;
|
||||
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.pvBuffer = NULL;
|
||||
OutBuffers.BufferType= SECBUFFER_TOKEN;
|
||||
OutBuffers.cbBuffer = 0;
|
||||
|
||||
OutBuffer.cBuffers = 1;
|
||||
OutBuffer.pBuffers = &OutBuffers;
|
||||
OutBuffer.ulVersion = SECBUFFER_VERSION;
|
||||
|
||||
|
||||
rc = InitializeSecurityContextA(&sctx->CredHdl,
|
||||
&sctx->hCtxt,
|
||||
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.cbBuffer && OutBuffers.pvBuffer)
|
||||
{
|
||||
ssize_t nbytes = pvio->methods->write(pvio, (uchar *)OutBuffers.pvBuffer, (size_t)OutBuffers.cbBuffer);
|
||||
if(nbytes <= 0)
|
||||
{
|
||||
FreeContextBuffer(OutBuffers.pvBuffer);
|
||||
DeleteSecurityContext(&sctx->hCtxt);
|
||||
return SEC_E_INTERNAL_ERROR;
|
||||
}
|
||||
cbData= (DWORD)nbytes;
|
||||
/* Free output context buffer */
|
||||
FreeContextBuffer(OutBuffers.pvBuffer);
|
||||
OutBuffers.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))
|
||||
{
|
||||
goto loopend;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ( InBuffers[1].BufferType == SECBUFFER_EXTRA )
|
||||
{
|
||||
MoveMemory( IoBuffer, IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer );
|
||||
cbIoBuffer = InBuffers[1].cbBuffer;
|
||||
}
|
||||
else
|
||||
cbIoBuffer = 0;
|
||||
}
|
||||
loopend:
|
||||
if (FAILED(rc))
|
||||
{
|
||||
ma_schannel_set_sec_error(pvio, rc);
|
||||
DeleteSecurityContext(&sctx->hCtxt);
|
||||
}
|
||||
LocalFree(IoBuffer);
|
||||
|
||||
return rc;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ SECURITY_STATUS ma_schannel_client_handshake(MARIADB_TLS *ctls) */
|
||||
/*
|
||||
performs client side handshake
|
||||
|
||||
SYNOPSIS
|
||||
ma_schannel_client_handshake()
|
||||
ctls Pointer to a MARIADB_TLS 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_TLS *ctls)
|
||||
{
|
||||
MARIADB_PVIO *pvio;
|
||||
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 BufferOut;
|
||||
SecBuffer BuffersOut;
|
||||
|
||||
if (!ctls || !ctls->pvio)
|
||||
return 1;
|
||||
|
||||
pvio= ctls->pvio;
|
||||
sctx= (SC_CTX *)ctls->ssl;
|
||||
|
||||
/* Initialie securifty context */
|
||||
BuffersOut.BufferType= SECBUFFER_TOKEN;
|
||||
BuffersOut.cbBuffer= 0;
|
||||
BuffersOut.pvBuffer= NULL;
|
||||
|
||||
|
||||
BufferOut.cBuffers= 1;
|
||||
BufferOut.pBuffers= &BuffersOut;
|
||||
BufferOut.ulVersion= SECBUFFER_VERSION;
|
||||
|
||||
sRet = InitializeSecurityContext(&sctx->CredHdl,
|
||||
NULL,
|
||||
pvio->mysql->host,
|
||||
SFlags,
|
||||
0,
|
||||
SECURITY_NATIVE_DREP,
|
||||
NULL,
|
||||
0,
|
||||
&sctx->hCtxt,
|
||||
&BufferOut,
|
||||
&OutFlags,
|
||||
NULL);
|
||||
|
||||
if(sRet != SEC_I_CONTINUE_NEEDED)
|
||||
{
|
||||
ma_schannel_set_sec_error(pvio, sRet);
|
||||
return sRet;
|
||||
}
|
||||
|
||||
/* send client hello packaet */
|
||||
if(BuffersOut.cbBuffer != 0 && BuffersOut.pvBuffer != NULL)
|
||||
{
|
||||
ssize_t nbytes = (DWORD)pvio->methods->write(pvio, (uchar *)BuffersOut.pvBuffer, (size_t)BuffersOut.cbBuffer);
|
||||
|
||||
if (nbytes <= 0)
|
||||
{
|
||||
sRet= SEC_E_INTERNAL_ERROR;
|
||||
goto end;
|
||||
}
|
||||
r = (DWORD)nbytes;
|
||||
}
|
||||
sRet= ma_schannel_handshake_loop(pvio, TRUE, &ExtraData);
|
||||
|
||||
/* allocate IO-Buffer for write operations: After handshake
|
||||
was successful, we are able now to calculate payload */
|
||||
if ((sRet = QueryContextAttributes(&sctx->hCtxt, SECPKG_ATTR_STREAM_SIZES, &sctx->Sizes )))
|
||||
goto end;
|
||||
|
||||
sctx->IoBufferSize= SCHANNEL_PAYLOAD(sctx->Sizes);
|
||||
if (!(sctx->IoBuffer= (PUCHAR)LocalAlloc(0, sctx->IoBufferSize)))
|
||||
{
|
||||
sRet= SEC_E_INSUFFICIENT_MEMORY;
|
||||
goto end;
|
||||
}
|
||||
|
||||
return sRet;
|
||||
end:
|
||||
if (BuffersOut.pvBuffer)
|
||||
FreeContextBuffer(BuffersOut.pvBuffer);
|
||||
return sRet;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio, PCredHandle phCreds, CtxtHandle * phContext,
|
||||
DWORD DecryptLength, uchar *ReadBuffer, DWORD ReadBufferSize) */
|
||||
/*
|
||||
Reads encrypted data from a SSL stream and decrypts it.
|
||||
|
||||
SYNOPSIS
|
||||
ma_schannel_read
|
||||
pvio 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 occurred
|
||||
*/
|
||||
|
||||
SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio,
|
||||
CtxtHandle * phContext,
|
||||
DWORD *DecryptLength,
|
||||
uchar *ReadBuffer,
|
||||
DWORD ReadBufferSize)
|
||||
{
|
||||
ssize_t nbytes = 0;
|
||||
DWORD dwOffset = 0;
|
||||
SC_CTX *sctx;
|
||||
SECURITY_STATUS sRet = 0;
|
||||
SecBufferDesc Msg;
|
||||
SecBuffer Buffers[4];
|
||||
int i;
|
||||
|
||||
if (!pvio || !pvio->methods || !pvio->methods->read || !pvio->ctls || !DecryptLength)
|
||||
return SEC_E_INTERNAL_ERROR;
|
||||
|
||||
sctx = (SC_CTX *)pvio->ctls->ssl;
|
||||
*DecryptLength = 0;
|
||||
|
||||
if (sctx->dataBuf.cbBuffer)
|
||||
{
|
||||
/* Have unread decrypted data from the last time, copy. */
|
||||
nbytes = MIN(ReadBufferSize, sctx->dataBuf.cbBuffer);
|
||||
memcpy(ReadBuffer, sctx->dataBuf.pvBuffer, nbytes);
|
||||
sctx->dataBuf.pvBuffer = (char *)(sctx->dataBuf.pvBuffer) + nbytes;
|
||||
sctx->dataBuf.cbBuffer -= (DWORD)nbytes;
|
||||
*DecryptLength = (DWORD)nbytes;
|
||||
return SEC_E_OK;
|
||||
}
|
||||
|
||||
|
||||
while (1)
|
||||
{
|
||||
/* Check for any encrypted data returned by last DecryptMessage() in SECBUFFER_EXTRA buffer. */
|
||||
if (sctx->extraBuf.cbBuffer)
|
||||
{
|
||||
memmove(sctx->IoBuffer, sctx->extraBuf.pvBuffer, sctx->extraBuf.cbBuffer);
|
||||
dwOffset = sctx->extraBuf.cbBuffer;
|
||||
sctx->extraBuf.cbBuffer = 0;
|
||||
}
|
||||
|
||||
do {
|
||||
assert(sctx->IoBufferSize > dwOffset);
|
||||
if (dwOffset == 0 || sRet == SEC_E_INCOMPLETE_MESSAGE)
|
||||
{
|
||||
nbytes = pvio->methods->read(pvio, sctx->IoBuffer + dwOffset, (size_t)(sctx->IoBufferSize - dwOffset));
|
||||
if (nbytes <= 0)
|
||||
{
|
||||
/* server closed connection, or an error */
|
||||
// todo: error
|
||||
return SEC_E_INVALID_HANDLE;
|
||||
}
|
||||
dwOffset += (DWORD)nbytes;
|
||||
}
|
||||
ZeroMemory(Buffers, sizeof(SecBuffer) * 4);
|
||||
Buffers[0].pvBuffer = sctx->IoBuffer;
|
||||
Buffers[0].cbBuffer = dwOffset;
|
||||
|
||||
Buffers[0].BufferType = SECBUFFER_DATA;
|
||||
Buffers[1].BufferType = SECBUFFER_EMPTY;
|
||||
Buffers[2].BufferType = SECBUFFER_EMPTY;
|
||||
Buffers[3].BufferType = SECBUFFER_EMPTY;
|
||||
|
||||
Msg.ulVersion = SECBUFFER_VERSION; // Version number
|
||||
Msg.cBuffers = 4;
|
||||
Msg.pBuffers = Buffers;
|
||||
|
||||
sRet = DecryptMessage(phContext, &Msg, 0, NULL);
|
||||
|
||||
} while (sRet == SEC_E_INCOMPLETE_MESSAGE); /* Continue reading until full message arrives */
|
||||
|
||||
|
||||
if (sRet != SEC_E_OK)
|
||||
{
|
||||
ma_schannel_set_sec_error(pvio, sRet);
|
||||
return sRet;
|
||||
}
|
||||
|
||||
sctx->extraBuf.cbBuffer = 0;
|
||||
sctx->dataBuf.cbBuffer = 0;
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (Buffers[i].BufferType == SECBUFFER_DATA)
|
||||
sctx->dataBuf = Buffers[i];
|
||||
if (Buffers[i].BufferType == SECBUFFER_EXTRA)
|
||||
sctx->extraBuf = Buffers[i];
|
||||
}
|
||||
|
||||
|
||||
if (sctx->dataBuf.cbBuffer)
|
||||
{
|
||||
assert(sctx->dataBuf.pvBuffer);
|
||||
/*
|
||||
Copy at most ReadBufferSize bytes to output.
|
||||
Store the rest (if any) to be processed next time.
|
||||
*/
|
||||
nbytes = MIN(sctx->dataBuf.cbBuffer, ReadBufferSize);
|
||||
memcpy((char *)ReadBuffer, sctx->dataBuf.pvBuffer, nbytes);
|
||||
sctx->dataBuf.cbBuffer -= (unsigned long)nbytes;
|
||||
sctx->dataBuf.pvBuffer = (char *)sctx->dataBuf.pvBuffer + nbytes;
|
||||
|
||||
*DecryptLength = (DWORD)nbytes;
|
||||
return SEC_E_OK;
|
||||
}
|
||||
// No data buffer, loop
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
#include "win32_errmsg.h"
|
||||
my_bool ma_schannel_verify_certs(MARIADB_TLS *ctls, BOOL verify_server_name)
|
||||
{
|
||||
SECURITY_STATUS status;
|
||||
|
||||
MARIADB_PVIO *pvio= ctls->pvio;
|
||||
MYSQL *mysql= pvio->mysql;
|
||||
SC_CTX *sctx = (SC_CTX *)ctls->ssl;
|
||||
const char *ca_file= mysql->options.ssl_ca;
|
||||
const char* ca_path = mysql->options.ssl_capath;
|
||||
const char *crl_file= mysql->options.extension ? mysql->options.extension->ssl_crl : NULL;
|
||||
const char* crl_path = mysql->options.extension ? mysql->options.extension->ssl_crlpath : NULL;
|
||||
PCCERT_CONTEXT pServerCert= NULL;
|
||||
char errmsg[256];
|
||||
HCERTSTORE store= NULL;
|
||||
int ret= 0;
|
||||
|
||||
status = schannel_create_store(ca_file, ca_path, crl_file, crl_path, &store, errmsg, sizeof(errmsg));
|
||||
if(status)
|
||||
goto end;
|
||||
|
||||
status = QueryContextAttributesA(&sctx->hCtxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert);
|
||||
if (status)
|
||||
{
|
||||
ma_format_win32_error(errmsg, sizeof(errmsg), GetLastError(),
|
||||
"QueryContextAttributes(SECPKG_ATTR_REMOTE_CERT_CONTEXT) failed.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
status = schannel_verify_server_certificate(
|
||||
pServerCert,
|
||||
store,
|
||||
crl_file != 0 || crl_path != 0,
|
||||
mysql->host,
|
||||
verify_server_name,
|
||||
errmsg, sizeof(errmsg));
|
||||
|
||||
if (status)
|
||||
goto end;
|
||||
|
||||
ret= 1;
|
||||
|
||||
end:
|
||||
if (!ret)
|
||||
{
|
||||
pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
"SSL connection error: %s", errmsg);
|
||||
}
|
||||
if (pServerCert)
|
||||
CertFreeCertificateContext(pServerCert);
|
||||
if(store)
|
||||
schannel_free_store(store);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* {{{ size_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio, PCredHandle phCreds, CtxtHandle * phContext) */
|
||||
/*
|
||||
Decrypts data and write to SSL stream
|
||||
SYNOPSIS
|
||||
ma_schannel_write_decrypt
|
||||
pvio 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 occurred
|
||||
*/
|
||||
ssize_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio,
|
||||
uchar *WriteBuffer,
|
||||
size_t WriteBufferSize)
|
||||
{
|
||||
SECURITY_STATUS scRet;
|
||||
SecBufferDesc Message;
|
||||
SecBuffer Buffers[4];
|
||||
DWORD cbMessage;
|
||||
PBYTE pbMessage;
|
||||
SC_CTX *sctx= (SC_CTX *)pvio->ctls->ssl;
|
||||
size_t payload;
|
||||
ssize_t nbytes;
|
||||
DWORD write_size;
|
||||
|
||||
payload= MIN(WriteBufferSize, sctx->Sizes.cbMaximumMessage);
|
||||
|
||||
memcpy(&sctx->IoBuffer[sctx->Sizes.cbHeader], WriteBuffer, payload);
|
||||
pbMessage = sctx->IoBuffer + sctx->Sizes.cbHeader;
|
||||
cbMessage = (DWORD)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 = (DWORD)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->hCtxt, 0, &Message, 0))!= SEC_E_OK)
|
||||
return -1;
|
||||
write_size = Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer;
|
||||
nbytes = pvio->methods->write(pvio, sctx->IoBuffer, write_size);
|
||||
return nbytes == write_size ? payload : -1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
extern char *ssl_protocol_version[5];
|
||||
|
||||
/* {{{ ma_tls_get_protocol_version(MARIADB_TLS *ctls) */
|
||||
int ma_tls_get_protocol_version(MARIADB_TLS *ctls)
|
||||
{
|
||||
SC_CTX *sctx;
|
||||
SecPkgContext_ConnectionInfo ConnectionInfo;
|
||||
if (!ctls->ssl)
|
||||
return 1;
|
||||
|
||||
sctx= (SC_CTX *)ctls->ssl;
|
||||
|
||||
if (QueryContextAttributes(&sctx->hCtxt, SECPKG_ATTR_CONNECTION_INFO, &ConnectionInfo) != SEC_E_OK)
|
||||
return -1;
|
||||
|
||||
switch(ConnectionInfo.dwProtocol)
|
||||
{
|
||||
case SP_PROT_SSL3_CLIENT:
|
||||
return PROTOCOL_SSLV3;
|
||||
case SP_PROT_TLS1_CLIENT:
|
||||
return PROTOCOL_TLS_1_0;
|
||||
case SP_PROT_TLS1_1_CLIENT:
|
||||
return PROTOCOL_TLS_1_1;
|
||||
case SP_PROT_TLS1_2_CLIENT:
|
||||
return PROTOCOL_TLS_1_2;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* }}} */
|
87
vendor/MDBC/libmariadb/secure/ma_schannel.h
vendored
Normal file
87
vendor/MDBC/libmariadb/secure/ma_schannel.h
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
/************************************************************************************
|
||||
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 <ma_global.h>
|
||||
#include <ma_sys.h>
|
||||
#include <ma_common.h>
|
||||
#include <ma_pvio.h>
|
||||
#include <errmsg.h>
|
||||
|
||||
|
||||
#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
|
||||
|
||||
|
||||
#include <ma_pthread.h>
|
||||
|
||||
struct st_DER {
|
||||
char* der_buffer;
|
||||
DWORD der_length;
|
||||
};
|
||||
|
||||
struct st_schannel {
|
||||
CredHandle CredHdl;
|
||||
PUCHAR IoBuffer;
|
||||
DWORD IoBufferSize;
|
||||
SecPkgContext_StreamSizes Sizes;
|
||||
CtxtHandle hCtxt;
|
||||
|
||||
/* Cached data from the last read/decrypt call.*/
|
||||
SecBuffer extraBuf; /* encrypted data read from server. */
|
||||
SecBuffer dataBuf; /* decrypted but still unread data from server.*/
|
||||
|
||||
};
|
||||
|
||||
typedef struct st_schannel SC_CTX;
|
||||
|
||||
extern HCERTSTORE ca_CertStore, crl_CertStore;
|
||||
extern my_bool ca_Check, crl_Check;
|
||||
|
||||
;
|
||||
SECURITY_STATUS ma_schannel_client_handshake(MARIADB_TLS *ctls);
|
||||
SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRead, SecBuffer *pExtraData);
|
||||
|
||||
my_bool ma_schannel_verify_certs(MARIADB_TLS *ctls, BOOL verify_server_name);
|
||||
ssize_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio,
|
||||
uchar *WriteBuffer,
|
||||
size_t WriteBufferSize);
|
||||
SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio,
|
||||
CtxtHandle* phContext,
|
||||
DWORD *DecryptLength,
|
||||
uchar *ReadBuffer,
|
||||
DWORD ReadBufferSize);
|
||||
|
||||
|
||||
#endif /* _ma_schannel_h_ */
|
797
vendor/MDBC/libmariadb/secure/openssl.c
vendored
Normal file
797
vendor/MDBC/libmariadb/secure/openssl.c
vendored
Normal file
@ -0,0 +1,797 @@
|
||||
/************************************************************************************
|
||||
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 <ma_global.h>
|
||||
#include <ma_sys.h>
|
||||
#include <ma_common.h>
|
||||
#include <ma_pvio.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>
|
||||
#include <openssl/md4.h>
|
||||
|
||||
#if defined(_WIN32) && !defined(_OPENSSL_Applink) && defined(HAVE_OPENSSL_APPLINK_C)
|
||||
#include <openssl/applink.c>
|
||||
#endif
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER)
|
||||
#include <openssl/x509v3.h>
|
||||
#define HAVE_OPENSSL_CHECK_HOST 1
|
||||
#endif
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
|
||||
#define HAVE_OPENSSL_1_1_API
|
||||
#endif
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10000000L
|
||||
#define SSL_OP_NO_TLSv1_1 0L
|
||||
#define SSL_OP_NO_TLSv1_2 0L
|
||||
#define CRYPTO_THREADID_set_callback CRYPTO_set_id_callback
|
||||
#define CRYPTO_THREADID_get_callback CRYPTO_get_id_callback
|
||||
#endif
|
||||
|
||||
#if defined(OPENSSL_USE_BIOMETHOD)
|
||||
#undef OPENSSL_USE_BIOMETHOD
|
||||
#endif
|
||||
#ifndef HAVE_OPENSSL_DEFAULT
|
||||
#include <memory.h>
|
||||
#define ma_malloc(A,B) malloc((A))
|
||||
#undef ma_free
|
||||
#define ma_free(A) free((A))
|
||||
#define ma_snprintf snprintf
|
||||
#define ma_vsnprintf vsnprintf
|
||||
#undef SAFE_MUTEX
|
||||
#endif
|
||||
#include <ma_pthread.h>
|
||||
|
||||
#include <mariadb_async.h>
|
||||
#include <ma_context.h>
|
||||
|
||||
extern my_bool ma_tls_initialized;
|
||||
extern unsigned int mariadb_deinitialize_ssl;
|
||||
|
||||
#define MAX_SSL_ERR_LEN 100
|
||||
char tls_library_version[TLS_VERSION_LENGTH];
|
||||
|
||||
static pthread_mutex_t LOCK_openssl_config;
|
||||
#ifndef HAVE_OPENSSL_1_1_API
|
||||
static pthread_mutex_t *LOCK_crypto= NULL;
|
||||
#endif
|
||||
#if defined(OPENSSL_USE_BIOMETHOD)
|
||||
static int ma_bio_read(BIO *h, char *buf, int size);
|
||||
static int ma_bio_write(BIO *h, const char *buf, int size);
|
||||
static BIO_METHOD ma_BIO_method;
|
||||
#endif
|
||||
|
||||
|
||||
static long ma_tls_version_options(const char *version)
|
||||
{
|
||||
long protocol_options,
|
||||
disable_all_protocols;
|
||||
|
||||
protocol_options= disable_all_protocols=
|
||||
SSL_OP_NO_SSLv2 |
|
||||
SSL_OP_NO_SSLv3 |
|
||||
SSL_OP_NO_TLSv1 |
|
||||
SSL_OP_NO_TLSv1_1 |
|
||||
SSL_OP_NO_TLSv1_2
|
||||
#ifdef TLS1_3_VERSION
|
||||
| SSL_OP_NO_TLSv1_3
|
||||
#endif
|
||||
;
|
||||
|
||||
if (!version)
|
||||
return 0;
|
||||
|
||||
if (strstr(version, "TLSv1.0"))
|
||||
protocol_options&= ~SSL_OP_NO_TLSv1;
|
||||
if (strstr(version, "TLSv1.1"))
|
||||
protocol_options&= ~SSL_OP_NO_TLSv1_1;
|
||||
if (strstr(version, "TLSv1.2"))
|
||||
protocol_options&= ~SSL_OP_NO_TLSv1_2;
|
||||
#ifdef TLS1_3_VERSION
|
||||
if (strstr(version, "TLSv1.3"))
|
||||
protocol_options&= ~SSL_OP_NO_TLSv1_3;
|
||||
#endif
|
||||
|
||||
if (protocol_options != disable_all_protocols)
|
||||
return protocol_options;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ma_tls_set_error(MYSQL *mysql)
|
||||
{
|
||||
ulong ssl_errno= ERR_get_error();
|
||||
char ssl_error[MAX_SSL_ERR_LEN];
|
||||
const char *ssl_error_reason;
|
||||
MARIADB_PVIO *pvio= mysql->net.pvio;
|
||||
|
||||
if (!ssl_errno)
|
||||
{
|
||||
pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error");
|
||||
return;
|
||||
}
|
||||
if ((ssl_error_reason= ERR_reason_error_string(ssl_errno)))
|
||||
{
|
||||
pvio->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);
|
||||
pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, ssl_error);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef HAVE_OPENSSL_1_1_API
|
||||
/*
|
||||
thread safe callbacks for OpenSSL
|
||||
Crypto call back functions will be
|
||||
set during ssl_initialization
|
||||
*/
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10000000L
|
||||
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
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_OPENSSL_1_1_API
|
||||
static void my_cb_locking(int mode, int n,
|
||||
const char *file __attribute__((unused)),
|
||||
int line __attribute__((unused)))
|
||||
{
|
||||
if (mode & CRYPTO_LOCK)
|
||||
pthread_mutex_lock(&LOCK_crypto[n]);
|
||||
else
|
||||
pthread_mutex_unlock(&LOCK_crypto[n]);
|
||||
}
|
||||
|
||||
static int ssl_thread_init()
|
||||
{
|
||||
if (!CRYPTO_THREADID_get_callback()
|
||||
#ifndef OPENSSL_NO_DEPRECATED
|
||||
&& !CRYPTO_get_id_callback()
|
||||
#endif
|
||||
)
|
||||
{
|
||||
int i, max= CRYPTO_num_locks();
|
||||
|
||||
if (LOCK_crypto == NULL)
|
||||
{
|
||||
if (!(LOCK_crypto=
|
||||
(pthread_mutex_t *)ma_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);
|
||||
CRYPTO_THREADID_set_callback(my_cb_threadid);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || !defined(DISABLE_SIGPIPE)
|
||||
#define disable_sigpipe()
|
||||
#else
|
||||
#include <signal.h>
|
||||
static void ma_sigpipe_handler()
|
||||
{
|
||||
}
|
||||
|
||||
static void disable_sigpipe()
|
||||
{
|
||||
struct sigaction old_handler, new_handler={NULL};
|
||||
if (!sigaction (SIGPIPE, NULL, &old_handler) &&
|
||||
!old_handler.sa_handler)
|
||||
{
|
||||
new_handler.sa_handler= ma_sigpipe_handler;
|
||||
new_handler.sa_flags= 0;
|
||||
if (!sigemptyset(&new_handler.sa_mask))
|
||||
sigaction(SIGPIPE, &new_handler, NULL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
Initializes SSL
|
||||
|
||||
SYNOPSIS
|
||||
my_ssl_start
|
||||
mysql connection handle
|
||||
|
||||
RETURN VALUES
|
||||
0 success
|
||||
1 error
|
||||
*/
|
||||
int ma_tls_start(char *errmsg __attribute__((unused)), size_t errmsg_len __attribute__((unused)))
|
||||
{
|
||||
int rc= 1;
|
||||
char *p;
|
||||
if (ma_tls_initialized)
|
||||
return 0;
|
||||
|
||||
/* lock mutex to prevent multiple initialization */
|
||||
pthread_mutex_init(&LOCK_openssl_config, NULL);
|
||||
pthread_mutex_lock(&LOCK_openssl_config);
|
||||
#ifdef HAVE_OPENSSL_1_1_API
|
||||
if (!OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL))
|
||||
goto end;
|
||||
#else
|
||||
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
|
||||
#endif
|
||||
#ifndef HAVE_OPENSSL_1_1_API
|
||||
/* load errors */
|
||||
SSL_load_error_strings();
|
||||
/* digests and ciphers */
|
||||
OpenSSL_add_all_algorithms();
|
||||
#endif
|
||||
disable_sigpipe();
|
||||
#ifdef OPENSSL_USE_BIOMETHOD
|
||||
memcpy(&ma_BIO_method, BIO_s_socket(), sizeof(BIO_METHOD));
|
||||
ma_BIO_method.bread= ma_bio_read;
|
||||
ma_BIO_method.bwrite= ma_bio_write;
|
||||
#endif
|
||||
snprintf(tls_library_version, TLS_VERSION_LENGTH - 1, "%s",
|
||||
#if defined(LIBRESSL_VERSION_NUMBER) || !defined(HAVE_OPENSSL_1_1_API)
|
||||
SSLeay_version(SSLEAY_VERSION));
|
||||
#else
|
||||
OpenSSL_version(OPENSSL_VERSION));
|
||||
#endif
|
||||
/* remove date from version */
|
||||
if ((p= strstr(tls_library_version, " ")))
|
||||
*p= 0;
|
||||
rc= 0;
|
||||
ma_tls_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_tls_end()
|
||||
{
|
||||
if (ma_tls_initialized)
|
||||
{
|
||||
pthread_mutex_lock(&LOCK_openssl_config);
|
||||
#ifndef HAVE_OPENSSL_1_1_API
|
||||
if (LOCK_crypto)
|
||||
{
|
||||
int i;
|
||||
CRYPTO_set_locking_callback(NULL);
|
||||
CRYPTO_THREADID_set_callback(NULL);
|
||||
|
||||
for (i=0; i < CRYPTO_num_locks(); i++)
|
||||
pthread_mutex_destroy(&LOCK_crypto[i]);
|
||||
ma_free((gptr)LOCK_crypto);
|
||||
LOCK_crypto= NULL;
|
||||
}
|
||||
#endif
|
||||
if (mariadb_deinitialize_ssl)
|
||||
{
|
||||
#ifndef HAVE_OPENSSL_1_1_API
|
||||
ERR_remove_thread_state(NULL);
|
||||
EVP_cleanup();
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
ERR_free_strings();
|
||||
CONF_modules_free();
|
||||
CONF_modules_unload(1);
|
||||
#endif
|
||||
}
|
||||
ma_tls_initialized= FALSE;
|
||||
pthread_mutex_unlock(&LOCK_openssl_config);
|
||||
pthread_mutex_destroy(&LOCK_openssl_config);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int ma_tls_get_password(char *buf, int size,
|
||||
int rwflag __attribute__((unused)),
|
||||
void *userdata)
|
||||
{
|
||||
memset(buf, 0, size);
|
||||
if (userdata)
|
||||
strncpy(buf, (char *)userdata, size);
|
||||
return (int)strlen(buf);
|
||||
}
|
||||
|
||||
|
||||
static int ma_tls_set_certs(MYSQL *mysql, SSL_CTX *ctx)
|
||||
{
|
||||
char *certfile= mysql->options.ssl_cert,
|
||||
*keyfile= mysql->options.ssl_key;
|
||||
char *pw= (mysql->options.extension) ?
|
||||
mysql->options.extension->tls_pw : NULL;
|
||||
|
||||
/* add cipher */
|
||||
if ((mysql->options.ssl_cipher &&
|
||||
mysql->options.ssl_cipher[0] != 0))
|
||||
{
|
||||
if(
|
||||
#ifdef TLS1_3_VERSION
|
||||
SSL_CTX_set_ciphersuites(ctx, mysql->options.ssl_cipher) == 0 &&
|
||||
#endif
|
||||
SSL_CTX_set_cipher_list(ctx, mysql->options.ssl_cipher) == 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* ca_file and ca_path */
|
||||
if (!SSL_CTX_load_verify_locations(ctx,
|
||||
mysql->options.ssl_ca,
|
||||
mysql->options.ssl_capath))
|
||||
{
|
||||
if (mysql->options.ssl_ca || mysql->options.ssl_capath)
|
||||
goto error;
|
||||
if (SSL_CTX_set_default_verify_paths(ctx) == 0)
|
||||
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(ctx)))
|
||||
{
|
||||
if (X509_STORE_load_locations(certstore, mysql->options.extension->ssl_crl,
|
||||
mysql->options.extension->ssl_crlpath) == 0)
|
||||
goto error;
|
||||
|
||||
if (X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL) == 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_chain_file(ctx, certfile) != 1)
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyfile && keyfile[0])
|
||||
{
|
||||
FILE *fp;
|
||||
if ((fp= fopen(keyfile, "rb")))
|
||||
{
|
||||
EVP_PKEY *key= EVP_PKEY_new();
|
||||
PEM_read_PrivateKey(fp, &key, NULL, pw);
|
||||
fclose(fp);
|
||||
if (SSL_CTX_use_PrivateKey(ctx, key) != 1)
|
||||
{
|
||||
unsigned long err= ERR_peek_error();
|
||||
EVP_PKEY_free(key);
|
||||
if (!(ERR_GET_LIB(err) == ERR_LIB_X509 &&
|
||||
ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE))
|
||||
goto error;
|
||||
}
|
||||
EVP_PKEY_free(key);
|
||||
} else {
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
CER(CR_FILE_NOT_FOUND), keyfile);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/* verify key */
|
||||
if (certfile && SSL_CTX_check_private_key(ctx) != 1)
|
||||
goto error;
|
||||
|
||||
SSL_CTX_set_verify(ctx, (mysql->options.ssl_ca || mysql->options.ssl_capath) ?
|
||||
SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
ma_tls_set_error(mysql);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *ma_tls_init(MYSQL *mysql)
|
||||
{
|
||||
SSL *ssl= NULL;
|
||||
SSL_CTX *ctx= NULL;
|
||||
long default_options= SSL_OP_ALL |
|
||||
SSL_OP_NO_SSLv2 |
|
||||
SSL_OP_NO_SSLv3;
|
||||
long options= 0;
|
||||
pthread_mutex_lock(&LOCK_openssl_config);
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
if (!(ctx= SSL_CTX_new(TLS_client_method())))
|
||||
#else
|
||||
if (!(ctx= SSL_CTX_new(SSLv23_client_method())))
|
||||
#endif
|
||||
goto error;
|
||||
if (mysql->options.extension)
|
||||
options= ma_tls_version_options(mysql->options.extension->tls_version);
|
||||
SSL_CTX_set_options(ctx, options ? options : default_options);
|
||||
|
||||
if (ma_tls_set_certs(mysql, ctx))
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!(ssl= SSL_new(ctx)))
|
||||
goto error;
|
||||
|
||||
if (!SSL_set_app_data(ssl, mysql))
|
||||
goto error;
|
||||
|
||||
pthread_mutex_unlock(&LOCK_openssl_config);
|
||||
return (void *)ssl;
|
||||
error:
|
||||
pthread_mutex_unlock(&LOCK_openssl_config);
|
||||
if (ctx)
|
||||
SSL_CTX_free(ctx);
|
||||
if (ssl)
|
||||
SSL_free(ssl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
my_bool ma_tls_connect(MARIADB_TLS *ctls)
|
||||
{
|
||||
SSL *ssl = (SSL *)ctls->ssl;
|
||||
my_bool blocking, try_connect= 1;
|
||||
MYSQL *mysql;
|
||||
MARIADB_PVIO *pvio;
|
||||
int rc;
|
||||
#ifdef OPENSSL_USE_BIOMETHOD
|
||||
BIO_METHOD *bio_method= NULL;
|
||||
BIO *bio;
|
||||
#endif
|
||||
|
||||
mysql= (MYSQL *)SSL_get_app_data(ssl);
|
||||
pvio= mysql->net.pvio;
|
||||
|
||||
/* Set socket to non blocking if not already set */
|
||||
if (!(blocking= pvio->methods->is_blocking(pvio)))
|
||||
pvio->methods->blocking(pvio, FALSE, 0);
|
||||
|
||||
SSL_clear(ssl);
|
||||
|
||||
#ifdef OPENSSL_USE_BIOMETHOD
|
||||
bio= BIO_new(&ma_BIO_method);
|
||||
bio->ptr= pvio;
|
||||
SSL_set_bio(ssl, bio, bio);
|
||||
BIO_set_fd(bio, mysql_get_socket(mysql), BIO_NOCLOSE);
|
||||
#else
|
||||
SSL_set_fd(ssl, (int)mysql_get_socket(mysql));
|
||||
#endif
|
||||
|
||||
while (try_connect && (rc= SSL_connect(ssl)) == -1)
|
||||
{
|
||||
switch((SSL_get_error(ssl, rc))) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
if (pvio->methods->wait_io_or_timeout(pvio, TRUE, mysql->options.connect_timeout) < 1)
|
||||
try_connect= 0;
|
||||
break;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
if (pvio->methods->wait_io_or_timeout(pvio, TRUE, mysql->options.connect_timeout) < 1)
|
||||
try_connect= 0;
|
||||
break;
|
||||
default:
|
||||
try_connect= 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* In case handshake failed or if a root certificate (ca) was specified,
|
||||
we need to check the result code of X509 verification. A detailed check
|
||||
of the peer certificate (hostname checking will follow later) */
|
||||
if (rc != 1 ||
|
||||
(mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) ||
|
||||
(mysql->options.ssl_ca || mysql->options.ssl_capath))
|
||||
{
|
||||
long x509_err= SSL_get_verify_result(ssl);
|
||||
if (x509_err != X509_V_OK)
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR), X509_verify_cert_error_string(x509_err));
|
||||
/* restore blocking mode */
|
||||
if (!blocking)
|
||||
pvio->methods->blocking(pvio, FALSE, 0);
|
||||
|
||||
return 1;
|
||||
} else if (rc != 1) {
|
||||
ma_tls_set_error(mysql);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
pvio->ctls->ssl= ctls->ssl= (void *)ssl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static my_bool
|
||||
ma_tls_async_check_result(int res, struct mysql_async_context *b, SSL *ssl)
|
||||
{
|
||||
int ssl_err;
|
||||
b->events_to_wait_for= 0;
|
||||
if (res >= 0)
|
||||
return 1;
|
||||
ssl_err= SSL_get_error(ssl, res);
|
||||
if (ssl_err == SSL_ERROR_WANT_READ)
|
||||
b->events_to_wait_for|= MYSQL_WAIT_READ;
|
||||
else if (ssl_err == SSL_ERROR_WANT_WRITE)
|
||||
b->events_to_wait_for|= MYSQL_WAIT_WRITE;
|
||||
else
|
||||
return 1;
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t ma_tls_read_async(MARIADB_PVIO *pvio,
|
||||
const unsigned char *buffer,
|
||||
size_t length)
|
||||
{
|
||||
int res;
|
||||
struct mysql_async_context *b= pvio->mysql->options.extension->async_context;
|
||||
MARIADB_TLS *ctls= pvio->ctls;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
res= SSL_read((SSL *)ctls->ssl, (void *)buffer, (int)length);
|
||||
if (ma_tls_async_check_result(res, b, (SSL *)ctls->ssl))
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t ma_tls_write_async(MARIADB_PVIO *pvio,
|
||||
const unsigned char *buffer,
|
||||
size_t length)
|
||||
{
|
||||
int res;
|
||||
struct mysql_async_context *b= pvio->mysql->options.extension->async_context;
|
||||
MARIADB_TLS *ctls= pvio->ctls;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
res= SSL_write((SSL *)ctls->ssl, (void *)buffer, (int)length);
|
||||
if (ma_tls_async_check_result(res, b, (SSL *)ctls->ssl))
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ssize_t ma_tls_read(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
|
||||
{
|
||||
int rc;
|
||||
MARIADB_PVIO *pvio= ctls->pvio;
|
||||
|
||||
while ((rc= SSL_read((SSL *)ctls->ssl, (void *)buffer, (int)length)) < 0)
|
||||
{
|
||||
int error= SSL_get_error((SSL *)ctls->ssl, rc);
|
||||
if (error != SSL_ERROR_WANT_READ)
|
||||
return rc;
|
||||
if (pvio->methods->wait_io_or_timeout(pvio, TRUE, pvio->mysql->options.read_timeout) < 1)
|
||||
return rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
ssize_t ma_tls_write(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
|
||||
{
|
||||
int rc;
|
||||
MARIADB_PVIO *pvio= ctls->pvio;
|
||||
|
||||
while ((rc= SSL_write((SSL *)ctls->ssl, (void *)buffer, (int)length)) <= 0)
|
||||
{
|
||||
int error= SSL_get_error((SSL *)ctls->ssl, rc);
|
||||
if (error != SSL_ERROR_WANT_WRITE)
|
||||
return rc;
|
||||
if (pvio->methods->wait_io_or_timeout(pvio, TRUE, pvio->mysql->options.write_timeout) < 1)
|
||||
return rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
my_bool ma_tls_close(MARIADB_TLS *ctls)
|
||||
{
|
||||
int i, rc;
|
||||
SSL *ssl;
|
||||
SSL_CTX *ctx= NULL;
|
||||
|
||||
if (!ctls || !ctls->ssl)
|
||||
return 1;
|
||||
ssl= (SSL *)ctls->ssl;
|
||||
ctx= SSL_get_SSL_CTX(ssl);
|
||||
if (ctx)
|
||||
SSL_CTX_free(ctx);
|
||||
|
||||
SSL_set_quiet_shutdown(ssl, 1);
|
||||
/* 2 x pending + 2 * data = 4 */
|
||||
for (i=0; i < 4; i++)
|
||||
if ((rc= SSL_shutdown(ssl)))
|
||||
break;
|
||||
|
||||
/* Since we transferred ownership of BIO to ssl, BIO will
|
||||
automatically freed - no need for an explicit BIO_free_all */
|
||||
|
||||
SSL_free(ssl);
|
||||
ctls->ssl= NULL;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ma_tls_verify_server_cert(MARIADB_TLS *ctls)
|
||||
{
|
||||
X509 *cert;
|
||||
MYSQL *mysql;
|
||||
SSL *ssl;
|
||||
MARIADB_PVIO *pvio;
|
||||
#if !defined(HAVE_OPENSSL_CHECK_HOST)
|
||||
X509_NAME *x509sn;
|
||||
int cn_pos;
|
||||
X509_NAME_ENTRY *cn_entry;
|
||||
ASN1_STRING *cn_asn1;
|
||||
const char *cn_str;
|
||||
#endif
|
||||
if (!ctls || !ctls->ssl)
|
||||
return 1;
|
||||
ssl= (SSL *)ctls->ssl;
|
||||
|
||||
mysql= (MYSQL *)SSL_get_app_data(ssl);
|
||||
pvio= mysql->net.pvio;
|
||||
|
||||
if (!mysql->host)
|
||||
{
|
||||
pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR), "Invalid (empty) hostname");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(cert= SSL_get_peer_certificate(ssl)))
|
||||
{
|
||||
pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR), "Unable to get server certificate");
|
||||
return 1;
|
||||
}
|
||||
#ifdef HAVE_OPENSSL_CHECK_HOST
|
||||
if (X509_check_host(cert, mysql->host, 0, 0, 0) != 1
|
||||
&& X509_check_ip_asc(cert, mysql->host, 0) != 1)
|
||||
goto error;
|
||||
#else
|
||||
x509sn= X509_get_subject_name(cert);
|
||||
|
||||
if ((cn_pos= X509_NAME_get_index_by_NID(x509sn, NID_commonName, -1)) < 0)
|
||||
goto error;
|
||||
|
||||
if (!(cn_entry= X509_NAME_get_entry(x509sn, cn_pos)))
|
||||
goto error;
|
||||
|
||||
if (!(cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry)))
|
||||
goto error;
|
||||
|
||||
cn_str = (char *)ASN1_STRING_data(cn_asn1);
|
||||
|
||||
/* Make sure there is no embedded \0 in the CN */
|
||||
if ((size_t)ASN1_STRING_length(cn_asn1) != strlen(cn_str))
|
||||
goto error;
|
||||
|
||||
if (strcmp(cn_str, mysql->host))
|
||||
goto error;
|
||||
#endif
|
||||
X509_free(cert);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
X509_free(cert);
|
||||
|
||||
pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR), "Validation of SSL server certificate failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *ma_tls_get_cipher(MARIADB_TLS *ctls)
|
||||
{
|
||||
if (!ctls || !ctls->ssl)
|
||||
return NULL;
|
||||
return SSL_get_cipher_name(ctls->ssl);
|
||||
}
|
||||
|
||||
unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int len)
|
||||
{
|
||||
X509 *cert= NULL;
|
||||
MYSQL *mysql;
|
||||
unsigned int fp_len;
|
||||
|
||||
if (!ctls || !ctls->ssl)
|
||||
return 0;
|
||||
|
||||
mysql= SSL_get_app_data(ctls->ssl);
|
||||
|
||||
if (!(cert= SSL_get_peer_certificate(ctls->ssl)))
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR),
|
||||
"Unable to get server certificate");
|
||||
goto end;
|
||||
}
|
||||
|
||||
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");
|
||||
goto end;
|
||||
}
|
||||
if (!X509_digest(cert, EVP_sha1(), (unsigned char *)fp, &fp_len))
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR),
|
||||
"invalid finger print of server certificate");
|
||||
goto end;
|
||||
}
|
||||
|
||||
X509_free(cert);
|
||||
return (fp_len);
|
||||
end:
|
||||
X509_free(cert);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ma_tls_get_protocol_version(MARIADB_TLS *ctls)
|
||||
{
|
||||
if (!ctls || !ctls->ssl)
|
||||
return -1;
|
||||
|
||||
return SSL_version(ctls->ssl) & 0xFF;
|
||||
}
|
||||
|
88
vendor/MDBC/libmariadb/secure/openssl_crypt.c
vendored
Normal file
88
vendor/MDBC/libmariadb/secure/openssl_crypt.c
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
Copyright (C) 2018 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_global.h>
|
||||
#include <ma_crypt.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
static const EVP_MD *ma_hash_get_algorithm(unsigned int alg)
|
||||
{
|
||||
switch(alg)
|
||||
{
|
||||
case MA_HASH_MD5:
|
||||
return EVP_md5();
|
||||
case MA_HASH_SHA1:
|
||||
return EVP_sha1();
|
||||
case MA_HASH_SHA224:
|
||||
return EVP_sha224();
|
||||
case MA_HASH_SHA256:
|
||||
return EVP_sha256();
|
||||
case MA_HASH_SHA384:
|
||||
return EVP_sha384();
|
||||
case MA_HASH_SHA512:
|
||||
return EVP_sha512();
|
||||
case MA_HASH_RIPEMD160:
|
||||
return EVP_ripemd160();
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
MA_HASH_CTX *ma_hash_new(unsigned int algorithm, MA_HASH_CTX *unused __attribute__((unused)))
|
||||
{
|
||||
EVP_MD_CTX *ctx= NULL;
|
||||
const EVP_MD *evp_md= ma_hash_get_algorithm(algorithm);
|
||||
|
||||
/* unknown or unsupported hash algorithm */
|
||||
if (!evp_md)
|
||||
return NULL;
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
if (!(ctx= EVP_MD_CTX_new()))
|
||||
#else
|
||||
if (!(ctx= EVP_MD_CTX_create()))
|
||||
#endif
|
||||
return NULL;
|
||||
if (!EVP_DigestInit(ctx, evp_md))
|
||||
{
|
||||
ma_hash_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void ma_hash_free(MA_HASH_CTX *ctx)
|
||||
{
|
||||
if (ctx)
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
EVP_MD_CTX_free(ctx);
|
||||
#else
|
||||
EVP_MD_CTX_destroy(ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ma_hash_input(MA_HASH_CTX *ctx,
|
||||
const unsigned char *buffer,
|
||||
size_t len)
|
||||
{
|
||||
EVP_DigestUpdate(ctx, buffer, len);
|
||||
}
|
||||
|
||||
void ma_hash_result(MA_HASH_CTX *ctx, unsigned char *digest)
|
||||
{
|
||||
EVP_DigestFinal_ex(ctx, digest, NULL);
|
||||
}
|
562
vendor/MDBC/libmariadb/secure/schannel.c
vendored
Normal file
562
vendor/MDBC/libmariadb/secure/schannel.c
vendored
Normal file
@ -0,0 +1,562 @@
|
||||
/************************************************************************************
|
||||
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"
|
||||
#include "schannel_certs.h"
|
||||
#include <string.h>
|
||||
|
||||
extern my_bool ma_tls_initialized;
|
||||
char tls_library_version[] = "Schannel";
|
||||
|
||||
#define PROT_SSL3 1
|
||||
#define PROT_TLS1_0 2
|
||||
#define PROT_TLS1_2 4
|
||||
#define PROT_TLS1_3 8
|
||||
|
||||
static struct
|
||||
{
|
||||
DWORD cipher_id;
|
||||
DWORD protocol;
|
||||
const char *iana_name;
|
||||
const char *openssl_name;
|
||||
ALG_ID algs[4]; /* exchange, encryption, hash, signature */
|
||||
}
|
||||
cipher_map[] =
|
||||
{
|
||||
{
|
||||
0x0002,
|
||||
PROT_TLS1_0 | PROT_TLS1_2 | PROT_SSL3,
|
||||
"TLS_RSA_WITH_NULL_SHA", "NULL-SHA",
|
||||
{ CALG_RSA_KEYX, 0, CALG_SHA1, CALG_RSA_SIGN }
|
||||
},
|
||||
{
|
||||
0x0004,
|
||||
PROT_TLS1_0 | PROT_TLS1_2 | PROT_SSL3,
|
||||
"TLS_RSA_WITH_RC4_128_MD5", "RC4-MD5",
|
||||
{ CALG_RSA_KEYX, CALG_RC4, CALG_MD5, CALG_RSA_SIGN }
|
||||
},
|
||||
{
|
||||
0x0005,
|
||||
PROT_TLS1_0 | PROT_TLS1_2 | PROT_SSL3,
|
||||
"TLS_RSA_WITH_RC4_128_SHA", "RC4-SHA",
|
||||
{ CALG_RSA_KEYX, CALG_RC4, CALG_SHA1, CALG_RSA_SIGN }
|
||||
},
|
||||
{
|
||||
0x000A,
|
||||
PROT_SSL3,
|
||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA", "DES-CBC3-SHA",
|
||||
{CALG_RSA_KEYX, CALG_3DES, CALG_SHA1, CALG_DSS_SIGN}
|
||||
},
|
||||
{
|
||||
0x0013,
|
||||
PROT_TLS1_0 | PROT_TLS1_2 | PROT_SSL3,
|
||||
"TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", "EDH-DSS-DES-CBC3-SHA",
|
||||
{ CALG_DH_EPHEM, CALG_3DES, CALG_SHA1, CALG_DSS_SIGN }
|
||||
},
|
||||
{
|
||||
0x002F,
|
||||
PROT_SSL3 | PROT_TLS1_0 | PROT_TLS1_2,
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA", "AES128-SHA",
|
||||
{ CALG_RSA_KEYX, CALG_AES_128, CALG_SHA, CALG_RSA_SIGN}
|
||||
},
|
||||
{
|
||||
0x0032,
|
||||
PROT_TLS1_0 | PROT_TLS1_2,
|
||||
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA", "DHE-DSS-AES128-SHA",
|
||||
{ CALG_DH_EPHEM, CALG_AES_128, CALG_SHA1, CALG_RSA_SIGN }
|
||||
},
|
||||
{
|
||||
0x0033,
|
||||
PROT_TLS1_0 | PROT_TLS1_2,
|
||||
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "DHE-RSA-AES128-SHA",
|
||||
{ CALG_DH_EPHEM, CALG_AES_128, CALG_SHA1, CALG_RSA_SIGN }
|
||||
},
|
||||
{
|
||||
0x0035,
|
||||
PROT_TLS1_0 | PROT_TLS1_2,
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA", "AES256-SHA",
|
||||
{ CALG_RSA_KEYX, CALG_AES_256, CALG_SHA1, CALG_RSA_SIGN }
|
||||
},
|
||||
{
|
||||
0x0038,
|
||||
PROT_TLS1_0 | PROT_TLS1_2,
|
||||
"TLS_DHE_DSS_WITH_AES_256_CBC_SHA", "DHE-DSS-AES256-SHA",
|
||||
{ CALG_DH_EPHEM, CALG_AES_256, CALG_SHA1, CALG_DSS_SIGN }
|
||||
},
|
||||
{
|
||||
0x0039,
|
||||
PROT_TLS1_0 | PROT_TLS1_2,
|
||||
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA", "DHE-RSA-AES256-SHA",
|
||||
{ CALG_DH_EPHEM, CALG_AES_256, CALG_SHA1, CALG_RSA_SIGN }
|
||||
},
|
||||
{
|
||||
0x003B,
|
||||
PROT_TLS1_2,
|
||||
"TLS_RSA_WITH_NULL_SHA256", "NULL-SHA256",
|
||||
{ CALG_RSA_KEYX, 0, CALG_SHA_256, CALG_RSA_SIGN }
|
||||
},
|
||||
{
|
||||
0x003C,
|
||||
PROT_TLS1_2,
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA256", "AES128-SHA256",
|
||||
{ CALG_RSA_KEYX, CALG_AES_128, CALG_SHA_256, CALG_RSA_SIGN }
|
||||
},
|
||||
{
|
||||
0x003D,
|
||||
PROT_TLS1_2,
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA256", "AES256-SHA256",
|
||||
{ CALG_RSA_KEYX, CALG_AES_256, CALG_SHA_256, CALG_RSA_SIGN }
|
||||
},
|
||||
{
|
||||
0x0040,
|
||||
PROT_TLS1_2,
|
||||
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", "DHE-DSS-AES128-SHA256",
|
||||
{ CALG_DH_EPHEM, CALG_AES_128, CALG_SHA_256, CALG_DSS_SIGN }
|
||||
},
|
||||
{
|
||||
0x009C,
|
||||
PROT_TLS1_2,
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256", "AES128-GCM-SHA256",
|
||||
{ CALG_RSA_KEYX, CALG_AES_128, CALG_SHA_256, CALG_RSA_SIGN }
|
||||
},
|
||||
{
|
||||
0x009D,
|
||||
PROT_TLS1_2,
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384", "AES256-GCM-SHA384",
|
||||
{ CALG_RSA_KEYX, CALG_AES_256, CALG_SHA_384, CALG_RSA_SIGN }
|
||||
},
|
||||
{
|
||||
0x009E,
|
||||
PROT_TLS1_2,
|
||||
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "DHE-RSA-AES128-GCM-SHA256",
|
||||
{ CALG_DH_EPHEM, CALG_AES_128, CALG_SHA_256, CALG_RSA_SIGN }
|
||||
},
|
||||
{
|
||||
0x009F,
|
||||
PROT_TLS1_2,
|
||||
"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "DHE-RSA-AES256-GCM-SHA384",
|
||||
{ CALG_DH_EPHEM, CALG_AES_256, CALG_SHA_384, CALG_RSA_SIGN }
|
||||
},
|
||||
{
|
||||
0xC027,
|
||||
PROT_TLS1_2,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "ECDHE-RSA-AES128-SHA256",
|
||||
{ CALG_ECDH, CALG_AES_128, CALG_SHA_256, CALG_RSA_SIGN }
|
||||
},
|
||||
{
|
||||
0xC028,
|
||||
PROT_TLS1_2,
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "ECDHE-RSA-AES256-SHA384",
|
||||
{ CALG_ECDH, CALG_AES_256, CALG_SHA_384, CALG_RSA_SIGN }
|
||||
}
|
||||
};
|
||||
|
||||
#define MAX_ALG_ID 50
|
||||
|
||||
extern void ma_schannel_set_sec_error(MARIADB_PVIO *pvio, DWORD ErrorNo);
|
||||
|
||||
/*
|
||||
Initializes SSL and allocate global
|
||||
context SSL_context
|
||||
|
||||
SYNOPSIS
|
||||
ma_tls_start
|
||||
|
||||
RETURN VALUES
|
||||
0 success
|
||||
1 error
|
||||
*/
|
||||
int ma_tls_start(char *errmsg, size_t errmsg_len)
|
||||
{
|
||||
ma_tls_initialized = TRUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Release SSL and free resources
|
||||
Will be automatically executed by
|
||||
mysql_server_end() function
|
||||
|
||||
SYNOPSIS
|
||||
ma_tls_end()
|
||||
void
|
||||
|
||||
RETURN VALUES
|
||||
void
|
||||
*/
|
||||
void ma_tls_end()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* {{{ static int ma_tls_set_client_certs(MARIADB_TLS *ctls) */
|
||||
static int ma_tls_set_client_certs(MARIADB_TLS *ctls,const CERT_CONTEXT **cert_ctx)
|
||||
{
|
||||
MYSQL *mysql= ctls->pvio->mysql;
|
||||
char *certfile= mysql->options.ssl_cert,
|
||||
*keyfile= mysql->options.ssl_key;
|
||||
MARIADB_PVIO *pvio= ctls->pvio;
|
||||
char errmsg[256];
|
||||
|
||||
if (!certfile && keyfile)
|
||||
certfile= keyfile;
|
||||
if (!keyfile && certfile)
|
||||
keyfile= certfile;
|
||||
|
||||
if (!certfile)
|
||||
return 0;
|
||||
|
||||
*cert_ctx = schannel_create_cert_context(certfile, keyfile, errmsg, sizeof(errmsg));
|
||||
if (!*cert_ctx)
|
||||
{
|
||||
pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "SSL connection error: %s", errmsg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ void *ma_tls_init(MARIADB_TLS *ctls, MYSQL *mysql) */
|
||||
void *ma_tls_init(MYSQL *mysql)
|
||||
{
|
||||
SC_CTX *sctx = (SC_CTX *)LocalAlloc(LMEM_ZEROINIT, sizeof(SC_CTX));
|
||||
if (sctx)
|
||||
{
|
||||
SecInvalidateHandle(&sctx->CredHdl);
|
||||
SecInvalidateHandle(&sctx->hCtxt);
|
||||
}
|
||||
return sctx;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
/*
|
||||
Maps between openssl suite names and schannel alg_ids.
|
||||
Every suite has 4 algorithms (for exchange, encryption, hash and signing).
|
||||
|
||||
The input string is a set of suite names (openssl), separated
|
||||
by ':'
|
||||
|
||||
The output is written into the array 'arr' of size 'arr_size'
|
||||
The function returns number of elements written to the 'arr'.
|
||||
*/
|
||||
|
||||
static struct _tls_version {
|
||||
const char *tls_version;
|
||||
DWORD protocol;
|
||||
} tls_version[]= {
|
||||
{"TLSv1.0", PROT_TLS1_0},
|
||||
{"TLSv1.2", PROT_TLS1_2},
|
||||
{"TLSv1.3", PROT_TLS1_3},
|
||||
{"SSLv3", PROT_SSL3}
|
||||
};
|
||||
|
||||
/* The following list was produced with OpenSSL 1.1.1j
|
||||
by executing `openssl ciphers -V`. */
|
||||
static struct {
|
||||
DWORD dwCipherSuite;
|
||||
const char *openssl_name;
|
||||
} openssl_ciphers[] = {
|
||||
{0x002F, "AES128-SHA"},
|
||||
{0x0033, "DHE-RSA-AES128-SHA"},
|
||||
{0x0035, "AES256-SHA"},
|
||||
{0x0039, "DHE-RSA-AES256-SHA"},
|
||||
{0x003C, "AES128-SHA256"},
|
||||
{0x003D, "AES256-SHA256"},
|
||||
{0x0067, "DHE-RSA-AES128-SHA256"},
|
||||
{0x006B, "DHE-RSA-AES256-SHA256"},
|
||||
{0x008C, "PSK-AES128-CBC-SHA"},
|
||||
{0x008D, "PSK-AES256-CBC-SHA"},
|
||||
{0x0090, "DHE-PSK-AES128-CBC-SHA"},
|
||||
{0x0091, "DHE-PSK-AES256-CBC-SHA"},
|
||||
{0x0094, "RSA-PSK-AES128-CBC-SHA"},
|
||||
{0x0095, "RSA-PSK-AES256-CBC-SHA"},
|
||||
{0x009C, "AES128-GCM-SHA256"},
|
||||
{0x009D, "AES256-GCM-SHA384"},
|
||||
{0x009E, "DHE-RSA-AES128-GCM-SHA256"},
|
||||
{0x009F, "DHE-RSA-AES256-GCM-SHA384"},
|
||||
{0x00A8, "PSK-AES128-GCM-SHA256"},
|
||||
{0x00A9, "PSK-AES256-GCM-SHA384"},
|
||||
{0x00AA, "DHE-PSK-AES128-GCM-SHA256"},
|
||||
{0x00AB, "DHE-PSK-AES256-GCM-SHA384"},
|
||||
{0x00AC, "RSA-PSK-AES128-GCM-SHA256"},
|
||||
{0x00AD, "RSA-PSK-AES256-GCM-SHA384"},
|
||||
{0x00AE, "PSK-AES128-CBC-SHA256"},
|
||||
{0x00AF, "PSK-AES256-CBC-SHA384"},
|
||||
{0x00B2, "DHE-PSK-AES128-CBC-SHA256"},
|
||||
{0x00B3, "DHE-PSK-AES256-CBC-SHA384"},
|
||||
{0x00B6, "RSA-PSK-AES128-CBC-SHA256"},
|
||||
{0x00B7, "RSA-PSK-AES256-CBC-SHA384"},
|
||||
{0x1301, "TLS_AES_128_GCM_SHA256"},
|
||||
{0x1302, "TLS_AES_256_GCM_SHA384"},
|
||||
{0x1303, "TLS_CHACHA20_POLY1305_SHA256"},
|
||||
{0xC009, "ECDHE-ECDSA-AES128-SHA"},
|
||||
{0xC00A, "ECDHE-ECDSA-AES256-SHA"},
|
||||
{0xC013, "ECDHE-RSA-AES128-SHA"},
|
||||
{0xC014, "ECDHE-RSA-AES256-SHA"},
|
||||
{0xC01D, "SRP-AES-128-CBC-SHA"},
|
||||
{0xC01E, "SRP-RSA-AES-128-CBC-SHA"},
|
||||
{0xC020, "SRP-AES-256-CBC-SHA"},
|
||||
{0xC021, "SRP-RSA-AES-256-CBC-SHA"},
|
||||
{0xC023, "ECDHE-ECDSA-AES128-SHA256"},
|
||||
{0xC024, "ECDHE-ECDSA-AES256-SHA384"},
|
||||
{0xC027, "ECDHE-RSA-AES128-SHA256"},
|
||||
{0xC028, "ECDHE-RSA-AES256-SHA384"},
|
||||
{0xC02B, "ECDHE-ECDSA-AES128-GCM-SHA256"},
|
||||
{0xC02C, "ECDHE-ECDSA-AES256-GCM-SHA384"},
|
||||
{0xC02F, "ECDHE-RSA-AES128-GCM-SHA256"},
|
||||
{0xC030, "ECDHE-RSA-AES256-GCM-SHA384"},
|
||||
{0xC035, "ECDHE-PSK-AES128-CBC-SHA"},
|
||||
{0xC036, "ECDHE-PSK-AES256-CBC-SHA"},
|
||||
{0xC037, "ECDHE-PSK-AES128-CBC-SHA256"},
|
||||
{0xC038, "ECDHE-PSK-AES256-CBC-SHA384"},
|
||||
{0xCCA8, "ECDHE-RSA-CHACHA20-POLY1305"},
|
||||
{0xCCA9, "ECDHE-ECDSA-CHACHA20-POLY1305"},
|
||||
{0xCCAA, "DHE-RSA-CHACHA20-POLY1305"},
|
||||
{0xCCAB, "PSK-CHACHA20-POLY1305"},
|
||||
{0xCCAC, "ECDHE-PSK-CHACHA20-POLY1305"},
|
||||
{0xCCAD, "DHE-PSK-CHACHA20-POLY1305"},
|
||||
{0xCCAE, "RSA-PSK-CHACHA20-POLY1305"}
|
||||
};
|
||||
|
||||
static size_t set_cipher(char * cipher_str, DWORD protocol, ALG_ID *arr , size_t arr_size)
|
||||
{
|
||||
char *token = strtok(cipher_str, ":");
|
||||
size_t pos = 0;
|
||||
|
||||
while (token)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for(i = 0; i < sizeof(cipher_map)/sizeof(cipher_map[0]) ; i++)
|
||||
{
|
||||
if((pos + 4 < arr_size && strcmp(cipher_map[i].openssl_name, token) == 0) ||
|
||||
(cipher_map[i].protocol <= protocol))
|
||||
{
|
||||
memcpy(arr + pos, cipher_map[i].algs, sizeof(ALG_ID)* 4);
|
||||
pos += 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
token = strtok(NULL, ":");
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
my_bool ma_tls_connect(MARIADB_TLS *ctls)
|
||||
{
|
||||
MYSQL *mysql;
|
||||
SCHANNEL_CRED Cred = {0};
|
||||
MARIADB_PVIO *pvio;
|
||||
my_bool rc= 1;
|
||||
SC_CTX *sctx;
|
||||
SECURITY_STATUS sRet;
|
||||
ALG_ID AlgId[MAX_ALG_ID];
|
||||
size_t i;
|
||||
DWORD protocol = 0;
|
||||
int verify_certs;
|
||||
const CERT_CONTEXT* cert_context = NULL;
|
||||
|
||||
if (!ctls)
|
||||
return 1;
|
||||
|
||||
pvio= ctls->pvio;
|
||||
sctx= (SC_CTX *)ctls->ssl;
|
||||
if (!pvio || !sctx)
|
||||
return 1;
|
||||
|
||||
mysql= pvio->mysql;
|
||||
if (!mysql)
|
||||
return 1;
|
||||
|
||||
/* Set cipher */
|
||||
if (mysql->options.ssl_cipher)
|
||||
{
|
||||
|
||||
/* check if a protocol was specified as a cipher:
|
||||
* In this case don't allow cipher suites which belong to newer protocols
|
||||
* Please note: There are no cipher suites for TLS1.1
|
||||
*/
|
||||
for (i = 0; i < sizeof(tls_version) / sizeof(tls_version[0]); i++)
|
||||
{
|
||||
if (!_stricmp(mysql->options.ssl_cipher, tls_version[i].tls_version))
|
||||
protocol |= tls_version[i].protocol;
|
||||
}
|
||||
memset(AlgId, 0, sizeof(AlgId));
|
||||
Cred.cSupportedAlgs = (DWORD)set_cipher(mysql->options.ssl_cipher, protocol, AlgId, MAX_ALG_ID);
|
||||
if (Cred.cSupportedAlgs)
|
||||
{
|
||||
Cred.palgSupportedAlgs = AlgId;
|
||||
}
|
||||
else if (!protocol)
|
||||
{
|
||||
ma_schannel_set_sec_error(pvio, SEC_E_ALGORITHM_MISMATCH);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
Cred.dwVersion= SCHANNEL_CRED_VERSION;
|
||||
|
||||
Cred.dwFlags = SCH_CRED_NO_SERVERNAME_CHECK | SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION;
|
||||
|
||||
if (mysql->options.extension && mysql->options.extension->tls_version)
|
||||
{
|
||||
if (strstr(mysql->options.extension->tls_version, "TLSv1.0"))
|
||||
Cred.grbitEnabledProtocols|= SP_PROT_TLS1_0_CLIENT;
|
||||
if (strstr(mysql->options.extension->tls_version, "TLSv1.1"))
|
||||
Cred.grbitEnabledProtocols|= SP_PROT_TLS1_1_CLIENT;
|
||||
if (strstr(mysql->options.extension->tls_version, "TLSv1.2"))
|
||||
Cred.grbitEnabledProtocols|= SP_PROT_TLS1_2_CLIENT;
|
||||
}
|
||||
if (!Cred.grbitEnabledProtocols)
|
||||
Cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT;
|
||||
|
||||
|
||||
if (ma_tls_set_client_certs(ctls, &cert_context))
|
||||
goto end;
|
||||
|
||||
if (cert_context)
|
||||
{
|
||||
Cred.cCreds = 1;
|
||||
Cred.paCred = &cert_context;
|
||||
}
|
||||
sRet= AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND,
|
||||
NULL, &Cred, NULL, NULL, &sctx->CredHdl, NULL);
|
||||
if (sRet)
|
||||
{
|
||||
ma_schannel_set_sec_error(pvio, sRet);
|
||||
goto end;
|
||||
}
|
||||
if (ma_schannel_client_handshake(ctls) != SEC_E_OK)
|
||||
goto end;
|
||||
|
||||
verify_certs = mysql->options.ssl_ca || mysql->options.ssl_capath ||
|
||||
(mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT);
|
||||
|
||||
if (verify_certs)
|
||||
{
|
||||
if (!ma_schannel_verify_certs(ctls, (mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT)))
|
||||
goto end;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
end:
|
||||
if (cert_context)
|
||||
schannel_free_cert_context(cert_context);
|
||||
return rc;
|
||||
}
|
||||
|
||||
ssize_t ma_tls_read(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
|
||||
{
|
||||
SC_CTX *sctx= (SC_CTX *)ctls->ssl;
|
||||
MARIADB_PVIO *pvio= ctls->pvio;
|
||||
DWORD dlength= 0;
|
||||
SECURITY_STATUS status = ma_schannel_read_decrypt(pvio, &sctx->hCtxt, &dlength, (uchar *)buffer, (DWORD)length);
|
||||
if (status == SEC_I_CONTEXT_EXPIRED)
|
||||
return 0; /* other side shut down the connection. */
|
||||
if (status == SEC_I_RENEGOTIATE)
|
||||
return -1; /* Do not handle renegotiate yet */
|
||||
|
||||
return (status == SEC_E_OK)? (ssize_t)dlength : -1;
|
||||
}
|
||||
|
||||
ssize_t ma_tls_write(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
|
||||
{
|
||||
MARIADB_PVIO *pvio= ctls->pvio;
|
||||
ssize_t rc, wlength= 0;
|
||||
ssize_t remain= length;
|
||||
|
||||
while (remain > 0)
|
||||
{
|
||||
if ((rc= ma_schannel_write_encrypt(pvio, (uchar *)buffer + wlength, remain)) <= 0)
|
||||
return rc;
|
||||
wlength+= rc;
|
||||
remain-= rc;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
/* {{{ my_bool ma_tls_close(MARIADB_PVIO *pvio) */
|
||||
my_bool ma_tls_close(MARIADB_TLS *ctls)
|
||||
{
|
||||
SC_CTX *sctx= (SC_CTX *)ctls->ssl;
|
||||
|
||||
if (sctx)
|
||||
{
|
||||
LocalFree(sctx->IoBuffer);
|
||||
|
||||
if (SecIsValidHandle(&sctx->CredHdl))
|
||||
FreeCredentialHandle(&sctx->CredHdl);
|
||||
|
||||
if (SecIsValidHandle(&sctx->hCtxt))
|
||||
DeleteSecurityContext(&sctx->hCtxt);
|
||||
}
|
||||
LocalFree(sctx);
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
int ma_tls_verify_server_cert(MARIADB_TLS *ctls)
|
||||
{
|
||||
/* Done elsewhere */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *cipher_name(const SecPkgContext_CipherInfo *CipherInfo)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for(i = 0; i < sizeof(openssl_ciphers)/sizeof(openssl_ciphers[0]) ; i++)
|
||||
{
|
||||
if (CipherInfo->dwCipherSuite == openssl_ciphers[i].dwCipherSuite)
|
||||
return openssl_ciphers[i].openssl_name;
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
const char *ma_tls_get_cipher(MARIADB_TLS *ctls)
|
||||
{
|
||||
SecPkgContext_CipherInfo CipherInfo = { SECPKGCONTEXT_CIPHERINFO_V1 };
|
||||
SECURITY_STATUS sRet;
|
||||
SC_CTX *sctx;
|
||||
|
||||
if (!ctls || !ctls->ssl)
|
||||
return NULL;
|
||||
|
||||
sctx= (SC_CTX *)ctls->ssl;
|
||||
sRet= QueryContextAttributesA(&sctx->hCtxt, SECPKG_ATTR_CIPHER_INFO, (PVOID)&CipherInfo);
|
||||
|
||||
if (sRet != SEC_E_OK)
|
||||
return NULL;
|
||||
|
||||
return cipher_name(&CipherInfo);
|
||||
}
|
||||
|
||||
unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int len)
|
||||
{
|
||||
SC_CTX *sctx= (SC_CTX *)ctls->ssl;
|
||||
PCCERT_CONTEXT pRemoteCertContext = NULL;
|
||||
if (QueryContextAttributes(&sctx->hCtxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext) != SEC_E_OK)
|
||||
return 0;
|
||||
CertGetCertificateContextProperty(pRemoteCertContext, CERT_HASH_PROP_ID, fp, (DWORD *)&len);
|
||||
CertFreeCertificateContext(pRemoteCertContext);
|
||||
return len;
|
||||
}
|
854
vendor/MDBC/libmariadb/secure/schannel_certs.c
vendored
Normal file
854
vendor/MDBC/libmariadb/secure/schannel_certs.c
vendored
Normal file
@ -0,0 +1,854 @@
|
||||
/************************************************************************************
|
||||
Copyright (C) 2019 MariaDB
|
||||
|
||||
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 module contain X509 certificate handling on Windows.
|
||||
PEM parsing, loading client certificate and key, server certificate validation
|
||||
*/
|
||||
|
||||
/*
|
||||
CERT_CHAIN_ENGINE_CONFIG has additional members in Windows 8.1
|
||||
To allow client to be work on pre-8.1 Windows, compile
|
||||
with corresponding _WIN32_WINNT
|
||||
*/
|
||||
#ifdef _WIN32_WINNT
|
||||
#undef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0601
|
||||
#endif
|
||||
|
||||
#include <winsock2.h>
|
||||
#include "schannel_certs.h"
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <winhttp.h>
|
||||
#include <assert.h>
|
||||
#include "win32_errmsg.h"
|
||||
|
||||
/*
|
||||
Return GetLastError(), or, if this unexpectedly gives success,
|
||||
return ERROR_INTERNAL_ERROR.
|
||||
|
||||
Background - in several cases in this module we return GetLastError()
|
||||
after an Windows function fails. However, we do not want the function to
|
||||
return success, even if GetLastError() was suddenly 0.
|
||||
*/
|
||||
static DWORD get_last_error()
|
||||
{
|
||||
DWORD ret = GetLastError();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
// We generally expect last error to be set API fails.
|
||||
// thus the debug assertion-
|
||||
assert(0);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
#define FAIL(...) \
|
||||
do{\
|
||||
status = get_last_error();\
|
||||
ma_format_win32_error(errmsg, errmsg_len, status, __VA_ARGS__);\
|
||||
goto cleanup;\
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
Load file into memory. Add null terminator at the end, so it will be a valid C string.
|
||||
*/
|
||||
static char* pem_file_to_string(const char* file, char* errmsg, size_t errmsg_len)
|
||||
{
|
||||
LARGE_INTEGER file_size;
|
||||
size_t file_bufsize = 0;
|
||||
size_t total_bytes_read = 0;
|
||||
char* file_buffer = NULL;
|
||||
SECURITY_STATUS status = SEC_E_OK;
|
||||
|
||||
HANDLE file_handle = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (file_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
FAIL("failed to open file '%s'", file);
|
||||
}
|
||||
|
||||
if (!GetFileSizeEx(file_handle, &file_size))
|
||||
{
|
||||
FAIL("GetFileSizeEx failed on '%s'", file);
|
||||
}
|
||||
|
||||
if (file_size.QuadPart > ULONG_MAX - 1)
|
||||
{
|
||||
SetLastError(SEC_E_INVALID_PARAMETER);
|
||||
FAIL("file '%s' too large", file);
|
||||
}
|
||||
|
||||
file_bufsize = (size_t)file_size.QuadPart;
|
||||
file_buffer = (char*)LocalAlloc(0,file_bufsize + 1);
|
||||
if (!file_buffer)
|
||||
{
|
||||
FAIL("LocalAlloc(0,%zu) failed", file_bufsize + 1);
|
||||
}
|
||||
|
||||
while (total_bytes_read < file_bufsize)
|
||||
{
|
||||
DWORD bytes_to_read = (DWORD)(file_bufsize - total_bytes_read);
|
||||
DWORD bytes_read = 0;
|
||||
|
||||
if (!ReadFile(file_handle, file_buffer + total_bytes_read,
|
||||
bytes_to_read, &bytes_read, NULL))
|
||||
{
|
||||
FAIL("ReadFile() failed to read file '%s'", file);
|
||||
}
|
||||
if (bytes_read == 0)
|
||||
{
|
||||
/* Premature EOF -- adjust the bufsize to the new value */
|
||||
file_bufsize = total_bytes_read;
|
||||
}
|
||||
else
|
||||
{
|
||||
total_bytes_read += bytes_read;
|
||||
}
|
||||
}
|
||||
|
||||
/* Null terminate the buffer */
|
||||
file_buffer[file_bufsize] = '\0';
|
||||
|
||||
cleanup:
|
||||
if (file_handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(file_handle);
|
||||
}
|
||||
if (status)
|
||||
{
|
||||
/* Some error happened. */
|
||||
LocalFree(file_buffer);
|
||||
file_buffer = NULL;
|
||||
}
|
||||
return file_buffer;
|
||||
}
|
||||
|
||||
|
||||
// Structure for parsing BEGIN/END sections inside pem.
|
||||
typedef struct _pem_type_desc
|
||||
{
|
||||
const char* begin_tag;
|
||||
size_t begin_tag_len;
|
||||
const char* end_tag;
|
||||
size_t end_tag_len;
|
||||
} pem_type_desc;
|
||||
|
||||
#define BEGIN_TAG(x) "-----BEGIN " x "-----"
|
||||
#define END_TAG(x) "\n-----END " x "-----"
|
||||
#define PEM_SECTION(tag) {BEGIN_TAG(tag), sizeof(BEGIN_TAG(tag))-1, END_TAG(tag), sizeof(END_TAG(tag))-1}
|
||||
|
||||
typedef enum {
|
||||
PEM_TYPE_CERTIFICATE = 0,
|
||||
PEM_TYPE_X509_CRL,
|
||||
PEM_TYPE_RSA_PRIVATE_KEY,
|
||||
PEM_TYPE_PRIVATE_KEY
|
||||
} PEM_TYPE;
|
||||
|
||||
static const pem_type_desc pem_sections[] = {
|
||||
PEM_SECTION("CERTIFICATE"),
|
||||
PEM_SECTION("X509 CRL"),
|
||||
PEM_SECTION("RSA PRIVATE KEY"),
|
||||
PEM_SECTION("PRIVATE KEY")
|
||||
};
|
||||
|
||||
/*
|
||||
Locate a substring in pem for given type,
|
||||
e.g section between BEGIN CERTIFICATE and END CERTIFICATE
|
||||
in PEMs base64 format, with header and footer.
|
||||
|
||||
output parameters 'begin' and 'end' are set upon return.
|
||||
it is possible that functions returns 'begin' != NULL but
|
||||
'end' = NULL. This is generally a format error, meaning that
|
||||
the end tag was not found
|
||||
*/
|
||||
void pem_locate(char* pem_str,
|
||||
PEM_TYPE type,
|
||||
char** begin,
|
||||
char** end)
|
||||
{
|
||||
*begin = NULL;
|
||||
*end = NULL;
|
||||
char c;
|
||||
|
||||
const pem_type_desc* desc = &pem_sections[type];
|
||||
*begin = strstr(pem_str, desc->begin_tag);
|
||||
if (!(*begin))
|
||||
return;
|
||||
|
||||
// We expect newline after the
|
||||
// begin tag, LF or CRLF
|
||||
c = (*begin)[desc->begin_tag_len];
|
||||
|
||||
if (c != '\r' && c != '\n')
|
||||
{
|
||||
*begin = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
*end = strstr(*begin + desc->begin_tag_len + 1, desc->end_tag);
|
||||
if (!*end)
|
||||
return; // error, end marker not found
|
||||
|
||||
(*end) += desc->end_tag_len;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Add certificates, or CRLs from a PEM file to Wincrypt store
|
||||
*/
|
||||
static SECURITY_STATUS add_certs_to_store(
|
||||
HCERTSTORE trust_store,
|
||||
const char* file,
|
||||
PEM_TYPE type,
|
||||
char* errmsg,
|
||||
size_t errmsg_len)
|
||||
{
|
||||
char* file_buffer = NULL;
|
||||
char* cur = NULL;
|
||||
SECURITY_STATUS status = SEC_E_OK;
|
||||
CRL_CONTEXT* crl_context = NULL;
|
||||
CERT_CONTEXT* cert_context = NULL;
|
||||
char* begin;
|
||||
char* end;
|
||||
|
||||
file_buffer = pem_file_to_string(file, errmsg, errmsg_len);
|
||||
if (!file_buffer)
|
||||
goto cleanup;
|
||||
|
||||
for (cur = file_buffer; ; cur = end)
|
||||
{
|
||||
pem_locate(cur, type, &begin, &end);
|
||||
|
||||
if (!begin)
|
||||
break;
|
||||
|
||||
if (!end)
|
||||
{
|
||||
SetLastError(SEC_E_INVALID_PARAMETER);
|
||||
FAIL("Invalid PEM file '%s', missing end marker corresponding to begin marker '%s' at offset %zu",
|
||||
file, pem_sections[type].begin_tag, (size_t)(begin - file_buffer));
|
||||
}
|
||||
CERT_BLOB cert_blob;
|
||||
void* context = NULL;
|
||||
DWORD actual_content_type = 0;
|
||||
|
||||
cert_blob.pbData = (BYTE*)begin;
|
||||
cert_blob.cbData = (DWORD)(end - begin);
|
||||
if (!CryptQueryObject(
|
||||
CERT_QUERY_OBJECT_BLOB, &cert_blob,
|
||||
CERT_QUERY_CONTENT_FLAG_CERT | CERT_QUERY_CONTENT_FLAG_CRL,
|
||||
CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, &actual_content_type,
|
||||
NULL, NULL, NULL, (const void**)&context))
|
||||
{
|
||||
FAIL("failed to extract certificate from PEM file '%s'",file);
|
||||
}
|
||||
|
||||
if (!context)
|
||||
{
|
||||
SetLastError(SEC_E_INTERNAL_ERROR);
|
||||
FAIL("unexpected result from CryptQueryObject(),cert_context is NULL"
|
||||
" after successful completion, file '%s'",
|
||||
file);
|
||||
}
|
||||
|
||||
if (actual_content_type == CERT_QUERY_CONTENT_CERT)
|
||||
{
|
||||
CERT_CONTEXT* cert_context = (CERT_CONTEXT*)context;
|
||||
if (!CertAddCertificateContextToStore(
|
||||
trust_store, cert_context,
|
||||
CERT_STORE_ADD_ALWAYS, NULL))
|
||||
{
|
||||
FAIL("CertAddCertificateContextToStore failed");
|
||||
}
|
||||
}
|
||||
else if (actual_content_type == CERT_QUERY_CONTENT_CRL)
|
||||
{
|
||||
CRL_CONTEXT* crl_context = (CRL_CONTEXT*)context;
|
||||
if (!CertAddCRLContextToStore(
|
||||
trust_store, crl_context,
|
||||
CERT_STORE_ADD_ALWAYS, NULL))
|
||||
{
|
||||
FAIL("CertAddCRLContextToStore() failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
cleanup:
|
||||
LocalFree(file_buffer);
|
||||
if (cert_context)
|
||||
CertFreeCertificateContext(cert_context);
|
||||
if (crl_context)
|
||||
CertFreeCRLContext(crl_context);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
Add a directory to store, i.e try to load all files.
|
||||
(extract certificates and add them to store)
|
||||
|
||||
@return 0 on success, error only if directory is invalid.
|
||||
*/
|
||||
SECURITY_STATUS add_dir_to_store(HCERTSTORE trust_store, const char* dir,
|
||||
PEM_TYPE type, char* errmsg, size_t errmsg_len)
|
||||
{
|
||||
WIN32_FIND_DATAA ffd;
|
||||
char path[MAX_PATH];
|
||||
char pattern[MAX_PATH];
|
||||
DWORD dwAttr;
|
||||
HANDLE hFind = INVALID_HANDLE_VALUE;
|
||||
SECURITY_STATUS status = SEC_E_OK;
|
||||
|
||||
if ((dwAttr = GetFileAttributes(dir)) == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
SetLastError(SEC_E_INVALID_PARAMETER);
|
||||
FAIL("directory '%s' does not exist", dir);
|
||||
}
|
||||
if (!(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
|
||||
{
|
||||
SetLastError(SEC_E_INVALID_PARAMETER);
|
||||
FAIL("'%s' is not a directory", dir);
|
||||
}
|
||||
sprintf_s(pattern, sizeof(pattern), "%s\\*", dir);
|
||||
hFind = FindFirstFile(pattern, &ffd);
|
||||
if (hFind == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
FAIL("FindFirstFile(%s) failed",pattern);
|
||||
}
|
||||
do
|
||||
{
|
||||
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
continue;
|
||||
sprintf_s(path, sizeof(path), "%s\\%s", dir, ffd.cFileName);
|
||||
|
||||
// ignore error from add_certs_to_store(), not all file
|
||||
// maybe PEM.
|
||||
add_certs_to_store(trust_store, path, type, errmsg,
|
||||
errmsg_len);
|
||||
} while (FindNextFile(hFind, &ffd) != 0);
|
||||
|
||||
cleanup:
|
||||
if (hFind != INVALID_HANDLE_VALUE)
|
||||
FindClose(hFind);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Count certificates in store. */
|
||||
static int count_certificates(HCERTSTORE store)
|
||||
{
|
||||
int num_certs = 0;
|
||||
PCCERT_CONTEXT c = NULL;
|
||||
|
||||
while ((c = CertEnumCertificatesInStore(store, c)))
|
||||
num_certs++;
|
||||
|
||||
return num_certs;
|
||||
}
|
||||
|
||||
/**
|
||||
Creates certificate store with user defined CA chain and/or CRL.
|
||||
Loads PEM certificate from files or directories.
|
||||
|
||||
If only CRLFile/CRLPath is defined, the "system" store is duplicated,
|
||||
and new CRLs are added to it.
|
||||
|
||||
If CAFile/CAPAth is defined, then new empty store is created, and CAs
|
||||
(and CRLs, if defined), are added to it.
|
||||
|
||||
The function throws an error, if none of the files in CAFile/CAPath have a valid certificate.
|
||||
It is also an error if CRLFile does not exist.
|
||||
*/
|
||||
SECURITY_STATUS schannel_create_store(
|
||||
const char* CAFile,
|
||||
const char* CAPath,
|
||||
const char* CRLFile,
|
||||
const char* CRLPath,
|
||||
HCERTSTORE* out_store,
|
||||
char* errmsg,
|
||||
size_t errmsg_len)
|
||||
{
|
||||
|
||||
HCERTSTORE store = NULL;
|
||||
HCERTSTORE system_store = NULL;
|
||||
int status = SEC_E_OK;
|
||||
|
||||
*out_store = NULL;
|
||||
if (!CAFile && !CAPath && !CRLFile && !CRLPath)
|
||||
{
|
||||
/* Nothing to do, caller will use default store*/
|
||||
*out_store = NULL;
|
||||
return SEC_E_OK;
|
||||
}
|
||||
if (CAFile || CAPath)
|
||||
{
|
||||
/* Open the certificate store */
|
||||
store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, (HCRYPTPROV)NULL,
|
||||
CERT_STORE_CREATE_NEW_FLAG, NULL);
|
||||
if (!store)
|
||||
{
|
||||
FAIL("CertOpenStore failed for memory store");
|
||||
}
|
||||
}
|
||||
else if (CRLFile || CRLPath)
|
||||
{
|
||||
/* Only CRL was provided, copy system store, add revocation list to
|
||||
* it. */
|
||||
system_store =
|
||||
CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, (HCRYPTPROV_LEGACY)NULL,
|
||||
CERT_SYSTEM_STORE_CURRENT_USER, L"MY");
|
||||
if (!system_store)
|
||||
{
|
||||
FAIL("CertOpenStore failed for system store");
|
||||
}
|
||||
|
||||
store = CertDuplicateStore(system_store);
|
||||
if (!store)
|
||||
{
|
||||
FAIL("CertDuplicateStore failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (CAFile)
|
||||
{
|
||||
status = add_certs_to_store(store, CAFile,
|
||||
PEM_TYPE_CERTIFICATE, errmsg, errmsg_len);
|
||||
if (status)
|
||||
goto cleanup;
|
||||
}
|
||||
if (CAPath)
|
||||
{
|
||||
status = add_dir_to_store(store, CAPath,
|
||||
PEM_TYPE_CERTIFICATE, errmsg, errmsg_len);
|
||||
if (status)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((CAFile || CAPath) && store && !count_certificates(store))
|
||||
{
|
||||
SetLastError(SEC_E_INVALID_PARAMETER);
|
||||
FAIL("no valid certificates were found, CAFile='%s', CAPath='%s'",
|
||||
CAFile ? CAFile : "<not set>", CAPath ? CAPath : "<not set>");
|
||||
}
|
||||
|
||||
if (CRLFile)
|
||||
{
|
||||
status = add_certs_to_store(store, CRLFile, PEM_TYPE_X509_CRL,
|
||||
errmsg, errmsg_len);
|
||||
}
|
||||
if (CRLPath)
|
||||
{
|
||||
status = add_dir_to_store(store, CRLPath, PEM_TYPE_X509_CRL,
|
||||
errmsg, errmsg_len);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (system_store)
|
||||
CertCloseStore(system_store, 0);
|
||||
if (status && store)
|
||||
{
|
||||
CertCloseStore(store, 0);
|
||||
store = NULL;
|
||||
}
|
||||
*out_store = store;
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
The main verification logic.
|
||||
Taken almost completely from Windows 2003 Platform SDK 2003
|
||||
(Samples\Security\SSPI\SSL\WebClient.c)
|
||||
|
||||
The only difference here is is usage of custom store
|
||||
and chain engine.
|
||||
*/
|
||||
static SECURITY_STATUS VerifyServerCertificate(
|
||||
PCCERT_CONTEXT pServerCert,
|
||||
HCERTSTORE hStore,
|
||||
LPWSTR pwszServerName,
|
||||
DWORD dwRevocationCheckFlags,
|
||||
DWORD dwVerifyFlags,
|
||||
LPSTR errmsg,
|
||||
size_t errmsg_len)
|
||||
{
|
||||
SSL_EXTRA_CERT_CHAIN_POLICY_PARA polExtra;
|
||||
CERT_CHAIN_POLICY_PARA PolicyPara;
|
||||
CERT_CHAIN_POLICY_STATUS PolicyStatus;
|
||||
CERT_CHAIN_PARA ChainPara;
|
||||
HCERTCHAINENGINE hChainEngine = NULL;
|
||||
PCCERT_CHAIN_CONTEXT pChainContext = NULL;
|
||||
LPSTR rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH,
|
||||
szOID_SERVER_GATED_CRYPTO,
|
||||
szOID_SGC_NETSCAPE };
|
||||
DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR);
|
||||
SECURITY_STATUS status = SEC_E_OK;
|
||||
|
||||
if (pServerCert == NULL)
|
||||
{
|
||||
SetLastError(SEC_E_WRONG_PRINCIPAL);
|
||||
FAIL("Invalid parameter pServerCert passed to VerifyServerCertificate");
|
||||
}
|
||||
|
||||
ZeroMemory(&ChainPara, sizeof(ChainPara));
|
||||
ChainPara.cbSize = sizeof(ChainPara);
|
||||
ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
|
||||
ChainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages;
|
||||
ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
|
||||
|
||||
if (hStore)
|
||||
{
|
||||
CERT_CHAIN_ENGINE_CONFIG EngineConfig = { 0 };
|
||||
EngineConfig.cbSize = sizeof(EngineConfig);
|
||||
EngineConfig.hExclusiveRoot = hStore;
|
||||
if (!CertCreateCertificateChainEngine(&EngineConfig, &hChainEngine))
|
||||
{
|
||||
FAIL("CertCreateCertificateChainEngine failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (!CertGetCertificateChain(
|
||||
hChainEngine,
|
||||
pServerCert,
|
||||
NULL,
|
||||
pServerCert->hCertStore,
|
||||
&ChainPara,
|
||||
dwRevocationCheckFlags,
|
||||
NULL,
|
||||
&pChainContext))
|
||||
{
|
||||
FAIL("CertGetCertificateChain failed");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Validate certificate chain.
|
||||
ZeroMemory(&polExtra, sizeof(SSL_EXTRA_CERT_CHAIN_POLICY_PARA));
|
||||
polExtra.cbStruct = sizeof(SSL_EXTRA_CERT_CHAIN_POLICY_PARA);
|
||||
polExtra.dwAuthType = AUTHTYPE_SERVER;
|
||||
polExtra.fdwChecks = dwVerifyFlags;
|
||||
polExtra.pwszServerName = pwszServerName;
|
||||
|
||||
memset(&PolicyPara, 0, sizeof(PolicyPara));
|
||||
PolicyPara.cbSize = sizeof(PolicyPara);
|
||||
PolicyPara.pvExtraPolicyPara = &polExtra;
|
||||
|
||||
memset(&PolicyStatus, 0, sizeof(PolicyStatus));
|
||||
PolicyStatus.cbSize = sizeof(PolicyStatus);
|
||||
|
||||
if (!CertVerifyCertificateChainPolicy(
|
||||
CERT_CHAIN_POLICY_SSL,
|
||||
pChainContext,
|
||||
&PolicyPara,
|
||||
&PolicyStatus))
|
||||
{
|
||||
FAIL("CertVerifyCertificateChainPolicy failed");
|
||||
}
|
||||
|
||||
if (PolicyStatus.dwError)
|
||||
{
|
||||
SetLastError(PolicyStatus.dwError);
|
||||
FAIL("Server certificate validation failed");
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (hChainEngine)
|
||||
{
|
||||
CertFreeCertificateChainEngine(hChainEngine);
|
||||
}
|
||||
if (pChainContext)
|
||||
{
|
||||
CertFreeCertificateChain(pChainContext);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
void schannel_free_store(HCERTSTORE store)
|
||||
{
|
||||
if (store)
|
||||
CertCloseStore(store, 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Verify server certificate against a wincrypt store
|
||||
@return 0 - success, otherwise error occurred.
|
||||
*/
|
||||
SECURITY_STATUS schannel_verify_server_certificate(
|
||||
const CERT_CONTEXT* cert,
|
||||
HCERTSTORE store,
|
||||
BOOL check_revocation,
|
||||
const char* server_name,
|
||||
BOOL check_server_name,
|
||||
char* errmsg,
|
||||
size_t errmsg_len)
|
||||
{
|
||||
SECURITY_STATUS status = SEC_E_OK;
|
||||
wchar_t* wserver_name = NULL;
|
||||
DWORD dwVerifyFlags;
|
||||
DWORD dwRevocationFlags;
|
||||
|
||||
if (check_server_name)
|
||||
{
|
||||
int cchServerName = (int)strlen(server_name) + 1;
|
||||
wserver_name = (wchar_t*)LocalAlloc(0,sizeof(wchar_t) * cchServerName);
|
||||
if (!wserver_name)
|
||||
{
|
||||
FAIL("LocalAlloc() failed");
|
||||
}
|
||||
if (MultiByteToWideChar(CP_UTF8, 0, server_name, cchServerName, wserver_name, cchServerName) < 0)
|
||||
{
|
||||
FAIL("MultiByteToWideChar() failed");
|
||||
}
|
||||
}
|
||||
|
||||
dwVerifyFlags = 0;
|
||||
dwRevocationFlags = 0;
|
||||
if (check_revocation)
|
||||
dwRevocationFlags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT | CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY;
|
||||
if (!check_server_name)
|
||||
dwVerifyFlags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
|
||||
|
||||
status = VerifyServerCertificate(cert, store, wserver_name ? wserver_name : L"SERVER_NAME",
|
||||
dwRevocationFlags, dwVerifyFlags, errmsg, errmsg_len);
|
||||
|
||||
cleanup:
|
||||
LocalFree(wserver_name);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/* Attach private key (in PEM format) to client certificate */
|
||||
static SECURITY_STATUS load_private_key(CERT_CONTEXT* cert, char* private_key_str, size_t len, char* errmsg, size_t errmsg_len)
|
||||
{
|
||||
DWORD derlen = (DWORD)len;
|
||||
BYTE* derbuf = NULL;
|
||||
DWORD keyblob_len = 0;
|
||||
BYTE* keyblob = NULL;
|
||||
HCRYPTPROV hProv = 0;
|
||||
HCRYPTKEY hKey = 0;
|
||||
CERT_KEY_CONTEXT cert_key_context = { 0 };
|
||||
PCRYPT_PRIVATE_KEY_INFO pki = NULL;
|
||||
DWORD pki_len = 0;
|
||||
SECURITY_STATUS status = SEC_E_OK;
|
||||
|
||||
derbuf = LocalAlloc(0, derlen);
|
||||
if (!derbuf)
|
||||
{
|
||||
FAIL("LocalAlloc failed");
|
||||
}
|
||||
|
||||
if (!CryptStringToBinaryA(private_key_str, (DWORD)len, CRYPT_STRING_BASE64HEADER, derbuf, &derlen, NULL, NULL))
|
||||
{
|
||||
FAIL("Failed to convert BASE64 private key");
|
||||
}
|
||||
|
||||
/*
|
||||
To accommodate for both "BEGIN PRIVATE KEY" vs "BEGIN RSA PRIVATE KEY"
|
||||
sections in PEM, we try to decode with PKCS_PRIVATE_KEY_INFO first,
|
||||
and, if it fails, with PKCS_RSA_PRIVATE_KEY flag.
|
||||
*/
|
||||
if (CryptDecodeObjectEx(
|
||||
X509_ASN_ENCODING,
|
||||
PKCS_PRIVATE_KEY_INFO,
|
||||
derbuf, derlen,
|
||||
CRYPT_DECODE_ALLOC_FLAG,
|
||||
NULL, &pki, &pki_len))
|
||||
{
|
||||
// convert private key info to RSA private key blob
|
||||
if (!CryptDecodeObjectEx(
|
||||
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
||||
PKCS_RSA_PRIVATE_KEY,
|
||||
pki->PrivateKey.pbData,
|
||||
pki->PrivateKey.cbData,
|
||||
CRYPT_DECODE_ALLOC_FLAG,
|
||||
NULL, &keyblob, &keyblob_len))
|
||||
{
|
||||
FAIL("Failed to parse private key");
|
||||
}
|
||||
}
|
||||
else if (!CryptDecodeObjectEx(
|
||||
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
||||
PKCS_RSA_PRIVATE_KEY,
|
||||
derbuf, derlen,
|
||||
CRYPT_DECODE_ALLOC_FLAG, NULL,
|
||||
&keyblob, &keyblob_len))
|
||||
{
|
||||
FAIL("Failed to parse private key");
|
||||
}
|
||||
|
||||
if (!CryptAcquireContext(&hProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
|
||||
{
|
||||
FAIL("CryptAcquireContext failed");
|
||||
}
|
||||
|
||||
if (!CryptImportKey(hProv, keyblob, keyblob_len, 0, 0, (HCRYPTKEY*)&hKey))
|
||||
{
|
||||
FAIL("CryptImportKey failed");
|
||||
}
|
||||
cert_key_context.hCryptProv = hProv;
|
||||
cert_key_context.dwKeySpec = AT_KEYEXCHANGE;
|
||||
cert_key_context.cbSize = sizeof(cert_key_context);
|
||||
|
||||
/* assign private key to certificate context */
|
||||
if (!CertSetCertificateContextProperty(cert, CERT_KEY_CONTEXT_PROP_ID,
|
||||
CERT_STORE_NO_CRYPT_RELEASE_FLAG,
|
||||
&cert_key_context))
|
||||
{
|
||||
FAIL("CertSetCertificateContextProperty failed");
|
||||
}
|
||||
|
||||
cleanup:
|
||||
LocalFree(derbuf);
|
||||
LocalFree(keyblob);
|
||||
LocalFree(pki);
|
||||
if (hKey)
|
||||
CryptDestroyKey(hKey);
|
||||
if (status)
|
||||
{
|
||||
if (hProv)
|
||||
CryptReleaseContext(hProv, 0);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
Given PEM strings for certificate and private key,
|
||||
create a client certificate*
|
||||
*/
|
||||
static CERT_CONTEXT* create_client_certificate_mem(
|
||||
char* cert_file_content,
|
||||
char* key_file_content,
|
||||
char* errmsg,
|
||||
size_t errmsg_len)
|
||||
{
|
||||
CERT_CONTEXT* ctx = NULL;
|
||||
char* begin;
|
||||
char* end;
|
||||
CERT_BLOB cert_blob;
|
||||
DWORD actual_content_type = 0;
|
||||
SECURITY_STATUS status = SEC_E_OK;
|
||||
|
||||
/* Parse certificate */
|
||||
pem_locate(cert_file_content, PEM_TYPE_CERTIFICATE,
|
||||
&begin, &end);
|
||||
|
||||
if (!begin || !end)
|
||||
{
|
||||
SetLastError(SEC_E_INVALID_PARAMETER);
|
||||
FAIL("Client certificate not found in PEM file");
|
||||
}
|
||||
|
||||
cert_blob.pbData = (BYTE*)begin;
|
||||
cert_blob.cbData = (DWORD)(end - begin);
|
||||
if (!CryptQueryObject(
|
||||
CERT_QUERY_OBJECT_BLOB, &cert_blob,
|
||||
CERT_QUERY_CONTENT_FLAG_CERT,
|
||||
CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, &actual_content_type,
|
||||
NULL, NULL, NULL, (const void**)&ctx))
|
||||
{
|
||||
FAIL("Can't parse client certficate");
|
||||
}
|
||||
|
||||
/* Parse key */
|
||||
PEM_TYPE types[] = { PEM_TYPE_RSA_PRIVATE_KEY, PEM_TYPE_PRIVATE_KEY };
|
||||
for (int i = 0; i < sizeof(types) / sizeof(types[0]); i++)
|
||||
{
|
||||
pem_locate(key_file_content, types[i], &begin, &end);
|
||||
if (begin && end)
|
||||
{
|
||||
/* Assign key to certificate.*/
|
||||
status = load_private_key(ctx, begin, (end - begin), errmsg, errmsg_len);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (!begin || !end)
|
||||
{
|
||||
SetLastError(SEC_E_INVALID_PARAMETER);
|
||||
FAIL("Client private key not found in PEM");
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (status && ctx)
|
||||
{
|
||||
CertFreeCertificateContext(ctx);
|
||||
ctx = NULL;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
/* Given cert and key, as PEM file names, create a client certificate */
|
||||
CERT_CONTEXT* schannel_create_cert_context(char* cert_file, char* key_file, char* errmsg, size_t errmsg_len)
|
||||
{
|
||||
CERT_CONTEXT* ctx = NULL;
|
||||
char* key_file_content = NULL;
|
||||
char* cert_file_content = NULL;
|
||||
|
||||
cert_file_content = pem_file_to_string(cert_file, errmsg, errmsg_len);
|
||||
|
||||
if (!cert_file_content)
|
||||
goto cleanup;
|
||||
|
||||
if (cert_file == key_file)
|
||||
{
|
||||
key_file_content = cert_file_content;
|
||||
}
|
||||
else
|
||||
{
|
||||
key_file_content = pem_file_to_string(key_file, errmsg, errmsg_len);
|
||||
if (!key_file_content)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ctx = create_client_certificate_mem(cert_file_content, key_file_content, errmsg, errmsg_len);
|
||||
|
||||
cleanup:
|
||||
LocalFree(cert_file_content);
|
||||
if (cert_file != key_file)
|
||||
LocalFree(key_file_content);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/*
|
||||
Free certificate, and all resources, created by schannel_create_cert_context()
|
||||
*/
|
||||
void schannel_free_cert_context(const CERT_CONTEXT* cert)
|
||||
{
|
||||
/* release provider handle which was acquires in load_private_key() */
|
||||
CERT_KEY_CONTEXT cert_key_context = { 0 };
|
||||
cert_key_context.cbSize = sizeof(cert_key_context);
|
||||
DWORD cbData = sizeof(CERT_KEY_CONTEXT);
|
||||
HCRYPTPROV hProv = 0;
|
||||
|
||||
if (CertGetCertificateContextProperty(cert, CERT_KEY_CONTEXT_PROP_ID, &cert_key_context, &cbData))
|
||||
{
|
||||
hProv = cert_key_context.hCryptProv;
|
||||
}
|
||||
CertFreeCertificateContext(cert);
|
||||
if (hProv)
|
||||
{
|
||||
CryptReleaseContext(cert_key_context.hCryptProv, 0);
|
||||
}
|
||||
}
|
53
vendor/MDBC/libmariadb/secure/schannel_certs.h
vendored
Normal file
53
vendor/MDBC/libmariadb/secure/schannel_certs.h
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
/************************************************************************************
|
||||
Copyright (C) 2019 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
|
||||
|
||||
*************************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include <wincrypt.h>
|
||||
|
||||
extern SECURITY_STATUS schannel_create_store(
|
||||
const char* CAFile,
|
||||
const char* CAPath,
|
||||
const char* CRLFile,
|
||||
const char* CRLPath,
|
||||
HCERTSTORE* store,
|
||||
char* errmsg,
|
||||
size_t errmsg_len
|
||||
);
|
||||
|
||||
extern SECURITY_STATUS schannel_verify_server_certificate(
|
||||
const CERT_CONTEXT* cert,
|
||||
HCERTSTORE store,
|
||||
BOOL check_revocation,
|
||||
const char* server_name,
|
||||
BOOL check_server_name,
|
||||
char* errmsg,
|
||||
size_t errmsg_len);
|
||||
|
||||
extern void schannel_free_store(HCERTSTORE store);
|
||||
|
||||
extern CERT_CONTEXT* schannel_create_cert_context(
|
||||
char* cert_file,
|
||||
char* key_file,
|
||||
char* errmsg,
|
||||
size_t errmsg_len);
|
||||
|
||||
extern void schannel_free_cert_context(const CERT_CONTEXT* cert);
|
||||
|
103
vendor/MDBC/libmariadb/secure/win_crypt.c
vendored
Normal file
103
vendor/MDBC/libmariadb/secure/win_crypt.c
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
Copyright (C) 2018 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 <windows.h>
|
||||
#include <bcrypt.h>
|
||||
#include <ma_crypt.h>
|
||||
#include <malloc.h>
|
||||
|
||||
BCRYPT_ALG_HANDLE Sha256Prov= 0;
|
||||
BCRYPT_ALG_HANDLE Sha512Prov= 0;
|
||||
BCRYPT_ALG_HANDLE RsaProv= 0;
|
||||
|
||||
static LPCWSTR ma_hash_get_algorithm(unsigned int alg, BCRYPT_ALG_HANDLE *algHdl)
|
||||
{
|
||||
switch(alg)
|
||||
{
|
||||
case MA_HASH_SHA256:
|
||||
*algHdl= Sha256Prov;
|
||||
return BCRYPT_SHA256_ALGORITHM;
|
||||
case MA_HASH_SHA512:
|
||||
*algHdl= Sha512Prov;
|
||||
return BCRYPT_SHA512_ALGORITHM;
|
||||
default:
|
||||
*algHdl= 0;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
MA_HASH_CTX *ma_hash_new(unsigned int algorithm, MA_HASH_CTX *ctx)
|
||||
{
|
||||
MA_HASH_CTX *newctx= ctx;
|
||||
DWORD cbObjSize, cbData;
|
||||
LPCWSTR alg;
|
||||
BCRYPT_ALG_HANDLE algHdl= 0;
|
||||
|
||||
alg= ma_hash_get_algorithm(algorithm, &algHdl);
|
||||
|
||||
if (!alg || !algHdl)
|
||||
return NULL;
|
||||
|
||||
if (BCryptGetProperty(algHdl, BCRYPT_OBJECT_LENGTH,
|
||||
(PBYTE)&cbObjSize, sizeof(DWORD),
|
||||
&cbData, 0) < 0)
|
||||
goto error;
|
||||
|
||||
if (!newctx)
|
||||
{
|
||||
newctx= (MA_HASH_CTX *)calloc(1, sizeof(MA_HASH_CTX));
|
||||
newctx->free_me= 1;
|
||||
}
|
||||
else
|
||||
memset(newctx, 0, sizeof(MA_HASH_CTX));
|
||||
|
||||
newctx->hashObject= (PBYTE)malloc(cbObjSize);
|
||||
newctx->digest_len= (DWORD)ma_hash_digest_size(algorithm);
|
||||
BCryptCreateHash(algHdl, &newctx->hHash, newctx->hashObject, cbObjSize, NULL, 0, 0);
|
||||
|
||||
return newctx;
|
||||
error:
|
||||
if (newctx && !ctx)
|
||||
free(newctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ma_hash_free(MA_HASH_CTX *ctx)
|
||||
{
|
||||
if (ctx)
|
||||
{
|
||||
if (ctx->hHash)
|
||||
BCryptDestroyHash(ctx->hHash);
|
||||
if (ctx->hashObject)
|
||||
free(ctx->hashObject);
|
||||
if (ctx->free_me)
|
||||
free(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void ma_hash_input(MA_HASH_CTX *ctx,
|
||||
const unsigned char *buffer,
|
||||
size_t len)
|
||||
{
|
||||
BCryptHashData(ctx->hHash, (PUCHAR)buffer, (LONG)len, 0);
|
||||
}
|
||||
|
||||
void ma_hash_result(MA_HASH_CTX *ctx, unsigned char *digest)
|
||||
{
|
||||
BCryptFinishHash(ctx->hHash, digest, ctx->digest_len, 0);
|
||||
}
|
137
vendor/MDBC/libmariadb/win32_errmsg.c
vendored
Normal file
137
vendor/MDBC/libmariadb/win32_errmsg.c
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
/************************************************************************************
|
||||
Copyright (C) 2019 MariaDB
|
||||
|
||||
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 <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
Format Windows error, with optional text.
|
||||
|
||||
For "known" errors we also output their symbolic error constant, e.g
|
||||
CERT_E_CN_NO_MATCH in addition to numeric value.
|
||||
|
||||
We also try to output English text for all error messages, as not to mess up
|
||||
with encodings.
|
||||
*/
|
||||
void ma_format_win32_error(char* buf, size_t buflen, DWORD code, _Printf_format_string_ const char* fmt, ...)
|
||||
{
|
||||
char* cur = buf;
|
||||
char* end = cur + buflen;
|
||||
*cur = 0;
|
||||
if (fmt)
|
||||
{
|
||||
va_list vargs;
|
||||
va_start(vargs, fmt);
|
||||
cur += vsnprintf_s(cur, end - cur, _TRUNCATE, fmt, vargs);
|
||||
va_end(vargs);
|
||||
}
|
||||
|
||||
if (code == 0)
|
||||
return;
|
||||
|
||||
static struct map_entry
|
||||
{
|
||||
DWORD code;
|
||||
const char* sym;
|
||||
const char* msg;
|
||||
}
|
||||
map[] =
|
||||
{
|
||||
#define ENTRY(x, y) {x,#x, y}
|
||||
ENTRY(SEC_E_WRONG_PRINCIPAL, "The target principal name is incorrect"),
|
||||
ENTRY(CERT_E_CN_NO_MATCH,"The certificate's CN name does not match the passed value"),
|
||||
ENTRY(SEC_E_UNTRUSTED_ROOT,"The certificate chain was issued by an authority that is not trusted"),
|
||||
ENTRY(TRUST_E_CERT_SIGNATURE,"The signature of the certificate cannot be verified"),
|
||||
ENTRY(SEC_E_CERT_EXPIRED,"The received certificate has expired"),
|
||||
ENTRY(CERT_E_EXPIRED,"A required certificate is not within its validity period when verifying against the current system clock or the timestamp in the signed file"),
|
||||
ENTRY(CRYPT_E_NO_REVOCATION_CHECK, "The revocation function was unable to check revocation for the certificate"),
|
||||
ENTRY(CRYPT_E_REVOCATION_OFFLINE,"The revocation function was unable to check revocation because the revocation server was offline"),
|
||||
ENTRY(CRYPT_E_REVOKED,"The certificate is revoked"),
|
||||
ENTRY(SEC_E_CERT_UNKNOWN,"An unknown error occurred while processing the certificate"),
|
||||
ENTRY(CERT_E_ROLE," A certificate that can only be used as an end-entity is being used as a CA or vice versa"),
|
||||
ENTRY(CERT_E_WRONG_USAGE,"The certificate is not valid for the requested usage"),
|
||||
ENTRY(SEC_E_ILLEGAL_MESSAGE, "The message received was unexpected or badly formatted"),
|
||||
ENTRY(CERT_E_VALIDITYPERIODNESTING,"The validity periods of the certification chain do not nest correctly"),
|
||||
ENTRY(CERT_E_PATHLENCONST,"A path length constraint in the certification chain has been violated"),
|
||||
ENTRY(CERT_E_CRITICAL,"A certificate contains an unknown extension that is marked 'critical'"),
|
||||
ENTRY(CERT_E_PURPOSE,"A certificate being used for a purpose other than the ones specified by its CA"),
|
||||
ENTRY(CERT_E_ISSUERCHAINING,"A parent of a given certificate in fact did not issue that child certificate"),
|
||||
ENTRY(CERT_E_MALFORMED, "A certificate is missing or has an empty value for an important field, such as a subject or issuer name"),
|
||||
ENTRY(CERT_E_CHAINING,"A certificate chain could not be built to a trusted root authority"),
|
||||
ENTRY(TRUST_E_FAIL," Generic trust failure"),
|
||||
ENTRY(CERT_E_UNTRUSTEDTESTROOT,"The certification path terminates with the test root which is not trusted with the current policy settings"),
|
||||
ENTRY(CERT_E_UNTRUSTEDROOT,"A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider"),
|
||||
ENTRY(CERT_E_REVOCATION_FAILURE,"The revocation process could not continue - the certificate(s) could not be checked"),
|
||||
ENTRY(SEC_E_ILLEGAL_MESSAGE, "The message received was unexpected or badly formatted"),
|
||||
ENTRY(SEC_E_UNTRUSTED_ROOT, "Untrusted root certificate"),
|
||||
ENTRY(SEC_E_BUFFER_TOO_SMALL, "Buffer too small"),
|
||||
ENTRY(SEC_E_CRYPTO_SYSTEM_INVALID, "Cipher is not supported"),
|
||||
ENTRY(SEC_E_INSUFFICIENT_MEMORY, "Out of memory"),
|
||||
ENTRY(SEC_E_OUT_OF_SEQUENCE, "Invalid message sequence"),
|
||||
ENTRY(SEC_E_DECRYPT_FAILURE, "The specified data could not be decrypted"),
|
||||
ENTRY(SEC_I_INCOMPLETE_CREDENTIALS, "Incomplete credentials"),
|
||||
ENTRY(SEC_E_ENCRYPT_FAILURE, "The specified data could not be encrypted"),
|
||||
ENTRY(SEC_I_CONTEXT_EXPIRED, "The context has expired and can no longer be used"),
|
||||
ENTRY(SEC_E_ALGORITHM_MISMATCH, "no cipher match"),
|
||||
ENTRY(SEC_E_NO_CREDENTIALS, "no credentials"),
|
||||
ENTRY(SEC_E_INVALID_TOKEN, "The token supplied to function is invalid"),
|
||||
ENTRY(SEC_E_UNSUPPORTED_FUNCTION,"The function requested is not supported")
|
||||
};
|
||||
|
||||
struct map_entry* entry = NULL;
|
||||
|
||||
if (cur > buf && cur[-1] != ' ' && cur[-1] != '.')
|
||||
{
|
||||
strncpy_s(cur,end-cur, ". ", _TRUNCATE);
|
||||
cur += 2;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sizeof(map) / sizeof(map[0]); i++)
|
||||
{
|
||||
if (code == map[i].code)
|
||||
{
|
||||
entry = &map[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cur > end - 20)
|
||||
return;
|
||||
if (entry)
|
||||
{
|
||||
sprintf_s(cur, end - cur, "%s. Error 0x%08lX(%s)", entry->msg, code, entry->sym);
|
||||
}
|
||||
else
|
||||
{
|
||||
cur += FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, code, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
|
||||
cur, (DWORD)(end - cur), NULL);
|
||||
while (cur > buf && (*cur == '\0' || *cur == '\n' || *cur == '\r' || *cur == '.'))
|
||||
cur--;
|
||||
if (*cur)
|
||||
{
|
||||
cur++;
|
||||
*cur = 0;
|
||||
}
|
||||
sprintf_s(cur, end - cur, ". Error %lu/0x%08lX", code, code);
|
||||
}
|
||||
}
|
||||
|
2
vendor/MDBC/libmariadb/win32_errmsg.h
vendored
Normal file
2
vendor/MDBC/libmariadb/win32_errmsg.h
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
#include <windows.h>
|
||||
void ma_format_win32_error(char* buf, size_t buflen, DWORD code, _Printf_format_string_ const char* fmt, ...);
|
Reference in New Issue
Block a user