List:Commits« Previous MessageNext Message »
From:Frazer Clement Date:August 11 2009 3:56pm
Subject:bzr commit into mysql-5.1-telco-7.0 branch (frazer:2953) Bug#46336
View as plain text  
#At file:///home/frazer/bzr/mysql-5.1-telco-7.0/

 2953 Frazer Clement	2009-08-11
      Bug#46336 Backport IPv6 bug fixes to cluster-7.0
      
      - Patch back of 'official' IPv6 fixes from mysql-azalea
        e.g. fixes for bugs 45584, 43006 and 45606
      - Minor modification to get_peername call to match
        different Windows-specific code in mysql-5.1-telco-7.0
      - Includes fix for Solaris getnameinfo() address length
        issue.
      
       
      modified:
        CMakeLists.txt
        configure.in
        include/violite.h
        mysql-test/t/skip_name_resolve.test
        sql/hostname.cc
        sql/mysql_priv.h
        sql/sql_connect.cc
        vio/viosocket.c

=== modified file 'CMakeLists.txt'
--- a/CMakeLists.txt	2009-05-27 15:21:45 +0000
+++ b/CMakeLists.txt	2009-08-11 15:56:39 +0000
@@ -34,6 +34,9 @@ CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/inclu
 # Set standard options
 ADD_DEFINITIONS(-DHAVE_YASSL)
 
+# Enable IPv6 handling code
+ADD_DEFINITIONS(-DHAVE_IPV6)
+
 # Set debug options
 SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DFORCE_INIT_OF_VARS")
 

=== modified file 'configure.in'
--- a/configure.in	2009-08-05 14:37:58 +0000
+++ b/configure.in	2009-08-11 15:56:39 +0000
@@ -883,17 +883,35 @@ fi
 
 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]])
+AC_CHECK_TYPES([struct sockaddr_in6, struct in6_addr],
+                [have_in6_types=yes],
+                [have_in6_types=no], 
+  [[
+    #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
+  ]])
+
+AC_MSG_CHECKING([for IPv6 support])
+
+AC_ARG_ENABLE(ipv6,
+  AS_HELP_STRING([--disable-ipv6], [Disable support for IPv6 networking]),
+  [disable_ipv6=yes], [disable_ipv6=no])
+
+if test x"$disable_ipv6" = xyes -o x"$have_in6_types" = xno; then
+  AC_MSG_RESULT([no])
+else
+  AC_DEFINE([HAVE_IPV6], [1], [Define if IPv6 networking support is present])
+  AC_MSG_RESULT([yes])
+fi
 
 #--------------------------------------------------------------------
 # Check for TCP wrapper support

=== modified file 'include/violite.h'
--- a/include/violite.h	2009-05-27 15:21:45 +0000
+++ b/include/violite.h	2009-08-11 15:56:39 +0000
@@ -90,6 +90,14 @@ my_socket vio_fd(Vio*vio);
 my_bool vio_peer_addr(Vio* vio, char *buf, uint16 *port, size_t buflen);
 my_bool	vio_poll_read(Vio *vio,uint timeout);
 
+my_bool vio_get_normalized_ip_string(const struct sockaddr *addr, int addr_length,
+                                     char *ip_string, size_t ip_string_size);
+
+int vio_getnameinfo(const struct sockaddr *sa,
+                    char *hostname, size_t hostname_size,
+                    char *port, size_t port_size,
+                    int flags);
+
 #ifdef HAVE_OPENSSL
 #include <openssl/opensslv.h>
 #if OPENSSL_VERSION_NUMBER < 0x0090700f

=== modified file 'mysql-test/t/skip_name_resolve.test'
--- a/mysql-test/t/skip_name_resolve.test	2009-03-10 15:54:24 +0000
+++ b/mysql-test/t/skip_name_resolve.test	2009-08-11 15:56:39 +0000
@@ -15,7 +15,7 @@ DROP USER mysqltest_1@'127.0.0.1/255.255
 # Bug#13407 Remote connecting crashes server
 # Server crashed when one used USER() function in connection for which
 # was impossible to obtain peer hostname.
