1
0
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:
Georg Richter
2015-08-06 15:08:25 +02:00
parent f886562fb2
commit cc85e25666
18 changed files with 5819 additions and 84 deletions

457
libmariadb/ma_cio.c Normal file
View 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;
}
/* }}} */

View 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 */

View 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);
}

View 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;
}

View 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
}
/* }}} */

View 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;
}
}
/* }}} */

View 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_ */

View File

@@ -1,36 +1,10 @@
/************************************************************************************
Copyright (C) 2012-2015 Monty Program AB, MariaDB Corporation AB,
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not see <http://www.gnu.org/licenses>
or write to the Free Software Foundation, Inc.,
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
Part of this code includes code from the PHP project which
is freely available from http://www.php.net
Originally written by Sergei Golubchik
*************************************************************************************/
#include <my_global.h> #include <my_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)
{ {
/* /*

View 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
View 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

View File

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

307
plugins/cio/cio_shmem.c Normal file
View 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

File diff suppressed because it is too large Load Diff

Binary file not shown.

View 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}")

View 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

Binary file not shown.

View File

@@ -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)