#At file:///home/frazer/bzr/mysql-5.1-telco-7.0/
2946 Frazer Clement 2009-07-23
Bug#46336 Backport IPv6 bug fixes to cluster-7.0
This patch contains the fixes to the mysql-5.4 bugs mentioned in
the bug report, plus a couple of other fixes.
Passes MTR testcases and manual testing including replication.
Appears to resolve issues with skip_name_resolve.
Appears to resolve issues with V4MAPPED addresses in ACLs
Extra fix information available in bug report.
modified:
configure.in
include/config-win.h
scripts/mysql_system_tables_data.sql
sql/hostname.cc
sql/mysql_priv.h
sql/sql_acl.cc
sql/sql_connect.cc
vio/viosocket.c
=== modified file 'configure.in'
--- a/configure.in 2009-06-15 07:27:14 +0000
+++ b/configure.in 2009-07-23 18:19:38 +0000
@@ -878,6 +878,24 @@ then
fi
#--------------------------------------------------------------------
+# Check for IPv6 support
+#--------------------------------------------------------------------
+
+AC_CHECK_HEADERS(netinet/in6.h)
+
+AC_CHECK_TYPES([struct in6_addr], [], [],
+[[#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN6_H
+include <netinet/in6.h>
+#endif]])
+
+#--------------------------------------------------------------------
# Check for TCP wrapper support
#--------------------------------------------------------------------
=== modified file 'include/config-win.h'
--- a/include/config-win.h 2009-06-15 07:27:14 +0000
+++ b/include/config-win.h 2009-07-23 18:19:38 +0000
@@ -21,8 +21,8 @@
#define BIG_TABLES
/* We have to do this define before including windows.h to get the
- AWE API functions. this #define means we target W2K (NT5) and newer */
-#define _WIN32_WINNT 0x0500
+ AWE API functions. this #define means we target Windows XP and newer */
+#define _WIN32_WINNT 0x0501
#if defined(_MSC_VER) && _MSC_VER >= 1400
/* Avoid endless warnings about sprintf() etc. being unsafe. */
=== modified file 'scripts/mysql_system_tables_data.sql'
--- a/scripts/mysql_system_tables_data.sql 2008-10-03 15:54:22 +0000
+++ b/scripts/mysql_system_tables_data.sql 2009-07-23 18:19:38 +0000
@@ -24,6 +24,7 @@ set @current_hostname= @@hostname;
INSERT INTO tmp_user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0 FROM dual WHERE LOWER( @current_hostname) != 'localhost';
REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
+REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
INSERT INTO tmp_user (host,user) VALUES ('localhost','');
INSERT INTO tmp_user (host,user) SELECT @current_hostname,'' FROM dual WHERE LOWER(@current_hostname ) != 'localhost';
INSERT INTO user SELECT * FROM tmp_user WHERE @had_user_table=0;
=== modified file 'sql/hostname.cc'
--- a/sql/hostname.cc 2009-05-27 15:21:45 +0000
+++ b/sql/hostname.cc 2009-07-23 18:19:38 +0000
@@ -24,7 +24,7 @@
Get hostname for an IP.
Hostnames are checked with reverse name lookup and
- checked that they doesn't resemble an ip.
+ checked that they doesn't resemble an IP.
*/
#include "mysql_priv.h"
@@ -43,13 +43,248 @@ extern "C" { // Because of SCO 3.2V4
}
#endif
+#ifdef __WIN__
+#define HAVE_STRUCT_IN6_ADDR
+#endif /* __WIN__ */
-class host_entry :public hash_filo_element
+#define HOST_ENTRY_KEY_SIZE NI_MAXHOST
+
+/************************************************************************/
+
+/*
+ When this code was written there were issues with winsock in pusbuild,
+ this constant is in this place for this reason.
+*/
+
+#ifdef EAI_NODATA
+ const int MY_NONAME_ERR_CODE= EAI_NODATA;
+#else
+ const int MY_NONAME_ERR_CODE= EAI_NONAME;
+#endif
+
+/************************************************************************/
+
+/**
+ Get the string representation for IP address. IPv6 and IPv4 addresses are
+ supported. The function is needed because getnameinfo() is known to be
+ buggy in some circumstances. Actually, this is a basic replacement for
+ getnameinfo() called with the NI_NUMERICHOST flag. Only the hostname part is
+ dumped (the port part is omitted).
+*/
+
+static void get_ip_string(const struct sockaddr *ip,
+ char *ip_str, int ip_str_size)
+{
+ switch (ip->sa_family) {
+ case AF_INET:
+ {
+ struct in_addr *ip4= &((struct sockaddr_in *) ip)->sin_addr;
+
+ int n= my_snprintf(ip_str, ip_str_size, "%08X", ntohl(ip4->s_addr));
+
+ DBUG_ASSERT(n < ip_str_size);
+ break;
+ }
+
+#ifdef HAVE_STRUCT_IN6_ADDR
+ case AF_INET6:
+ {
+ struct in6_addr *ip6= &((struct sockaddr_in6 *) ip)->sin6_addr;
+ uint32 *ip6_int32= (uint32 *) ip6->s6_addr;
+
+ int n= my_snprintf(ip_str, ip_str_size, "%08X:%08X:%08X:%08X",
+ ntohl(ip6_int32[0]), ntohl(ip6_int32[1]),
+ ntohl(ip6_int32[2]), ntohl(ip6_int32[3]));
+
+ DBUG_ASSERT(n < ip_str_size);
+ break;
+ }
+#endif /* HAVE_STRUCT_IN6_ADDR */
+
+ default:
+ /* In some cases, the socket address is unset - in this
+ * case we output an empty pseudo-IPv4 address
+ */
+ DBUG_ASSERT(sizeof("0.0.0.0") < ip_str_size);
+ my_snprintf(ip_str, ip_str_size, "0.0.0.0");
+ break;
+ }
+}
+
+/**
+ Return client IP address.
+
+ This function returns an IP address in a form, which is compatible with
+ the ACL subsystem. The thing is that the ACL subsystem works with IPv4
+ addresses only. On IPv6 enabled hosts, IP-addresses are usually returned
+ in IPv6 form. So, even if IPv4 is used, and a client connection was made
+ from IPv4 address, it will be returned in the IPv6 form.
+*/
+
+bool get_client_ip_address(const struct sockaddr *addr, uint addr_length,
+ char *ip_buffer, uint ip_buffer_size)
+{
+ const struct sockaddr *resolving_addr= addr;
+ uint resolving_addr_length= addr_length;
+
+#ifdef HAVE_STRUCT_IN6_ADDR
+ struct sockaddr_storage addr_storage;
+ memset(&addr_storage, 0, sizeof (addr_storage));
+
+ if (addr->sa_family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&((const struct sockaddr_in6 *) addr)->sin6_addr))
+ {
+ const struct sockaddr_in6 *ip6= (const struct sockaddr_in6 *) addr;
+
+ struct sockaddr_in *ip4= (struct sockaddr_in *) &addr_storage;
+ ip4->sin_family= AF_INET;
+ ip4->sin_port= 0;
+ ip4->sin_addr.s_addr= *(((uint32 *) &ip6->sin6_addr.s6_addr) + 3);
+
+ resolving_addr= (const sockaddr *) ip4;
+ resolving_addr_length= sizeof (addr_storage);
+ }
+#endif /* HAVE_STRUCT_IN6_ADDR */
+
+ return getnameinfo(resolving_addr, resolving_addr_length,
+ ip_buffer, ip_buffer_size, NULL, 0,
+ NI_NUMERICHOST) != 0;
+}
+
+/**
+ Get key value for IP address. Key value is a string representation of
+ normalized IP address.
+
+ When IPv4 and IPv6 are both used in the network stack, or in the network
+ path between a client and a server, a client can have different apparent
+ IP addresses, based on the exact route taken.
+
+ This function normalize the client IP representation, so it's suitable to
+ use as a key for searches.
+
+ Transformations are implemented as follows:
+ - IPv4 a.b.c.d --> IPv6 mapped IPv4 ::ffff:a.b.c.d
+ - IPv6 compat IPv4 ::a.b.c.d --> IPv6 mapped IPv4 ::ffff:a.b.c.d
+ - IPv6 --> IPv6
+
+ If IPv6 is not available at compile-time, IPv4 form is used.
+
+ @param [in] ip IP address
+ @param [out] ip_key Key for the given IP value
+
+ @note According to RFC3493 the only specified member of the in6_addr
+ structure is s6_addr.
+
+ @return Error status.
+ @retval FALSE Success
+ @retval TRUE Error
+*/
+
+static void hostname_cache_get_key(const struct sockaddr *ip, char *ip_key)
+{
+ const struct sockaddr *ip_to_generate_key= ip;
+
+#ifdef HAVE_STRUCT_IN6_ADDR
+ /* Prepare normalized IP address. */
+
+ struct sockaddr_storage norm_ip;
+ struct in6_addr *norm_ip6= &((sockaddr_in6 *) &norm_ip)->sin6_addr;
+ uint32 *norm_ip6_int32= (uint32 *) norm_ip6->s6_addr;
+
+ memset(&norm_ip, 0, sizeof (sockaddr_storage));
+
+ switch (ip->sa_family) {
+ case AF_INET:
+ {
+ struct in_addr *ip4= &((struct sockaddr_in *) ip)->sin_addr;
+
+ norm_ip6_int32[0]= 0;
+ norm_ip6_int32[1]= 0;
+ norm_ip6_int32[2]= htonl(0xffff);
+ norm_ip6_int32[3]= ip4->s_addr; /* in net byte order */
+
+ DBUG_ASSERT(IN6_IS_ADDR_V4MAPPED(norm_ip6));
+ break;
+ }
+
+ case AF_INET6:
+ {
+ struct in6_addr *ip6= &((struct sockaddr_in6 *) ip)->sin6_addr;
+ uint32 *ip6_int32= (uint32 *) ip6->s6_addr;
+
+ if (IN6_IS_ADDR_V4COMPAT(ip6))
+ {
+ norm_ip6_int32[0]= 0;
+ norm_ip6_int32[1]= 0;
+ norm_ip6_int32[2]= htonl(0xffff);
+ norm_ip6_int32[3]= ip6_int32[3]; /* in net byte order */
+ DBUG_ASSERT(IN6_IS_ADDR_V4MAPPED(norm_ip6));
+ }
+ else
+ {
+ /* All in net byte order: just copy 16 bytes. */
+ memcpy(norm_ip6_int32, ip6_int32, 4 * sizeof (uint32));
+ }
+
+ break;
+ }
+
+ default:
+ /* In some cases, the sockaddr is not set, for those we return
+ * an 'empty' key
+ */
+ break;
+ }
+
+ norm_ip.ss_family= AF_INET6;
+ ip_to_generate_key= (sockaddr *) &norm_ip;
+#endif /* HAVE_STRUCT_IN6_ADDR */
+
+ /*
+ Zero all bytes of the key, because it's not just 0-terminated string.
+ All bytes are taken into account during hash search.
+ */
+
+ memset(ip_key, 0, HOST_ENTRY_KEY_SIZE);
+
+ /* Get numeric representation of the normalized IP address. */
+ get_ip_string(ip_to_generate_key, ip_key, HOST_ENTRY_KEY_SIZE);
+}
+
+
+/**
+ 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:
- char ip[sizeof(struct sockaddr_storage)];
- 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[HOST_ENTRY_KEY_SIZE];
+
+ /**
+ Number of errors during handshake phase from the IP address.
+ */
+ uint connect_errors;
+
+ /**
+ One of host names for the IP address. May be NULL.
+ */
+ const char *hostname;
};
static hash_filo *hostname_cache;
@@ -62,13 +297,15 @@ 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 sockaddr_storage),NULL,
- (hash_free_key) free,
- &my_charset_bin)))
+ Host_entry tmp;
+ uint key_offset= (uint) ((char*) (&tmp.ip) - (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;
@@ -76,150 +313,306 @@ bool hostname_cache_init()
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 add_hostname(struct sockaddr_storage *in,const char *name)
+static inline Host_entry *hostname_cache_search(const char *ip)
{
- if (!(specialflag & SPECIAL_NO_HOST_CACHE))
+ return (Host_entry *) hostname_cache->search((uchar *) ip, 0);
+}
+
+static bool add_hostname_impl(const char *ip, const char *hostname)
+{
+ if (hostname_cache_search(ip))
+ return FALSE;
+
+ uint hostname_length= hostname ? (uint) strlen(hostname) : 0;
+
+ Host_entry *entry= (Host_entry *) malloc(sizeof (Host_entry) +
+ hostname_length + 1);
+
+ if (!entry)
+ return TRUE;
+
+ char *hostname_copy;
+
+ memcpy_fixed(&entry->ip, ip, HOST_ENTRY_KEY_SIZE);
+
+ if (hostname_length)
{
- VOID(pthread_mutex_lock(&hostname_cache->lock));
- host_entry *entry;
- if (!(entry=(host_entry*) hostname_cache->search((uchar*) in,0)))
- {
- uint length=name ? (uint) strlen(name) : 0;
+ hostname_copy= (char *) (entry + 1);
+ memcpy(hostname_copy, hostname, hostname_length + 1);
+ DBUG_PRINT("info", ("Adding '%s' -> '%s' to the hostname cache...'",
+ (const char *) ip,
+ (const char *) hostname_copy));
+ }
+ else
+ {
+ hostname_copy= NULL;
- if ((entry=(host_entry*) malloc(sizeof(host_entry)+length+1)))
- {
- char *new_name;
- memcpy_fixed(&entry->ip, in, sizeof(struct sockaddr_storage));
- 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);
- }
- }
- VOID(pthread_mutex_unlock(&hostname_cache->lock));
+ DBUG_PRINT("info", ("Adding '%s' -> NULL to the hostname cache...'",
+ (const char *) ip));
}
+
+ entry->hostname= hostname_copy;
+ entry->connect_errors= 0;
+
+ return hostname_cache->add(entry);
}
-inline void add_wrong_ip(struct sockaddr_storage *in)
+static bool add_hostname(const char *ip, 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, hostname);
+
+ pthread_mutex_unlock(&hostname_cache->lock);
+
+ return err_status;
}
-void inc_host_errors(struct sockaddr_storage *in)
+void inc_host_errors(struct sockaddr_storage *ip)
{
+ char key[HOST_ENTRY_KEY_SIZE];
+
+ hostname_cache_get_key((struct sockaddr *) ip, key);
+
VOID(pthread_mutex_lock(&hostname_cache->lock));
- host_entry *entry;
- if ((entry=(host_entry*) hostname_cache->search((uchar*) in,0)))
- entry->errors++;
+
+ Host_entry *entry= hostname_cache_search(key);
+
+ if (entry)
+ entry->connect_errors++;
+
VOID(pthread_mutex_unlock(&hostname_cache->lock));
}
-void reset_host_errors(struct sockaddr_storage *in)
+void reset_host_errors(struct sockaddr_storage *ip)
{
+ char key[HOST_ENTRY_KEY_SIZE];
+
+ hostname_cache_get_key((struct sockaddr *) ip, key);
+
VOID(pthread_mutex_lock(&hostname_cache->lock));
- host_entry *entry;
- if ((entry=(host_entry*) hostname_cache->search((uchar*) in,0)))
- entry->errors=0;
+
+ Host_entry *entry= hostname_cache_search(key);
+
+ if (entry)
+ entry->connect_errors=0;
+
VOID(pthread_mutex_unlock(&hostname_cache->lock));
}
-char * ip_to_hostname(struct sockaddr_storage *in, int addrLen, uint *errors)
+static inline bool is_ip_loopback(const struct sockaddr *ip)
{
- char *name= NULL;
-
- struct addrinfo hints,*res_lst= NULL,*t_res;
- int gxi_error;
- char hostname_buff[NI_MAXHOST];
+ switch (ip->sa_family){
+ case AF_INET:
+ {
+ /* Check for IPv4 127.0.0.1. */
+ struct in_addr *ip4_addr= &((struct sockaddr_in *) ip)->sin_addr;
+ return ip4_addr->s_addr == INADDR_LOOPBACK;
+ }
- host_entry *entry;
- DBUG_ENTER("ip_to_hostname");
- *errors=0;
+#ifdef HAVE_STRUCT_IN6_ADDR
+ case AF_INET6:
+ {
+ /* Check for IPv6 ::1. */
+ struct in6_addr *ip6_addr= &((struct sockaddr_in6 *) ip)->sin6_addr;
+ if (IN6_IS_ADDR_V4MAPPED(ip6_addr))
+ {
+ /* Check for ::ffff:127.0.0.1 */
+ uint32 relevantWord= *(((uint32*)(&ip6_addr->s6_addr))+ 3);
+ return (ntohl(relevantWord) == INADDR_LOOPBACK);
+ }
+ return IN6_IS_ADDR_LOOPBACK(ip6_addr);
+ }
+#endif /* HAVE_STRUCT_IN6_ADDR */
- /* Historical comparison for 127.0.0.1 */
- gxi_error= getnameinfo((struct sockaddr *)in, addrLen,
- hostname_buff, NI_MAXHOST,
- NULL, 0, NI_NUMERICHOST);
- if (gxi_error)
- {
- DBUG_PRINT("error",("getnameinfo returned %d", gxi_error));
- DBUG_RETURN(0);
+ default:
+ DBUG_ASSERT(FALSE);
+ return FALSE;
}
- DBUG_PRINT("info",("resolved: %s", hostname_buff));
-
- /* The next three compares are to solve historical solutions with localhost */
- if (!memcmp(hostname_buff, "127.0.0.1", sizeof("127.0.0.1")))
- {
- DBUG_RETURN((char *)my_localhost);
- }
- if (!memcmp(hostname_buff, "::ffff:127.0.0.1", sizeof("::ffff:127.0.0.1")))
+}
+
+static bool is_ipv4_address(const char *str)
+{
+ const char *p= str;
+
+ while (my_isdigit(&my_charset_latin1, *p))
+ ++p;
+
+ return *p == '.';
+}
+
+static bool is_ipv6_address(const char *str)
+{
+ bool colon_found= FALSE;
+
+ for (const char *p= str; *p; ++p)
{
- DBUG_RETURN((char *)my_localhost);
+ if (!my_isxdigit(&my_charset_latin1, *p) && *p != '.' && *p != ':')
+ return FALSE;
+
+ colon_found= colon_found || (*p == ':');
}
- if (!memcmp(hostname_buff, "::1", sizeof("::1")))
+
+ return colon_found;
+}
+
+static inline bool is_hostname_valid(const char *hostname)
+{
+ /*
+ IPv6 addresses are not allowed even if IPv6 is not supported on the
+ platform.
+ */
+
+ return !is_ipv4_address(hostname) &&
+ !is_ipv6_address(hostname);
+}
+
+/**
+ 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
+ @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,
+ char **hostname, uint *connect_errors)
+{
+ const struct sockaddr *ip= (const sockaddr *) ip_storage;
+ char ip_string[HOST_ENTRY_KEY_SIZE];
+ int err_code;
+
+ DBUG_ENTER("ip_to_hostname");
+
+ /* Check if we have loopback address (127.0.0.1 or ::1). */
+
+ if (is_ip_loopback(ip))
{
- DBUG_RETURN((char *)my_localhost);
+ DBUG_PRINT("info", ("Loopback address detected."));
+
+ *connect_errors= 0; /* Do not count connect errors from localhost. */
+ *hostname= (char *) my_localhost;
+
+ DBUG_RETURN(FALSE);
}
+
+ /* Get hostname cache key for the IP address. */
+
+ hostname_cache_get_key(ip, ip_string);
+
+ DBUG_PRINT("info", ("IP address: '%s'.", (const char *) ip_string));
+
+ /* Check first if we have host name in the cache. */
- /* Check first if we have name in cache */
if (!(specialflag & SPECIAL_NO_HOST_CACHE))
{
VOID(pthread_mutex_lock(&hostname_cache->lock));
- if ((entry=(host_entry*) hostname_cache->search((uchar*) in,0)))
+
+ Host_entry *entry= hostname_cache_search(ip_string);
+
+ if (entry)
{
+ *connect_errors= entry->connect_errors;
+ *hostname= NULL;
+
if (entry->hostname)
- name=my_strdup(entry->hostname,MYF(0));
- else
- name= NULL;
+ *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_string,
+ (const char *) (*hostname? *hostname : "null"),
+ (int) *connect_errors));
- DBUG_PRINT("info",("cached data %s", name ? name : "null" ));
- *errors= entry->errors;
VOID(pthread_mutex_unlock(&hostname_cache->lock));
- DBUG_RETURN(name);
+
+ DBUG_RETURN(FALSE);
}
+
VOID(pthread_mutex_unlock(&hostname_cache->lock));
}
- if (!(name= my_strdup(hostname_buff,MYF(0))))
+ /* Resolve host name. */
+
+ char hostname_buffer[NI_MAXHOST];
+
+ DBUG_PRINT("info", ("Resolving '%s'...", (const char *) ip_string));
+
+ err_code= getnameinfo(ip, sizeof (sockaddr_storage),
+ hostname_buffer, NI_MAXHOST, NULL, 0, 0);
+ if (err_code)
{
- DBUG_PRINT("error",("out of memory"));
- DBUG_RETURN(0);
+ DBUG_PRINT("error",("getnameinfo() returned %d.", err_code));
+ sql_print_warning("getnameinfo() returned error (code: %d).", err_code);
+ DBUG_RETURN(TRUE);
}
- /* Don't accept hostnames that starts with digits because they may be
- false ip:s */
- if (my_isdigit(&my_charset_latin1,name[0]))
+ DBUG_PRINT("info", ("IP '%s' resolved to '%s'.",
+ (const char *) ip_string,
+ (const char *) hostname_buffer));
+
+ /*
+ Validate host name: we don't accept hostnames that starts with digits
+ because they may be false IPs.
+ */
+
+ if (!is_hostname_valid(hostname_buffer))
{
- char *pos;
- for (pos= name+1 ; my_isdigit(&my_charset_latin1,*pos); pos++) ;
- if (*pos == '.')
- {
- DBUG_PRINT("error",("mysqld doesn't accept hostnames that starts with a number followed by a '.'"));
- goto add_wrong_ip_and_return;
- }
+ DBUG_PRINT("error", ("mysqld doesn't accept hostnames that starts "
+ "with a number followed by a '.'"));
+
+ bool err_status= add_hostname(ip_string, NULL);
+
+ *hostname= NULL;
+ *connect_errors= 0; /* New IP added to the cache. */
+
+ DBUG_RETURN(err_status);
}
- DBUG_PRINT("info",("resolved: %s",name));
-
- bzero(&hints, sizeof (struct addrinfo));
+
+ /* 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_socktype= SOCK_STREAM;
hints.ai_family= AF_UNSPEC;
- gxi_error= getaddrinfo(hostname_buff, NULL, &hints, &res_lst);
- if (gxi_error != 0)
+ 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 == MY_NONAME_ERR_CODE)
{
/*
Don't cache responses when the DNS server is down, as otherwise
@@ -227,38 +620,97 @@ char * ip_to_hostname(struct sockaddr_st
that attempted to connect during the outage) unable to connect
indefinitely.
*/
- DBUG_PRINT("error",("getaddrinfo returned %d", gxi_error));
-#ifdef EAI_NODATA
- if (gxi_error == EAI_NODATA )
-#else
- if (gxi_error == EAI_NONAME )
-#endif
- add_wrong_ip(in);
+ bool err_status= add_hostname(ip_string, NULL);
+
+ *hostname= NULL;
+ *connect_errors= 0; /* New IP added to the cache. */
+
+ DBUG_RETURN(err_status);
+ }
+ 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). */
- if (res_lst)
- freeaddrinfo(res_lst);
+ DBUG_PRINT("info", ("The following IP addresses found for '%s':",
+ (const char *) hostname_buffer));
- my_free(name, MYF(0));
- DBUG_RETURN(0);
+ for (struct addrinfo *addr_info= addr_info_list;
+ addr_info; addr_info=addr_info->ai_next)
+
+ {
+ struct sockaddr *resolved_ip= addr_info->ai_addr;
+ char resolved_ip_key[HOST_ENTRY_KEY_SIZE];
+
+ hostname_cache_get_key(resolved_ip, resolved_ip_key);
+
+ DBUG_PRINT("info", (" - '%s'", (const char *) resolved_ip_key));
+
+ if (strcmp(ip_string, resolved_ip_key) == 0)
+ {
+ /* 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 'getaddrinfo' returned the used ip */
- for (t_res= res_lst; t_res; t_res=t_res->ai_next)
+ /* Log resolved IP-addresses if no match was found. */
+
+ if (!*hostname)
{
- if (!memcmp(&(t_res->ai_addr), in,
- sizeof(struct sockaddr_storage) ) )
+ sql_print_information("Hostname '%s' does not resolve to '%s'.",
+ (const char *) hostname_buffer,
+ (const char *) ip_string);
+ 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);
- freeaddrinfo(res_lst);
- DBUG_RETURN(name);
+ struct sockaddr *resolved_ip= addr_info->ai_addr;
+ char resolved_ip_key[HOST_ENTRY_KEY_SIZE];
+
+ hostname_cache_get_key(resolved_ip, resolved_ip_key);
+
+ sql_print_information(" - %s\n", (const char *) resolved_ip_key);
}
}
- freeaddrinfo(res_lst);
- DBUG_PRINT("error",("Couldn't verify hostname with getaddrinfo"));
+ /* Free the result of getaddrinfo(). */
+
+ freeaddrinfo(addr_info_list);
+
+ /* Add an entry for the IP to the cache. */
+
+ bool err_status;
-add_wrong_ip_and_return:
- my_free(name,MYF(0));
- add_wrong_ip(in);
- DBUG_RETURN(0);
+ if (*hostname)
+ {
+ err_status= add_hostname(ip_string, *hostname);
+ *connect_errors= 0;
+ }
+ else
+ {
+ DBUG_PRINT("error",("Couldn't verify hostname with getaddrinfo()."));
+
+ err_status= add_hostname(ip_string, NULL);
+ *hostname= NULL;
+ *connect_errors= 0;
+ }
+
+ DBUG_RETURN(err_status);
}
=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h 2009-06-15 07:27:14 +0000
+++ b/sql/mysql_priv.h 2009-07-23 18:19:38 +0000
@@ -2303,8 +2303,10 @@ uint build_table_shadow_filename(char *b
#define FRM_ONLY (1 << 3)
/* from hostname.cc */
-struct in_addr;
-char * ip_to_hostname(struct sockaddr_storage *in, int addrLen, uint *errors);
+bool get_client_ip_address(const struct sockaddr *addr, uint addr_length,
+ char *ip_buffer, uint ip_buffer_size);
+bool ip_to_hostname(struct sockaddr_storage *ip_storage,
+ char **hostname, uint *connect_errors);
void inc_host_errors(struct sockaddr_storage *in);
void reset_host_errors(struct sockaddr_storage *in);
bool hostname_cache_init();
=== modified file 'sql/sql_acl.cc'
--- a/sql/sql_acl.cc 2009-06-15 05:46:54 +0000
+++ b/sql/sql_acl.cc 2009-07-23 18:19:38 +0000
@@ -1780,24 +1780,70 @@ static bool compare_hostname(const acl_h
(ip && !wild_compare(ip, host->hostname, 0)));
}
+/**
+ Check if the given hostname needs to be resolved or not.
+ Hostname has to be resolved if it actually contains *name*.
+
+ For example:
+ 192.168.1.1 --> FALSE
+ 192.168.1.0/255.255.255.0 --> FALSE
+ % --> FALSE
+ 192.168.1.% --> FALSE
+
+ AAAAFFFF --> TRUE (Hostname)
+ AAAA:FFFF:1234:5678 --> FALSE
+ ::1 --> FALSE
+*/
+
bool hostname_requires_resolving(const char *hostname)
{
- char cur;
if (!hostname)
return FALSE;
- size_t namelen= strlen(hostname);
- size_t lhlen= strlen(my_localhost);
- if ((namelen == lhlen) &&
- !my_strnncoll(system_charset_info, (const uchar *)hostname, namelen,
- (const uchar *)my_localhost, strlen(my_localhost)))
+
+ /* Check if hostname is the localhost. */
+
+ size_t hostname_len= strlen(hostname);
+ size_t localhost_len= strlen(my_localhost);
+
+ if (hostname == my_localhost ||
+ hostname_len == localhost_len &&
+ !my_strnncoll(system_charset_info,
+ (const uchar *) hostname, hostname_len,
+ (const uchar *) my_localhost, strlen(my_localhost)))
+ {
return FALSE;
- for (; (cur=*hostname); hostname++)
+ }
+
+ /* Check if hostname is not an IP address. */
+
+ bool hex_digit_found= FALSE;
+ bool colon_found= FALSE;
+
+ for (const char *p= hostname; *p; ++p)
{
- if ((cur != '%') && (cur != '_') && (cur != '.') && (cur != '/') &&
- ((cur < '0') || (cur > '9')))
+ bool is_hex_digit= (*p >= 'a' && *p <= 'f') ||
+ (*p >= 'A' && *p <= 'F');
+
+ bool is_colon= *p == ':';
+
+ if (!is_hex_digit && !is_colon && (*p < '0' || *p > '9') &&
+ *p != '%' && *p != '_' && *p != '.' && *p != '/')
+ {
+ /* Ordinary hostname found. */
return TRUE;
+ }
+
+ hex_digit_found= hex_digit_found || is_hex_digit;
+ colon_found= colon_found || is_colon;
}
- return FALSE;
+
+ /*
+ Check if it was just a hostname from HEX letters (A-F).
+
+ 'AAAAFFFF' is a valid hostname, while 'AAAA:FFFF::' is an IPv6 address.
+ */
+
+ return hex_digit_found ? !colon_found : FALSE;
}
=== modified file 'sql/sql_connect.cc'
--- a/sql/sql_connect.cc 2009-05-27 15:21:45 +0000
+++ b/sql/sql_connect.cc 2009-07-23 18:19:38 +0000
@@ -655,18 +655,35 @@ static int check_connection(THD *thd)
{
char ip[NI_MAXHOST];
+ /* The value to which 'ip' is set here, is useless */
if (vio_peer_addr(net->vio, ip, &thd->peer_port, NI_MAXHOST))
{
my_error(ER_BAD_HOST_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
return 1;
}
+
+ /* Set actual 'ip' value. */
+ if (get_client_ip_address((const struct sockaddr *) &net->vio->remote,
+ net->vio->addrLen, ip, sizeof (ip)))
+ {
+ my_error(ER_BAD_HOST_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
+ return 1;
+ }
+
+ DBUG_PRINT("info", ("get_client_ip_address() gives ip of %s", ip));
+
if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(MY_WME))))
return 1; /* The error is set by my_strdup(). */
thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip;
if (!(specialflag & SPECIAL_NO_RESOLVE))
{
- thd->main_security_ctx.host=
- ip_to_hostname(&net->vio->remote, net->vio->addrLen, &connect_errors);
+ if (ip_to_hostname(&net->vio->remote,
+ &thd->main_security_ctx.host, &connect_errors))
+ {
+ my_error(ER_BAD_HOST_ERROR, MYF(0), ip);
+ return 1;
+ }
+
/* Cut very long hostnames to avoid possible overflows */
if (thd->main_security_ctx.host)
{
=== modified file 'vio/viosocket.c'
--- a/vio/viosocket.c 2009-06-15 07:27:14 +0000
+++ b/vio/viosocket.c 2009-07-23 18:19:38 +0000
@@ -346,16 +346,6 @@ my_bool vio_peer_addr(Vio * vio, char *b
}
*port= (uint16)strtol(port_buf, (char **)NULL, 10);
-
- /*
- A lot of users do not have IPv6 loopback resolving to localhost
- correctly setup. Should this exist? No. If we do not do it though
- we will be getting a lot of support questions from users who
- have bad setups. This code should be removed by say... 2012.
- -Brian
- */
- if (!memcmp(buf, "::ffff:127.0.0.1", sizeof("::ffff:127.0.0.1")))
- strmov(buf, "127.0.0.1");
}
DBUG_PRINT("exit", ("addr: %s", buf));
DBUG_RETURN(0);
| Thread |
|---|
| • bzr commit into mysql-5.1-telco-7.0 branch (frazer:2946) Bug#46336 | Frazer Clement | 24 Jul |