List:Commits« Previous MessageNext Message »
From:Alexander Nozdrin Date:August 5 2009 5:50pm
Subject:bzr commit into mysql branch (alik:2844) Bug#45584
View as plain text  
#At file:///mnt/raid/alik/MySQL/bzr/bug38247/azalea-bf-bug45584/ based on revid:alik@stripped

 2844 Alexander Nozdrin	2009-08-05
      Fix for Bug#45584 (Host name cache does not work as a cache).
      
      The problem is described in the bug report.
      
      The solution is the following:
      
        - Make hostname cache key type of (char *);
      
        - Use string representation of normalized IPv6 addresses
          as hostname cache keys when IPv6 is supported.
      
          Use string representation of normalized IPv4 addresses
          as hostname cache keys when IPv6 is not supported.
      
        - Use only the host part of client address for hostname
          cache keys;
      
        - Actually resolve IP addresses to hostnames,
          not to IP strings;
      
        - Minimal supported Windows version has been changed to Windows XP.
          We don't support Windows 2000 for the newer versions any more.
      
          Windows XP has IPv6 support, so declaring it minimal supported
          version removes much of "windows portability hassle".

    modified:
      include/config-win.h
      include/violite.h
      sql/hostname.cc
      sql/mysql_priv.h
      sql/sql_connect.cc
      vio/viosocket.c
=== modified file 'include/config-win.h'
--- a/include/config-win.h	2009-07-31 20:21:25 +0000
+++ b/include/config-win.h	2009-08-05 17:50:09 +0000
@@ -17,11 +17,11 @@
 
 #define BIG_TABLES
 
-/* 
+/*
   Minimal version of Windows we should be able to run on.
-  Currently Windows 2000
+  Currently Windows XP.
 */
-#define _WIN32_WINNT     0x0500
+#define _WIN32_WINNT     0x0501
 
 
 #if defined(_MSC_VER) && _MSC_VER >= 1400

=== modified file 'include/violite.h'
--- a/include/violite.h	2009-07-23 13:07:41 +0000
+++ b/include/violite.h	2009-08-05 17:50:09 +0000
@@ -86,6 +86,9 @@ my_bool vio_poll_read(Vio *vio, uint tim
 my_bool vio_is_connected(Vio *vio);
 ssize_t vio_pending(Vio *vio);
 
+my_bool vio_get_normalized_ip_string(const struct sockaddr *addr, int addr_length,
+                                     char *ip_string, size_t ip_string_size);
+
 #ifdef HAVE_OPENSSL
 #include <openssl/opensslv.h>
 #if OPENSSL_VERSION_NUMBER < 0x0090700f

=== modified file 'sql/hostname.cc'
--- a/sql/hostname.cc	2009-01-27 02:08:48 +0000
+++ b/sql/hostname.cc	2009-08-05 17:50:09 +0000
@@ -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"
@@ -40,12 +40,45 @@ extern "C" {					// Because of SCO 3.2V4
 }
 #endif
 
-class host_entry :public hash_filo_element
+/*
+  HOST_ENTRY_KEY_SIZE -- size of IP address string in the hash cache.
+*/
+
+#define HOST_ENTRY_KEY_SIZE INET6_ADDRSTRLEN
+
+/**
+  An entry in the hostname hash table cache.
+
+  Host name cache does two things:
+    - caches host names to save DNS look ups;
+    - counts connect errors from IP.
+
+  Host name can be NULL (that means DNS look up failed), but connect errors
+  still are counted.
+*/
+
+class Host_entry :public hash_filo_element
 {
 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_string[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;
@@ -57,13 +90,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,
-				     (my_hash_free_key) free,
-				     &my_charset_bin)))
+  Host_entry tmp;
+  uint key_offset= (uint) ((char*) (&tmp.ip_string) - (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();
 
   return 0;
@@ -71,190 +106,432 @@ bool hostname_cache_init()
 
 void hostname_cache_free()
 {
-  if (hostname_cache)
-  {
-    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_string)
 {
-  if (!(specialflag & SPECIAL_NO_HOST_CACHE))
+  return (Host_entry *) hostname_cache->search((uchar *) ip_string, 0);
+}
+
+static bool add_hostname_impl(const char *ip_string, const char *hostname)
+{
+  if (hostname_cache_search(ip_string))
+    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_string, ip_string, 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, 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, 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);
-      }
-    }
-    pthread_mutex_unlock(&hostname_cache->lock);
+    DBUG_PRINT("info", ("Adding '%s' -> '%s' to the hostname cache...'",
+                        (const char *) ip_string,
+                        (const char *) hostname_copy));
   }