-connect (con1, 127.0.0.1, root, , test, $MASTER_MYPORT, );
+connect (con1, localhost, root, , test, $MASTER_MYPORT, );
 --replace_column 1 #
 SELECT USER();
 # We are only interested in the fact that statement below doesn't

=== modified file 'sql/hostname.cc'
--- a/sql/hostname.cc	2009-08-05 17:56:06 +0000
+++ b/sql/hostname.cc	2009-08-11 15:56:39 +0000
@@ -43,197 +43,11 @@ extern "C" {					// Because of SCO 3.2V4
 }
 #endif
 
-#ifdef __WIN__
-#define HAVE_STRUCT_IN6_ADDR
-#endif /* __WIN__ */
-
 /*
-  HOST_ENTRY_KEY_SIZE -- size of IP address string in the hash cache. The
-  system constant NI_MAXHOST could be used here. However, it means at least
-  1024 bytes per IP, which seems to be quite big.
-
-  Since IP address string is created by our function get_ip_string(), we
-  can reduce the space.  get_ip_string() returns a hexadecimal
-  respresentation (1111:2222:...:8888) in case of IPv6 and the standard
-  representation (111.222.333.444) in case of IPv4, which means 39 bytes at
-  most. So, we need 40 bytes for storing IP address string including
-  trailing zero.
-*/
-
-#define HOST_ENTRY_KEY_SIZE 40
-
-/************************************************************************/
-
-/*
-  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).
+  HOST_ENTRY_KEY_SIZE -- size of IP address string in the hash cache.
 */
 
-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;
-    uint32 ip4_int32= ntohl(ip4->s_addr);
-    uint8 *ip4_int8= (uint8 *) &ip4_int32;
-
-    int n= my_snprintf(ip_str, ip_str_size, "%d.%d.%d.%d",
-                       ip4_int8[0], ip4_int8[1], ip4_int8[2], ip4_int8[3]);
-
-    DBUG_ASSERT(n < ip_str_size);
-    (void)n; // Remove compiler warning
-    break;
-  }
-
-#ifdef HAVE_STRUCT_IN6_ADDR
-  case AF_INET6:
-  {
-    struct in6_addr *ip6= &((struct sockaddr_in6 *) ip)->sin6_addr;
-    uint16 *ip6_int16= (uint16 *) ip6->s6_addr;
-
-    int n= my_snprintf(ip_str, ip_str_size,
-                       "%04X:%04X:%04X:%04X:%04X:%04X:%04X:%04X",
-                       ntohs(ip6_int16[0]), ntohs(ip6_int16[1]),
-                       ntohs(ip6_int16[2]), ntohs(ip6_int16[3]),
-                       ntohs(ip6_int16[4]), ntohs(ip6_int16[5]),
-                       ntohs(ip6_int16[6]), ntohs(ip6_int16[7]));
-
-    DBUG_ASSERT(n < ip_str_size);
-    (void)n; // Remove compiler warning
-    break;
-  }
-#endif /* HAVE_STRUCT_IN6_ADDR */
-
-  default:
-    DBUG_ASSERT(FALSE);
-  }
-}
-
-/**
-  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.
-
-  @note It is possible to call hostname_cache_get_key() with zeroed IP
-  address (ip->sa_family == 0). In this case hostname_cache_get_key()
-  returns TRUE (error status).
-
-  @return Error status.
-  @retval FALSE Success
-  @retval TRUE Error
-*/
-
-static bool hostname_cache_get_key(const struct sockaddr *ip, char *ip_key)
-{
-  const struct sockaddr *ip_to_generate_key= ip;
-
-  if (ip->sa_family == 0)
-    return TRUE; /* IP address is not set. */
-
-#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:
-    DBUG_ASSERT(FALSE);
-    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);
-
-  return FALSE;
-}
-
+#define HOST_ENTRY_KEY_SIZE INET6_ADDRSTRLEN
 
 /**
   An entry in the hostname hash table cache.
@@ -257,7 +71,7 @@ public:
 
     This IP address is never used to connect to a socket.
   */
-  char ip[HOST_ENTRY_KEY_SIZE];
+  char ip_key[HOST_ENTRY_KEY_SIZE];
 
   /**
     Number of errors during handshake phase from the IP address.
@@ -265,7 +79,7 @@ public:
   uint connect_errors;
 
   /**
-    One of host names for the IP address. May be NULL.
+    One of the host names for the IP address. May be NULL.
   */
   const char *hostname;
 };
