Commit ecbf36a9 authored by unknown's avatar unknown

MDEV-4058

MySQL 5.6.10 performance schema: merge of host_cache table
parent 68262ba6
......@@ -34,6 +34,27 @@
return values of the plugin authenticate_user() method.
*/
/**
Authentication failed, plugin internal error.
An error occurred in the authentication plugin itself.
These errors are reported in table performance_schema.host_cache,
column COUNT_AUTH_PLUGIN_ERRORS.
*/
#define CR_AUTH_PLUGIN_ERROR 3
/**
Authentication failed, client server handshake.
An error occurred during the client server handshake.
These errors are reported in table performance_schema.host_cache,
column COUNT_HANDSHAKE_ERRORS.
*/
#define CR_AUTH_HANDSHAKE 2
/**
Authentication failed, user credentials.
For example, wrong passwords.
These errors are reported in table performance_schema.host_cache,
column COUNT_AUTHENTICATION_ERRORS.
*/
#define CR_AUTH_USER_CREDENTIALS 1
/**
Authentication failed. Additionally, all other CR_xxx values
(libmysql error code) can be used too.
......
......@@ -497,10 +497,9 @@ SET @cmd="CREATE TABLE performance_schema.host_cache("
")ENGINE=PERFORMANCE_SCHEMA;";
SET @str = IF(@have_pfs = 1, @cmd, 'SET @dummy = 0');
# Host cache is disable until host cache code is merged from 5.6
#PREPARE stmt FROM @str;
#EXECUTE stmt;
#DROP PREPARE stmt;
PREPARE stmt FROM @str;
EXECUTE stmt;
DROP PREPARE stmt;
--
-- TABLE MUTEX_INSTANCES
......
/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
/* Copyright (c) 2000, 2011, 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
......@@ -47,8 +47,11 @@ private:
class hash_filo
{
const uint size, key_offset, key_length;
private:
const uint key_offset, key_length;
const my_hash_get_key get_key;
/** Size of this hash table. */
uint m_size;
my_hash_free_key free_element;
bool init;
CHARSET_INFO *hash_charset;
......@@ -61,9 +64,12 @@ public:
hash_filo(uint size_arg, uint key_offset_arg , uint key_length_arg,
my_hash_get_key get_key_arg, my_hash_free_key free_element_arg,
CHARSET_INFO *hash_charset_arg)
:size(size_arg), key_offset(key_offset_arg), key_length(key_length_arg),
get_key(get_key_arg), free_element(free_element_arg),init(0),
hash_charset(hash_charset_arg)
:key_offset(key_offset_arg), key_length(key_length_arg),
get_key(get_key_arg), m_size(size_arg),
free_element(free_element_arg),init(0),
hash_charset(hash_charset_arg),
first_link(NULL),
last_link(NULL)
{
bzero((char*) &cache,sizeof(cache));
}
......@@ -86,32 +92,61 @@ public:
}
if (!locked)
mysql_mutex_lock(&lock);
first_link= NULL;
last_link= NULL;
(void) my_hash_free(&cache);
(void) my_hash_init(&cache,hash_charset,size,key_offset,
(void) my_hash_init(&cache,hash_charset,m_size,key_offset,
key_length, get_key, free_element,0);
if (!locked)
mysql_mutex_unlock(&lock);
first_link=last_link=0;
}
hash_filo_element *first()
{
mysql_mutex_assert_owner(&lock);
return first_link;
}
hash_filo_element *last()
{
mysql_mutex_assert_owner(&lock);
return last_link;
}
hash_filo_element *search(uchar* key, size_t length)
{
mysql_mutex_assert_owner(&lock);
hash_filo_element *entry=(hash_filo_element*)
my_hash_search(&cache,(uchar*) key,length);
if (entry)
{ // Found; link it first
DBUG_ASSERT(first_link != NULL);
DBUG_ASSERT(last_link != NULL);
if (entry != first_link)
{ // Relink used-chain
if (entry == last_link)
last_link=entry->prev_used;
{
last_link= last_link->prev_used;
/*
The list must have at least 2 elements,
otherwise entry would be equal to first_link.
*/
DBUG_ASSERT(last_link != NULL);
last_link->next_used= NULL;
}
else
{
DBUG_ASSERT(entry->next_used != NULL);
DBUG_ASSERT(entry->prev_used != NULL);
entry->next_used->prev_used = entry->prev_used;
entry->prev_used->next_used = entry->next_used;
}
if ((entry->next_used= first_link))
first_link->prev_used=entry;
first_link=entry;
entry->prev_used= NULL;
entry->next_used= first_link;
first_link->prev_used= entry;
first_link=entry;
}
}
return entry;
......@@ -119,10 +154,20 @@ public:
bool add(hash_filo_element *entry)
{
if (cache.records == size)
if (!m_size) return 1;
if (cache.records == m_size)
{
hash_filo_element *tmp=last_link;
last_link=last_link->prev_used;
last_link= last_link->prev_used;
if (last_link != NULL)
{
last_link->next_used= NULL;
}
else
{
/* Pathological case, m_size == 1 */
first_link= NULL;
}
my_hash_delete(&cache,(uchar*) tmp);
}
if (my_hash_insert(&cache,(uchar*) entry))
......@@ -131,13 +176,27 @@ public:
(*free_element)(entry); // This should never happen
return 1;
}
if ((entry->next_used=first_link))
first_link->prev_used=entry;
entry->prev_used= NULL;
entry->next_used= first_link;
if (first_link != NULL)
first_link->prev_used= entry;
else
last_link=entry;
first_link=entry;
last_link= entry;
first_link= entry;
return 0;
}
uint size()
{ return m_size; }
void resize(uint new_size)
{
mysql_mutex_lock(&lock);
m_size= new_size;
clear(true);
mysql_mutex_unlock(&lock);
}
};
#endif
This diff is collapsed.
/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
/* Copyright (c) 2006, 2011, 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
......@@ -17,14 +17,168 @@
#define HOSTNAME_INCLUDED
#include "my_global.h" /* uint */
#include "my_net.h"
#include "hash_filo.h"
bool ip_to_hostname(struct sockaddr_storage *ip_storage,
const char *ip_string,
char **hostname, uint *connect_errors);
void inc_host_errors(const char *ip_string);
void reset_host_errors(const char *ip_string);
struct Host_errors
{
public:
Host_errors();
~Host_errors();
void reset();
void aggregate(const Host_errors *errors);
/** Number of connect errors. */
ulong m_connect;
/** Number of host blocked errors. */
ulong m_host_blocked;
/** Number of transient errors from getnameinfo(). */
ulong m_nameinfo_transient;
/** Number of permanent errors from getnameinfo(). */
ulong m_nameinfo_permanent;
/** Number of errors from is_hostname_valid(). */
ulong m_format;
/** Number of transient errors from getaddrinfo(). */
ulong m_addrinfo_transient;
/** Number of permanent errors from getaddrinfo(). */
ulong m_addrinfo_permanent;
/** Number of errors from Forward-Confirmed reverse DNS checks. */
ulong m_FCrDNS;
/** Number of errors from host grants. */
ulong m_host_acl;
/** Number of errors from missing auth plugin. */
ulong m_no_auth_plugin;
/** Number of errors from auth plugin. */
ulong m_auth_plugin;
/** Number of errors from authentication plugins. */
ulong m_handshake;
/** Number of errors from proxy user. */
ulong m_proxy_user;
/** Number of errors from proxy user acl. */
ulong m_proxy_user_acl;
/** Number of errors from authentication. */
ulong m_authentication;
/** Number of errors from ssl. */
ulong m_ssl;
/** Number of errors from max user connection. */
ulong m_max_user_connection;
/** Number of errors from max user connection per hour. */
ulong m_max_user_connection_per_hour;
/** Number of errors from the default database. */
ulong m_default_database;
/** Number of errors from init_connect. */
ulong m_init_connect;
/** Number of errors from the server itself. */
ulong m_local;
bool has_error() const
{
return ((m_host_blocked != 0)
|| (m_nameinfo_transient != 0)
|| (m_nameinfo_permanent != 0)
|| (m_format != 0)
|| (m_addrinfo_transient != 0)
|| (m_addrinfo_permanent != 0)
|| (m_FCrDNS != 0)
|| (m_host_acl != 0)
|| (m_no_auth_plugin != 0)
|| (m_auth_plugin != 0)
|| (m_handshake != 0)
|| (m_proxy_user != 0)
|| (m_proxy_user_acl != 0)
|| (m_authentication != 0)
|| (m_ssl != 0)
|| (m_max_user_connection != 0)
|| (m_max_user_connection_per_hour != 0)
|| (m_default_database != 0)
|| (m_init_connect != 0)
|| (m_local != 0));
}
void sum_connect_errors()
{
/* Current (historical) behavior: */
m_connect= m_handshake;
}
void clear_connect_errors()
{
m_connect= 0;
}
};
/** Size of IP address string in the hash cache. */
#define HOST_ENTRY_KEY_SIZE INET6_ADDRSTRLEN
/**
An entry in the hostname hash table cache.
Host name cache does two things:
- caches host names to save DNS look ups;
- counts errors from IP.
Host name can be empty (that means DNS look up failed),
but errors still are counted.
*/
class Host_entry : public hash_filo_element
{
public:
Host_entry *next()
{ return (Host_entry*) hash_filo_element::next(); }
/**
Client IP address. This is the key used with the hash table.
The client IP address is always expressed in IPv6, even when the
network IPv6 stack is not present.
This IP address is never used to connect to a socket.
*/
char ip_key[HOST_ENTRY_KEY_SIZE];
/**
One of the host names for the IP address. May be a zero length string.
*/
char m_hostname[HOSTNAME_LENGTH + 1];
/** Length in bytes of @c m_hostname. */
uint m_hostname_length;
/** The hostname is validated and used for authorization. */
bool m_host_validated;
ulonglong m_first_seen;
ulonglong m_last_seen;
ulonglong m_first_error_seen;
ulonglong m_last_error_seen;
/** Error statistics. */
Host_errors m_errors;
void set_error_timestamps(ulonglong now)
{
if (m_first_error_seen == 0)
m_first_error_seen= now;
m_last_error_seen= now;
}
};
/** The size of the host_cache. */
extern ulong host_cache_size;
#define RC_OK 0
#define RC_BLOCKED_HOST 1
int ip_to_hostname(struct sockaddr_storage *ip_storage,
const char *ip_string,
char **hostname, uint *connect_errors);
void inc_host_errors(const char *ip_string, Host_errors *errors);
void reset_host_connect_errors(const char *ip_string);
bool hostname_cache_init();
void hostname_cache_free();
void hostname_cache_refresh(void);
uint hostname_cache_size();
void hostname_cache_resize(uint size);
void hostname_cache_lock();
void hostname_cache_unlock();
Host_entry *hostname_cache_first();
#endif /* HOSTNAME_INCLUDED */
......@@ -612,6 +612,19 @@ const char *in_left_expr_name= "<left expr>";
const char *in_additional_cond= "<IN COND>";
const char *in_having_cond= "<IN HAVING>";
/** Number of connection errors when selecting on the listening port */
ulong connection_errors_select= 0;
/** Number of connection errors when accepting sockets in the listening port. */
ulong connection_errors_accept= 0;
/** Number of connection errors from TCP wrappers. */
ulong connection_errors_tcpwrap= 0;
/** Number of connection errors from internal server errors. */
ulong connection_errors_internal= 0;
/** Number of connection errors from the server max_connection limit. */
ulong connection_errors_max_connection= 0;
/** Number of errors when reading the peer address. */
ulong connection_errors_peer_addr= 0;
/* classes for comparation parsing/processing */
Eq_creator eq_creator;
Ne_creator ne_creator;
......@@ -3648,6 +3661,12 @@ static void my_malloc_size_cb_func(long long size, my_bool is_thread_specific)
static int init_common_variables()
{
umask(((~my_umask) & 0666));
connection_errors_select= 0;
connection_errors_accept= 0;
connection_errors_tcpwrap= 0;
connection_errors_internal= 0;
connection_errors_max_connection= 0;
connection_errors_peer_addr= 0;
my_decimal_set_zero(&decimal_zero); // set decimal_zero constant;
if (pthread_key_create(&THR_THD,NULL) ||
......@@ -5655,6 +5674,7 @@ void create_thread_to_handle_connection(THD *thd)
mysql_mutex_unlock(&LOCK_connection_count);
statistic_increment(aborted_connects,&LOCK_status);
statistic_increment(connection_errors_internal, &LOCK_status);
/* Can't use my_error() since store_globals has not been called. */
my_snprintf(error_message_buff, sizeof(error_message_buff),
ER_THD(thd, ER_CANT_CREATE_THREAD), error);
......@@ -5705,6 +5725,7 @@ static void create_new_thread(THD *thd)
close_connection(thd, ER_CON_COUNT_ERROR);
statistic_increment(denied_connections, &LOCK_status);
delete thd;
statistic_increment(connection_errors_max_connection, &LOCK_status);
DBUG_VOID_RETURN;
}
......@@ -5819,6 +5840,12 @@ void handle_connections_sockets()
{
if (socket_errno != SOCKET_EINTR)
{
/*
select(2)/poll(2) failed on the listening port.
There is not much details to report about the client,
increment the server global status variable.
*/
statistic_increment(connection_errors_accept, &LOCK_status);
if (!select_errors++ && !abort_loop) /* purecov: inspected */
sql_print_error("mysqld: Got error %d from select",socket_errno); /* purecov: inspected */
}
......@@ -5899,6 +5926,12 @@ void handle_connections_sockets()
#endif
if (mysql_socket_getfd(new_sock) == INVALID_SOCKET)
{
/*
accept(2) failed on the listening port, after many retries.
There is not much details to report about the client,
increment the server global status variable.
*/
statistic_increment(connection_errors_accept, &LOCK_status);
if ((error_count++ & 255) == 0) // This can happen often
sql_perror("Error in accept");
MAYBE_BROKEN_SYSCALL;
......@@ -5938,6 +5971,11 @@ void handle_connections_sockets()
(void) mysql_socket_shutdown(new_sock, SHUT_RDWR);
(void) mysql_socket_close(new_sock);
/*
The connection was refused by TCP wrappers.
There are no details (by client IP) available to update the host_cache.
*/
statistic_increment(connection_tcpwrap_errors, &LOCK_status);
continue;
}
}
......@@ -5968,6 +6006,7 @@ void handle_connections_sockets()
{
(void) mysql_socket_shutdown(new_sock, SHUT_RDWR);
(void) mysql_socket_close(new_sock);
statistic_increment(connection_errors_internal, &LOCK_status);
continue;
}
/* Set to get io buffers to be part of THD */
......@@ -5996,6 +6035,7 @@ void handle_connections_sockets()
}
delete thd;
set_current_thd(0);
statistic_increment(connection_errors_internal, &LOCK_status);
continue;
}
......@@ -7363,6 +7403,12 @@ SHOW_VAR status_vars[]= {
{"Com", (char*) com_status_vars, SHOW_ARRAY},
{"Compression", (char*) &show_net_compression, SHOW_SIMPLE_FUNC},
{"Connections", (char*) &thread_id, SHOW_LONG_NOFLUSH},
{"Connection_errors_accept", (char*) &connection_errors_accept, SHOW_LONG},
{"Connection_errors_internal", (char*) &connection_errors_internal, SHOW_LONG},
{"Connection_errors_max_connections", (char*) &connection_errors_max_connection, SHOW_LONG},
{"Connection_errors_peer_address", (char*) &connection_errors_peer_addr, SHOW_LONG},
{"Connection_errors_select", (char*) &connection_errors_select, SHOW_LONG},
{"Connection_errors_tcpwrap", (char*) &connection_errors_tcpwrap, SHOW_LONG},
{"Cpu_time", (char*) offsetof(STATUS_VAR, cpu_time), SHOW_DOUBLE_STATUS},
{"Created_tmp_disk_tables", (char*) offsetof(STATUS_VAR, created_tmp_disk_tables_), SHOW_LONG_STATUS},
{"Created_tmp_files", (char*) &my_tmp_file_created, SHOW_LONG},
......
......@@ -213,6 +213,12 @@ extern int bootstrap_error;
extern I_List<THD> threads;
extern char err_shared_dir[];
extern TYPELIB thread_handling_typelib;
extern ulong connection_errors_select;
extern ulong connection_errors_accept;
extern ulong connection_errors_tcpwrap;
extern ulong connection_errors_internal;
extern ulong connection_errors_max_connection;
extern ulong connection_errors_peer_addr;
extern ulong log_warnings;
/*
......
......@@ -1829,6 +1829,13 @@ bool acl_check_host(const char *host, const char *ip)
}
}
mysql_mutex_unlock(&acl_cache->lock);
if (ip != NULL)
{
/* Increment HOST_CACHE.COUNT_HOST_ACL_ERRORS. */
Host_errors errors;
errors.m_host_acl= 1;
inc_host_errors(ip, &errors);
}
return 1; // Host is not allowed
}
......@@ -8354,7 +8361,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
DBUG_ASSERT(net->read_pos[pkt_len] == 0);
if (mpvio->connect_errors)
reset_host_errors(thd->main_security_ctx.ip);
reset_host_connect_errors(thd->main_security_ctx.ip);
ulong client_capabilities= uint2korr(net->read_pos);
if (client_capabilities & CLIENT_PROTOCOL_41)
......@@ -8731,7 +8738,6 @@ static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
err:
if (mpvio->status == MPVIO_EXT::FAILURE)
{
inc_host_errors(mpvio->thd->security_ctx->ip);
if (!mpvio->thd->is_error())
{
if (mpvio->make_it_fail)
......@@ -8891,6 +8897,9 @@ static int do_auth_once(THD *thd, const LEX_STRING *auth_plugin_name,
else
{
/* Server cannot load the required plugin. */
Host_errors errors;
errors.m_no_auth_plugin= 1;
inc_host_errors(mpvio->thd->security_ctx->ip, &errors);
my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth_plugin_name->str);
res= CR_ERROR;
}
......@@ -9018,8 +9027,26 @@ bool acl_authenticate(THD *thd, uint connect_errors,
if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
{
Host_errors errors;
DBUG_ASSERT(mpvio.status == MPVIO_EXT::FAILURE);
switch (res)
{
case CR_AUTH_PLUGIN_ERROR:
errors.m_auth_plugin= 1;
break;
case CR_AUTH_HANDSHAKE:
errors.m_handshake= 1;
break;
case CR_AUTH_USER_CREDENTIALS:
errors.m_authentication= 1;
break;
case CR_ERROR:
default:
/* Unknown of unspecified auth plugin error. */
errors.m_auth_plugin= 1;
break;
}
inc_host_errors(mpvio.thd->security_ctx->ip, &errors);
if (!thd->is_error())
login_failed_error(thd);
DBUG_RETURN(1);
......@@ -9207,7 +9234,7 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
/* and send it to the client */
if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
DBUG_RETURN(CR_ERROR);
DBUG_RETURN(CR_AUTH_HANDSHAKE);
}
/* reply and authenticate */
......@@ -9249,7 +9276,7 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
/* read the reply with the encrypted password */
if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
DBUG_RETURN(CR_ERROR);
DBUG_RETURN(CR_AUTH_HANDSHAKE);
DBUG_PRINT("info", ("reply read : pkt_len=%d", pkt_len));
#ifdef NO_EMBEDDED_ACCESS_CHECKS
......@@ -9257,23 +9284,22 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
#endif
if (pkt_len == 0) /* no password */
DBUG_RETURN(info->auth_string[0] ? CR_ERROR : CR_OK);
DBUG_RETURN(mpvio->acl_user->salt_len != 0 ? CR_AUTH_USER_CREDENTIALS : CR_OK);
info->password_used= PASSWORD_USED_YES;
if (pkt_len == SCRAMBLE_LENGTH)
{
if (!mpvio->acl_user->salt_len)
DBUG_RETURN(CR_ERROR);
DBUG_RETURN(CR_AUTH_USER_CREDENTIALS);
if (check_scramble(pkt, thd->scramble, mpvio->acl_user->salt))
DBUG_RETURN(CR_ERROR);
DBUG_RETURN(CR_AUTH_USER_CREDENTIALS);
else
DBUG_RETURN(CR_OK);
}
inc_host_errors(mpvio->thd->security_ctx->ip);
my_error(ER_HANDSHAKE_ERROR, MYF(0));
DBUG_RETURN(CR_ERROR);
DBUG_RETURN(CR_AUTH_HANDSHAKE);
}
static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
......@@ -9290,12 +9316,12 @@ static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
/* and send it to the client */
if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
return CR_ERROR;
return CR_AUTH_HANDSHAKE;
}
/* read the reply and authenticate */
if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
return CR_ERROR;
return CR_AUTH_HANDSHAKE;
#ifdef NO_EMBEDDED_ACCESS_CHECKS
return CR_OK;
......@@ -9310,26 +9336,25 @@ static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
pkt_len= strnlen((char*)pkt, pkt_len);
if (pkt_len == 0) /* no password */
return info->auth_string[0] ? CR_ERROR : CR_OK;
return info->auth_string[0] ? CR_AUTH_USER_CREDENTIALS : CR_OK;
if (secure_auth(thd))
return CR_ERROR;
return CR_AUTH_HANDSHAKE;
info->password_used= PASSWORD_USED_YES;
if (pkt_len == SCRAMBLE_LENGTH_323)
{
if (!mpvio->acl_user->salt_len)
return CR_ERROR;
return CR_AUTH_USER_CREDENTIALS;
return check_scramble_323(pkt, thd->scramble,
(ulong *) mpvio->acl_user->salt) ?
CR_ERROR : CR_OK;
CR_AUTH_USER_CREDENTIALS : CR_OK;
}
inc_host_errors(mpvio->thd->security_ctx->ip);
my_error(ER_HANDSHAKE_ERROR, MYF(0));
return CR_ERROR;
return CR_AUTH_HANDSHAKE;
}
static struct st_mysql_auth native_password_handler=
......
......@@ -124,6 +124,7 @@ end:
int check_for_max_user_connections(THD *thd, USER_CONN *uc)
{
int error= 1;
Host_errors errors;
DBUG_ENTER("check_for_max_user_connections");
mysql_mutex_lock(&LOCK_user_conn);
......@@ -135,6 +136,8 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc)
!(thd->security_ctx->master_access & SUPER_ACL))
{
my_error(ER_TOO_MANY_USER_CONNECTIONS, MYF(0), uc->user);
error=1;
errors.m_max_user_connection= 1;
goto end;
}
time_out_user_resource_limits(thd, uc);
......@@ -144,6 +147,8 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc)
my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user,
"max_user_connections",
(long) uc->user_resources.user_conn);
error= 1;
errors.m_max_user_connection= 1;
goto end;
}
if (uc->user_resources.conn_per_hour &&
......@@ -152,6 +157,8 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc)
my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user,
"max_connections_per_hour",
(long) uc->user_resources.conn_per_hour);
error=1;
errors.m_max_user_connection_per_hour= 1;
goto end;
}
uc->conn_per_hour++;
......@@ -169,6 +176,10 @@ end:
thd->user_connect= NULL;
}
mysql_mutex_unlock(&LOCK_user_conn);
if (error)
{
inc_host_errors(thd->main_security_ctx.ip, &errors);
}
DBUG_RETURN(error);
}
......@@ -867,7 +878,10 @@ bool init_new_connection_handler_thread()
{
pthread_detach_this_thread();
if (my_thread_init())
{
statistic_increment(connection_errors_internal, &LOCK_status);
return 1;
}
return 0;
}
......@@ -887,6 +901,7 @@ bool init_new_connection_handler_thread()
static int check_connection(THD *thd)
{
uint connect_errors= 0;
int auth_rc;
NET *net= &thd->net;
DBUG_PRINT("info",
......@@ -898,37 +913,103 @@ static int check_connection(THD *thd)
if (!thd->main_security_ctx.host) // If TCP/IP connection
{
my_bool peer_rc;
char ip[NI_MAXHOST];
if (vio_peer_addr(net->vio, ip, &thd->peer_port, NI_MAXHOST))
{
my_error(ER_BAD_HOST_ERROR, MYF(0));
return 1;
}
/* BEGIN : DEBUG */
DBUG_EXECUTE_IF("addr_fake_ipv4",
peer_rc= vio_peer_addr(net->vio, ip, &thd->peer_port, NI_MAXHOST);
/*
===========================================================================
DEBUG code only (begin)
Simulate various output from vio_peer_addr().
===========================================================================
*/
DBUG_EXECUTE_IF("vio_peer_addr_error",
{
peer_rc= 1;
}
);
DBUG_EXECUTE_IF("vio_peer_addr_fake_ipv4",
{
struct sockaddr *sa= (sockaddr *) &net->vio->remote;
sa->sa_family= AF_INET;
struct in_addr *ip4= &((struct sockaddr_in *)sa)->sin_addr;
/* See RFC 5737, 192.0.2.0/23 is reserved */
struct in_addr *ip4= &((struct sockaddr_in *) sa)->sin_addr;
/* See RFC 5737, 192.0.2.0/24 is reserved. */
const char* fake= "192.0.2.4";
ip4->s_addr= inet_addr(fake);
strcpy(ip, fake);
};);
/* END : DEBUG */
peer_rc= 0;
}
);
#ifdef HAVE_IPV6
DBUG_EXECUTE_IF("vio_peer_addr_fake_ipv6",
{
struct sockaddr_in6 *sa= (sockaddr_in6 *) &net->vio->remote;
sa->sin6_family= AF_INET6;
struct in6_addr *ip6= & sa->sin6_addr;
/* See RFC 3849, ipv6 2001:DB8::/32 is reserved. */
const char* fake= "2001:db8::6:6";
/* inet_pton(AF_INET6, fake, ip6); not available on Windows XP. */
ip6->s6_addr[ 0] = 0x20;
ip6->s6_addr[ 1] = 0x01;
ip6->s6_addr[ 2] = 0x0d;
ip6->s6_addr[ 3] = 0xb8;
ip6->s6_addr[ 4] = 0x00;
ip6->s6_addr[ 5] = 0x00;
ip6->s6_addr[ 6] = 0x00;
ip6->s6_addr[ 7] = 0x00;
ip6->s6_addr[ 8] = 0x00;
ip6->s6_addr[ 9] = 0x00;
ip6->s6_addr[10] = 0x00;
ip6->s6_addr[11] = 0x00;
ip6->s6_addr[12] = 0x00;
ip6->s6_addr[13] = 0x06;
ip6->s6_addr[14] = 0x00;
ip6->s6_addr[15] = 0x06;
strcpy(ip, fake);
peer_rc= 0;
}
);
#endif /* HAVE_IPV6 */
/*
===========================================================================
DEBUG code only (end)
===========================================================================
*/
if (peer_rc)
{
/*
Since we can not even get the peer IP address,
there is nothing to show in the host_cache,
so increment the global status variable for peer address errors.
*/
statistic_increment(connection_errors_peer_addr, &LOCK_status);
my_error(ER_BAD_HOST_ERROR, MYF(0));
return 1;
}
if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(MY_WME))))
{
/*
No error accounting per IP in host_cache,
this is treated as a global server OOM error.
TODO: remove the need for my_strdup.
*/
statistic_increment(connection_errors_internal, &LOCK_status);
return 1; /* The error is set by my_strdup(). */
}
thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip;
if (!(specialflag & SPECIAL_NO_RESOLVE))
{
if (ip_to_hostname(&net->vio->remote, thd->main_security_ctx.ip,
&thd->main_security_ctx.host, &connect_errors))
{
my_error(ER_BAD_HOST_ERROR, MYF(0));
return 1;
}
int rc;
rc= ip_to_hostname(&net->vio->remote,
thd->main_security_ctx.ip,
&thd->main_security_ctx.host,
&connect_errors);
/* Cut very long hostnames to avoid possible overflows */
if (thd->main_security_ctx.host)
......@@ -938,8 +1019,10 @@ static int check_connection(THD *thd)
HOSTNAME_LENGTH)]= 0;
thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
}
if (connect_errors > max_connect_errors)
if (rc == RC_BLOCKED_HOST)
{
/* HOST_CACHE stats updated by ip_to_hostname(). */
my_error(ER_HOST_IS_BLOCKED, MYF(0), thd->main_security_ctx.host_or_ip);
return 1;
}
......@@ -951,6 +1034,7 @@ static int check_connection(THD *thd)
thd->main_security_ctx.ip : "unknown ip")));
if (acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip))
{
/* HOST_CACHE stats updated by acl_check_host(). */
my_error(ER_HOST_NOT_PRIVILEGED, MYF(0),
thd->main_security_ctx.host_or_ip);
return 1;
......@@ -967,9 +1051,34 @@ static int check_connection(THD *thd)
vio_keepalive(net->vio, TRUE);
if (thd->packet.alloc(thd->variables.net_buffer_length))
{
/*
Important note:
net_buffer_length is a SESSION variable,
so it may be tempting to account OOM conditions per IP in the HOST_CACHE,
in case some clients are more demanding than others ...
However, this session variable is *not* initialized with a per client
value during the initial connection, it is initialized from the
GLOBAL net_buffer_length variable from the server.
Hence, there is no reason to account on OOM conditions per client IP,
we count failures in the global server status instead.
*/
statistic_increment(connection_errors_internal, &LOCK_status);
return 1; /* The error is set by alloc(). */
}
auth_rc= acl_authenticate(thd, connect_errors, 0);
if (auth_rc == 0 && connect_errors != 0)
{
/*
A client connection from this IP was successful,
after some previous failures.
Reset the connection error counter.
*/
reset_host_connect_errors(thd->main_security_ctx.ip);
}
return acl_authenticate(thd, connect_errors, 0);
return auth_rc;
}
......@@ -1118,6 +1227,7 @@ void prepare_new_connection_state(THD* thd)
execute_init_command(thd, &opt_init_connect, &LOCK_sys_init_connect);
if (thd->is_error())
{
Host_errors errors;
thd->killed= KILL_CONNECTION;
thd->print_aborted_warning(0, "init_connect command failed");
sql_print_warning("%s", thd->get_stmt_da()->message());
......@@ -1145,6 +1255,8 @@ void prepare_new_connection_state(THD* thd)
thd->server_status&= ~SERVER_STATUS_CLEAR_SET;
thd->protocol->end_statement();
thd->killed = KILL_CONNECTION;
errors.m_init_connect= 1;
inc_host_errors(thd->main_security_ctx.ip, &errors);
return;
}
......
......@@ -1148,7 +1148,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#endif
case COM_CHANGE_USER:
{
bool rc;
int auth_rc;
status_var_increment(thd->status_var.com_other);
thd->change_user();
......@@ -1179,13 +1179,13 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (thd->failed_com_change_user >= 3)
{
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
rc= 1;
auth_rc= 1;
}
else
rc= acl_authenticate(thd, 0, packet_length);
auth_rc= acl_authenticate(thd, 0, packet_length);
MYSQL_AUDIT_NOTIFY_CONNECTION_CHANGE_USER(thd);
if (rc)
if (auth_rc)
{
/* Free user if allocated by acl_authenticate */
my_free(thd->security_ctx->user);
......
......@@ -48,6 +48,7 @@
// mysql_user_table_is_in_short_password_format
#include "derror.h" // read_texts
#include "sql_base.h" // close_cached_tables
#include "hostname.h" // host_cache_size
#include <myisam.h>
#include "log_slow.h"
#include "debug_sync.h" // DEBUG_SYNC
......@@ -3773,6 +3774,22 @@ static Sys_var_tz Sys_time_zone(
SESSION_VAR(time_zone), NO_CMD_LINE,
DEFAULT(&default_tz), NO_MUTEX_GUARD, IN_BINLOG);
static bool fix_host_cache_size(sys_var *, THD *, enum_var_type)
{
hostname_cache_resize((uint) host_cache_size);
return false;
}
static Sys_var_ulong Sys_host_cache_size(
"host_cache_size",
"How many host names should be cached to avoid resolving.",
GLOBAL_VAR(host_cache_size),
CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 65536),
DEFAULT(HOST_CACHE_SIZE),
BLOCK_SIZE(1),
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL),
ON_UPDATE(fix_host_cache_size));
static Sys_var_charptr Sys_ignore_db_dirs(
"ignore_db_dirs",
"Specifies a directory to add to the ignore list when collecting "
......
......@@ -105,9 +105,7 @@ static PFS_engine_table_share *all_shares[]=
&table_file_instances::m_share,
&table_file_summary_by_event_name::m_share,
&table_file_summary_by_instance::m_share,
#ifdef QQ_NOT_YET
&table_host_cache::m_share,
#endif
&table_mutex_instances::m_share,
&table_os_global_by_type::m_share,
&table_performance_timers::m_share,
......@@ -164,7 +162,7 @@ void PFS_engine_table_share::check_all_tables(THD *thd)
DBUG_EXECUTE_IF("tampered_perfschema_table1",
{
/* Hack SETUP_INSTRUMENT, incompatible change. */
all_shares[19]->m_field_def->count++;
all_shares[20]->m_field_def->count++;
});
for (current= &all_shares[0]; (*current) != NULL; current++)
......@@ -1412,11 +1410,7 @@ bool pfs_show_status(handlerton *hton, THD *thd,
- no host_cache.memory
*/
name= "host_cache.size";
#ifdef NOT_YET_IMPLEMENTED
size= sizeof(Host_entry);
#else
size= 0;
#endif
break;
/*
......
......@@ -23,8 +23,6 @@
#include "table_host_cache.h"
#include "hostname.h"
#ifdef NOT_YET_PORTED
THR_LOCK table_host_cache::m_table_lock;
static const TABLE_FIELD_TYPE field_types[]=
......@@ -478,4 +476,3 @@ int table_host_cache::read_row_values(TABLE *table,
return 0;
}
#endif /* NOT_YET_PORTED */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment