490 lines
16 KiB
C++
490 lines
16 KiB
C++
/* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
|
|
#include <mysql/plugin_audit.h> /* mysql_event_connection */
|
|
#include "mysql_version.h"
|
|
#include <my_global.h>
|
|
#include <my_atomic.h>
|
|
|
|
#include "connection_control.h"
|
|
#include "connection_delay_api.h" /* connection_delay apis */
|
|
#include "connection_control_coordinator.h" /* g_connection_event_coordinator */
|
|
|
|
namespace connection_control
|
|
{
|
|
class Connection_control_error_handler : public Error_handler
|
|
{
|
|
public:
|
|
Connection_control_error_handler(MYSQL_PLUGIN plugin_info)
|
|
: m_plugin_info(plugin_info)
|
|
{}
|
|
|
|
void handle_error(const char * error_message)
|
|
{
|
|
my_plugin_log_message(&m_plugin_info,
|
|
MY_ERROR_LEVEL,
|
|
error_message);
|
|
}
|
|
private:
|
|
MYSQL_PLUGIN m_plugin_info;
|
|
};
|
|
}
|
|
|
|
using connection_control::Connection_event_coordinator;
|
|
using connection_control::Connection_event_coordinator_services;
|
|
using connection_control::Connection_control_statistics;
|
|
using connection_control::Connection_control_variables;
|
|
using connection_control::Error_handler;
|
|
using connection_control::Connection_control_error_handler;
|
|
|
|
Connection_control_statistics g_statistics;
|
|
Connection_control_variables g_variables;
|
|
|
|
Connection_event_coordinator *g_connection_event_coordinator= 0;
|
|
MYSQL_PLUGIN connection_control_plugin_info= 0;
|
|
|
|
|
|
/**
|
|
event_notify() implementation for connection_control
|
|
|
|
For connection event, notify Connection_event_coordinator
|
|
which in turn will notify subscribers.
|
|
|
|
@param [in] thd Handle to THD
|
|
@param [in] event_class Event class.
|
|
We are interested in MYSQL_AUDIT_CONNECTION_CLASS.
|
|
@param [in] event mysql_event_connection handle
|
|
*/
|
|
|
|
static int
|
|
connection_control_notify(MYSQL_THD thd,
|
|
mysql_event_class_t event_class,
|
|
const void *event)
|
|
{
|
|
DBUG_ENTER("connection_control_notify");
|
|
try
|
|
{
|
|
if (event_class == MYSQL_AUDIT_CONNECTION_CLASS)
|
|
{
|
|
const struct mysql_event_connection *connection_event=
|
|
(const struct mysql_event_connection *) event;
|
|
Connection_control_error_handler error_handler(connection_control_plugin_info);
|
|
/** Notify event coordinator */
|
|
g_connection_event_coordinator->notify_event(thd, &error_handler,
|
|
connection_event);
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
/* Happily ignore any bad behavior */
|
|
}
|
|
|
|
DBUG_RETURN(0);
|
|
}
|
|
|
|
|
|
/**
|
|
Plugin initialization function
|
|
|
|
@param [in] plugin_info MYSQL_PLUGIN information
|
|
|
|
@returns initialization status
|
|
@retval 0 Success
|
|
@retval 1 Failure
|
|
*/
|
|
|
|
static int
|
|
connection_control_init(MYSQL_PLUGIN plugin_info)
|
|
{
|
|
connection_control_plugin_info= plugin_info;
|
|
Connection_control_error_handler error_handler(connection_control_plugin_info);
|
|
g_connection_event_coordinator= new Connection_event_coordinator();
|
|
if (!g_connection_event_coordinator)
|
|
{
|
|
error_handler.handle_error("Failed to initialize Connection_event_coordinator");
|
|
return 1;
|
|
}
|
|
|
|
if (init_connection_delay_event((Connection_event_coordinator_services *)
|
|
g_connection_event_coordinator,
|
|
&error_handler))
|
|
{
|
|
delete g_connection_event_coordinator;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
Plugin deinitialization
|
|
|
|
@param arg Unused
|
|
|
|
@returns success
|
|
*/
|
|
|
|
static int
|
|
connection_control_deinit(void *arg MY_ATTRIBUTE((unused)))
|
|
{
|
|
delete g_connection_event_coordinator;
|
|
g_connection_event_coordinator= 0;
|
|
connection_control::deinit_connection_delay_event();
|
|
connection_control_plugin_info= 0;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/** Connection_control plugin descriptor */
|
|
static struct st_mysql_audit connection_control_descriptor=
|
|
{
|
|
MYSQL_AUDIT_INTERFACE_VERSION, /* interface version */
|
|
NULL, /* release_thd() */
|
|
connection_control_notify, /* event_notify() */
|
|
{ 0, /* MYSQL_AUDIT_GENERAL_CLASS */
|
|
(unsigned long) MYSQL_AUDIT_CONNECTION_CONNECT |
|
|
MYSQL_AUDIT_CONNECTION_CHANGE_USER, /* MYSQL_AUDIT_CONNECTION_CLASS */
|
|
0, /* MYSQL_AUDIT_PARSE_CLASS */
|
|
0, /* MYSQL_AUDIT_AUTHORIZATION_CLASS */
|
|
0, /* MYSQL_AUDIT_TABLE_ACCESS_CLASS */
|
|
0, /* MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS */
|
|
0, /* MYSQL_AUDIT_SERVER_STARTUP_CLASS */
|
|
0, /* MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS */
|
|
0, /* MYSQL_AUDIT_COMMAND_CLASS */
|
|
0, /* MYSQL_AUDIT_QUERY_CLASS */
|
|
0, /* MYSQL_AUDIT_STORED_PROGRAM_CLASS */
|
|
} /* class mask */
|
|
};
|
|
|
|
|
|
/**
|
|
check() function for connection_control_failed_connections_threshold
|
|
|
|
Check whether new value is within valid bounds or not.
|
|
|
|
@param thd Not used.
|
|
@param var Not used.
|
|
@param save Not used.
|
|
@param value New value for the option.
|
|
|
|
@returns whether new value is within valid bounds or not.
|
|
@retval 0 Value is ok
|
|
@retval 1 Value is not within valid bounds
|
|
*/
|
|
|
|
static int
|
|
check_failed_connections_threshold(MYSQL_THD thd MY_ATTRIBUTE((unused)),
|
|
struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
|
|
void *save MY_ATTRIBUTE((unused)),
|
|
struct st_mysql_value *value)
|
|
{
|
|
longlong new_value;
|
|
if (value->val_int(value, &new_value))
|
|
return 1; /* NULL value */
|
|
|
|
if (new_value >= connection_control::MIN_THRESHOLD &&
|
|
new_value <= connection_control::MAX_THRESHOLD)
|
|
{
|
|
*(reinterpret_cast<longlong *>(save))= new_value;
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/**
|
|
update() function for connection_control_failed_connections_threshold
|
|
|
|
Updates g_connection_event_coordinator with new value.
|
|
Also notifies observers about the update.
|
|
|
|
@param thd Not used.
|
|
@param var Not used.
|
|
@param var_ptr Variable information
|
|
@param save New value for connection_control_failed_connections_threshold
|
|
*/
|
|
|
|
static void
|
|
update_failed_connections_threshold(MYSQL_THD thd MY_ATTRIBUTE((unused)),
|
|
struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
|
|
void *var_ptr, const void *save)
|
|
{
|
|
/*
|
|
This won't result in overflow because we have already checked that this is
|
|
within valid bounds.
|
|
*/
|
|
longlong new_value= *(reinterpret_cast<const longlong *>(save));
|
|
g_variables.failed_connections_threshold= (int64)new_value;
|
|
Connection_control_error_handler error_handler(connection_control_plugin_info);
|
|
g_connection_event_coordinator->notify_sys_var(&error_handler,
|
|
OPT_FAILED_CONNECTIONS_THRESHOLD,
|
|
&new_value);
|
|
return;
|
|
}
|
|
|
|
|
|
/** Declaration of connection_control_failed_connections_threshold */
|
|
static MYSQL_SYSVAR_LONGLONG(
|
|
failed_connections_threshold,
|
|
g_variables.failed_connections_threshold,
|
|
PLUGIN_VAR_RQCMDARG,
|
|
"Failed connection threshold to trigger delay. Default is 3.",
|
|
check_failed_connections_threshold,
|
|
update_failed_connections_threshold,
|
|
connection_control::DEFAULT_THRESHOLD,
|
|
connection_control::MIN_THRESHOLD,
|
|
connection_control::MAX_THRESHOLD,
|
|
1
|
|
);
|
|
|
|
|
|
/**
|
|
check() function for connection_control_min_connection_delay
|
|
|
|
Check whether new value is within valid bounds or not.
|
|
|
|
@param thd Not used.
|
|
@param var Not used.
|
|
@param save Not used.
|
|
@param value New value for the option.
|
|
|
|
@returns whether new value is within valid bounds or not.
|
|
@retval 0 Value is ok
|
|
@retval 1 Value is not within valid bounds
|
|
*/
|
|
|
|
static int
|
|
check_min_connection_delay(MYSQL_THD thd MY_ATTRIBUTE((unused)),
|
|
struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
|
|
void *save MY_ATTRIBUTE((unused)),
|
|
struct st_mysql_value *value)
|
|
{
|
|
long long new_value;
|
|
int64 existing_value= g_variables.max_connection_delay;
|
|
if (value->val_int(value, &new_value))
|
|
return 1; /* NULL value */
|
|
|
|
if (new_value >= connection_control::MIN_DELAY &&
|
|
new_value <= connection_control::MAX_DELAY &&
|
|
new_value <= existing_value)
|
|
{
|
|
*(reinterpret_cast<longlong *>(save))= new_value;
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/**
|
|
update() function for connection_control_min_connection_delay
|
|
|
|
Updates g_connection_event_coordinator with new value.
|
|
Also notifies observers about the update.
|
|
|
|
@param thd Not used.
|
|
@param var Not used.
|
|
@param var_ptr Variable information
|
|
@param save New value for connection_control_min_connection_delay
|
|
*/
|
|
|
|
static void
|
|
update_min_connection_delay(MYSQL_THD thd MY_ATTRIBUTE((unused)),
|
|
struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
|
|
void *var_ptr, const void *save)
|
|
{
|
|
longlong new_value= *(reinterpret_cast<const longlong *>(save));
|
|
g_variables.min_connection_delay= (int64)new_value;
|
|
Connection_control_error_handler error_handler(connection_control_plugin_info);
|
|
g_connection_event_coordinator->notify_sys_var(&error_handler,
|
|
OPT_MIN_CONNECTION_DELAY,
|
|
&new_value);
|
|
return;
|
|
}
|
|
|
|
|
|
/** Declaration of connection_control_max_connection_delay */
|
|
static MYSQL_SYSVAR_LONGLONG(
|
|
min_connection_delay,
|
|
g_variables.min_connection_delay,
|
|
PLUGIN_VAR_RQCMDARG,
|
|
"Maximum delay to be introduced. Default is 1000.",
|
|
check_min_connection_delay,
|
|
update_min_connection_delay,
|
|
connection_control::DEFAULT_MIN_DELAY,
|
|
connection_control::MIN_DELAY,
|
|
connection_control::MAX_DELAY,
|
|
1
|
|
);
|
|
|
|
|
|
/**
|
|
check() function for connection_control_max_connection_delay
|
|
|
|
Check whether new value is within valid bounds or not.
|
|
|
|
@param thd Not used.
|
|
@param var Not used.
|
|
@param save Not used.
|
|
@param value New value for the option.
|
|
|
|
@returns whether new value is within valid bounds or not.
|
|
@retval 0 Value is ok
|
|
@retval 1 Value is not within valid bounds
|
|
*/
|
|
|
|
static int
|
|
check_max_connection_delay(MYSQL_THD thd MY_ATTRIBUTE((unused)),
|
|
struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
|
|
void *save MY_ATTRIBUTE((unused)),
|
|
struct st_mysql_value *value)
|
|
{
|
|
long long new_value;
|
|
int64 existing_value= my_atomic_load64(&g_variables.min_connection_delay);
|
|
if (value->val_int(value, &new_value))
|
|
return 1; /* NULL value */
|
|
|
|
if (new_value >= connection_control::MIN_DELAY &&
|
|
new_value <= connection_control::MAX_DELAY &&
|
|
new_value >= existing_value)
|
|
{
|
|
*(reinterpret_cast<longlong *>(save))= new_value;
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/**
|
|
update() function for connection_control_max_connection_delay
|
|
|
|
Updates g_connection_event_coordinator with new value.
|
|
Also notifies observers about the update.
|
|
|
|
@param thd Not used.
|
|
@param var Not used.
|
|
@param var_ptr Variable information
|
|
@param save New value for connection_control_max_connection_delay
|
|
*/
|
|
|
|
static void
|
|
update_max_connection_delay(MYSQL_THD thd MY_ATTRIBUTE((unused)),
|
|
struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
|
|
void *var_ptr, const void *save)
|
|
{
|
|
longlong new_value= *(reinterpret_cast<const longlong *>(save));
|
|
my_atomic_store64(&g_variables.max_connection_delay, (int64)new_value);
|
|
Connection_control_error_handler error_handler(connection_control_plugin_info);
|
|
g_connection_event_coordinator->notify_sys_var(&error_handler,
|
|
OPT_MAX_CONNECTION_DELAY,
|
|
&new_value);
|
|
return;
|
|
}
|
|
|
|
|
|
/** Declaration of connection_control_max_connection_delay */
|
|
static MYSQL_SYSVAR_LONGLONG(
|
|
max_connection_delay,
|
|
g_variables.max_connection_delay,
|
|
PLUGIN_VAR_RQCMDARG,
|
|
"Maximum delay to be introduced. Default is 2147483647.",
|
|
check_max_connection_delay,
|
|
update_max_connection_delay,
|
|
connection_control::DEFAULT_MAX_DELAY,
|
|
connection_control::MIN_DELAY,
|
|
connection_control::MAX_DELAY,
|
|
1
|
|
);
|
|
|
|
|
|
/** Array of system variables. Used in plugin declaration. */
|
|
struct st_mysql_sys_var *
|
|
connection_control_system_variables[OPT_LAST + 1]=
|
|
{
|
|
MYSQL_SYSVAR(failed_connections_threshold),
|
|
MYSQL_SYSVAR(min_connection_delay),
|
|
MYSQL_SYSVAR(max_connection_delay),
|
|
NULL
|
|
};
|
|
|
|
|
|
/**
|
|
Function to display value for status variable : Connection_control_delay_generated
|
|
|
|
@param thd MYSQL_THD handle. Unused.
|
|
@param var Status variable structure
|
|
@param buff Value buffer.
|
|
|
|
@returns Always returns success.
|
|
*/
|
|
|
|
static int show_delay_generated(MYSQL_THD,
|
|
struct st_mysql_show_var *var,
|
|
char *buff)
|
|
{
|
|
var->type= SHOW_LONGLONG;
|
|
var->value= buff;
|
|
longlong *value= reinterpret_cast<longlong *>(buff);
|
|
int64 current_val= my_atomic_load64(&g_statistics.stats_array[STAT_CONNECTION_DELAY_TRIGGERED]);
|
|
*value= static_cast<longlong>(current_val);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/** Array of status variables. Used in plugin declaration. */
|
|
struct st_mysql_show_var
|
|
connection_control_status_variables[STAT_LAST + 1]={
|
|
{
|
|
"Connection_control_delay_generated",
|
|
(char *)&show_delay_generated,
|
|
SHOW_FUNC, SHOW_SCOPE_GLOBAL
|
|
},
|
|
{0, 0, enum_mysql_show_type(0), enum_mysql_show_scope(0)}
|
|
};
|
|
|
|
|
|
mysql_declare_plugin(audit_log)
|
|
{
|
|
MYSQL_AUDIT_PLUGIN, /* plugin type */
|
|
&connection_control_descriptor, /* type specific descriptor */
|
|
"CONNECTION_CONTROL", /* plugin name */
|
|
"Oracle Inc", /* author */
|
|
"Connection event processing", /* description */
|
|
PLUGIN_LICENSE_GPL , /* license */
|
|
connection_control_init, /* plugin initializer */
|
|
connection_control_deinit, /* plugin deinitializer */
|
|
0x0100, /* version */
|
|
connection_control_status_variables, /* status variables */
|
|
connection_control_system_variables, /* system variables */
|
|
NULL, /* reserverd */
|
|
0 /* flags */
|
|
},
|
|
{
|
|
MYSQL_INFORMATION_SCHEMA_PLUGIN,
|
|
&connection_control_failed_attempts_view,
|
|
"CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS",
|
|
"Oracle Inc",
|
|
"I_S table providing a view into failed attempts statistics",
|
|
PLUGIN_LICENSE_GPL,
|
|
connection_control_failed_attempts_view_init,
|
|
NULL,
|
|
0x0100,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
0
|
|
}
|
|
mysql_declare_plugin_end;
|