@@ -281,7 +95,7 @@ void hostname_cache_refresh()
 bool hostname_cache_init()
 {
   Host_entry tmp;
-  uint key_offset= (uint) ((char*) (&tmp.ip) - (char*) &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,
@@ -301,34 +115,43 @@ void hostname_cache_free()
 }
 
 
-static inline Host_entry *hostname_cache_search(const char *ip)
+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);
+
+  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)
 {
-  return (Host_entry *) hostname_cache->search((uchar *) ip, 0);
+  return (Host_entry *) hostname_cache->search((uchar *) ip_key, 0);
 }
 
-static bool add_hostname_impl(const char *ip, const char *hostname)
+static bool add_hostname_impl(const char *ip_key, const char *hostname)
 {
-  if (hostname_cache_search(ip))
+  if (hostname_cache_search(ip_key))
     return FALSE;
 
-  uint hostname_length= hostname ? (uint) strlen(hostname) : 0;
+  size_t hostname_size= hostname ? strlen(hostname) + 1 : 0;
 
-  Host_entry *entry= (Host_entry *) malloc(sizeof (Host_entry) +
-                                           hostname_length + 1);
+  Host_entry *entry= (Host_entry *) malloc(sizeof (Host_entry) + hostname_size);
 
   if (!entry)
     return TRUE;
 
   char *hostname_copy;
 
-  memcpy_fixed(&entry->ip, ip, HOST_ENTRY_KEY_SIZE);
+  memcpy_fixed(&entry->ip_key, ip_key, HOST_ENTRY_KEY_SIZE);
 
-  if (hostname_length)
+  if (hostname_size)
   {
     hostname_copy= (char *) (entry + 1);
-    memcpy(hostname_copy, hostname, hostname_length + 1);
+    memcpy(hostname_copy, hostname, hostname_size);
     DBUG_PRINT("info", ("Adding '%s' -> '%s' to the hostname cache...'",
-                        (const char *) ip,
+                        (const char *) ip_key,
                         (const char *) hostname_copy));
   }
   else
@@ -336,7 +159,7 @@ static bool add_hostname_impl(const char
     hostname_copy= NULL;
 
     DBUG_PRINT("info", ("Adding '%s' -> NULL to the hostname cache...'",
-                        (const char *) ip));
+                        (const char *) ip_key));
   }
 
   entry->hostname= hostname_copy;
@@ -346,30 +169,31 @@ static bool add_hostname_impl(const char
 }
 
 
-static bool add_hostname(const char *ip, const char *hostname)
+static bool add_hostname(const char *ip_key, const char *hostname)
 {
   if (specialflag & SPECIAL_NO_HOST_CACHE)
     return FALSE;
 
   pthread_mutex_lock(&hostname_cache->lock);
 
-  bool err_status= add_hostname_impl(ip, hostname);
+  bool err_status= add_hostname_impl(ip_key, hostname);
 
   pthread_mutex_unlock(&hostname_cache->lock);
 
   return err_status;
 }
 
