polardbxengine/plugin/connection_control/connection_delay.h

246 lines
7.7 KiB
C++

/* Copyright (c) 2016, 2019, 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, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
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, version 2.0, 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 */
#ifndef CONNECTION_DELAY_H
#define CONNECTION_DELAY_H
#include <atomic>
#include "lf.h" /* LF Hash */
#include "my_hostname.h" /* HOSTNAME_LENGTH */
#include "my_inttypes.h"
#include "mysql_com.h" /* USERNAME_LENGTH */
#include "plugin/connection_control/connection_control_data.h" /* variables and status */
#include "plugin/connection_control/connection_control_interfaces.h" /* Observer interface */
#include "plugin/connection_control/connection_control_memory.h" /* Connection_control_alloc */
#include "plugin/connection_control/connection_delay_api.h" /* Constants */
#include "sql/table.h" /* TABLE_LIST */
namespace connection_control {
/**
Class to store failed attempts information for a given user.
*/
class Connection_event_record : public Connection_control_alloc {
public:
/**
Constructor for Connection_event_record. Always initializes failed login
count to 1.
*/
Connection_event_record(const Sql_string &s) : m_count(1) {
memset((void *)m_userhost, 0, sizeof(m_userhost));
memcpy((void *)m_userhost, s.c_str(), s.length());
m_length = s.length();
}
/**
Retrives failed login count for given user entry
@returns Failed login count
*/
int64 get_count() const { return m_count.load(); }
/** Increment failed login count for given user entry by 1 */
void inc_count() { ++m_count; }
/** Reset failed login count for given user entry */
void reset_count() { m_count.store(0); }
/** Get user information */
uchar *get_userhost() const { return const_cast<uchar *>(m_userhost); }
/** Get length information */
size_t get_length() const { return m_length; }
/** Destructor */
~Connection_event_record() { m_count = DISABLE_THRESHOLD; }
private:
/* '<user>'@'<host>' */
uchar m_userhost[1 + USERNAME_LENGTH + 3 + HOSTNAME_LENGTH + 1 + 1];
/* Length of m_userhost */
size_t m_length;
/* connection event count */
std::atomic<int64> m_count;
};
/**
Hash for a connection event.
Stores information in Connection_event_record object for each user.
*/
class Connection_delay_event : public Connection_event_records {
public:
/** Constructor. Also initializes the hash */
Connection_delay_event();
/** Destructor. Removes all entries from hash before destroying hash */
~Connection_delay_event() {
reset_all();
lf_hash_destroy(&m_entries);
}
void fill_IS_table(TABLE_LIST *tables);
/* Overridden function */
bool create_or_update_entry(const Sql_string &s);
bool remove_entry(const Sql_string &s);
bool match_entry(const Sql_string &s, void *value);
void reset_all();
private:
/** Hash for storing Connection_event_record per user */
LF_HASH m_entries;
};
/**
Connection event action to enforce max failed login constraint
*/
class Connection_delay_action : public Connection_event_observer,
public Connection_control_alloc {
public:
Connection_delay_action(int64 threshold, int64 min_delay, int64 max_delay,
opt_connection_control *sys_vars,
size_t sys_vars_size,
stats_connection_control *status_vars,
size_t status_vars_size, mysql_rwlock_t *lock);
/** Destructor */
~Connection_delay_action() {
deinit();
m_lock = 0;
}
void init(Connection_event_coordinator_services *coordinator);
/**
Set threshold value.
@param threshold [in] New threshold value
@returns whether threshold value was changed successfully or not
@retval true Success
@retval false Failure. Invalid threshold value specified.
*/
void set_threshold(int64 threshold) {
m_threshold.store(threshold);
/* Clear the hash */
m_userhost_hash.reset_all();
}
/** Get threshold value */
int64 get_threshold() { return m_threshold.load(); }
/**
Set min/max delay
@param new_value [in] New m_min_delay/m_max_delay value
@param min [in] true for m_min_delay. false otherwise.
@returns whether m_min_delay/m_max_delay value was changed successfully or
not
@retval false Success
@retval true Failure. Invalid value specified.
*/
bool set_delay(int64 new_value, bool min) {
int64 current_max = get_max_delay();
int64 current_min = get_min_delay();
if (new_value < MIN_DELAY) return true;
if ((min && new_value > current_max) || (!min && new_value < current_min))
return true;
else
min ? m_min_delay.store(new_value) : m_max_delay.store(new_value);
return false;
}
/** Get max value */
int64 get_max_delay() { return m_max_delay.load(); }
/** Get min value */
int64 get_min_delay() { return m_min_delay.load(); }
void fill_IS_table(THD *thd, TABLE_LIST *tables, Item *cond);
/** Overridden functions */
bool notify_event(MYSQL_THD thd,
Connection_event_coordinator_services *coordinator,
const mysql_event_connection *connection_event,
Error_handler *error_handler);
bool notify_sys_var(Connection_event_coordinator_services *coordinator,
opt_connection_control variable, void *new_value,
Error_handler *error_handler);
private:
void deinit();
void make_hash_key(MYSQL_THD thd, Sql_string &s);
/**
Generates wait time
@param count [in] Proposed delay
@returns wait time
*/
inline ulonglong get_wait_time(int64 count) {
int64 max_delay = get_max_delay();
int64 min_delay = get_min_delay();
int64 count_mili = count * 1000;
/*
if count < 0 (can happen in edge cases
we return max_delay.
Otherwise, following equation will be used:
wait_time = MIN(MIN(count, min_delay),
max_delay)
*/
return (static_cast<ulonglong>(
(count_mili >= MIN_DELAY && count_mili < max_delay)
? (count_mili < min_delay ? min_delay : count_mili)
: max_delay));
}
void conditional_wait(THD *thd, ulonglong wait_time);
private:
/** Threshold value which triggers wait */
std::atomic<int64> m_threshold;
/** Lower cap on delay to be generated */
std::atomic<int64> m_min_delay;
/** Upper cap on delay to be generated */
std::atomic<int64> m_max_delay;
/** System variables */
std::vector<opt_connection_control> m_sys_vars;
/** Status variables */
std::vector<stats_connection_control> m_stats_vars;
/** Hash to store failed attempts for each user entry */
Connection_delay_event m_userhost_hash;
/** RW lock */
mysql_rwlock_t *m_lock;
};
} // namespace connection_control
#endif /* !CONNECTION_DELAY_H */