+  else
+  {
+    hostname_copy= NULL;
+
+    DBUG_PRINT("info", ("Adding '%s' -> NULL to the hostname cache...'",
+                        (const char *) ip_string));
+  }
+
+  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_string, 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_string, hostname);
+
+  pthread_mutex_unlock(&hostname_cache->lock);
+
+  return err_status;
 }
 
-void inc_host_errors(struct sockaddr_storage *in)
+void inc_host_errors(const char *ip_string)
 {
+  if (!ip_string)
+    return;
+
   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(ip_string);
+
+  if (entry)
+    entry->connect_errors++;
 
   pthread_mutex_unlock(&hostname_cache->lock);
 }
 
 
-void reset_host_errors(struct sockaddr_storage *in)
+void reset_host_errors(const char *ip_string)
 {
+  if (!ip_string)
+    return;
+
   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(ip_string);
+
+  if (entry)
+    entry->connect_errors= 0;
 
   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)
+{
+  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 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)
 {
-  char *name;
+  /*
+    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;
 
-  struct addrinfo hints,*res_lst,*t_res;
-  int gxi_error;
-  char hostname_buff[NI_MAXHOST];
+  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;
 
-  host_entry *entry;
   DBUG_ENTER("ip_to_hostname");
-  *errors=0;
+  DBUG_PRINT("info", ("IP address: '%s'; family: %d.",
+                      (const char *) ip_string,
+                      (int) ip->sa_family));
 
-  /* 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);
-  }
-  DBUG_PRINT("info",("resolved: %s", hostname_buff));
+  /* Check if we have loopback address (127.0.0.1 or ::1). */
 
-  /* 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")))
-  {
-    DBUG_RETURN((char *)my_localhost);
-  }
-  if (!memcmp(hostname_buff, "::1", sizeof("::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);
   }
 
-  /* Check first if we have name in cache */
+  /* Check first if we have host name in the cache. */
+
   if (!(specialflag & SPECIAL_NO_HOST_CACHE))
   {
     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;
       pthread_mutex_unlock(&hostname_cache->lock);
-      DBUG_RETURN(name);
+
+      DBUG_RETURN(FALSE);
     }
+
     pthread_mutex_unlock(&hostname_cache->lock);
   }
 
-  if (!(name= my_strdup(hostname_buff, MYF(0))))
+  /*
+    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_string));
+
+  err_code= getnameinfo(ip, sizeof (sockaddr_storage),
+                        hostname_buffer, NI_MAXHOST, NULL, 0, NI_NAMEREQD);
+
+  if (err_code == EAI_NONAME)
+  {
+    /*
+      There is no reverse address mapping for the IP address. A host name
+      can not be resolved.
+    */
+
+    DBUG_PRINT("error", ("IP address '%s' could not be resolved: "
+                         "no reverse address mapping.",
+                         (const char *) ip_string));
+
+    sql_print_warning("IP address '%s' could not be resolved: "
+                      "no reverse address mapping.",
+                      (const char *) ip_string);
+
+    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",("out of memory"));
-    DBUG_RETURN(0);
+    DBUG_PRINT("error", ("IP address '%s' could not be resolved: "
+                         "getnameinfo() returned %d.",
+                         (const char *) ip_string,
+                         (int) err_code));
+
+    sql_print_warning("IP address '%s' could not be resolved: "
+                      "getnameinfo() returned error (code: %d).",
+                      (const char *) ip_string,
+                      (int) 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 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))
   {
-    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", ("IP address '%s' has been resolved "
+                         "to the host name '%s', which resembles "
+                         "IPv4-address itself.",
+                         (const char *) ip_string,
+                         (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_string,
+                      (const char *) hostname_buffer);
+
+    bool err_status= add_hostname(ip_string, NULL);
+
+    *hostname= NULL;
+    *connect_errors= 0; /* New IP added to the cache. */
+
+    DBUG_RETURN(err_status);
   }
 
