MDEV-4058
MySQL 5.6.10 performance schema: merge of host_cache table
This commit is contained in:
parent
68262ba648
commit
ecbf36a914
@ -34,6 +34,27 @@
|
|||||||
return values of the plugin authenticate_user() method.
|
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
|
Authentication failed. Additionally, all other CR_xxx values
|
||||||
(libmysql error code) can be used too.
|
(libmysql error code) can be used too.
|
||||||
|
@ -497,10 +497,9 @@ SET @cmd="CREATE TABLE performance_schema.host_cache("
|
|||||||
")ENGINE=PERFORMANCE_SCHEMA;";
|
")ENGINE=PERFORMANCE_SCHEMA;";
|
||||||
|
|
||||||
SET @str = IF(@have_pfs = 1, @cmd, 'SET @dummy = 0');
|
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;
|
||||||
#PREPARE stmt FROM @str;
|
EXECUTE stmt;
|
||||||
#EXECUTE stmt;
|
DROP PREPARE stmt;
|
||||||
#DROP PREPARE stmt;
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- TABLE MUTEX_INSTANCES
|
-- TABLE MUTEX_INSTANCES
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* 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
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -47,8 +47,11 @@ private:
|
|||||||
|
|
||||||
class hash_filo
|
class hash_filo
|
||||||
{
|
{
|
||||||
const uint size, key_offset, key_length;
|
private:
|
||||||
|
const uint key_offset, key_length;
|
||||||
const my_hash_get_key get_key;
|
const my_hash_get_key get_key;
|
||||||
|
/** Size of this hash table. */
|
||||||
|
uint m_size;
|
||||||
my_hash_free_key free_element;
|
my_hash_free_key free_element;
|
||||||
bool init;
|
bool init;
|
||||||
CHARSET_INFO *hash_charset;
|
CHARSET_INFO *hash_charset;
|
||||||
@ -61,9 +64,12 @@ public:
|
|||||||
hash_filo(uint size_arg, uint key_offset_arg , uint key_length_arg,
|
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,
|
my_hash_get_key get_key_arg, my_hash_free_key free_element_arg,
|
||||||
CHARSET_INFO *hash_charset_arg)
|
CHARSET_INFO *hash_charset_arg)
|
||||||
:size(size_arg), key_offset(key_offset_arg), key_length(key_length_arg),
|
:key_offset(key_offset_arg), key_length(key_length_arg),
|
||||||
get_key(get_key_arg), free_element(free_element_arg),init(0),
|
get_key(get_key_arg), m_size(size_arg),
|
||||||
hash_charset(hash_charset_arg)
|
free_element(free_element_arg),init(0),
|
||||||
|
hash_charset(hash_charset_arg),
|
||||||
|
first_link(NULL),
|
||||||
|
last_link(NULL)
|
||||||
{
|
{
|
||||||
bzero((char*) &cache,sizeof(cache));
|
bzero((char*) &cache,sizeof(cache));
|
||||||
}
|
}
|
||||||
@ -86,32 +92,61 @@ public:
|
|||||||
}
|
}
|
||||||
if (!locked)
|
if (!locked)
|
||||||
mysql_mutex_lock(&lock);
|
mysql_mutex_lock(&lock);
|
||||||
|
first_link= NULL;
|
||||||
|
last_link= NULL;
|
||||||
(void) my_hash_free(&cache);
|
(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);
|
key_length, get_key, free_element,0);
|
||||||
if (!locked)
|
if (!locked)
|
||||||
mysql_mutex_unlock(&lock);
|
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)
|
hash_filo_element *search(uchar* key, size_t length)
|
||||||
{
|
{
|
||||||
|
mysql_mutex_assert_owner(&lock);
|
||||||
|
|
||||||
hash_filo_element *entry=(hash_filo_element*)
|
hash_filo_element *entry=(hash_filo_element*)
|
||||||
my_hash_search(&cache,(uchar*) key,length);
|
my_hash_search(&cache,(uchar*) key,length);
|
||||||
if (entry)
|
if (entry)
|
||||||
{ // Found; link it first
|
{ // Found; link it first
|
||||||
|
DBUG_ASSERT(first_link != NULL);
|
||||||
|
DBUG_ASSERT(last_link != NULL);
|
||||||
if (entry != first_link)
|
if (entry != first_link)
|
||||||
{ // Relink used-chain
|
{ // Relink used-chain
|
||||||
if (entry == last_link)
|
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
|
else
|
||||||
{
|
{
|
||||||
|
DBUG_ASSERT(entry->next_used != NULL);
|
||||||
|
DBUG_ASSERT(entry->prev_used != NULL);
|
||||||
entry->next_used->prev_used = entry->prev_used;
|
entry->next_used->prev_used = entry->prev_used;
|
||||||
entry->prev_used->next_used = entry->next_used;
|
entry->prev_used->next_used = entry->next_used;
|
||||||
}
|
}
|
||||||
if ((entry->next_used= first_link))
|
entry->prev_used= NULL;
|
||||||
first_link->prev_used=entry;
|
entry->next_used= first_link;
|
||||||
first_link=entry;
|
|
||||||
|
first_link->prev_used= entry;
|
||||||
|
first_link=entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return entry;
|
return entry;
|
||||||
@ -119,10 +154,20 @@ public:
|
|||||||
|
|
||||||
bool add(hash_filo_element *entry)
|
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;
|
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);
|
my_hash_delete(&cache,(uchar*) tmp);
|
||||||
}
|
}
|
||||||
if (my_hash_insert(&cache,(uchar*) entry))
|
if (my_hash_insert(&cache,(uchar*) entry))
|
||||||
@ -131,13 +176,27 @@ public:
|
|||||||
(*free_element)(entry); // This should never happen
|
(*free_element)(entry); // This should never happen
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if ((entry->next_used=first_link))
|
entry->prev_used= NULL;
|
||||||
first_link->prev_used=entry;
|
entry->next_used= first_link;
|
||||||
|
if (first_link != NULL)
|
||||||
|
first_link->prev_used= entry;
|
||||||
else
|
else
|
||||||
last_link=entry;
|
last_link= entry;
|
||||||
first_link=entry;
|
first_link= entry;
|
||||||
|
|
||||||
return 0;
|
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
|
#endif
|
||||||
|
741
sql/hostname.cc
741
sql/hostname.cc
@ -24,7 +24,6 @@
|
|||||||
Hostnames are checked with reverse name lookup and checked that they
|
Hostnames are checked with reverse name lookup and checked that they
|
||||||
doesn't resemble an IP address.
|
doesn't resemble an IP address.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "sql_priv.h"
|
#include "sql_priv.h"
|
||||||
#include "hostname.h"
|
#include "hostname.h"
|
||||||
#include "my_global.h"
|
#include "my_global.h"
|
||||||
@ -50,54 +49,101 @@ extern "C" { // Because of SCO 3.2V4.2
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
Host_errors::Host_errors()
|
||||||
HOST_ENTRY_KEY_SIZE -- size of IP address string in the hash cache.
|
: 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)
|
||||||
|
{}
|
||||||
|
|
||||||
#define HOST_ENTRY_KEY_SIZE INET6_ADDRSTRLEN
|
Host_errors::~Host_errors()
|
||||||
|
{}
|
||||||
|
|
||||||
/**
|
void Host_errors::reset()
|
||||||
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
|
|
||||||
{
|
{
|
||||||
public:
|
m_connect= 0;
|
||||||
/**
|
m_host_blocked= 0;
|
||||||
Client IP address. This is the key used with the hash table.
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
The client IP address is always expressed in IPv6, even when the
|
void Host_errors::aggregate(const Host_errors *errors)
|
||||||
network IPv6 stack is not present.
|
{
|
||||||
|
m_connect+= errors->m_connect;
|
||||||
This IP address is never used to connect to a socket.
|
m_host_blocked+= errors->m_host_blocked;
|
||||||
*/
|
m_nameinfo_transient+= errors->m_nameinfo_transient;
|
||||||
char ip_key[HOST_ENTRY_KEY_SIZE];
|
m_nameinfo_permanent+= errors->m_nameinfo_permanent;
|
||||||
|
m_format+= errors->m_format;
|
||||||
/**
|
m_addrinfo_transient+= errors->m_addrinfo_transient;
|
||||||
Number of errors during handshake phase from the IP address.
|
m_addrinfo_permanent+= errors->m_addrinfo_permanent;
|
||||||
*/
|
m_FCrDNS+= errors->m_FCrDNS;
|
||||||
uint connect_errors;
|
m_host_acl+= errors->m_host_acl;
|
||||||
|
m_no_auth_plugin+= errors->m_no_auth_plugin;
|
||||||
/**
|
m_auth_plugin+= errors->m_auth_plugin;
|
||||||
One of the host names for the IP address. May be NULL.
|
m_handshake+= errors->m_handshake;
|
||||||
*/
|
m_proxy_user+= errors->m_proxy_user;
|
||||||
const char *hostname;
|
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;
|
static hash_filo *hostname_cache;
|
||||||
|
ulong host_cache_size;
|
||||||
|
|
||||||
void hostname_cache_refresh()
|
void hostname_cache_refresh()
|
||||||
{
|
{
|
||||||
hostname_cache->clear();
|
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()
|
bool hostname_cache_init()
|
||||||
{
|
{
|
||||||
Host_entry tmp;
|
Host_entry tmp;
|
||||||
@ -120,6 +166,16 @@ void hostname_cache_free()
|
|||||||
hostname_cache= NULL;
|
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,
|
static void prepare_hostname_cache_key(const char *ip_string,
|
||||||
char *ip_key)
|
char *ip_key)
|
||||||
{
|
{
|
||||||
@ -130,65 +186,141 @@ static void prepare_hostname_cache_key(const char *ip_string,
|
|||||||
memcpy(ip_key, ip_string, ip_string_length);
|
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)
|
static inline Host_entry *hostname_cache_search(const char *ip_key)
|
||||||
{
|
{
|
||||||
return (Host_entry *) hostname_cache->search((uchar *) ip_key, 0);
|
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))
|
Host_entry *entry;
|
||||||
return FALSE;
|
bool need_add= false;
|
||||||
|
|
||||||
size_t hostname_size= hostname ? strlen(hostname) + 1 : 0;
|
entry= hostname_cache_search(ip_key);
|
||||||
|
|
||||||
Host_entry *entry= (Host_entry *) malloc(sizeof (Host_entry) + hostname_size);
|
if (likely(entry == NULL))
|
||||||
|
|
||||||
if (!entry)
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
char *hostname_copy;
|
|
||||||
|
|
||||||
memcpy(&entry->ip_key, ip_key, HOST_ENTRY_KEY_SIZE);
|
|
||||||
|
|
||||||
if (hostname_size)
|
|
||||||
{
|
{
|
||||||
hostname_copy= (char *) (entry + 1);
|
entry= (Host_entry *) malloc(sizeof (Host_entry));
|
||||||
memcpy(hostname_copy, hostname, hostname_size);
|
if (entry == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
DBUG_PRINT("info", ("Adding '%s' -> '%s' to the hostname cache...'",
|
need_add= true;
|
||||||
(const char *) ip_key,
|
memcpy(&entry->ip_key, ip_key, HOST_ENTRY_KEY_SIZE);
|
||||||
(const char *) hostname_copy));
|
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
|
else
|
||||||
{
|
{
|
||||||
hostname_copy= NULL;
|
entry->m_last_seen= now;
|
||||||
|
|
||||||
DBUG_PRINT("info", ("Adding '%s' -> NULL to the hostname cache...'",
|
|
||||||
(const char *) ip_key));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entry->hostname= hostname_copy;
|
if (validated)
|
||||||
entry->connect_errors= 0;
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
return hostname_cache->add(entry);
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors->has_error())
|
||||||
|
entry->set_error_timestamps(now);
|
||||||
|
|
||||||
|
entry->m_errors.aggregate(errors);
|
||||||
|
|
||||||
|
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)
|
if (specialflag & SPECIAL_NO_HOST_CACHE)
|
||||||
return FALSE;
|
return;
|
||||||
|
|
||||||
|
ulonglong now= my_time();
|
||||||
|
|
||||||
mysql_mutex_lock(&hostname_cache->lock);
|
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);
|
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);
|
||||||
|
|
||||||
|
mysql_mutex_lock(&hostname_cache->lock);
|
||||||
|
|
||||||
|
Host_entry *entry= hostname_cache_search(ip_key);
|
||||||
|
|
||||||
|
if (entry)
|
||||||
|
{
|
||||||
|
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_connect_errors(const char *ip_string)
|
||||||
{
|
{
|
||||||
if (!ip_string)
|
if (!ip_string)
|
||||||
return;
|
return;
|
||||||
@ -201,31 +333,11 @@ void inc_host_errors(const char *ip_string)
|
|||||||
Host_entry *entry= hostname_cache_search(ip_key);
|
Host_entry *entry= hostname_cache_search(ip_key);
|
||||||
|
|
||||||
if (entry)
|
if (entry)
|
||||||
entry->connect_errors++;
|
entry->m_errors.clear_connect_errors();
|
||||||
|
|
||||||
mysql_mutex_unlock(&hostname_cache->lock);
|
mysql_mutex_unlock(&hostname_cache->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void reset_host_errors(const char *ip_string)
|
|
||||||
{
|
|
||||||
if (!ip_string)
|
|
||||||
return;
|
|
||||||
|
|
||||||
char ip_key[HOST_ENTRY_KEY_SIZE];
|
|
||||||
prepare_hostname_cache_key(ip_string, ip_key);
|
|
||||||
|
|
||||||
mysql_mutex_lock(&hostname_cache->lock);
|
|
||||||
|
|
||||||
Host_entry *entry= hostname_cache_search(ip_key);
|
|
||||||
|
|
||||||
if (entry)
|
|
||||||
entry->connect_errors= 0;
|
|
||||||
|
|
||||||
mysql_mutex_unlock(&hostname_cache->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static inline bool is_ip_loopback(const struct sockaddr *ip)
|
static inline bool is_ip_loopback(const struct sockaddr *ip)
|
||||||
{
|
{
|
||||||
switch (ip->sa_family) {
|
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;
|
- returns host name if IP-address is validated;
|
||||||
- set value to out-variable connect_errors -- this variable represents the
|
- set value to out-variable connect_errors -- this variable represents the
|
||||||
number of connection errors from the specified IP-address.
|
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
|
NOTE: connect_errors are counted (are supported) only for the clients
|
||||||
where IP-address can be resolved and FCrDNS check is passed.
|
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
|
@param [out] connect_errors
|
||||||
|
|
||||||
@return Error status
|
@return Error status
|
||||||
@retval FALSE Success
|
@retval 0 Success
|
||||||
@retval TRUE Error
|
@retval RC_BLOCKED_HOST The host is blocked.
|
||||||
|
|
||||||
The function does not set/report MySQL server error in case of failure.
|
The function does not set/report MySQL server error in case of failure.
|
||||||
It's caller's responsibility to handle failures of this function
|
It's caller's responsibility to handle failures of this function
|
||||||
properly.
|
properly.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool ip_to_hostname(struct sockaddr_storage *ip_storage,
|
int ip_to_hostname(struct sockaddr_storage *ip_storage,
|
||||||
const char *ip_string,
|
const char *ip_string,
|
||||||
char **hostname, uint *connect_errors)
|
char **hostname,
|
||||||
|
uint *connect_errors)
|
||||||
{
|
{
|
||||||
const struct sockaddr *ip= (const sockaddr *) ip_storage;
|
const struct sockaddr *ip= (const sockaddr *) ip_storage;
|
||||||
int err_code;
|
int err_code;
|
||||||
bool err_status;
|
bool err_status;
|
||||||
|
Host_errors errors;
|
||||||
|
|
||||||
DBUG_ENTER("ip_to_hostname");
|
DBUG_ENTER("ip_to_hostname");
|
||||||
DBUG_PRINT("info", ("IP address: '%s'; family: %d.",
|
DBUG_PRINT("info", ("IP address: '%s'; family: %d.",
|
||||||
(const char *) ip_string,
|
(const char *) ip_string,
|
||||||
(int) ip->sa_family));
|
(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). */
|
/* Check if we have loopback address (127.0.0.1 or ::1). */
|
||||||
|
|
||||||
if (is_ip_loopback(ip))
|
if (is_ip_loopback(ip))
|
||||||
{
|
{
|
||||||
DBUG_PRINT("info", ("Loopback address detected."));
|
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;
|
*hostname= (char *) my_localhost;
|
||||||
|
|
||||||
DBUG_RETURN(FALSE);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prepare host name cache key. */
|
/* Prepare host name cache key. */
|
||||||
@ -329,27 +448,45 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
|
|||||||
|
|
||||||
if (!(specialflag & SPECIAL_NO_HOST_CACHE))
|
if (!(specialflag & SPECIAL_NO_HOST_CACHE))
|
||||||
{
|
{
|
||||||
|
ulonglong now= my_time();
|
||||||
|
|
||||||
mysql_mutex_lock(&hostname_cache->lock);
|
mysql_mutex_lock(&hostname_cache->lock);
|
||||||
|
|
||||||
Host_entry *entry= hostname_cache_search(ip_key);
|
Host_entry *entry= hostname_cache_search(ip_key);
|
||||||
|
|
||||||
if (entry)
|
if (entry)
|
||||||
{
|
{
|
||||||
*connect_errors= entry->connect_errors;
|
entry->m_last_seen= now;
|
||||||
*hostname= NULL;
|
|
||||||
|
|
||||||
if (entry->hostname)
|
if (entry->m_errors.m_connect > max_connect_errors)
|
||||||
*hostname= my_strdup(entry->hostname, MYF(0));
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
DBUG_PRINT("info",("IP (%s) has been found in the cache. "
|
/*
|
||||||
"Hostname: '%s'; connect_errors: %d",
|
If there is an IP -> HOSTNAME association in the cache,
|
||||||
(const char *) ip_key,
|
but for a hostname that was not validated,
|
||||||
(const char *) (*hostname? *hostname : "null"),
|
do not return that hostname: perform the network validation again.
|
||||||
(int) *connect_errors));
|
*/
|
||||||
|
if (entry->m_host_validated)
|
||||||
|
{
|
||||||
|
if (entry->m_hostname_length)
|
||||||
|
*hostname= my_strdup(entry->m_hostname, MYF(0));
|
||||||
|
|
||||||
mysql_mutex_unlock(&hostname_cache->lock);
|
DBUG_PRINT("info",("IP (%s) has been found in the cache. "
|
||||||
|
"Hostname: '%s'",
|
||||||
|
(const char *) ip_key,
|
||||||
|
(const char *) (*hostname? *hostname : "null")
|
||||||
|
));
|
||||||
|
|
||||||
DBUG_RETURN(FALSE);
|
mysql_mutex_unlock(&hostname_cache->lock);
|
||||||
|
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mysql_mutex_unlock(&hostname_cache->lock);
|
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,
|
err_code= vio_getnameinfo(ip, hostname_buffer, NI_MAXHOST, NULL, 0,
|
||||||
NI_NAMEREQD);
|
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");
|
strcpy(hostname_buffer, "santa.claus.ipv4.example.com");
|
||||||
err_code= 0;
|
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)
|
if (err_code)
|
||||||
{
|
{
|
||||||
@ -387,23 +571,29 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
|
|||||||
(const char *) ip_key,
|
(const char *) ip_key,
|
||||||
(const char *) gai_strerror(err_code));
|
(const char *) gai_strerror(err_code));
|
||||||
|
|
||||||
|
bool validated;
|
||||||
if (vio_is_no_name_error(err_code))
|
if (vio_is_no_name_error(err_code))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
The no-name error means that there is no reverse address mapping
|
The no-name error means that there is no reverse address mapping
|
||||||
for the IP address. A host name can not be resolved.
|
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
|
If it is not the no-name error, we should not cache the hostname
|
||||||
(or rather its absence), because the failure might be transient.
|
(or rather its absence), because the failure might be transient.
|
||||||
|
Only the ip error statistics are cached.
|
||||||
*/
|
*/
|
||||||
|
errors.m_nameinfo_transient= 1;
|
||||||
add_hostname(ip_key, NULL);
|
validated= false;
|
||||||
|
|
||||||
*hostname= NULL;
|
|
||||||
*connect_errors= 0; /* New IP added to the cache. */
|
|
||||||
}
|
}
|
||||||
|
add_hostname(ip_key, NULL, validated, &errors);
|
||||||
|
|
||||||
DBUG_RETURN(FALSE);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
DBUG_PRINT("info", ("IP '%s' resolved to '%s'.",
|
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 *) ip_key,
|
||||||
(const char *) hostname_buffer);
|
(const char *) hostname_buffer);
|
||||||
|
|
||||||
err_status= add_hostname(ip_key, NULL);
|
errors.m_format= 1;
|
||||||
|
add_hostname(ip_key, hostname_buffer, false, &errors);
|
||||||
|
|
||||||
*hostname= NULL;
|
DBUG_RETURN(false);
|
||||||
*connect_errors= 0; /* New IP added to the cache. */
|
|
||||||
|
|
||||||
DBUG_RETURN(err_status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
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). */
|
/* Get IP-addresses for the resolved host name (FCrDNS technique). */
|
||||||
|
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
struct addrinfo *addr_info_list;
|
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));
|
memset(&hints, 0, sizeof (struct addrinfo));
|
||||||
hints.ai_flags= AI_PASSIVE;
|
hints.ai_flags= AI_PASSIVE;
|
||||||
@ -470,8 +657,72 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
|
|||||||
if (err_code == 0)
|
if (err_code == 0)
|
||||||
free_addr_info_list= true;
|
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)
|
if (free_addr_info_list)
|
||||||
freeaddrinfo(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];
|
addr_info_list= & debug_addr_info[0];
|
||||||
err_code= 0;
|
err_code= 0;
|
||||||
free_addr_info_list= false;
|
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)
|
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];
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
/*
|
sql_print_warning("Host name '%s' could not be resolved: %s",
|
||||||
Don't cache responses when the DNS server is down, as otherwise
|
(const char *) hostname_buffer,
|
||||||
transient DNS failure may leave any number of clients (those
|
(const char *) gai_strerror(err_code));
|
||||||
that attempted to connect during the outage) unable to connect
|
|
||||||
indefinitely.
|
|
||||||
*/
|
|
||||||
|
|
||||||
err_status= add_hostname(ip_key, NULL);
|
bool validated;
|
||||||
|
|
||||||
*hostname= NULL;
|
if (err_code == EAI_NONAME)
|
||||||
*connect_errors= 0; /* New IP added to the cache. */
|
{
|
||||||
|
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(err_status);
|
DBUG_RETURN(false);
|
||||||
}
|
|
||||||
else if (err_code)
|
|
||||||
{
|
|
||||||
DBUG_PRINT("error", ("getaddrinfo() failed with error code %d.", err_code));
|
|
||||||
DBUG_RETURN(TRUE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check that getaddrinfo() returned the used IP (FCrDNS technique). */
|
/* 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));
|
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. */
|
/* 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)
|
if (free_addr_info_list)
|
||||||
freeaddrinfo(addr_info_list);
|
freeaddrinfo(addr_info_list);
|
||||||
DBUG_RETURN(TRUE);
|
DBUG_RETURN(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -568,9 +975,11 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
|
|||||||
|
|
||||||
if (!*hostname)
|
if (!*hostname)
|
||||||
{
|
{
|
||||||
sql_print_information("Hostname '%s' does not resolve to '%s'.",
|
errors.m_FCrDNS= 1;
|
||||||
(const char *) hostname_buffer,
|
|
||||||
(const char *) ip_key);
|
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:",
|
sql_print_information("Hostname '%s' has the following IP addresses:",
|
||||||
(const char *) hostname_buffer);
|
(const char *) hostname_buffer);
|
||||||
|
|
||||||
@ -584,30 +993,16 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
|
|||||||
ip_buffer, sizeof (ip_buffer));
|
ip_buffer, sizeof (ip_buffer));
|
||||||
DBUG_ASSERT(!err_status);
|
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)
|
if (free_addr_info_list)
|
||||||
freeaddrinfo(addr_info_list);
|
freeaddrinfo(addr_info_list);
|
||||||
|
|
||||||
/* Add an entry for the IP to the cache. */
|
DBUG_RETURN(false);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
166
sql/hostname.h
166
sql/hostname.h
@ -1,4 +1,4 @@
|
|||||||
/* 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
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -17,14 +17,168 @@
|
|||||||
#define HOSTNAME_INCLUDED
|
#define HOSTNAME_INCLUDED
|
||||||
|
|
||||||
#include "my_global.h" /* uint */
|
#include "my_global.h" /* uint */
|
||||||
|
#include "my_net.h"
|
||||||
|
#include "hash_filo.h"
|
||||||
|
|
||||||
bool ip_to_hostname(struct sockaddr_storage *ip_storage,
|
struct Host_errors
|
||||||
const char *ip_string,
|
{
|
||||||
char **hostname, uint *connect_errors);
|
public:
|
||||||
void inc_host_errors(const char *ip_string);
|
Host_errors();
|
||||||
void reset_host_errors(const char *ip_string);
|
~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();
|
bool hostname_cache_init();
|
||||||
void hostname_cache_free();
|
void hostname_cache_free();
|
||||||
void hostname_cache_refresh(void);
|
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 */
|
#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_additional_cond= "<IN COND>";
|
||||||
const char *in_having_cond= "<IN HAVING>";
|
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 */
|
/* classes for comparation parsing/processing */
|
||||||
Eq_creator eq_creator;
|
Eq_creator eq_creator;
|
||||||
Ne_creator ne_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()
|
static int init_common_variables()
|
||||||
{
|
{
|
||||||
umask(((~my_umask) & 0666));
|
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;
|
my_decimal_set_zero(&decimal_zero); // set decimal_zero constant;
|
||||||
|
|
||||||
if (pthread_key_create(&THR_THD,NULL) ||
|
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);
|
mysql_mutex_unlock(&LOCK_connection_count);
|
||||||
|
|
||||||
statistic_increment(aborted_connects,&LOCK_status);
|
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. */
|
/* Can't use my_error() since store_globals has not been called. */
|
||||||
my_snprintf(error_message_buff, sizeof(error_message_buff),
|
my_snprintf(error_message_buff, sizeof(error_message_buff),
|
||||||
ER_THD(thd, ER_CANT_CREATE_THREAD), error);
|
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);
|
close_connection(thd, ER_CON_COUNT_ERROR);
|
||||||
statistic_increment(denied_connections, &LOCK_status);
|
statistic_increment(denied_connections, &LOCK_status);
|
||||||
delete thd;
|
delete thd;
|
||||||
|
statistic_increment(connection_errors_max_connection, &LOCK_status);
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5819,6 +5840,12 @@ void handle_connections_sockets()
|
|||||||
{
|
{
|
||||||
if (socket_errno != SOCKET_EINTR)
|
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 */
|
if (!select_errors++ && !abort_loop) /* purecov: inspected */
|
||||||
sql_print_error("mysqld: Got error %d from select",socket_errno); /* purecov: inspected */
|
sql_print_error("mysqld: Got error %d from select",socket_errno); /* purecov: inspected */
|
||||||
}
|
}
|
||||||
@ -5899,6 +5926,12 @@ void handle_connections_sockets()
|
|||||||
#endif
|
#endif
|
||||||
if (mysql_socket_getfd(new_sock) == INVALID_SOCKET)
|
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
|
if ((error_count++ & 255) == 0) // This can happen often
|
||||||
sql_perror("Error in accept");
|
sql_perror("Error in accept");
|
||||||
MAYBE_BROKEN_SYSCALL;
|
MAYBE_BROKEN_SYSCALL;
|
||||||
@ -5938,6 +5971,11 @@ void handle_connections_sockets()
|
|||||||
|
|
||||||
(void) mysql_socket_shutdown(new_sock, SHUT_RDWR);
|
(void) mysql_socket_shutdown(new_sock, SHUT_RDWR);
|
||||||
(void) mysql_socket_close(new_sock);
|
(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;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5968,6 +6006,7 @@ void handle_connections_sockets()
|
|||||||
{
|
{
|
||||||
(void) mysql_socket_shutdown(new_sock, SHUT_RDWR);
|
(void) mysql_socket_shutdown(new_sock, SHUT_RDWR);
|
||||||
(void) mysql_socket_close(new_sock);
|
(void) mysql_socket_close(new_sock);
|
||||||
|
statistic_increment(connection_errors_internal, &LOCK_status);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/* Set to get io buffers to be part of THD */
|
/* Set to get io buffers to be part of THD */
|
||||||
@ -5996,6 +6035,7 @@ void handle_connections_sockets()
|
|||||||
}
|
}
|
||||||
delete thd;
|
delete thd;
|
||||||
set_current_thd(0);
|
set_current_thd(0);
|
||||||
|
statistic_increment(connection_errors_internal, &LOCK_status);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7363,6 +7403,12 @@ SHOW_VAR status_vars[]= {
|
|||||||
{"Com", (char*) com_status_vars, SHOW_ARRAY},
|
{"Com", (char*) com_status_vars, SHOW_ARRAY},
|
||||||
{"Compression", (char*) &show_net_compression, SHOW_SIMPLE_FUNC},
|
{"Compression", (char*) &show_net_compression, SHOW_SIMPLE_FUNC},
|
||||||
{"Connections", (char*) &thread_id, SHOW_LONG_NOFLUSH},
|
{"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},
|
{"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_disk_tables", (char*) offsetof(STATUS_VAR, created_tmp_disk_tables_), SHOW_LONG_STATUS},
|
||||||
{"Created_tmp_files", (char*) &my_tmp_file_created, SHOW_LONG},
|
{"Created_tmp_files", (char*) &my_tmp_file_created, SHOW_LONG},
|
||||||
|
@ -213,6 +213,12 @@ extern int bootstrap_error;
|
|||||||
extern I_List<THD> threads;
|
extern I_List<THD> threads;
|
||||||
extern char err_shared_dir[];
|
extern char err_shared_dir[];
|
||||||
extern TYPELIB thread_handling_typelib;
|
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;
|
extern ulong log_warnings;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1829,6 +1829,13 @@ bool acl_check_host(const char *host, const char *ip)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
mysql_mutex_unlock(&acl_cache->lock);
|
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
|
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);
|
DBUG_ASSERT(net->read_pos[pkt_len] == 0);
|
||||||
|
|
||||||
if (mpvio->connect_errors)
|
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);
|
ulong client_capabilities= uint2korr(net->read_pos);
|
||||||
if (client_capabilities & CLIENT_PROTOCOL_41)
|
if (client_capabilities & CLIENT_PROTOCOL_41)
|
||||||
@ -8731,7 +8738,6 @@ static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
|
|||||||
err:
|
err:
|
||||||
if (mpvio->status == MPVIO_EXT::FAILURE)
|
if (mpvio->status == MPVIO_EXT::FAILURE)
|
||||||
{
|
{
|
||||||
inc_host_errors(mpvio->thd->security_ctx->ip);
|
|
||||||
if (!mpvio->thd->is_error())
|
if (!mpvio->thd->is_error())
|
||||||
{
|
{
|
||||||
if (mpvio->make_it_fail)
|
if (mpvio->make_it_fail)
|
||||||
@ -8891,6 +8897,9 @@ static int do_auth_once(THD *thd, const LEX_STRING *auth_plugin_name,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Server cannot load the required plugin. */
|
/* 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);
|
my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth_plugin_name->str);
|
||||||
res= CR_ERROR;
|
res= CR_ERROR;
|
||||||
}
|
}
|
||||||
@ -9018,8 +9027,26 @@ bool acl_authenticate(THD *thd, uint connect_errors,
|
|||||||
|
|
||||||
if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
|
if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
|
||||||
{
|
{
|
||||||
|
Host_errors errors;
|
||||||
DBUG_ASSERT(mpvio.status == MPVIO_EXT::FAILURE);
|
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())
|
if (!thd->is_error())
|
||||||
login_failed_error(thd);
|
login_failed_error(thd);
|
||||||
DBUG_RETURN(1);
|
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);
|
create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
|
||||||
/* and send it to the client */
|
/* and send it to the client */
|
||||||
if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
|
if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
|
||||||
DBUG_RETURN(CR_ERROR);
|
DBUG_RETURN(CR_AUTH_HANDSHAKE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* reply and authenticate */
|
/* reply and authenticate */
|
||||||
@ -9249,7 +9276,7 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
|
|||||||
|
|
||||||
/* read the reply with the encrypted password */
|
/* read the reply with the encrypted password */
|
||||||
if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
|
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));
|
DBUG_PRINT("info", ("reply read : pkt_len=%d", pkt_len));
|
||||||
|
|
||||||
#ifdef NO_EMBEDDED_ACCESS_CHECKS
|
#ifdef NO_EMBEDDED_ACCESS_CHECKS
|
||||||
@ -9257,23 +9284,22 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (pkt_len == 0) /* no password */
|
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;
|
info->password_used= PASSWORD_USED_YES;
|
||||||
if (pkt_len == SCRAMBLE_LENGTH)
|
if (pkt_len == SCRAMBLE_LENGTH)
|
||||||
{
|
{
|
||||||
if (!mpvio->acl_user->salt_len)
|
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))
|
if (check_scramble(pkt, thd->scramble, mpvio->acl_user->salt))
|
||||||
DBUG_RETURN(CR_ERROR);
|
DBUG_RETURN(CR_AUTH_USER_CREDENTIALS);
|
||||||
else
|
else
|
||||||
DBUG_RETURN(CR_OK);
|
DBUG_RETURN(CR_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
inc_host_errors(mpvio->thd->security_ctx->ip);
|
|
||||||
my_error(ER_HANDSHAKE_ERROR, MYF(0));
|
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,
|
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);
|
create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
|
||||||
/* and send it to the client */
|
/* and send it to the client */
|
||||||
if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
|
if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
|
||||||
return CR_ERROR;
|
return CR_AUTH_HANDSHAKE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* read the reply and authenticate */
|
/* read the reply and authenticate */
|
||||||
if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
|
if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
|
||||||
return CR_ERROR;
|
return CR_AUTH_HANDSHAKE;
|
||||||
|
|
||||||
#ifdef NO_EMBEDDED_ACCESS_CHECKS
|
#ifdef NO_EMBEDDED_ACCESS_CHECKS
|
||||||
return CR_OK;
|
return CR_OK;
|
||||||
@ -9310,26 +9336,25 @@ static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
|
|||||||
pkt_len= strnlen((char*)pkt, pkt_len);
|
pkt_len= strnlen((char*)pkt, pkt_len);
|
||||||
|
|
||||||
if (pkt_len == 0) /* no password */
|
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))
|
if (secure_auth(thd))
|
||||||
return CR_ERROR;
|
return CR_AUTH_HANDSHAKE;
|
||||||
|
|
||||||
info->password_used= PASSWORD_USED_YES;
|
info->password_used= PASSWORD_USED_YES;
|
||||||
|
|
||||||
if (pkt_len == SCRAMBLE_LENGTH_323)
|
if (pkt_len == SCRAMBLE_LENGTH_323)
|
||||||
{
|
{
|
||||||
if (!mpvio->acl_user->salt_len)
|
if (!mpvio->acl_user->salt_len)
|
||||||
return CR_ERROR;
|
return CR_AUTH_USER_CREDENTIALS;
|
||||||
|
|
||||||
return check_scramble_323(pkt, thd->scramble,
|
return check_scramble_323(pkt, thd->scramble,
|
||||||
(ulong *) mpvio->acl_user->salt) ?
|
(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));
|
my_error(ER_HANDSHAKE_ERROR, MYF(0));
|
||||||
return CR_ERROR;
|
return CR_AUTH_HANDSHAKE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct st_mysql_auth native_password_handler=
|
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 check_for_max_user_connections(THD *thd, USER_CONN *uc)
|
||||||
{
|
{
|
||||||
int error= 1;
|
int error= 1;
|
||||||
|
Host_errors errors;
|
||||||
DBUG_ENTER("check_for_max_user_connections");
|
DBUG_ENTER("check_for_max_user_connections");
|
||||||
|
|
||||||
mysql_mutex_lock(&LOCK_user_conn);
|
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))
|
!(thd->security_ctx->master_access & SUPER_ACL))
|
||||||
{
|
{
|
||||||
my_error(ER_TOO_MANY_USER_CONNECTIONS, MYF(0), uc->user);
|
my_error(ER_TOO_MANY_USER_CONNECTIONS, MYF(0), uc->user);
|
||||||
|
error=1;
|
||||||
|
errors.m_max_user_connection= 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
time_out_user_resource_limits(thd, uc);
|
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,
|
my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user,
|
||||||
"max_user_connections",
|
"max_user_connections",
|
||||||
(long) uc->user_resources.user_conn);
|
(long) uc->user_resources.user_conn);
|
||||||
|
error= 1;
|
||||||
|
errors.m_max_user_connection= 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
if (uc->user_resources.conn_per_hour &&
|
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,
|
my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user,
|
||||||
"max_connections_per_hour",
|
"max_connections_per_hour",
|
||||||
(long) uc->user_resources.conn_per_hour);
|
(long) uc->user_resources.conn_per_hour);
|
||||||
|
error=1;
|
||||||
|
errors.m_max_user_connection_per_hour= 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
uc->conn_per_hour++;
|
uc->conn_per_hour++;
|
||||||
@ -169,6 +176,10 @@ end:
|
|||||||
thd->user_connect= NULL;
|
thd->user_connect= NULL;
|
||||||
}
|
}
|
||||||
mysql_mutex_unlock(&LOCK_user_conn);
|
mysql_mutex_unlock(&LOCK_user_conn);
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
inc_host_errors(thd->main_security_ctx.ip, &errors);
|
||||||
|
}
|
||||||
DBUG_RETURN(error);
|
DBUG_RETURN(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -867,7 +878,10 @@ bool init_new_connection_handler_thread()
|
|||||||
{
|
{
|
||||||
pthread_detach_this_thread();
|
pthread_detach_this_thread();
|
||||||
if (my_thread_init())
|
if (my_thread_init())
|
||||||
|
{
|
||||||
|
statistic_increment(connection_errors_internal, &LOCK_status);
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -887,6 +901,7 @@ bool init_new_connection_handler_thread()
|
|||||||
static int check_connection(THD *thd)
|
static int check_connection(THD *thd)
|
||||||
{
|
{
|
||||||
uint connect_errors= 0;
|
uint connect_errors= 0;
|
||||||
|
int auth_rc;
|
||||||
NET *net= &thd->net;
|
NET *net= &thd->net;
|
||||||
|
|
||||||
DBUG_PRINT("info",
|
DBUG_PRINT("info",
|
||||||
@ -898,37 +913,103 @@ static int check_connection(THD *thd)
|
|||||||
|
|
||||||
if (!thd->main_security_ctx.host) // If TCP/IP connection
|
if (!thd->main_security_ctx.host) // If TCP/IP connection
|
||||||
{
|
{
|
||||||
|
my_bool peer_rc;
|
||||||
char ip[NI_MAXHOST];
|
char ip[NI_MAXHOST];
|
||||||
|
|
||||||
if (vio_peer_addr(net->vio, ip, &thd->peer_port, NI_MAXHOST))
|
peer_rc= vio_peer_addr(net->vio, ip, &thd->peer_port, NI_MAXHOST);
|
||||||
{
|
|
||||||
my_error(ER_BAD_HOST_ERROR, MYF(0));
|
/*
|
||||||
return 1;
|
===========================================================================
|
||||||
}
|
DEBUG code only (begin)
|
||||||
/* BEGIN : DEBUG */
|
Simulate various output from vio_peer_addr().
|
||||||
DBUG_EXECUTE_IF("addr_fake_ipv4",
|
===========================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
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;
|
struct sockaddr *sa= (sockaddr *) &net->vio->remote;
|
||||||
sa->sa_family= AF_INET;
|
sa->sa_family= AF_INET;
|
||||||
struct in_addr *ip4= &((struct sockaddr_in *)sa)->sin_addr;
|
struct in_addr *ip4= &((struct sockaddr_in *) sa)->sin_addr;
|
||||||
/* See RFC 5737, 192.0.2.0/23 is reserved */
|
/* See RFC 5737, 192.0.2.0/24 is reserved. */
|
||||||
const char* fake= "192.0.2.4";
|
const char* fake= "192.0.2.4";
|
||||||
ip4->s_addr= inet_addr(fake);
|
ip4->s_addr= inet_addr(fake);
|
||||||
strcpy(ip, fake);
|
strcpy(ip, fake);
|
||||||
};);
|
peer_rc= 0;
|
||||||
/* END : DEBUG */
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#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))))
|
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(). */
|
return 1; /* The error is set by my_strdup(). */
|
||||||
|
}
|
||||||
thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip;
|
thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip;
|
||||||
if (!(specialflag & SPECIAL_NO_RESOLVE))
|
if (!(specialflag & SPECIAL_NO_RESOLVE))
|
||||||
{
|
{
|
||||||
if (ip_to_hostname(&net->vio->remote, thd->main_security_ctx.ip,
|
int rc;
|
||||||
&thd->main_security_ctx.host, &connect_errors))
|
|
||||||
{
|
rc= ip_to_hostname(&net->vio->remote,
|
||||||
my_error(ER_BAD_HOST_ERROR, MYF(0));
|
thd->main_security_ctx.ip,
|
||||||
return 1;
|
&thd->main_security_ctx.host,
|
||||||
}
|
&connect_errors);
|
||||||
|
|
||||||
/* Cut very long hostnames to avoid possible overflows */
|
/* Cut very long hostnames to avoid possible overflows */
|
||||||
if (thd->main_security_ctx.host)
|
if (thd->main_security_ctx.host)
|
||||||
@ -938,8 +1019,10 @@ static int check_connection(THD *thd)
|
|||||||
HOSTNAME_LENGTH)]= 0;
|
HOSTNAME_LENGTH)]= 0;
|
||||||
thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
|
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);
|
my_error(ER_HOST_IS_BLOCKED, MYF(0), thd->main_security_ctx.host_or_ip);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -951,6 +1034,7 @@ static int check_connection(THD *thd)
|
|||||||
thd->main_security_ctx.ip : "unknown ip")));
|
thd->main_security_ctx.ip : "unknown ip")));
|
||||||
if (acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.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),
|
my_error(ER_HOST_NOT_PRIVILEGED, MYF(0),
|
||||||
thd->main_security_ctx.host_or_ip);
|
thd->main_security_ctx.host_or_ip);
|
||||||
return 1;
|
return 1;
|
||||||
@ -967,9 +1051,34 @@ static int check_connection(THD *thd)
|
|||||||
vio_keepalive(net->vio, TRUE);
|
vio_keepalive(net->vio, TRUE);
|
||||||
|
|
||||||
if (thd->packet.alloc(thd->variables.net_buffer_length))
|
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(). */
|
return 1; /* The error is set by alloc(). */
|
||||||
|
}
|
||||||
|
|
||||||
return acl_authenticate(thd, connect_errors, 0);
|
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 auth_rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1118,6 +1227,7 @@ void prepare_new_connection_state(THD* thd)
|
|||||||
execute_init_command(thd, &opt_init_connect, &LOCK_sys_init_connect);
|
execute_init_command(thd, &opt_init_connect, &LOCK_sys_init_connect);
|
||||||
if (thd->is_error())
|
if (thd->is_error())
|
||||||
{
|
{
|
||||||
|
Host_errors errors;
|
||||||
thd->killed= KILL_CONNECTION;
|
thd->killed= KILL_CONNECTION;
|
||||||
thd->print_aborted_warning(0, "init_connect command failed");
|
thd->print_aborted_warning(0, "init_connect command failed");
|
||||||
sql_print_warning("%s", thd->get_stmt_da()->message());
|
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->server_status&= ~SERVER_STATUS_CLEAR_SET;
|
||||||
thd->protocol->end_statement();
|
thd->protocol->end_statement();
|
||||||
thd->killed = KILL_CONNECTION;
|
thd->killed = KILL_CONNECTION;
|
||||||
|
errors.m_init_connect= 1;
|
||||||
|
inc_host_errors(thd->main_security_ctx.ip, &errors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1148,7 +1148,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
|||||||
#endif
|
#endif
|
||||||
case COM_CHANGE_USER:
|
case COM_CHANGE_USER:
|
||||||
{
|
{
|
||||||
bool rc;
|
int auth_rc;
|
||||||
status_var_increment(thd->status_var.com_other);
|
status_var_increment(thd->status_var.com_other);
|
||||||
|
|
||||||
thd->change_user();
|
thd->change_user();
|
||||||
@ -1179,13 +1179,13 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
|||||||
if (thd->failed_com_change_user >= 3)
|
if (thd->failed_com_change_user >= 3)
|
||||||
{
|
{
|
||||||
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
|
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
|
||||||
rc= 1;
|
auth_rc= 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
rc= acl_authenticate(thd, 0, packet_length);
|
auth_rc= acl_authenticate(thd, 0, packet_length);
|
||||||
|
|
||||||
MYSQL_AUDIT_NOTIFY_CONNECTION_CHANGE_USER(thd);
|
MYSQL_AUDIT_NOTIFY_CONNECTION_CHANGE_USER(thd);
|
||||||
if (rc)
|
if (auth_rc)
|
||||||
{
|
{
|
||||||
/* Free user if allocated by acl_authenticate */
|
/* Free user if allocated by acl_authenticate */
|
||||||
my_free(thd->security_ctx->user);
|
my_free(thd->security_ctx->user);
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
// mysql_user_table_is_in_short_password_format
|
// mysql_user_table_is_in_short_password_format
|
||||||
#include "derror.h" // read_texts
|
#include "derror.h" // read_texts
|
||||||
#include "sql_base.h" // close_cached_tables
|
#include "sql_base.h" // close_cached_tables
|
||||||
|
#include "hostname.h" // host_cache_size
|
||||||
#include <myisam.h>
|
#include <myisam.h>
|
||||||
#include "log_slow.h"
|
#include "log_slow.h"
|
||||||
#include "debug_sync.h" // DEBUG_SYNC
|
#include "debug_sync.h" // DEBUG_SYNC
|
||||||
@ -3773,6 +3774,22 @@ static Sys_var_tz Sys_time_zone(
|
|||||||
SESSION_VAR(time_zone), NO_CMD_LINE,
|
SESSION_VAR(time_zone), NO_CMD_LINE,
|
||||||
DEFAULT(&default_tz), NO_MUTEX_GUARD, IN_BINLOG);
|
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(
|
static Sys_var_charptr Sys_ignore_db_dirs(
|
||||||
"ignore_db_dirs",
|
"ignore_db_dirs",
|
||||||
"Specifies a directory to add to the ignore list when collecting "
|
"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_instances::m_share,
|
||||||
&table_file_summary_by_event_name::m_share,
|
&table_file_summary_by_event_name::m_share,
|
||||||
&table_file_summary_by_instance::m_share,
|
&table_file_summary_by_instance::m_share,
|
||||||
#ifdef QQ_NOT_YET
|
|
||||||
&table_host_cache::m_share,
|
&table_host_cache::m_share,
|
||||||
#endif
|
|
||||||
&table_mutex_instances::m_share,
|
&table_mutex_instances::m_share,
|
||||||
&table_os_global_by_type::m_share,
|
&table_os_global_by_type::m_share,
|
||||||
&table_performance_timers::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",
|
DBUG_EXECUTE_IF("tampered_perfschema_table1",
|
||||||
{
|
{
|
||||||
/* Hack SETUP_INSTRUMENT, incompatible change. */
|
/* 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++)
|
for (current= &all_shares[0]; (*current) != NULL; current++)
|
||||||
@ -1412,11 +1410,7 @@ bool pfs_show_status(handlerton *hton, THD *thd,
|
|||||||
- no host_cache.memory
|
- no host_cache.memory
|
||||||
*/
|
*/
|
||||||
name= "host_cache.size";
|
name= "host_cache.size";
|
||||||
#ifdef NOT_YET_IMPLEMENTED
|
|
||||||
size= sizeof(Host_entry);
|
size= sizeof(Host_entry);
|
||||||
#else
|
|
||||||
size= 0;
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -23,8 +23,6 @@
|
|||||||
#include "table_host_cache.h"
|
#include "table_host_cache.h"
|
||||||
#include "hostname.h"
|
#include "hostname.h"
|
||||||
|
|
||||||
#ifdef NOT_YET_PORTED
|
|
||||||
|
|
||||||
THR_LOCK table_host_cache::m_table_lock;
|
THR_LOCK table_host_cache::m_table_lock;
|
||||||
|
|
||||||
static const TABLE_FIELD_TYPE field_types[]=
|
static const TABLE_FIELD_TYPE field_types[]=
|
||||||
@ -478,4 +476,3 @@ int table_host_cache::read_row_values(TABLE *table,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* NOT_YET_PORTED */
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user