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
......@@ -24,7 +24,6 @@
Hostnames are checked with reverse name lookup and checked that they
doesn't resemble an IP address.
*/
#include "sql_priv.h"
#include "hostname.h"
#include "my_global.h"
......@@ -50,54 +49,101 @@ extern "C" { // Because of SCO 3.2V4.2
}
#endif
/*
HOST_ENTRY_KEY_SIZE -- 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 connect errors from IP.
Host name can be NULL (that means DNS look up failed), but connect errors
still are counted.
*/
class Host_entry :public hash_filo_element
Host_errors::Host_errors()
: m_connect(0),
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)
{}
Host_errors::~Host_errors()
{}
void Host_errors::reset()
{
public:
/**
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];
/**
Number of errors during handshake phase from the IP address.
*/
uint connect_errors;
m_connect= 0;
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;
}
/**
One of the host names for the IP address. May be NULL.
*/
const char *hostname;
};
void Host_errors::aggregate(const Host_errors *errors)
{
m_connect+= errors->m_connect;
m_host_blocked+= errors->m_host_blocked;
m_nameinfo_transient+= errors->m_nameinfo_transient;
m_nameinfo_permanent+= errors->m_nameinfo_permanent;
m_format+= errors->m_format;
m_addrinfo_transient+= errors->m_addrinfo_transient;
m_addrinfo_permanent+= errors->m_addrinfo_permanent;
m_FCrDNS+= errors->m_FCrDNS;
m_host_acl+= errors->m_host_acl;
m_no_auth_plugin+= errors->m_no_auth_plugin;
m_auth_plugin+= errors->m_auth_plugin;
m_handshake+= errors->m_handshake;
m_proxy_user+= errors->m_proxy_user;
m_proxy_user_acl+= errors->m_proxy_user_acl;
m_authentication+= errors->m_authentication;
m_ssl+= errors->m_ssl;
m_max_user_connection+= errors->m_max_user_connection;
m_max_user_connection_per_hour+= errors->m_max_user_connection_per_hour;
m_default_database+= errors->m_default_database;
m_init_connect+= errors->m_init_connect;
m_local+= errors->m_local;
}
static hash_filo *hostname_cache;
ulong host_cache_size;
void hostname_cache_refresh()
{
hostname_cache->clear();
}
uint hostname_cache_size()
{
return hostname_cache->size();
}
void hostname_cache_resize(uint size)
{
hostname_cache->resize(size);
}
bool hostname_cache_init()
{
Host_entry tmp;
......@@ -120,6 +166,16 @@ void hostname_cache_free()
hostname_cache= NULL;
}
void hostname_cache_lock()
{
mysql_mutex_lock(&hostname_cache->lock);
}
void hostname_cache_unlock()
{
mysql_mutex_unlock(&hostname_cache->lock);
}
static void prepare_hostname_cache_key(const char *ip_string,
char *ip_key)
{
......@@ -130,69 +186,119 @@ static void prepare_hostname_cache_key(const char *ip_string,
memcpy(ip_key, ip_string, ip_string_length);
}
Host_entry *hostname_cache_first()
{ return (Host_entry *) hostname_cache->first(); }
static inline Host_entry *hostname_cache_search(const char *ip_key)
{
return (Host_entry *) hostname_cache->search((uchar *) ip_key, 0);
}
static bool add_hostname_impl(const char *ip_key, const char *hostname)
static void add_hostname_impl(const char *ip_key, const char *hostname,
bool validated, Host_errors *errors,
ulonglong now)
{
if (hostname_cache_search(ip_key))
return FALSE;
size_t hostname_size= hostname ? strlen(hostname) + 1 : 0;
Host_entry *entry= (Host_entry *) malloc(sizeof (Host_entry) + hostname_size);
if (!entry)
return TRUE;
char *hostname_copy;
Host_entry *entry;
bool need_add= false;
memcpy(&entry->ip_key, ip_key, HOST_ENTRY_KEY_SIZE);
entry= hostname_cache_search(ip_key);
if (hostname_size)
if (likely(entry == NULL))
{
hostname_copy= (char *) (entry + 1);
memcpy(hostname_copy, hostname, hostname_size);
DBUG_PRINT("info", ("Adding '%s' -> '%s' to the hostname cache...'",
(const char *) ip_key,
(const char *) hostname_copy));
entry= (Host_entry *) malloc(sizeof (Host_entry));
if (entry == NULL)
return;
need_add= true;
memcpy(&entry->ip_key, ip_key, HOST_ENTRY_KEY_SIZE);
entry->m_errors.reset();
entry->m_hostname_length= 0;
entry->m_host_validated= false;
entry->m_first_seen= now;
entry->m_last_seen= now;
entry->m_first_error_seen= 0;
entry->m_last_error_seen= 0;
}
else
{
hostname_copy= NULL;
entry->m_last_seen= now;
}
DBUG_PRINT("info", ("Adding '%s' -> NULL to the hostname cache...'",
(const char *) ip_key));
if (validated)
{
if (hostname != NULL)
{
uint len= strlen(hostname);
if (len > sizeof(entry->m_hostname) - 1)
len= sizeof(entry->m_hostname) - 1;
memcpy(entry->m_hostname, hostname, len);
entry->m_hostname[len]= '\0';
entry->m_hostname_length= len;
DBUG_PRINT("info",
("Adding/Updating '%s' -> '%s' (validated) to the hostname cache...'",
(const char *) ip_key,
(const char *) entry->m_hostname));
}
else
{
entry->m_hostname_length= 0;
DBUG_PRINT("info",
("Adding/Updating '%s' -> NULL (validated) to the hostname cache...'",
(const char *) ip_key));
}
entry->m_host_validated= true;
/*
New errors that are considered 'blocking',
that will eventually cause the IP to be black listed and blocked.
*/
errors->sum_connect_errors();
}
else
{
entry->m_hostname_length= 0;
entry->m_host_validated= false;
/* Do not count new blocking errors during DNS failures. */
errors->clear_connect_errors();
DBUG_PRINT("info",
("Adding/Updating '%s' -> NULL (not validated) to the hostname cache...'",
(const char *) ip_key));
}
entry->hostname= hostname_copy;
entry->connect_errors= 0;
if (errors->has_error())
entry->set_error_timestamps(now);
entry->m_errors.aggregate(errors);
return hostname_cache->add(entry);
if (need_add)
hostname_cache->add(entry);
return;
}
static bool add_hostname(const char *ip_key, const char *hostname)
static void add_hostname(const char *ip_key, const char *hostname,
bool validated, Host_errors *errors)
{
if (specialflag & SPECIAL_NO_HOST_CACHE)
return FALSE;
return;
ulonglong now= my_time();
mysql_mutex_lock(&hostname_cache->lock);
bool err_status= add_hostname_impl(ip_key, hostname);
add_hostname_impl(ip_key, hostname, validated, errors, now);
mysql_mutex_unlock(&hostname_cache->lock);
return err_status;
return;
}
void inc_host_errors(const char *ip_string)
void inc_host_errors(const char *ip_string, Host_errors *errors)
{
if (!ip_string)
return;
ulonglong now= my_time();
char ip_key[HOST_ENTRY_KEY_SIZE];
prepare_hostname_cache_key(ip_string, ip_key);
......@@ -201,13 +307,20 @@ void inc_host_errors(const char *ip_string)
Host_entry *entry= hostname_cache_search(ip_key);
if (entry)
entry->connect_errors++;
{
if (entry->m_host_validated)
errors->sum_connect_errors();
else
errors->clear_connect_errors();
entry->m_errors.aggregate(errors);
entry->set_error_timestamps(now);
}
mysql_mutex_unlock(&hostname_cache->lock);
}
void reset_host_errors(const char *ip_string)
void reset_host_connect_errors(const char *ip_string)
{
if (!ip_string)
return;
......@@ -220,12 +333,11 @@ void reset_host_errors(const char *ip_string)
Host_entry *entry= hostname_cache_search(ip_key);
if (entry)
entry->connect_errors= 0;
entry->m_errors.clear_connect_errors();
mysql_mutex_unlock(&hostname_cache->lock);
}
static inline bool is_ip_loopback(const struct sockaddr *ip)
{
switch (ip->sa_family) {
......@@ -277,6 +389,7 @@ static inline bool is_hostname_valid(const char *hostname)
- returns host name if IP-address is validated;
- set value to out-variable connect_errors -- this variable represents the
number of connection errors from the specified IP-address.
- update the host_cache statistics
NOTE: connect_errors are counted (are supported) only for the clients
where IP-address can be resolved and FCrDNS check is passed.
......@@ -287,37 +400,43 @@ static inline bool is_hostname_valid(const char *hostname)
@param [out] connect_errors
@return Error status
@retval FALSE Success
@retval TRUE Error
@retval 0 Success
@retval RC_BLOCKED_HOST The host is blocked.
The function does not set/report MySQL server error in case of failure.
It's caller's responsibility to handle failures of this function
properly.
*/
bool ip_to_hostname(struct sockaddr_storage *ip_storage,
const char *ip_string,
char **hostname, uint *connect_errors)
int ip_to_hostname(struct sockaddr_storage *ip_storage,
const char *ip_string,
char **hostname,
uint *connect_errors)
{
const struct sockaddr *ip= (const sockaddr *) ip_storage;
int err_code;
bool err_status;
Host_errors errors;
DBUG_ENTER("ip_to_hostname");
DBUG_PRINT("info", ("IP address: '%s'; family: %d.",
(const char *) ip_string,
(int) ip->sa_family));
/* Default output values, for most cases. */
*hostname= NULL;
*connect_errors= 0;
/* Check if we have loopback address (127.0.0.1 or ::1). */
if (is_ip_loopback(ip))
{
DBUG_PRINT("info", ("Loopback address detected."));
*connect_errors= 0; /* Do not count connect errors from localhost. */
/* Do not count connect errors from localhost. */
*hostname= (char *) my_localhost;
DBUG_RETURN(FALSE);
DBUG_RETURN(0);
}
/* Prepare host name cache key. */
......@@ -329,27 +448,45 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
if (!(specialflag & SPECIAL_NO_HOST_CACHE))
{
ulonglong now= my_time();
mysql_mutex_lock(&hostname_cache->lock);
Host_entry *entry= hostname_cache_search(ip_key);
if (entry)
{
*connect_errors= entry->connect_errors;
*hostname= NULL;
entry->m_last_seen= now;
if (entry->m_errors.m_connect > max_connect_errors)
{
entry->m_errors.m_host_blocked++;
entry->set_error_timestamps(now);
*connect_errors= entry->m_errors.m_connect;
mysql_mutex_unlock(&hostname_cache->lock);
DBUG_RETURN(RC_BLOCKED_HOST);
}
if (entry->hostname)
*hostname= my_strdup(entry->hostname, MYF(0));
/*
If there is an IP -> HOSTNAME association in the cache,
but for a hostname that was not validated,
do not return that hostname: perform the network validation again.
*/
if (entry->m_host_validated)
{
if (entry->m_hostname_length)
*hostname= my_strdup(entry->m_hostname, MYF(0));
DBUG_PRINT("info",("IP (%s) has been found in the cache. "
"Hostname: '%s'; connect_errors: %d",
(const char *) ip_key,
(const char *) (*hostname? *hostname : "null"),
(int) *connect_errors));
DBUG_PRINT("info",("IP (%s) has been found in the cache. "
"Hostname: '%s'",
(const char *) ip_key,
(const char *) (*hostname? *hostname : "null")
));
mysql_mutex_unlock(&hostname_cache->lock);
mysql_mutex_unlock(&hostname_cache->lock);
DBUG_RETURN(FALSE);
DBUG_RETURN(0);
}
}
mysql_mutex_unlock(&hostname_cache->lock);
......@@ -367,13 +504,60 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
err_code= vio_getnameinfo(ip, hostname_buffer, NI_MAXHOST, NULL, 0,
NI_NAMEREQD);
/* BEGIN : DEBUG */
DBUG_EXECUTE_IF("addr_fake_ipv4",
/*
===========================================================================
DEBUG code only (begin)
Simulate various output from vio_getnameinfo().
===========================================================================
*/
DBUG_EXECUTE_IF("getnameinfo_error_noname",
{
strcpy(hostname_buffer, "<garbage>");
err_code= EAI_NONAME;
}
);
DBUG_EXECUTE_IF("getnameinfo_error_again",
{
strcpy(hostname_buffer, "<garbage>");
err_code= EAI_AGAIN;
}
);
DBUG_EXECUTE_IF("getnameinfo_fake_ipv4",
{
strcpy(hostname_buffer, "santa.claus.ipv4.example.com");
err_code= 0;
};);
/* END : DEBUG */
}
);
DBUG_EXECUTE_IF("getnameinfo_fake_ipv6",
{
strcpy(hostname_buffer, "santa.claus.ipv6.example.com");
err_code= 0;
}
);
DBUG_EXECUTE_IF("getnameinfo_format_ipv4",
{
strcpy(hostname_buffer, "12.12.12.12");
err_code= 0;
}
);
DBUG_EXECUTE_IF("getnameinfo_format_ipv6",
{
strcpy(hostname_buffer, "12:DEAD:BEEF:0");
err_code= 0;
}
);
/*
===========================================================================
DEBUG code only (end)
===========================================================================
*/
if (err_code)
{
......@@ -387,23 +571,29 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
(const char *) ip_key,
(const char *) gai_strerror(err_code));
bool validated;
if (vio_is_no_name_error(err_code))
{
/*
The no-name error means that there is no reverse address mapping
for the IP address. A host name can not be resolved.
*/
errors.m_nameinfo_permanent= 1;
validated= true;
}
else
{
/*
If it is not the no-name error, we should not cache the hostname
(or rather its absence), because the failure might be transient.
Only the ip error statistics are cached.
*/
add_hostname(ip_key, NULL);
*hostname= NULL;
*connect_errors= 0; /* New IP added to the cache. */
errors.m_nameinfo_transient= 1;
validated= false;
}
add_hostname(ip_key, NULL, validated, &errors);
DBUG_RETURN(FALSE);
DBUG_RETURN(0);
}
DBUG_PRINT("info", ("IP '%s' resolved to '%s'.",
......@@ -439,24 +629,21 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
(const char *) ip_key,
(const char *) hostname_buffer);
err_status= add_hostname(ip_key, NULL);
*hostname= NULL;
*connect_errors= 0; /* New IP added to the cache. */
errors.m_format= 1;
add_hostname(ip_key, hostname_buffer, false, &errors);
DBUG_RETURN(err_status);
DBUG_RETURN(false);
}
/*
To avoid crashing the server in DBUG_EXECUTE_IF,
Define a variable which depicts state of addr_info_list.
*/
bool free_addr_info_list= false;
/* Get IP-addresses for the resolved host name (FCrDNS technique). */
struct addrinfo hints;
struct addrinfo *addr_info_list;
/*
Makes fault injection with DBUG_EXECUTE_IF easier.
Invoking free_addr_info(NULL) crashes on some platforms.
*/
bool free_addr_info_list= false;
memset(&hints, 0, sizeof (struct addrinfo));
hints.ai_flags= AI_PASSIVE;
......@@ -470,8 +657,72 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
if (err_code == 0)
free_addr_info_list= true;
/* BEGIN : DEBUG */
DBUG_EXECUTE_IF("addr_fake_ipv4",
/*
===========================================================================
DEBUG code only (begin)
Simulate various output from getaddrinfo().
===========================================================================
*/
DBUG_EXECUTE_IF("getaddrinfo_error_noname",
{
if (free_addr_info_list)
freeaddrinfo(addr_info_list);
addr_info_list= NULL;
err_code= EAI_NONAME;
free_addr_info_list= false;
}
);
DBUG_EXECUTE_IF("getaddrinfo_error_again",
{
if (free_addr_info_list)
freeaddrinfo(addr_info_list);
addr_info_list= NULL;
err_code= EAI_AGAIN;
free_addr_info_list= false;
}
);
DBUG_EXECUTE_IF("getaddrinfo_fake_bad_ipv4",
{
if (free_addr_info_list)
freeaddrinfo(addr_info_list);
struct sockaddr_in *debug_addr;
/*
Not thread safe, which is ok.
Only one connection at a time is tested with
fault injection.
*/
static struct sockaddr_in debug_sock_addr[2];
static struct addrinfo debug_addr_info[2];
/* Simulating ipv4 192.0.2.126 */
debug_addr= & debug_sock_addr[0];
debug_addr->sin_family= AF_INET;
debug_addr->sin_addr.s_addr= inet_addr("192.0.2.126");
/* Simulating ipv4 192.0.2.127 */
debug_addr= & debug_sock_addr[1];
debug_addr->sin_family= AF_INET;
debug_addr->sin_addr.s_addr= inet_addr("192.0.2.127");
debug_addr_info[0].ai_addr= (struct sockaddr*) & debug_sock_addr[0];
debug_addr_info[0].ai_addrlen= sizeof (struct sockaddr_in);
debug_addr_info[0].ai_next= & debug_addr_info[1];
debug_addr_info[1].ai_addr= (struct sockaddr*) & debug_sock_addr[1];
debug_addr_info[1].ai_addrlen= sizeof (struct sockaddr_in);
debug_addr_info[1].ai_next= NULL;
addr_info_list= & debug_addr_info[0];
err_code= 0;
free_addr_info_list= false;
}
);
DBUG_EXECUTE_IF("getaddrinfo_fake_good_ipv4",
{
if (free_addr_info_list)
freeaddrinfo(addr_info_list);
......@@ -500,30 +751,186 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
addr_info_list= & debug_addr_info[0];
err_code= 0;
free_addr_info_list= false;
};);
}
);
/* END : DEBUG */
#ifdef HAVE_IPV6
DBUG_EXECUTE_IF("getaddrinfo_fake_bad_ipv6",
{
if (free_addr_info_list)
freeaddrinfo(addr_info_list);
if (err_code == EAI_NONAME)
{
/*
Don't cache responses when the DNS server is down, as otherwise
transient DNS failure may leave any number of clients (those
that attempted to connect during the outage) unable to connect
indefinitely.
*/
struct sockaddr_in6 *debug_addr;
struct in6_addr *ip6;
/*
Not thread safe, which is ok.
Only one connection at a time is tested with
fault injection.
*/
static struct sockaddr_in6 debug_sock_addr[2];
static struct addrinfo debug_addr_info[2];
/* Simulating ipv6 2001:DB8::6:7E */
debug_addr= & debug_sock_addr[0];
debug_addr->sin6_family= AF_INET6;
ip6= & debug_addr->sin6_addr;
/* inet_pton 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] = 0x7e;
/* Simulating ipv6 2001:DB8::6:7F */
debug_addr= & debug_sock_addr[1];
debug_addr->sin6_family= AF_INET6;
ip6= & debug_addr->sin6_addr;
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] = 0x7f;
debug_addr_info[0].ai_addr= (struct sockaddr*) & debug_sock_addr[0];
debug_addr_info[0].ai_addrlen= sizeof (struct sockaddr_in6);
debug_addr_info[0].ai_next= & debug_addr_info[1];
err_status= add_hostname(ip_key, NULL);
debug_addr_info[1].ai_addr= (struct sockaddr*) & debug_sock_addr[1];
debug_addr_info[1].ai_addrlen= sizeof (struct sockaddr_in6);
debug_addr_info[1].ai_next= NULL;
*hostname= NULL;
*connect_errors= 0; /* New IP added to the cache. */
addr_info_list= & debug_addr_info[0];
err_code= 0;
free_addr_info_list= false;
}
);
DBUG_RETURN(err_status);
}
else if (err_code)
DBUG_EXECUTE_IF("getaddrinfo_fake_good_ipv6",
{
if (free_addr_info_list)
freeaddrinfo(addr_info_list);
struct sockaddr_in6 *debug_addr;
struct in6_addr *ip6;
/*
Not thread safe, which is ok.
Only one connection at a time is tested with
fault injection.
*/
static struct sockaddr_in6 debug_sock_addr[2];
static struct addrinfo debug_addr_info[2];
/* Simulating ipv6 2001:DB8::6:7 */
debug_addr= & debug_sock_addr[0];
debug_addr->sin6_family= AF_INET6;
ip6= & debug_addr->sin6_addr;
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] = 0x07;
/* Simulating ipv6 2001:DB8::6:6 */
debug_addr= & debug_sock_addr[1];
debug_addr->sin6_family= AF_INET6;
ip6= & debug_addr->sin6_addr;
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;
debug_addr_info[0].ai_addr= (struct sockaddr*) & debug_sock_addr[0];
debug_addr_info[0].ai_addrlen= sizeof (struct sockaddr_in6);
debug_addr_info[0].ai_next= & debug_addr_info[1];
debug_addr_info[1].ai_addr= (struct sockaddr*) & debug_sock_addr[1];
debug_addr_info[1].ai_addrlen= sizeof (struct sockaddr_in6);
debug_addr_info[1].ai_next= NULL;
addr_info_list= & debug_addr_info[0];
err_code= 0;
free_addr_info_list= false;
}
);
#endif /* HAVE_IPV6 */
/*
===========================================================================
DEBUG code only (end)
===========================================================================
*/
if (err_code != 0)
{
DBUG_PRINT("error", ("getaddrinfo() failed with error code %d.", err_code));
DBUG_RETURN(TRUE);
sql_print_warning("Host name '%s' could not be resolved: %s",
(const char *) hostname_buffer,
(const char *) gai_strerror(err_code));
bool validated;
if (err_code == EAI_NONAME)
{
errors.m_addrinfo_permanent= 1;
validated= true;
}
else
{
/*
Don't cache responses when the DNS server is down, as otherwise
transient DNS failure may leave any number of clients (those
that attempted to connect during the outage) unable to connect
indefinitely.
Only cache error statistics.
*/
errors.m_addrinfo_transient= 1;
validated= false;
}
add_hostname(ip_key, NULL, validated, &errors);
DBUG_RETURN(false);
}
/* Check that getaddrinfo() returned the used IP (FCrDNS technique). */
......@@ -545,7 +952,7 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
DBUG_PRINT("info", (" - '%s'", (const char *) ip_buffer));
if (strcmp(ip_key, ip_buffer) == 0)
if (strcasecmp(ip_key, ip_buffer) == 0)
{
/* Copy host name string to be stored in the cache. */
......@@ -557,7 +964,7 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
if (free_addr_info_list)
freeaddrinfo(addr_info_list);
DBUG_RETURN(TRUE);
DBUG_RETURN(true);
}
break;
......@@ -568,9 +975,11 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
if (!*hostname)
{
sql_print_information("Hostname '%s' does not resolve to '%s'.",
(const char *) hostname_buffer,
(const char *) ip_key);
errors.m_FCrDNS= 1;
sql_print_warning("Hostname '%s' does not resolve to '%s'.",
(const char *) hostname_buffer,
(const char *) ip_key);
sql_print_information("Hostname '%s' has the following IP addresses:",
(const char *) hostname_buffer);
......@@ -584,30 +993,16 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
ip_buffer, sizeof (ip_buffer));
DBUG_ASSERT(!err_status);
sql_print_information(" - %s\n", (const char *) ip_buffer);
sql_print_information(" - %s", (const char *) ip_buffer);
}
}
/* Free the result of getaddrinfo(). */
/* Add an entry for the IP to the cache. */
add_hostname(ip_key, *hostname, true, &errors);
/* Free the result of getaddrinfo(). */
if (free_addr_info_list)
freeaddrinfo(addr_info_list);
/* Add an entry for the IP to the cache. */
if (*hostname)
{
err_status= add_hostname(ip_key, *hostname);
*connect_errors= 0;
}
else
{
DBUG_PRINT("error",("Couldn't verify hostname with getaddrinfo()."));
err_status= add_hostname(ip_key, NULL);
*hostname= NULL;
*connect_errors= 0;
}
DBUG_RETURN(err_status);
DBUG_RETURN(false);
}
/* 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