-  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)
+  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)
   {
     /*
-      Don't cache responses when the DSN server is down, as otherwise
+      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.
     */
-    /* 
-      When this code was written there were issues with winsock in pusbuild, 
-      this define is in this place for this reason.
-    */
-    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);
 
-    my_free(name,MYF(0));
-    DBUG_RETURN(0);
+    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). */
+
+  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 ip_buffer[HOST_ENTRY_KEY_SIZE];
+
+    {
+      bool 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_string, ip_buffer) == 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);
+      char ip_buffer[HOST_ENTRY_KEY_SIZE];
+
+      bool 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);
     }
   }
-  freeaddrinfo(res_lst);
-  DBUG_PRINT("error",("Couldn't verify hostname with getaddrinfo"));
 
-add_wrong_ip_and_return:
-  my_free(name,MYF(0));
-  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. */
+
+  bool err_status;
+
+  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-07-31 19:46:24 +0000
+++ b/sql/mysql_priv.h	2009-08-05 17:50:09 +0000
@@ -2301,9 +2301,11 @@ uint build_table_shadow_filename(char *b
 #define FRM_ONLY        (1 << 3)
 
 /* from hostname.cc */
-char *ip_to_hostname(struct sockaddr_storage *in, int addrLen, uint *errors);
-void inc_host_errors(struct sockaddr_storage *in);
-void reset_host_errors(struct sockaddr_storage *in);
+bool ip_to_hostname(struct sockaddr_storage *ip_storage,
+                    const char *ip_string,
+                    char **hostname, uint *connect_errors);
+void inc_host_errors(const char *ip_string);
+void reset_host_errors(const char *ip_string);
 bool hostname_cache_init();
 void hostname_cache_free();
 void hostname_cache_refresh(void);

=== modified file 'sql/sql_connect.cc'
--- a/sql/sql_connect.cc	2009-06-09 14:36:52 +0000
+++ b/sql/sql_connect.cc	2009-08-05 17:50:09 +0000
@@ -377,7 +377,7 @@ check_user(THD *thd, enum enum_server_co
     if (send_old_password_request(thd) ||
         my_net_read(net) != SCRAMBLE_LENGTH_323 + 1)
     {
-      inc_host_errors(&net->vio->remote);
+      inc_host_errors(thd->main_security_ctx.ip);
 
       my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
       DBUG_RETURN(1);
@@ -679,8 +679,13 @@ static int check_connection(THD *thd)
     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.ip,
+                         &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)
       {
@@ -768,7 +773,7 @@ static int check_connection(THD *thd)
 	(pkt_len= my_net_read(net)) == packet_error ||
 	pkt_len < MIN_HANDSHAKE_SIZE)
     {
-      inc_host_errors(&net->vio->remote);
+      inc_host_errors(thd->main_security_ctx.ip);
 
       my_error(ER_HANDSHAKE_ERROR, MYF(0),
                thd->main_security_ctx.host_or_ip);
@@ -779,7 +784,7 @@ static int check_connection(THD *thd)
 #include "_cust_sql_parse.h"
 #endif
   if (connect_errors)
-    reset_host_errors(&net->vio->remote);
+    reset_host_errors(thd->main_security_ctx.ip);
   if (thd->packet.alloc(thd->variables.net_buffer_length))
     return 1; /* The error is set by alloc(). */
 
@@ -813,7 +818,7 @@ static int check_connection(THD *thd)
     /* Do the SSL layering. */
     if (!ssl_acceptor_fd)
     {
-      inc_host_errors(&net->vio->remote);
+      inc_host_errors(thd->main_security_ctx.ip);
       my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
       return 1;
     }
@@ -821,7 +826,7 @@ static int check_connection(THD *thd)
     if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout))
     {
       DBUG_PRINT("error", ("Failed to accept new SSL connection"));
-      inc_host_errors(&net->vio->remote);
+      inc_host_errors(thd->main_security_ctx.ip);
 
       my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
       return 1;
@@ -832,7 +837,7 @@ static int check_connection(THD *thd)
     {
       DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
 			   pkt_len));
-      inc_host_errors(&net->vio->remote);
+      inc_host_errors(thd->main_security_ctx.ip);
 
       my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
       return 1;
@@ -842,7 +847,7 @@ static int check_connection(THD *thd)
 
   if (end >= (char*) net->read_pos+ pkt_len +2)
   {
-    inc_host_errors(&net->vio->remote);
+    inc_host_errors(thd->main_security_ctx.ip);
 
     my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
     return 1;
@@ -881,7 +886,7 @@ static int check_connection(THD *thd)
 
   if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len)
   {
-    inc_host_errors(&net->vio->remote);
+    inc_host_errors(thd->main_security_ctx.ip);
 
     my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
     return 1;

=== modified file 'vio/viosocket.c'
--- a/vio/viosocket.c	2009-08-05 17:39:37 +0000
+++ b/vio/viosocket.c	2009-08-05 17:50:09 +0000
@@ -385,6 +385,53 @@ static void vio_get_normalized_ip(const 
 
 
 /**
+  Return the normalized IP address string for a sock-address.
+
+  The idea is to return an IPv4-address for an IPv4-mapped and
+  IPv4-compatible IPv6 address.
+
+  The function writes the normalized IP address to the given buffer.
+  The buffer should have enough space, otherwise error flag is returned.
+  The system constant INET6_ADDRSTRLEN can be used to reserve buffers of
+  the right size.
+
+  @param addr           [in]  sockaddr object (AF_INET or AF_INET6).
+  @param addr_length    [in]  length of the addr.
+  @param ip_string      [out] buffer to write normalized IP address.
+  @param ip_string_size [in]  size of the ip_string.
+
+  @return Error status.
+  @retval TRUE in case of error (the ip_string buffer is not enough).
+  @retval FALSE on success.
+*/
+
+my_bool vio_get_normalized_ip_string(const struct sockaddr *addr,
+                                     int addr_length,
+                                     char *ip_string,
+                                     size_t ip_string_size)
+{
+  struct sockaddr_storage norm_addr_storage;
+  struct sockaddr *norm_addr= (struct sockaddr *) &norm_addr_storage;
+  int norm_addr_length;
+  int err_code;
+
+  vio_get_normalized_ip(addr, addr_length, norm_addr, &norm_addr_length);
+
+  err_code= getnameinfo(norm_addr, norm_addr_length,
+                        ip_string, ip_string_size, NULL, 0,
+                        NI_NUMERICHOST);
+
+  if (!err_code)
+    return FALSE;
+
+  DBUG_PRINT("error", ("getnameinfo() failed with %d (%s).",
+                       (int) err_code,
+                       (const char *) gai_strerror(err_code)));
+  return TRUE;
+}
+
+
+/**
   Return IP address and port of a VIO client socket.
 
   The function returns an IPv4 address if IPv6 support is disabled.


Attachment: [text/bzr-bundle] bzr/alik@sun.com-20090805175009-g1od16i3t1xkw2qr.bundle
Thread
bzr commit into mysql branch (alik:2844) Bug#45584Alexander Nozdrin5 Aug