/* 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 */ #ifndef CONNECTION_DELAY_H #define CONNECTION_DELAY_H #include /* LF Hash */ #include #include /* my_atomic_* */ #include /* USERNAME_LENGTH */ #include "table.h" /* TABLE_LIST */ #include "connection_control_interfaces.h" /* Observer interface */ #include "connection_delay_api.h" /* Constants */ #include "connection_control_data.h" /* variables and status */ #include "connection_control_memory.h" /* Connection_control_alloc */ 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(); m_count= 1; } /** Retrives failed login count for given user entry @returns Failed login count */ int64 get_count() const { int64 result= my_atomic_load64((volatile int64*)&m_count); return result; } /** Increment failed login count for given user entry by 1 */ void inc_count() { my_atomic_add64((volatile int64*)&m_count, 1); } /** Reset failed login count for given user entry */ void reset_count() { my_atomic_store64(&m_count, 0); } /** Get user information */ uchar * get_userhost() const { return const_cast (m_userhost); } /** Get length information */ size_t get_length() const { return m_length; } /** Destructor */ ~Connection_event_record() { m_count= DISABLE_THRESHOLD; } private: /* ''@'' */ uchar m_userhost[1 + USERNAME_LENGTH + 3 + HOSTNAME_LENGTH + 1 + 1]; /* Length of m_userhost */ size_t m_length; /* connection event count */ volatile 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(THD *thd, 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) { my_atomic_store64(&m_threshold, threshold); /* Clear the hash */ m_userhost_hash.reset_all(); } /** Get threshold value */ int64 get_threshold() { int64 result= my_atomic_load64(&m_threshold); return result; } /** 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 ? my_atomic_store64(&m_min_delay, new_value) : my_atomic_store64(&m_max_delay, new_value); return false; } /** Get max value */ int64 get_max_delay() { int64 result= my_atomic_load64(&m_max_delay); return result; } /** Get min value */ int64 get_min_delay() { int64 result= my_atomic_load64(&m_min_delay); return result; } 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((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 */ volatile int64 m_threshold; /** Lower cap on delay to be generated */ volatile int64 m_min_delay; /** Upper cap on delay to be generated */ volatile int64 m_max_delay; /** System variables */ std::vector m_sys_vars; /** Status variables */ std::vector 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; }; } #endif /* !CONNECTION_DELAY_H */