-void inc_host_errors(struct sockaddr_storage *ip)
+void inc_host_errors(const char *ip_string)
 {
-  char key[HOST_ENTRY_KEY_SIZE];
-
-  if (hostname_cache_get_key((struct sockaddr *) ip, key))
+  if (!ip_string)
     return;
 
+  char ip_key[HOST_ENTRY_KEY_SIZE];
+  prepare_hostname_cache_key(ip_string, ip_key);
+
   VOID(pthread_mutex_lock(&hostname_cache->lock));
 
-  Host_entry *entry= hostname_cache_search(key);
+  Host_entry *entry= hostname_cache_search(ip_key);
 
   if (entry)
     entry->connect_errors++;
@@ -377,16 +201,17 @@ void inc_host_errors(struct sockaddr_sto
   VOID(pthread_mutex_unlock(&hostname_cache->lock));
 }
 
-void reset_host_errors(struct sockaddr_storage *ip)
+void reset_host_errors(const char* ip_string)
 {
-  char key[HOST_ENTRY_KEY_SIZE];
-
-  if (hostname_cache_get_key((struct sockaddr *) ip, key))
+  if (!ip_string)
     return;
 
+  char ip_key[HOST_ENTRY_KEY_SIZE];
+  prepare_hostname_cache_key(ip_string, ip_key);
+
   VOID(pthread_mutex_lock(&hostname_cache->lock));
 
-  Host_entry *entry= hostname_cache_search(key);
+  Host_entry *entry= hostname_cache_search(ip_key);
 
   if (entry)
     entry->connect_errors= 0;
@@ -402,37 +227,22 @@ static inline bool is_ip_loopback(const 
     {
       /* Check for IPv4 127.0.0.1. */
       struct in_addr *ip4= &((struct sockaddr_in *) ip)->sin_addr;
-      return ip4->s_addr == INADDR_LOOPBACK;
+      return ntohl(ip4->s_addr) == INADDR_LOOPBACK;
     }
 
-#ifdef HAVE_STRUCT_IN6_ADDR
+#ifdef HAVE_IPV6
   case AF_INET6:
     {
-      /*
-        Check if we have loopback here:
-          - IPv6 loopback             (::1)
-          - IPv4-compatible 127.0.0.1 (0:0:0:0:0:0000:7f00:0001)
-          - IPv4-mapped 127.0.0.1     (0:0:0:0:0:ffff:7f00:0001)
-      */
+      /* Check for IPv6 ::1. */
       struct in6_addr *ip6= &((struct sockaddr_in6 *) ip)->sin6_addr;
-      if (IN6_IS_ADDR_V4COMPAT(ip6) || IN6_IS_ADDR_V4MAPPED(ip6))
-      {
-        uint32 *ip6_int32= (uint32 *) ip6->s6_addr;
-        return ntohl(ip6_int32[3]) == INADDR_LOOPBACK;
-      }
-      else
-      {
-        return IN6_IS_ADDR_LOOPBACK(ip6);
-      }
+      return IN6_IS_ADDR_LOOPBACK(ip6);
     }
-#endif /* HAVE_STRUCT_IN6_ADDR */
+#endif /* HAVE_IPV6 */
 
   default:
-    DBUG_ASSERT(FALSE);
     return FALSE;
   }
 }
-
 static inline bool is_hostname_valid(const char *hostname)
 {
   /*
@@ -464,7 +274,8 @@ static inline bool is_hostname_valid(con
   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 address. Must be set.
+  @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
 
@@ -478,18 +289,18 @@ static inline bool is_hostname_valid(con
 */
 
 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;
-  char ip_string[HOST_ENTRY_KEY_SIZE];
   int err_code;
+  bool err_status;
   
   DBUG_ENTER("ip_to_hostname");
 
-  /* IP address must be set properly. */
-
-  DBUG_ASSERT(ip_storage->ss_family == AF_INET ||
-              ip_storage->ss_family == AF_INET6);
+  DBUG_PRINT("info", ("IP address: '%s'; family: %d.",
+                      (const char *) ip_string,
+                      (int) ip->sa_family));
 
   /* Check if we have loopback address (127.0.0.1 or ::1). */
 
@@ -503,15 +314,10 @@ bool ip_to_hostname(struct sockaddr_stor
     DBUG_RETURN(FALSE);
   }
 
-  /* Get hostname cache key for the IP address. */
+  /* Prepare host name cache key. */
 
-  {
-    bool err_status= hostname_cache_get_key(ip, ip_string);
-    DBUG_ASSERT(!err_status);
-    (void)err_status; // Remove compiler warning
-  }
-
-  DBUG_PRINT("info", ("IP address: '%s'.", (const char *) ip_string));
+  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. */
   
@@ -519,7 +325,7 @@ bool ip_to_hostname(struct sockaddr_stor
   {
     VOID(pthread_mutex_lock(&hostname_cache->lock));
 
-    Host_entry *entry= hostname_cache_search(ip_string);
+    Host_entry *entry= hostname_cache_search(ip_key);
 
     if (entry)
     {
@@ -531,7 +337,7 @@ bool ip_to_hostname(struct sockaddr_stor
 
       DBUG_PRINT("info",("IP (%s) has been found in the cache. "
                          "Hostname: '%s'; connect_errors: %d",
-                         (const char *) ip_string,
+                         (const char *) ip_key,
                          (const char *) (*hostname? *hostname : "null"),
                          (int) *connect_errors));
 
@@ -549,10 +355,10 @@ bool ip_to_hostname(struct sockaddr_stor
 
   char hostname_buffer[NI_MAXHOST];
 
-  DBUG_PRINT("info", ("Resolving '%s'...", (const char *) ip_string));
+  DBUG_PRINT("info", ("Resolving '%s'...", (const char *) ip_key));
 
-  err_code= getnameinfo(ip, sizeof (sockaddr_storage),
-                        hostname_buffer, NI_MAXHOST, NULL, 0, NI_NAMEREQD);
+  err_code= vio_getnameinfo(ip, hostname_buffer, NI_MAXHOST, NULL, 0,
+                            NI_NAMEREQD);
 
   if (err_code == EAI_NONAME)
   {
@@ -563,13 +369,13 @@ bool ip_to_hostname(struct sockaddr_stor
 
     DBUG_PRINT("error", ("IP address '%s' could not be resolved: "
                          "no reverse address mapping.",
-                         (const char *) ip_string));
+                         (const char *) ip_key));
 
     sql_print_warning("IP address '%s' could not be resolved: "
                       "no reverse address mapping.",
-                      (const char *) ip_string);
+                      (const char *) ip_key);
 
-    bool err_status= add_hostname(ip_string, NULL);
+    bool err_status= add_hostname(ip_key, NULL);
 
     *hostname= NULL;
     *connect_errors= 0; /* New IP added to the cache. */
@@ -580,19 +386,19 @@ bool ip_to_hostname(struct sockaddr_stor
   {
     DBUG_PRINT("error", ("IP address '%s' could not be resolved: "
                          "getnameinfo() returned %d.",
-                         (const char *) ip_string,
+                         (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_string,
+                      (const char *) ip_key,
                       (int) err_code);
 
     DBUG_RETURN(TRUE);
   }
 
   DBUG_PRINT("info", ("IP '%s' resolved to '%s'.",
-                      (const char *) ip_string,
+                      (const char *) ip_key,
                       (const char *) hostname_buffer));
 
   /*
@@ -603,7 +409,7 @@ bool ip_to_hostname(struct sockaddr_stor
     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 exmaple, it is possible to specify a host name mask (like
+    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
@@ -615,16 +421,16 @@ bool ip_to_hostname(struct sockaddr_stor
     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 *) 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_string,
+                      (const char *) ip_key,
                       (const char *) hostname_buffer);
 
-    bool err_status= add_hostname(ip_string, NULL);
+    bool err_status= add_hostname(ip_key, NULL);
 
     *hostname= NULL;
     *connect_errors= 0; /* New IP added to the cache. */
@@ -647,7 +453,7 @@ bool ip_to_hostname(struct sockaddr_stor
 
   err_code= getaddrinfo(hostname_buffer, NULL, &hints, &addr_info_list);
 
-  if (err_code == MY_NONAME_ERR_CODE)
+  if (err_code == EAI_NONAME)
   {
     /*
       Don't cache responses when the DNS server is down, as otherwise
@@ -655,7 +461,7 @@ bool ip_to_hostname(struct sockaddr_stor
       that attempted to connect during the outage) unable to connect
       indefinitely.
     */
-    bool err_status= add_hostname(ip_string, NULL);
+    bool err_status= add_hostname(ip_key, NULL);
 
     *hostname= NULL;
     *connect_errors= 0; /* New IP added to the cache. */
@@ -677,18 +483,15 @@ bool ip_to_hostname(struct sockaddr_stor
        addr_info; addr_info= addr_info->ai_next)
 
   {
-    struct sockaddr *resolved_ip= addr_info->ai_addr;
-    char resolved_ip_key[HOST_ENTRY_KEY_SIZE];
+    char ip_buffer[HOST_ENTRY_KEY_SIZE];
 
-    {
-      bool err_status= hostname_cache_get_key(resolved_ip, resolved_ip_key);
-      DBUG_ASSERT(!err_status);
-      (void)err_status; // Remove compiler warning
-    }
+    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 *) resolved_ip_key));
+    DBUG_PRINT("info", (" - '%s'", (const char*) ip_buffer));
 
-    if (strcmp(ip_string, resolved_ip_key) == 0)
+    if (strcmp(ip_key, ip_buffer) == 0)
     {
       /* Copy host name string to be stored in the cache. */
 
@@ -712,7 +515,7 @@ bool ip_to_hostname(struct sockaddr_stor
   {
     sql_print_information("Hostname '%s' does not resolve to '%s'.",
                           (const char *) hostname_buffer,
-                          (const char *) ip_string);
+                          (const char *) ip_key);
     sql_print_information("Hostname '%s' has the following IP addresses:",
                           (const char *) hostname_buffer);
 
@@ -720,12 +523,13 @@ bool ip_to_hostname(struct sockaddr_stor
          addr_info; addr_info= addr_info->ai_next)
 
     {
-      struct sockaddr *resolved_ip= addr_info->ai_addr;
-      char resolved_ip_key[HOST_ENTRY_KEY_SIZE];
+      char ip_buffer[HOST_ENTRY_KEY_SIZE];
 
-      hostname_cache_get_key(resolved_ip, resolved_ip_key);
+      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 *) resolved_ip_key);
+      sql_print_information(" - %s\n", (const char *) ip_buffer);
     }
   }
   
@@ -735,18 +539,16 @@ bool ip_to_hostname(struct sockaddr_stor
 
   /* Add an entry for the IP to the cache. */
 
-  bool err_status;
-
   if (*hostname)
   {
-    err_status= add_hostname(ip_string, *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_string, NULL);
+    err_status= add_hostname(ip_key, NULL);
     *hostname= NULL;
     *connect_errors= 0;
   }

=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h	2009-08-05 14:37:58 +0000
+++ b/sql/mysql_priv.h	2009-08-11 15:56:39 +0000
@@ -2304,9 +2304,10 @@ uint build_table_shadow_filename(char *b
 
 /* from hostname.cc */
 bool ip_to_hostname(struct sockaddr_storage *ip_storage,
+                    const char *ip_string,
                     char **hostname, uint *connect_errors);
-void inc_host_errors(struct sockaddr_storage *in);
-void reset_host_errors(struct sockaddr_storage *in);
+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-08-05 14:37:58 +0000
+++ b/sql/sql_connect.cc	2009-08-11 15:56:39 +0000
@@ -375,7 +375,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);
     }
@@ -665,7 +665,7 @@ static int check_connection(THD *thd)
     thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip;
     if (!(specialflag & SPECIAL_NO_RESOLVE))
     {
-      if (ip_to_hostname(&net->vio->remote,
+      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);
@@ -759,7 +759,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);
       return 1;
@@ -769,7 +769,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(). */
 
@@ -803,7 +803,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;
     }
@@ -811,7 +811,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;
     }
@@ -821,7 +821,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;
     }
@@ -830,7 +830,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;
   }
@@ -868,7 +868,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 14:37:58 +0000
+++ b/vio/viosocket.c	2009-08-11 15:56:39 +0000
@@ -311,6 +311,93 @@ my_socket vio_fd(Vio* vio)
   return vio->sd;
 }
 
+/**
+  Convert a sock-address (AF_INET or AF_INET6) into the "normalized" form,
+  which is the IPv4 form for IPv4-mapped or IPv4-compatible IPv6 addresses.
+
+  @note Background: when IPv4 and IPv6 are used simultaneously, IPv4
+  addresses may be written in a form of IPv4-mapped or IPv4-compatible IPv6
+  addresses. That means, one address (a.b.c.d) can be written in three forms:
+    - IPv4: a.b.c.d;
+    - IPv4-compatible IPv6: ::a.b.c.d;
+    - IPv4-mapped IPv4: ::ffff:a.b.c.d;
+
+  Having three forms of one address makes it a little difficult to compare
+  addresses with each other (the IPv4-compatible IPv6-address of foo.bar
+  will be different from the IPv4-mapped IPv6-address of foo.bar).
+
+  @note This function can be made public when it's needed.
+
+  @param src        [in] source IP address (AF_INET or AF_INET6).
+  @param src_length [in] length of the src.
+  @param dst        [out] a buffer to store normalized IP address
+                          (sockaddr_storage).
+  @param dst_length [out] actual length of the normalized IP address.
+*/
+static void vio_get_normalized_ip(const struct sockaddr *src,
+                                  int src_length,
+                                  struct sockaddr *dst,
+                                  int *dst_length)
+{
+  switch (src->sa_family) {
+  case AF_INET:
+    memcpy(dst, src, src_length);
+    *dst_length= src_length;
+    break;
+
+#ifdef HAVE_IPV6
+  case AF_INET6:
+  {
+    const struct sockaddr_in6 *src_addr6= (const struct sockaddr_in6 *) src;
+    const struct in6_addr *src_ip6= &(src_addr6->sin6_addr);
+    const uint32 *src_ip6_int32= (uint32 *) src_ip6->s6_addr;
+
+    if (IN6_IS_ADDR_V4MAPPED(src_ip6) || IN6_IS_ADDR_V4COMPAT(src_ip6))
+    {
+      struct sockaddr_in *dst_ip4= (struct sockaddr_in *) dst;
+
+      /*
+        This is an IPv4-mapped or IPv4-compatible IPv6 address. It should
+        be converted to the IPv4 form.
+      */
+
+      *dst_length= sizeof (struct sockaddr_in);
+
+      memset(dst_ip4, 0, *dst_length);
+      dst_ip4->sin_family= AF_INET;
+      dst_ip4->sin_port= src_addr6->sin6_port;
+
+      /*
+        In an IPv4 mapped or compatible address, the last 32 bits represent
+        the IPv4 address. The byte orders for IPv6 and IPv4 addresses are
+        the same, so a simple copy is possible.
+      */
+      dst_ip4->sin_addr.s_addr= src_ip6_int32[3];
+    }
+    else
+    {
+      /* This is a "native" IPv6 address. */
+
+      memcpy(dst, src, src_length);
+      *dst_length= src_length;
+    }
+
+    break;
+  }
+#endif /* HAVE_IPV6 */
+  }
+}
+
+
+/**
+  Return IP address and port of a VIO client socket.
+
+  The function returns an IPv4 address if IPv6 support is disabled.
+
+  The function returns an IPv4 address if the client socket is associated
+  with an IPv4-compatible or IPv4-mapped IPv6 address. Otherwise, the native
+  IPv6 address is returned.
+*/
 
 my_bool vio_peer_addr(Vio * vio, char *ip_buffer, uint16 *port, 
                       size_t ip_buffer_size)
@@ -320,6 +407,20 @@ my_bool vio_peer_addr(Vio * vio, char *i
                        MY_SOCKET_FORMAT_VALUE(vio->sd)));
   if (vio->localhost)
   {
+    /*
+      Initialize vio->remote and vio->addLen. Set vio->remote to IPv4 loopback
+      address.
+    */
+    struct in_addr *ip4= &((struct sockaddr_in *)
+                           &(vio->remote))->sin_addr;
+
+    vio->remote.ss_family= AF_INET;
+    vio->addrLen= sizeof (struct sockaddr_in);
+
+    ip4->s_addr= htonl(INADDR_LOOPBACK);
+
+    /* Initialize ip_buffer and port. */
+
     strmov(ip_buffer,"127.0.0.1");
     *port= 0;
   }
@@ -328,19 +429,13 @@ my_bool vio_peer_addr(Vio * vio, char *i
     int err_code;
     char port_buffer[NI_MAXSERV];
     
-    struct sockaddr *addr= (struct sockaddr *) (&vio->remote);
-    size_socket addr_size = sizeof (vio->remote);
-
-    struct sockaddr *resolving_addr= addr;
-    uint resolving_addr_size= addr_size;
-
-#ifdef HAVE_STRUCT_IN6_ADDR
     struct sockaddr_storage addr_storage;
-#endif
+    struct sockaddr *addr= (struct sockaddr *) &addr_storage;
+    size_socket addr_length= sizeof (addr_storage);
 
-    /* Get sockaddr by socked fd (fill vio->remote) */
+    /* Get sockaddr by socked fd */
 
-    err_code= my_getpeername(vio->sd, addr, &addr_size);
+    err_code= my_getpeername(vio->sd, addr, &addr_length);
 
     if (err_code)
     {
@@ -348,55 +443,26 @@ my_bool vio_peer_addr(Vio * vio, char *i
       DBUG_RETURN(TRUE);
     }
 
-    vio->addrLen= (int) addr_size;
-
-    /*
-      Convert IPv6 address to IPv4 if it is IPv4-mapped or IPv4-compatible.
-    */
-
-#ifdef HAVE_STRUCT_IN6_ADDR
-    if (addr->sa_family == AF_INET6)
-    {
-      const struct sockaddr_in6 *addr6= (const struct sockaddr_in6 *) addr;
-      const struct in6_addr *ip6= &(addr6->sin6_addr);
-      const uint32 *ip6_int32= (uint32 *) ip6->s6_addr;
-
-      memset(&addr_storage, 0, sizeof (addr_storage));
+    /* Normalize IP address. */
 
-      if (IN6_IS_ADDR_V4MAPPED(ip6) || IN6_IS_ADDR_V4COMPAT(ip6))
-      {
-        struct sockaddr_in *ip4= (struct sockaddr_in *) &addr_storage;
-        ip4->sin_family= AF_INET;
-        ip4->sin_port= 0;
-
-        /*
-          In an IPv4 mapped or compatible address, the last 32 bits represent
-          the IPv4 address. The byte orders for IPv6 and IPv4 addresses are
-          the same, so a simple copy is possible.
-        */
-        ip4->sin_addr.s_addr= ip6_int32[3];
-
-        resolving_addr= (struct sockaddr *) ip4;
-        resolving_addr_size= sizeof (addr_storage);
-      }
-    }
-#endif /* HAVE_STRUCT_IN6_ADDR */
+    vio_get_normalized_ip(addr, addr_length,
+                          (struct sockaddr *) &vio->remote, &vio->addrLen);
 
     /* Get IP address & port number. */
+    
+    err_code= vio_getnameinfo((struct sockaddr *) &vio->remote,
+                              ip_buffer, ip_buffer_size,
+                              port_buffer, NI_MAXSERV,
+                              NI_NUMERICHOST | NI_NUMERICSERV);
 
-    err_code= getnameinfo(resolving_addr, resolving_addr_size,
-                          ip_buffer, ip_buffer_size,
-                          port_buffer, NI_MAXSERV,
-                          NI_NUMERICHOST | NI_NUMERICSERV);
-
-    if (err_code)
+    if(err_code)
     {
       DBUG_PRINT("exit", ("getnameinfo() gave error: %s",
                           gai_strerror(err_code)));
       DBUG_RETURN(TRUE);
     }
-
-    *port= (uint16) strtol(port_buffer, (char **) NULL, 10);
+    
+    *port= (uint16) strtol(port_buffer, NULL, 10);
   }
 
 
@@ -694,3 +760,82 @@ int vio_close_shared_memory(Vio * vio)
 }
 #endif /* HAVE_SMEM */
 #endif /* __WIN__ */
+
+
+/**
+  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= vio_getnameinfo(norm_addr, 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;
+}
+
+
+/**
+  This is a wrapper for the system getnameinfo(), because different OS
+  differ in the getnameinfo() implementation. For instance, Solaris 10
+  requires that the 2nd argument (salen) must match the actual size of the
+  struct sockaddr_storage passed to it.
+*/
+
+int vio_getnameinfo(const struct sockaddr *sa,
+                    char *hostname, size_t hostname_size,
+                    char *port, size_t port_size,
+                    int flags)
+{
+  int sa_length= 0;
+
+  switch (sa->sa_family) {
+  case AF_INET:
+    sa_length= sizeof (struct sockaddr_in);
+    break;
+
+#ifdef HAVE_IPV6
+  case AF_INET6:
+    sa_length= sizeof (struct sockaddr_in6);
+    break;
+#endif /* HAVE_IPV6 */
+  }
+
+  return getnameinfo(sa, sa_length,
+                     hostname, hostname_size,
+                     port, port_size,
+                     flags);
+}

Thread
bzr commit into mysql-5.1-telco-7.0 branch (frazer:2953) Bug#46336Frazer Clement11 Aug