#At file:///mnt/raid/alik/MySQL/bzr/bug38247/azalea-bf-bug45584/ based on revid:alik@stripped
2843 Alexander Nozdrin 2009-08-05
A patch for Bug#45606 (ACL requires IPv4-mapped addresses to be used).
The problem is that if IPv6 is available, getpeername() returns all
addresses in IPv6 form (IPv4 addresses are returned as IPv4-mapped).
The server uses the IP string returned by getpeername() to authorize
connected clients. So, if ACL contains an ordinary IPv4 address, it will
not be matched, because it is compared against IPv4-mapped address.
The fix is to use ordinary IPv4 addresses for IPv4-mapped or
IPv4-compatible IPv6 addresses.
A new build option has been also added to configure.in: --disable-ipv6.
If this option is specified, all IPv6-specific code will be compiled out
from the server. NOTE: if this option is misused, it may break the server:
in IPv6-enabled environments, getnameinfo() will still return IPv6 addresses,
but the server will not be able to handle that.
modified:
CMakeLists.txt
configure.in
vio/viosocket.c
=== modified file 'CMakeLists.txt'
--- a/CMakeLists.txt 2009-07-31 20:21:25 +0000
+++ b/CMakeLists.txt 2009-08-05 17:39:37 +0000
@@ -39,6 +39,9 @@ ADD_DEFINITIONS(-DDEFAULT_CHARSET_HOME="
ADD_DEFINITIONS(-DPACKAGE=mysql)
ADD_DEFINITIONS(-DSHAREDIR="share")
+# 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-07-28 14:16:37 +0000
+++ b/configure.in 2009-08-05 17:39:37 +0000
@@ -877,6 +877,42 @@ then
fi
#--------------------------------------------------------------------
+# Check for IPv6 support
+#--------------------------------------------------------------------
+
+AC_CHECK_HEADERS(netinet/in6.h)
+
+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 'vio/viosocket.c'
--- a/vio/viosocket.c 2009-05-23 17:29:37 +0000
+++ b/vio/viosocket.c 2009-08-05 17:39:37 +0000
@@ -306,53 +306,151 @@ my_socket vio_fd(Vio* vio)
return vio->sd;
}
-my_bool vio_peer_addr(Vio *vio, char *buf, uint16 *port, size_t buflen)
+/**
+ 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)
{
DBUG_ENTER("vio_peer_addr");
- DBUG_PRINT("enter", ("sd: %d", vio->sd));
+ DBUG_PRINT("enter", ("Client socked fd: %d", (int) vio->sd));
if (vio->localhost)
{
- strmov(buf, "127.0.0.1");
+ strmov(ip_buffer, "127.0.0.1");
*port= 0;
+ /* NOTE: vio->remote left uninitialized here. */
}
else
{
- int error;
- char port_buf[NI_MAXSERV];
- size_socket addrLen = sizeof(vio->remote);
- if (getpeername(vio->sd, (struct sockaddr *) (&vio->remote),
- &addrLen) != 0)
+ int err_code;
+ char port_buffer[NI_MAXSERV];
+
+ struct sockaddr_storage addr_storage;
+ struct sockaddr *addr= (struct sockaddr *) &addr_storage;
+ size_socket addr_length= sizeof (addr_storage);
+
+ /* Get sockaddr by socked fd. */
+
+ err_code= getpeername(vio->sd, addr, &addr_length);
+
+ if (err_code)
{
- DBUG_PRINT("exit", ("getpeername gave error: %d", socket_errno));
- DBUG_RETURN(1);
+ DBUG_PRINT("exit", ("getpeername() gave error: %d", socket_errno));
+ DBUG_RETURN(TRUE);
}
- vio->addrLen= (int)addrLen;
- if ((error= getnameinfo((struct sockaddr *)(&vio->remote),
- addrLen,
- buf, buflen,
- port_buf, NI_MAXSERV, NI_NUMERICHOST|NI_NUMERICSERV)))
+ /* Normalize IP address. */
+
+ vio_get_normalized_ip(addr, addr_length,
+ (struct sockaddr *) &vio->remote, &vio->addrLen);
+
+ /* Get IP address & port number. */
+
+ err_code= getnameinfo((struct sockaddr *) &vio->remote, vio->addrLen,
+ ip_buffer, ip_buffer_size,
+ port_buffer, NI_MAXSERV,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+
+ if (err_code)
{
- DBUG_PRINT("exit", ("getnameinfo gave error: %s",
- gai_strerror(error)));
- DBUG_RETURN(1);
+ DBUG_PRINT("exit", ("getnameinfo() gave error: %s",
+ gai_strerror(err_code)));
+ DBUG_RETURN(TRUE);
}
- *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");
+ *port= (uint16) strtol(port_buffer, NULL, 10);
}
- DBUG_PRINT("exit", ("addr: %s", buf));
- DBUG_RETURN(0);
+
+ DBUG_PRINT("exit", ("Client IP address: %s; port: %d",
+ (const char *) ip_buffer,
+ (int) *port));
+ DBUG_RETURN(FALSE);
}
Attachment: [text/bzr-bundle] bzr/alik@sun.com-20090805173937-fcv1fdveodq5x9gb.bundle
| Thread |
|---|
| • bzr commit into mysql branch (alik:2843) Bug#45606 | Alexander Nozdrin | 5 Aug |