diff options
Diffstat (limited to 'libmysql')
-rw-r--r-- | libmysql/.cvsignore | 10 | ||||
-rw-r--r--[-rwxr-xr-x] | libmysql/CMakeLists.txt | 334 | ||||
-rw-r--r-- | libmysql/Makefile.am | 116 | ||||
-rw-r--r-- | libmysql/Makefile.shared | 118 | ||||
-rw-r--r-- | libmysql/acinclude.m4 | 91 | ||||
-rw-r--r-- | libmysql/authentication_win/CMakeLists.txt | 33 | ||||
-rw-r--r-- | libmysql/authentication_win/common.cc | 492 | ||||
-rw-r--r-- | libmysql/authentication_win/common.h | 324 | ||||
-rw-r--r-- | libmysql/authentication_win/handshake.cc | 289 | ||||
-rw-r--r-- | libmysql/authentication_win/handshake.h | 181 | ||||
-rw-r--r-- | libmysql/authentication_win/handshake_client.cc | 378 | ||||
-rw-r--r-- | libmysql/authentication_win/log_client.cc | 55 | ||||
-rw-r--r-- | libmysql/authentication_win/plugin_client.cc | 68 | ||||
-rw-r--r-- | libmysql/client_settings.h | 38 | ||||
-rw-r--r-- | libmysql/dll.c | 124 | ||||
-rw-r--r-- | libmysql/errmsg.c | 146 | ||||
-rw-r--r-- | libmysql/get_password.c | 18 | ||||
-rw-r--r-- | libmysql/libmysql.c | 816 | ||||
-rw-r--r-- | libmysql/libmysql.def | 47 | ||||
-rw-r--r-- | libmysql/manager.c | 269 |
20 files changed, 2281 insertions, 1666 deletions
diff --git a/libmysql/.cvsignore b/libmysql/.cvsignore deleted file mode 100644 index a58235fee47..00000000000 --- a/libmysql/.cvsignore +++ /dev/null @@ -1,10 +0,0 @@ -*.lo -.deps -.libs -Makefile -Makefile.in -conf_to_src -config.status -configure -ctype_extra_sources.c -libmysqlclient.la diff --git a/libmysql/CMakeLists.txt b/libmysql/CMakeLists.txt index 55138e4aa06..d0e383c6640 100755..100644 --- a/libmysql/CMakeLists.txt +++ b/libmysql/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2006 MySQL AB +# Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -11,121 +11,225 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -INCLUDE("${PROJECT_SOURCE_DIR}/win/mysql_manifest.cmake") +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +INCLUDE_DIRECTORIES( + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/libmysql + ${CMAKE_SOURCE_DIR}/regex + ${CMAKE_SOURCE_DIR}/sql + ${CMAKE_SOURCE_DIR}/strings + ${SSL_INCLUDE_DIRS} + ${SSL_INTERNAL_INCLUDE_DIRS} + ${ZLIB_INCLUDE_DIR}) +ADD_DEFINITIONS(${SSL_DEFINES}) + +SET(CLIENT_API_FUNCTIONS +load_defaults +mysql_thread_end +mysql_thread_init +myodbc_remove_escape +mysql_affected_rows +mysql_autocommit +mysql_stmt_bind_param +mysql_stmt_bind_result +mysql_change_user +mysql_character_set_name +mysql_close +mysql_commit +mysql_data_seek +mysql_debug +mysql_dump_debug_info +mysql_eof +mysql_errno +mysql_error +mysql_escape_string +mysql_hex_string +mysql_stmt_execute +mysql_stmt_fetch +mysql_stmt_fetch_column +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_client_info +mysql_get_host_info +mysql_get_proto_info +mysql_get_server_info +mysql_get_client_version +mysql_get_ssl_cipher +mysql_info +mysql_init +mysql_insert_id +mysql_kill +mysql_set_server_option +mysql_list_dbs +mysql_list_fields +mysql_list_processes +mysql_list_tables +mysql_more_results +mysql_next_result +mysql_num_fields +mysql_num_rows +mysql_options +mysql_stmt_param_count +mysql_stmt_param_metadata +mysql_ping +mysql_stmt_result_metadata +mysql_query +mysql_read_query_result +mysql_real_connect +mysql_real_escape_string +mysql_real_query +mysql_refresh +mysql_rollback +mysql_row_seek +mysql_row_tell +mysql_select_db +mysql_stmt_send_long_data +mysql_send_query +mysql_shutdown +mysql_ssl_set +mysql_stat +mysql_stmt_affected_rows +mysql_stmt_close +mysql_stmt_reset +mysql_stmt_data_seek +mysql_stmt_errno +mysql_stmt_error +mysql_stmt_free_result +mysql_stmt_num_rows +mysql_stmt_row_seek +mysql_stmt_row_tell +mysql_stmt_store_result +mysql_store_result +mysql_thread_id +mysql_thread_safe +mysql_use_result +mysql_warning_count +mysql_stmt_sqlstate +mysql_sqlstate +mysql_get_server_version +mysql_stmt_prepare +mysql_stmt_init +mysql_stmt_insert_id +mysql_stmt_attr_get +mysql_stmt_attr_set +mysql_stmt_field_count +mysql_set_local_infile_default +mysql_set_local_infile_handler +mysql_embedded +mysql_server_init +mysql_server_end +mysql_set_character_set +mysql_get_character_set_info +mysql_stmt_next_result + +CACHE INTERNAL "Functions exported by client API" + +) -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSAFEMALLOC -DSAFE_MUTEX") - -# Note that we don't link with the libraries "strings" or "mysys" -# here, instead we recompile the files needed and include them -# directly. This means we don't have to worry here about if these -# libraries are compiled defining USE_TLS or not. Not that it *should* -# have been a problem anyway, they don't use thread local storage. - -INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include - ${CMAKE_SOURCE_DIR}/libmysql - ${CMAKE_SOURCE_DIR}/regex - ${CMAKE_SOURCE_DIR}/sql - ${CMAKE_SOURCE_DIR}/strings) - -# We include the source file listing instead of referencing the -# libraries. At least with CMake 2.4 and Visual Studio 2005 a static -# library created from other static libraries would not be complete, -# i.e. the libraries listed in TARGET_LINK_LIBRARIES() were just -# ignored. - - -# Include and add the directory path -SET(SOURCE_SUBLIBS TRUE) -SET(LIB_SOURCES "") - -INCLUDE(${CMAKE_SOURCE_DIR}/zlib/CMakeLists.txt) -FOREACH(rpath ${ZLIB_SOURCES}) - SET(LIB_SOURCES ${LIB_SOURCES} ../zlib/${rpath}) -ENDFOREACH(rpath) - -# FIXME only needed if build type is "Debug", but CMAKE_BUILD_TYPE is -# not set during configure time. -INCLUDE(${CMAKE_SOURCE_DIR}/dbug/CMakeLists.txt) -FOREACH(rpath ${DBUG_SOURCES}) - SET(LIB_SOURCES ${LIB_SOURCES} ../dbug/${rpath}) -ENDFOREACH(rpath) - -INCLUDE(${CMAKE_SOURCE_DIR}/extra/yassl/taocrypt/CMakeLists.txt) -FOREACH(rpath ${TAOCRYPT_SOURCES}) - SET(LIB_SOURCES ${LIB_SOURCES} ../extra/yassl/taocrypt/${rpath}) -ENDFOREACH(rpath) - -INCLUDE(${CMAKE_SOURCE_DIR}/extra/yassl/CMakeLists.txt) -FOREACH(rpath ${YASSL_SOURCES}) - SET(LIB_SOURCES ${LIB_SOURCES} ../extra/yassl/${rpath}) -ENDFOREACH(rpath) - -SET(CLIENT_SOURCES ../mysys/array.c ../strings/bchange.c ../strings/bmove.c - ../strings/bmove_upp.c ../mysys/charset-def.c ../mysys/charset.c - ../sql-common/client.c ../strings/ctype-big5.c ../strings/ctype-bin.c - ../strings/ctype-cp932.c ../strings/ctype-czech.c ../strings/ctype-euc_kr.c - ../strings/ctype-eucjpms.c ../strings/ctype-extra.c ../strings/ctype-gb2312.c - ../strings/ctype-gbk.c ../strings/ctype-latin1.c ../strings/ctype-mb.c - ../strings/ctype-simple.c ../strings/ctype-sjis.c ../strings/ctype-tis620.c - ../strings/ctype-uca.c ../strings/ctype-ucs2.c ../strings/ctype-ujis.c - ../strings/ctype-utf8.c ../strings/ctype-win1250ch.c ../strings/ctype.c - ../mysys/default.c errmsg.c ../mysys/errors.c - ../mysys/hash.c ../mysys/my_sleep.c ../mysys/default_modify.c - get_password.c ../strings/int2str.c ../strings/is_prefix.c - libmysql.c ../mysys/list.c ../strings/llstr.c - ../strings/longlong2str.c manager.c ../mysys/mf_arr_appstr.c ../mysys/mf_cache.c - ../mysys/mf_dirname.c ../mysys/mf_fn_ext.c ../mysys/mf_format.c - ../mysys/mf_iocache.c ../mysys/mf_iocache2.c ../mysys/mf_loadpath.c - ../mysys/mf_pack.c ../mysys/mf_path.c ../mysys/mf_tempfile.c ../mysys/mf_unixpath.c - ../mysys/mf_wcomp.c ../mysys/mulalloc.c ../mysys/my_access.c ../mysys/my_alloc.c - ../mysys/my_chsize.c ../mysys/my_compress.c ../mysys/my_create.c - ../mysys/my_delete.c ../mysys/my_div.c ../mysys/my_error.c ../mysys/my_file.c - ../mysys/my_fopen.c ../mysys/my_fstream.c ../mysys/my_gethostbyname.c - ../mysys/my_getopt.c ../mysys/my_getwd.c ../mysys/my_init.c ../mysys/my_lib.c - ../mysys/my_malloc.c ../mysys/my_messnc.c ../mysys/my_net.c ../mysys/my_once.c - ../mysys/my_open.c ../mysys/my_pread.c ../mysys/my_pthread.c ../mysys/my_read.c - ../mysys/my_realloc.c ../mysys/my_rename.c ../mysys/my_seek.c - ../mysys/my_static.c ../strings/my_strtoll10.c ../mysys/my_symlink.c - ../mysys/my_symlink2.c ../mysys/my_thr_init.c ../sql-common/my_time.c - ../strings/my_vsnprintf.c ../mysys/my_wincond.c ../mysys/my_winthread.c - ../mysys/my_write.c ../sql/net_serv.cc ../sql-common/pack.c ../sql/password.c - ../mysys/safemalloc.c ../mysys/sha1.c ../strings/str2int.c - ../strings/str_alloc.c ../strings/strcend.c ../strings/strcont.c ../strings/strend.c - ../strings/strfill.c ../mysys/string.c ../strings/strinstr.c ../strings/strmake.c - ../strings/strmov.c ../strings/strnlen.c ../strings/strnmov.c ../strings/strtod.c - ../strings/strtoll.c ../strings/strtoull.c ../strings/strxmov.c ../strings/strxnmov.c - ../mysys/thr_mutex.c ../mysys/typelib.c ../vio/vio.c ../vio/viosocket.c - ../vio/viossl.c ../vio/viosslfactories.c ../strings/xml.c ../mysys/mf_qsort.c - ../mysys/my_getsystime.c ../mysys/my_sync.c ${LIB_SOURCES}) - -# Need to set USE_TLS for building the DLL, since __declspec(thread) -# approach to thread local storage does not work properly in DLLs. -# -# The static library might be used to form another DLL, as is the case -# with the ODBC driver, so it has to be compiled with USE_TLS as well. -# -# We create a third library without USE_TLS for internal use. We can't -# be sure that some client application part of this build doesn't go -# beond the documented API, and try access the Thread Local Storage. -# The "_notls" means no Tls*() functions used, i.e. "static" TLS. - -ADD_LIBRARY(mysqlclient STATIC ${CLIENT_SOURCES}) -ADD_DEPENDENCIES(mysqlclient GenError) -TARGET_LINK_LIBRARIES(mysqlclient) - -ADD_LIBRARY(mysqlclient_notls STATIC ${CLIENT_SOURCES}) -ADD_DEPENDENCIES(mysqlclient_notls GenError) -TARGET_LINK_LIBRARIES(mysqlclient_notls) - -ADD_LIBRARY(libmysql SHARED ${CLIENT_SOURCES} dll.c libmysql.def) IF(WIN32) - SET_TARGET_PROPERTIES(libmysql mysqlclient PROPERTIES COMPILE_FLAGS "-DUSE_TLS") + ADD_SUBDIRECTORY(authentication_win) + SET(WITH_AUTHENTICATION_WIN 1) + ADD_DEFINITIONS(-DAUTHENTICATION_WIN) ENDIF(WIN32) -ADD_DEPENDENCIES(libmysql GenError) -TARGET_LINK_LIBRARIES(libmysql wsock32) -IF(EMBED_MANIFESTS) - MYSQL_EMBED_MANIFEST("myTest" "asInvoker") -ENDIF(EMBED_MANIFESTS) +SET(CLIENT_SOURCES + get_password.c + libmysql.c + errmsg.c + ../sql-common/client.c + ../sql-common/my_time.c + ../sql-common/client_plugin.c + ../sql/net_serv.cc + ../sql-common/pack.c + ../sql/password.c +) +ADD_CONVENIENCE_LIBRARY(clientlib ${CLIENT_SOURCES}) +DTRACE_INSTRUMENT(clientlib) +ADD_DEPENDENCIES(clientlib GenError) + +SET(LIBS clientlib dbug strings vio mysys ${ZLIB_LIBRARY} ${SSL_LIBRARIES} ${LIBDL}) + +IF(WITH_AUTHENTICATION_WIN) + LIST(APPEND LIBS auth_win_client) +ENDIF(WITH_AUTHENTICATION_WIN) + +# Merge several convenience libraries into one big mysqlclient +# and link them together into shared library. +MERGE_LIBRARIES(mysqlclient STATIC ${LIBS} COMPONENT Development) + +# Visual Studio users need debug static library for debug projects +IF(MSVC) + INSTALL_DEBUG_TARGET(mysqlclient DESTINATION ${INSTALL_LIBDIR}/debug) +ENDIF() + +IF(UNIX) + MACRO(GET_VERSIONED_LIBNAME LIBNAME EXTENSION VERSION OUTNAME) + SET(DOT_VERSION ".${VERSION}") + IF(DOT_VERSION STREQUAL ".") + SET(DOT_VERSION "") + ENDIF() + IF(APPLE) + SET(${OUTNAME} ${LIBNAME}${DOT_VERSION}${EXTENSION}) + ELSE() + SET(${OUTNAME} ${LIBNAME}${EXTENSION}${DOT_VERSION}) + ENDIF() + ENDMACRO() + INSTALL_SYMLINK(${CMAKE_STATIC_LIBRARY_PREFIX}mysqlclient_r.a mysqlclient ${INSTALL_LIBDIR} Development) +ENDIF() + +IF(NOT DISABLE_SHARED) + MERGE_LIBRARIES(libmysql SHARED ${LIBS} EXPORTS ${CLIENT_API_FUNCTIONS} COMPONENT SharedLibraries) + IF(UNIX) + # libtool compatability + IF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR APPLE) + SET(OS_SHARED_LIB_VERSION "${SHARED_LIB_MAJOR_VERSION}") + ELSEIF(CMAKE_SYSTEM_NAME MATCHES "HP-UX") + SET(OS_SHARED_LIB_VERSION "${SHARED_LIB_MAJOR_VERSION}.0") + ELSE() + SET(OS_SHARED_LIB_VERSION "${SHARED_LIB_MAJOR_VERSION}.0.0") + ENDIF() + # Name of shared library is mysqlclient on Unix + SET_TARGET_PROPERTIES(libmysql PROPERTIES + OUTPUT_NAME mysqlclient + VERSION "${OS_SHARED_LIB_VERSION}" + SOVERSION "${SHARED_LIB_MAJOR_VERSION}") + IF(LINK_FLAG_NO_UNDEFINED) + GET_TARGET_PROPERTY(libmysql_link_flags libmysql LINK_FLAGS) + IF(NOT libmysql_link_flag) + SET(libmysql_link_flags) + ENDIF() + SET_TARGET_PROPERTIES(libmysql PROPERTIES LINK_FLAGS + "${libmysql_link_flags} ${LINK_FLAG_NO_UNDEFINED}") + ENDIF() + # clean direct output needs to be set several targets have the same name + #(mysqlclient in this case) + SET_TARGET_PROPERTIES(mysqlclient PROPERTIES CLEAN_DIRECT_OUTPUT 1) + SET_TARGET_PROPERTIES(libmysql PROPERTIES CLEAN_DIRECT_OUTPUT 1) + + # Install links to libmysqlclient.so (client_r) + GET_VERSIONED_LIBNAME( + "${CMAKE_SHARED_LIBRARY_PREFIX}mysqlclient_r" + "${CMAKE_SHARED_LIBRARY_SUFFIX}" + "" + linkname) + INSTALL_SYMLINK(${linkname} libmysql ${INSTALL_LIBDIR} SharedLibraries) + SET(OS_SHARED_LIB_SYMLINKS "${SHARED_LIB_MAJOR_VERSION}" "${OS_SHARED_LIB_VERSION}") + LIST(REMOVE_DUPLICATES OS_SHARED_LIB_SYMLINKS) + FOREACH(ver ${OS_SHARED_LIB_SYMLINKS}) + GET_VERSIONED_LIBNAME( + "${CMAKE_SHARED_LIBRARY_PREFIX}mysqlclient_r" + "${CMAKE_SHARED_LIBRARY_SUFFIX}" + "${ver}" + linkname) + INSTALL_SYMLINK(${linkname} libmysql ${INSTALL_LIBDIR} SharedLibraries) + ENDFOREACH() + ENDIF() +ENDIF() diff --git a/libmysql/Makefile.am b/libmysql/Makefile.am deleted file mode 100644 index 2453547cac8..00000000000 --- a/libmysql/Makefile.am +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (c) 2000-2007 MySQL AB, 2009 Sun Microsystems, Inc. -# Use is subject to license terms. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of version 2 GNU General Public License as -# published by the Free Software Foundation. -# -# There are special exceptions to the terms and conditions of the GPL as it -# is applied to this software. -# -# 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 St, Fifth Floor, Boston, -# MA 02110-1301, USA -# -# This file is public domain and comes with NO WARRANTY of any kind - -target = libmysqlclient.la -target_defs = -DMYSQL_CLIENT_NO_THREADS -DDONT_USE_RAID @LIB_EXTRA_CCFLAGS@ -LIBS = @CLIENT_LIBS@ -INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ - $(openssl_includes) @ZLIB_INCLUDES@ - -include $(srcdir)/Makefile.shared - -libmysqlclient_la_SOURCES = $(target_sources) -libmysqlclient_la_LIBADD = $(target_libadd) $(yassl_las) -libmysqlclient_la_LDFLAGS = $(target_ldflags) -EXTRA_DIST = Makefile.shared libmysql.def dll.c CMakeLists.txt -noinst_HEADERS = client_settings.h - -link_sources: - set -x; \ - ss=`echo $(mystringsobjects) | sed "s;\.lo;.c;g"`; \ - ds=`echo $(dbugobjects) | sed "s;\.lo;.c;g"`; \ - ms=`echo $(mysysobjects) | sed "s;\.lo;.c;g"`; \ - vs=`echo $(vio_objects) | sed "s;\.lo;.c;g"`; \ - scs=`echo $(sql_cmn_objects) | sed "s;\.lo;.c;g"`; \ - for f in $$ss; do \ - rm -f $$f; \ - @LN_CP_F@ $(top_srcdir)/strings/$$f $$f; \ - done; \ - for f in $$vs $(vioheaders); do \ - rm -f $$f; \ - @LN_CP_F@ $(top_srcdir)/vio/$$f $$f; \ - done; \ - for f in $$scs; do \ - rm -f $$f; \ - @LN_CP_F@ $(top_srcdir)/sql-common/$$f $$f; \ - done; \ - for f in $(mystringsextra); do \ - rm -f $$f; \ - @LN_CP_F@ $(top_srcdir)/strings/$$f $$f; \ - done; \ - for f in $$ds; do \ - rm -f $$f; \ - @LN_CP_F@ $(top_srcdir)/dbug/$$f $$f; \ - done; \ - for f in $$ms $(mysysheaders); do \ - rm -f $$f; \ - @LN_CP_F@ $(top_srcdir)/mysys/$$f $$f; \ - done; \ - rm -f net.c; \ - @LN_CP_F@ $(top_srcdir)/sql/net_serv.cc net.c ; \ - rm -f password.c; \ - @LN_CP_F@ $(top_srcdir)/sql/password.c password.c - echo timestamp > link_sources - -# This part requires GNUmake -# -# This makes a distribution file with only the files needed to compile -# a minimal MySQL client library -# -# For a really minimal distribution (without debugging code) we could -# keep only the stubs for safemalloc.c and debug.c -# -# A list of needed headers collected from the deps information 000213 -nh = my_global.h config-win32.h dbug.h errmsg.h \ - m_ctype.h m_string.h \ - my_alarm.h my_config.h my_dir.h my_list.h my_net.h my_sys.h \ - mysql.h mysql_com.h mysql_version.h mysqld_error.h \ - mysys_err.h my_pthread.h thr_alarm.h violite.h hash.h \ - sql_common.h ../libmysql/client_settings.h -# Get a list of the needed objects -lobjs = $(mysysobjects1) $(dbugobjects) $(mystringsobjects) $(sqlobjects) - -do-lib-dist: - dir=libmysql-$(MYSQL_NO_DASH_VERSION); \ - srcs1=`echo $(lobjs) | sed "s;\.lo;.c;g"`; \ - srcs2=$(target_sources); \ - srcs="$$srcs1 $$srcs2"; \ - objs1=`echo $(lobjs) | sed "s;\.lo;.o;g"`; \ - objs2=`echo $(target_sources) | sed "s;\.c;.o;g"`; \ - objs="$$objs1 $$objs2"; \ - rm -rf $$dir; \ - mkdir $$dir; \ - $(INSTALL_DATA) $$srcs $(mysysheaders) $$dir; \ - for i in $(nh); do $(INSTALL_DATA) ../include/$$i $$dir; done; \ - echo "# A very minimal Makefile to compile" > $$dir/Makefile; \ - echo "# the minimized libmysql library" >> $$dir/Makefile; \ - echo "# This file is autogenerated from Makefile.am" >> $$dir/Makefile; \ - echo 'CFLAGS= -I. -DMYSQL_CLIENT_NO_THREADS' >>$$dir/Makefile; \ - echo "obj=$$objs" >>$$dir/Makefile; \ - echo 'all: libmysql.a' >>$$dir/Makefile; \ - echo 'libmysql.a: $$(obj)' >>$$dir/Makefile; \ - echo ' $$(AR) r $$@ $$?' >>$$dir/Makefile; \ - gtar cvzf $$dir.tar.gz $$dir; \ - cd $$dir; gmake - -# Don't update the files from bitkeeper -%::SCCS/s.% diff --git a/libmysql/Makefile.shared b/libmysql/Makefile.shared deleted file mode 100644 index d62c62fc91c..00000000000 --- a/libmysql/Makefile.shared +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of version 2 GNU General Public License as -# published by the Free Software Foundation. -# -# There are special exceptions to the terms and conditions of the GPL as it -# is applied to this software. -# -# 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 St, Fifth Floor, Boston, -# MA 02110-1301, USA -# -# This file is public domain and comes with NO WARRANTY of any kind - -MYSQLDATAdir = $(localstatedir) -MYSQLSHAREdir = $(pkgdatadir) -MYSQLBASEdir= $(prefix) -## We'll use CLIENT_EXTRA_LDFLAGS for threaded and non-threaded -## until someone complains that they need separate options. -LDADD = @CLIENT_EXTRA_LDFLAGS@ $(target) -pkglib_LTLIBRARIES = $(target) - -noinst_PROGRAMS = conf_to_src - - -target_sources = libmysql.c password.c manager.c \ - get_password.c errmsg.c - -mystringsobjects = strmov.lo strxmov.lo strxnmov.lo strnmov.lo \ - strmake.lo strend.lo strtod.lo \ - strnlen.lo strfill.lo is_prefix.lo \ - int2str.lo str2int.lo strinstr.lo strcont.lo \ - strcend.lo ctype-latin1.lo \ - bchange.lo bmove.lo bmove_upp.lo longlong2str.lo \ - strtoull.lo strtoll.lo llstr.lo my_vsnprintf.lo \ - ctype.lo ctype-simple.lo ctype-bin.lo ctype-mb.lo \ - ctype-big5.lo ctype-czech.lo ctype-cp932.lo ctype-eucjpms.lo ctype-euc_kr.lo \ - ctype-win1250ch.lo ctype-utf8.lo ctype-extra.lo \ - ctype-ucs2.lo ctype-gb2312.lo ctype-gbk.lo \ - ctype-sjis.lo ctype-tis620.lo ctype-ujis.lo \ - ctype-uca.lo xml.lo my_strtoll10.lo str_alloc.lo - -mystringsextra= strto.c -dbugobjects = dbug.lo # IT IS IN SAFEMALLOC.C sanity.lo -mysysheaders = mysys_priv.h my_static.h -vioheaders = vio_priv.h -mysysobjects1 = my_init.lo my_static.lo my_malloc.lo my_realloc.lo \ - my_create.lo my_delete.lo mf_tempfile.lo my_open.lo \ - my_file.lo my_read.lo my_write.lo errors.lo \ - my_error.lo my_getwd.lo my_div.lo \ - mf_pack.lo my_messnc.lo mf_dirname.lo mf_fn_ext.lo\ - mf_wcomp.lo typelib.lo safemalloc.lo my_alloc.lo \ - mf_format.lo mf_path.lo mf_unixpath.lo my_fopen.lo \ - my_symlink.lo my_fstream.lo mf_arr_appstr.lo \ - mf_loadpath.lo my_pthread.lo my_thr_init.lo \ - thr_mutex.lo mulalloc.lo string.lo \ - default.lo default_modify.lo \ - my_compress.lo array.lo my_once.lo list.lo my_net.lo \ - charset.lo charset-def.lo hash.lo mf_iocache.lo \ - mf_iocache2.lo my_seek.lo my_sleep.lo \ - my_pread.lo mf_cache.lo md5.lo sha1.lo \ - my_getopt.lo my_gethostbyname.lo my_port.lo \ - my_rename.lo my_chsize.lo my_sync.lo my_getsystime.lo -sqlobjects = net.lo -sql_cmn_objects = pack.lo client.lo my_time.lo - -# Not needed in the minimum library -mysysobjects2 = my_lib.lo mf_qsort.lo -mysysobjects = $(mysysobjects1) $(mysysobjects2) -target_libadd = $(mysysobjects) $(mystringsobjects) $(dbugobjects) \ - $(sql_cmn_objects) $(vio_objects) $(sqlobjects) -target_ldflags = -version-info @SHARED_LIB_VERSION@ @LD_VERSION_SCRIPT@ -vio_objects= vio.lo viosocket.lo viossl.lo viosslfactories.lo - -BUILT_SOURCES = link_sources - -CLEANFILES = $(target_libadd) $(SHLIBOBJS) \ - $(target) $(BUILT_SOURCES) -DEFS = -DDEFAULT_CHARSET_HOME="\"$(MYSQLBASEdir)\"" \ - -DMYSQL_DATADIR="\"$(MYSQLDATAdir)\"" \ - -DDEFAULT_HOME_ENV=MYSQL_HOME \ - -DDEFAULT_GROUP_SUFFIX_ENV=MYSQL_GROUP_SUFFIX \ - -DDEFAULT_SYSCONFDIR="\"$(sysconfdir)\"" \ - -DSHAREDIR="\"$(MYSQLSHAREdir)\"" $(target_defs) - -if HAVE_YASSL -yassl_las = $(top_builddir)/extra/yassl/src/libyassl.la \ - $(top_builddir)/extra/yassl/taocrypt/src/libtaocrypt.la -endif - -# The automatic dependencies miss this -#bmove_upp.lo: $(LTCHARSET_OBJS) - -clean-local: - rm -f `echo $(mystringsobjects) | sed "s;\.lo;.c;g"` \ - `echo $(dbugobjects) | sed "s;\.lo;.c;g"` \ - `echo $(mysysobjects) | sed "s;\.lo;.c;g"` \ - `echo $(vio_objects) | sed "s;\.lo;.c;g"` \ - `echo $(sql_cmn_objects) | sed "s;\.lo;.c;g"` \ - $(CHARSET_SRCS) $(CHARSET_OBJS) \ - $(mystringsextra) $(mysysheaders) $(vioheaders) \ - net.c - -conf_to_src_SOURCES = conf_to_src.c -conf_to_src_LDADD= -#force static linking of conf_to_src - essential when linking against -#custom installation of libc -conf_to_src_LDFLAGS=@NOINST_LDFLAGS@ - -# Don't update the files from bitkeeper -%::SCCS/s.% diff --git a/libmysql/acinclude.m4 b/libmysql/acinclude.m4 deleted file mode 100644 index c0211f3759f..00000000000 --- a/libmysql/acinclude.m4 +++ /dev/null @@ -1,91 +0,0 @@ -# Local macros for automake & autoconf - -AC_DEFUN(MYSQL_TYPE_ACCEPT, -[ac_save_CXXFLAGS="$CXXFLAGS" -AC_CACHE_CHECK([base type of last arg to accept], mysql_cv_btype_last_arg_accept, -AC_LANG_SAVE -AC_LANG_CPLUSPLUS -if test "$ac_cv_prog_gxx" = "yes" -then - CXXFLAGS="$CXXFLAGS -Werror" -fi -mysql_cv_btype_last_arg_accept=none -[AC_TRY_COMPILE([#include <stdlib.h> -#include <sys/types.h> -#include <sys/socket.h> -], -[int a = accept(1, (struct sockaddr *) 0, (socklen_t *) 0);], -mysql_cv_btype_last_arg_accept=socklen_t)] -if test $mysql_cv_btype_last_arg_accept = none; then -[AC_TRY_COMPILE([#include <stdlib.h> -#include <sys/types.h> -#include <sys/socket.h> -], -[int a = accept(1, (struct sockaddr *) 0, (size_t *) 0);], -mysql_cv_btype_last_arg_accept=size_t)] -fi -if test $mysql_cv_btype_last_arg_accept = none; then -mysql_cv_btype_last_arg_accept=int -fi) -AC_LANG_RESTORE -AC_DEFINE_UNQUOTED(SOCKET_SIZE_TYPE, $mysql_cv_btype_last_arg_accept) -CXXFLAGS="$ac_save_CXXFLAGS" -]) - - -#---START: Used in for client configure -AC_DEFUN(MYSQL_CHECK_ULONG, -[AC_MSG_CHECKING(for type ulong) -AC_CACHE_VAL(ac_cv_ulong, -[AC_TRY_RUN([#include <stdio.h> -#include <sys/types.h> -main() -{ - ulong foo; - foo++; - exit(0); -}], ac_cv_ulong=yes, ac_cv_ulong=no, ac_cv_ulong=no)]) -AC_MSG_RESULT($ac_cv_ulong) -if test "$ac_cv_ulong" = "yes" -then - AC_DEFINE(HAVE_ULONG) -fi -]) - -AC_DEFUN(MYSQL_CHECK_UCHAR, -[AC_MSG_CHECKING(for type uchar) -AC_CACHE_VAL(ac_cv_uchar, -[AC_TRY_RUN([#include <stdio.h> -#include <sys/types.h> -main() -{ - uchar foo; - foo++; - exit(0); -}], ac_cv_uchar=yes, ac_cv_uchar=no, ac_cv_uchar=no)]) -AC_MSG_RESULT($ac_cv_uchar) -if test "$ac_cv_uchar" = "yes" -then - AC_DEFINE(HAVE_UCHAR) -fi -]) - -AC_DEFUN(MYSQL_CHECK_UINT, -[AC_MSG_CHECKING(for type uint) -AC_CACHE_VAL(ac_cv_uint, -[AC_TRY_RUN([#include <stdio.h> -#include <sys/types.h> -main() -{ - uint foo; - foo++; - exit(0); -}], ac_cv_uint=yes, ac_cv_uint=no, ac_cv_uint=no)]) -AC_MSG_RESULT($ac_cv_uint) -if test "$ac_cv_uint" = "yes" -then - AC_DEFINE(HAVE_UINT) -fi -]) - -#---END: diff --git a/libmysql/authentication_win/CMakeLists.txt b/libmysql/authentication_win/CMakeLists.txt new file mode 100644 index 00000000000..80cd14780e6 --- /dev/null +++ b/libmysql/authentication_win/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# +# Configuration for building Windows Authentication Plugin (client-side) +# + +ADD_DEFINITIONS(-DSECURITY_WIN32) +ADD_DEFINITIONS(-DDEBUG_ERRROR_LOG) # no error logging in production builds +ADD_DEFINITIONS(-DWINAUTH_USE_DBUG_LIB) # it is OK to use dbug library in statically + # linked plugin + +SET(HEADERS common.h handshake.h) +SET(PLUGIN_SOURCES plugin_client.cc handshake_client.cc log_client.cc common.cc handshake.cc) + +ADD_CONVENIENCE_LIBRARY(auth_win_client ${PLUGIN_SOURCES} ${HEADERS}) +TARGET_LINK_LIBRARIES(auth_win_client Secur32) + +# In IDE, group headers in a separate folder. + +SOURCE_GROUP(Headers REGULAR_EXPRESSION ".*h$") diff --git a/libmysql/authentication_win/common.cc b/libmysql/authentication_win/common.cc new file mode 100644 index 00000000000..1d1f2938969 --- /dev/null +++ b/libmysql/authentication_win/common.cc @@ -0,0 +1,492 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "common.h" +#include <sddl.h> // for ConvertSidToStringSid() +#include <secext.h> // for GetUserNameEx() + + +template <> void error_log_print<error_log_level::INFO>(const char *fmt, ...); +template <> void error_log_print<error_log_level::WARNING>(const char *fmt, ...); +template <> void error_log_print<error_log_level::ERROR>(const char *fmt, ...); + + +/** Connection class **************************************************/ + +/** + Create connection out of an active MYSQL_PLUGIN_VIO object. + + @param[in] vio pointer to a @c MYSQL_PLUGIN_VIO object used for + connection - it can not be NULL +*/ + +Connection::Connection(MYSQL_PLUGIN_VIO *vio): m_vio(vio), m_error(0) +{ + DBUG_ASSERT(vio); +} + + +/** + Write data to the connection. + + @param[in] blob data to be written + + @return 0 on success, VIO error code on failure. + + @note In case of error, VIO error code is stored in the connection object + and can be obtained with @c error() method. +*/ + +int Connection::write(const Blob &blob) +{ + m_error= m_vio->write_packet(m_vio, blob.ptr(), blob.len()); + +#ifndef DBUG_OFF + if (m_error) + DBUG_PRINT("error", ("vio write error %d", m_error)); +#endif + + return m_error; +} + + +/** + Read data from connection. + + @return A Blob containing read packet or null Blob in case of error. + + @note In case of error, VIO error code is stored in the connection object + and can be obtained with @c error() method. +*/ + +Blob Connection::read() +{ + unsigned char *ptr; + int len= m_vio->read_packet(m_vio, &ptr); + + if (len < 0) + { + m_error= true; + return Blob(); + } + + return Blob(ptr, len); +} + + +/** Sid class *****************************************************/ + + +/** + Create Sid object corresponding to a given account name. + + @param[in] account_name name of a Windows account + + The account name can be in any form accepted by @c LookupAccountName() + function. + + @note In case of errors created object is invalid and its @c is_valid() + method returns @c false. +*/ + +Sid::Sid(const wchar_t *account_name): m_data(NULL) +#ifndef DBUG_OFF +, m_as_string(NULL) +#endif +{ + DWORD sid_size= 0, domain_size= 0; + bool success; + + // Determine required buffer sizes + + success= LookupAccountNameW(NULL, account_name, NULL, &sid_size, + NULL, &domain_size, &m_type); + + if (!success && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not determine SID buffer size, " + "LookupAccountName() failed with error %X (%s)", + GetLastError(), get_last_error_message(error_buf))); +#endif + return; + } + + // Query for SID (domain is ignored) + + wchar_t *domain= new wchar_t[domain_size]; + m_data= (TOKEN_USER*) new BYTE[sid_size + sizeof(TOKEN_USER)]; + m_data->User.Sid= (BYTE*)m_data + sizeof(TOKEN_USER); + + success= LookupAccountNameW(NULL, account_name, + m_data->User.Sid, &sid_size, + domain, &domain_size, + &m_type); + + if (!success || !is_valid()) + { +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not determine SID of '%S', " + "LookupAccountName() failed with error %X (%s)", + account_name, GetLastError(), + get_last_error_message(error_buf))); +#endif + goto fail; + } + + goto end; + +fail: + if (m_data) + delete [] m_data; + m_data= NULL; + +end: + if (domain) + delete [] domain; +} + + +/** + Create Sid object corresponding to a given security token. + + @param[in] token security token of a Windows account + + @note In case of errors created object is invalid and its @c is_valid() + method returns @c false. +*/ + +Sid::Sid(HANDLE token): m_data(NULL) +#ifndef DBUG_OFF +, m_as_string(NULL) +#endif +{ + DWORD req_size= 0; + bool success; + + // Determine required buffer size + + success= GetTokenInformation(token, TokenUser, NULL, 0, &req_size); + if (!success && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not determine SID buffer size, " + "GetTokenInformation() failed with error %X (%s)", + GetLastError(), get_last_error_message(error_buf))); +#endif + return; + } + + m_data= (TOKEN_USER*) new BYTE[req_size]; + success= GetTokenInformation(token, TokenUser, m_data, req_size, &req_size); + + if (!success || !is_valid()) + { + delete [] m_data; + m_data= NULL; +#ifndef DBUG_OFF + if (!success) + { + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not read SID from security token, " + "GetTokenInformation() failed with error %X (%s)", + GetLastError(), get_last_error_message(error_buf))); + } +#endif + } +} + + +Sid::~Sid() +{ + if (m_data) + delete [] m_data; +#ifndef DBUG_OFF + if (m_as_string) + LocalFree(m_as_string); +#endif +} + +/// Check if Sid object is valid. +bool Sid::is_valid(void) const +{ + return m_data && m_data->User.Sid && IsValidSid(m_data->User.Sid); +} + + +#ifndef DBUG_OFF + +/** + Produces string representation of the SID. + + @return String representation of the SID or NULL in case of errors. + + @note Memory allocated for the string is automatically freed in Sid's + destructor. +*/ + +const char* Sid::as_string() +{ + if (!m_data) + return NULL; + + if (!m_as_string) + { + bool success= ConvertSidToStringSid(m_data->User.Sid, &m_as_string); + + if (!success) + { +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not get textual representation of a SID, " + "ConvertSidToStringSid() failed with error %X (%s)", + GetLastError(), get_last_error_message(error_buf))); +#endif + m_as_string= NULL; + return NULL; + } + } + + return m_as_string; +} + +#endif + + +bool Sid::operator ==(const Sid &other) +{ + if (!is_valid() || !other.is_valid()) + return false; + + return EqualSid(m_data->User.Sid, other.m_data->User.Sid); +} + + +/** Generating User Principal Name *************************/ + +/** + Call Windows API functions to get UPN of the current user and store it + in internal buffer. +*/ + +UPN::UPN(): m_buf(NULL) +{ + wchar_t buf1[MAX_SERVICE_NAME_LENGTH]; + + // First we try to use GetUserNameEx. + + m_len= sizeof(buf1)/sizeof(wchar_t); + + if (!GetUserNameExW(NameUserPrincipal, buf1, (PULONG)&m_len)) + { + if (GetLastError()) + { +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("note", ("When determining UPN" + ", GetUserNameEx() failed with error %X (%s)", + GetLastError(), get_last_error_message(error_buf))); +#endif + if (ERROR_MORE_DATA == GetLastError()) + ERROR_LOG(INFO, ("Buffer overrun when determining UPN:" + " need %ul characters but have %ul", + m_len, sizeof(buf1)/sizeof(WCHAR))); + } + + m_len= 0; // m_len == 0 indicates invalid UPN + return; + } + + /* + UPN is stored in buf1 in wide-char format - convert it to utf8 + for sending over network. + */ + + m_buf= wchar_to_utf8(buf1, &m_len); + + if(!m_buf) + ERROR_LOG(ERROR, ("Failed to convert UPN to utf8")); + + // Note: possible error would be indicated by the fact that m_buf is NULL. + return; +} + + +UPN::~UPN() +{ + if (m_buf) + free(m_buf); +} + + +/** + Convert a wide-char string to utf8 representation. + + @param[in] string null-terminated wide-char string to be converted + @param[in,out] len length of the string to be converted or 0; on + return length (in bytes, excluding terminating + null character) of the converted string + + If len is 0 then the length of the string will be computed by this function. + + @return Pointer to a buffer containing utf8 representation or NULL in + case of error. + + @note The returned buffer must be freed with @c free() call. +*/ + +char* wchar_to_utf8(const wchar_t *string, size_t *len) +{ + char *buf= NULL; + size_t str_len= len && *len ? *len : wcslen(string); + + /* + A conversion from utf8 to wchar_t will never take more than 3 bytes per + character, so a buffer of length 3 * str_len schould be sufficient. + We check that assumption with an assertion later. + */ + + size_t buf_len= 3 * str_len; + + buf= (char*)malloc(buf_len + 1); + if (!buf) + { + DBUG_PRINT("error",("Out of memory when converting string '%S' to utf8", + string)); + return NULL; + } + + int res= WideCharToMultiByte(CP_UTF8, // convert to UTF-8 + 0, // conversion flags + string, // input buffer + str_len, // its length + buf, buf_len, // output buffer and its size + NULL, NULL); // default character (not used) + + if (res) + { + buf[res]= '\0'; + if (len) + *len= res; + return buf; + } + + // res is 0 which indicates error + +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not convert string '%S' to utf8" + ", WideCharToMultiByte() failed with error %X (%s)", + string, GetLastError(), + get_last_error_message(error_buf))); +#endif + + // Let's check our assumption about sufficient buffer size + DBUG_ASSERT(ERROR_INSUFFICIENT_BUFFER != GetLastError()); + + return NULL; +} + + +/** + Convert an utf8 string to a wide-char string. + + @param[in] string null-terminated utf8 string to be converted + @param[in,out] len length of the string to be converted or 0; on + return length (in chars) of the converted string + + If len is 0 then the length of the string will be computed by this function. + + @return Pointer to a buffer containing wide-char representation or NULL in + case of error. + + @note The returned buffer must be freed with @c free() call. +*/ + +wchar_t* utf8_to_wchar(const char *string, size_t *len) +{ + size_t buf_len; + + /* + Note: length (in bytes) of an utf8 string is always bigger than the + number of characters in this string. Hence a buffer of size len will + be sufficient. We add 1 for the terminating null character. + */ + + buf_len= len && *len ? *len : strlen(string); + wchar_t *buf= (wchar_t*)malloc((buf_len+1)*sizeof(wchar_t)); + + if (!buf) + { + DBUG_PRINT("error",("Out of memory when converting utf8 string '%s'" + " to wide-char representation", string)); + return NULL; + } + + size_t res; + res= MultiByteToWideChar(CP_UTF8, // convert from UTF-8 + 0, // conversion flags + string, // input buffer + buf_len, // its size + buf, buf_len); // output buffer and its size + if (res) + { + buf[res]= '\0'; + if (len) + *len= res; + return buf; + } + + // error in MultiByteToWideChar() + +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not convert UPN from UTF-8" + ", MultiByteToWideChar() failed with error %X (%s)", + GetLastError(), get_last_error_message(error_buf))); +#endif + + // Let's check our assumption about sufficient buffer size + DBUG_ASSERT(ERROR_INSUFFICIENT_BUFFER != GetLastError()); + + return NULL; +} + + +/** Error handling ****************************************************/ + + +/** + Returns error message corresponding to the last Windows error given + by GetLastError(). + + @note Error message is overwritten by next call to + @c get_last_error_message(). +*/ + +const char* get_last_error_message(Error_message_buf buf) +{ + int error= GetLastError(); + + buf[0]= '\0'; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)buf, sizeof(buf), NULL ); + + return buf; +} diff --git a/libmysql/authentication_win/common.h b/libmysql/authentication_win/common.h new file mode 100644 index 00000000000..ff0f7153664 --- /dev/null +++ b/libmysql/authentication_win/common.h @@ -0,0 +1,324 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef COMMON_H +#define COMMON_H + +#include <my_global.h> +#include <windows.h> +#include <sspi.h> // for CtxtHandle +#include <mysql/plugin_auth.h> // for MYSQL_PLUGIN_VIO + +/// Maximum length of the target service name. +#define MAX_SERVICE_NAME_LENGTH 1024 + + +/** Debugging and error reporting infrastructure ***************************/ + +/* + Note: We use plugin local logging and error reporting mechanisms until + WL#2940 (plugin service: error reporting) is available. +*/ + +#undef INFO +#undef WARNING +#undef ERROR + +struct error_log_level +{ + typedef enum {INFO, WARNING, ERROR} type; +}; + + +/* + If DEBUG_ERROR_LOG is defined then error logging happens only + in debug-copiled code. Otherwise ERROR_LOG() expands to + error_log_print() even in production code. Note that in client + plugin, error_log_print() will print nothing if opt_auth_win_clinet_log + is 0. + + Note: Macro ERROR_LOG() can use printf-like format string like this: + + ERROR_LOG(Level, ("format string", args)); + + The implementation should handle it correctly. Currently it is passed + to fprintf() (see error_log_vprint() function). +*/ + +extern "C" int opt_auth_win_client_log; + +#if defined(DEBUG_ERROR_LOG) && defined(DBUG_OFF) +#define ERROR_LOG(Level, Msg) do {} while (0) +#else +#define ERROR_LOG(Level, Msg) error_log_print< error_log_level::Level > Msg +#endif + + +void error_log_vprint(error_log_level::type level, + const char *fmt, va_list args); + +template <error_log_level::type Level> +void error_log_print(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + error_log_vprint(Level, fmt, args); + va_end(args); +} + +typedef char Error_message_buf[1024]; +const char* get_last_error_message(Error_message_buf); + + +/* + Internal implementation of debug message printing which does not use + dbug library. This is invoked via macro: + + DBUG_PRINT_DO(Keyword, ("format string", args)); + + This is supposed to be used as an implementation of DBUG_PRINT() macro, + unless the dbug library implementation is used or debug messages are disabled. +*/ + +#ifndef DBUG_OFF + +#define DBUG_PRINT_DO(Keyword, Msg) \ + do { \ + if (2 > opt_auth_win_client_log) break; \ + fprintf(stderr, "winauth: %s: ", Keyword); \ + debug_msg Msg; \ + } while (0) + +inline +void debug_msg(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + fflush(stderr); + va_end(args); +} + +#else +#define DBUG_PRINT_DO(K, M) do {} while (0) +#endif + + +#ifndef WINAUTH_USE_DBUG_LIB + +#undef DBUG_PRINT +#define DBUG_PRINT(Keyword, Msg) DBUG_PRINT_DO(Keyword, Msg) + +/* + Redefine few more debug macros to make sure that no symbols from + dbug library are used. +*/ + +#undef DBUG_ENTER +#define DBUG_ENTER(X) do {} while (0) + +#undef DBUG_RETURN +#define DBUG_RETURN(X) return (X) + +#undef DBUG_ASSERT +#ifndef DBUG_OFF +#define DBUG_ASSERT(X) assert (X) +#else +#define DBUG_ASSERT(X) do {} while (0) +#endif + +#undef DBUG_DUMP +#define DBUG_DUMP(A,B,C) do {} while (0) + +#endif + + +/** Blob class *************************************************************/ + +typedef unsigned char byte; + +/** + Class representing a region of memory (e.g., a string or binary buffer). + + @note This class does not allocate memory. It merely describes a region + of memory which must be allocated externally (if it is dynamic memory). +*/ + +class Blob +{ + byte *m_ptr; ///< Pointer to the first byte of the memory region. + size_t m_len; ///< Length of the memory region. + +public: + + Blob(): m_ptr(NULL), m_len(0) + {} + + Blob(const byte *ptr, const size_t len) + : m_ptr(const_cast<byte*>(ptr)), m_len(len) + {} + + Blob(const char *str): m_ptr((byte*)str) + { + m_len= strlen(str); + } + + byte* ptr() const + { + return m_ptr; + } + + size_t len() const + { + return m_len; + } + + byte& operator[](unsigned pos) const + { + static byte out_of_range= 0; // alas, no exceptions... + return pos < len() ? m_ptr[pos] : out_of_range; + } + + bool is_null() const + { + return m_ptr == NULL; + } + + void trim(size_t l) + { + m_len= l; + } +}; + + +/** Connection class *******************************************************/ + +/** + Convenience wrapper around MYSQL_PLUGIN_VIO object providing basic + read/write operations. +*/ + +class Connection +{ + MYSQL_PLUGIN_VIO *m_vio; ///< Pointer to @c MYSQL_PLUGIN_VIO structure. + + /** + If non-zero, indicates that connection is broken. If this has happened + because of failed operation, stores non-zero error code from that failure. + */ + int m_error; + +public: + + Connection(MYSQL_PLUGIN_VIO *vio); + int write(const Blob&); + Blob read(); + + int error() const + { + return m_error; + } +}; + + +/** Sid class **************************************************************/ + +/** + Class for storing and manipulating Windows security identifiers (SIDs). +*/ + +class Sid +{ + TOKEN_USER *m_data; ///< Pointer to structure holding identifier's data. + SID_NAME_USE m_type; ///< Type of identified entity. + +public: + + Sid(const wchar_t*); + Sid(HANDLE sec_token); + ~Sid(); + + bool is_valid(void) const; + + bool is_group(void) const + { + return m_type == SidTypeGroup + || m_type == SidTypeWellKnownGroup + || m_type == SidTypeAlias; + } + + bool is_user(void) const + { + return m_type == SidTypeUser; + } + + bool operator==(const Sid&); + + operator PSID() const + { + return (PSID)m_data->User.Sid; + } + +#ifndef DBUG_OFF + +private: + char *m_as_string; ///< Cached string representation of the SID. +public: + const char* as_string(); + +#endif +}; + + +/** UPN class **************************************************************/ + +/** + An object of this class obtains and stores User Principal Name of the + account under which current process is running. +*/ + +class UPN +{ + char *m_buf; ///< Pointer to UPN in utf8 representation. + size_t m_len; ///< Length of the name. + +public: + + UPN(); + ~UPN(); + + bool is_valid() const + { + return m_len > 0; + } + + const Blob as_blob() const + { + return m_len ? Blob((byte*)m_buf, m_len) : Blob(); + } + + const char* as_string() const + { + return (const char*)m_buf; + } + +}; + + +char* wchar_to_utf8(const wchar_t*, size_t*); +wchar_t* utf8_to_wchar(const char*, size_t*); + +#endif diff --git a/libmysql/authentication_win/handshake.cc b/libmysql/authentication_win/handshake.cc new file mode 100644 index 00000000000..ec665af9ef9 --- /dev/null +++ b/libmysql/authentication_win/handshake.cc @@ -0,0 +1,289 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "handshake.h" + + +/** Handshake class implementation **********************************/ + +/** + Create common part of handshake context. + + @param[in] ssp name of the SSP (Security Service Provider) to + be used for authentication + @param[in] side is this handshake object used for server- or + client-side handshake + + Prepare for handshake using the @c ssp security module. We use + "Negotiate" which picks best available module. Parameter @c side + tells if this is preparing for server or client side authentication + and is used to prepare appropriate credentials. +*/ + +Handshake::Handshake(const char *ssp, side_t side) +: m_atts(0L), m_error(0), m_complete(FALSE), + m_have_credentials(false), m_have_sec_context(false) +#ifndef DBUG_OFF + , m_ssp_info(NULL) +#endif +{ + SECURITY_STATUS ret; + + // Obtain credentials for the authentication handshake. + + ret= AcquireCredentialsHandle(NULL, (SEC_CHAR*)ssp, + side == SERVER ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND, + NULL, NULL, NULL, NULL, &m_cred, &m_expire); + + if (ret != SEC_E_OK) + { + DBUG_PRINT("error", ("AcqireCredentialsHandle() failed" + " with error %X", ret)); + ERROR_LOG(ERROR, ("Could not obtain local credentials" + " required for authentication")); + m_error= ret; + } + + m_have_credentials= true; +} + + +Handshake::~Handshake() +{ + if (m_have_credentials) + FreeCredentialsHandle(&m_cred); + if (m_have_sec_context) + DeleteSecurityContext(&m_sctx); + m_output.free(); + +#ifndef DBUG_OFF + if (m_ssp_info) + FreeContextBuffer(m_ssp_info); +#endif +} + + +/** + Read and process data packets from the other end of a connection. + + @param[IN] con a connection to read packets from + + Packets are read and processed until authentication handshake is + complete. It is assumed that the peer will send at least one packet. + Packets are processed with @c process_data() method. If new data is + generated during packet processing, this data is sent to the peer and + another round of packet exchange starts. + + @return 0 on success. + + @note In case of error, appropriate error message is logged. +*/ +int Handshake::packet_processing_loop() +{ + m_round= 0; + + do { + ++m_round; + // Read packet send by the peer + + DBUG_PRINT("info", ("Waiting for packet")); + Blob packet= read_packet(); + if (error()) + { + ERROR_LOG(ERROR, ("Error reading packet in round %d", m_round)); + return 1; + } + DBUG_PRINT("info", ("Got packet of length %d", packet.len())); + + /* + Process received data, possibly generating new data to be sent. + */ + + Blob new_data= process_data(packet); + + if (error()) + { + ERROR_LOG(ERROR, ("Error processing packet in round %d", m_round)); + return 1; + } + + /* + If new data has been generated, send it to the peer. Otherwise + handshake must be completed. + */ + + if (!new_data.is_null()) + { + DBUG_PRINT("info", ("Round %d started", m_round)); + + DBUG_PRINT("info", ("Sending packet of length %d", new_data.len())); + int ret= write_packet(new_data); + if (ret) + { + ERROR_LOG(ERROR, ("Error writing packet in round %d", m_round)); + return 1; + } + DBUG_PRINT("info", ("Data sent")); + } + else if (!is_complete()) + { + ERROR_LOG(ERROR, ("No data to send in round %d" + " but handshake is not complete", m_round)); + return 1; + } + + /* + To protect against malicious clients, break handshake exchange if + too many rounds. + */ + + if (m_round > MAX_HANDSHAKE_ROUNDS) + { + ERROR_LOG(ERROR, ("Authentication handshake could not be completed" + " after %d rounds", m_round)); + return 1; + } + + } while(!is_complete()); + + ERROR_LOG(INFO, ("Handshake completed after %d rounds", m_round)); + return 0; +} + + +#ifndef DBUG_OFF + +/** + Get name of the security package which was used in authentication. + + This method should be called only after handshake was completed. It is + available only in debug builds. + + @return Name of security package or NULL if it can not be obtained. +*/ + +const char* Handshake::ssp_name() +{ + if (!m_ssp_info && m_complete) + { + SecPkgContext_PackageInfo pinfo; + + int ret= QueryContextAttributes(&m_sctx, SECPKG_ATTR_PACKAGE_INFO, &pinfo); + + if (SEC_E_OK == ret) + { + m_ssp_info= pinfo.PackageInfo; + } + else + DBUG_PRINT("error", + ("Could not obtain SSP info from authentication context" + ", QueryContextAttributes() failed with error %X", ret)); + } + + return m_ssp_info ? m_ssp_info->Name : NULL; +} + +#endif + + +/** + Process result of @c {Initialize,Accept}SecurityContext() function. + + @param[in] ret return code from @c {Initialize,Accept}SecurityContext() + function + + This function analyses return value of Windows + @c {Initialize,Accept}SecurityContext() function. A call to + @c CompleteAuthToken() is done if requested. If authentication is complete, + this fact is marked in the internal state of the Handshake object. + If errors are detected the object is moved to error state. + + @return True if error has been detected. +*/ + +bool Handshake::process_result(int ret) +{ + /* + First check for errors and set the m_complete flag if the result + indicates that handshake is complete. + */ + + switch (ret) + { + case SEC_E_OK: + case SEC_I_COMPLETE_NEEDED: + // Handshake completed + m_complete= true; + break; + + case SEC_I_CONTINUE_NEEDED: + case SEC_I_COMPLETE_AND_CONTINUE: + break; + + default: + m_error= ret; + return true; + } + + m_have_sec_context= true; + + /* + If the result indicates a need for this, complete the authentication + token. + */ + + switch (ret) + { + case SEC_I_COMPLETE_NEEDED: + case SEC_I_COMPLETE_AND_CONTINUE: + ret= CompleteAuthToken(&m_sctx, &m_output); + if (ret != 0) + { + DBUG_PRINT("error", ("CompleteAuthToken() failed with error %X", ret)); + m_error= ret; + return true; + } + default: + break; + } + + return false; +} + + +/** Security_buffer class implementation **********************************/ + + +Security_buffer::Security_buffer(const Blob &blob): m_allocated(false) +{ + init(blob.ptr(), blob.len()); +} + + +Security_buffer::Security_buffer(): m_allocated(true) +{ + init(NULL, 0); +} + + +void Security_buffer::free(void) +{ + if (!m_allocated) + return; + if (!ptr()) + return; + FreeContextBuffer(ptr()); + m_allocated= false; +} diff --git a/libmysql/authentication_win/handshake.h b/libmysql/authentication_win/handshake.h new file mode 100644 index 00000000000..5292948b583 --- /dev/null +++ b/libmysql/authentication_win/handshake.h @@ -0,0 +1,181 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef HANDSHAKE_H +#define HANDSHAKE_H + +#include "common.h" + +/** + Name of the SSP (Security Support Provider) to be used for authentication. + + We use "Negotiate" which will find the most secure SSP which can be used + and redirect to that SSP. +*/ +#define SSP_NAME "Negotiate" + +/** + Maximal number of rounds in authentication handshake. + + Server will interrupt authentication handshake with error if client's + identity can not be determined within this many rounds. +*/ +#define MAX_HANDSHAKE_ROUNDS 50 + + +/// Convenience wrapper around @c SecBufferDesc. + +class Security_buffer: public SecBufferDesc +{ + SecBuffer m_buf; ///< A @c SecBuffer instance. + + void init(byte *ptr, size_t len) + { + ulVersion= 0; + cBuffers= 1; + pBuffers= &m_buf; + + m_buf.BufferType= SECBUFFER_TOKEN; + m_buf.pvBuffer= ptr; + m_buf.cbBuffer= len; + } + + /// If @c false, no deallocation will be done in the destructor. + bool m_allocated; + + public: + + Security_buffer(const Blob&); + Security_buffer(); + + ~Security_buffer() + { + free(); + } + + byte* ptr() const + { + return (byte*)m_buf.pvBuffer; + } + + size_t len() const + { + return m_buf.cbBuffer; + } + + bool is_valid() const + { + return ptr() != NULL; + } + + const Blob as_blob() const + { + return Blob(ptr(), len()); + } + + void free(void); +}; + + +/// Common base for Handshake_{server,client}. + +class Handshake +{ +public: + + typedef enum {CLIENT, SERVER} side_t; + + Handshake(const char *ssp, side_t side); + virtual ~Handshake(); + + int Handshake::packet_processing_loop(); + + bool virtual is_complete() const + { + return m_complete; + } + + int error() const + { + return m_error; + } + +protected: + + /// Security context object created during the handshake. + CtxtHandle m_sctx; + + /// Credentials of the principal performing this handshake. + CredHandle m_cred; + + /// Stores expiry date of the created security context. + TimeStamp m_expire; + + /// Stores attributes of the created security context. + ULONG m_atts; + + /** + Round of the handshake (starting from round 1). One round + consist of reading packet from the other side, processing it and + optionally sending a reply (see @c packet_processing_loop()). + */ + unsigned int m_round; + + /// If non-zero, stores error code of the last failed operation. + int m_error; + + /// @c true when handshake is complete. + bool m_complete; + + /// @c true when the principal credentials has been determined. + bool m_have_credentials; + + /// @c true when the security context has been created. + bool m_have_sec_context; + + /// Buffer for data to be send to the other side. + Security_buffer m_output; + + bool process_result(int); + + /** + This method is used inside @c packet_processing_loop to process + data packets received from the other end. + + @param[IN] data data to be processed + + @return A blob with data to be sent to the other end or null blob if + no more data needs to be exchanged. + */ + virtual Blob process_data(const Blob &data) =0; + + /// Read packet from the other end. + virtual Blob read_packet() =0; + + /// Write packet to the other end. + virtual int write_packet(Blob &data) =0; + +#ifndef DBUG_OFF + +private: + SecPkgInfo *m_ssp_info; +public: + const char* ssp_name(); + +#endif +}; + + +#endif diff --git a/libmysql/authentication_win/handshake_client.cc b/libmysql/authentication_win/handshake_client.cc new file mode 100644 index 00000000000..7e89fc92ae7 --- /dev/null +++ b/libmysql/authentication_win/handshake_client.cc @@ -0,0 +1,378 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "handshake.h" + +#include <mysql.h> // for MYSQL structure + + +/// Client-side context for authentication handshake + +class Handshake_client: public Handshake +{ + /** + Name of the server's service for which we authenticate. + + The service name is sent by server in the initial packet. If no + service name is used, this member is @c NULL. + */ + SEC_WCHAR *m_service_name; + + /// Buffer for storing service name obtained from server. + SEC_WCHAR m_service_name_buf[MAX_SERVICE_NAME_LENGTH]; + + Connection &m_con; + +public: + + Handshake_client(Connection &con, const char *target, size_t len); + ~Handshake_client(); + + Blob first_packet(); + Blob process_data(const Blob&); + + Blob read_packet(); + int write_packet(Blob &data); +}; + + +/** + Create authentication handshake context for client. + + @param con connection for communication with the peer + @param target name of the target service with which we will authenticate + (can be NULL if not used) + + Some security packages (like Kerberos) require providing explicit name + of the service with which a client wants to authenticate. The server-side + authentication plugin sends this name in the greeting packet + (see @c win_auth_handshake_{server,client}() functions). +*/ + +Handshake_client::Handshake_client(Connection &con, + const char *target, size_t len) +: Handshake(SSP_NAME, CLIENT), m_service_name(NULL), m_con(con) +{ + if (!target || 0 == len) + return; + + // Convert received UPN to internal WCHAR representation. + + m_service_name= utf8_to_wchar(target, &len); + + if (m_service_name) + DBUG_PRINT("info", ("Using target service: %S\n", m_service_name)); + else + { + /* + Note: we ignore errors here - m_target will be NULL, the target name + will not be used and system will fall-back to NTLM authentication. But + we leave trace in error log. + */ + ERROR_LOG(WARNING, ("Could not decode UPN sent by the server" + "; target service name will not be used" + " and Kerberos authentication will not work")); + } +} + + +Handshake_client::~Handshake_client() +{ + if (m_service_name) + free(m_service_name); +} + + +Blob Handshake_client::read_packet() +{ + /* + We do a fake read in the first round because first + packet from the server containing UPN must be read + before the handshake context is created and the packet + processing loop starts. We return an empty blob here + and process_data() function will ignore it. + */ + if (m_round == 1) + return Blob(); + + // Otherwise we read packet from the connection. + + Blob packet= m_con.read(); + m_error= m_con.error(); + if (!m_error && packet.is_null()) + m_error= true; // (no specific error code assigned) + + if (m_error) + return Blob(); + + DBUG_PRINT("dump", ("Got the following bytes")); + DBUG_DUMP("dump", packet.ptr(), packet.len()); + return packet; +} + + + +int Handshake_client::write_packet(Blob &data) +{ + /* + Length of the first data payload send by client authentication plugin is + limited to 255 bytes (because it is wrapped inside client authentication + packet and is length-encoded with 1 byte for the length). + + If the data payload is longer than 254 bytes, then it is sent in two parts: + first part of length 255 will be embedded in the authentication packet, + second part will be sent in the following packet. Byte 255 of the first + part contains information about the total length of the payload. It is a + number of blocks of size 512 bytes which is sufficient to store the + combined packets. + + Server's logic for reading first client's payload is as follows + (see Handshake_server::read_packet()): + 1. Read data from the authentication packet, if it is shorter than 255 bytes + then that is all data sent by client. + 2. If there is 255 bytes of data in the authentication packet, read another + packet and append it to the data, skipping byte 255 of the first packet + which can be used to allocate buffer of appropriate size. + */ + + size_t len2= 0; // length of the second part of first data payload + byte saved_byte; // for saving byte 255 in which data length is stored + + if (m_round == 1 && data.len() > 254) + { + len2= data.len() - 254; + DBUG_PRINT("info", ("Splitting first packet of length %lu" + ", %lu bytes will be sent in a second part", + data.len(), len2)); + /* + Store in byte 255 the number of 512b blocks that are needed to + keep all the data. + */ + unsigned block_count= data.len()/512 + ((data.len() % 512) ? 1 : 0); + DBUG_ASSERT(block_count < (unsigned)0x100); + saved_byte= data[254]; + data[254] = block_count; + + data.trim(255); + } + + DBUG_PRINT("dump", ("Sending the following data")); + DBUG_DUMP("dump", data.ptr(), data.len()); + int ret= m_con.write(data); + + if (ret) + return ret; + + // Write second part if it is present. + if (len2) + { + data[254]= saved_byte; + Blob data2(data.ptr() + 254, len2); + DBUG_PRINT("info", ("Sending second part of data")); + DBUG_DUMP("info", data2.ptr(), data2.len()); + ret= m_con.write(data2); + } + + return ret; +} + + +/** + Process data sent by server. + + @param[in] data blob with data from server + + This method analyses data sent by server during authentication handshake. + If client should continue packet exchange, this method returns data to + be sent to the server next. If no more data needs to be exchanged, an + empty blob is returned and @c is_complete() is @c true. In case of error + an empty blob is returned and @c error() gives non-zero error code. + + When invoked for the first time (in the first round of the handshake) + there is no data from the server (data blob is null) and the intial + packet is generated without an input. + + @return Data to be sent to the server next or null blob if no more data + needs to be exchanged or in case of error. +*/ + +Blob Handshake_client::process_data(const Blob &data) +{ +#if !defined(DBUG_OFF) && defined(WINAUTH_USE_DBUG_LIB) + /* + Code for testing the logic for sending the first client payload. + + A fake data of length given by environment variable TEST_PACKET_LENGTH + (or default 255 bytes) is sent to the server. First 2 bytes of the + payload contain its total length (LSB first). The length of test data + is limited to 2048 bytes. + + Upon receiving test data, server will check that data is correct and + refuse connection. If server detects data errors it will crash on + assertion. + + This code is executed if debug flag "winauth_first_packet_test" is + set, e.g. using client option: + + --debug="d,winauth_first_packet_test" + + The same debug flag must be enabled in the server, e.g. using + statement: + + SET GLOBAL debug= '+d,winauth_first_packet_test'; + */ + + static byte test_buf[2048]; + + if (m_round == 1 + && DBUG_EVALUATE_IF("winauth_first_packet_test", true, false)) + { + const char *env= getenv("TEST_PACKET_LENGTH"); + size_t len= env ? atoi(env) : 0; + if (!len) + len= 255; + if (len > sizeof(test_buf)) + len= sizeof(test_buf); + + // Store data length in first 2 bytes. + byte *ptr= test_buf; + *ptr++= len & 0xFF; + *ptr++= len >> 8; + + // Fill remaining bytes with known values. + for (byte b= 0; ptr < test_buf + len; ++ptr, ++b) + *ptr= b; + + return Blob(test_buf, len); + }; + +#endif + + Security_buffer input(data); + SECURITY_STATUS ret; + + m_output.free(); + + ret= InitializeSecurityContextW( + &m_cred, + m_round == 1 ? NULL : &m_sctx, // partial context + m_service_name, // service name + ASC_REQ_ALLOCATE_MEMORY, // requested attributes + 0, // reserved + SECURITY_NETWORK_DREP, // data representation + m_round == 1 ? NULL : &input, // input data + 0, // reserved + &m_sctx, // context + &m_output, // output data + &m_atts, // attributes + &m_expire); // expire date + + if (process_result(ret)) + { + DBUG_PRINT("error", + ("InitializeSecurityContext() failed with error %X", ret)); + return Blob(); + } + + return m_output.as_blob(); +} + + +/**********************************************************************/ + + +/** + Perform authentication handshake from client side. + + @param[in] vio pointer to @c MYSQL_PLUGIN_VIO instance to be used + for communication with the server + @param[in] mysql pointer to a MySQL connection for which we authenticate + + After reading the initial packet from server, containing its UPN to be + used as service name, client starts packet exchange by sending the first + packet in this exchange. While handshake is not yet completed, client + reads packets sent by the server and process them, possibly generating new + data to be sent to the server. + + This function reports errors. + + @return 0 on success. +*/ + +int win_auth_handshake_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) +{ + DBUG_ENTER("win_auth_handshake_client"); + + /* + Check if we should enable logging. + */ + { + const char *opt= getenv("AUTHENTICATION_WIN_LOG"); + int opt_val= opt ? atoi(opt) : 0; + if (opt && !opt_val) + { + if (!strncasecmp("on", opt, 2)) opt_val= 1; + if (!strncasecmp("yes", opt, 3)) opt_val= 1; + if (!strncasecmp("true", opt, 4)) opt_val= 1; + if (!strncasecmp("debug", opt, 5)) opt_val= 2; + if (!strncasecmp("dbug", opt, 4)) opt_val= 2; + } + opt_auth_win_client_log= opt_val; + } + + ERROR_LOG(INFO, ("Authentication handshake for account %s", mysql->user)); + + // Create connection object. + + Connection con(vio); + DBUG_ASSERT(!con.error()); + + // Read initial packet from server containing service name. + + Blob service_name= con.read(); + + if (con.error() || service_name.is_null()) + { + ERROR_LOG(ERROR, ("Error reading initial packet")); + DBUG_RETURN(CR_ERROR); + } + DBUG_PRINT("info", ("Got initial packet of length %d", service_name.len())); + + // Create authentication handshake context using the given service name. + + Handshake_client hndshk(con, + service_name[0] ? (char *)service_name.ptr() : NULL, + service_name.len()); + if (hndshk.error()) + { + ERROR_LOG(ERROR, ("Could not create authentication handshake context")); + DBUG_RETURN(CR_ERROR); + } + + DBUG_ASSERT(!hndshk.error()); + + /* + Read and process packets from server until handshake is complete. + Note that the first read from server is dummy + (see Handshake_client::read_packet()) as we already have read the + first packet to establish service name. + */ + if (hndshk.packet_processing_loop()) + DBUG_RETURN(CR_ERROR); + + DBUG_ASSERT(!hndshk.error() && hndshk.is_complete()); + + DBUG_RETURN(CR_OK); +} diff --git a/libmysql/authentication_win/log_client.cc b/libmysql/authentication_win/log_client.cc new file mode 100644 index 00000000000..df4ce4f9c2a --- /dev/null +++ b/libmysql/authentication_win/log_client.cc @@ -0,0 +1,55 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <my_global.h> +#include "common.h" + +/** + This option is set in win_auth_handshake_client() function + in handshake_client.cc. + + Values: + 0 - no logging + 1 - log error/warning/info messages + 2 - also log debug messages + + Note: No error or debug messages are logged in production code + (see logging macros in common.h). +*/ +int opt_auth_win_client_log= 0; + + +// Client-side logging function + +void error_log_vprint(error_log_level::type level, + const char *fmt, va_list args) +{ + if (0 == opt_auth_win_client_log) + return; + + const char *level_string= ""; + + switch (level) + { + case error_log_level::INFO: level_string= "Note"; break; + case error_log_level::WARNING: level_string= "Warning"; break; + case error_log_level::ERROR: level_string= "ERROR"; break; + } + + fprintf(stderr, "Windows Authentication Plugin %s: ", level_string); + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + fflush(stderr); +} diff --git a/libmysql/authentication_win/plugin_client.cc b/libmysql/authentication_win/plugin_client.cc new file mode 100644 index 00000000000..30dc5b1493d --- /dev/null +++ b/libmysql/authentication_win/plugin_client.cc @@ -0,0 +1,68 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <my_global.h> +#include <mysql.h> +#include <mysql/plugin_auth.h> +#include <mysql/client_plugin.h> + +#include "common.h" + +/* + The following MS C++ specific pragma embeds a comment in the resulting + object file. A "lib" comment tells the linker to use the specified + library, thus the dependency is handled automagically. +*/ + +#ifdef _MSC_VER +#pragma comment(lib, "Secur32") +#endif + +static int win_auth_client_plugin_init(char*, size_t, int, va_list) +{ + return 0; +} + + +static int win_auth_client_plugin_deinit() +{ + return 0; +} + + +int win_auth_handshake_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql); + + +/* + Client plugin declaration. This is added to mysql_client_builtins[] + in sql-common/client.c +*/ + +extern "C" +st_mysql_client_plugin_AUTHENTICATION win_auth_client_plugin= +{ + MYSQL_CLIENT_AUTHENTICATION_PLUGIN, + MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, + "authentication_windows_client", + "Rafal Somla", + "Windows Authentication Plugin - client side", + {0,1,0}, + "GPL", + NULL, + win_auth_client_plugin_init, + win_auth_client_plugin_deinit, + NULL, // option handling + win_auth_handshake_client +}; diff --git a/libmysql/client_settings.h b/libmysql/client_settings.h index f87e625771f..d04d61067f2 100644 --- a/libmysql/client_settings.h +++ b/libmysql/client_settings.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2003-2005 MySQL AB +/* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -11,34 +11,30 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef CLIENT_SETTINGS_INCLUDED +#define CLIENT_SETTINGS_INCLUDED +#else +#error You have already included an client_settings.h and it should not be included twice +#endif /* CLIENT_SETTINGS_INCLUDED */ extern uint mysql_port; extern char * mysql_unix_port; -#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | \ - CLIENT_TRANSACTIONS | \ - CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION) +#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | \ + CLIENT_LONG_FLAG | \ + CLIENT_TRANSACTIONS | \ + CLIENT_PROTOCOL_41 | \ + CLIENT_SECURE_CONNECTION | \ + CLIENT_MULTI_RESULTS | \ + CLIENT_PS_MULTI_RESULTS | \ + CLIENT_PLUGIN_AUTH) sig_handler my_pipe_sig_handler(int sig); void read_user_name(char *name); my_bool handle_local_infile(MYSQL *mysql, const char *net_filename); -/* - Let the user specify that we don't want SIGPIPE; This doesn't however work - with threaded applications as we can have multiple read in progress. -*/ - -#if !defined(__WIN__) && defined(SIGPIPE) && !defined(THREAD) -#define init_sigpipe_variables sig_return old_signal_handler=(sig_return) 0; -#define set_sigpipe(mysql) if ((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE) old_signal_handler=signal(SIGPIPE, my_pipe_sig_handler) -#define reset_sigpipe(mysql) if ((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE) signal(SIGPIPE,old_signal_handler); -#else -#define init_sigpipe_variables -#define set_sigpipe(mysql) -#define reset_sigpipe(mysql) -#endif - void mysql_read_default_options(struct st_mysql_options *options, const char *filename,const char *group); void mysql_detach_stmt_list(LIST **stmt_list, const char *func_name); @@ -57,7 +53,7 @@ int cli_stmt_execute(MYSQL_STMT *stmt); int cli_read_binary_rows(MYSQL_STMT *stmt); int cli_unbuffered_fetch(MYSQL *mysql, char **row); const char * cli_read_statistics(MYSQL *mysql); -int cli_read_change_user_result(MYSQL *mysql, char *buff, const char *passwd); +int cli_read_change_user_result(MYSQL *mysql); #ifdef EMBEDDED_LIBRARY int init_embedded_server(int argc, char **argv, char **groups); diff --git a/libmysql/dll.c b/libmysql/dll.c deleted file mode 100644 index 8654c035c63..00000000000 --- a/libmysql/dll.c +++ /dev/null @@ -1,124 +0,0 @@ -/* Copyright (C) 2000-2004 MySQL AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation. - - There are special exceptions to the terms and conditions of the GPL as it - is applied to this software. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -/* -** Handling initialization of the dll library -*/ - -#include <my_global.h> -#include <my_sys.h> -#include <my_pthread.h> - -static my_bool libmysql_inited=0; - -void libmysql_init(void) -{ - if (libmysql_inited) - return; - libmysql_inited=1; - my_init(); - { - DBUG_ENTER("libmysql_init"); -#ifdef LOG_ALL - DBUG_PUSH("d:t:S:O,c::\\tmp\\libmysql.log"); -#else - if (getenv("LIBMYSQL_LOG") != NULL) - DBUG_PUSH(getenv("LIBMYSQL_LOG")); -#endif - DBUG_VOID_RETURN; - } -} - -#ifdef __WIN__ - -static int inited=0,threads=0; -HINSTANCE NEAR s_hModule; /* Saved module handle */ -DWORD main_thread; - -BOOL APIENTRY LibMain(HANDLE hInst,DWORD ul_reason_being_called, - LPVOID lpReserved) -{ - switch (ul_reason_being_called) { - case DLL_PROCESS_ATTACH: /* case of libentry call in win 3.x */ - if (!inited++) - { - s_hModule=hInst; - libmysql_init(); - main_thread=GetCurrentThreadId(); - } - break; - case DLL_THREAD_ATTACH: - threads++; - my_thread_init(); - break; - case DLL_PROCESS_DETACH: /* case of wep call in win 3.x */ - if (!--inited) /* Safety */ - { - /* my_thread_init() */ /* This may give extra safety */ - my_end(0); - } - break; - case DLL_THREAD_DETACH: - /* Main thread will free by my_end() */ - threads--; - if (main_thread != GetCurrentThreadId()) - my_thread_end(); - break; - default: - break; - } /* switch */ - - return TRUE; - - UNREFERENCED_PARAMETER(lpReserved); -} /* LibMain */ - - -static BOOL do_libmain; -int __stdcall DllMain(HANDLE hInst,DWORD ul_reason_being_called,LPVOID lpReserved) -{ - /* - Unless environment variable LIBMYSQL_DLLINIT is set, do nothing. - The environment variable is checked once, during the first call to DllMain() - (in DLL_PROCESS_ATTACH hook). - */ - if (ul_reason_being_called == DLL_PROCESS_ATTACH) - do_libmain = (getenv("LIBMYSQL_DLLINIT") != NULL); - if (do_libmain) - return LibMain(hInst,ul_reason_being_called,lpReserved); - return TRUE; -} - -#elif defined(WINDOWS) - -/**************************************************************************** -** This routine is called by LIBSTART.ASM at module load time. All it -** does in this sample is remember the DLL module handle. The module -** handle is needed if you want to do things like load stuff from the -** resource file (for instance string resources). -****************************************************************************/ - -int _export FAR PASCAL libmain(HANDLE hModule,short cbHeapSize, - UCHAR FAR *lszCmdLine) -{ - s_hModule = hModule; - libmysql_init(); - return TRUE; -} - -#endif diff --git a/libmysql/errmsg.c b/libmysql/errmsg.c index 163632127c3..498ba6e9829 100644 --- a/libmysql/errmsg.c +++ b/libmysql/errmsg.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2004 MySQL AB +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -14,7 +14,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* Error messages for MySQL clients */ /* (Error messages for the daemon are in share/language/errmsg.sys) */ @@ -23,137 +23,6 @@ #include <my_sys.h> #include "errmsg.h" -#ifdef GERMAN -const char *client_errors[]= -{ - "Unbekannter MySQL Fehler", - "Kann UNIX-Socket nicht anlegen (%d)", - "Keine Verbindung zu lokalem MySQL Server, socket: '%-.100s' (%d)", - "Keine Verbindung zu MySQL Server auf %-.100s (%d)", - "Kann TCP/IP-Socket nicht anlegen (%d)", - "Unbekannter MySQL Server Host (%-.100s) (%d)", - "MySQL Server nicht vorhanden", - "Protokolle ungleich; Server Version = %d, Client Version = %d", - "MySQL client ran out of memory", - "Wrong host info", - "Localhost via UNIX socket", - "%-.100s via TCP/IP", - "Error in server handshake", - "Lost connection to MySQL server during query", - "Commands out of sync; you can't run this command now", - "Verbindung ueber Named Pipe: %-.32s", - "Kann nicht auf Named Pipe warten. Host: %-.64s pipe: %-.32s (%lu)", - "Kann Named Pipe nicht oeffnen. Host: %-.64s pipe: %-.32s (%lu)", - "Kann den Status der Named Pipe nicht setzen. Host: %-.64s pipe: %-.32s (%lu)", - "Can't initialize character set %-.32s (path: %-.100s)", - "Got packet bigger than 'max_allowed_packet' bytes", - "Embedded server", - "Error on SHOW SLAVE STATUS:", - "Error on SHOW SLAVE HOSTS:", - "Error connecting to slave:", - "Error connecting to master:", - "SSL connection error", - "Malformed packet", - "This client library is licensed only for use with MySQL servers having '%s' license", - "Invalid use of null pointer", - "Statement not prepared", - "No data supplied for parameters in prepared statement", - "Data truncated", - "No parameters exist in the statement", - "Invalid parameter number", - "Can't send long data for non-string/non-binary data types (parameter: %d)", - "Using unsupported buffer type: %d (parameter: %d)", - "Shared memory: %-.100s", - "Can't open shared memory; client could not create request event (%lu)", - "Can't open shared memory; no answer event received from server (%lu)", - "Can't open shared memory; server could not allocate file mapping (%lu)", - "Can't open shared memory; server could not get pointer to file mapping (%lu)", - "Can't open shared memory; client could not allocate file mapping (%lu)", - "Can't open shared memory; client could not get pointer to file mapping (%lu)", - "Can't open shared memory; client could not create %s event (%lu)", - "Can't open shared memory; no answer from server (%lu)", - "Can't open shared memory; cannot send request event to server (%lu)", - "Wrong or unknown protocol", - "Invalid connection handle", - "Connection using old (pre-4.1.1) authentication protocol refused (client option 'secure_auth' enabled)", - "Row retrieval was canceled by mysql_stmt_close() call", - "Attempt to read column without prior row fetch", - "Prepared statement contains no metadata", - "Attempt to read a row while there is no result set associated with the statement", - "This feature is not implemented yet", - "Lost connection to MySQL server at '%s', system error: %d", - "Statement closed indirectly because of a preceeding %s() call", - "The number of columns in the result set differs from the number of bound buffers. You must reset the statement, rebind the result set columns, and execute the statement again", - "" -}; - -/* Start of code added by Roberto M. Serqueira - martinsc@uol.com.br - 05.24.2001 */ - -#elif defined PORTUGUESE -const char *client_errors[]= -{ - "Erro desconhecido do MySQL", - "Não pode criar 'UNIX socket' (%d)", - "Não pode se conectar ao servidor MySQL local através do 'socket' '%-.100s' (%d)", - "Não pode se conectar ao servidor MySQL em '%-.100s' (%d)", - "Não pode criar 'socket TCP/IP' (%d)", - "'Host' servidor MySQL '%-.100s' (%d) desconhecido", - "Servidor MySQL desapareceu", - "Incompatibilidade de protocolos; versão do servidor = %d, versão do cliente = %d", - "Cliente do MySQL com falta de memória", - "Informação inválida de 'host'", - "Localhost via 'UNIX socket'", - "%-.100s via 'TCP/IP'", - "Erro na negociação de acesso ao servidor", - "Conexão perdida com servidor MySQL durante 'query'", - "Comandos fora de sincronismo; você não pode executar este comando agora", - "Named pipe: %-.32s", - "Não pode esperar pelo 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)", - "Não pode abrir 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)", - "Não pode estabelecer o estado do 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)", - "Não pode inicializar conjunto de caracteres %-.32s (caminho %-.100s)", - "Obteve pacote maior do que 'max_allowed_packet' bytes", - "Embedded server" - "Error on SHOW SLAVE STATUS:", - "Error on SHOW SLAVE HOSTS:", - "Error connecting to slave:", - "Error connecting to master:", - "SSL connection error", - "Malformed packet", - "This client library is licensed only for use with MySQL servers having '%s' license", - "Invalid use of null pointer", - "Statement not prepared", - "No data supplied for parameters in prepared statement", - "Data truncated", - "No parameters exist in the statement", - "Invalid parameter number", - "Can't send long data for non-string/non-binary data types (parameter: %d)", - "Using unsupported buffer type: %d (parameter: %d)", - "Shared memory: %-.100s", - "Can't open shared memory; client could not create request event (%lu)", - "Can't open shared memory; no answer event received from server (%lu)", - "Can't open shared memory; server could not allocate file mapping (%lu)", - "Can't open shared memory; server could not get pointer to file mapping (%lu)", - "Can't open shared memory; client could not allocate file mapping (%lu)", - "Can't open shared memory; client could not get pointer to file mapping (%lu)", - "Can't open shared memory; client could not create %s event (%lu)", - "Can't open shared memory; no answer from server (%lu)", - "Can't open shared memory; cannot send request event to server (%lu)", - "Wrong or unknown protocol", - "Invalid connection handle", - "Connection using old (pre-4.1.1) authentication protocol refused (client option 'secure_auth' enabled)", - "Row retrieval was canceled by mysql_stmt_close() call", - "Attempt to read column without prior row fetch", - "Prepared statement contains no metadata", - "Attempt to read a row while there is no result set associated with the statement", - "This feature is not implemented yet", - "Lost connection to MySQL server at '%s', system error: %d", - "Statement closed indirectly because of a preceeding %s() call", - "The number of columns in the result set differs from the number of bound buffers. You must reset the statement, rebind the result set columns, and execute the statement again", - "" -}; - -#else /* ENGLISH */ const char *client_errors[]= { "Unknown MySQL error", @@ -182,7 +51,7 @@ const char *client_errors[]= "Error on SHOW SLAVE HOSTS:", "Error connecting to slave:", "Error connecting to master:", - "SSL connection error", + "SSL connection error: %-.100s", "Malformed packet", "This client library is licensed only for use with MySQL servers having '%s' license", "Invalid use of null pointer", @@ -214,10 +83,15 @@ const char *client_errors[]= "Lost connection to MySQL server at '%s', system error: %d", "Statement closed indirectly because of a preceeding %s() call", "The number of columns in the result set differs from the number of bound buffers. You must reset the statement, rebind the result set columns, and execute the statement again", + "This handle is already connected. Use a separate handle for each connection.", + "Authentication plugin '%s' cannot be loaded: %s", "" }; -#endif +const char** get_client_errmsgs() +{ + return client_errors; +} /* Register client error messages for use with my_error(). @@ -231,7 +105,7 @@ const char *client_errors[]= void init_client_errs(void) { - (void) my_error_register(client_errors, CR_ERROR_FIRST, CR_ERROR_LAST); + (void) my_error_register(get_client_errmsgs, CR_ERROR_FIRST, CR_ERROR_LAST); } diff --git a/libmysql/get_password.c b/libmysql/get_password.c index 63d4e68541f..55b7bf41cee 100644 --- a/libmysql/get_password.c +++ b/libmysql/get_password.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2004 MySQL AB +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -14,7 +14,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* ** Ask for a password from tty @@ -35,7 +35,7 @@ #include <pwd.h> #endif /* HAVE_PWD_H */ #else /* ! HAVE_GETPASS */ -#if !defined(__WIN__) && !defined(__NETWARE__) +#if !defined(__WIN__) #include <sys/ioctl.h> #ifdef HAVE_TERMIOS_H /* For tty-password */ #include <termios.h> @@ -54,9 +54,7 @@ #include <asm/termiobits.h> #endif #else -#ifndef __NETWARE__ #include <conio.h> -#endif /* __NETWARE__ */ #endif /* __WIN__ */ #endif /* HAVE_GETPASS */ @@ -64,16 +62,8 @@ #define getpass(A) getpassphrase(A) #endif -#if defined( __WIN__) || defined(__NETWARE__) +#if defined(__WIN__) /* were just going to fake it here and get input from the keyboard */ - -#ifdef __NETWARE__ -#undef _getch -#undef _cputs -#define _getch getcharacter -#define _cputs(A) putstring(A) -#endif - char *get_tty_password(const char *opt_message) { char to[80]; diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 562da594fea..9f5e6cc5f1a 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -33,7 +33,7 @@ #ifdef HAVE_PWD_H #include <pwd.h> #endif -#if !defined(MSDOS) && !defined(__WIN__) +#if !defined(__WIN__) #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> @@ -44,14 +44,14 @@ #ifdef HAVE_SYS_SELECT_H #include <sys/select.h> #endif -#endif /* !defined(MSDOS) && !defined(__WIN__) */ +#endif /* !defined(__WIN__) */ #ifdef HAVE_POLL #include <sys/poll.h> #endif #ifdef HAVE_SYS_UN_H #include <sys/un.h> #endif -#if defined(THREAD) && !defined(__WIN__) +#if !defined(__WIN__) #include <my_pthread.h> /* because of signal() */ #endif #ifndef INADDR_NONE @@ -73,7 +73,7 @@ ulong max_allowed_packet= 1024L*1024L*1024L; my_bool net_flush(NET *net); #endif -#if defined(MSDOS) || defined(__WIN__) +#if defined(__WIN__) /* socket_errno is defined in my_global.h for all platforms */ #define perror(A) #else @@ -94,6 +94,11 @@ sig_handler my_pipe_sig_handler(int sig); static my_bool mysql_client_init= 0; static my_bool org_my_init_done= 0; +typedef struct st_mysql_stmt_extension +{ + MEM_ROOT fields_mem_root; +} MYSQL_STMT_EXT; + /* Initialize the MySQL client library @@ -125,33 +130,33 @@ int STDCALL mysql_server_init(int argc __attribute__((unused)), if (my_init()) /* Will init threads */ return 1; init_client_errs(); + if (mysql_client_plugin_init()) + return 1; if (!mysql_port) { + char *env; + struct servent *serv_ptr __attribute__((unused)); + mysql_port = MYSQL_PORT; -#ifndef MSDOS - { - char *env; - struct servent *serv_ptr __attribute__((unused)); - /* - if builder specifically requested a default port, use that - (even if it coincides with our factory default). - only if they didn't do we check /etc/services (and, failing - on that, fall back to the factory default of 3306). - either default can be overridden by the environment variable - MYSQL_TCP_PORT, which in turn can be overridden with command - line options. - */ + /* + if builder specifically requested a default port, use that + (even if it coincides with our factory default). + only if they didn't do we check /etc/services (and, failing + on that, fall back to the factory default of 3306). + either default can be overridden by the environment variable + MYSQL_TCP_PORT, which in turn can be overridden with command + line options. + */ #if MYSQL_PORT_DEFAULT == 0 - if ((serv_ptr = getservbyname("mysql", "tcp"))) - mysql_port = (uint) ntohs((ushort) serv_ptr->s_port); -#endif - if ((env = getenv("MYSQL_TCP_PORT"))) - mysql_port =(uint) atoi(env); - } + if ((serv_ptr= getservbyname("mysql", "tcp"))) + mysql_port= (uint) ntohs((ushort) serv_ptr->s_port); #endif + if ((env= getenv("MYSQL_TCP_PORT"))) + mysql_port=(uint) atoi(env); } + if (!mysql_unix_port) { char *env; @@ -164,7 +169,7 @@ int STDCALL mysql_server_init(int argc __attribute__((unused)), mysql_unix_port = env; } mysql_debug(NullS); -#if defined(SIGPIPE) && !defined(__WIN__) && !defined(__NETWARE__) +#if defined(SIGPIPE) && !defined(__WIN__) (void) signal(SIGPIPE, SIG_IGN); #endif #ifdef EMBEDDED_LIBRARY @@ -172,10 +177,8 @@ int STDCALL mysql_server_init(int argc __attribute__((unused)), result= init_embedded_server(argc, argv, groups); #endif } -#ifdef THREAD else result= (int)my_thread_init(); /* Init if new thread */ -#endif return result; } @@ -197,6 +200,8 @@ void STDCALL mysql_server_end() if (!mysql_client_init) return; + mysql_client_plugin_deinit(); + #ifdef EMBEDDED_LIBRARY end_embedded_server(); #endif @@ -215,13 +220,6 @@ void STDCALL mysql_server_end() } mysql_client_init= org_my_init_done= 0; -#ifdef EMBEDDED_SERVER - if (stderror_file) - { - fclose(stderror_file); - stderror_file= 0; - } -#endif } static MYSQL_PARAMETERS mysql_internal_parameters= @@ -234,30 +232,14 @@ MYSQL_PARAMETERS *STDCALL mysql_get_parameters(void) my_bool STDCALL mysql_thread_init() { -#ifdef THREAD return my_thread_init(); -#else - return 0; -#endif } void STDCALL mysql_thread_end() { -#ifdef THREAD my_thread_end(); -#endif } -/* - Let the user specify that we don't want SIGPIPE; This doesn't however work - with threaded applications as we can have multiple read in progress. -*/ -static MYSQL* spawn_init(MYSQL* parent, const char* host, - unsigned int port, - const char* user, - const char* passwd); - - /* Expand wildcard to a sql string @@ -319,7 +301,7 @@ mysql_debug(const char *debug __attribute__((unused))) /************************************************************************** - Close the server connection if we get a SIGPIPE + Ignore SIGPIPE handler ARGSUSED **************************************************************************/ @@ -332,305 +314,6 @@ my_pipe_sig_handler(int sig __attribute__((unused))) #endif } -/* perform query on master */ -my_bool STDCALL mysql_master_query(MYSQL *mysql, const char *q, - unsigned long length) -{ - DBUG_ENTER("mysql_master_query"); - if (mysql_master_send_query(mysql, q, length)) - DBUG_RETURN(1); - DBUG_RETURN((*mysql->methods->read_query_result)(mysql)); -} - -my_bool STDCALL mysql_master_send_query(MYSQL *mysql, const char *q, - unsigned long length) -{ - MYSQL *master = mysql->master; - DBUG_ENTER("mysql_master_send_query"); - if (!master->net.vio && !mysql_real_connect(master,0,0,0,0,0,0,0)) - DBUG_RETURN(1); - master->reconnect= 1; - mysql->last_used_con = master; - DBUG_RETURN(simple_command(master, COM_QUERY, (const uchar*) q, length, 1)); -} - - -/* perform query on slave */ -my_bool STDCALL mysql_slave_query(MYSQL *mysql, const char *q, - unsigned long length) -{ - DBUG_ENTER("mysql_slave_query"); - if (mysql_slave_send_query(mysql, q, length)) - DBUG_RETURN(1); - DBUG_RETURN((*mysql->methods->read_query_result)(mysql)); -} - - -my_bool STDCALL mysql_slave_send_query(MYSQL *mysql, const char *q, - unsigned long length) -{ - MYSQL* last_used_slave, *slave_to_use = 0; - DBUG_ENTER("mysql_slave_send_query"); - - if ((last_used_slave = mysql->last_used_slave)) - slave_to_use = last_used_slave->next_slave; - else - slave_to_use = mysql->next_slave; - /* - Next_slave is always safe to use - we have a circular list of slaves - if there are no slaves, mysql->next_slave == mysql - */ - mysql->last_used_con = mysql->last_used_slave = slave_to_use; - if (!slave_to_use->net.vio && !mysql_real_connect(slave_to_use, 0,0,0, - 0,0,0,0)) - DBUG_RETURN(1); - slave_to_use->reconnect= 1; - DBUG_RETURN(simple_command(slave_to_use, COM_QUERY, (const uchar*) q, - length, 1)); -} - - -/* enable/disable parsing of all queries to decide - if they go on master or slave */ -void STDCALL mysql_enable_rpl_parse(MYSQL* mysql) -{ - mysql->options.rpl_parse = 1; -} - -void STDCALL mysql_disable_rpl_parse(MYSQL* mysql) -{ - mysql->options.rpl_parse = 0; -} - -/* get the value of the parse flag */ -int STDCALL mysql_rpl_parse_enabled(MYSQL* mysql) -{ - return mysql->options.rpl_parse; -} - -/* enable/disable reads from master */ -void STDCALL mysql_enable_reads_from_master(MYSQL* mysql) -{ - mysql->options.no_master_reads = 0; -} - -void STDCALL mysql_disable_reads_from_master(MYSQL* mysql) -{ - mysql->options.no_master_reads = 1; -} - -/* get the value of the master read flag */ -my_bool STDCALL mysql_reads_from_master_enabled(MYSQL* mysql) -{ - return !(mysql->options.no_master_reads); -} - - -/* - We may get an error while doing replication internals. - In this case, we add a special explanation to the original - error -*/ - -static void expand_error(MYSQL* mysql, int error) -{ - char tmp[MYSQL_ERRMSG_SIZE]; - char *p; - uint err_length; - strmake(tmp, mysql->net.last_error, MYSQL_ERRMSG_SIZE-1); - p = strmake(mysql->net.last_error, ER(error), MYSQL_ERRMSG_SIZE-1); - err_length= (uint) (p - mysql->net.last_error); - strmake(p, tmp, MYSQL_ERRMSG_SIZE-1 - err_length); - mysql->net.last_errno = error; -} - -/* - This function assumes we have just called SHOW SLAVE STATUS and have - read the given result and row -*/ - -static my_bool get_master(MYSQL* mysql, MYSQL_RES* res, MYSQL_ROW row) -{ - MYSQL* master; - DBUG_ENTER("get_master"); - if (mysql_num_fields(res) < 3) - DBUG_RETURN(1); /* safety */ - - /* use the same username and password as the original connection */ - if (!(master = spawn_init(mysql, row[0], atoi(row[2]), 0, 0))) - DBUG_RETURN(1); - mysql->master = master; - DBUG_RETURN(0); -} - - -/* - Assuming we already know that mysql points to a master connection, - retrieve all the slaves -*/ - -static my_bool get_slaves_from_master(MYSQL* mysql) -{ - MYSQL_RES* res = 0; - MYSQL_ROW row; - my_bool error = 1; - int has_auth_info; - int port_ind; - DBUG_ENTER("get_slaves_from_master"); - - if (!mysql->net.vio && !mysql_real_connect(mysql,0,0,0,0,0,0,0)) - { - expand_error(mysql, CR_PROBE_MASTER_CONNECT); - DBUG_RETURN(1); - } - mysql->reconnect= 1; - - if (mysql_query(mysql, "SHOW SLAVE HOSTS") || - !(res = mysql_store_result(mysql))) - { - expand_error(mysql, CR_PROBE_SLAVE_HOSTS); - DBUG_RETURN(1); - } - - switch (mysql_num_fields(res)) { - case 5: - has_auth_info = 0; - port_ind=2; - break; - case 7: - has_auth_info = 1; - port_ind=4; - break; - default: - goto err; - } - - while ((row = mysql_fetch_row(res))) - { - MYSQL* slave; - const char* tmp_user, *tmp_pass; - - if (has_auth_info) - { - tmp_user = row[2]; - tmp_pass = row[3]; - } - else - { - tmp_user = mysql->user; - tmp_pass = mysql->passwd; - } - - if (!(slave = spawn_init(mysql, row[1], atoi(row[port_ind]), - tmp_user, tmp_pass))) - goto err; - - /* Now add slave into the circular linked list */ - slave->next_slave = mysql->next_slave; - mysql->next_slave = slave; - } - error = 0; -err: - if (res) - mysql_free_result(res); - DBUG_RETURN(error); -} - - -my_bool STDCALL mysql_rpl_probe(MYSQL* mysql) -{ - MYSQL_RES *res= 0; - MYSQL_ROW row; - my_bool error= 1; - DBUG_ENTER("mysql_rpl_probe"); - - /* - First determine the replication role of the server we connected to - the most reliable way to do this is to run SHOW SLAVE STATUS and see - if we have a non-empty master host. This is still not fool-proof - - it is not a sin to have a master that has a dormant slave thread with - a non-empty master host. However, it is more reliable to check - for empty master than whether the slave thread is actually running - */ - if (mysql_query(mysql, "SHOW SLAVE STATUS") || - !(res = mysql_store_result(mysql))) - { - expand_error(mysql, CR_PROBE_SLAVE_STATUS); - DBUG_RETURN(1); - } - - row= mysql_fetch_row(res); - /* - Check master host for emptiness/NULL - For MySQL 4.0 it's enough to check for row[0] - */ - if (row && row[0] && *(row[0])) - { - /* this is a slave, ask it for the master */ - if (get_master(mysql, res, row) || get_slaves_from_master(mysql)) - goto err; - } - else - { - mysql->master = mysql; - if (get_slaves_from_master(mysql)) - goto err; - } - - error = 0; -err: - if (res) - mysql_free_result(res); - DBUG_RETURN(error); -} - - -/* - Make a not so fool-proof decision on where the query should go, to - the master or the slave. Ideally the user should always make this - decision himself with mysql_master_query() or mysql_slave_query(). - However, to be able to more easily port the old code, we support the - option of an educated guess - this should work for most applications, - however, it may make the wrong decision in some particular cases. If - that happens, the user would have to change the code to call - mysql_master_query() or mysql_slave_query() explicitly in the place - where we have made the wrong decision -*/ - -enum mysql_rpl_type -STDCALL mysql_rpl_query_type(const char* q, int len) -{ - const char *q_end= q + len; - for (; q < q_end; ++q) - { - char c; - if (my_isalpha(&my_charset_latin1, (c= *q))) - { - switch (my_tolower(&my_charset_latin1,c)) { - case 'i': /* insert */ - case 'u': /* update or unlock tables */ - case 'l': /* lock tables or load data infile */ - case 'd': /* drop or delete */ - case 'a': /* alter */ - return MYSQL_RPL_MASTER; - case 'c': /* create or check */ - return my_tolower(&my_charset_latin1,q[1]) == 'h' ? MYSQL_RPL_ADMIN : - MYSQL_RPL_MASTER; - case 's': /* select or show */ - return my_tolower(&my_charset_latin1,q[1]) == 'h' ? MYSQL_RPL_ADMIN : - MYSQL_RPL_SLAVE; - case 'f': /* flush */ - case 'r': /* repair */ - case 'g': /* grant */ - return MYSQL_RPL_ADMIN; - default: - return MYSQL_RPL_SLAVE; - } - } - } - return MYSQL_RPL_MASTER; /* By default, send to master */ -} - /************************************************************************** Connect to sql server @@ -649,7 +332,7 @@ mysql_connect(MYSQL *mysql,const char *host, if (!(res=mysql_real_connect(mysql,host,user,passwd,NullS,0,NullS,0))) { if (mysql->free_me) - my_free((uchar*) mysql,MYF(0)); + my_free(mysql); } mysql->reconnect= 1; DBUG_RETURN(res); @@ -662,44 +345,14 @@ mysql_connect(MYSQL *mysql,const char *host, Change user and database **************************************************************************/ -int cli_read_change_user_result(MYSQL *mysql, char *buff, const char *passwd) -{ - NET *net= &mysql->net; - ulong pkt_length; - - pkt_length= cli_safe_read(mysql); - - if (pkt_length == packet_error) - return 1; - - if (pkt_length == 1 && net->read_pos[0] == 254 && - mysql->server_capabilities & CLIENT_SECURE_CONNECTION) - { - /* - By sending this very specific reply server asks us to send scrambled - password in old format. The reply contains scramble_323. - */ - scramble_323(buff, mysql->scramble, passwd); - if (my_net_write(net, (uchar*) buff, SCRAMBLE_LENGTH_323 + 1) || - net_flush(net)) - { - set_mysql_error(mysql, CR_SERVER_LOST, unknown_sqlstate); - return 1; - } - /* Read what server thinks about out new auth message report */ - if (cli_safe_read(mysql) == packet_error) - return 1; - } - return 0; -} - my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, const char *passwd, const char *db) { - char buff[USERNAME_LENGTH+SCRAMBLED_PASSWORD_CHAR_LENGTH+NAME_LEN+2]; - char *end= buff; int rc; CHARSET_INFO *saved_cs= mysql->charset; + char *saved_user= mysql->user; + char *saved_passwd= mysql->passwd; + char *saved_db= mysql->db; DBUG_ENTER("mysql_change_user"); @@ -713,49 +366,11 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, /* Use an empty string instead of NULL. */ - if (!user) - user=""; - if (!passwd) - passwd=""; - - /* - Store user into the buffer. - Advance position as strmake returns a pointer to the closing NUL. - */ - end= strmake(end, user, USERNAME_LENGTH) + 1; - - /* write scrambled password according to server capabilities */ - if (passwd[0]) - { - if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION) - { - *end++= SCRAMBLE_LENGTH; - scramble(end, mysql->scramble, passwd); - end+= SCRAMBLE_LENGTH; - } - else - { - scramble_323(end, mysql->scramble, passwd); - end+= SCRAMBLE_LENGTH_323 + 1; - } - } - else - *end++= '\0'; /* empty password */ - /* Add database if needed */ - end= strmake(end, db ? db : "", NAME_LEN) + 1; - - /* Add character set number. */ + mysql->user= (char*)(user ? user : ""); + mysql->passwd= (char*)(passwd ? passwd : ""); + mysql->db= 0; - if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION) - { - int2store(end, (ushort) mysql->charset->number); - end+= 2; - } - - /* Write authentication package */ - simple_command(mysql,COM_CHANGE_USER, (uchar*) buff, (ulong) (end-buff), 1); - - rc= (*mysql->methods->read_change_user_result)(mysql, buff, passwd); + rc= run_plugin_auth(mysql, 0, 0, 0, db); /* The server will close all statements no matter was the attempt @@ -765,18 +380,21 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, if (rc == 0) { /* Free old connect information */ - my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR)); + my_free(saved_user); + my_free(saved_passwd); + my_free(saved_db); /* alloc new connect information */ - mysql->user= my_strdup(user,MYF(MY_WME)); - mysql->passwd=my_strdup(passwd,MYF(MY_WME)); - mysql->db= db ? my_strdup(db,MYF(MY_WME)) : 0; + mysql->user= my_strdup(mysql->user, MYF(MY_WME)); + mysql->passwd= my_strdup(mysql->passwd, MYF(MY_WME)); + mysql->db= db ? my_strdup(db, MYF(MY_WME)) : 0; } else { mysql->charset= saved_cs; + mysql->user= saved_user; + mysql->passwd= saved_passwd; + mysql->db= saved_db; } DBUG_RETURN(rc); @@ -787,15 +405,7 @@ struct passwd *getpwuid(uid_t); char* getlogin(void); #endif -#if defined(__NETWARE__) -/* Default to value of USER on NetWare, if unset use "UNKNOWN_USER" */ -void read_user_name(char *name) -{ - char *str=getenv("USER"); - strmake(name, str ? str : "UNKNOWN_USER", USERNAME_LENGTH); -} - -#elif !defined(MSDOS) && ! defined(VMS) && !defined(__WIN__) +#if !defined(__WIN__) void read_user_name(char *name) { @@ -825,7 +435,7 @@ void read_user_name(char *name) DBUG_VOID_RETURN; } -#else /* If MSDOS || VMS */ +#else /* If Windows */ void read_user_name(char *name) { @@ -867,7 +477,7 @@ my_bool handle_local_infile(MYSQL *mysql, const char *net_filename) if ((*options->local_infile_init)(&li_ptr, net_filename, options->local_infile_userdata)) { - VOID(my_net_write(net,(const uchar*) "",0)); /* Server needs one packet */ + (void) my_net_write(net,(const uchar*) "",0); /* Server needs one packet */ net_flush(net); strmov(net->sqlstate, unknown_sqlstate); net->last_errno= @@ -912,7 +522,7 @@ my_bool handle_local_infile(MYSQL *mysql, const char *net_filename) err: /* free up memory allocated with _init, usually */ (*options->local_infile_end)(li_ptr); - my_free(buf, MYF(0)); + my_free(buf); DBUG_RETURN(result); } @@ -1023,7 +633,7 @@ static void default_local_infile_end(void *ptr) { if (data->fd >= 0) my_close(data->fd, MYF(MY_WME)); - my_free(ptr, MYF(MY_WME)); + my_free(ptr); } } @@ -1095,68 +705,6 @@ mysql_query(MYSQL *mysql, const char *query) } -static MYSQL* spawn_init(MYSQL* parent, const char* host, - unsigned int port, const char* user, - const char* passwd) -{ - MYSQL* child; - DBUG_ENTER("spawn_init"); - if (!(child= mysql_init(0))) - DBUG_RETURN(0); - - child->options.user= my_strdup((user) ? user : - (parent->user ? parent->user : - parent->options.user), MYF(0)); - child->options.password= my_strdup((passwd) ? passwd : - (parent->passwd ? - parent->passwd : - parent->options.password), MYF(0)); - child->options.port= port; - child->options.host= my_strdup((host) ? host : - (parent->host ? - parent->host : - parent->options.host), MYF(0)); - if (parent->db) - child->options.db= my_strdup(parent->db, MYF(0)); - else if (parent->options.db) - child->options.db= my_strdup(parent->options.db, MYF(0)); - - /* - rpl_pivot is set to 1 in mysql_init(); Reset it as we are not doing - replication here - */ - child->rpl_pivot= 0; - DBUG_RETURN(child); -} - - -int -STDCALL mysql_set_master(MYSQL* mysql, const char* host, - unsigned int port, const char* user, - const char* passwd) -{ - if (mysql->master != mysql && !mysql->master->rpl_pivot) - mysql_close(mysql->master); - if (!(mysql->master = spawn_init(mysql, host, port, user, passwd))) - return 1; - return 0; -} - - -int -STDCALL mysql_add_slave(MYSQL* mysql, const char* host, - unsigned int port, - const char* user, - const char* passwd) -{ - MYSQL* slave; - if (!(slave = spawn_init(mysql, host, port, user, passwd))) - return 1; - slave->next_slave = mysql->next_slave; - mysql->next_slave = slave; - return 0; -} - /************************************************************************** Return next field of the query results **************************************************************************/ @@ -1485,17 +1033,17 @@ MYSQL_FIELD_OFFSET STDCALL mysql_field_tell(MYSQL_RES *res) unsigned int STDCALL mysql_field_count(MYSQL *mysql) { - return mysql->last_used_con->field_count; + return mysql->field_count; } my_ulonglong STDCALL mysql_affected_rows(MYSQL *mysql) { - return mysql->last_used_con->affected_rows; + return mysql->affected_rows; } my_ulonglong STDCALL mysql_insert_id(MYSQL *mysql) { - return mysql->last_used_con->insert_id; + return mysql->insert_id; } const char *STDCALL mysql_sqlstate(MYSQL *mysql) @@ -1541,11 +1089,7 @@ void STDCALL mysql_get_character_set_info(MYSQL *mysql, MY_CHARSET_INFO *csinfo) uint STDCALL mysql_thread_safe(void) { -#ifdef THREAD return 1; -#else - return 0; -#endif } @@ -1860,7 +1404,6 @@ my_bool cli_read_prepare_result(MYSQL *mysql, MYSQL_STMT *stmt) MYSQL_DATA *fields_data; DBUG_ENTER("cli_read_prepare_result"); - mysql= mysql->last_used_con; if ((packet_length= cli_safe_read(mysql)) == packet_error) DBUG_RETURN(1); mysql->warning_count= 0; @@ -1942,11 +1485,16 @@ mysql_stmt_init(MYSQL *mysql) MYSQL_STMT *stmt; DBUG_ENTER("mysql_stmt_init"); - if (!(stmt= (MYSQL_STMT *) my_malloc(sizeof(MYSQL_STMT), + if (!(stmt= + (MYSQL_STMT *) my_malloc(sizeof (MYSQL_STMT), + MYF(MY_WME | MY_ZEROFILL))) || + !(stmt->extension= + (MYSQL_STMT_EXT *) my_malloc(sizeof (MYSQL_STMT_EXT), MYF(MY_WME | MY_ZEROFILL)))) { set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate); - DBUG_RETURN(0); + my_free(stmt); + DBUG_RETURN(NULL); } init_alloc_root(&stmt->mem_root, 2048, 2048); @@ -1961,6 +1509,8 @@ mysql_stmt_init(MYSQL *mysql) strmov(stmt->sqlstate, not_error_sqlstate); /* The rest of statement members was bzeroed inside malloc */ + init_alloc_root(&stmt->extension->fields_mem_root, 2048, 0); + DBUG_RETURN(stmt); } @@ -2033,6 +1583,7 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) stmt->bind_param_done= stmt->bind_result_done= FALSE; stmt->param_count= stmt->field_count= 0; free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC)); + free_root(&stmt->extension->fields_mem_root, MYF(0)); int4store(buff, stmt->stmt_id); @@ -2093,19 +1644,21 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) static void alloc_stmt_fields(MYSQL_STMT *stmt) { MYSQL_FIELD *fields, *field, *end; - MEM_ROOT *alloc= &stmt->mem_root; - MYSQL *mysql= stmt->mysql->last_used_con; + MEM_ROOT *fields_mem_root= &stmt->extension->fields_mem_root; + MYSQL *mysql= stmt->mysql; - stmt->field_count= mysql->field_count; + DBUG_ASSERT(stmt->field_count); + + free_root(fields_mem_root, MYF(0)); /* Get the field information for non-select statements like SHOW and DESCRIBE commands */ - if (!(stmt->fields= (MYSQL_FIELD *) alloc_root(alloc, + if (!(stmt->fields= (MYSQL_FIELD *) alloc_root(fields_mem_root, sizeof(MYSQL_FIELD) * stmt->field_count)) || - !(stmt->bind= (MYSQL_BIND *) alloc_root(alloc, + !(stmt->bind= (MYSQL_BIND *) alloc_root(fields_mem_root, sizeof(MYSQL_BIND) * stmt->field_count))) { @@ -2117,18 +1670,39 @@ static void alloc_stmt_fields(MYSQL_STMT *stmt) field= stmt->fields; field && fields < end; fields++, field++) { - field->db = strdup_root(alloc,fields->db); - field->table = strdup_root(alloc,fields->table); - field->org_table= strdup_root(alloc,fields->org_table); - field->name = strdup_root(alloc,fields->name); - field->org_name = strdup_root(alloc,fields->org_name); - field->charsetnr= fields->charsetnr; - field->length = fields->length; - field->type = fields->type; - field->flags = fields->flags; - field->decimals = fields->decimals; - field->def = fields->def ? strdup_root(alloc,fields->def): 0; - field->max_length= 0; + *field= *fields; /* To copy all numeric parts. */ + field->catalog= strmake_root(fields_mem_root, + fields->catalog, + fields->catalog_length); + field->db= strmake_root(fields_mem_root, + fields->db, + fields->db_length); + field->table= strmake_root(fields_mem_root, + fields->table, + fields->table_length); + field->org_table= strmake_root(fields_mem_root, + fields->org_table, + fields->org_table_length); + field->name= strmake_root(fields_mem_root, + fields->name, + fields->name_length); + field->org_name= strmake_root(fields_mem_root, + fields->org_name, + fields->org_name_length); + if (fields->def) + { + field->def= strmake_root(fields_mem_root, + fields->def, + fields->def_length); + field->def_length= fields->def_length; + } + else + { + field->def= NULL; + field->def_length= 0; + } + field->extension= 0; /* Avoid dangling links. */ + field->max_length= 0; /* max_length is set in mysql_stmt_store_result() */ } } @@ -2481,7 +2055,6 @@ static my_bool execute(MYSQL_STMT *stmt, char *packet, ulong length) DBUG_ENTER("execute"); DBUG_DUMP("packet", (uchar *) packet, length); - mysql->last_used_con= mysql; int4store(buff, stmt->stmt_id); /* Send stmt id to server */ buff[4]= (char) stmt->flags; int4store(buff+5, 1); /* iteration count */ @@ -2580,7 +2153,7 @@ int cli_stmt_execute(MYSQL_STMT *stmt) } result= execute(stmt, param_data, length); stmt->send_types_to_server=0; - my_free(param_data, MYF(MY_WME)); + my_free(param_data); DBUG_RETURN(result); } DBUG_RETURN((int) execute(stmt,0,0)); @@ -2845,6 +2418,9 @@ static void reinit_result_set_metadata(MYSQL_STMT *stmt) prepared statements can't send result set metadata for these queries on prepare stage. Read it now. */ + + stmt->field_count= stmt->mysql->field_count; + alloc_stmt_fields(stmt); } else @@ -2862,12 +2438,39 @@ static void reinit_result_set_metadata(MYSQL_STMT *stmt) previous branch always works. TODO: send metadata only when it's really necessary and add a warning 'Metadata changed' when it's sent twice. - */ + */ update_stmt_fields(stmt); } } +static void prepare_to_fetch_result(MYSQL_STMT *stmt) +{ + if (stmt->server_status & SERVER_STATUS_CURSOR_EXISTS) + { + stmt->mysql->status= MYSQL_STATUS_READY; + stmt->read_row_func= stmt_read_row_from_cursor; + } + else if (stmt->flags & CURSOR_TYPE_READ_ONLY) + { + /* + This is a single-row result set, a result set with no rows, EXPLAIN, + SHOW VARIABLES, or some other command which either a) bypasses the + cursors framework in the server and writes rows directly to the + network or b) is more efficient if all (few) result set rows are + precached on client and server's resources are freed. + */ + mysql_stmt_store_result(stmt); + } + else + { + stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled; + stmt->unbuffered_fetch_cancelled= FALSE; + stmt->read_row_func= stmt_read_row_unbuffered; + } +} + + /* Send placeholders data to server (if there are placeholders) and execute prepared statement. @@ -2935,28 +2538,7 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) if (mysql->field_count) { reinit_result_set_metadata(stmt); - if (stmt->server_status & SERVER_STATUS_CURSOR_EXISTS) - { - mysql->status= MYSQL_STATUS_READY; - stmt->read_row_func= stmt_read_row_from_cursor; - } - else if (stmt->flags & CURSOR_TYPE_READ_ONLY) - { - /* - This is a single-row result set, a result set with no rows, EXPLAIN, - SHOW VARIABLES, or some other command which either a) bypasses the - cursors framework in the server and writes rows directly to the - network or b) is more efficient if all (few) result set rows are - precached on client and server's resources are freed. - */ - mysql_stmt_store_result(stmt); - } - else - { - stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled; - stmt->unbuffered_fetch_cancelled= FALSE; - stmt->read_row_func= stmt_read_row_unbuffered; - } + prepare_to_fetch_result(stmt); } DBUG_RETURN(test(stmt->last_errno)); } @@ -3754,12 +3336,13 @@ static void fetch_long_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, param output buffer descriptor field column metadata value column data - width default number of significant digits used when converting - float/double to string + type either MY_GCVT_ARG_FLOAT or MY_GCVT_ARG_DOUBLE. + Affects the maximum number of significant digits + returned by my_gcvt(). */ static void fetch_float_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, - double value, int width) + double value, my_gcvt_arg_type type) { char *buffer= (char *)param->buffer; double val64 = (value < 0 ? -floor(-value) : floor(value)); @@ -3843,42 +3426,24 @@ static void fetch_float_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, floating point -> string conversion nicely, honor all typecodes and param->offset possibly set in mysql_stmt_fetch_column */ - char buff[MAX_DOUBLE_STRING_REP_LENGTH]; - char *end; - /* TODO: move this to a header shared between client and server. */ -#define NOT_FIXED_DEC 31 + char buff[FLOATING_POINT_BUFFER]; + size_t len; if (field->decimals >= NOT_FIXED_DEC) -#undef NOT_FIXED_DEC - { - /* - DBL_DIG below is to ensure that the server and client has the same - precisions. This will ensure that on the same machine you get the - same value as a string independent of the protocol you use. - */ - sprintf(buff, "%-*.*g", (int) min(sizeof(buff)-1, - param->buffer_length), - min(DBL_DIG, width), value); - end= strcend(buff, ' '); - *end= 0; - } + len= my_gcvt(value, type, + (int) min(sizeof(buff)-1, param->buffer_length), + buff, NULL); else - { - sprintf(buff, "%.*f", (int) field->decimals, value); - end= strend(buff); - } + len= my_fcvt(value, (int) field->decimals, buff, NULL); + if (field->flags & ZEROFILL_FLAG && len < field->length && + field->length < MAX_DOUBLE_STRING_REP_LENGTH - 1) { - size_t length= end - buff; - if (field->flags & ZEROFILL_FLAG && length < field->length && - field->length < MAX_DOUBLE_STRING_REP_LENGTH - 1) - { - bmove_upp((uchar*) buff + field->length, (uchar*) buff + length, - length); - bfill((char*) buff, field->length - length, '0'); - length= field->length; - } - fetch_string_with_conversion(param, buff, length); + bmove_upp((uchar*) buff + field->length, (uchar*) buff + len, + len); + bfill((char*) buff, field->length - len, '0'); + len= field->length; } + fetch_string_with_conversion(param, buff, len); break; } @@ -3923,7 +3488,7 @@ static void fetch_datetime_with_conversion(MYSQL_BIND *param, { ulonglong value= TIME_to_ulonglong(my_time); fetch_float_with_conversion(param, field, - ulonglong2double(value), DBL_DIG); + ulonglong2double(value), MY_GCVT_ARG_DOUBLE); break; } case MYSQL_TYPE_TINY: @@ -4017,7 +3582,7 @@ static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, { float value; float4get(value,*row); - fetch_float_with_conversion(param, field, value, FLT_DIG); + fetch_float_with_conversion(param, field, value, MY_GCVT_ARG_FLOAT); *row+= 4; break; } @@ -4025,7 +3590,7 @@ static void fetch_result_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, { double value; float8get(value,*row); - fetch_float_with_conversion(param, field, value, DBL_DIG); + fetch_float_with_conversion(param, field, value, MY_GCVT_ARG_DOUBLE); *row+= 8; break; } @@ -4708,7 +4273,6 @@ int cli_read_binary_rows(MYSQL_STMT *stmt) } net = &mysql->net; - mysql= mysql->last_used_con; while ((pkt_len= cli_safe_read(mysql)) != packet_error) { @@ -4806,8 +4370,6 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) DBUG_RETURN(1); } - mysql= mysql->last_used_con; - if (!stmt->field_count) DBUG_RETURN(0); @@ -5014,7 +4576,7 @@ static my_bool reset_stmt_handle(MYSQL_STMT *stmt, uint flags) if (stmt->field_count && mysql->status != MYSQL_STATUS_READY) { /* There is a result set and it belongs to this statement */ - (*mysql->methods->flush_use_result)(mysql); + (*mysql->methods->flush_use_result)(mysql, FALSE); if (mysql->unbuffered_fetch_owner) *mysql->unbuffered_fetch_owner= TRUE; mysql->status= MYSQL_STATUS_READY; @@ -5077,6 +4639,7 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) free_root(&stmt->result.alloc, MYF(0)); free_root(&stmt->mem_root, MYF(0)); + free_root(&stmt->extension->fields_mem_root, MYF(0)); if (mysql) { @@ -5098,7 +4661,7 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) Flush result set of the connection. If it does not belong to this statement, set a warning. */ - (*mysql->methods->flush_use_result)(mysql); + (*mysql->methods->flush_use_result)(mysql, TRUE); if (mysql->unbuffered_fetch_owner) *mysql->unbuffered_fetch_owner= TRUE; mysql->status= MYSQL_STATUS_READY; @@ -5111,7 +4674,8 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) } } - my_free((uchar*) stmt, MYF(MY_WME)); + my_free(stmt->extension); + my_free(stmt); DBUG_RETURN(test(rc)); } @@ -5217,8 +4781,7 @@ my_bool STDCALL mysql_more_results(MYSQL *mysql) my_bool res; DBUG_ENTER("mysql_more_results"); - res= ((mysql->last_used_con->server_status & SERVER_MORE_RESULTS_EXISTS) ? - 1: 0); + res= ((mysql->server_status & SERVER_MORE_RESULTS_EXISTS) ? 1: 0); DBUG_PRINT("exit",("More results exists ? %d", res)); DBUG_RETURN(res); } @@ -5240,13 +4803,56 @@ int STDCALL mysql_next_result(MYSQL *mysql) net_clear_error(&mysql->net); mysql->affected_rows= ~(my_ulonglong) 0; - if (mysql->last_used_con->server_status & SERVER_MORE_RESULTS_EXISTS) + if (mysql->server_status & SERVER_MORE_RESULTS_EXISTS) DBUG_RETURN((*mysql->methods->next_result)(mysql)); DBUG_RETURN(-1); /* No more results */ } +int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt) +{ + MYSQL *mysql= stmt->mysql; + int rc; + DBUG_ENTER("mysql_stmt_next_result"); + + if (!mysql) + DBUG_RETURN(1); + + if (stmt->last_errno) + DBUG_RETURN(stmt->last_errno); + + if (mysql->server_status & SERVER_MORE_RESULTS_EXISTS) + { + if (reset_stmt_handle(stmt, RESET_STORE_RESULT)) + DBUG_RETURN(1); + } + + rc= mysql_next_result(mysql); + + if (rc) + { + set_stmt_errmsg(stmt, &mysql->net); + DBUG_RETURN(rc); + } + + if (mysql->status == MYSQL_STATUS_GET_RESULT) + mysql->status= MYSQL_STATUS_STATEMENT_GET_RESULT; + + stmt->state= MYSQL_STMT_EXECUTE_DONE; + stmt->bind_result_done= FALSE; + stmt->field_count= mysql->field_count; + + if (mysql->field_count) + { + alloc_stmt_fields(stmt); + prepare_to_fetch_result(stmt); + } + + DBUG_RETURN(0); +} + + MYSQL_RES * STDCALL mysql_use_result(MYSQL *mysql) { return (*mysql->methods->use_result)(mysql); diff --git a/libmysql/libmysql.def b/libmysql/libmysql.def index 81f86dc8726..ce85d2a4086 100644 --- a/libmysql/libmysql.def +++ b/libmysql/libmysql.def @@ -1,33 +1,7 @@ LIBRARY LIBMYSQL VERSION 6.0 EXPORTS - _dig_vec_lower - _dig_vec_upper - bmove_upp - delete_dynamic - free_defaults - getopt_compare_strings - getopt_ull_limit_value - handle_options - init_dynamic_array - insert_dynamic - int2str - is_prefix - list_add - list_delete load_defaults - my_end - my_getopt_print_errors - my_init - my_malloc - my_memdup - my_no_flags_free - my_path - mysql_get_parameters - my_print_help - my_print_variables - my_realloc - my_strdup mysql_thread_end mysql_thread_init myodbc_remove_escape @@ -117,37 +91,16 @@ EXPORTS mysql_stmt_sqlstate mysql_sqlstate mysql_get_server_version - set_dynamic - strcend - strcont - strdup_root - strfill - strinstr - strmake - strmov - strxmov mysql_stmt_prepare mysql_stmt_init mysql_stmt_insert_id mysql_stmt_attr_get mysql_stmt_attr_set mysql_stmt_field_count - client_errors mysql_set_local_infile_default mysql_set_local_infile_handler - mysql_disable_reads_from_master - mysql_disable_rpl_parse - mysql_enable_reads_from_master - mysql_enable_rpl_parse - mysql_master_query - mysql_rpl_parse_enabled - mysql_rpl_probe - mysql_rpl_query_type - mysql_slave_query mysql_embedded mysql_server_init mysql_server_end mysql_set_character_set mysql_get_character_set_info - get_defaults_options - modify_defaults_file diff --git a/libmysql/manager.c b/libmysql/manager.c deleted file mode 100644 index 335323cb31d..00000000000 --- a/libmysql/manager.c +++ /dev/null @@ -1,269 +0,0 @@ -/* Copyright (c) 2001-2004, 2006-2008 MySQL AB, 2009 Sun Microsystems, Inc. - Use is subject to license terms. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation. - - There are special exceptions to the terms and conditions of the GPL as it - is applied to this software. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include <my_global.h> -#if defined(THREAD) -#include <my_pthread.h> /* because of signal() */ -#endif -#include "mysql.h" -#include "mysql_version.h" -#include "mysqld_error.h" -#include <my_sys.h> -#include <mysys_err.h> -#include <m_string.h> -#include <m_ctype.h> -#include <my_net.h> -#include <errmsg.h> -#include <violite.h> -#include <sys/stat.h> -#include <signal.h> -#include <errno.h> - -#if defined(__NETWARE__) -#include <netdb.h> -#include <sys/select.h> -#include <sys/utsname.h> -#elif !defined( __WIN__) -#include <sys/resource.h> -#ifdef HAVE_SYS_UN_H -# include <sys/un.h> -#endif -#include <netdb.h> -#ifdef HAVE_SELECT_H -# include <select.h> -#endif -#ifdef HAVE_SYS_SELECT_H -#include <sys/select.h> -#endif -#include <sys/utsname.h> -#endif /* __WIN__ */ - -#ifndef INADDR_NONE -#define INADDR_NONE -1 -#endif - -#define RES_BUF_SHIFT 5 -#define NET_BUF_SIZE 2048 - -MYSQL_MANAGER* STDCALL mysql_manager_init(MYSQL_MANAGER* con) -{ - int net_buf_size=NET_BUF_SIZE; - if (!con) - { - if (!(con=(MYSQL_MANAGER*)my_malloc(sizeof(*con)+net_buf_size, - MYF(MY_WME|MY_ZEROFILL)))) - return 0; - con->free_me=1; - con->net_buf=(char*)con+sizeof(*con); - } - else - { - bzero((char*)con,sizeof(*con)); - if (!(con->net_buf=my_malloc(net_buf_size,MYF(0)))) - return 0; - } - con->net_buf_pos=con->net_data_end=con->net_buf; - con->net_buf_size=net_buf_size; - return con; -} - -MYSQL_MANAGER* STDCALL mysql_manager_connect(MYSQL_MANAGER* con, - const char* host, - const char* user, - const char* passwd, - unsigned int port) -{ - my_socket sock; - struct sockaddr_in sock_addr; - in_addr_t ip_addr; - char msg_buf[MAX_MYSQL_MANAGER_MSG]; - int msg_len; - Vio* vio; - my_bool not_used; - - if (!host) - host="localhost"; - if (!user) - user="root"; - if (!passwd) - passwd=""; - - if ((sock=(my_socket)socket(AF_INET,SOCK_STREAM,0)) == INVALID_SOCKET) - { - con->last_errno=errno; - strmov(con->last_error,"Cannot create socket"); - goto err; - } - if (!(vio=vio_new(sock,VIO_TYPE_TCPIP,FALSE))) - { - con->last_errno=ENOMEM; - strmov(con->last_error,"Cannot create network I/O object"); - goto err; - } - vio_blocking(vio, TRUE, ¬_used); - my_net_init(&con->net,vio); - bzero((char*) &sock_addr,sizeof(sock_addr)); - sock_addr.sin_family = AF_INET; - if ((int) (ip_addr = inet_addr(host)) != (int) INADDR_NONE) - { - memcpy_fixed(&sock_addr.sin_addr,&ip_addr,sizeof(ip_addr)); - } - else - { - int tmp_errno; - struct hostent tmp_hostent,*hp; - char buff2[GETHOSTBYNAME_BUFF_SIZE]; - hp = my_gethostbyname_r(host,&tmp_hostent,buff2,sizeof(buff2), - &tmp_errno); - if (!hp) - { - con->last_errno=tmp_errno; - sprintf(con->last_error,"Could not resolve host '%-.64s'",host); - my_gethostbyname_r_free(); - goto err; - } - memcpy(&sock_addr.sin_addr,hp->h_addr, (size_t) hp->h_length); - my_gethostbyname_r_free(); - } - sock_addr.sin_port = (ushort) htons((ushort) port); - if (my_connect(sock,(struct sockaddr *) &sock_addr, sizeof(sock_addr), - 0)) - { - con->last_errno=errno; - sprintf(con->last_error ,"Could not connect to %-.64s", host); - goto err; - } - /* read the greating */ - if (my_net_read(&con->net) == packet_error) - { - con->last_errno=errno; - strmov(con->last_error,"Read error on socket"); - goto err; - } - sprintf(msg_buf,"%-.16s %-.16s\n",user,passwd); - msg_len=strlen(msg_buf); - if (my_net_write(&con->net,(uchar*) msg_buf,msg_len) || net_flush(&con->net)) - { - con->last_errno=con->net.last_errno; - strmov(con->last_error,"Write error on socket"); - goto err; - } - if (my_net_read(&con->net) == packet_error) - { - con->last_errno=errno; - strmov(con->last_error,"Read error on socket"); - goto err; - } - if ((con->cmd_status=atoi((char*) con->net.read_pos)) != MANAGER_OK) - { - strmov(con->last_error,"Access denied"); - goto err; - } - if (!my_multi_malloc(MYF(0), &con->host, (uint)strlen(host)+1, - &con->user, (uint)strlen(user)+1, - &con->passwd, (uint)strlen(passwd)+1, - NullS)) - { - con->last_errno=ENOMEM; - strmov(con->last_error,"Out of memory"); - goto err; - } - strmov(con->host,host); - strmov(con->user,user); - strmov(con->passwd,passwd); - return con; - -err: - { - my_bool free_me=con->free_me; - con->free_me=0; - mysql_manager_close(con); - con->free_me=free_me; - } - return 0; -} - -void STDCALL mysql_manager_close(MYSQL_MANAGER* con) -{ - /* - No need to free con->user and con->passwd, because they were - allocated in my_multimalloc() along with con->host, freeing - con->hosts frees the whole block - */ - my_free((uchar*)con->host,MYF(MY_ALLOW_ZERO_PTR)); - net_end(&con->net); - if (con->free_me) - my_free((uchar*)con,MYF(0)); -} - - -int STDCALL mysql_manager_command(MYSQL_MANAGER* con,const char* cmd, - int cmd_len) -{ - if (!cmd_len) - cmd_len=strlen(cmd); - if (my_net_write(&con->net,(const uchar*)cmd,cmd_len) || net_flush(&con->net)) - { - con->last_errno=errno; - strmov(con->last_error,"Write error on socket"); - return 1; - } - con->eof=0; - return 0; -} - - -int STDCALL mysql_manager_fetch_line(MYSQL_MANAGER* con, char* res_buf, - int res_buf_size) -{ - char* res_buf_end=res_buf+res_buf_size; - char* net_buf=(char*) con->net.read_pos, *net_buf_end; - int res_buf_shift=RES_BUF_SHIFT; - ulong num_bytes; - - if (res_buf_size<RES_BUF_SHIFT) - { - con->last_errno=ENOMEM; - strmov(con->last_error,"Result buffer too small"); - return 1; - } - - if ((num_bytes=my_net_read(&con->net)) == packet_error) - { - con->last_errno=errno; - strmov(con->last_error,"socket read failed"); - return 1; - } - - net_buf_end=net_buf+num_bytes; - - if ((con->eof=(net_buf[3]==' '))) - res_buf_shift--; - net_buf+=res_buf_shift; - res_buf_end[-1]=0; - for (;net_buf<net_buf_end && res_buf < res_buf_end;res_buf++,net_buf++) - { - if ((*res_buf=*net_buf) == '\r') - { - *res_buf=0; - break; - } - } - return 0; -} |