summaryrefslogtreecommitdiff
path: root/vio
diff options
context:
space:
mode:
authorAlexander Nozdrin <alik@sun.com>2009-11-25 13:53:23 +0300
committerAlexander Nozdrin <alik@sun.com>2009-11-25 13:53:23 +0300
commit90f2a469c98f8235edb1d94a71c326c0ce459375 (patch)
treee44eeb900770af5a8a6c8712d985329fd1d7ba44 /vio
parent12b5d5b19a608d1697ba8d77632b9fc5b90976cd (diff)
downloadmariadb-git-90f2a469c98f8235edb1d94a71c326c0ce459375.tar.gz
Backport of WL#798 (MySQL IPv6 support) from 6.0.
The following 6.0 revisions were analyzed: - sp1r-brian@zim.(none)-20071228102738-21894 - sp1r-brian@zim.(none)-20071228121841-56447 - sp1r-brian@zim.(none)-20071228205403-56423 - sp1r-brian@zim.(none)-20071228221139-55341 - sp1r-brian@zim.(none)-20071228233443-55352 - sp1r-brian@zim.(none)-20071229094527-61763 - sp1r-brian@zim.(none)-20071230203739-61746 - sp1r-brian@zim.(none)-20080102213805-61741 - sp1r-brian@zim.(none)-20080103201041-61746 - sp1r-brian@zim.(none)-20080104234927-59875 - sp1r-brian@zim.(none)-20080105005827-59874 - sp1r-brian@zim.(none)-20080105012020-59865 - sp1r-brian@zim.(none)-20080106003858-59857 - sp1r-brian@zim.(none)-20080123195552-31680 - sp1r-brian@zim.(none)-20080124201848-29999 - sp1r-brian@zim.(none)-20080129075144-36991 - sp1r-cbell/Chuck@mysql_cab_desk.-20080215041806-21954 - sp1r-vvaintroub@wva.-20080212124547-06272 - sp1r-dfischer/mysqldev@mysql.com/production.mysql.com-20071223184258-15140 - sp1r-brian@zim.(none)-20080206122216-35774 - sp1r-baker@bk-internal.mysql.com-20080209005622-35947 - sp1r-baker@bk-internal.mysql.com-20080224215608-24613 - sp1r-baker@bk-internal.mysql.com-20080307170710-63543 - sp1r-baker@bk-internal.mysql.com-20080312233205-13069 - sp1r-Reggie@core.-20080402175211-28643 - kpettersson@mysql.com-20080901101150-4ne74r8v0492vv42 - alik@sun.com-20090805173811-9fzt0ymcp9tsvn7k - alik@sun.com-20090805173937-fcv1fdveodq5x9gb - alik@sun.com-20090805175009-g1od16i3t1xkw2qr - kostja@sun.com-20090805200643-j9i4fy7ii8ijho5c - alik@sun.com-20090807195303-j4fb5m4l1dgdahwo - alik@sun.com-20090808114848-3rkzr9kifrijzaqy - alik@sun.com-20090810041739-ugmx34h34uid8mox - alik@sun.com-20090810105306-rf43rfyzzblsy5e7 - alik@sun.com-20090810123113-ccdjwai68b5woqdm - alik@sun.com-20090811080423-gb7pibec1znaydzy - alik@sun.com-20090811082130-5uckar1vx3kdsw7g - alik@sun.com-20090812202051-uqkfwwxxcjvo5ean The following bugfixes are also backported within this patch: - Bug#34292: netdb.h missing in hostname.cc - Bug#39153: Failing to lookup a host name can lead to crash in current IPv6 implementation - Bug#38247: Server does not resolve connecting ip's - Bug#43006: main.skip_name_resolve fails on Windows in PB2 - Bug#45606: ACL requires IPv4-mapped addresses to be used - Bug#45584: Host name cache does not work as a cache
Diffstat (limited to 'vio')
-rw-r--r--vio/vio.c4
-rw-r--r--vio/vio_priv.h5
-rw-r--r--vio/viosocket.c254
3 files changed, 227 insertions, 36 deletions
diff --git a/vio/vio.c b/vio/vio.c
index 34e02adae3d..b7bac69fc3c 100644
--- a/vio/vio.c
+++ b/vio/vio.c
@@ -78,7 +78,6 @@ static void vio_init(Vio* vio, enum enum_vio_type type,
vio->was_interrupted=vio_was_interrupted;
vio->vioclose =vio_close_pipe;
vio->peer_addr =vio_peer_addr;
- vio->in_addr =vio_in_addr;
vio->vioblocking =vio_blocking;
vio->is_blocking =vio_is_blocking;
@@ -108,7 +107,6 @@ static void vio_init(Vio* vio, enum enum_vio_type type,
vio->was_interrupted=vio_was_interrupted;
vio->vioclose =vio_close_shared_memory;
vio->peer_addr =vio_peer_addr;
- vio->in_addr =vio_in_addr;
vio->vioblocking =vio_blocking;
vio->is_blocking =vio_is_blocking;
@@ -136,7 +134,6 @@ static void vio_init(Vio* vio, enum enum_vio_type type,
vio->was_interrupted=vio_was_interrupted;
vio->vioclose =vio_ssl_close;
vio->peer_addr =vio_peer_addr;
- vio->in_addr =vio_in_addr;
vio->vioblocking =vio_ssl_blocking;
vio->is_blocking =vio_is_blocking;
vio->timeout =vio_timeout;
@@ -156,7 +153,6 @@ static void vio_init(Vio* vio, enum enum_vio_type type,
vio->was_interrupted=vio_was_interrupted;
vio->vioclose =vio_close;
vio->peer_addr =vio_peer_addr;
- vio->in_addr =vio_in_addr;
vio->vioblocking =vio_blocking;
vio->is_blocking =vio_is_blocking;
vio->timeout =vio_timeout;
diff --git a/vio/vio_priv.h b/vio/vio_priv.h
index c1ac2ab8365..fd4d767b271 100644
--- a/vio/vio_priv.h
+++ b/vio/vio_priv.h
@@ -25,6 +25,11 @@
#include <m_string.h>
#include <violite.h>
+#ifndef _WIN_
+#include <sys/socket.h>
+#include <netdb.h>
+#endif
+
#ifdef _WIN32
void vio_win32_timeout(Vio *vio, uint which, uint timeout);
#endif
diff --git a/vio/viosocket.c b/vio/viosocket.c
index 51345d072b2..8dcf978f2fa 100644
--- a/vio/viosocket.c
+++ b/vio/viosocket.c
@@ -300,53 +300,209 @@ 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);
-my_bool vio_peer_addr(Vio * vio, char *buf, uint16 *port)
+ 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 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;
+}
+
+
+/**
+ 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");
+ /*
+ 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;
}
else
{
- 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);
}
- my_inet_ntoa(vio->remote.sin_addr,buf);
- *port= ntohs(vio->remote.sin_port);
- }
- DBUG_PRINT("exit", ("addr: %s", buf));
- DBUG_RETURN(0);
-}
+ /* Normalize IP address. */
-/*
- Get in_addr for a TCP/IP connection
+ vio_get_normalized_ip(addr, addr_length,
+ (struct sockaddr *) &vio->remote, &vio->addrLen);
- SYNOPSIS
- vio_in_addr()
- vio vio handle
- in put in_addr here
+ /* Get IP address & port number. */
- NOTES
- one must call vio_peer_addr() before calling this one
-*/
+ err_code= vio_getnameinfo((struct sockaddr *) &vio->remote,
+ ip_buffer, ip_buffer_size,
+ port_buffer, NI_MAXSERV,
+ NI_NUMERICHOST | NI_NUMERICSERV);
-void vio_in_addr(Vio *vio, struct in_addr *in)
-{
- DBUG_ENTER("vio_in_addr");
- if (vio->localhost)
- bzero((char*) in, sizeof(*in));
- else
- *in=vio->remote.sin_addr;
- DBUG_VOID_RETURN;
+ if (err_code)
+ {
+ DBUG_PRINT("exit", ("getnameinfo() gave error: %s",
+ gai_strerror(err_code)));
+ DBUG_RETURN(TRUE);
+ }
+
+ *port= (uint16) strtol(port_buffer, NULL, 10);
+ }
+
+ DBUG_PRINT("exit", ("Client IP address: %s; port: %d",
+ (const char *) ip_buffer,
+ (int) *port));
+ DBUG_RETURN(FALSE);
}
@@ -377,7 +533,8 @@ static my_bool socket_poll_read(my_socket sd, uint timeout)
FD_ZERO(&errorfds);
FD_SET(fd, &readfds);
FD_SET(fd, &errorfds);
- if ((res= select(fd, &readfds, NULL, &errorfds, &tm) <= 0))
+ /* The first argument is ignored on Windows, so a conversion to int is OK */
+ if ((res= select((int) fd, &readfds, NULL, &errorfds, &tm) <= 0))
{
DBUG_RETURN(res < 0 ? 0 : 1);
}
@@ -879,3 +1036,36 @@ ssize_t vio_pending(Vio *vio)
return 0;
}
+
+
+/**
+ 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);
+}