|
|
|
@ -18,10 +18,10 @@ |
|
|
|
@file |
|
|
|
|
|
|
|
@brief |
|
|
|
Get hostname for an IP. |
|
|
|
Get hostname for an IP address. |
|
|
|
|
|
|
|
Hostnames are checked with reverse name lookup and |
|
|
|
checked that they doesn't resemble an ip. |
|
|
|
Hostnames are checked with reverse name lookup and checked that they |
|
|
|
doesn't resemble an IP address. |
|
|
|
*/ |
|
|
|
|
|
|
|
#include "mysql_priv.h"
|
|
|
|
@ -34,24 +34,54 @@ extern "C" { // Because of SCO 3.2V4.2 |
|
|
|
#ifdef HAVE_SYS_UN_H
|
|
|
|
#include <sys/un.h>
|
|
|
|
#endif
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <sys/utsname.h>
|
|
|
|
#endif // __WIN__
|
|
|
|
#ifdef __cplusplus
|
|
|
|
} |
|
|
|
#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. |
|
|
|
|
|
|
|
class host_entry :public hash_filo_element |
|
|
|
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: |
|
|
|
char ip[sizeof(((struct in_addr *) 0)->s_addr)]; |
|
|
|
uint errors; |
|
|
|
char *hostname; |
|
|
|
/**
|
|
|
|
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; |
|
|
|
|
|
|
|
/**
|
|
|
|
One of the host names for the IP address. May be NULL. |
|
|
|
*/ |
|
|
|
const char *hostname; |
|
|
|
}; |
|
|
|
|
|
|
|
static hash_filo *hostname_cache; |
|
|
|
static pthread_mutex_t LOCK_hostname; |
|
|
|
|
|
|
|
void hostname_cache_refresh() |
|
|
|
{ |
|
|
|
@ -60,219 +90,468 @@ void hostname_cache_refresh() |
|
|
|
|
|
|
|
bool hostname_cache_init() |
|
|
|
{ |
|
|
|
host_entry tmp; |
|
|
|
uint offset= (uint) ((char*) (&tmp.ip) - (char*) &tmp); |
|
|
|
if (!(hostname_cache=new hash_filo(HOST_CACHE_SIZE, offset, |
|
|
|
sizeof(struct in_addr),NULL, |
|
|
|
(my_hash_free_key) free, |
|
|
|
&my_charset_bin))) |
|
|
|
Host_entry tmp; |
|
|
|
uint key_offset= (uint) ((char*) (&tmp.ip_key) - (char*) &tmp); |
|
|
|
|
|
|
|
if (!(hostname_cache= new hash_filo(HOST_CACHE_SIZE, |
|
|
|
key_offset, HOST_ENTRY_KEY_SIZE, |
|
|
|
NULL, (my_hash_free_key) free, |
|
|
|
&my_charset_bin))) |
|
|
|
return 1; |
|
|
|
|
|
|
|
hostname_cache->clear(); |
|
|
|
(void) pthread_mutex_init(&LOCK_hostname,MY_MUTEX_INIT_SLOW); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
void hostname_cache_free() |
|
|
|
{ |
|
|
|
if (hostname_cache) |
|
|
|
{ |
|
|
|
(void) pthread_mutex_destroy(&LOCK_hostname); |
|
|
|
delete hostname_cache; |
|
|
|
hostname_cache= 0; |
|
|
|
} |
|
|
|
delete hostname_cache; |
|
|
|
hostname_cache= NULL; |
|
|
|
} |
|
|
|
|
|
|
|
static void prepare_hostname_cache_key(const char *ip_string, |
|
|
|
char *ip_key) |
|
|
|
{ |
|
|
|
int ip_string_length= strlen(ip_string); |
|
|
|
DBUG_ASSERT(ip_string_length < HOST_ENTRY_KEY_SIZE); |
|
|
|
|
|
|
|
static void add_hostname(struct in_addr *in,const char *name) |
|
|
|
memset(ip_key, 0, HOST_ENTRY_KEY_SIZE); |
|
|
|
memcpy_fixed(ip_key, ip_string, ip_string_length); |
|
|
|
} |
|
|
|
|
|
|
|
static inline Host_entry *hostname_cache_search(const char *ip_key) |
|
|
|
{ |
|
|
|
if (!(specialflag & SPECIAL_NO_HOST_CACHE)) |
|
|
|
return (Host_entry *) hostname_cache->search((uchar *) ip_key, 0); |
|
|
|
} |
|
|
|
|
|
|
|
static bool add_hostname_impl(const char *ip_key, const char *hostname) |
|
|
|
{ |
|
|
|
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; |
|
|
|
|
|
|
|
memcpy_fixed(&entry->ip_key, ip_key, HOST_ENTRY_KEY_SIZE); |
|
|
|
|
|
|
|
if (hostname_size) |
|
|
|
{ |
|
|
|
pthread_mutex_lock(&hostname_cache->lock); |
|
|
|
host_entry *entry; |
|
|
|
if (!(entry=(host_entry*) hostname_cache->search((uchar*) &in->s_addr,0))) |
|
|
|
{ |
|
|
|
uint length=name ? (uint) strlen(name) : 0; |
|
|
|
hostname_copy= (char *) (entry + 1); |
|
|
|
memcpy(hostname_copy, hostname, hostname_size); |
|
|
|
|
|
|
|
if ((entry=(host_entry*) malloc(sizeof(host_entry)+length+1))) |
|
|
|
{ |
|
|
|
char *new_name; |
|
|
|
memcpy_fixed(&entry->ip, &in->s_addr, sizeof(in->s_addr)); |
|
|
|
if (length) |
|
|
|
memcpy(new_name= (char *) (entry+1), name, length+1); |
|
|
|
else |
|
|
|
new_name=0; |
|
|
|
entry->hostname=new_name; |
|
|
|
entry->errors=0; |
|
|
|
(void) hostname_cache->add(entry); |
|
|
|
} |
|
|
|
} |
|
|
|
pthread_mutex_unlock(&hostname_cache->lock); |
|
|
|
DBUG_PRINT("info", ("Adding '%s' -> '%s' to the hostname cache...'", |
|
|
|
(const char *) ip_key, |
|
|
|
(const char *) hostname_copy)); |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
hostname_copy= NULL; |
|
|
|
|
|
|
|
DBUG_PRINT("info", ("Adding '%s' -> NULL to the hostname cache...'", |
|
|
|
(const char *) ip_key)); |
|
|
|
} |
|
|
|
|
|
|
|
entry->hostname= hostname_copy; |
|
|
|
entry->connect_errors= 0; |
|
|
|
|
|
|
|
return hostname_cache->add(entry); |
|
|
|
} |
|
|
|
|
|
|
|
inline void add_wrong_ip(struct in_addr *in) |
|
|
|
static bool add_hostname(const char *ip_key, const char *hostname) |
|
|
|
{ |
|
|
|
add_hostname(in,NullS); |
|
|
|
if (specialflag & SPECIAL_NO_HOST_CACHE) |
|
|
|
return FALSE; |
|
|
|
|
|
|
|
pthread_mutex_lock(&hostname_cache->lock); |
|
|
|
|
|
|
|
bool err_status= add_hostname_impl(ip_key, hostname); |
|
|
|
|
|
|
|
pthread_mutex_unlock(&hostname_cache->lock); |
|
|
|
|
|
|
|
return err_status; |
|
|
|
} |
|
|
|
|
|
|
|
void inc_host_errors(struct in_addr *in) |
|
|
|
void inc_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); |
|
|
|
|
|
|
|
pthread_mutex_lock(&hostname_cache->lock); |
|
|
|
host_entry *entry; |
|
|
|
if ((entry=(host_entry*) hostname_cache->search((uchar*) &in->s_addr,0))) |
|
|
|
entry->errors++; |
|
|
|
|
|
|
|
Host_entry *entry= hostname_cache_search(ip_key); |
|
|
|
|
|
|
|
if (entry) |
|
|
|
entry->connect_errors++; |
|
|
|
|
|
|
|
pthread_mutex_unlock(&hostname_cache->lock); |
|
|
|
} |
|
|
|
|
|
|
|
void reset_host_errors(struct in_addr *in) |
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
pthread_mutex_lock(&hostname_cache->lock); |
|
|
|
host_entry *entry; |
|
|
|
if ((entry=(host_entry*) hostname_cache->search((uchar*) &in->s_addr,0))) |
|
|
|
entry->errors=0; |
|
|
|
|
|
|
|
Host_entry *entry= hostname_cache_search(ip_key); |
|
|
|
|
|
|
|
if (entry) |
|
|
|
entry->connect_errors= 0; |
|
|
|
|
|
|
|
pthread_mutex_unlock(&hostname_cache->lock); |
|
|
|
} |
|
|
|
|
|
|
|
/* Deal with systems that don't defined INADDR_LOOPBACK */ |
|
|
|
#ifndef INADDR_LOOPBACK
|
|
|
|
#define INADDR_LOOPBACK 0x7f000001UL
|
|
|
|
#endif
|
|
|
|
|
|
|
|
char * ip_to_hostname(struct in_addr *in, uint *errors) |
|
|
|
static inline bool is_ip_loopback(const struct sockaddr *ip) |
|
|
|
{ |
|
|
|
switch (ip->sa_family) { |
|
|
|
case AF_INET: |
|
|
|
{ |
|
|
|
/* Check for IPv4 127.0.0.1. */ |
|
|
|
struct in_addr *ip4= &((struct sockaddr_in *) ip)->sin_addr; |
|
|
|
return ntohl(ip4->s_addr) == INADDR_LOOPBACK; |
|
|
|
} |
|
|
|
|
|
|
|
#ifdef HAVE_IPV6
|
|
|
|
case AF_INET6: |
|
|
|
{ |
|
|
|
/* Check for IPv6 ::1. */ |
|
|
|
struct in6_addr *ip6= &((struct sockaddr_in6 *) ip)->sin6_addr; |
|
|
|
return IN6_IS_ADDR_LOOPBACK(ip6); |
|
|
|
} |
|
|
|
#endif /* HAVE_IPV6 */
|
|
|
|
|
|
|
|
default: |
|
|
|
return FALSE; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static inline bool is_hostname_valid(const char *hostname) |
|
|
|
{ |
|
|
|
uint i; |
|
|
|
host_entry *entry; |
|
|
|
/*
|
|
|
|
A hostname is invalid if it starts with a number followed by a dot |
|
|
|
(IPv4 address). |
|
|
|
*/ |
|
|
|
|
|
|
|
if (!my_isdigit(&my_charset_latin1, hostname[0])) |
|
|
|
return TRUE; |
|
|
|
|
|
|
|
const char *p= hostname + 1; |
|
|
|
|
|
|
|
while (my_isdigit(&my_charset_latin1, *p)) |
|
|
|
++p; |
|
|
|
|
|
|
|
return *p != '.'; |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
Resolve IP-address to host name. |
|
|
|
|
|
|
|
This function does the following things: |
|
|
|
- resolves IP-address; |
|
|
|
- employs Forward Confirmed Reverse DNS technique to validate IP-address; |
|
|
|
- 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. |
|
|
|
|
|
|
|
NOTE: connect_errors are counted (are supported) only for the clients |
|
|
|
where IP-address can be resolved and FCrDNS check is passed. |
|
|
|
|
|
|
|
@param [in] ip_storage IP address (sockaddr). Must be set. |
|
|
|
@param [in] ip_string IP address (string). Must be set. |
|
|
|
@param [out] hostname |
|
|
|
@param [out] connect_errors |
|
|
|
|
|
|
|
@return Error status |
|
|
|
@retval FALSE Success |
|
|
|
@retval TRUE Error |
|
|
|
|
|
|
|
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) |
|
|
|
{ |
|
|
|
const struct sockaddr *ip= (const sockaddr *) ip_storage; |
|
|
|
int err_code; |
|
|
|
bool err_status; |
|
|
|
|
|
|
|
DBUG_ENTER("ip_to_hostname"); |
|
|
|
*errors=0; |
|
|
|
DBUG_PRINT("info", ("IP address: '%s'; family: %d.", |
|
|
|
(const char *) ip_string, |
|
|
|
(int) ip->sa_family)); |
|
|
|
|
|
|
|
/* We always treat the loopback address as "localhost". */ |
|
|
|
if (in->s_addr == htonl(INADDR_LOOPBACK)) // is expanded inline by gcc
|
|
|
|
DBUG_RETURN((char *)my_localhost); |
|
|
|
/* 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. */ |
|
|
|
*hostname= (char *) my_localhost; |
|
|
|
|
|
|
|
DBUG_RETURN(FALSE); |
|
|
|
} |
|
|
|
|
|
|
|
/* Prepare host name cache key. */ |
|
|
|
|
|
|
|
char ip_key[HOST_ENTRY_KEY_SIZE]; |
|
|
|
prepare_hostname_cache_key(ip_string, ip_key); |
|
|
|
|
|
|
|
/* Check first if we have host name in the cache. */ |
|
|
|
|
|
|
|
/* Check first if we have name in cache */ |
|
|
|
if (!(specialflag & SPECIAL_NO_HOST_CACHE)) |
|
|
|
{ |
|
|
|
pthread_mutex_lock(&hostname_cache->lock); |
|
|
|
if ((entry=(host_entry*) hostname_cache->search((uchar*) &in->s_addr,0))) |
|
|
|
|
|
|
|
Host_entry *entry= hostname_cache_search(ip_key); |
|
|
|
|
|
|
|
if (entry) |
|
|
|
{ |
|
|
|
char *name; |
|
|
|
if (!entry->hostname) |
|
|
|
name=0; // Don't allow connection
|
|
|
|
else |
|
|
|
name=my_strdup(entry->hostname,MYF(0)); |
|
|
|
*errors= entry->errors; |
|
|
|
*connect_errors= entry->connect_errors; |
|
|
|
*hostname= NULL; |
|
|
|
|
|
|
|
if (entry->hostname) |
|
|
|
*hostname= my_strdup(entry->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)); |
|
|
|
|
|
|
|
pthread_mutex_unlock(&hostname_cache->lock); |
|
|
|
DBUG_RETURN(name); |
|
|
|
|
|
|
|
DBUG_RETURN(FALSE); |
|
|
|
} |
|
|
|
|
|
|
|
pthread_mutex_unlock(&hostname_cache->lock); |
|
|
|
} |
|
|
|
|
|
|
|
struct hostent *hp, *check; |
|
|
|
char *name; |
|
|
|
LINT_INIT(check); |
|
|
|
#if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST)
|
|
|
|
char buff[GETHOSTBYADDR_BUFF_SIZE],buff2[GETHOSTBYNAME_BUFF_SIZE]; |
|
|
|
int tmp_errno; |
|
|
|
struct hostent tmp_hostent, tmp_hostent2; |
|
|
|
#ifdef HAVE_purify
|
|
|
|
bzero(buff,sizeof(buff)); // Bug in purify
|
|
|
|
#endif
|
|
|
|
if (!(hp=gethostbyaddr_r((char*) in,sizeof(*in), |
|
|
|
AF_INET, |
|
|
|
&tmp_hostent,buff,sizeof(buff),&tmp_errno))) |
|
|
|
{ |
|
|
|
DBUG_PRINT("error",("gethostbyaddr_r returned %d",tmp_errno)); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
if (!(check=my_gethostbyname_r(hp->h_name,&tmp_hostent2,buff2,sizeof(buff2), |
|
|
|
&tmp_errno))) |
|
|
|
/*
|
|
|
|
Resolve host name. Return an error if a host name can not be resolved |
|
|
|
(instead of returning the numeric form of the host name). |
|
|
|
*/ |
|
|
|
|
|
|
|
char hostname_buffer[NI_MAXHOST]; |
|
|
|
|
|
|
|
DBUG_PRINT("info", ("Resolving '%s'...", (const char *) ip_key)); |
|
|
|
|
|
|
|
err_code= vio_getnameinfo(ip, hostname_buffer, NI_MAXHOST, NULL, 0, |
|
|
|
NI_NAMEREQD); |
|
|
|
|
|
|
|
if (err_code == EAI_NONAME) |
|
|
|
{ |
|
|
|
DBUG_PRINT("error",("gethostbyname_r returned %d",tmp_errno)); |
|
|
|
/*
|
|
|
|
Don't cache responses when the DSN 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. |
|
|
|
There is no reverse address mapping for the IP address. A host name |
|
|
|
can not be resolved. |
|
|
|
*/ |
|
|
|
if (tmp_errno == HOST_NOT_FOUND || tmp_errno == NO_DATA) |
|
|
|
add_wrong_ip(in); |
|
|
|
my_gethostbyname_r_free(); |
|
|
|
DBUG_RETURN(0); |
|
|
|
} |
|
|
|
if (!hp->h_name[0]) |
|
|
|
{ |
|
|
|
DBUG_PRINT("error",("Got an empty hostname")); |
|
|
|
add_wrong_ip(in); |
|
|
|
my_gethostbyname_r_free(); |
|
|
|
DBUG_RETURN(0); // Don't allow empty hostnames
|
|
|
|
} |
|
|
|
if (!(name=my_strdup(hp->h_name,MYF(0)))) |
|
|
|
{ |
|
|
|
my_gethostbyname_r_free(); |
|
|
|
DBUG_RETURN(0); // out of memory
|
|
|
|
|
|
|
|
DBUG_PRINT("error", ("IP address '%s' could not be resolved: " |
|
|
|
"no reverse address mapping.", |
|
|
|
(const char *) ip_key)); |
|
|
|
|
|
|
|
sql_print_warning("IP address '%s' could not be resolved: " |
|
|
|
"no reverse address mapping.", |
|
|
|
(const char *) ip_key); |
|
|
|
|
|
|
|
err_status= add_hostname(ip_key, NULL); |
|
|
|
|
|
|
|
*hostname= NULL; |
|
|
|
*connect_errors= 0; /* New IP added to the cache. */ |
|
|
|
|
|
|
|
DBUG_RETURN(err_status); |
|
|
|
} |
|
|
|
my_gethostbyname_r_free(); |
|
|
|
#else
|
|
|
|
pthread_mutex_lock(&LOCK_hostname); |
|
|
|
if (!(hp=gethostbyaddr((char*) in,sizeof(*in), AF_INET))) |
|
|
|
else if (err_code) |
|
|
|
{ |
|
|
|
pthread_mutex_unlock(&LOCK_hostname); |
|
|
|
DBUG_PRINT("error",("gethostbyaddr returned %d",errno)); |
|
|
|
DBUG_PRINT("error", ("IP address '%s' could not be resolved: " |
|
|
|
"getnameinfo() returned %d.", |
|
|
|
(const char *) ip_key, |
|
|
|
(int) err_code)); |
|
|
|
|
|
|
|
sql_print_warning("IP address '%s' could not be resolved: " |
|
|
|
"getnameinfo() returned error (code: %d).", |
|
|
|
(const char *) ip_key, |
|
|
|
(int) err_code); |
|
|
|
|
|
|
|
if (errno == HOST_NOT_FOUND || errno == NO_DATA) |
|
|
|
goto add_wrong_ip_and_return; |
|
|
|
/* Failure, don't cache responce */ |
|
|
|
DBUG_RETURN(0); |
|
|
|
DBUG_RETURN(TRUE); |
|
|
|
} |
|
|
|
if (!hp->h_name[0]) // Don't allow empty hostnames
|
|
|
|
|
|
|
|
DBUG_PRINT("info", ("IP '%s' resolved to '%s'.", |
|
|
|
(const char *) ip_key, |
|
|
|
(const char *) hostname_buffer)); |
|
|
|
|
|
|
|
/*
|
|
|
|
Validate hostname: the server does not accept host names, which |
|
|
|
resemble IP addresses. |
|
|
|
|
|
|
|
The thing is that theoretically, a host name can be in a form of IPv4 |
|
|
|
address (123.example.org, or 1.2 or even 1.2.3.4). We have to deny such |
|
|
|
host names because ACL-systems is not designed to work with them. |
|
|
|
|
|
|
|
For example, it is possible to specify a host name mask (like |
|
|
|
192.168.1.%) for an ACL rule. Then, if IPv4-like hostnames are allowed, |
|
|
|
there is a security hole: instead of allowing access for |
|
|
|
192.168.1.0/255 network (which was assumed by the user), the access |
|
|
|
will be allowed for host names like 192.168.1.example.org. |
|
|
|
*/ |
|
|
|
|
|
|
|
if (!is_hostname_valid(hostname_buffer)) |
|
|
|
{ |
|
|
|
pthread_mutex_unlock(&LOCK_hostname); |
|
|
|
DBUG_PRINT("error",("Got an empty hostname")); |
|
|
|
goto add_wrong_ip_and_return; |
|
|
|
DBUG_PRINT("error", ("IP address '%s' has been resolved " |
|
|
|
"to the host name '%s', which resembles " |
|
|
|
"IPv4-address itself.", |
|
|
|
(const char *) ip_key, |
|
|
|
(const char *) hostname_buffer)); |
|
|
|
|
|
|
|
sql_print_warning("IP address '%s' has been resolved " |
|
|
|
"to the host name '%s', which resembles " |
|
|
|
"IPv4-address itself.", |
|
|
|
(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. */ |
|
|
|
|
|
|
|
DBUG_RETURN(err_status); |
|
|
|
} |
|
|
|
if (!(name=my_strdup(hp->h_name,MYF(0)))) |
|
|
|
|
|
|
|
/* Get IP-addresses for the resolved host name (FCrDNS technique). */ |
|
|
|
|
|
|
|
struct addrinfo hints; |
|
|
|
struct addrinfo *addr_info_list; |
|
|
|
|
|
|
|
memset(&hints, 0, sizeof (struct addrinfo)); |
|
|
|
hints.ai_flags= AI_PASSIVE; |
|
|
|
hints.ai_socktype= SOCK_STREAM; |
|
|
|
hints.ai_family= AF_UNSPEC; |
|
|
|
|
|
|
|
DBUG_PRINT("info", ("Getting IP addresses for hostname '%s'...", |
|
|
|
(const char *) hostname_buffer)); |
|
|
|
|
|
|
|
err_code= getaddrinfo(hostname_buffer, NULL, &hints, &addr_info_list); |
|
|
|
|
|
|
|
if (err_code == EAI_NONAME) |
|
|
|
{ |
|
|
|
pthread_mutex_unlock(&LOCK_hostname); |
|
|
|
DBUG_RETURN(0); // out of memory
|
|
|
|
/*
|
|
|
|
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. |
|
|
|
*/ |
|
|
|
|
|
|
|
err_status= add_hostname(ip_key, NULL); |
|
|
|
|
|
|
|
*hostname= NULL; |
|
|
|
*connect_errors= 0; /* New IP added to the cache. */ |
|
|
|
|
|
|
|
DBUG_RETURN(err_status); |
|
|
|
} |
|
|
|
check=gethostbyname(name); |
|
|
|
pthread_mutex_unlock(&LOCK_hostname); |
|
|
|
if (!check) |
|
|
|
else if (err_code) |
|
|
|
{ |
|
|
|
DBUG_PRINT("error",("gethostbyname returned %d",errno)); |
|
|
|
my_free(name,MYF(0)); |
|
|
|
DBUG_RETURN(0); |
|
|
|
DBUG_PRINT("error", ("getaddrinfo() failed with error code %d.", err_code)); |
|
|
|
DBUG_RETURN(TRUE); |
|
|
|
} |
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Don't accept hostnames that starts with digits because they may be
|
|
|
|
false ip:s */ |
|
|
|
if (my_isdigit(&my_charset_latin1,name[0])) |
|
|
|
/* Check that getaddrinfo() returned the used IP (FCrDNS technique). */ |
|
|
|
|
|
|
|
DBUG_PRINT("info", ("The following IP addresses found for '%s':", |
|
|
|
(const char *) hostname_buffer)); |
|
|
|
|
|
|
|
for (struct addrinfo *addr_info= addr_info_list; |
|
|
|
addr_info; addr_info= addr_info->ai_next) |
|
|
|
{ |
|
|
|
char *pos; |
|
|
|
for (pos= name+1 ; my_isdigit(&my_charset_latin1,*pos); pos++) ; |
|
|
|
if (*pos == '.') |
|
|
|
char ip_buffer[HOST_ENTRY_KEY_SIZE]; |
|
|
|
|
|
|
|
{ |
|
|
|
err_status= |
|
|
|
vio_get_normalized_ip_string(addr_info->ai_addr, addr_info->ai_addrlen, |
|
|
|
ip_buffer, sizeof (ip_buffer)); |
|
|
|
DBUG_ASSERT(!err_status); |
|
|
|
} |
|
|
|
|
|
|
|
DBUG_PRINT("info", (" - '%s'", (const char *) ip_buffer)); |
|
|
|
|
|
|
|
if (strcmp(ip_key, ip_buffer) == 0) |
|
|
|
{ |
|
|
|
DBUG_PRINT("error",("mysqld doesn't accept hostnames that starts with a number followed by a '.'")); |
|
|
|
my_free(name,MYF(0)); |
|
|
|
goto add_wrong_ip_and_return; |
|
|
|
/* Copy host name string to be stored in the cache. */ |
|
|
|
|
|
|
|
*hostname= my_strdup(hostname_buffer, MYF(0)); |
|
|
|
|
|
|
|
if (!*hostname) |
|
|
|
{ |
|
|
|
DBUG_PRINT("error", ("Out of memory.")); |
|
|
|
|
|
|
|
freeaddrinfo(addr_info_list); |
|
|
|
DBUG_RETURN(TRUE); |
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* Check that 'gethostbyname' returned the used ip */ |
|
|
|
for (i=0; check->h_addr_list[i]; i++) |
|
|
|
/* Log resolved IP-addresses if no match was found. */ |
|
|
|
|
|
|
|
if (!*hostname) |
|
|
|
{ |
|
|
|
if (*(uint32*)(check->h_addr_list)[i] == in->s_addr) |
|
|
|
sql_print_information("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); |
|
|
|
|
|
|
|
for (struct addrinfo *addr_info= addr_info_list; |
|
|
|
addr_info; addr_info= addr_info->ai_next) |
|
|
|
{ |
|
|
|
add_hostname(in,name); |
|
|
|
DBUG_RETURN(name); |
|
|
|
char ip_buffer[HOST_ENTRY_KEY_SIZE]; |
|
|
|
|
|
|
|
err_status= |
|
|
|
vio_get_normalized_ip_string(addr_info->ai_addr, addr_info->ai_addrlen, |
|
|
|
ip_buffer, sizeof (ip_buffer)); |
|
|
|
DBUG_ASSERT(!err_status); |
|
|
|
|
|
|
|
sql_print_information(" - %s\n", (const char *) ip_buffer); |
|
|
|
} |
|
|
|
} |
|
|
|
DBUG_PRINT("error",("Couldn't verify hostname with gethostbyname")); |
|
|
|
my_free(name,MYF(0)); |
|
|
|
|
|
|
|
add_wrong_ip_and_return: |
|
|
|
add_wrong_ip(in); |
|
|
|
DBUG_RETURN(0); |
|
|
|
/* Free the result of getaddrinfo(). */ |
|
|
|
|
|
|
|
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); |
|
|
|
} |