You've already forked mariadb-connector-c
mirror of
https://github.com/mariadb-corporation/mariadb-connector-c.git
synced 2025-08-08 14:02:17 +03:00
Added missing cio components
This commit is contained in:
457
libmariadb/ma_cio.c
Normal file
457
libmariadb/ma_cio.c
Normal file
@@ -0,0 +1,457 @@
|
|||||||
|
/************************************************************************************
|
||||||
|
Copyright (C) 2015 MariaDB Corporation AB,
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with this library; if not see <http://www.gnu.org/licenses>
|
||||||
|
or write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
|
||||||
|
*************************************************************************************/
|
||||||
|
|
||||||
|
/* MariaDB Communication IO (CIO) interface
|
||||||
|
|
||||||
|
CIO is the interface for client server communication and replaces former vio
|
||||||
|
component of the client library.
|
||||||
|
|
||||||
|
CIO support various protcols like sockets, pipes and shared memory, which are
|
||||||
|
implemented as plugins and can be extended therfore easily.
|
||||||
|
|
||||||
|
Interface function description:
|
||||||
|
|
||||||
|
ma_cio_init allocates a new CIO object which will be used
|
||||||
|
for the current connection
|
||||||
|
|
||||||
|
ma_cio_close frees all resources of previously allocated CIO object
|
||||||
|
and closes open connections
|
||||||
|
|
||||||
|
ma_cio_read reads data from server
|
||||||
|
|
||||||
|
ma_cio_write sends data to server
|
||||||
|
|
||||||
|
ma_cio_set_timeout sets timeout for connection, read and write
|
||||||
|
|
||||||
|
ma_cio_register_callback
|
||||||
|
register callback functions for read and write
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <my_global.h>
|
||||||
|
#include <my_sys.h>
|
||||||
|
#include <mysql.h>
|
||||||
|
#include <errmsg.h>
|
||||||
|
#include <mysql/client_plugin.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ma_common.h>
|
||||||
|
#include <ma_cio.h>
|
||||||
|
#include <mysql_async.h>
|
||||||
|
#include <my_context.h>
|
||||||
|
|
||||||
|
extern pthread_mutex_t THR_LOCK_lock;
|
||||||
|
|
||||||
|
/* callback functions for read/write */
|
||||||
|
LIST *cio_callback= NULL;
|
||||||
|
|
||||||
|
/* {{{ MARIADB_CIO *ma_cio_init */
|
||||||
|
MARIADB_CIO *ma_cio_init(MA_CIO_CINFO *cinfo)
|
||||||
|
{
|
||||||
|
/* check connection type and load the required plugin.
|
||||||
|
* Currently we support the following cio types:
|
||||||
|
* cio_socket
|
||||||
|
* cio_namedpipe
|
||||||
|
*/
|
||||||
|
char *cio_plugins[] = {"cio_socket", "cio_npipe"};
|
||||||
|
int type;
|
||||||
|
MARIADB_CIO_PLUGIN *cio_plugin;
|
||||||
|
MARIADB_CIO *cio= NULL;
|
||||||
|
|
||||||
|
switch (cinfo->type)
|
||||||
|
{
|
||||||
|
case CIO_TYPE_UNIXSOCKET:
|
||||||
|
case CIO_TYPE_SOCKET:
|
||||||
|
type= 0;
|
||||||
|
break;
|
||||||
|
#ifdef _WIN32
|
||||||
|
case CIO_TYPE_NAMEDPIPE:
|
||||||
|
type= 1;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(cio_plugin= (MARIADB_CIO_PLUGIN *)
|
||||||
|
mysql_client_find_plugin(cinfo->mysql,
|
||||||
|
cio_plugins[type],
|
||||||
|
MYSQL_CLIENT_CIO_PLUGIN)))
|
||||||
|
{
|
||||||
|
/* error handling */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!(cio= (MARIADB_CIO *)my_malloc(sizeof(MARIADB_CIO),
|
||||||
|
MYF(MY_WME | MY_ZEROFILL))))
|
||||||
|
{
|
||||||
|
CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* register error routine and methods */
|
||||||
|
cio->methods= cio_plugin->methods;
|
||||||
|
cio->set_error= my_set_error;
|
||||||
|
|
||||||
|
/* set tineouts */
|
||||||
|
if (cio->methods->set_timeout)
|
||||||
|
{
|
||||||
|
cio->methods->set_timeout(cio, CIO_CONNECT_TIMEOUT, cinfo->mysql->options.connect_timeout);
|
||||||
|
cio->methods->set_timeout(cio, CIO_READ_TIMEOUT, cinfo->mysql->options.read_timeout);
|
||||||
|
cio->methods->set_timeout(cio, CIO_WRITE_TIMEOUT, cinfo->mysql->options.write_timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(cio->cache= my_malloc(CIO_READ_AHEAD_CACHE_SIZE, MYF(MY_WME))))
|
||||||
|
{
|
||||||
|
CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cio->cache_size= 0;
|
||||||
|
cio->cache_pos= cio->cache;
|
||||||
|
|
||||||
|
return cio;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ my_bool ma_cio_is_alive */
|
||||||
|
my_bool ma_cio_is_alive(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
if (cio->methods->is_alive)
|
||||||
|
return cio->methods->is_alive(cio);
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ int ma_cio_fast_send */
|
||||||
|
int ma_cio_fast_send(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
if (!cio || !cio->methods->fast_send)
|
||||||
|
return 1;
|
||||||
|
return cio->methods->fast_send(cio);
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ int ma_cio_keepalive */
|
||||||
|
int ma_cio_keepalive(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
if (!cio || !cio->methods->keepalive)
|
||||||
|
return 1;
|
||||||
|
return cio->methods->keepalive(cio);
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ my_bool ma_cio_set_timeout */
|
||||||
|
my_bool ma_cio_set_timeout(MARIADB_CIO *cio,
|
||||||
|
enum enum_cio_timeout type,
|
||||||
|
int timeout)
|
||||||
|
{
|
||||||
|
if (!cio)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (cio->methods->set_timeout)
|
||||||
|
return cio->methods->set_timeout(cio, type, timeout);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ size_t ma_cio_read_async */
|
||||||
|
static size_t ma_cio_read_async(MARIADB_CIO *cio, const uchar *buffer, size_t length)
|
||||||
|
{
|
||||||
|
ssize_t res;
|
||||||
|
struct mysql_async_context *b= cio->async_context;
|
||||||
|
int timeout= cio->timeout[CIO_READ_TIMEOUT];
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
/* todo: async */
|
||||||
|
if (cio->methods->async_read)
|
||||||
|
res= cio->methods->async_read(cio, buffer, length);
|
||||||
|
if (res >= 0 /* || IS_BLOCKING_ERROR()*/)
|
||||||
|
return res;
|
||||||
|
b->events_to_wait_for= MYSQL_WAIT_READ;
|
||||||
|
if (timeout >= 0)
|
||||||
|
{
|
||||||
|
b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT;
|
||||||
|
b->timeout_value= timeout;
|
||||||
|
}
|
||||||
|
if (b->suspend_resume_hook)
|
||||||
|
(*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
|
||||||
|
my_context_yield(&b->async_context);
|
||||||
|
if (b->suspend_resume_hook)
|
||||||
|
(*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
|
||||||
|
if (b->events_occured & MYSQL_WAIT_TIMEOUT)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ size_t ma_cio_read */
|
||||||
|
size_t ma_cio_read(MARIADB_CIO *cio, const uchar *buffer, size_t length)
|
||||||
|
{
|
||||||
|
size_t r= -1;
|
||||||
|
if (!cio)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
|
||||||
|
if (cio && cio->async_context && cio->async_context->active)
|
||||||
|
{
|
||||||
|
goto end;
|
||||||
|
r= ma_cio_read_async(cio, buffer, length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (cio->async_context)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
If switching from non-blocking to blocking API usage, set the socket
|
||||||
|
back to blocking mode.
|
||||||
|
*/
|
||||||
|
my_bool old_mode;
|
||||||
|
ma_cio_blocking(cio, TRUE, &old_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* secure connection */
|
||||||
|
#ifdef HAVE_SSL
|
||||||
|
if (cio->cssl)
|
||||||
|
r= ma_cio_ssl_read(cio->cssl, buffer, length);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
if (cio->methods->read)
|
||||||
|
r= cio->methods->read(cio, buffer, length);
|
||||||
|
end:
|
||||||
|
if (cio_callback)
|
||||||
|
{
|
||||||
|
void (*callback)(int mode, MYSQL *mysql, const uchar *buffer, size_t length);
|
||||||
|
LIST *p= cio_callback;
|
||||||
|
while (p)
|
||||||
|
{
|
||||||
|
callback= p->data;
|
||||||
|
callback(0, cio->mysql, buffer, r);
|
||||||
|
p= p->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ size_t ma_cio_cache_read */
|
||||||
|
size_t ma_cio_cache_read(MARIADB_CIO *cio, uchar *buffer, size_t length)
|
||||||
|
{
|
||||||
|
size_t r;
|
||||||
|
|
||||||
|
if (!cio)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!cio->cache)
|
||||||
|
return ma_cio_read(cio, buffer, length);
|
||||||
|
|
||||||
|
if (cio->cache + cio->cache_size > cio->cache_pos)
|
||||||
|
{
|
||||||
|
r= MIN(length, cio->cache + cio->cache_size - cio->cache_pos);
|
||||||
|
memcpy(buffer, cio->cache_pos, r);
|
||||||
|
cio->cache_pos+= r;
|
||||||
|
}
|
||||||
|
else if (length >= CIO_READ_AHEAD_CACHE_MIN_SIZE)
|
||||||
|
{
|
||||||
|
r= ma_cio_read(cio, buffer, length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
r= ma_cio_read(cio, cio->cache, CIO_READ_AHEAD_CACHE_SIZE);
|
||||||
|
if ((ssize_t)r > 0)
|
||||||
|
{
|
||||||
|
if (length < r)
|
||||||
|
{
|
||||||
|
cio->cache_size= r;
|
||||||
|
cio->cache_pos= cio->cache + length;
|
||||||
|
r= length;
|
||||||
|
}
|
||||||
|
memcpy(buffer, cio->cache, r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ size_t ma_cio_write */
|
||||||
|
size_t ma_cio_write(MARIADB_CIO *cio, const uchar *buffer, size_t length)
|
||||||
|
{
|
||||||
|
size_t r;
|
||||||
|
|
||||||
|
if (!cio)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (cio_callback)
|
||||||
|
{
|
||||||
|
void (*callback)(int mode, MYSQL *mysql, const uchar *buffer, size_t length);
|
||||||
|
LIST *p= cio_callback;
|
||||||
|
while (p)
|
||||||
|
{
|
||||||
|
callback= p->data;
|
||||||
|
callback(1, cio->mysql, buffer, length);
|
||||||
|
p= p->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* secure connection */
|
||||||
|
#ifdef HAVE_SSL
|
||||||
|
if (cio->cssl)
|
||||||
|
r= ma_cio_ssl_write(cio->cssl, buffer, length);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
if (cio->methods->write)
|
||||||
|
r= cio->methods->write(cio, buffer, length);
|
||||||
|
if (cio->callback)
|
||||||
|
cio->callback(cio, 0, buffer, r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ void ma_cio_close */
|
||||||
|
void ma_cio_close(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
/* free internal structures and close connection */
|
||||||
|
#ifdef HAVE_SSL
|
||||||
|
if (cio && cio->cssl)
|
||||||
|
{
|
||||||
|
ma_cio_ssl_close(cio->cssl);
|
||||||
|
my_free((gptr)cio->cssl);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (cio && cio->methods->close)
|
||||||
|
cio->methods->close(cio);
|
||||||
|
|
||||||
|
if (cio->cache)
|
||||||
|
my_free((gptr)cio->cache);
|
||||||
|
|
||||||
|
if (cio->fp)
|
||||||
|
my_fclose(cio->fp, MYF(0));
|
||||||
|
|
||||||
|
my_free((gptr)cio);
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ my_bool ma_cio_get_handle */
|
||||||
|
my_bool ma_cio_get_handle(MARIADB_CIO *cio, void *handle)
|
||||||
|
{
|
||||||
|
if (cio && cio->methods->get_handle)
|
||||||
|
return cio->methods->get_handle(cio, handle);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ ma_cio_wait_io_or_timeout */
|
||||||
|
int ma_cio_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout)
|
||||||
|
{
|
||||||
|
if (cio && cio->async_context && cio->async_context->active)
|
||||||
|
return my_io_wait_async(cio->async_context,
|
||||||
|
(is_read) ? VIO_IO_EVENT_READ : VIO_IO_EVENT_WRITE,
|
||||||
|
timeout);
|
||||||
|
|
||||||
|
|
||||||
|
if (cio && cio->methods->wait_io_or_timeout)
|
||||||
|
return cio->methods->wait_io_or_timeout(cio, is_read, timeout);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ my_bool ma_cio_connect */
|
||||||
|
my_bool ma_cio_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo)
|
||||||
|
{
|
||||||
|
if (cio && cio->methods->connect)
|
||||||
|
return cio->methods->connect(cio, cinfo);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ my_bool ma_cio_blocking */
|
||||||
|
my_bool ma_cio_blocking(MARIADB_CIO *cio, my_bool block, my_bool *previous_mode)
|
||||||
|
{
|
||||||
|
if (cio && cio->methods->blocking)
|
||||||
|
return cio->methods->blocking(cio, block, previous_mode);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ my_bool ma_cio_is_blocking */
|
||||||
|
my_bool ma_cio_is_blocking(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
if (cio && cio->methods->is_blocking)
|
||||||
|
return cio->methods->is_blocking(cio);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
#ifdef HAVE_SSL
|
||||||
|
/* {{{ my_bool ma_cio_start_ssl */
|
||||||
|
my_bool ma_cio_start_ssl(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
if (!cio || !cio->mysql)
|
||||||
|
return 1;
|
||||||
|
CLEAR_CLIENT_ERROR(cio->mysql);
|
||||||
|
if (!(cio->cssl= ma_cio_ssl_init(cio->mysql)))
|
||||||
|
return 1;
|
||||||
|
if (ma_cio_ssl_connect(cio->cssl))
|
||||||
|
{
|
||||||
|
my_free((gptr)cio->cssl);
|
||||||
|
cio->cssl= NULL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if ((cio->mysql->options.ssl_ca || cio->mysql->options.ssl_capath) &&
|
||||||
|
(cio->mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) &&
|
||||||
|
ma_cio_ssl_verify_server_cert(cio->cssl))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* {{{ ma_cio_register_callback */
|
||||||
|
int ma_cio_register_callback(my_bool register_callback,
|
||||||
|
void (*callback_function)(int mode, MYSQL *mysql, const uchar *buffer, size_t length))
|
||||||
|
{
|
||||||
|
LIST *list;
|
||||||
|
|
||||||
|
if (!callback_function)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* plugin will unregister in it's deinit function */
|
||||||
|
if (register_callback)
|
||||||
|
{
|
||||||
|
list= (LIST *)malloc(sizeof(LIST));
|
||||||
|
|
||||||
|
list->data= (void *)callback_function;
|
||||||
|
cio_callback= list_add(cio_callback, list);
|
||||||
|
}
|
||||||
|
else /* unregister callback function */
|
||||||
|
{
|
||||||
|
LIST *p= cio_callback;
|
||||||
|
while (p)
|
||||||
|
{
|
||||||
|
if (p->data == callback_function)
|
||||||
|
{
|
||||||
|
list_delete(cio_callback, p);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p= p->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* }}} */
|
405
plugins/builtin/cio_gnutls.c
Normal file
405
plugins/builtin/cio_gnutls.c
Normal file
@@ -0,0 +1,405 @@
|
|||||||
|
/************************************************************************************
|
||||||
|
Copyright (C) 2014 MariaDB Corporation AB
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with this library; if not see <http://www.gnu.org/licenses>
|
||||||
|
or write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
|
||||||
|
|
||||||
|
*************************************************************************************/
|
||||||
|
#ifdef HAVE_GNUTLS
|
||||||
|
|
||||||
|
#include <gnutls/gnutls.h>
|
||||||
|
#include <gnutls/x509.h>
|
||||||
|
#include <my_global.h>
|
||||||
|
#include <my_sys.h>
|
||||||
|
#include <ma_common.h>
|
||||||
|
#include <ma_cio.h>
|
||||||
|
#include <errmsg.h>
|
||||||
|
#include <my_pthread.h>
|
||||||
|
#include <mysql/client_plugin.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
pthread_mutex_t LOCK_gnutls_config;
|
||||||
|
static my_bool my_gnutls_initialized= FALSE;
|
||||||
|
|
||||||
|
static gnutls_certificate_credentials_t GNUTLS_xcred;
|
||||||
|
|
||||||
|
#define MAX_SSL_ERR_LEN 100
|
||||||
|
|
||||||
|
int cio_gnutls_start(char *errmsg, size_t errmsg_len, int count, va_list);
|
||||||
|
int cio_gnutls_end();
|
||||||
|
void *cio_gnutls_init(MARIADB_SSL *cssl, MYSQL *mysql);
|
||||||
|
my_bool cio_gnutls_connect(MARIADB_SSL *cssl);
|
||||||
|
size_t cio_gnutls_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length);
|
||||||
|
size_t cio_gnutls_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length);
|
||||||
|
my_bool cio_gnutls_close(MARIADB_SSL *cssl);
|
||||||
|
int cio_gnutls_verify_server_cert(MARIADB_SSL *cssl);
|
||||||
|
const char *cio_gnutls_cipher(MARIADB_SSL *cssl);
|
||||||
|
static int my_verify_callback(gnutls_session_t ssl);
|
||||||
|
|
||||||
|
struct st_ma_cio_ssl_methods cio_gnutls_methods= {
|
||||||
|
cio_gnutls_init,
|
||||||
|
cio_gnutls_connect,
|
||||||
|
cio_gnutls_read,
|
||||||
|
cio_gnutls_write,
|
||||||
|
cio_gnutls_close,
|
||||||
|
cio_gnutls_verify_server_cert,
|
||||||
|
cio_gnutls_cipher
|
||||||
|
};
|
||||||
|
|
||||||
|
MARIADB_CIO_PLUGIN cio_gnutls_plugin=
|
||||||
|
{
|
||||||
|
MYSQL_CLIENT_CIO_PLUGIN,
|
||||||
|
MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION,
|
||||||
|
"cio_gnutls",
|
||||||
|
"Georg Richter",
|
||||||
|
"MariaDB communication IO plugin for GnuTLS SSL communication",
|
||||||
|
{1, 0, 0},
|
||||||
|
"LGPL",
|
||||||
|
NULL,
|
||||||
|
cio_gnutls_start,
|
||||||
|
cio_gnutls_end,
|
||||||
|
NULL,
|
||||||
|
&cio_gnutls_methods,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static void cio_gnutls_set_error(MYSQL *mysql, int ssl_errno)
|
||||||
|
{
|
||||||
|
char ssl_error[MAX_SSL_ERR_LEN];
|
||||||
|
const char *ssl_error_reason;
|
||||||
|
MARIADB_CIO *cio= mysql->net.cio;
|
||||||
|
|
||||||
|
if (!ssl_errno)
|
||||||
|
{
|
||||||
|
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((ssl_error_reason= gnutls_strerror(ssl_errno)))
|
||||||
|
{
|
||||||
|
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0,
|
||||||
|
ssl_error_reason);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
my_snprintf(ssl_error, MAX_SSL_ERR_LEN, "SSL errno=%lu", ssl_errno, mysql->charset);
|
||||||
|
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||||
|
ssl_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void cio_gnutls_get_error(char *errmsg, size_t length, int ssl_errno)
|
||||||
|
{
|
||||||
|
const char *ssl_error_reason;
|
||||||
|
|
||||||
|
if (!ssl_errno)
|
||||||
|
{
|
||||||
|
strncpy(errmsg, "Unknown SSL error", length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((ssl_error_reason= gnutls_strerror(ssl_errno)))
|
||||||
|
{
|
||||||
|
strncpy(errmsg, ssl_error_reason, length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
snprintf(errmsg, length, "SSL errno=%lu", ssl_errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Initializes SSL and allocate global
|
||||||
|
context SSL_context
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
my_gnutls_start
|
||||||
|
mysql connection handle
|
||||||
|
|
||||||
|
RETURN VALUES
|
||||||
|
0 success
|
||||||
|
1 error
|
||||||
|
*/
|
||||||
|
int cio_gnutls_start(char *errmsg, size_t errmsg_len, int count, va_list list)
|
||||||
|
{
|
||||||
|
int rc= 0;
|
||||||
|
|
||||||
|
pthread_mutex_init(&LOCK_gnutls_config,MY_MUTEX_INIT_FAST);
|
||||||
|
pthread_mutex_lock(&LOCK_gnutls_config);
|
||||||
|
|
||||||
|
if (!my_gnutls_initialized)
|
||||||
|
{
|
||||||
|
if ((rc= gnutls_global_init()) != GNUTLS_E_SUCCESS)
|
||||||
|
{
|
||||||
|
cio_gnutls_get_error(errmsg, errmsg_len, rc);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
my_gnutls_initialized= TRUE;
|
||||||
|
}
|
||||||
|
/* Allocate a global context for credentials */
|
||||||
|
rc= gnutls_certificate_allocate_credentials(&GNUTLS_xcred);
|
||||||
|
end:
|
||||||
|
pthread_mutex_unlock(&LOCK_gnutls_config);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Release SSL and free resources
|
||||||
|
Will be automatically executed by
|
||||||
|
mysql_server_end() function
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
my_gnutls_end()
|
||||||
|
void
|
||||||
|
|
||||||
|
RETURN VALUES
|
||||||
|
void
|
||||||
|
*/
|
||||||
|
int cio_gnutls_end()
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&LOCK_gnutls_config);
|
||||||
|
if (my_gnutls_initialized)
|
||||||
|
{
|
||||||
|
gnutls_certificate_free_keys(GNUTLS_xcred);
|
||||||
|
gnutls_certificate_free_cas(GNUTLS_xcred);
|
||||||
|
gnutls_certificate_free_crls(GNUTLS_xcred);
|
||||||
|
gnutls_certificate_free_ca_names(GNUTLS_xcred);
|
||||||
|
gnutls_certificate_free_credentials(GNUTLS_xcred);
|
||||||
|
gnutls_global_deinit();
|
||||||
|
my_gnutls_initialized= FALSE;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&LOCK_gnutls_config);
|
||||||
|
pthread_mutex_destroy(&LOCK_gnutls_config);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cio_gnutls_set_certs(MYSQL *mysql, MARIADB_SSL *cssl)
|
||||||
|
{
|
||||||
|
char *certfile= mysql->options.ssl_cert,
|
||||||
|
*keyfile= mysql->options.ssl_key;
|
||||||
|
char *cipher= NULL;
|
||||||
|
int ssl_error= 0;
|
||||||
|
|
||||||
|
if (mysql->options.ssl_ca)
|
||||||
|
{
|
||||||
|
|
||||||
|
ssl_error= gnutls_certificate_set_x509_trust_file(GNUTLS_xcred,
|
||||||
|
mysql->options.ssl_ca,
|
||||||
|
GNUTLS_X509_FMT_PEM);
|
||||||
|
if (ssl_error < 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
gnutls_certificate_set_verify_function(GNUTLS_xcred,
|
||||||
|
my_verify_callback);
|
||||||
|
|
||||||
|
/* GNUTLS doesn't support ca_path */
|
||||||
|
|
||||||
|
if (keyfile && !certfile)
|
||||||
|
certfile= keyfile;
|
||||||
|
if (certfile && !keyfile)
|
||||||
|
keyfile= certfile;
|
||||||
|
|
||||||
|
/* set key */
|
||||||
|
if (certfile || keyfile)
|
||||||
|
{
|
||||||
|
if ((ssl_error= gnutls_certificate_set_x509_key_file(GNUTLS_xcred,
|
||||||
|
certfile, keyfile,
|
||||||
|
GNUTLS_X509_FMT_PEM)) < 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (cipher)
|
||||||
|
my_free(cipher));
|
||||||
|
return ssl_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *cio_gnutls_init(MARIADB_SSL *cssl, MYSQL *mysql)
|
||||||
|
{
|
||||||
|
gnutls_session_t ssl= NULL;
|
||||||
|
int ssl_error= 0;
|
||||||
|
const char *err;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&LOCK_gnutls_config);
|
||||||
|
|
||||||
|
if ((ssl_error= cio_gnutls_set_certs(mysql, cssl)) < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if ((ssl_error = gnutls_init(&ssl, GNUTLS_CLIENT & GNUTLS_NONBLOCK)) < 0)
|
||||||
|
goto error;
|
||||||
|
gnutls_session_set_ptr(ssl, (void *)mysql);
|
||||||
|
|
||||||
|
ssl_error= gnutls_priority_set_direct(ssl, "NORMAL:-DHE-RSA", &err);
|
||||||
|
if (ssl_error < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if ((ssl_error= gnutls_credentials_set(ssl, GNUTLS_CRD_CERTIFICATE, GNUTLS_xcred)) < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
cssl->ssl= ssl;
|
||||||
|
pthread_mutex_unlock(&LOCK_gnutls_config);
|
||||||
|
return (void *)ssl;
|
||||||
|
error:
|
||||||
|
cio_gnutls_set_error(mysql, ssl_error);
|
||||||
|
if (ssl)
|
||||||
|
gnutls_deinit(ssl);
|
||||||
|
pthread_mutex_unlock(&LOCK_gnutls_config);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
my_bool cio_gnutls_connect(MARIADB_SSL *cssl)
|
||||||
|
{
|
||||||
|
gnutls_session_t ssl = (gnutls_session_t)cssl->ssl;
|
||||||
|
my_bool blocking;
|
||||||
|
MYSQL *mysql;
|
||||||
|
MARIADB_CIO *cio;
|
||||||
|
int ret;
|
||||||
|
mysql= (MYSQL *)gnutls_session_get_ptr(ssl);
|
||||||
|
|
||||||
|
if (!mysql)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
cio= mysql->net.cio;
|
||||||
|
|
||||||
|
/* Set socket to blocking if not already set */
|
||||||
|
if (!(blocking= cio->methods->is_blocking(cio)))
|
||||||
|
cio->methods->blocking(cio, TRUE, 0);
|
||||||
|
|
||||||
|
gnutls_transport_set_int(ssl, cio->methods->get_socket(cio));
|
||||||
|
gnutls_handshake_set_timeout(ssl, mysql->options.connect_timeout);
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = gnutls_handshake(ssl);
|
||||||
|
} while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
cio_gnutls_set_error(mysql, ret);
|
||||||
|
/* restore blocking mode */
|
||||||
|
if (!blocking)
|
||||||
|
cio->methods->blocking(cio, FALSE, 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
cssl->ssl= (void *)ssl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cio_gnutls_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length)
|
||||||
|
{
|
||||||
|
return gnutls_record_recv((gnutls_session_t )cssl->ssl, (void *)buffer, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cio_gnutls_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length)
|
||||||
|
{
|
||||||
|
return gnutls_record_send((gnutls_session_t )cssl->ssl, (void *)buffer, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
my_bool cio_gnutls_close(MARIADB_SSL *cssl)
|
||||||
|
{
|
||||||
|
gnutls_bye((gnutls_session_t )cssl->ssl, GNUTLS_SHUT_WR);
|
||||||
|
gnutls_deinit((gnutls_session_t )cssl->ssl);
|
||||||
|
cssl->ssl= NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cio_gnutls_verify_server_cert(MARIADB_SSL *cssl)
|
||||||
|
{
|
||||||
|
/* server verification is already handled before */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *cio_gnutls_cipher(MARIADB_SSL *cssl)
|
||||||
|
{
|
||||||
|
if (!cssl || !cssl->ssl)
|
||||||
|
return NULL;
|
||||||
|
return gnutls_cipher_get_name (gnutls_cipher_get((gnutls_session_t )cssl->ssl));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int my_verify_callback(gnutls_session_t ssl)
|
||||||
|
{
|
||||||
|
unsigned int status;
|
||||||
|
const gnutls_datum_t *cert_list;
|
||||||
|
unsigned int cert_list_size;
|
||||||
|
int ret;
|
||||||
|
MYSQL *mysql= (MYSQL *)gnutls_session_get_ptr(ssl);
|
||||||
|
MARIADB_CIO *cio= mysql->net.cio;
|
||||||
|
gnutls_x509_crt_t cert;
|
||||||
|
const char *hostname;
|
||||||
|
|
||||||
|
/* read hostname */
|
||||||
|
hostname = mysql->host;
|
||||||
|
|
||||||
|
/* skip verification if no ca_file/path was specified */
|
||||||
|
if (!mysql->options.ssl_ca)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* This verification function uses the trusted CAs in the credentials
|
||||||
|
* structure. So you must have installed one or more CA certificates.
|
||||||
|
*/
|
||||||
|
ret = gnutls_certificate_verify_peers2 (ssl, &status);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "CA verification failed");
|
||||||
|
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mysql->net.vio->status= status;
|
||||||
|
|
||||||
|
if (status & GNUTLS_CERT_INVALID)
|
||||||
|
{
|
||||||
|
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
|
}
|
||||||
|
/* Up to here the process is the same for X.509 certificates and
|
||||||
|
* OpenPGP keys. From now on X.509 certificates are assumed. This can
|
||||||
|
* be easily extended to work with openpgp keys as well.
|
||||||
|
*/
|
||||||
|
if (gnutls_certificate_type_get (ssl) != GNUTLS_CRT_X509)
|
||||||
|
{
|
||||||
|
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Expected X509 certificate");
|
||||||
|
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
|
}
|
||||||
|
if (gnutls_x509_crt_init (&cert) < 0)
|
||||||
|
{
|
||||||
|
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Error during certificate initialization");
|
||||||
|
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
|
}
|
||||||
|
cert_list = gnutls_certificate_get_peers (ssl, &cert_list_size);
|
||||||
|
if (cert_list == NULL)
|
||||||
|
{
|
||||||
|
gnutls_x509_crt_deinit (cert);
|
||||||
|
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "No certificate found");
|
||||||
|
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
|
}
|
||||||
|
if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
|
||||||
|
{
|
||||||
|
gnutls_x509_crt_deinit (cert);
|
||||||
|
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error");
|
||||||
|
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) &&
|
||||||
|
gnutls_x509_crt_check_hostname (cert, hostname) < 0)
|
||||||
|
{
|
||||||
|
printf("Error: %s does not match\n", hostname);
|
||||||
|
gnutls_x509_crt_deinit (cert);
|
||||||
|
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Hostname in certificate doesn't match");
|
||||||
|
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
|
}
|
||||||
|
gnutls_x509_crt_deinit (cert);
|
||||||
|
/* notify gnutls to continue handshake normally */
|
||||||
|
|
||||||
|
CLEAR_CLIENT_ERROR(mysql);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* HAVE_GNUTLS */
|
512
plugins/builtin/cio_openssl.c
Normal file
512
plugins/builtin/cio_openssl.c
Normal file
@@ -0,0 +1,512 @@
|
|||||||
|
/************************************************************************************
|
||||||
|
Copyright (C) 2012 Monty Program AB
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with this library; if not see <http://www.gnu.org/licenses>
|
||||||
|
or write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
|
||||||
|
|
||||||
|
*************************************************************************************/
|
||||||
|
#include <my_global.h>
|
||||||
|
#include <my_sys.h>
|
||||||
|
#include <ma_common.h>
|
||||||
|
#include <ma_cio.h>
|
||||||
|
#include <errmsg.h>
|
||||||
|
#include <mysql/client_plugin.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <openssl/ssl.h> /* SSL and SSL_CTX */
|
||||||
|
#include <openssl/err.h> /* error reporting */
|
||||||
|
#include <openssl/conf.h>
|
||||||
|
|
||||||
|
#ifndef HAVE_OPENSSL_DEFAULT
|
||||||
|
#include <memory.h>
|
||||||
|
#define my_malloc(A,B) malloc((A))
|
||||||
|
#undef my_free
|
||||||
|
#define my_free(A,B) free((A))
|
||||||
|
#define my_snprintf snprintf
|
||||||
|
#define my_vsnprintf vsnprintf
|
||||||
|
#undef SAFE_MUTEX
|
||||||
|
#endif
|
||||||
|
#include <my_pthread.h>
|
||||||
|
|
||||||
|
static my_bool my_openssl_initialized= FALSE;
|
||||||
|
static SSL_CTX *SSL_context= NULL;
|
||||||
|
|
||||||
|
#define MAX_SSL_ERR_LEN 100
|
||||||
|
|
||||||
|
static pthread_mutex_t LOCK_openssl_config;
|
||||||
|
static pthread_mutex_t *LOCK_crypto= NULL;
|
||||||
|
|
||||||
|
int cio_openssl_start(char *errmsg, size_t errmsg_len, int count, va_list);
|
||||||
|
int cio_openssl_end();
|
||||||
|
void *cio_openssl_init(MARIADB_SSL *cssl, MYSQL *mysql);
|
||||||
|
my_bool cio_openssl_connect(MARIADB_SSL *cssl);
|
||||||
|
size_t cio_openssl_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length);
|
||||||
|
size_t cio_openssl_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length);
|
||||||
|
my_bool cio_openssl_close(MARIADB_SSL *cssl);
|
||||||
|
int cio_openssl_verify_server_cert(MARIADB_SSL *cssl);
|
||||||
|
const char *cio_openssl_cipher(MARIADB_SSL *cssl);
|
||||||
|
|
||||||
|
struct st_ma_cio_ssl_methods cio_openssl_methods= {
|
||||||
|
cio_openssl_init,
|
||||||
|
cio_openssl_connect,
|
||||||
|
cio_openssl_read,
|
||||||
|
cio_openssl_write,
|
||||||
|
cio_openssl_close,
|
||||||
|
cio_openssl_verify_server_cert,
|
||||||
|
cio_openssl_cipher
|
||||||
|
};
|
||||||
|
|
||||||
|
MARIADB_CIO_PLUGIN cio_openssl_plugin=
|
||||||
|
{
|
||||||
|
MYSQL_CLIENT_CIO_PLUGIN,
|
||||||
|
MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION,
|
||||||
|
"cio_openssl",
|
||||||
|
"Georg Richter",
|
||||||
|
"MariaDB communication IO plugin for OpenSSL communication",
|
||||||
|
{1, 0, 0},
|
||||||
|
"LGPL",
|
||||||
|
cio_openssl_start,
|
||||||
|
cio_openssl_end,
|
||||||
|
NULL,
|
||||||
|
&cio_openssl_methods,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static void cio_openssl_set_error(MYSQL *mysql)
|
||||||
|
{
|
||||||
|
ulong ssl_errno= ERR_get_error();
|
||||||
|
char ssl_error[MAX_SSL_ERR_LEN];
|
||||||
|
const char *ssl_error_reason;
|
||||||
|
MARIADB_CIO *cio= mysql->net.cio;
|
||||||
|
|
||||||
|
if (!ssl_errno)
|
||||||
|
{
|
||||||
|
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((ssl_error_reason= ERR_reason_error_string(ssl_errno)))
|
||||||
|
{
|
||||||
|
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||||
|
0, ssl_error_reason);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
snprintf(ssl_error, MAX_SSL_ERR_LEN, "SSL errno=%lu", ssl_errno, mysql->charset);
|
||||||
|
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, ssl_error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void cio_openssl_get_error(char *errmsg, size_t length)
|
||||||
|
{
|
||||||
|
ulong ssl_errno= ERR_get_error();
|
||||||
|
const char *ssl_error_reason;
|
||||||
|
|
||||||
|
if (!ssl_errno)
|
||||||
|
{
|
||||||
|
strncpy(errmsg, "Unknown SSL error", length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((ssl_error_reason= ERR_reason_error_string(ssl_errno)))
|
||||||
|
{
|
||||||
|
strncpy(errmsg, ssl_error_reason, length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
snprintf(errmsg, length, "SSL errno=%lu", ssl_errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
thread safe callbacks for OpenSSL
|
||||||
|
Crypto call back functions will be
|
||||||
|
set during ssl_initialization
|
||||||
|
*/
|
||||||
|
#if (OPENSSL_VERSION_NUMBER < 0x10000000)
|
||||||
|
static unsigned long my_cb_threadid(void)
|
||||||
|
{
|
||||||
|
/* cast pthread_t to unsigned long */
|
||||||
|
return (unsigned long) pthread_self();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static void my_cb_threadid(CRYPTO_THREADID *id)
|
||||||
|
{
|
||||||
|
CRYPTO_THREADID_set_numeric(id, (unsigned long)pthread_self());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void my_cb_locking(int mode, int n, const char *file, int line)
|
||||||
|
{
|
||||||
|
if (mode & CRYPTO_LOCK)
|
||||||
|
pthread_mutex_lock(&LOCK_crypto[n]);
|
||||||
|
else
|
||||||
|
pthread_mutex_unlock(&LOCK_crypto[n]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int ssl_thread_init()
|
||||||
|
{
|
||||||
|
int i, max= CRYPTO_num_locks();
|
||||||
|
|
||||||
|
if (LOCK_crypto == NULL)
|
||||||
|
{
|
||||||
|
if (!(LOCK_crypto=
|
||||||
|
(pthread_mutex_t *)my_malloc(sizeof(pthread_mutex_t) * max, MYF(0))))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
for (i=0; i < max; i++)
|
||||||
|
pthread_mutex_init(&LOCK_crypto[i], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (OPENSSL_VERSION_NUMBER < 0x10000000)
|
||||||
|
CRYPTO_set_id_callback(my_cb_threadid);
|
||||||
|
#else
|
||||||
|
CRYPTO_THREADID_set_callback(my_cb_threadid);
|
||||||
|
#endif
|
||||||
|
CRYPTO_set_locking_callback(my_cb_locking);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Initializes SSL and allocate global
|
||||||
|
context SSL_context
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
my_ssl_start
|
||||||
|
mysql connection handle
|
||||||
|
|
||||||
|
RETURN VALUES
|
||||||
|
0 success
|
||||||
|
1 error
|
||||||
|
*/
|
||||||
|
int cio_openssl_start(char *errmsg, size_t errmsg_len, int count, va_list list)
|
||||||
|
{
|
||||||
|
int rc= 1;
|
||||||
|
/* lock mutex to prevent multiple initialization */
|
||||||
|
pthread_mutex_init(&LOCK_openssl_config,MY_MUTEX_INIT_FAST);
|
||||||
|
pthread_mutex_lock(&LOCK_openssl_config);
|
||||||
|
if (!my_openssl_initialized)
|
||||||
|
{
|
||||||
|
if (ssl_thread_init())
|
||||||
|
{
|
||||||
|
strncpy(errmsg, "Not enough memory", errmsg_len);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
SSL_library_init();
|
||||||
|
|
||||||
|
#if SSLEAY_VERSION_NUMBER >= 0x00907000L
|
||||||
|
OPENSSL_config(NULL);
|
||||||
|
#endif
|
||||||
|
/* load errors */
|
||||||
|
SSL_load_error_strings();
|
||||||
|
/* digests and ciphers */
|
||||||
|
OpenSSL_add_all_algorithms();
|
||||||
|
|
||||||
|
if (!(SSL_context= SSL_CTX_new(TLSv1_client_method())))
|
||||||
|
{
|
||||||
|
cio_openssl_get_error(errmsg, errmsg_len);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
rc= 0;
|
||||||
|
my_openssl_initialized= TRUE;
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
pthread_mutex_unlock(&LOCK_openssl_config);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Release SSL and free resources
|
||||||
|
Will be automatically executed by
|
||||||
|
mysql_server_end() function
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
my_ssl_end()
|
||||||
|
void
|
||||||
|
|
||||||
|
RETURN VALUES
|
||||||
|
void
|
||||||
|
*/
|
||||||
|
int cio_openssl_end()
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&LOCK_openssl_config);
|
||||||
|
if (my_openssl_initialized)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
CRYPTO_set_locking_callback(NULL);
|
||||||
|
CRYPTO_set_id_callback(NULL);
|
||||||
|
|
||||||
|
for (i=0; i < CRYPTO_num_locks(); i++)
|
||||||
|
pthread_mutex_destroy(&LOCK_crypto[i]);
|
||||||
|
|
||||||
|
my_free((gptr)LOCK_crypto, MYF(0));
|
||||||
|
LOCK_crypto= NULL;
|
||||||
|
|
||||||
|
if (SSL_context)
|
||||||
|
{
|
||||||
|
SSL_CTX_free(SSL_context);
|
||||||
|
SSL_context= NULL;
|
||||||
|
}
|
||||||
|
ERR_remove_state(0);
|
||||||
|
EVP_cleanup();
|
||||||
|
CRYPTO_cleanup_all_ex_data();
|
||||||
|
ERR_free_strings();
|
||||||
|
//ENGINE_cleanup();
|
||||||
|
CONF_modules_free();
|
||||||
|
CONF_modules_unload(1);
|
||||||
|
sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
|
||||||
|
my_openssl_initialized= FALSE;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&LOCK_openssl_config);
|
||||||
|
pthread_mutex_destroy(&LOCK_openssl_config);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cio_openssl_set_certs(MYSQL *mysql)
|
||||||
|
{
|
||||||
|
char *certfile= mysql->options.ssl_cert,
|
||||||
|
*keyfile= mysql->options.ssl_key;
|
||||||
|
|
||||||
|
/* add cipher */
|
||||||
|
if ((mysql->options.ssl_cipher &&
|
||||||
|
mysql->options.ssl_cipher[0] != 0) &&
|
||||||
|
SSL_CTX_set_cipher_list(SSL_context, mysql->options.ssl_cipher) == 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
/* ca_file and ca_path */
|
||||||
|
if (SSL_CTX_load_verify_locations(SSL_context,
|
||||||
|
mysql->options.ssl_ca,
|
||||||
|
mysql->options.ssl_capath) == 0)
|
||||||
|
{
|
||||||
|
if (mysql->options.ssl_ca || mysql->options.ssl_capath)
|
||||||
|
goto error;
|
||||||
|
if (SSL_CTX_set_default_verify_paths(SSL_context) == 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyfile && !certfile)
|
||||||
|
certfile= keyfile;
|
||||||
|
if (certfile && !keyfile)
|
||||||
|
keyfile= certfile;
|
||||||
|
|
||||||
|
/* set cert */
|
||||||
|
if (certfile && certfile[0] != 0)
|
||||||
|
if (SSL_CTX_use_certificate_file(SSL_context, certfile, SSL_FILETYPE_PEM) != 1)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
/* set key */
|
||||||
|
if (keyfile && keyfile[0])
|
||||||
|
{
|
||||||
|
if (SSL_CTX_use_PrivateKey_file(SSL_context, keyfile, SSL_FILETYPE_PEM) != 1)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
/* verify key */
|
||||||
|
if (certfile && !SSL_CTX_check_private_key(SSL_context))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (mysql->options.extension &&
|
||||||
|
(mysql->options.extension->ssl_crl || mysql->options.extension->ssl_crlpath))
|
||||||
|
{
|
||||||
|
X509_STORE *certstore;
|
||||||
|
|
||||||
|
if ((certstore= SSL_CTX_get_cert_store(SSL_context)))
|
||||||
|
{
|
||||||
|
if (X509_STORE_load_locations(certstore, mysql->options.ssl_ca,
|
||||||
|
mysql->options.ssl_capath) == 0 ||
|
||||||
|
X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK |
|
||||||
|
X509_V_FLAG_CRL_CHECK_ALL) == 0)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
cio_openssl_set_error(mysql);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int my_verify_callback(int ok, X509_STORE_CTX *ctx)
|
||||||
|
{
|
||||||
|
X509 *check_cert;
|
||||||
|
SSL *ssl;
|
||||||
|
MYSQL *mysql;
|
||||||
|
|
||||||
|
ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
|
||||||
|
mysql= (MYSQL *)SSL_get_app_data(ssl);
|
||||||
|
|
||||||
|
/* skip verification if no ca_file/path was specified */
|
||||||
|
if (!mysql->options.ssl_ca && !mysql->options.ssl_capath)
|
||||||
|
{
|
||||||
|
ok= 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
uint depth;
|
||||||
|
if (!(check_cert= X509_STORE_CTX_get_current_cert(ctx)))
|
||||||
|
return 0;
|
||||||
|
depth= X509_STORE_CTX_get_error_depth(ctx);
|
||||||
|
if (depth == 0)
|
||||||
|
ok= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *cio_openssl_init(MARIADB_SSL *cssl, MYSQL *mysql)
|
||||||
|
{
|
||||||
|
int verify;
|
||||||
|
SSL *ssl= NULL;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&LOCK_openssl_config);
|
||||||
|
if (cio_openssl_set_certs(mysql))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (!(ssl= SSL_new(SSL_context)))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (!SSL_set_app_data(ssl, mysql))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
verify= (!mysql->options.ssl_ca && !mysql->options.ssl_capath) ?
|
||||||
|
SSL_VERIFY_NONE : SSL_VERIFY_PEER;
|
||||||
|
|
||||||
|
SSL_CTX_set_verify(SSL_context, verify, my_verify_callback);
|
||||||
|
SSL_CTX_set_verify_depth(SSL_context, 1);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&LOCK_openssl_config);
|
||||||
|
return (void *)ssl;
|
||||||
|
error:
|
||||||
|
pthread_mutex_unlock(&LOCK_openssl_config);
|
||||||
|
if (ssl)
|
||||||
|
SSL_free(ssl);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
my_bool cio_openssl_connect(MARIADB_SSL *cssl)
|
||||||
|
{
|
||||||
|
SSL *ssl = (SSL *)cssl->ssl;
|
||||||
|
my_bool blocking;
|
||||||
|
MYSQL *mysql;
|
||||||
|
MARIADB_CIO *cio;
|
||||||
|
|
||||||
|
mysql= (MYSQL *)SSL_get_app_data(ssl);
|
||||||
|
cio= mysql->net.cio;
|
||||||
|
|
||||||
|
/* Set socket to blocking if not already set */
|
||||||
|
if (!(blocking= cio->methods->is_blocking(cio)))
|
||||||
|
cio->methods->blocking(cio, TRUE, 0);
|
||||||
|
|
||||||
|
SSL_clear(ssl);
|
||||||
|
SSL_SESSION_set_timeout(SSL_get_session(ssl),
|
||||||
|
mysql->options.connect_timeout);
|
||||||
|
SSL_set_fd(ssl, mysql_get_socket(mysql));
|
||||||
|
|
||||||
|
if (SSL_connect(ssl) != 1)
|
||||||
|
{
|
||||||
|
cio_openssl_set_error(mysql);
|
||||||
|
/* restore blocking mode */
|
||||||
|
if (!blocking)
|
||||||
|
cio->methods->blocking(cio, FALSE, 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
cio->cssl->ssl= cssl->ssl= (void *)ssl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cio_openssl_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length)
|
||||||
|
{
|
||||||
|
return SSL_read((SSL *)cssl->ssl, (void *)buffer, (int)length);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cio_openssl_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length)
|
||||||
|
{
|
||||||
|
return SSL_write((SSL *)cssl->ssl, (void *)buffer, (int)length);
|
||||||
|
}
|
||||||
|
|
||||||
|
my_bool cio_openssl_close(MARIADB_SSL *cssl)
|
||||||
|
{
|
||||||
|
int i, rc;
|
||||||
|
SSL *ssl;
|
||||||
|
|
||||||
|
if (!cssl || !cssl->ssl)
|
||||||
|
return 1;
|
||||||
|
ssl= (SSL *)cssl->ssl;
|
||||||
|
|
||||||
|
SSL_set_quiet_shutdown(ssl, 1);
|
||||||
|
/* 2 x pending + 2 * data = 4 */
|
||||||
|
for (i=0; i < 4; i++)
|
||||||
|
if ((rc= SSL_shutdown(ssl)))
|
||||||
|
break;
|
||||||
|
|
||||||
|
SSL_free(ssl);
|
||||||
|
cssl->ssl= NULL;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cio_openssl_verify_server_cert(MARIADB_SSL *cssl)
|
||||||
|
{
|
||||||
|
X509 *cert;
|
||||||
|
MYSQL *mysql;
|
||||||
|
MARIADB_CIO *cio;
|
||||||
|
SSL *ssl;
|
||||||
|
char *p1, *p2, buf[256];
|
||||||
|
|
||||||
|
if (!cssl || !cssl->ssl)
|
||||||
|
return 1;
|
||||||
|
ssl= (SSL *)cssl->ssl;
|
||||||
|
|
||||||
|
mysql= (MYSQL *)SSL_get_app_data(ssl);
|
||||||
|
cio= mysql->net.cio;
|
||||||
|
|
||||||
|
if (!mysql->host)
|
||||||
|
{
|
||||||
|
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0,
|
||||||
|
"Invalid (empty) hostname");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(cert= SSL_get_peer_certificate(ssl)))
|
||||||
|
{
|
||||||
|
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0,
|
||||||
|
"Unable to get server certificate");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
X509_NAME_oneline(X509_get_subject_name(cert), buf, 256);
|
||||||
|
X509_free(cert);
|
||||||
|
|
||||||
|
/* Extract the server name from buffer:
|
||||||
|
Format: ....CN=/hostname/.... */
|
||||||
|
if ((p1= strstr(buf, "/CN=")))
|
||||||
|
{
|
||||||
|
p1+= 4;
|
||||||
|
if ((p2= strchr(p1, '/')))
|
||||||
|
*p2= 0;
|
||||||
|
if (!strcmp(mysql->host, p1))
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0,
|
||||||
|
"Validation of SSL server certificate failed");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *cio_openssl_cipher(MARIADB_SSL *cssl)
|
||||||
|
{
|
||||||
|
if (!cssl || !cssl->ssl)
|
||||||
|
return NULL;
|
||||||
|
return SSL_get_cipher_name(cssl->ssl);
|
||||||
|
}
|
410
plugins/builtin/cio_schannel.c
Normal file
410
plugins/builtin/cio_schannel.c
Normal file
@@ -0,0 +1,410 @@
|
|||||||
|
/************************************************************************************
|
||||||
|
Copyright (C) 2014 MariaDB Corporation Ab
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with this library; if not see <http://www.gnu.org/licenses>
|
||||||
|
or write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
|
||||||
|
|
||||||
|
*************************************************************************************/
|
||||||
|
#include "ma_schannel.h"
|
||||||
|
|
||||||
|
#pragma comment (lib, "crypt32.lib")
|
||||||
|
#pragma comment (lib, "secur32.lib")
|
||||||
|
|
||||||
|
#define VOID void
|
||||||
|
|
||||||
|
static my_bool my_schannel_initialized= FALSE;
|
||||||
|
|
||||||
|
#define MAX_SSL_ERR_LEN 100
|
||||||
|
|
||||||
|
static pthread_mutex_t LOCK_schannel_config;
|
||||||
|
static pthread_mutex_t *LOCK_crypto= NULL;
|
||||||
|
|
||||||
|
int cio_schannel_start(char *errmsg, size_t errmsg_len, int count, va_list);
|
||||||
|
int cio_schannel_end();
|
||||||
|
void *cio_schannel_init(MARIADB_SSL *cssl, MYSQL *mysql);
|
||||||
|
my_bool cio_schannel_connect(MARIADB_SSL *cssl);
|
||||||
|
size_t cio_schannel_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length);
|
||||||
|
size_t cio_schannel_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length);
|
||||||
|
my_bool cio_schannel_close(MARIADB_SSL *cssl);
|
||||||
|
int cio_schannel_verify_server_cert(MARIADB_SSL *cssl);
|
||||||
|
const char *cio_schannel_cipher(MARIADB_SSL *cssl);
|
||||||
|
|
||||||
|
struct st_ma_cio_ssl_methods cio_schannel_methods= {
|
||||||
|
cio_schannel_init,
|
||||||
|
cio_schannel_connect,
|
||||||
|
cio_schannel_read,
|
||||||
|
cio_schannel_write,
|
||||||
|
cio_schannel_close,
|
||||||
|
cio_schannel_verify_server_cert,
|
||||||
|
cio_schannel_cipher
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef HAVE_SCHANNEL_DEFAULT
|
||||||
|
MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_=
|
||||||
|
#else
|
||||||
|
MARIADB_CIO_PLUGIN cio_schannel_plugin=
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
MYSQL_CLIENT_CIO_PLUGIN,
|
||||||
|
MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION,
|
||||||
|
"cio_schannel",
|
||||||
|
"Georg Richter",
|
||||||
|
"MariaDB communication IO plugin for Windows SSL/SChannel communication",
|
||||||
|
{1, 0, 0},
|
||||||
|
"LGPL",
|
||||||
|
cio_schannel_start,
|
||||||
|
cio_schannel_end,
|
||||||
|
NULL,
|
||||||
|
&cio_schannel_methods,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static void cio_schannel_set_error(MYSQL *mysql)
|
||||||
|
{
|
||||||
|
ulong ssl_errno= GetLastError();
|
||||||
|
char ssl_error[MAX_SSL_ERR_LEN];
|
||||||
|
char *ssl_error_reason= NULL;
|
||||||
|
MARIADB_CIO *cio= mysql->net.cio;
|
||||||
|
|
||||||
|
if (!ssl_errno)
|
||||||
|
{
|
||||||
|
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* todo: obtain error messge */
|
||||||
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
NULL, ssl_errno, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
(LPTSTR) &ssl_error_reason, 0, NULL );
|
||||||
|
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ssl_error_reason);
|
||||||
|
|
||||||
|
if (ssl_error_reason)
|
||||||
|
LocalFree(ssl_error_reason);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int ssl_thread_init()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Initializes SSL and allocate global
|
||||||
|
context SSL_context
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
cio_schannel_start
|
||||||
|
|
||||||
|
RETURN VALUES
|
||||||
|
0 success
|
||||||
|
1 error
|
||||||
|
*/
|
||||||
|
int cio_schannel_start(char *errmsg, size_t errmsg_len, int count, va_list list)
|
||||||
|
{
|
||||||
|
if (!my_schannel_initialized)
|
||||||
|
{
|
||||||
|
pthread_mutex_init(&LOCK_schannel_config,MY_MUTEX_INIT_FAST);
|
||||||
|
pthread_mutex_lock(&LOCK_schannel_config);
|
||||||
|
|
||||||
|
// SecureZeroMemory(&SC_CTX, sizeof(struct st_schannel_global));
|
||||||
|
|
||||||
|
my_schannel_initialized= TRUE;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&LOCK_schannel_config);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Release SSL and free resources
|
||||||
|
Will be automatically executed by
|
||||||
|
mysql_server_end() function
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
cio_schannel_end()
|
||||||
|
void
|
||||||
|
|
||||||
|
RETURN VALUES
|
||||||
|
void
|
||||||
|
*/
|
||||||
|
int cio_schannel_end()
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&LOCK_schannel_config);
|
||||||
|
if (my_schannel_initialized)
|
||||||
|
{
|
||||||
|
|
||||||
|
my_schannel_initialized= FALSE;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&LOCK_schannel_config);
|
||||||
|
pthread_mutex_destroy(&LOCK_schannel_config);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* {{{ static int cio_schannel_set_client_certs(MARIADB_SSL *cssl) */
|
||||||
|
static int cio_schannel_set_client_certs(MARIADB_SSL *cssl)
|
||||||
|
{
|
||||||
|
MYSQL *mysql= cssl->cio->mysql;
|
||||||
|
char *certfile= mysql->options.ssl_cert,
|
||||||
|
*keyfile= mysql->options.ssl_key,
|
||||||
|
*cafile= mysql->options.ssl_ca;
|
||||||
|
|
||||||
|
SC_CTX *sctx= (SC_CTX *)cssl->ssl;
|
||||||
|
|
||||||
|
if (cafile)
|
||||||
|
if (!(sctx->client_ca_ctx = ma_schannel_create_cert_context(cafile)))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
if (certfile)
|
||||||
|
{
|
||||||
|
if (!(sctx->client_cert_ctx = ma_schannel_create_cert_context(certfile)))
|
||||||
|
goto end;
|
||||||
|
if (keyfile)
|
||||||
|
if (!ma_schannel_load_private_key(sctx->client_cert_ctx, keyfile))
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mysql->options.extension && mysql->options.extension->ssl_crl)
|
||||||
|
{
|
||||||
|
sctx->client_crl_ctx= ma_schannel_create_crl_context(mysql->options.extension->ssl_crl);
|
||||||
|
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
end:
|
||||||
|
if (sctx->client_ca_ctx)
|
||||||
|
CertFreeCertificateContext(sctx->client_ca_ctx);
|
||||||
|
if (sctx->client_cert_ctx)
|
||||||
|
CertFreeCertificateContext(sctx->client_cert_ctx);
|
||||||
|
if (sctx->client_crl_ctx)
|
||||||
|
CertFreeCRLContext(sctx->client_crl_ctx);
|
||||||
|
|
||||||
|
cio_schannel_set_error(mysql);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ void *cio_schannel_init(MARIADB_SSL *cssl, MYSQL *mysql) */
|
||||||
|
void *cio_schannel_init(MARIADB_SSL *cssl, MYSQL *mysql)
|
||||||
|
{
|
||||||
|
int verify;
|
||||||
|
SC_CTX *sctx;
|
||||||
|
|
||||||
|
if (!(sctx= LocalAlloc(0, sizeof(SC_CTX))))
|
||||||
|
return NULL;
|
||||||
|
ZeroMemory(sctx, sizeof(SC_CTX));
|
||||||
|
|
||||||
|
cssl->data= (void *)sctx;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&LOCK_schannel_config);
|
||||||
|
return (void *)sctx;
|
||||||
|
error:
|
||||||
|
pthread_mutex_unlock(&LOCK_schannel_config);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static my_bool VerifyServerCertificate(SC_CTX *sctx, PCCERT_CONTEXT pServerCert, PSTR pszServerName, DWORD dwCertFlags )
|
||||||
|
{
|
||||||
|
SECURITY_STATUS sRet;
|
||||||
|
DWORD flags;
|
||||||
|
char *szName= NULL;
|
||||||
|
int rc= 0;
|
||||||
|
|
||||||
|
/* We perform a manually validation, as described at
|
||||||
|
http://msdn.microsoft.com/en-us/library/windows/desktop/aa378740%28v=vs.85%29.aspx
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Check if
|
||||||
|
- The certificate chain is complete and the root is a certificate from a trusted certification authority (CA).
|
||||||
|
- The current time is not beyond the begin and end dates for each of the certificates in the certificate chain.
|
||||||
|
*/
|
||||||
|
flags= CERT_STORE_SIGNATURE_FLAG |
|
||||||
|
CERT_STORE_TIME_VALIDITY_FLAG;
|
||||||
|
|
||||||
|
if (!(sRet= CertVerifySubjectCertificateContext(pServerCert,
|
||||||
|
sctx->client_ca_ctx,
|
||||||
|
&flags)))
|
||||||
|
{
|
||||||
|
/* todo: error handling */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if none of the certificates in the certificate chain have been revoked. */
|
||||||
|
if (sctx->client_crl_ctx)
|
||||||
|
{
|
||||||
|
PCRL_INFO Info[1];
|
||||||
|
|
||||||
|
Info[0]= sctx->client_crl_ctx->pCrlInfo;
|
||||||
|
if (!(CertVerifyCRLRevocation(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
||||||
|
pServerCert->pCertInfo,
|
||||||
|
1, Info)) )
|
||||||
|
{
|
||||||
|
/* todo: error handling */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check server name */
|
||||||
|
if (pszServerName)
|
||||||
|
{
|
||||||
|
DWORD NameSize= 0;
|
||||||
|
char *p1, *p2;
|
||||||
|
|
||||||
|
if (!(NameSize= CertNameToStr(pServerCert->dwCertEncodingType,
|
||||||
|
&pServerCert->pCertInfo->Subject,
|
||||||
|
CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
|
||||||
|
NULL, 0)))
|
||||||
|
{
|
||||||
|
/* todo: error handling */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(szName= LocalAlloc(0, NameSize + 1)))
|
||||||
|
{
|
||||||
|
/* error handling */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CertNameToStr(pServerCert->dwCertEncodingType,
|
||||||
|
&pServerCert->pCertInfo->Subject,
|
||||||
|
CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
|
||||||
|
szName, NameSize))
|
||||||
|
{
|
||||||
|
/* error handling */
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if ((p1 = strstr(szName, "CN=")))
|
||||||
|
{
|
||||||
|
p1+= 3;
|
||||||
|
if ((p2= strstr(p1, ", ")))
|
||||||
|
*p2= 0;
|
||||||
|
if (!strcmp(pszServerName, p1))
|
||||||
|
{
|
||||||
|
rc= 1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
if (szName)
|
||||||
|
LocalFree(szName);
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
my_bool cio_schannel_connect(MARIADB_SSL *cssl)
|
||||||
|
{
|
||||||
|
my_bool blocking;
|
||||||
|
MYSQL *mysql;
|
||||||
|
SCHANNEL_CRED Cred;
|
||||||
|
MARIADB_CIO *cio;
|
||||||
|
SC_CTX *sctx;
|
||||||
|
SECURITY_STATUS sRet;
|
||||||
|
PCCERT_CONTEXT pRemoteCertContext = NULL;
|
||||||
|
|
||||||
|
if (!cssl || !cssl->cio || !cssl->data)
|
||||||
|
return 1;;
|
||||||
|
|
||||||
|
cio= cssl->cio;
|
||||||
|
sctx= (SC_CTX *)cssl->data;
|
||||||
|
|
||||||
|
/* Set socket to blocking if not already set */
|
||||||
|
if (!(blocking= cio->methods->is_blocking(cio)))
|
||||||
|
cio->methods->blocking(cio, TRUE, 0);
|
||||||
|
|
||||||
|
mysql= cio->mysql;
|
||||||
|
|
||||||
|
if (cio_schannel_set_client_certs(cssl))
|
||||||
|
{
|
||||||
|
cio_schannel_set_error(mysql);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZeroMemory(&Cred, sizeof(SCHANNEL_CRED));
|
||||||
|
Cred.dwVersion= SCHANNEL_CRED_VERSION;
|
||||||
|
Cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK |
|
||||||
|
SCH_CRED_NO_DEFAULT_CREDS |
|
||||||
|
SCH_CRED_MANUAL_CRED_VALIDATION;
|
||||||
|
if (sctx->client_cert_ctx)
|
||||||
|
{
|
||||||
|
Cred.cCreds = 1;
|
||||||
|
Cred.paCred = &sctx->client_cert_ctx;
|
||||||
|
}
|
||||||
|
Cred.grbitEnabledProtocols= SP_PROT_TLS1;
|
||||||
|
|
||||||
|
/* We allocate 2 x net_buffer_length */
|
||||||
|
if (!(sctx->IoBuffer= (PUCHAR)LocalAlloc(0, 0x4000)))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
if (AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND,
|
||||||
|
NULL, &Cred, NULL, NULL, &sctx->CredHdl, NULL) != SEC_E_OK)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
if (ma_schannel_client_handshake(cssl))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext);
|
||||||
|
if (sRet != SEC_E_OK)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
if (!VerifyServerCertificate(sctx,
|
||||||
|
pRemoteCertContext,
|
||||||
|
mysql->host,
|
||||||
|
0 ))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
end:
|
||||||
|
/* todo: cleanup */
|
||||||
|
if (sctx->IoBuffer)
|
||||||
|
LocalFree(sctx->IoBuffer);
|
||||||
|
if (sctx->client_ca_ctx)
|
||||||
|
CertFreeCertificateContext(sctx->client_ca_ctx);
|
||||||
|
if (sctx->client_cert_ctx)
|
||||||
|
CertFreeCertificateContext(sctx->client_cert_ctx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cio_schannel_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cio_schannel_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
my_bool cio_schannel_close(MARIADB_SSL *cssl)
|
||||||
|
{
|
||||||
|
int i, rc;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cio_schannel_verify_server_cert(MARIADB_SSL *cssl)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *cio_schannel_cipher(MARIADB_SSL *cssl)
|
||||||
|
{
|
||||||
|
if (!cssl || !cssl->ssl)
|
||||||
|
return NULL;
|
||||||
|
}
|
902
plugins/builtin/cio_socket.c
Normal file
902
plugins/builtin/cio_socket.c
Normal file
@@ -0,0 +1,902 @@
|
|||||||
|
/************************************************************************************
|
||||||
|
Copyright (C) 2015 MariaDB Corporation AB,
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with this library; if not see <http://www.gnu.org/licenses>
|
||||||
|
or write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
|
||||||
|
*************************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
MariaDB Communication IO (CIO) plugin for socket communication:
|
||||||
|
|
||||||
|
The plugin handles connections via unix and network sockets. it is enabled by
|
||||||
|
default and compiled into Connector/C.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <my_global.h>
|
||||||
|
#include <my_sys.h>
|
||||||
|
#include <errmsg.h>
|
||||||
|
#include <mysql.h>
|
||||||
|
#include <mysql/client_plugin.h>
|
||||||
|
#include <my_context.h>
|
||||||
|
#include <mysql_async.h>
|
||||||
|
#include <ma_common.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <m_string.h>
|
||||||
|
#ifndef _WIN32
|
||||||
|
#ifdef HAVE_SYS_UN_H
|
||||||
|
#include <sys/un.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_POLL
|
||||||
|
#include <sys/poll.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_IOCTL_H
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_FCNTL_H
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
#include <netinet/in_systm.h>
|
||||||
|
#include <netinet/ip.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#else
|
||||||
|
#define O_NONBLOCK 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SOCKET_ERROR
|
||||||
|
#define SOCKET_ERROR -1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Function prototypes */
|
||||||
|
my_bool cio_socket_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout);
|
||||||
|
int cio_socket_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type);
|
||||||
|
size_t cio_socket_read(MARIADB_CIO *cio, const uchar *buffer, size_t length);
|
||||||
|
size_t cio_socket_async_read(MARIADB_CIO *cio, const uchar *buffer, size_t length);
|
||||||
|
size_t cio_socket_write(MARIADB_CIO *cio, const uchar *buffer, size_t length);
|
||||||
|
size_t cio_socket_async_write(MARIADB_CIO *cio, const uchar *buffer, size_t length);
|
||||||
|
int cio_socket_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout);
|
||||||
|
my_bool cio_socket_blocking(MARIADB_CIO *cio, my_bool value, my_bool *old_value);
|
||||||
|
my_bool cio_socket_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo);
|
||||||
|
my_bool cio_socket_close(MARIADB_CIO *cio);
|
||||||
|
int cio_socket_fast_send(MARIADB_CIO *cio);
|
||||||
|
int cio_socket_keepalive(MARIADB_CIO *cio);
|
||||||
|
my_bool cio_socket_get_handle(MARIADB_CIO *cio, void *handle);
|
||||||
|
my_bool cio_socket_is_blocking(MARIADB_CIO *cio);
|
||||||
|
my_bool cio_socket_is_alive(MARIADB_CIO *cio);
|
||||||
|
|
||||||
|
static int cio_socket_init(char *unused1,
|
||||||
|
size_t unused2,
|
||||||
|
int unused3,
|
||||||
|
va_list);
|
||||||
|
static int cio_socket_end(void);
|
||||||
|
|
||||||
|
struct st_ma_cio_methods cio_socket_methods= {
|
||||||
|
cio_socket_set_timeout,
|
||||||
|
cio_socket_get_timeout,
|
||||||
|
cio_socket_read,
|
||||||
|
cio_socket_async_read,
|
||||||
|
cio_socket_write,
|
||||||
|
cio_socket_async_write,
|
||||||
|
cio_socket_wait_io_or_timeout,
|
||||||
|
cio_socket_blocking,
|
||||||
|
cio_socket_connect,
|
||||||
|
cio_socket_close,
|
||||||
|
cio_socket_fast_send,
|
||||||
|
cio_socket_keepalive,
|
||||||
|
cio_socket_get_handle,
|
||||||
|
cio_socket_is_blocking,
|
||||||
|
cio_socket_is_alive
|
||||||
|
};
|
||||||
|
|
||||||
|
MARIADB_CIO_PLUGIN cio_socket_plugin=
|
||||||
|
{
|
||||||
|
MYSQL_CLIENT_CIO_PLUGIN,
|
||||||
|
MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION,
|
||||||
|
"cio_socket",
|
||||||
|
"Georg Richter",
|
||||||
|
"MariaDB communication IO plugin for socket communication",
|
||||||
|
{1, 0, 0},
|
||||||
|
"LGPL",
|
||||||
|
&cio_socket_init,
|
||||||
|
&cio_socket_end,
|
||||||
|
&cio_socket_methods,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct st_cio_socket {
|
||||||
|
my_socket socket;
|
||||||
|
int fcntl_mode;
|
||||||
|
MYSQL *mysql;
|
||||||
|
};
|
||||||
|
|
||||||
|
static my_bool cio_socket_initialized= FALSE;
|
||||||
|
|
||||||
|
|
||||||
|
static int cio_socket_init(char *errmsg,
|
||||||
|
size_t errmsg_length,
|
||||||
|
int unused,
|
||||||
|
va_list va)
|
||||||
|
{
|
||||||
|
cio_socket_initialized= TRUE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cio_socket_end(void)
|
||||||
|
{
|
||||||
|
if (!cio_socket_initialized)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* {{{ cio_socket_set_timeout */
|
||||||
|
/*
|
||||||
|
set timeout value
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
cio_socket_set_timeout
|
||||||
|
cio CIO
|
||||||
|
type timeout type (connect, read, write)
|
||||||
|
timeout timeout in seconds
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Sets timeout values for connection-, read or write time out.
|
||||||
|
CIO internally stores all timeout values in milliseconds, but
|
||||||
|
accepts and returns all time values in seconds (like api does).
|
||||||
|
|
||||||
|
RETURNS
|
||||||
|
0 Success
|
||||||
|
1 Error
|
||||||
|
*/
|
||||||
|
my_bool cio_socket_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout)
|
||||||
|
{
|
||||||
|
if (!cio)
|
||||||
|
return 1;
|
||||||
|
cio->timeout[type]= (timeout > 0) ? timeout * 1000 : -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ cio_socket_get_timeout */
|
||||||
|
/*
|
||||||
|
get timeout value
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
cio_socket_get_timeout
|
||||||
|
cio CIO
|
||||||
|
type timeout type (connect, read, write)
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Returns timeout values for connection-, read or write time out.
|
||||||
|
CIO internally stores all timeout values in milliseconds, but
|
||||||
|
accepts and returns all time values in seconds (like api does).
|
||||||
|
|
||||||
|
RETURNS
|
||||||
|
0...n time out value
|
||||||
|
-1 error
|
||||||
|
*/
|
||||||
|
int cio_socket_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type)
|
||||||
|
{
|
||||||
|
if (!cio)
|
||||||
|
return -1;
|
||||||
|
return cio->timeout[type] / 1000;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ cio_socket_read */
|
||||||
|
/*
|
||||||
|
read from socket
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
cio_socket_read()
|
||||||
|
cio CIO
|
||||||
|
buffer read buffer
|
||||||
|
length buffer length
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
reads up to length bytes into specified buffer. In the event of an
|
||||||
|
error erno is set to indicate it.
|
||||||
|
|
||||||
|
RETURNS
|
||||||
|
1..n number of bytes read
|
||||||
|
0 peer has performed shutdown
|
||||||
|
-1 on error
|
||||||
|
|
||||||
|
*/
|
||||||
|
size_t cio_socket_read(MARIADB_CIO *cio, const uchar *buffer, size_t length)
|
||||||
|
{
|
||||||
|
ssize_t r= -1;
|
||||||
|
#ifndef _WIN32
|
||||||
|
/* don't ignore SIGPIPE globally like in libmysql!! */
|
||||||
|
int read_flags= MSG_NOSIGNAL;
|
||||||
|
#endif
|
||||||
|
struct st_cio_socket *csock= NULL;
|
||||||
|
|
||||||
|
if (!cio || !cio->data)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
csock= (struct st_cio_socket *)cio->data;
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
do {
|
||||||
|
r= recv(csock->socket, (void *)buffer, length, read_flags);
|
||||||
|
} while (r == -1 && errno == EINTR);
|
||||||
|
|
||||||
|
while (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||||
|
&& cio->timeout[CIO_READ_TIMEOUT] > 0)
|
||||||
|
{
|
||||||
|
if (cio_socket_wait_io_or_timeout(cio, TRUE, cio->timeout[CIO_READ_TIMEOUT]) < 1)
|
||||||
|
return -1;
|
||||||
|
do {
|
||||||
|
r= recv(csock->socket, (void *)buffer, length, read_flags);
|
||||||
|
} while (r == -1 && errno == EINTR);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
WSABUF wsaData;
|
||||||
|
DWORD flags= 0,
|
||||||
|
dwBytes= 0;
|
||||||
|
|
||||||
|
/* clear error */
|
||||||
|
errno= 0;
|
||||||
|
wsaData.len = (u_long)length;
|
||||||
|
wsaData.buf = (char*) buffer;
|
||||||
|
|
||||||
|
r = WSARecv(csock->socket, &wsaData, 1, &dwBytes, &flags, NULL, NULL);
|
||||||
|
if (r == SOCKET_ERROR)
|
||||||
|
{
|
||||||
|
errno= WSAGetLastError();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
r= dwBytes;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ cio_socket_async_read */
|
||||||
|
/*
|
||||||
|
read from socket
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
cio_socket_async_read()
|
||||||
|
cio CIO
|
||||||
|
buffer read buffer
|
||||||
|
length buffer length
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
reads up to length bytes into specified buffer. In the event of an
|
||||||
|
error erno is set to indicate it.
|
||||||
|
|
||||||
|
RETURNS
|
||||||
|
1..n number of bytes read
|
||||||
|
0 peer has performed shutdown
|
||||||
|
-1 on error
|
||||||
|
|
||||||
|
*/
|
||||||
|
size_t cio_socket_async_read(MARIADB_CIO *cio, const uchar *buffer, size_t length)
|
||||||
|
{
|
||||||
|
ssize_t r= -1;
|
||||||
|
#ifndef _WIN32
|
||||||
|
int read_flags= MSG_NOSIGNAL | MSG_DONTWAIT;
|
||||||
|
#endif
|
||||||
|
struct st_cio_socket *csock= NULL;
|
||||||
|
|
||||||
|
if (!cio || !cio->data)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
csock= (struct st_cio_socket *)cio->data;
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
r= recv(csock->socket,(void *)buffer, length, read_flags);
|
||||||
|
#else
|
||||||
|
r= recv(csock->socket, buffer, length, 0);
|
||||||
|
#endif
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ cio_socket_async_write */
|
||||||
|
/*
|
||||||
|
write to socket
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
cio_socket_async_write()
|
||||||
|
cio CIO
|
||||||
|
buffer read buffer
|
||||||
|
length buffer length
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
writes up to length bytes to socket. In the event of an
|
||||||
|
error erno is set to indicate it.
|
||||||
|
|
||||||
|
RETURNS
|
||||||
|
1..n number of bytes read
|
||||||
|
0 peer has performed shutdown
|
||||||
|
-1 on error
|
||||||
|
|
||||||
|
*/
|
||||||
|
size_t cio_socket_async_write(MARIADB_CIO *cio, const uchar *buffer, size_t length)
|
||||||
|
{
|
||||||
|
ssize_t r= -1;
|
||||||
|
#ifndef WIN32
|
||||||
|
int write_flags= MSG_NOSIGNAL | MSG_DONTWAIT;
|
||||||
|
#endif
|
||||||
|
struct st_cio_socket *csock= NULL;
|
||||||
|
|
||||||
|
if (!cio || !cio->data)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
csock= (struct st_cio_socket *)cio->data;
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
r= send(csock->socket, buffer, length, write_flags);
|
||||||
|
#else
|
||||||
|
r= send(csock->socket, buffer, length, 0);
|
||||||
|
#endif
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ cio_socket_write */
|
||||||
|
/*
|
||||||
|
write to socket
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
cio_socket_write()
|
||||||
|
cio CIO
|
||||||
|
buffer read buffer
|
||||||
|
length buffer length
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
writes up to length bytes to socket. In the event of an
|
||||||
|
error erno is set to indicate it.
|
||||||
|
|
||||||
|
RETURNS
|
||||||
|
1..n number of bytes read
|
||||||
|
0 peer has performed shutdown
|
||||||
|
-1 on error
|
||||||
|
|
||||||
|
*/
|
||||||
|
size_t cio_socket_write(MARIADB_CIO *cio, const uchar *buffer, size_t length)
|
||||||
|
{
|
||||||
|
ssize_t r= -1;
|
||||||
|
#ifndef _WIN32
|
||||||
|
int send_flags= MSG_NOSIGNAL;
|
||||||
|
#endif
|
||||||
|
struct st_cio_socket *csock= NULL;
|
||||||
|
if (!cio || !cio->data)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
csock= (struct st_cio_socket *)cio->data;
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
do {
|
||||||
|
r= send(csock->socket, buffer, length, send_flags);
|
||||||
|
} while (r == -1 && errno == EINTR);
|
||||||
|
|
||||||
|
while (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK) &&
|
||||||
|
cio->timeout[CIO_WRITE_TIMEOUT] != 0)
|
||||||
|
{
|
||||||
|
if (cio_socket_wait_io_or_timeout(cio, FALSE, cio->timeout[CIO_WRITE_TIMEOUT]) < 1)
|
||||||
|
return -1;
|
||||||
|
do {
|
||||||
|
r= send(csock->socket, buffer, length, send_flags);
|
||||||
|
} while (r == -1 && errno == EINTR);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
WSABUF wsaData;
|
||||||
|
DWORD dwBytes= 0;
|
||||||
|
|
||||||
|
wsaData.len = (u_long)length;
|
||||||
|
wsaData.buf = (char*) buffer;
|
||||||
|
|
||||||
|
r = WSASend(csock->socket, &wsaData, 1, &dwBytes, 0, NULL, NULL);
|
||||||
|
if (r == SOCKET_ERROR) {
|
||||||
|
errno= WSAGetLastError();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
r= dwBytes;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
int cio_socket_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
struct st_cio_socket *csock= NULL;
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
struct pollfd p_fd;
|
||||||
|
#else
|
||||||
|
struct timeval tv= {0,0};
|
||||||
|
fd_set fds, exc_fds;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!cio || !cio->data)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
csock= (struct st_cio_socket *)cio->data;
|
||||||
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
|
p_fd.fd= csock->socket;
|
||||||
|
p_fd.events= (is_read) ? POLLIN : POLLOUT;
|
||||||
|
|
||||||
|
do {
|
||||||
|
rc= poll(&p_fd, 1, timeout);
|
||||||
|
} while (rc == -1 || errno == EINTR);
|
||||||
|
|
||||||
|
if (rc == 0)
|
||||||
|
errno= ETIMEDOUT;
|
||||||
|
#else
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_ZERO(&exc_fds);
|
||||||
|
|
||||||
|
FD_SET(csock->socket, &fds);
|
||||||
|
FD_SET(csock->socket, &exc_fds);
|
||||||
|
|
||||||
|
if (timeout >= 0)
|
||||||
|
{
|
||||||
|
tv.tv_sec= timeout / 1000;
|
||||||
|
tv.tv_usec= (timeout % 1000) * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc= select(0, (is_read) ? &fds : NULL,
|
||||||
|
(is_read) ? NULL : &fds,
|
||||||
|
&exc_fds,
|
||||||
|
(timeout >= 0) ? &tv : NULL);
|
||||||
|
if (rc == SOCKET_ERROR)
|
||||||
|
errno= WSAGetLastError();
|
||||||
|
if (rc == 0)
|
||||||
|
errno= ETIMEDOUT;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
my_bool cio_socket_blocking(MARIADB_CIO *cio, my_bool block, my_bool *previous_mode)
|
||||||
|
{
|
||||||
|
int *sd_flags, save_flags;
|
||||||
|
my_bool tmp;
|
||||||
|
struct st_cio_socket *csock= NULL;
|
||||||
|
|
||||||
|
if (!cio || !cio->data)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
csock= (struct st_cio_socket *)cio->data;
|
||||||
|
sd_flags= &csock->fcntl_mode;
|
||||||
|
save_flags= csock->fcntl_mode;
|
||||||
|
|
||||||
|
if (!previous_mode)
|
||||||
|
previous_mode= &tmp;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
*previous_mode= (*sd_flags & O_NONBLOCK) != 0;
|
||||||
|
*sd_flags = (block) ? *sd_flags & ~O_NONBLOCK : *sd_flags | O_NONBLOCK;
|
||||||
|
{
|
||||||
|
ulong arg= 1 - block;
|
||||||
|
if (ioctlsocket(csock->socket, FIONBIO, (void *)&arg))
|
||||||
|
{
|
||||||
|
csock->fcntl_mode= save_flags;
|
||||||
|
return(WSAGetLastError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#if defined(O_NONBLOCK)
|
||||||
|
*previous_mode= (*sd_flags & O_NONBLOCK) != 0;
|
||||||
|
*sd_flags = (block) ? *sd_flags & ~O_NONBLOCK : *sd_flags | O_NONBLOCK;
|
||||||
|
#elif defined(O_NDELAY)
|
||||||
|
*previous_mode= (*sd_flags & O_NODELAY) != 0;
|
||||||
|
*sd_flags = (block) ? *sd_flags & ~O_NODELAY : *sd_flags | O_NODELAY;
|
||||||
|
#elif defined(FNDELAY)
|
||||||
|
*previous_mode= (*sd_flags & O_FNDELAY) != 0;
|
||||||
|
*sd_flags = (block) ? *sd_flags & ~O_FNDELAY : *sd_flags | O_FNDELAY;
|
||||||
|
#else
|
||||||
|
#error socket blocking is not supported on this platform
|
||||||
|
#endif
|
||||||
|
if (fcntl(csock->socket, F_SETFL, *sd_flags) == -1)
|
||||||
|
{
|
||||||
|
csock->fcntl_mode= save_flags;
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cio_socket_internal_connect(MARIADB_CIO *cio,
|
||||||
|
const struct sockaddr *name,
|
||||||
|
size_t namelen)
|
||||||
|
{
|
||||||
|
int rc= 0;
|
||||||
|
struct st_cio_socket *csock= NULL;
|
||||||
|
int timeout;
|
||||||
|
|
||||||
|
if (!cio || !cio->data)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
csock= (struct st_cio_socket *)cio->data;
|
||||||
|
timeout= cio->timeout[CIO_CONNECT_TIMEOUT];
|
||||||
|
|
||||||
|
/* set non blocking */
|
||||||
|
cio_socket_blocking(cio, 0, 0);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
|
||||||
|
do {
|
||||||
|
rc= connect(csock->socket, (struct sockaddr*) name, (int)namelen);
|
||||||
|
} while (rc == -1 && errno == EINTR);
|
||||||
|
/* in case a timeout values was set we need to check error values
|
||||||
|
EINPROGRESS and EAGAIN */
|
||||||
|
if (timeout != 0 && rc == -1 &&
|
||||||
|
(errno == EINPROGRESS || errno == EAGAIN))
|
||||||
|
{
|
||||||
|
rc= cio_socket_wait_io_or_timeout(cio, FALSE, timeout);
|
||||||
|
if (rc < 1)
|
||||||
|
return -1;
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
socklen_t error_len= sizeof(error);
|
||||||
|
if ((rc = getsockopt(csock->socket, SOL_SOCKET, SO_ERROR,
|
||||||
|
(char *)&error, &error_len)) < 0)
|
||||||
|
return errno;
|
||||||
|
else if (error)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
rc= connect(csock->socket, (struct sockaddr*) name, (int)namelen);
|
||||||
|
if (rc == SOCKET_ERROR)
|
||||||
|
{
|
||||||
|
if (WSAGetLastError() == WSAEWOULDBLOCK)
|
||||||
|
{
|
||||||
|
if (cio_socket_wait_io_or_timeout(cio, FALSE, timeout) < 0)
|
||||||
|
return -1;
|
||||||
|
rc= 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cio_socket_keepalive(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
int opt= 1;
|
||||||
|
struct st_cio_socket *csock= NULL;
|
||||||
|
|
||||||
|
if (!cio || !cio->data)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
csock= (struct st_cio_socket *)cio->data;
|
||||||
|
|
||||||
|
return setsockopt(csock->socket, SOL_SOCKET, SO_KEEPALIVE,
|
||||||
|
#ifndef _WIN32
|
||||||
|
(const void *)&opt, sizeof(opt));
|
||||||
|
#else
|
||||||
|
(char *)&opt, (int)sizeof(opt));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int cio_socket_fast_send(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
int r= 0;
|
||||||
|
struct st_cio_socket *csock= NULL;
|
||||||
|
|
||||||
|
if (!cio || !cio->data)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
csock= (struct st_cio_socket *)cio->data;
|
||||||
|
|
||||||
|
/* Setting IP_TOS is not recommended on Windows. See
|
||||||
|
http://msdn.microsoft.com/en-us/library/windows/desktop/ms738586(v=vs.85).aspx
|
||||||
|
*/
|
||||||
|
#ifndef _WIN32
|
||||||
|
#ifdef IPTOS_THROUGHPUT
|
||||||
|
{
|
||||||
|
int tos = IPTOS_THROUGHPUT;
|
||||||
|
r= setsockopt(csock->socket, IPPROTO_IP, IP_TOS,
|
||||||
|
(const void *)&tos, sizeof(tos));
|
||||||
|
}
|
||||||
|
#endif /* IPTOS_THROUGHPUT */
|
||||||
|
#endif
|
||||||
|
if (!r)
|
||||||
|
{
|
||||||
|
int opt = 1;
|
||||||
|
/* turn off nagle algorithm */
|
||||||
|
r= setsockopt(csock->socket, IPPROTO_TCP, TCP_NODELAY,
|
||||||
|
#ifdef _WIN32
|
||||||
|
(const char *)&opt, (int)sizeof(opt));
|
||||||
|
#else
|
||||||
|
(const void *)&opt, sizeof(opt));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cio_socket_connect_sync_or_async(MARIADB_CIO *cio,
|
||||||
|
const struct sockaddr *name, uint namelen)
|
||||||
|
{
|
||||||
|
MYSQL *mysql= cio->mysql;
|
||||||
|
if (mysql->options.extension && mysql->options.extension->async_context &&
|
||||||
|
mysql->options.extension->async_context->active)
|
||||||
|
{
|
||||||
|
cio_socket_blocking(cio,0, 0);
|
||||||
|
return my_connect_async(cio, name, namelen, cio->timeout[CIO_CONNECT_TIMEOUT]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cio_socket_internal_connect(cio, name, namelen);
|
||||||
|
}
|
||||||
|
|
||||||
|
my_bool cio_socket_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo)
|
||||||
|
{
|
||||||
|
struct st_cio_socket *csock= NULL;
|
||||||
|
|
||||||
|
if (!cio || !cinfo)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!(csock= (struct st_cio_socket *)my_malloc(sizeof(struct st_cio_socket),
|
||||||
|
MYF(MY_WME | MY_ZEROFILL))))
|
||||||
|
{
|
||||||
|
CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0, "");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
cio->data= (void *)csock;
|
||||||
|
csock->socket= -1;
|
||||||
|
cio->mysql= cinfo->mysql;
|
||||||
|
cio->type= cinfo->type;
|
||||||
|
|
||||||
|
if (cinfo->type == CIO_TYPE_UNIXSOCKET)
|
||||||
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
|
#ifdef HAVE_SYS_UN_H
|
||||||
|
struct sockaddr_un UNIXaddr;
|
||||||
|
if ((csock->socket = socket(AF_UNIX,SOCK_STREAM,0)) == SOCKET_ERROR)
|
||||||
|
{
|
||||||
|
CIO_SET_ERROR(cinfo->mysql, CR_SOCKET_CREATE_ERROR, unknown_sqlstate, 0, errno);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
bzero((char*) &UNIXaddr,sizeof(UNIXaddr));
|
||||||
|
UNIXaddr.sun_family = AF_UNIX;
|
||||||
|
strmov(UNIXaddr.sun_path, cinfo->unix_socket);
|
||||||
|
if (cio_socket_connect_sync_or_async(cio, (struct sockaddr *) &UNIXaddr,
|
||||||
|
sizeof(UNIXaddr)))
|
||||||
|
{
|
||||||
|
CIO_SET_ERROR(cinfo->mysql, CR_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||||
|
ER(CR_CONNECTION_ERROR), cinfo->unix_socket, socket_errno);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (cio_socket_blocking(cio, 1, 0) == SOCKET_ERROR)
|
||||||
|
{
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* todo: error, not supported */
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
} else if (cinfo->type == CIO_TYPE_SOCKET)
|
||||||
|
{
|
||||||
|
struct addrinfo hints, *save_res= 0, *bind_res= 0, *res= 0, *bres= 0;
|
||||||
|
char server_port[NI_MAXSERV];
|
||||||
|
int gai_rc;
|
||||||
|
int rc= 0;
|
||||||
|
|
||||||
|
bzero(&server_port, NI_MAXSERV);
|
||||||
|
my_snprintf(server_port, NI_MAXSERV, "%d", cinfo->port);
|
||||||
|
|
||||||
|
/* set hints for getaddrinfo */
|
||||||
|
bzero(&hints, sizeof(hints));
|
||||||
|
hints.ai_protocol= IPPROTO_TCP; /* TCP connections only */
|
||||||
|
hints.ai_family= AF_UNSPEC; /* includes: IPv4, IPv6 or hostname */
|
||||||
|
hints.ai_socktype= SOCK_STREAM;
|
||||||
|
|
||||||
|
/* if client has multiple interfaces, we will bind socket to given
|
||||||
|
* bind_address */
|
||||||
|
if (cinfo->mysql->options.bind_address)
|
||||||
|
{
|
||||||
|
gai_rc= getaddrinfo(cinfo->mysql->options.bind_address, 0,
|
||||||
|
&hints, &res);
|
||||||
|
if (gai_rc != 0)
|
||||||
|
{
|
||||||
|
CIO_SET_ERROR(cinfo->mysql, CR_BIND_ADDR_FAILED, SQLSTATE_UNKNOWN,
|
||||||
|
CER(CR_BIND_ADDR_FAILED), cinfo->mysql->options.bind_address, gai_rc);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Get the address information for the server using getaddrinfo() */
|
||||||
|
gai_rc= getaddrinfo(cinfo->host, server_port, &hints, &res);
|
||||||
|
if (gai_rc != 0)
|
||||||
|
{
|
||||||
|
CIO_SET_ERROR(cinfo->mysql, CR_UNKNOWN_HOST, SQLSTATE_UNKNOWN,
|
||||||
|
ER(CR_UNKNOWN_HOST), cinfo->host, gai_rc);
|
||||||
|
if (bres)
|
||||||
|
freeaddrinfo(bres);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* res is a linked list of addresses for the given hostname. We loop until
|
||||||
|
we are able to connect to one address or all connect attempts failed */
|
||||||
|
for (save_res= res; save_res; save_res= save_res->ai_next)
|
||||||
|
{
|
||||||
|
csock->socket= socket(save_res->ai_family, save_res->ai_socktype,
|
||||||
|
save_res->ai_protocol);
|
||||||
|
if (csock->socket == SOCKET_ERROR)
|
||||||
|
/* Errors will be handled after loop finished */
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (bind_res)
|
||||||
|
{
|
||||||
|
for (bind_res= bres; bind_res; bind_res= bind_res->ai_next)
|
||||||
|
{
|
||||||
|
if (!(rc= bind(csock->socket, bind_res->ai_addr, bind_res->ai_addrlen)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (rc)
|
||||||
|
{
|
||||||
|
closesocket(csock->socket);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rc= cio_socket_connect_sync_or_async(cio, save_res->ai_addr, save_res->ai_addrlen);
|
||||||
|
if (!rc)
|
||||||
|
{
|
||||||
|
/* if (mysql->options.extension && mysql->options.extension->async_context &&
|
||||||
|
mysql->options.extension->async_context->active)
|
||||||
|
break; */
|
||||||
|
if (cio_socket_blocking(cio, 1, 0) == SOCKET_ERROR)
|
||||||
|
{
|
||||||
|
closesocket(csock->socket);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break; /* success! */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(res);
|
||||||
|
freeaddrinfo(bres);
|
||||||
|
|
||||||
|
if (csock->socket == SOCKET_ERROR)
|
||||||
|
{
|
||||||
|
CIO_SET_ERROR(cinfo->mysql, CR_IPSOCK_ERROR, SQLSTATE_UNKNOWN, ER(CR_IPSOCK_ERROR),
|
||||||
|
socket_errno);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* last call to connect 2 failed */
|
||||||
|
if (rc)
|
||||||
|
{
|
||||||
|
CIO_SET_ERROR(cinfo->mysql, CR_CONN_HOST_ERROR, SQLSTATE_UNKNOWN, ER(CR_CONN_HOST_ERROR),
|
||||||
|
cinfo->host, socket_errno);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
/* apply timeouts */
|
||||||
|
if (cio->timeout[CIO_WRITE_TIMEOUT] > 0)
|
||||||
|
setsockopt(csock->socket, SOL_SOCKET, SO_SNDTIMEO, (const char *)&cio->timeout[CIO_WRITE_TIMEOUT], sizeof(int));
|
||||||
|
if (cio->timeout[CIO_READ_TIMEOUT] > 0)
|
||||||
|
setsockopt(csock->socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&cio->timeout[CIO_READ_TIMEOUT], sizeof(int));
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
error:
|
||||||
|
if (cio->data)
|
||||||
|
{
|
||||||
|
my_free((gptr)cio->data);
|
||||||
|
cio->data= NULL;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* {{{ my_bool cio_socket_close() */
|
||||||
|
my_bool cio_socket_close(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
struct st_cio_socket *csock= NULL;
|
||||||
|
int r= 0;
|
||||||
|
|
||||||
|
if (!cio)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (cio->data)
|
||||||
|
{
|
||||||
|
csock= (struct st_cio_socket *)cio->data;
|
||||||
|
if (csock && csock->socket != -1)
|
||||||
|
{
|
||||||
|
r= shutdown(csock->socket ,2);
|
||||||
|
r= closesocket(csock->socket);
|
||||||
|
csock->socket= -1;
|
||||||
|
}
|
||||||
|
my_free((gptr)cio->data);
|
||||||
|
cio->data= NULL;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ my_socket cio_socket_get_handle */
|
||||||
|
my_bool cio_socket_get_handle(MARIADB_CIO *cio, void *handle)
|
||||||
|
{
|
||||||
|
if (cio && cio->data && handle)
|
||||||
|
{
|
||||||
|
*(my_socket *)handle= ((struct st_cio_socket *)cio->data)->socket;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ my_bool cio_socket_is_blocking(MARIADB_CIO *cio) */
|
||||||
|
my_bool cio_socket_is_blocking(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
struct st_cio_socket *csock= NULL;
|
||||||
|
my_bool r;
|
||||||
|
|
||||||
|
if (!cio || !cio->data)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
csock= (struct st_cio_socket *)cio->data;
|
||||||
|
r = !(csock->fcntl_mode & O_NONBLOCK);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ my_bool cio_socket_is_alive(MARIADB_CIO *cio) */
|
||||||
|
my_bool cio_socket_is_alive(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
struct st_cio_socket *csock= NULL;
|
||||||
|
#ifndef _WIN32
|
||||||
|
struct pollfd poll_fd;
|
||||||
|
#else
|
||||||
|
FD_SET sfds;
|
||||||
|
struct timeval tv= {0,0};
|
||||||
|
#endif
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if (!cio || !cio->data)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
csock= (struct st_cio_socket *)cio->data;
|
||||||
|
#ifndef _WIN32
|
||||||
|
memset(&poll_fd, 0, sizeof(struct pollfd));
|
||||||
|
poll_fd.events= POLLPRI | POLLIN;
|
||||||
|
poll_fd.fd= csock->socket;
|
||||||
|
|
||||||
|
res= poll(&poll_fd, 1, 0);
|
||||||
|
if (res <= 0) /* timeout or error */
|
||||||
|
return FALSE;
|
||||||
|
if (!(poll_fd.revents & (POLLIN | POLLPRI)))
|
||||||
|
return FALSE;
|
||||||
|
return TRUE;
|
||||||
|
#else
|
||||||
|
/* We can't use the WSAPoll function, it's broken :-(
|
||||||
|
(see Windows 8 Bugs 309411 - WSAPoll does not report failed connections)
|
||||||
|
Instead we need to use select function:
|
||||||
|
If TIMEVAL is initialized to {0, 0}, select will return immediately;
|
||||||
|
this is used to poll the state of the selected sockets.
|
||||||
|
*/
|
||||||
|
FD_ZERO(&sfds);
|
||||||
|
FD_SET(csock->socket, &sfds);
|
||||||
|
|
||||||
|
res= select(csock->socket + 1, &sfds, NULL, NULL, &tv);
|
||||||
|
if (res > 0 && FD_ISSET(csock->socket, &sfds))
|
||||||
|
return TRUE;
|
||||||
|
return FALSE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
/* }}} */
|
687
plugins/builtin/ma_schannel.c
Normal file
687
plugins/builtin/ma_schannel.c
Normal file
@@ -0,0 +1,687 @@
|
|||||||
|
/************************************************************************************
|
||||||
|
Copyright (C) 2014 MariaDB Corporation Ab
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with this library; if not see <http://www.gnu.org/licenses>
|
||||||
|
or write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
|
||||||
|
|
||||||
|
Author: Georg Richter
|
||||||
|
|
||||||
|
*************************************************************************************/
|
||||||
|
#include "ma_schannel.h"
|
||||||
|
|
||||||
|
#define SC_IO_BUFFER_SIZE 0x4000
|
||||||
|
|
||||||
|
/* {{{ LPBYTE ma_schannel_load_pem(const char *PemFileName, DWORD *buffer_len) */
|
||||||
|
/*
|
||||||
|
Load a pem or clr file and convert it to a binary DER object
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
ma_schannel_load_pem()
|
||||||
|
PemFileName name of the pem file (in)
|
||||||
|
buffer_len length of the converted DER binary
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Loads a X509 file (ca, certification, key or clr) into memory and converts
|
||||||
|
it to a DER binary object. This object can be decoded and loaded into
|
||||||
|
a schannel crypto context.
|
||||||
|
If the function failed, error can be retrieved by GetLastError()
|
||||||
|
The returned binary object must be freed by caller.
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
NULL if the conversion failed or file was not found
|
||||||
|
LPBYTE * a pointer to a binary der object
|
||||||
|
buffer_len will contain the length of binary der object
|
||||||
|
*/
|
||||||
|
static LPBYTE ma_schannel_load_pem(const char *PemFileName, DWORD *buffer_len)
|
||||||
|
{
|
||||||
|
HANDLE hfile;
|
||||||
|
char *buffer= NULL;
|
||||||
|
DWORD dwBytesRead= 0;
|
||||||
|
LPBYTE der_buffer= NULL;
|
||||||
|
DWORD der_buffer_length;
|
||||||
|
DWORD x;
|
||||||
|
|
||||||
|
if (buffer_len == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
|
||||||
|
if ((hfile= CreateFile(PemFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL, NULL )) == INVALID_HANDLE_VALUE)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!(*buffer_len = GetFileSize(hfile, NULL)))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
if (!(buffer= LocalAlloc(0, *buffer_len + 1)))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
if (!ReadFile(hfile, buffer, *buffer_len, &dwBytesRead, NULL))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
CloseHandle(hfile);
|
||||||
|
|
||||||
|
/* calculate the length of DER binary */
|
||||||
|
if (!CryptStringToBinaryA(buffer, *buffer_len, CRYPT_STRING_BASE64HEADER,
|
||||||
|
NULL, &der_buffer_length, NULL, NULL))
|
||||||
|
goto end;
|
||||||
|
/* allocate DER binary buffer */
|
||||||
|
if (!(der_buffer= (LPBYTE)LocalAlloc(0, der_buffer_length)))
|
||||||
|
goto end;
|
||||||
|
/* convert to DER binary */
|
||||||
|
if (!CryptStringToBinaryA(buffer, *buffer_len, CRYPT_STRING_BASE64HEADER,
|
||||||
|
der_buffer, &der_buffer_length, NULL, NULL))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
*buffer_len= der_buffer_length;
|
||||||
|
LocalFree(buffer);
|
||||||
|
|
||||||
|
return der_buffer;
|
||||||
|
|
||||||
|
end:
|
||||||
|
if (hfile != INVALID_HANDLE_VALUE)
|
||||||
|
CloseHandle(hfile);
|
||||||
|
if (buffer)
|
||||||
|
LocalFree(buffer);
|
||||||
|
if (der_buffer)
|
||||||
|
LocalFree(der_buffer);
|
||||||
|
*buffer_len= 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ CERT_CONTEXT *ma_schannel_create_cert_context(const char *pem_file) */
|
||||||
|
/*
|
||||||
|
Create a certification context from ca or cert file
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
ma_schannel_create_cert_context()
|
||||||
|
pem_file name of certificate or ca file
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Loads a PEM file (certificate authority or certificate) creates a certification
|
||||||
|
context and loads the binary representation into context.
|
||||||
|
The returned context must be freed by caller.
|
||||||
|
If the function failed, error can be retrieved by GetLastError().
|
||||||
|
|
||||||
|
RETURNS
|
||||||
|
NULL If loading of the file or creating context failed
|
||||||
|
CERT_CONTEXT * A pointer to a certification context structure
|
||||||
|
*/
|
||||||
|
CERT_CONTEXT *ma_schannel_create_cert_context(const char *pem_file)
|
||||||
|
{
|
||||||
|
DWORD der_buffer_length;
|
||||||
|
LPBYTE der_buffer= NULL;
|
||||||
|
|
||||||
|
CERT_CONTEXT *ctx= NULL;
|
||||||
|
|
||||||
|
/* create DER binary object from ca/certification file */
|
||||||
|
if (!(der_buffer= ma_schannel_load_pem(pem_file, (DWORD *)&der_buffer_length)))
|
||||||
|
goto end;
|
||||||
|
ctx= CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
||||||
|
der_buffer, der_buffer_length);
|
||||||
|
end:
|
||||||
|
if (der_buffer)
|
||||||
|
LocalFree(der_buffer);
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ PCCRL_CONTEXT ma_schannel_create_crl_context(const char *pem_file) */
|
||||||
|
/*
|
||||||
|
Create a crl context from crlfile
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
ma_schannel_create_crl_context()
|
||||||
|
pem_file name of certificate or ca file
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Loads a certification revocation list file, creates a certification
|
||||||
|
context and loads the binary representation into crl context.
|
||||||
|
The returned context must be freed by caller.
|
||||||
|
If the function failed, error can be retrieved by GetLastError().
|
||||||
|
|
||||||
|
RETURNS
|
||||||
|
NULL If loading of the file or creating context failed
|
||||||
|
PCCRL_CONTEXT A pointer to a certification context structure
|
||||||
|
*/
|
||||||
|
PCCRL_CONTEXT ma_schannel_create_crl_context(const char *pem_file)
|
||||||
|
{
|
||||||
|
DWORD der_buffer_length;
|
||||||
|
LPBYTE der_buffer= NULL;
|
||||||
|
|
||||||
|
PCCRL_CONTEXT ctx= NULL;
|
||||||
|
|
||||||
|
/* load ca pem file into memory */
|
||||||
|
if (!(der_buffer= ma_schannel_load_pem(pem_file, (DWORD *)&der_buffer_length)))
|
||||||
|
goto end;
|
||||||
|
ctx= CertCreateCRLContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
||||||
|
der_buffer, der_buffer_length);
|
||||||
|
end:
|
||||||
|
if (der_buffer)
|
||||||
|
LocalFree(der_buffer);
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ my_bool ma_schannel_load_private_key(CERT_CONTEXT *ctx, char *key_file) */
|
||||||
|
/*
|
||||||
|
Load privte key into context
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
ma_schannel_load_private_key()
|
||||||
|
ctx pointer to a certification context
|
||||||
|
pem_file name of certificate or ca file
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Loads a certification revocation list file, creates a certification
|
||||||
|
context and loads the binary representation into crl context.
|
||||||
|
The returned context must be freed by caller.
|
||||||
|
If the function failed, error can be retrieved by GetLastError().
|
||||||
|
|
||||||
|
RETURNS
|
||||||
|
NULL If loading of the file or creating context failed
|
||||||
|
PCCRL_CONTEXT A pointer to a certification context structure
|
||||||
|
*/
|
||||||
|
|
||||||
|
my_bool ma_schannel_load_private_key(CERT_CONTEXT *ctx, char *key_file)
|
||||||
|
{
|
||||||
|
DWORD der_buffer_len= 0;
|
||||||
|
LPBYTE der_buffer= NULL;
|
||||||
|
DWORD priv_key_len= 0;
|
||||||
|
LPBYTE priv_key= NULL;
|
||||||
|
HCRYPTPROV crypt_prov= NULL;
|
||||||
|
HCRYPTKEY crypt_key= NULL;
|
||||||
|
CRYPT_KEY_PROV_INFO kpi;
|
||||||
|
my_bool rc= 0;
|
||||||
|
|
||||||
|
/* load private key into der binary object */
|
||||||
|
if (!(der_buffer= ma_schannel_load_pem(key_file, &der_buffer_len)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* determine required buffer size for decoded private key */
|
||||||
|
if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
||||||
|
PKCS_RSA_PRIVATE_KEY,
|
||||||
|
der_buffer, der_buffer_len,
|
||||||
|
0, NULL,
|
||||||
|
NULL, &priv_key_len))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
/* allocate buffer for decoded private key */
|
||||||
|
if (!(priv_key= LocalAlloc(0, priv_key_len)))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
/* decode */
|
||||||
|
if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
||||||
|
PKCS_RSA_PRIVATE_KEY,
|
||||||
|
der_buffer, der_buffer_len,
|
||||||
|
0, NULL,
|
||||||
|
priv_key, &priv_key_len))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
/* Acquire context */
|
||||||
|
if (!CryptAcquireContext(&crypt_prov, "cio_schannel", MS_ENHANCED_PROV, PROV_RSA_FULL, 0))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
/* ... and import the private key */
|
||||||
|
if (!CryptImportKey(crypt_prov, priv_key, priv_key_len, NULL, 0, &crypt_key))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
SecureZeroMemory(&kpi, sizeof(kpi));
|
||||||
|
kpi.pwszContainerName = "cio-schanel";
|
||||||
|
kpi.dwKeySpec = AT_KEYEXCHANGE;
|
||||||
|
kpi.dwFlags = CRYPT_MACHINE_KEYSET;
|
||||||
|
|
||||||
|
/* assign private key to certificate context */
|
||||||
|
if (CertSetCertificateContextProperty(ctx, CERT_KEY_PROV_INFO_PROP_ID, 0, &kpi))
|
||||||
|
rc= 1;
|
||||||
|
end:
|
||||||
|
if (der_buffer)
|
||||||
|
LocalFree(der_buffer);
|
||||||
|
if (priv_key)
|
||||||
|
{
|
||||||
|
if (crypt_key)
|
||||||
|
CryptDestroyKey(crypt_key);
|
||||||
|
LocalFree(priv_key);
|
||||||
|
if (!rc)
|
||||||
|
if (crypt_prov)
|
||||||
|
CryptReleaseContext(crypt_prov, 0);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_CIO *cio, my_bool InitialRead, SecBuffer *pExtraData) */
|
||||||
|
/*
|
||||||
|
perform handshake loop
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
ma_schannel_handshake_loop()
|
||||||
|
cio Pointer to an Communication/IO structure
|
||||||
|
InitialRead TRUE if it's the very first read
|
||||||
|
ExtraData Pointer to an SecBuffer which contains extra data (sent by application)
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_CIO *cio, my_bool InitialRead, SecBuffer *pExtraData)
|
||||||
|
{
|
||||||
|
SecBufferDesc OutBuffer, InBuffer;
|
||||||
|
SecBuffer InBuffers[2], OutBuffers[1];
|
||||||
|
DWORD dwSSPIFlags, dwSSPIOutFlags, cbData, cbIoBuffer;
|
||||||
|
TimeStamp tsExpiry;
|
||||||
|
SECURITY_STATUS rc;
|
||||||
|
PUCHAR IoBuffer;
|
||||||
|
BOOL fDoRead;
|
||||||
|
MARIADB_SSL *cssl= cio->cssl;
|
||||||
|
SC_CTX *sctx= (SC_CTX *)cssl->data;
|
||||||
|
|
||||||
|
|
||||||
|
dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
|
||||||
|
ISC_REQ_REPLAY_DETECT |
|
||||||
|
ISC_REQ_CONFIDENTIALITY |
|
||||||
|
ISC_RET_EXTENDED_ERROR |
|
||||||
|
ISC_REQ_ALLOCATE_MEMORY |
|
||||||
|
ISC_REQ_STREAM;
|
||||||
|
|
||||||
|
|
||||||
|
/* Allocate data buffer */
|
||||||
|
if (!(IoBuffer = LocalAlloc(LMEM_FIXED, SC_IO_BUFFER_SIZE)))
|
||||||
|
return SEC_E_INSUFFICIENT_MEMORY;
|
||||||
|
|
||||||
|
cbIoBuffer = 0;
|
||||||
|
fDoRead = InitialRead;
|
||||||
|
|
||||||
|
/* handshake loop: We will leave a handshake is finished
|
||||||
|
or an error occurs */
|
||||||
|
|
||||||
|
rc = SEC_I_CONTINUE_NEEDED;
|
||||||
|
|
||||||
|
while (rc == SEC_I_CONTINUE_NEEDED ||
|
||||||
|
rc == SEC_E_INCOMPLETE_MESSAGE ||
|
||||||
|
rc == SEC_I_INCOMPLETE_CREDENTIALS )
|
||||||
|
{
|
||||||
|
/* Read data */
|
||||||
|
if (rc == SEC_E_INCOMPLETE_MESSAGE ||
|
||||||
|
!cbIoBuffer)
|
||||||
|
{
|
||||||
|
if(fDoRead)
|
||||||
|
{
|
||||||
|
cbData = cio->methods->read(cio, IoBuffer + cbIoBuffer, SC_IO_BUFFER_SIZE - cbIoBuffer, 0);
|
||||||
|
if (cbData == SOCKET_ERROR || cbData == 0)
|
||||||
|
{
|
||||||
|
rc = SEC_E_INTERNAL_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cbIoBuffer += cbData;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fDoRead = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* input buffers
|
||||||
|
First buffer stores data received from server. leftover data
|
||||||
|
will be stored in second buffer with BufferType SECBUFFER_EXTRA */
|
||||||
|
|
||||||
|
InBuffers[0].pvBuffer = IoBuffer;
|
||||||
|
InBuffers[0].cbBuffer = cbIoBuffer;
|
||||||
|
InBuffers[0].BufferType = SECBUFFER_TOKEN;
|
||||||
|
|
||||||
|
InBuffers[1].pvBuffer = NULL;
|
||||||
|
InBuffers[1].cbBuffer = 0;
|
||||||
|
InBuffers[1].BufferType = SECBUFFER_EMPTY;
|
||||||
|
|
||||||
|
InBuffer.cBuffers = 2;
|
||||||
|
InBuffer.pBuffers = InBuffers;
|
||||||
|
InBuffer.ulVersion = SECBUFFER_VERSION;
|
||||||
|
|
||||||
|
|
||||||
|
/* output buffer */
|
||||||
|
OutBuffers[0].pvBuffer = NULL;
|
||||||
|
OutBuffers[0].BufferType= SECBUFFER_TOKEN;
|
||||||
|
OutBuffers[0].cbBuffer = 0;
|
||||||
|
|
||||||
|
OutBuffer.cBuffers = 1;
|
||||||
|
OutBuffer.pBuffers = OutBuffers;
|
||||||
|
OutBuffer.ulVersion = SECBUFFER_VERSION;
|
||||||
|
|
||||||
|
|
||||||
|
rc = InitializeSecurityContextA(&sctx->CredHdl,
|
||||||
|
&sctx->ctxt,
|
||||||
|
NULL,
|
||||||
|
dwSSPIFlags,
|
||||||
|
0,
|
||||||
|
SECURITY_NATIVE_DREP,
|
||||||
|
&InBuffer,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
&OutBuffer,
|
||||||
|
&dwSSPIOutFlags,
|
||||||
|
&tsExpiry );
|
||||||
|
|
||||||
|
|
||||||
|
if (rc == SEC_E_OK ||
|
||||||
|
rc == SEC_I_CONTINUE_NEEDED ||
|
||||||
|
FAILED(rc) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))
|
||||||
|
{
|
||||||
|
if(OutBuffers[0].cbBuffer && OutBuffers[0].pvBuffer)
|
||||||
|
{
|
||||||
|
cbData= cio->methods->write(cio, (uchar *)OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer);
|
||||||
|
if(cbData == SOCKET_ERROR || cbData == 0)
|
||||||
|
{
|
||||||
|
FreeContextBuffer(OutBuffers[0].pvBuffer);
|
||||||
|
DeleteSecurityContext(&sctx->ctxt);
|
||||||
|
return SEC_E_INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free output context buffer */
|
||||||
|
FreeContextBuffer(OutBuffers[0].pvBuffer);
|
||||||
|
OutBuffers[0].pvBuffer = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if we need to read more data */
|
||||||
|
switch (rc) {
|
||||||
|
case SEC_E_INCOMPLETE_MESSAGE:
|
||||||
|
/* we didn't receive all data, so just continue loop */
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
case SEC_E_OK:
|
||||||
|
/* handshake completed, but we need to check if extra
|
||||||
|
data was sent (which contains encrypted application data) */
|
||||||
|
if (InBuffers[1].BufferType == SECBUFFER_EXTRA)
|
||||||
|
{
|
||||||
|
if (!(pExtraData->pvBuffer= LocalAlloc(0, InBuffers[1].cbBuffer)))
|
||||||
|
return SEC_E_INSUFFICIENT_MEMORY;
|
||||||
|
|
||||||
|
MoveMemory(pExtraData->pvBuffer, IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer );
|
||||||
|
pExtraData->BufferType = SECBUFFER_TOKEN;
|
||||||
|
pExtraData->cbBuffer = InBuffers[1].cbBuffer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pExtraData->BufferType= SECBUFFER_EMPTY;
|
||||||
|
pExtraData->pvBuffer= NULL;
|
||||||
|
pExtraData->cbBuffer= 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SEC_I_INCOMPLETE_CREDENTIALS:
|
||||||
|
/* Provided credentials didn't contain a valid client certificate.
|
||||||
|
We will try to connect anonymously, using current credentials */
|
||||||
|
fDoRead= FALSE;
|
||||||
|
rc= SEC_I_CONTINUE_NEEDED;
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (FAILED(rc))
|
||||||
|
goto loopend;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( InBuffers[1].BufferType == SECBUFFER_EXTRA )
|
||||||
|
{
|
||||||
|
MoveMemory( IoBuffer, IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer );
|
||||||
|
cbIoBuffer = InBuffers[1].cbBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
cbIoBuffer = 0;
|
||||||
|
}
|
||||||
|
loopend:
|
||||||
|
if (FAILED(rc))
|
||||||
|
DeleteSecurityContext(&sctx->ctxt);
|
||||||
|
LocalFree(IoBuffer);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ SECURITY_STATUS ma_schannel_client_handshake(MARIADB_SSL *cssl) */
|
||||||
|
/*
|
||||||
|
performs client side handshake
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
ma_schannel_client_handshake()
|
||||||
|
cssl Pointer to a MARIADB_SSL structure
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
initiates a client/server handshake. This function can be used
|
||||||
|
by clients only
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
SEC_E_OK on success
|
||||||
|
*/
|
||||||
|
|
||||||
|
SECURITY_STATUS ma_schannel_client_handshake(MARIADB_SSL *cssl)
|
||||||
|
{
|
||||||
|
MARIADB_CIO *cio;
|
||||||
|
SECURITY_STATUS sRet;
|
||||||
|
DWORD OutFlags;
|
||||||
|
DWORD r;
|
||||||
|
SC_CTX *sctx;
|
||||||
|
SecBuffer ExtraData;
|
||||||
|
DWORD SFlags= ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
|
||||||
|
ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR |
|
||||||
|
ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
|
||||||
|
|
||||||
|
SecBufferDesc BufferIn, BufferOut;
|
||||||
|
SecBuffer BuffersOut[1], BuffersIn[2];
|
||||||
|
|
||||||
|
if (!cssl || !cssl->cio || !cssl->data)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
cio= cssl->cio;
|
||||||
|
sctx= (SC_CTX *)cssl->data;
|
||||||
|
|
||||||
|
/* Initialie securifty context */
|
||||||
|
BuffersOut[0].BufferType= SECBUFFER_TOKEN;
|
||||||
|
BuffersOut[0].cbBuffer= 0;
|
||||||
|
BuffersOut[0].pvBuffer= NULL;
|
||||||
|
|
||||||
|
BufferOut.cBuffers= 1;
|
||||||
|
BufferOut.pBuffers= BuffersOut;
|
||||||
|
BufferOut.ulVersion= SECBUFFER_VERSION;
|
||||||
|
|
||||||
|
sRet = InitializeSecurityContext(&sctx->CredHdl,
|
||||||
|
NULL,
|
||||||
|
cio->mysql->host,
|
||||||
|
SFlags,
|
||||||
|
0,
|
||||||
|
SECURITY_NATIVE_DREP,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
&sctx->ctxt,
|
||||||
|
&BufferOut,
|
||||||
|
&OutFlags,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if(sRet != SEC_I_CONTINUE_NEEDED)
|
||||||
|
return sRet;
|
||||||
|
|
||||||
|
/* send client hello packaet */
|
||||||
|
if(BuffersOut[0].cbBuffer != 0 && BuffersOut[0].pvBuffer != NULL)
|
||||||
|
{
|
||||||
|
r= cio->methods->write(cio, (uchar *)BuffersOut[0].pvBuffer, BuffersOut[0].cbBuffer);
|
||||||
|
if (r <= 0)
|
||||||
|
{
|
||||||
|
sRet= SEC_E_INTERNAL_ERROR;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ma_schannel_handshake_loop(cio, TRUE, &ExtraData);
|
||||||
|
|
||||||
|
end:
|
||||||
|
FreeContextBuffer(BuffersOut[0].pvBuffer);
|
||||||
|
DeleteSecurityContext(&sctx->ctxt);
|
||||||
|
return sRet;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ static PUCHAR ma_schannel_alloc_iobuffer(CtxtHandle *Context, DWORD *BufferLength) */
|
||||||
|
/*
|
||||||
|
alloates an IO Buffer for ssl communication
|
||||||
|
|
||||||
|
SYNOPSUS
|
||||||
|
ma_schannel_alloc_iobuffer()
|
||||||
|
Context SChannel context handle
|
||||||
|
BufferLength a pointer for the buffer length
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
calculates the required buffer length and allocates a memory buffer for en- and
|
||||||
|
decryption. The calculated buffer length will be returned in BufferLength pointer.
|
||||||
|
The allocated buffer needs to be freed at end of the connection.
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
NULL if an error occured
|
||||||
|
PUCHAR an IO Buffer
|
||||||
|
*/
|
||||||
|
static PUCHAR ma_schannel_alloc_iobuffer(CtxtHandle *Context, DWORD *BufferLength)
|
||||||
|
{
|
||||||
|
SecPkgContext_StreamSizes StreamSizes;
|
||||||
|
|
||||||
|
PUCHAR Buffer= NULL;
|
||||||
|
|
||||||
|
if (!BufferLength || QueryContextAttributes(Context, SECPKG_ATTR_STREAM_SIZES, &StreamSizes) != SEC_E_OK)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Calculate BufferLength */
|
||||||
|
*BufferLength= StreamSizes.cbHeader + StreamSizes.cbTrailer + StreamSizes.cbMaximumMessage;
|
||||||
|
|
||||||
|
Buffer= LocalAlloc(LMEM_FIXED, *BufferLength);
|
||||||
|
return Buffer;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
/* {{{ SECURITY_STATUS ma_schannel_read(MARIADB_CIO *cio, PCredHandle phCreds, CtxtHandle * phContext) */
|
||||||
|
/*
|
||||||
|
Reads encrypted data from a SSL stream and decrypts it.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
ma_schannel_read
|
||||||
|
cio pointer to Communication IO structure
|
||||||
|
phContext a context handle
|
||||||
|
DecryptLength size of decrypted buffer
|
||||||
|
ReadBuffer Buffer for decrypted data
|
||||||
|
ReadBufferSize size of ReadBuffer
|
||||||
|
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Reads decrypted data from a SSL stream and encrypts it.
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
SEC_E_OK on success
|
||||||
|
SEC_E_* if an error occured
|
||||||
|
*/
|
||||||
|
|
||||||
|
SECURITY_STATUS ma_schannel_read(MARIADB_CIO *cio,
|
||||||
|
PCredHandle phCreds,
|
||||||
|
CtxtHandle * phContext,
|
||||||
|
DWORD *DecryptLength,
|
||||||
|
uchar *ReadBuffer,
|
||||||
|
DWORD ReadBufferSize)
|
||||||
|
{
|
||||||
|
DWORD dwBytesRead= 0;
|
||||||
|
DWORD dwOffset= 0;
|
||||||
|
SC_CTX *sctx;
|
||||||
|
SECURITY_STATUS sRet= 0;
|
||||||
|
SecBuffersDesc Msg;
|
||||||
|
SecBuffer Buffers[4],
|
||||||
|
ExtraBuffer,
|
||||||
|
*pData, *pExtra;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!cio || !cio->methods || !cio->methods->read || !cio->cssl || !cio->cssl->data | !DecryptLength)
|
||||||
|
return SEC_E_INTERNAL_ERROR;
|
||||||
|
|
||||||
|
sctx= (SC_CTX *)cio->cssl->data;
|
||||||
|
*DecryptLength= 0;
|
||||||
|
|
||||||
|
/* Allocate IoBuffer */
|
||||||
|
if (!sctx->IoBuffer)
|
||||||
|
{
|
||||||
|
if (!(sctx->IoBuffer= ma_schannel_alloc_iobuffer(&sctx->ctxt, &sctx->IoBufferSize)))
|
||||||
|
{
|
||||||
|
/* todo: error */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (!dwOffset || sRet == SEC_E_INCOMPLETE_MESSAGE)
|
||||||
|
{
|
||||||
|
dwBytesRead= cio->methods->read(cio, sctx->IoBuffer + dwOffset, cbIoBufferLength - dwOffset);
|
||||||
|
if (dwBytesRead == 0)
|
||||||
|
{
|
||||||
|
/* server closed connection */
|
||||||
|
// todo: error
|
||||||
|
printf("Server closed connection\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (dwBytesRead < 0)
|
||||||
|
{
|
||||||
|
printf("Socket error\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ZeroMem(Buffers, sizeof(SecBuffer) * 4);
|
||||||
|
Buffers[0].pvBuffer= sctx->IoBuffer;
|
||||||
|
Buffers[0].cbBuffer= dwOffset;
|
||||||
|
|
||||||
|
Buffers[0].BufferType= SECBUFFER_DATA;
|
||||||
|
Buffers[1].BufferType=
|
||||||
|
Buffers[2].BufferType=
|
||||||
|
Buffers[3].BufferType= SECBUFFER_EMPTY;
|
||||||
|
|
||||||
|
Msg.ulVersion= SECBUFFER_VERSION; // Version number
|
||||||
|
Msg.cBuffers= 4;
|
||||||
|
Msg.pBuffers= Buffers;
|
||||||
|
|
||||||
|
sRet = DecryptMessage(phContext, &Msg, 0, NULL);
|
||||||
|
|
||||||
|
/* Check for possible errors: we continue in case context has
|
||||||
|
expired or renogitiation is required */
|
||||||
|
if (sRet != SEC_E_OK && sRet != SEC_I_CONTEXT_EXPIRED &&
|
||||||
|
sRet != SEC_I_RENEGOTIARE)
|
||||||
|
{
|
||||||
|
// set error
|
||||||
|
return sRet;
|
||||||
|
}
|
||||||
|
|
||||||
|
pData= pExtra= NULL;
|
||||||
|
for (i=0; i < 4; i++)
|
||||||
|
{
|
||||||
|
if (!pData && Buffers[i].BufferType == SECBUFFER_DATA)
|
||||||
|
pData= &Buffers[i];
|
||||||
|
if (!pExtra && Buffers[i].BufferType == SECBUFFER_EXTRA)
|
||||||
|
pExtra= &Buffers[i];
|
||||||
|
if (pData && pExtra)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pData && pData->cbBuffer)
|
||||||
|
{
|
||||||
|
memcpy(ReadBuffer + *DecrypthLength, pData->pvBuffer, pData->cbBuffer);
|
||||||
|
*DecryptLength+= pData->cbBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pExtra)
|
||||||
|
{
|
||||||
|
MoveMemory(sctx->IoBuffer, pExtra->pvBuffer, pExtra->cbBuffer);
|
||||||
|
dwOffset= pExtra->cbBuffer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
dwOffset= 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* }}} */
|
72
plugins/builtin/ma_schannel.h
Normal file
72
plugins/builtin/ma_schannel.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/************************************************************************************
|
||||||
|
Copyright (C) 2014 MariaDB Corporation Ab
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with this library; if not see <http://www.gnu.org/licenses>
|
||||||
|
or write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
|
||||||
|
|
||||||
|
Author: Georg Richter
|
||||||
|
|
||||||
|
*************************************************************************************/
|
||||||
|
#ifndef _ma_schannel_h_
|
||||||
|
#define _ma_schannel_h_
|
||||||
|
|
||||||
|
#define SECURITY_WIN32
|
||||||
|
#include <my_global.h>
|
||||||
|
#include <my_sys.h>
|
||||||
|
#include <ma_common.h>
|
||||||
|
#include <ma_cio.h>
|
||||||
|
#include <errmsg.h>
|
||||||
|
#include <mysql/client_plugin.h>
|
||||||
|
|
||||||
|
typedef void VOID;
|
||||||
|
|
||||||
|
#include <wincrypt.h>
|
||||||
|
#define SECURITY_WIN32
|
||||||
|
#include <security.h>
|
||||||
|
#include <schnlsp.h>
|
||||||
|
#undef SECURITY_WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
#define SC_IO_BUFFER_SIZE 0x4000
|
||||||
|
|
||||||
|
CERT_CONTEXT *ma_schannel_create_cert_context(const char *pem_file);
|
||||||
|
SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_CIO *cio, my_bool InitialRead, SecBuffer *pExtraData);
|
||||||
|
my_bool ma_schannel_load_private_key(CERT_CONTEXT *ctx, char *key_file);
|
||||||
|
PCCRL_CONTEXT ma_schannel_create_crl_context(const char *pem_file);
|
||||||
|
|
||||||
|
#ifndef HAVE_SCHANNEL_DEFAULT
|
||||||
|
#define my_snprintf snprintf
|
||||||
|
#define my_vsnprintf vsnprintf
|
||||||
|
#undef SAFE_MUTEX
|
||||||
|
#endif
|
||||||
|
#include <my_pthread.h>
|
||||||
|
|
||||||
|
struct st_schannel {
|
||||||
|
HCERTSTORE cert_store;
|
||||||
|
CERT_CONTEXT *client_cert_ctx;
|
||||||
|
CERT_CONTEXT *client_ca_ctx;
|
||||||
|
CRL_CONTEXT *client_crl_ctx;
|
||||||
|
CredHandle CredHdl;
|
||||||
|
PUCHAR IoBuffer;
|
||||||
|
DWORD IoBufferSize;
|
||||||
|
PUCHAR DecryptBuffer;
|
||||||
|
DWORD DecryptBufferSize;
|
||||||
|
DWORD DecryptBufferLength;
|
||||||
|
CtxtHandle ctxt;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct st_schannel SC_CTX;
|
||||||
|
|
||||||
|
#endif /* _ma_schannel_h_ */
|
@@ -1,36 +1,10 @@
|
|||||||
/************************************************************************************
|
|
||||||
Copyright (C) 2012-2015 Monty Program AB, MariaDB Corporation AB,
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not see <http://www.gnu.org/licenses>
|
|
||||||
or write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
|
|
||||||
|
|
||||||
Part of this code includes code from the PHP project which
|
|
||||||
is freely available from http://www.php.net
|
|
||||||
|
|
||||||
Originally written by Sergei Golubchik
|
|
||||||
*************************************************************************************/
|
|
||||||
#include <my_global.h>
|
#include <my_global.h>
|
||||||
#include <my_sys.h>
|
#include <my_sys.h>
|
||||||
#include <m_string.h>
|
#include <m_string.h>
|
||||||
#include <errmsg.h>
|
#include <errmsg.h>
|
||||||
#include <ma_common.h>
|
#include <ma_common.h>
|
||||||
#include <mysql/client_plugin.h>
|
#include <mysql/client_plugin.h>
|
||||||
#include <violite.h>
|
#include <ma_cio.h>
|
||||||
#ifdef HAVE_OPENSSL
|
|
||||||
#include <ma_secure.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct st_mysql_client_plugin_AUTHENTICATION auth_plugin_t;
|
typedef struct st_mysql_client_plugin_AUTHENTICATION auth_plugin_t;
|
||||||
static int client_mpvio_write_packet(struct st_plugin_vio*, const uchar*, size_t);
|
static int client_mpvio_write_packet(struct st_plugin_vio*, const uchar*, size_t);
|
||||||
@@ -39,10 +13,12 @@ static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql);
|
|||||||
extern void read_user_name(char *name);
|
extern void read_user_name(char *name);
|
||||||
extern uchar *ma_send_connect_attr(MYSQL *mysql, uchar *buffer);
|
extern uchar *ma_send_connect_attr(MYSQL *mysql, uchar *buffer);
|
||||||
|
|
||||||
|
/*
|
||||||
#define compile_time_assert(A) \
|
#define compile_time_assert(A) \
|
||||||
do {\
|
do {\
|
||||||
typedef char constraint[(A) ? 1 : -1];\
|
typedef char constraint[(A) ? 1 : -1];\
|
||||||
} while (0);
|
} while (0);
|
||||||
|
*/
|
||||||
|
|
||||||
auth_plugin_t native_password_client_plugin=
|
auth_plugin_t native_password_client_plugin=
|
||||||
{
|
{
|
||||||
@@ -52,6 +28,7 @@ auth_plugin_t native_password_client_plugin=
|
|||||||
"R.J.Silk, Sergei Golubchik",
|
"R.J.Silk, Sergei Golubchik",
|
||||||
"Native MySQL authentication",
|
"Native MySQL authentication",
|
||||||
{1, 0, 0},
|
{1, 0, 0},
|
||||||
|
"LGPL",
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
native_password_auth_client
|
native_password_auth_client
|
||||||
@@ -65,6 +42,7 @@ auth_plugin_t old_password_client_plugin=
|
|||||||
"R.J.Silk, Sergei Golubchik",
|
"R.J.Silk, Sergei Golubchik",
|
||||||
"Old MySQL-3.23 authentication",
|
"Old MySQL-3.23 authentication",
|
||||||
{1, 0, 0},
|
{1, 0, 0},
|
||||||
|
"LGPL",
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
old_password_auth_client
|
old_password_auth_client
|
||||||
@@ -254,30 +232,14 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
|
|||||||
if (mysql->client_flag & CLIENT_MULTI_STATEMENTS)
|
if (mysql->client_flag & CLIENT_MULTI_STATEMENTS)
|
||||||
mysql->client_flag|= CLIENT_MULTI_RESULTS;
|
mysql->client_flag|= CLIENT_MULTI_RESULTS;
|
||||||
|
|
||||||
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
|
#if defined(HAVE_SSL) && !defined(EMBEDDED_LIBRARY)
|
||||||
if (mysql->options.ssl_key || mysql->options.ssl_cert ||
|
if (mysql->options.ssl_key || mysql->options.ssl_cert ||
|
||||||
mysql->options.ssl_ca || mysql->options.ssl_capath ||
|
mysql->options.ssl_ca || mysql->options.ssl_capath ||
|
||||||
mysql->options.ssl_cipher)
|
mysql->options.ssl_cipher)
|
||||||
mysql->options.use_ssl= 1;
|
mysql->options.use_ssl= 1;
|
||||||
if (mysql->options.use_ssl)
|
if (mysql->options.use_ssl)
|
||||||
mysql->client_flag|= CLIENT_SSL;
|
mysql->client_flag|= CLIENT_SSL;
|
||||||
|
#endif /* HAVE_SSL && !EMBEDDED_LIBRARY*/
|
||||||
/* if server doesn't support SSL and verification of server certificate
|
|
||||||
was set to mandator, we need to return an error */
|
|
||||||
if (mysql->options.use_ssl && !(mysql->server_capabilities & CLIENT_SSL))
|
|
||||||
{
|
|
||||||
if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) ||
|
|
||||||
(mysql->options.extension && (mysql->options.extension->ssl_fp ||
|
|
||||||
mysql->options.extension->ssl_fp_list)))
|
|
||||||
{
|
|
||||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
|
||||||
ER(CR_SSL_CONNECTION_ERROR),
|
|
||||||
"Server doesn't support SSL");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY*/
|
|
||||||
if (mpvio->db)
|
if (mpvio->db)
|
||||||
mysql->client_flag|= CLIENT_CONNECT_WITH_DB;
|
mysql->client_flag|= CLIENT_CONNECT_WITH_DB;
|
||||||
|
|
||||||
@@ -305,7 +267,7 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
|
|||||||
int3store(buff+2, net->max_packet_size);
|
int3store(buff+2, net->max_packet_size);
|
||||||
end= buff+5;
|
end= buff+5;
|
||||||
}
|
}
|
||||||
#ifdef HAVE_OPENSSL
|
#ifdef HAVE_SSL
|
||||||
if (mysql->options.ssl_key ||
|
if (mysql->options.ssl_key ||
|
||||||
mysql->options.ssl_cert ||
|
mysql->options.ssl_cert ||
|
||||||
mysql->options.ssl_ca ||
|
mysql->options.ssl_ca ||
|
||||||
@@ -318,11 +280,9 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
|
|||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
mysql->options.use_ssl= 1;
|
mysql->options.use_ssl= 1;
|
||||||
|
|
||||||
if (mysql->options.use_ssl &&
|
if (mysql->options.use_ssl &&
|
||||||
(mysql->client_flag & CLIENT_SSL))
|
(mysql->client_flag & CLIENT_SSL))
|
||||||
{
|
{
|
||||||
SSL *ssl;
|
|
||||||
/*
|
/*
|
||||||
Send mysql->client_flag, max_packet_size - unencrypted otherwise
|
Send mysql->client_flag, max_packet_size - unencrypted otherwise
|
||||||
the server does not know we want to do SSL
|
the server does not know we want to do SSL
|
||||||
@@ -335,38 +295,15 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
|
|||||||
errno);
|
errno);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
if (ma_cio_start_ssl(mysql->net.cio))
|
||||||
/* Create SSL */
|
|
||||||
if (!(ssl= my_ssl_init(mysql)))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
/* Connect to the server */
|
|
||||||
if (my_ssl_connect(ssl))
|
|
||||||
{
|
|
||||||
SSL_free(ssl);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mysql->options.extension &&
|
|
||||||
(mysql->options.extension->ssl_fp || mysql->options.extension->ssl_fp_list))
|
|
||||||
{
|
|
||||||
if (ma_ssl_verify_fingerprint(ssl))
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((mysql->options.ssl_ca || mysql->options.ssl_capath) &&
|
|
||||||
(mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) &&
|
|
||||||
my_ssl_verify_server_cert(ssl))
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
#endif /* HAVE_OPENSSL */
|
#endif /* HAVE_SSL */
|
||||||
|
|
||||||
DBUG_PRINT("info",("Server version = '%s' capabilities: %lu status: %u client_flag: %lu",
|
DBUG_PRINT("info",("Server version = '%s' capabilites: %lu status: %u client_flag: %lu",
|
||||||
mysql->server_version, mysql->server_capabilities,
|
mysql->server_version, mysql->server_capabilities,
|
||||||
mysql->server_status, mysql->client_flag));
|
mysql->server_status, mysql->client_flag));
|
||||||
|
|
||||||
compile_time_assert(MYSQL_USERNAME_LENGTH == USERNAME_LENGTH);
|
|
||||||
|
|
||||||
/* This needs to be changed as it's not useful with big packets */
|
/* This needs to be changed as it's not useful with big packets */
|
||||||
if (mysql->user[0])
|
if (mysql->user[0])
|
||||||
strmake(end, mysql->user, USERNAME_LENGTH);
|
strmake(end, mysql->user, USERNAME_LENGTH);
|
||||||
@@ -527,18 +464,19 @@ static int client_mpvio_write_packet(struct st_plugin_vio *mpv,
|
|||||||
connection
|
connection
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info)
|
void mpvio_info(MARIADB_CIO *cio, MYSQL_PLUGIN_VIO_INFO *info)
|
||||||
{
|
{
|
||||||
bzero(info, sizeof(*info));
|
bzero(info, sizeof(*info));
|
||||||
switch (vio->type) {
|
switch (cio->type) {
|
||||||
case VIO_TYPE_TCPIP:
|
case CIO_TYPE_SOCKET:
|
||||||
info->protocol= MYSQL_VIO_TCP;
|
info->protocol= MYSQL_VIO_TCP;
|
||||||
info->socket= vio->sd;
|
ma_cio_get_handle(cio, &info->socket);
|
||||||
return;
|
return;
|
||||||
case VIO_TYPE_SOCKET:
|
case CIO_TYPE_UNIXSOCKET:
|
||||||
info->protocol= MYSQL_VIO_SOCKET;
|
info->protocol= MYSQL_VIO_SOCKET;
|
||||||
info->socket= vio->sd;
|
ma_cio_get_handle(cio, &info->socket);
|
||||||
return;
|
return;
|
||||||
|
/*
|
||||||
case VIO_TYPE_SSL:
|
case VIO_TYPE_SSL:
|
||||||
{
|
{
|
||||||
struct sockaddr addr;
|
struct sockaddr addr;
|
||||||
@@ -550,11 +488,14 @@ void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info)
|
|||||||
info->socket= vio->sd;
|
info->socket= vio->sd;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
/*
|
||||||
case VIO_TYPE_NAMEDPIPE:
|
case VIO_TYPE_NAMEDPIPE:
|
||||||
info->protocol= MYSQL_VIO_PIPE;
|
info->protocol= MYSQL_VIO_PIPE;
|
||||||
info->handle= vio->hPipe;
|
info->handle= vio->hPipe;
|
||||||
return;
|
return;
|
||||||
|
*/
|
||||||
/* not supported yet
|
/* not supported yet
|
||||||
case VIO_TYPE_SHARED_MEMORY:
|
case VIO_TYPE_SHARED_MEMORY:
|
||||||
info->protocol= MYSQL_VIO_MEMORY;
|
info->protocol= MYSQL_VIO_MEMORY;
|
||||||
@@ -570,7 +511,7 @@ static void client_mpvio_info(MYSQL_PLUGIN_VIO *vio,
|
|||||||
MYSQL_PLUGIN_VIO_INFO *info)
|
MYSQL_PLUGIN_VIO_INFO *info)
|
||||||
{
|
{
|
||||||
MCPVIO_EXT *mpvio= (MCPVIO_EXT*)vio;
|
MCPVIO_EXT *mpvio= (MCPVIO_EXT*)vio;
|
||||||
mpvio_info(mpvio->mysql->net.vio, info);
|
mpvio_info(mpvio->mysql->net.cio, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -637,8 +578,6 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len,
|
|||||||
|
|
||||||
res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql);
|
res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql);
|
||||||
|
|
||||||
compile_time_assert(CR_OK == -1);
|
|
||||||
compile_time_assert(CR_ERROR == 0);
|
|
||||||
if (res > CR_OK && mysql->net.read_pos[0] != 254)
|
if (res > CR_OK && mysql->net.read_pos[0] != 254)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
28
plugins/cio/CMakeLists.txt
Normal file
28
plugins/cio/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
IF(WIN32)
|
||||||
|
SET(EXPORT_FILE "cio_plugin.def")
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
IF(GNUTLS_FOUND)
|
||||||
|
IF (NOT ${DEFAULT_SSL} STREQUAL "GNUTLS")
|
||||||
|
SET(CMAKE_SHARED_LIBRARY_PREFIX "")
|
||||||
|
ADD_LIBRARY(cio_gnutls SHARED cio_gnutls.c ${EXPORT_FILE})
|
||||||
|
TARGET_LINK_LIBRARIES(cio_gnutls ${GNUTLS_LIBRARIES})
|
||||||
|
ENDIF()
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
IF(OPENSSL_FOUND)
|
||||||
|
IF (NOT ${DEFAULT_SSL} STREQUAL "OPENSSL")
|
||||||
|
SET(CMAKE_SHARED_LIBRARY_PREFIX "")
|
||||||
|
SET(source_files cio_openssl.c ${EXPORT_FILE})
|
||||||
|
ADD_LIBRARY(cio_openssl SHARED ${source_files})
|
||||||
|
TARGET_LINK_LIBRARIES(cio_openssl ${OPENSSL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||||
|
ENDIF()
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
IF(WIN32)
|
||||||
|
SET(CMAKE_SHARED_LIBRARY_PREFIX "")
|
||||||
|
IF (NOT ${DEFAULT_SSL} STREQUAL "SCHANNEL")
|
||||||
|
ADD_LIBRARY(cio_schannel SHARED cio_schannel.c ${EXPORT_FILE})
|
||||||
|
ENDIF()
|
||||||
|
ADD_LIBRARY(cio_npipe SHARED cio_npipe.c ${EXPORT_FILE})
|
||||||
|
ENDIF()
|
301
plugins/cio/cio_npipe.c
Normal file
301
plugins/cio/cio_npipe.c
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
/* MariaDB Communication IO (CIO) plugin for named pipe communication */
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#include <my_global.h>
|
||||||
|
#include <my_sys.h>
|
||||||
|
#include <errmsg.h>
|
||||||
|
#include <mysql.h>
|
||||||
|
#include <mysql/client_plugin.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <m_string.h>
|
||||||
|
|
||||||
|
#ifndef HAVE_NPIPE_DEFAULT
|
||||||
|
#define my_malloc(A, B) malloc((A))
|
||||||
|
#undef my_free
|
||||||
|
#define my_free(A,B) free(((A)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
my_bool cio_npipe_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout);
|
||||||
|
int cio_npipe_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type);
|
||||||
|
size_t cio_npipe_read(MARIADB_CIO *cio, uchar *buffer, size_t length);
|
||||||
|
size_t cio_npipe_write(MARIADB_CIO *cio, uchar *buffer, size_t length);
|
||||||
|
int cio_npipe_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout);
|
||||||
|
my_bool cio_npipe_blocking(MARIADB_CIO *cio, my_bool value, my_bool *old_value);
|
||||||
|
my_bool cio_npipe_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo);
|
||||||
|
my_bool cio_npipe_close(MARIADB_CIO *cio);
|
||||||
|
int cio_npipe_fast_send(MARIADB_CIO *cio);
|
||||||
|
int cio_npipe_keepalive(MARIADB_CIO *cio);
|
||||||
|
my_socket cio_npipe_get_socket(MARIADB_CIO *cio);
|
||||||
|
my_bool cio_npipe_is_blocking(MARIADB_CIO *cio);
|
||||||
|
|
||||||
|
struct st_ma_cio_methods cio_npipe_methods= {
|
||||||
|
cio_npipe_set_timeout,
|
||||||
|
cio_npipe_get_timeout,
|
||||||
|
cio_npipe_read,
|
||||||
|
cio_npipe_write,
|
||||||
|
cio_npipe_wait_io_or_timeout,
|
||||||
|
cio_npipe_blocking,
|
||||||
|
cio_npipe_connect,
|
||||||
|
cio_npipe_close,
|
||||||
|
cio_npipe_fast_send,
|
||||||
|
cio_npipe_keepalive,
|
||||||
|
cio_npipe_get_socket,
|
||||||
|
cio_npipe_is_blocking
|
||||||
|
};
|
||||||
|
|
||||||
|
MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_ =
|
||||||
|
{
|
||||||
|
MYSQL_CLIENT_CIO_PLUGIN,
|
||||||
|
MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION,
|
||||||
|
"cio_npipe",
|
||||||
|
"Georg Richter",
|
||||||
|
"MariaDB communication IO plugin for named pipe communication",
|
||||||
|
{1, 0, 0},
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&cio_npipe_methods,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct st_cio_npipe {
|
||||||
|
HANDLE pipe;
|
||||||
|
OVERLAPPED overlapped;
|
||||||
|
size_t rw_size;
|
||||||
|
int fcntl_mode;
|
||||||
|
MYSQL *mysql;
|
||||||
|
};
|
||||||
|
|
||||||
|
my_bool cio_npipe_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout)
|
||||||
|
{
|
||||||
|
if (!cio)
|
||||||
|
return 1;
|
||||||
|
cio->timeout[type]= (timeout > 0) ? timeout * 1000 : -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cio_npipe_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type)
|
||||||
|
{
|
||||||
|
if (!cio)
|
||||||
|
return -1;
|
||||||
|
return cio->timeout[type] / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cio_npipe_read(MARIADB_CIO *cio, uchar *buffer, size_t length)
|
||||||
|
{
|
||||||
|
DWORD dwRead= 0;
|
||||||
|
size_t r= -1;
|
||||||
|
struct st_cio_npipe *cpipe= NULL;
|
||||||
|
|
||||||
|
if (!cio || !cio->data)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
cpipe= (struct st_cio_npipe *)cio->data;
|
||||||
|
|
||||||
|
if (ReadFile(cpipe->pipe, buffer, length, &dwRead, &cpipe->overlapped))
|
||||||
|
{
|
||||||
|
r= (size_t)dwRead;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (GetLastError() == ERROR_IO_PENDING)
|
||||||
|
r= cio_npipe_wait_io_or_timeout(cio, 1, 0);
|
||||||
|
|
||||||
|
if (!r)
|
||||||
|
r= cpipe->rw_size;
|
||||||
|
end:
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cio_npipe_write(MARIADB_CIO *cio, uchar *buffer, size_t length)
|
||||||
|
{
|
||||||
|
DWORD dwWrite= 0;
|
||||||
|
size_t r= -1;
|
||||||
|
struct st_cio_npipe *cpipe= NULL;
|
||||||
|
|
||||||
|
if (!cio || !cio->data)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
cpipe= (struct st_cio_npipe *)cio->data;
|
||||||
|
|
||||||
|
if (WriteFile(cpipe->pipe, buffer, length, &dwWrite, &cpipe->overlapped))
|
||||||
|
{
|
||||||
|
r= (size_t)dwWrite;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (GetLastError() == ERROR_IO_PENDING)
|
||||||
|
r= cio_npipe_wait_io_or_timeout(cio, 1, 0);
|
||||||
|
|
||||||
|
if (!r)
|
||||||
|
r= cpipe->rw_size;
|
||||||
|
end:
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cio_npipe_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout)
|
||||||
|
{
|
||||||
|
int r= -1;
|
||||||
|
DWORD status;
|
||||||
|
int save_error;
|
||||||
|
struct st_cio_npipe *cpipe= NULL;
|
||||||
|
|
||||||
|
cpipe= (struct st_cio_npipe *)cio->data;
|
||||||
|
|
||||||
|
if (!timeout)
|
||||||
|
timeout= (is_read) ? cio->timeout[CIO_READ_TIMEOUT] : cio->timeout[CIO_WRITE_TIMEOUT];
|
||||||
|
|
||||||
|
status= WaitForSingleObject(cpipe->overlapped.hEvent, timeout);
|
||||||
|
if (status == WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
if (GetOverlappedResult(cpipe->pipe, &cpipe->overlapped, &cpipe->rw_size, FALSE))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* other status codes are: WAIT_ABANDONED, WAIT_TIMEOUT and WAIT_FAILED */
|
||||||
|
save_error= GetLastError();
|
||||||
|
CancelIo(cpipe->pipe);
|
||||||
|
SetLastError(save_error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
my_bool cio_npipe_blocking(MARIADB_CIO *cio, my_bool block, my_bool *previous_mode)
|
||||||
|
{
|
||||||
|
/* not supported */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cio_npipe_keepalive(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
/* not supported */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cio_npipe_fast_send(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
/* not supported */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
my_bool cio_npipe_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo)
|
||||||
|
{
|
||||||
|
struct st_cio_npipe *cpipe= NULL;
|
||||||
|
|
||||||
|
if (!cio || !cinfo)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!(cpipe= (struct st_cio_npipe *)my_malloc(sizeof(struct st_cio_npipe), MYF(0))))
|
||||||
|
{
|
||||||
|
CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0, "");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
bzero(cpipe, sizeof(struct st_cio_npipe));
|
||||||
|
cio->data= (void *)cpipe;
|
||||||
|
cpipe->pipe= INVALID_HANDLE_VALUE;
|
||||||
|
cio->mysql= cinfo->mysql;
|
||||||
|
cio->type= cinfo->type;
|
||||||
|
|
||||||
|
if (cinfo->type == CIO_TYPE_NAMEDPIPE)
|
||||||
|
{
|
||||||
|
my_bool has_timedout= 0;
|
||||||
|
char szPipeName[MAX_PATH];
|
||||||
|
DWORD dwMode;
|
||||||
|
|
||||||
|
if ( ! cinfo->unix_socket || (cinfo->unix_socket)[0] == 0x00)
|
||||||
|
cinfo->unix_socket = MYSQL_NAMEDPIPE;
|
||||||
|
if (!cinfo->host || !strcmp(cinfo->host,LOCAL_HOST))
|
||||||
|
cinfo->host=LOCAL_HOST_NAMEDPIPE;
|
||||||
|
|
||||||
|
szPipeName[MAX_PATH - 1]= 0;
|
||||||
|
snprintf(szPipeName, MAX_PATH - 1, "\\\\%s\\pipe\\%s", cinfo->host, cinfo->unix_socket);
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if ((cpipe->pipe = CreateFile(szPipeName,
|
||||||
|
GENERIC_READ |
|
||||||
|
GENERIC_WRITE,
|
||||||
|
0, /* no sharing */
|
||||||
|
NULL, /* default security attributes */
|
||||||
|
OPEN_EXISTING,
|
||||||
|
0, /* default attributes */
|
||||||
|
NULL)) != INVALID_HANDLE_VALUE)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (GetLastError() != ERROR_PIPE_BUSY)
|
||||||
|
{
|
||||||
|
cio->set_error(cio, CR_NAMEDPIPEOPEN_ERROR, SQLSTATE_UNKNOWN, 0,
|
||||||
|
cinfo->host, cinfo->unix_socket, GetLastError());
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_timedout || !WaitNamedPipe(szPipeName, cio->timeout[CIO_CONNECT_TIMEOUT]))
|
||||||
|
{
|
||||||
|
cio->set_error(cio, CR_NAMEDPIPEWAIT_ERROR, SQLSTATE_UNKNOWN, 0,
|
||||||
|
cinfo->host, cinfo->unix_socket, GetLastError());
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
has_timedout= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dwMode = PIPE_READMODE_BYTE | PIPE_WAIT;
|
||||||
|
if (!SetNamedPipeHandleState(cpipe->pipe, &dwMode, NULL, NULL))
|
||||||
|
{
|
||||||
|
cio->set_error(cio, CR_NAMEDPIPESETSTATE_ERROR, SQLSTATE_UNKNOWN, 0,
|
||||||
|
cinfo->host, cinfo->unix_socket, (ulong) GetLastError());
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Register event handler for overlapped IO */
|
||||||
|
if (!(cpipe->overlapped.hEvent= CreateEvent(NULL, FALSE, FALSE, NULL)))
|
||||||
|
{
|
||||||
|
cio->set_error(cio, CR_EVENT_CREATE_FAILED, SQLSTATE_UNKNOWN, 0,
|
||||||
|
GetLastError());
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
if (cpipe)
|
||||||
|
{
|
||||||
|
if (cpipe->pipe != INVALID_HANDLE_VALUE)
|
||||||
|
CloseHandle(cpipe->pipe);
|
||||||
|
my_free((gptr)cpipe, MYF(0));
|
||||||
|
cio->data= NULL;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
my_bool cio_npipe_close(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
struct st_cio_npipe *cpipe= NULL;
|
||||||
|
int r= 0;
|
||||||
|
|
||||||
|
if (!cio)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (cio->data)
|
||||||
|
{
|
||||||
|
cpipe= (struct st_cio_npipe *)cio->data;
|
||||||
|
CloseHandle(cpipe->overlapped.hEvent);
|
||||||
|
if (cpipe->pipe != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
CloseHandle(cpipe->pipe);
|
||||||
|
cpipe->pipe= INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
my_free((gptr)cio->data, MYF(0));
|
||||||
|
cio->data= NULL;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
my_socket cio_npipe_get_socket(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
if (cio && cio->data)
|
||||||
|
return (my_socket)((struct st_cio_npipe *)cio->data)->pipe;
|
||||||
|
return INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
my_bool cio_npipe_is_blocking(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
2
plugins/cio/cio_plugin.def
Normal file
2
plugins/cio/cio_plugin.def
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
EXPORTS
|
||||||
|
_mysql_client_plugin_declaration_ DATA
|
307
plugins/cio/cio_shmem.c
Normal file
307
plugins/cio/cio_shmem.c
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
/* MariaDB Communication IO (CIO) plugin for shate memory communication
|
||||||
|
*
|
||||||
|
* During initialization MariaDB serve creates a named file mapping
|
||||||
|
* object, named : *
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#include <my_global.h>
|
||||||
|
#include <my_sys.h>
|
||||||
|
#include <errmsg.h>
|
||||||
|
#include <mysql.h>
|
||||||
|
#include <mysql/client_plugin.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <m_string.h>
|
||||||
|
|
||||||
|
#ifndef HAVE_NPIPE_DEFAULT
|
||||||
|
#define my_malloc(A, B) malloc((A))
|
||||||
|
#undef my_free
|
||||||
|
#define my_free(A,B) free(((A)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
my_bool cio_shm_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout);
|
||||||
|
int cio_shm_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type);
|
||||||
|
size_t cio_shm_read(MARIADB_CIO *cio, uchar *buffer, size_t length);
|
||||||
|
size_t cio_shm_write(MARIADB_CIO *cio, uchar *buffer, size_t length);
|
||||||
|
int cio_shm_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout);
|
||||||
|
my_bool cio_shm_blocking(MARIADB_CIO *cio, my_bool value, my_bool *old_value);
|
||||||
|
my_bool cio_shm_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo);
|
||||||
|
my_bool cio_shm_close(MARIADB_CIO *cio);
|
||||||
|
int cio_shm_fast_send(MARIADB_CIO *cio);
|
||||||
|
int cio_shm_keepalive(MARIADB_CIO *cio);
|
||||||
|
my_socket cio_shm_get_socket(MARIADB_CIO *cio);
|
||||||
|
my_bool cio_shm_is_blocking(MARIADB_CIO *cio);
|
||||||
|
|
||||||
|
struct st_ma_cio_methods cio_shm_methods= {
|
||||||
|
cio_shm_set_timeout,
|
||||||
|
cio_shm_get_timeout,
|
||||||
|
cio_shm_read,
|
||||||
|
cio_shm_write,
|
||||||
|
cio_shm_wait_io_or_timeout,
|
||||||
|
cio_shm_blocking,
|
||||||
|
cio_shm_connect,
|
||||||
|
cio_shm_close,
|
||||||
|
cio_shm_fast_send,
|
||||||
|
cio_shm_keepalive,
|
||||||
|
cio_shm_get_socket,
|
||||||
|
cio_shm_is_blocking
|
||||||
|
};
|
||||||
|
|
||||||
|
MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_ =
|
||||||
|
{
|
||||||
|
MYSQL_CLIENT_CIO_PLUGIN,
|
||||||
|
MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION,
|
||||||
|
"cio_shm",
|
||||||
|
"Georg Richter",
|
||||||
|
"MariaDB communication IO plugin for named pipe communication",
|
||||||
|
{1, 0, 0},
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&cio_shm_methods,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct st_cio_shm {
|
||||||
|
HANDLE pipe;
|
||||||
|
OVERLAPPED overlapped;
|
||||||
|
size_t rw_size;
|
||||||
|
int fcntl_mode;
|
||||||
|
MYSQL *mysql;
|
||||||
|
};
|
||||||
|
|
||||||
|
my_bool cio_shm_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout)
|
||||||
|
{
|
||||||
|
if (!cio)
|
||||||
|
return 1;
|
||||||
|
cio->timeout[type]= (timeout > 0) ? timeout * 1000 : -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cio_shm_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type)
|
||||||
|
{
|
||||||
|
if (!cio)
|
||||||
|
return -1;
|
||||||
|
return cio->timeout[type] / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cio_shm_read(MARIADB_CIO *cio, uchar *buffer, size_t length)
|
||||||
|
{
|
||||||
|
DWORD dwRead= 0;
|
||||||
|
size_t r= -1;
|
||||||
|
struct st_cio_shm *cpipe= NULL;
|
||||||
|
|
||||||
|
if (!cio || !cio->data)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
cpipe= (struct st_cio_shm *)cio->data;
|
||||||
|
|
||||||
|
if (ReadFile(cpipe->pipe, buffer, length, &dwRead, &cpipe->overlapped))
|
||||||
|
{
|
||||||
|
r= (size_t)dwRead;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (GetLastError() == ERROR_IO_PENDING)
|
||||||
|
r= cio_shm_wait_io_or_timeout(cio, 1, 0);
|
||||||
|
|
||||||
|
if (!r)
|
||||||
|
r= cpipe->rw_size;
|
||||||
|
end:
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cio_shm_write(MARIADB_CIO *cio, uchar *buffer, size_t length)
|
||||||
|
{
|
||||||
|
DWORD dwWrite= 0;
|
||||||
|
size_t r= -1;
|
||||||
|
struct st_cio_shm *cpipe= NULL;
|
||||||
|
|
||||||
|
if (!cio || !cio->data)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
cpipe= (struct st_cio_shm *)cio->data;
|
||||||
|
|
||||||
|
if (WriteFile(cpipe->pipe, buffer, length, &dwWrite, &cpipe->overlapped))
|
||||||
|
{
|
||||||
|
r= (size_t)dwWrite;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (GetLastError() == ERROR_IO_PENDING)
|
||||||
|
r= cio_shm_wait_io_or_timeout(cio, 1, 0);
|
||||||
|
|
||||||
|
if (!r)
|
||||||
|
r= cpipe->rw_size;
|
||||||
|
end:
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cio_shm_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout)
|
||||||
|
{
|
||||||
|
int r= -1;
|
||||||
|
DWORD status;
|
||||||
|
int save_error;
|
||||||
|
struct st_cio_shm *cpipe= NULL;
|
||||||
|
|
||||||
|
cpipe= (struct st_cio_shm *)cio->data;
|
||||||
|
|
||||||
|
if (!timeout)
|
||||||
|
timeout= (is_read) ? cio->timeout[CIO_READ_TIMEOUT] : cio->timeout[CIO_WRITE_TIMEOUT];
|
||||||
|
|
||||||
|
status= WaitForSingleObject(cpipe->overlapped.hEvent, timeout);
|
||||||
|
if (status == WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
if (GetOverlappedResult(cpipe->pipe, &cpipe->overlapped, &cpipe->rw_size, FALSE))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* other status codes are: WAIT_ABANDONED, WAIT_TIMEOUT and WAIT_FAILED */
|
||||||
|
save_error= GetLastError();
|
||||||
|
CancelIo(cpipe->pipe);
|
||||||
|
SetLastError(save_error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
my_bool cio_shm_blocking(MARIADB_CIO *cio, my_bool block, my_bool *previous_mode)
|
||||||
|
{
|
||||||
|
/* not supported */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cio_shm_keepalive(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
/* not supported */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cio_shm_fast_send(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
/* not supported */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
my_bool cio_shm_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo)
|
||||||
|
{
|
||||||
|
struct st_cio_shm *cpipe= NULL;
|
||||||
|
|
||||||
|
if (!cio || !cinfo)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!(cpipe= (struct st_cio_shm *)my_malloc(sizeof(struct st_cio_shm), MYF(0))))
|
||||||
|
{
|
||||||
|
CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0, "");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
bzero(cpipe, sizeof(struct st_cio_shm));
|
||||||
|
cio->data= (void *)cpipe;
|
||||||
|
cpipe->pipe= INVALID_HANDLE_VALUE;
|
||||||
|
cio->mysql= cinfo->mysql;
|
||||||
|
cio->type= cinfo->type;
|
||||||
|
|
||||||
|
if (cinfo->type == CIO_TYPE_NAMEDPIPE)
|
||||||
|
{
|
||||||
|
my_bool has_timedout= 0;
|
||||||
|
char szPipeName[MAX_PATH];
|
||||||
|
DWORD dwMode;
|
||||||
|
|
||||||
|
if ( ! cinfo->unix_socket || (cinfo->unix_socket)[0] == 0x00)
|
||||||
|
cinfo->unix_socket = MYSQL_NAMEDPIPE;
|
||||||
|
if (!cinfo->host || !strcmp(cinfo->host,LOCAL_HOST))
|
||||||
|
cinfo->host=LOCAL_HOST_NAMEDPIPE;
|
||||||
|
|
||||||
|
szPipeName[MAX_PATH - 1]= 0;
|
||||||
|
snprintf(szPipeName, MAX_PATH - 1, "\\\\%s\\pipe\\%s", cinfo->host, cinfo->unix_socket);
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if ((cpipe->pipe = CreateFile(szPipeName,
|
||||||
|
GENERIC_READ |
|
||||||
|
GENERIC_WRITE,
|
||||||
|
0, /* no sharing */
|
||||||
|
NULL, /* default security attributes */
|
||||||
|
OPEN_EXISTING,
|
||||||
|
0, /* default attributes */
|
||||||
|
NULL)) != INVALID_HANDLE_VALUE)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (GetLastError() != ERROR_PIPE_BUSY)
|
||||||
|
{
|
||||||
|
cio->set_error(cio, CR_NAMEDPIPEOPEN_ERROR, SQLSTATE_UNKNOWN, 0,
|
||||||
|
cinfo->host, cinfo->unix_socket, GetLastError());
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_timedout || !WaitNamedPipe(szPipeName, cio->timeout[CIO_CONNECT_TIMEOUT]))
|
||||||
|
{
|
||||||
|
cio->set_error(cio, CR_NAMEDPIPEWAIT_ERROR, SQLSTATE_UNKNOWN, 0,
|
||||||
|
cinfo->host, cinfo->unix_socket, GetLastError());
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
has_timedout= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dwMode = PIPE_READMODE_BYTE | PIPE_WAIT;
|
||||||
|
if (!SetNamedPipeHandleState(cpipe->pipe, &dwMode, NULL, NULL))
|
||||||
|
{
|
||||||
|
cio->set_error(cio, CR_NAMEDPIPESETSTATE_ERROR, SQLSTATE_UNKNOWN, 0,
|
||||||
|
cinfo->host, cinfo->unix_socket, (ulong) GetLastError());
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Register event handler for overlapped IO */
|
||||||
|
if (!(cpipe->overlapped.hEvent= CreateEvent(NULL, FALSE, FALSE, NULL)))
|
||||||
|
{
|
||||||
|
cio->set_error(cio, CR_CREATE_EVENT_FAILED, SQLSTATE_UNKNOWN, 0,
|
||||||
|
GetLastError());
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
if (cpipe)
|
||||||
|
{
|
||||||
|
if (cpipe->pipe != INVALID_HANDLE_VALUE)
|
||||||
|
CloseHandle(cpipe->pipe);
|
||||||
|
my_free((gptr)cpipe, MYF(0));
|
||||||
|
cio->data= NULL;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
my_bool cio_shm_close(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
struct st_cio_shm *cpipe= NULL;
|
||||||
|
int r= 0;
|
||||||
|
|
||||||
|
if (!cio)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (cio->data)
|
||||||
|
{
|
||||||
|
cpipe= (struct st_cio_shm *)cio->data;
|
||||||
|
CloseHandle(cpipe->overlapped.hEvent);
|
||||||
|
if (cpipe->pipe != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
CloseHandle(cpipe->pipe);
|
||||||
|
cpipe->pipe= INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
my_free((gptr)cio->data, MYF(0));
|
||||||
|
cio->data= NULL;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
my_socket cio_shm_get_socket(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
if (cio && cio->data)
|
||||||
|
return (my_socket)((struct st_cio_shm *)cio->data)->pipe;
|
||||||
|
return INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
my_bool cio_shm_is_blocking(MARIADB_CIO *cio)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
1257
plugins/cio/tls_schannel.c
Normal file
1257
plugins/cio/tls_schannel.c
Normal file
File diff suppressed because it is too large
Load Diff
BIN
plugins/trace/.trace_example.c.swo
Normal file
BIN
plugins/trace/.trace_example.c.swo
Normal file
Binary file not shown.
16
plugins/trace/CMakeLists.txt
Normal file
16
plugins/trace/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include)
|
||||||
|
|
||||||
|
# Trace example plugin
|
||||||
|
SET(TRACE_EXAMPLE_SOURCES trace_example.c)
|
||||||
|
IF(WIN32)
|
||||||
|
SET(TRACE_EXAMPLE_SOURCES ${TRACE_EXAMPLE_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def)
|
||||||
|
ENDIF()
|
||||||
|
ADD_LIBRARY(trace_example SHARED ${TRACE_EXAMPLE_SOURCES})
|
||||||
|
SET_TARGET_PROPERTIES(trace_example PROPERTIES PREFIX "")
|
||||||
|
|
||||||
|
INSTALL(TARGETS
|
||||||
|
trace_example
|
||||||
|
RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}"
|
||||||
|
LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}"
|
||||||
|
ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}")
|
||||||
|
|
440
plugins/trace/trace_example.c
Normal file
440
plugins/trace/trace_example.c
Normal file
@@ -0,0 +1,440 @@
|
|||||||
|
/************************************************************************************
|
||||||
|
Copyright (C) 2015 MariaDB Corporation AB
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with this library; if not see <http://www.gnu.org/licenses>
|
||||||
|
or write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
|
||||||
|
*************************************************************************************/
|
||||||
|
#ifndef _WIN32
|
||||||
|
#define _GNU_SOURCE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <my_global.h>
|
||||||
|
#include <mysql.h>
|
||||||
|
#include <mysql/client_plugin.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <memory.h>
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define READ 0
|
||||||
|
#define WRITE 1
|
||||||
|
|
||||||
|
/* function prototypes */
|
||||||
|
static int trace_init(char *errormsg,
|
||||||
|
size_t errormsg_size,
|
||||||
|
int unused __attribute__((unused)),
|
||||||
|
va_list unused1 __attribute__((unused)));
|
||||||
|
static int trace_deinit(void);
|
||||||
|
|
||||||
|
int (*register_callback)(my_bool register_callback,
|
||||||
|
void (*callback_function)(int mode, MYSQL *mysql, const uchar *buffer, size_t length));
|
||||||
|
void trace_callback(int mode, MYSQL *mysql, const uchar *buffer, size_t length);
|
||||||
|
|
||||||
|
mysql_declare_client_plugin(TRACE)
|
||||||
|
"trace_example",
|
||||||
|
"Georg Richter",
|
||||||
|
"Trace example plugin",
|
||||||
|
{1,0,0},
|
||||||
|
"LGPL",
|
||||||
|
&trace_init,
|
||||||
|
&trace_deinit,
|
||||||
|
mysql_end_client_plugin;
|
||||||
|
|
||||||
|
static char *commands[]= {
|
||||||
|
"MYSQL_COM_SLEEP",
|
||||||
|
"MYSQL_COM_QUIT",
|
||||||
|
"MYSQL_COM_INIT_DB",
|
||||||
|
"MYSQL_COM_QUERY",
|
||||||
|
"MYSQL_COM_FIELD_LIST",
|
||||||
|
"MYSQL_COM_CREATE_DB",
|
||||||
|
"MYSQL_COM_DROP_DB",
|
||||||
|
"MYSQL_COM_REFRESH",
|
||||||
|
"MYSQL_COM_SHUTDOWN",
|
||||||
|
"MYSQL_COM_STATISTICS",
|
||||||
|
"MYSQL_COM_PROCESS_INFO",
|
||||||
|
"MYSQL_COM_CONNECT",
|
||||||
|
"MYSQL_COM_PROCESS_KILL",
|
||||||
|
"MYSQL_COM_DEBUG",
|
||||||
|
"MYSQL_COM_PING",
|
||||||
|
"MYSQL_COM_TIME",
|
||||||
|
"MYSQL_COM_DELAYED_INSERT",
|
||||||
|
"MYSQL_COM_CHANGE_USER",
|
||||||
|
"MYSQL_COM_BINLOG_DUMP",
|
||||||
|
"MYSQL_COM_TABLE_DUMP",
|
||||||
|
"MYSQL_COM_CONNECT_OUT",
|
||||||
|
"MYSQL_COM_REGISTER_SLAVE",
|
||||||
|
"MYSQL_COM_STMT_PREPARE",
|
||||||
|
"MYSQL_COM_STMT_EXECUTE",
|
||||||
|
"MYSQL_COM_STMT_SEND_LONG_DATA",
|
||||||
|
"MYSQL_COM_STMT_CLOSE",
|
||||||
|
"MYSQL_COM_STMT_RESET",
|
||||||
|
"MYSQL_COM_SET_OPTION",
|
||||||
|
"MYSQL_COM_STMT_FETCH",
|
||||||
|
"MYSQL_COM_DAEMON",
|
||||||
|
"MYSQL_COM_END"
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned long thread_id;
|
||||||
|
int last_command; /* MYSQL_COM_* values, -1 for handshake */
|
||||||
|
unsigned int max_packet_size;
|
||||||
|
size_t total_size[2];
|
||||||
|
unsigned int client_flags;
|
||||||
|
char *username;
|
||||||
|
char *db;
|
||||||
|
char *command;
|
||||||
|
char *filename;
|
||||||
|
unsigned long refid; /* stmt_id, thread_id for kill */
|
||||||
|
uchar charset;
|
||||||
|
void *next;
|
||||||
|
int local_infile;
|
||||||
|
} TRACE_INFO;
|
||||||
|
|
||||||
|
#define TRACE_STATUS(a) (!a) ? "ok" : "error"
|
||||||
|
|
||||||
|
TRACE_INFO *trace_info= NULL;
|
||||||
|
|
||||||
|
static TRACE_INFO *get_trace_info(unsigned long thread_id)
|
||||||
|
{
|
||||||
|
TRACE_INFO *info= trace_info;
|
||||||
|
|
||||||
|
while (info)
|
||||||
|
{
|
||||||
|
if (info->thread_id == thread_id)
|
||||||
|
return info;
|
||||||
|
else
|
||||||
|
info= info->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(info= (TRACE_INFO *)calloc(sizeof(TRACE_INFO), 1)))
|
||||||
|
return NULL;
|
||||||
|
info->thread_id= thread_id;
|
||||||
|
info->next= trace_info;
|
||||||
|
trace_info= info;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void delete_trace_info(unsigned long thread_id)
|
||||||
|
{
|
||||||
|
TRACE_INFO *last= NULL, *current;
|
||||||
|
current= trace_info;
|
||||||
|
|
||||||
|
while (current)
|
||||||
|
{
|
||||||
|
if (current->thread_id == thread_id)
|
||||||
|
{
|
||||||
|
printf("deleting thread %d\n", thread_id);
|
||||||
|
|
||||||
|
if (last)
|
||||||
|
last->next= current->next;
|
||||||
|
else
|
||||||
|
trace_info= current->next;
|
||||||
|
if (current->command)
|
||||||
|
free(current->command);
|
||||||
|
if (current->db)
|
||||||
|
free(current->db);
|
||||||
|
if (current->username)
|
||||||
|
free(current->username);
|
||||||
|
if (current->filename)
|
||||||
|
free(current->filename);
|
||||||
|
free(current);
|
||||||
|
}
|
||||||
|
last= current;
|
||||||
|
current= current->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* {{{ static int trace_init */
|
||||||
|
/*
|
||||||
|
Initialization routine
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
trace_init
|
||||||
|
unused1
|
||||||
|
unused2
|
||||||
|
unused3
|
||||||
|
unused4
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Init function registers a callback handler for CIO interface.
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
0 success
|
||||||
|
*/
|
||||||
|
static int trace_init(char *errormsg,
|
||||||
|
size_t errormsg_size,
|
||||||
|
int unused1 __attribute__((unused)),
|
||||||
|
va_list unused2 __attribute__((unused)))
|
||||||
|
{
|
||||||
|
void *func;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
if (!(func= GetProcAddress(GetModuleHandle(NULL), "ma_cio_register_callback")))
|
||||||
|
#else
|
||||||
|
if (!(func= dlsym(RTLD_DEFAULT, "ma_cio_register_callback")))
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
strncpy(errormsg, "Can't find ma_cio_register_callback function", errormsg_size);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
register_callback= func;
|
||||||
|
register_callback(TRUE, trace_callback);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
static int trace_deinit()
|
||||||
|
{
|
||||||
|
/* unregister plugin */
|
||||||
|
while(trace_info)
|
||||||
|
{
|
||||||
|
printf("Warning: Connection for thread %d not properly closed\n", trace_info->thread_id);
|
||||||
|
trace_info= trace_info->next;
|
||||||
|
}
|
||||||
|
register_callback(FALSE, trace_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void trace_set_command(TRACE_INFO *info, char *buffer, size_t size)
|
||||||
|
{
|
||||||
|
if (info->command)
|
||||||
|
free(info->command);
|
||||||
|
|
||||||
|
info->command= (char *)malloc(size);
|
||||||
|
strncpy(info->command, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_buffer(uchar *buffer, size_t len)
|
||||||
|
{
|
||||||
|
char *p= buffer;
|
||||||
|
while (p < buffer + len)
|
||||||
|
{
|
||||||
|
printf("%02x ", *p);
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_simple(TRACE_INFO *info, my_bool is_error)
|
||||||
|
{
|
||||||
|
printf("%8d: %s %s\n", info->thread_id, commands[info->last_command], TRACE_STATUS(is_error));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_reference(TRACE_INFO *info, my_bool is_error)
|
||||||
|
{
|
||||||
|
printf("%8d: %s(%d) %s\n", info->thread_id, commands[info->last_command], info->refid, TRACE_STATUS(is_error));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_command(TRACE_INFO *info, my_bool is_error)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
printf("%8d: %s(", info->thread_id, commands[info->last_command]);
|
||||||
|
for (i= 0; info->command && i < strlen(info->command); i++)
|
||||||
|
if (info->command[i] == '\n')
|
||||||
|
printf("\\n");
|
||||||
|
else if (info->command[i] == '\r')
|
||||||
|
printf("\\r");
|
||||||
|
else if (info->command[i] == '\t')
|
||||||
|
printf("\\t");
|
||||||
|
else
|
||||||
|
printf("%c", info->command[i]);
|
||||||
|
printf(") %s\n", TRACE_STATUS(is_error));
|
||||||
|
}
|
||||||
|
|
||||||
|
void trace_callback(int mode, MYSQL *mysql, const uchar *buffer, size_t length)
|
||||||
|
{
|
||||||
|
unsigned long thread_id= mysql->thread_id;
|
||||||
|
TRACE_INFO *info;
|
||||||
|
|
||||||
|
|
||||||
|
/* check if package is server greeting package,
|
||||||
|
* and set thread_id */
|
||||||
|
if (!thread_id && mode == READ)
|
||||||
|
{
|
||||||
|
char *p= buffer;
|
||||||
|
p+= 4; /* packet length */
|
||||||
|
if (*p != 0xFF) /* protocol version 0xFF indicates error */
|
||||||
|
{
|
||||||
|
p+= strlen(p + 1) + 2;
|
||||||
|
thread_id= uint4korr(p);
|
||||||
|
}
|
||||||
|
info= get_trace_info(thread_id);
|
||||||
|
info->last_command= -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char *p= buffer;
|
||||||
|
info= get_trace_info(thread_id);
|
||||||
|
|
||||||
|
if (info->last_command == -1)
|
||||||
|
{
|
||||||
|
if (mode == WRITE)
|
||||||
|
{
|
||||||
|
/* client authentication reply packet:
|
||||||
|
*
|
||||||
|
* ofs description length
|
||||||
|
* ------------------------
|
||||||
|
* 0 length 3
|
||||||
|
* 3 packet_no 1
|
||||||
|
* 4 client capab. 4
|
||||||
|
* 8 max_packet_size 4
|
||||||
|
* 12 character set 1
|
||||||
|
* 13 reserved 23
|
||||||
|
* ------------------------
|
||||||
|
* 36 username (zero terminated)
|
||||||
|
* len (1 byte) + password or
|
||||||
|
*/
|
||||||
|
int len;
|
||||||
|
|
||||||
|
p+= 4;
|
||||||
|
info->client_flags= uint4korr(p);
|
||||||
|
p+= 4;
|
||||||
|
info->max_packet_size= uint4korr(p);
|
||||||
|
p+= 4;
|
||||||
|
info->charset= *p;
|
||||||
|
p+= 24;
|
||||||
|
info->username= strdup(p);
|
||||||
|
p+= strlen(p) + 1;
|
||||||
|
if (*p) /* we are not interested in authentication data */
|
||||||
|
p+= *p;
|
||||||
|
p++;
|
||||||
|
if (info->client_flags & CLIENT_CONNECT_WITH_DB)
|
||||||
|
info->db= strdup(p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p++;
|
||||||
|
if (*p == 0xFF)
|
||||||
|
printf("%8d: CONNECT_ERROR(%d)\n", info->thread_id, uint4korr(p+1));
|
||||||
|
else
|
||||||
|
printf("%8d: CONNECT_SUCCESS(host=%s,user=%s,db=%s)\n", info->thread_id,
|
||||||
|
mysql->host, info->username, info->db ? info->db : "'none'");
|
||||||
|
info->last_command= MYSQL_COM_SLEEP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
char *p= buffer;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (mode == WRITE)
|
||||||
|
{
|
||||||
|
len= uint3korr(p);
|
||||||
|
p+= 4;
|
||||||
|
info->last_command= *p;
|
||||||
|
p++;
|
||||||
|
switch (info->last_command) {
|
||||||
|
case MYSQL_COM_INIT_DB:
|
||||||
|
case MYSQL_COM_DROP_DB:
|
||||||
|
case MYSQL_COM_CREATE_DB:
|
||||||
|
case MYSQL_COM_DEBUG:
|
||||||
|
case MYSQL_COM_QUERY:
|
||||||
|
case MYSQL_COM_STMT_PREPARE:
|
||||||
|
trace_set_command(info, p, len - 1);
|
||||||
|
break;
|
||||||
|
case MYSQL_COM_PROCESS_KILL:
|
||||||
|
info->refid= uint4korr(p);
|
||||||
|
break;
|
||||||
|
case MYSQL_COM_QUIT:
|
||||||
|
printf("%8d: MYSQL_COM_QUIT\n", info->thread_id);
|
||||||
|
delete_trace_info(info->thread_id);
|
||||||
|
break;
|
||||||
|
case MYSQL_COM_PING:
|
||||||
|
printf("%8d: MYSQL_COM_PING\n", info->thread_id);
|
||||||
|
break;
|
||||||
|
case MYSQL_COM_STMT_EXECUTE:
|
||||||
|
case MYSQL_COM_STMT_RESET:
|
||||||
|
case MYSQL_COM_STMT_CLOSE:
|
||||||
|
info->refid= uint4korr(p);
|
||||||
|
break;
|
||||||
|
case MYSQL_COM_CHANGE_USER:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (info->local_infile == 1)
|
||||||
|
{
|
||||||
|
printf("%8d: SEND_LOCAL_INFILE(%s) ", info->thread_id, info->filename);
|
||||||
|
if (len)
|
||||||
|
printf("sent %d bytes\n", len);
|
||||||
|
else
|
||||||
|
printf("- error\n");
|
||||||
|
info->local_infile= 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printf("%8d: UNKNOWN_COMMAND: %d\n", info->thread_id, info->last_command);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
my_bool is_error;
|
||||||
|
|
||||||
|
len= uint3korr(p);
|
||||||
|
p+= 4;
|
||||||
|
|
||||||
|
is_error= ((unsigned int)len == -1);
|
||||||
|
|
||||||
|
switch(info->last_command) {
|
||||||
|
case MYSQL_COM_STMT_EXECUTE:
|
||||||
|
case MYSQL_COM_STMT_RESET:
|
||||||
|
case MYSQL_COM_STMT_CLOSE:
|
||||||
|
case MYSQL_COM_PROCESS_KILL:
|
||||||
|
dump_reference(info, is_error);
|
||||||
|
info->refid= 0;
|
||||||
|
info->last_command= 0;
|
||||||
|
break;
|
||||||
|
case MYSQL_COM_QUIT:
|
||||||
|
dump_simple(info, is_error);
|
||||||
|
break;
|
||||||
|
case MYSQL_COM_QUERY:
|
||||||
|
case MYSQL_COM_INIT_DB:
|
||||||
|
case MYSQL_COM_DROP_DB:
|
||||||
|
case MYSQL_COM_CREATE_DB:
|
||||||
|
case MYSQL_COM_DEBUG:
|
||||||
|
case MYSQL_COM_CHANGE_USER:
|
||||||
|
if (info->last_command == MYSQL_COM_QUERY && (uchar)*p == 251)
|
||||||
|
{
|
||||||
|
info->local_infile= 1;
|
||||||
|
p++;
|
||||||
|
info->filename= (char *)malloc(len);
|
||||||
|
strncpy(info->filename, (char *)p, len);
|
||||||
|
dump_command(info, is_error);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dump_command(info, is_error);
|
||||||
|
if (info->local_infile != 1)
|
||||||
|
{
|
||||||
|
free(info->command);
|
||||||
|
info->command= NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MYSQL_COM_STMT_PREPARE:
|
||||||
|
printf("%8d: MYSQL_COM_STMT_PREPARE(%s) ", info->thread_id, info->command);
|
||||||
|
if (!*p)
|
||||||
|
{
|
||||||
|
unsigned long stmt_id= uint4korr(p+1);
|
||||||
|
printf("-> stmt_id(%d)\n", stmt_id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printf("error\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info->total_size[mode]+= length;
|
||||||
|
}
|
BIN
plugins/trace/trace_example.so
Executable file
BIN
plugins/trace/trace_example.so
Executable file
Binary file not shown.
@@ -62,7 +62,7 @@ ENDIF()
|
|||||||
|
|
||||||
FOREACH(API_TEST ${API_TESTS})
|
FOREACH(API_TEST ${API_TESTS})
|
||||||
ADD_EXECUTABLE(${API_TEST} ${API_TEST}.c)
|
ADD_EXECUTABLE(${API_TEST} ${API_TEST}.c)
|
||||||
TARGET_LINK_LIBRARIES(${API_TEST} mytap libmariadb ${EXTRA_LIBS})
|
TARGET_LINK_LIBRARIES(${API_TEST} mytap libmariadb )
|
||||||
ADD_TEST(${API_TEST} ${EXECUTABLE_OUTPUT_PATH}/${API_TEST})
|
ADD_TEST(${API_TEST} ${EXECUTABLE_OUTPUT_PATH}/${API_TEST})
|
||||||
SET_TESTS_PROPERTIES(${API_TEST} PROPERTIES TIMEOUT 120)
|
SET_TESTS_PROPERTIES(${API_TEST} PROPERTIES TIMEOUT 120)
|
||||||
ENDFOREACH(API_TEST)
|
ENDFOREACH(API_TEST)
|
||||||
|
Reference in New Issue
Block a user