Description:
On line 245 of sql/hostname.cc, memcmp is used to compare a struct sockaddr_storage* (in) to a struct sockaddr* (t_res->ai_addr). I believe this is invalid, because of the following code I added to debug this problem.
printf("Getaddrinfo result: %0*X\n", t_res->ai_addrlen, t_res->ai_addr);
printf("In: %0*X\n", addrLen, in);
printf("Getaddrinfo result: %0*X\n", 4, ((struct sockaddr_in*)t_res->ai_addr)->sin_addr);
printf("In: %0*X\n", 4, ((struct sockaddr_in*)in)->sin_addr);
printf("Getaddrinfo result: %p\n", &((struct sockaddr_in*)t_res->ai_addr)->sin_addr);
printf("In: %p\n", &((struct sockaddr_in*)in)->sin_addr);
This code prints (for example):
Getaddrinfo result: 0000000008062110
In: 0000000008047B84
Getaddrinfo result: FC5F5582
In: FC5F5582
Getaddrinfo result: 8062114
In: 8047b88
This shows that because t_res->ai_addr (a struct sockaddr*) has a pointer as part of its structure, using memcmp() to compare the contents of "t_res->ai_addr" and "in" is invalid.
The attached patch *should* fix the problem... but it doesn't.
How to repeat:
Build mysql-cluster-gpl-7.0.6 on Solaris (or install the precompiled binaries). Add a GRANT like:
GRANT ALL ON *.* to 'user'@'chasca.cs.umbc.edu';
Try to connect from the specified user and host. Get an error like this:
Access denied for user 'user'@'::ffff:130.85.95.252'
Suggested fix:
--- mysql-cluster-gpl-7.0.6/sql/hostname.cc Tue May 26 10:34:43 2009
+++ mysql-cluster-gpl-7.0.6-mod//sql/hostname.cc Tue Jul 28 17:22:14 2009
@@ -137,6 +148,18 @@
char * ip_to_hostname(struct sockaddr_storage *in, int addrLen, uint *errors)
{
char *name= NULL;
+ if (in->ss_family == AF_INET6)
+ {
+ if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6*)in)->sin6_addr))
+ {
+ struct sockaddr_in foo;
+ bzero(&foo, sizeof(struct sockaddr));
+ foo.sin_family = AF_INET;
+ foo.sin_port = ((struct sockaddr_in6*)in)->sin6_port;
+ memcpy(&foo.sin_addr.s_addr, &((struct sockaddr_in6*)in)->sin6_addr.s6_addr[12], 4);
+ in = (struct sockaddr_storage*)&foo;
+ }
+ }
struct addrinfo hints,*res_lst= NULL,*t_res;
int gxi_error;
@@ -149,7 +172,7 @@
/* Historical comparison for 127.0.0.1 */
gxi_error= getnameinfo((struct sockaddr *)in, addrLen,
hostname_buff, NI_MAXHOST,
- NULL, 0, NI_NUMERICHOST);
+ NULL, 0, 0);
if (gxi_error)
{
DBUG_PRINT("error",("getnameinfo returned %d", gxi_error));
@@ -242,8 +265,16 @@
/* Check that 'getaddrinfo' returned the used ip */
for (t_res= res_lst; t_res; t_res=t_res->ai_next)
{
- if (!memcmp(&(t_res->ai_addr), in,
- sizeof(struct sockaddr_storage) ) )
+ if ((in->ss_family == t_res->ai_addr->sa_family) &&
+ (((in->ss_family == AF_INET) &&
+ (((struct sockaddr_in*)in)->sin_addr.s_addr ==
+ ((struct sockaddr_in*)t_res->ai_addr)->sin_addr.s_addr))
+ ||
+ ((in->ss_family == AF_INET6) &&
+ (!memcmp(
+ (void*)&((struct sockaddr_in6*)in)->sin6_addr,
+ (void*)&((struct sockaddr_in6*)t_res->ai_addr)->sin6_addr,
+ sizeof(struct in6_addr))))))
{
add_hostname(in,name);
freeaddrinfo(res_lst);