summaryrefslogtreecommitdiff
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
commit7eb84da890698c3109795742c6f34d4580567b17 (patch)
treee44eeb900770af5a8a6c8712d985329fd1d7ba44
parent027a2fff1573550baae84ff97b962169a4961518 (diff)
downloadmariadb-git-7eb84da890698c3109795742c6f34d4580567b17.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
-rwxr-xr-xCMakeLists.txt3
-rw-r--r--configure.in42
-rw-r--r--include/config-win.h14
-rw-r--r--include/my_net.h16
-rw-r--r--include/violite.h24
-rw-r--r--mysql-test/t/skip_name_resolve.test2
-rw-r--r--scripts/mysql_system_tables_data.sql1
-rw-r--r--sql-common/client.c198
-rw-r--r--sql/client_settings.h4
-rw-r--r--sql/hostname.cc593
-rw-r--r--sql/mysql_priv.h12
-rw-r--r--sql/mysqld.cc114
-rw-r--r--sql/sql_acl.cc81
-rw-r--r--sql/sql_class.h1
-rw-r--r--sql/sql_connect.cc43
-rw-r--r--vio/vio.c4
-rw-r--r--vio/vio_priv.h5
-rw-r--r--vio/viosocket.c254
18 files changed, 1034 insertions, 377 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7a5ee9223bc..6e8c7db3c2a 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -39,6 +39,9 @@ ADD_DEFINITIONS(-DDEFAULT_CHARSET_HOME="c:/Program Files/MySQL/MySQL Server ${MY
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")
diff --git a/configure.in b/configure.in
index 361d117b2dc..8e7dbcfeda6 100644
--- a/configure.in
+++ b/configure.in
@@ -864,6 +864,42 @@ AC_CHECK_DECLS(MHA_MAPSIZE_VA,
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
#--------------------------------------------------------------------
@@ -961,12 +997,6 @@ AC_CHECK_TYPES([int8, uint8, int16, uint16, int32, uint32, int64, uint64,
uchar, uint, ulong],[],[], [
#include <sys/types.h>
])
-AC_CHECK_TYPES([in_addr_t], [], [], [
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-])
AC_CHECK_TYPES([fp_except], [], [], [
#include <sys/types.h>
#include <ieeefp.h>
diff --git a/include/config-win.h b/include/config-win.h
index 431bfcfa702..68248c09bef 100644
--- a/include/config-win.h
+++ b/include/config-win.h
@@ -20,6 +20,13 @@
#define BIG_TABLES
+/*
+ Minimal version of Windows we should be able to run on.
+ Currently Windows XP.
+*/
+#define _WIN32_WINNT 0x0501
+
+
#if defined(_MSC_VER) && _MSC_VER >= 1400
/* Avoid endless warnings about sprintf() etc. being unsafe. */
#define _CRT_SECURE_NO_DEPRECATE 1
@@ -27,6 +34,7 @@
#include <sys/locking.h>
#include <winsock2.h>
+#include <Ws2tcpip.h>
#include <fcntl.h>
#include <io.h>
#include <malloc.h>
@@ -88,6 +96,12 @@
#define S_IROTH S_IREAD /* for my_lib */
+/* Winsock2 constant (Vista SDK and later)*/
+#define IPPROTO_IPV6 41
+#ifndef IPV6_V6ONLY
+#define IPV6_V6ONLY 27
+#endif
+
#ifdef __BORLANDC__
#define FILE_BINARY O_BINARY /* my_fopen in binary mode */
#define O_TEMPORARY 0
diff --git a/include/my_net.h b/include/my_net.h
index 3af79ea3db5..18fb3db8e88 100644
--- a/include/my_net.h
+++ b/include/my_net.h
@@ -43,7 +43,7 @@ C_MODE_START
#include <sys/ioctl.h>
#endif
-#if !defined(__WIN__) && !defined(HAVE_BROKEN_NETINET_INCLUDES) && !defined(__BEOS__) && !defined(__NETWARE__)
+#if !defined(__WIN__) && !defined(HAVE_BROKEN_NETINET_INCLUDES) && !defined(__NETWARE__)
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
@@ -73,11 +73,6 @@ C_MODE_START
#define in_addr_t uint32
#endif
-/* On some operating systems (e.g. Solaris) INADDR_NONE is not defined */
-#ifndef INADDR_NONE
-#define INADDR_NONE -1 /* Error value from inet_addr */
-#endif
-
/* Thread safe or portable version of some functions */
void my_inet_ntoa(struct in_addr in, char *buf);
@@ -86,9 +81,6 @@ void my_inet_ntoa(struct in_addr in, char *buf);
Handling of gethostbyname_r()
*/
-#if !defined(HPUX10)
-struct hostent;
-#endif /* HPUX */
#if !defined(HAVE_GETHOSTBYNAME_R)
struct hostent *my_gethostbyname_r(const char *name,
struct hostent *result, char *buffer,
@@ -118,11 +110,5 @@ struct hostent *my_gethostbyname_r(const char *name,
#define GETHOSTBYNAME_BUFF_SIZE 2048
#endif
-/* On SCO you get a link error when refering to h_errno */
-#ifdef SCO
-#undef h_errno
-#define h_errno errno
-#endif
-
C_MODE_END
#endif
diff --git a/include/violite.h b/include/violite.h
index 3e8e430392b..eed6c46271f 100644
--- a/include/violite.h
+++ b/include/violite.h
@@ -81,13 +81,19 @@ int vio_errno(Vio*vio);
/* Get socket number */
my_socket vio_fd(Vio*vio);
/* Remote peer's address and name in text form */
-my_bool vio_peer_addr(Vio* vio, char *buf, uint16 *port);
-/* Remotes in_addr */
-void vio_in_addr(Vio *vio, struct in_addr *in);
+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_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);
+
+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
@@ -154,8 +160,7 @@ void vio_end(void);
#define vio_should_retry(vio) (vio)->should_retry(vio)
#define vio_was_interrupted(vio) (vio)->was_interrupted(vio)
#define vio_close(vio) ((vio)->vioclose)(vio)
-#define vio_peer_addr(vio, buf, prt) (vio)->peer_addr(vio, buf, prt)
-#define vio_in_addr(vio, in) (vio)->in_addr(vio, in)
+#define vio_peer_addr(vio, buf, prt, buflen) (vio)->peer_addr(vio, buf, prt, buflen)
#define vio_timeout(vio, which, seconds) (vio)->timeout(vio, which, seconds)
#define vio_poll_read(vio, timeout) (vio)->poll_read(vio, timeout)
#define vio_is_connected(vio) (vio)->is_connected(vio)
@@ -180,8 +185,9 @@ struct st_vio
HANDLE hPipe;
my_bool localhost; /* Are we from localhost? */
int fcntl_mode; /* Buffered fcntl(sd,F_GETFL) */
- struct sockaddr_in local; /* Local internet address */
- struct sockaddr_in remote; /* Remote internet address */
+ struct sockaddr_storage local; /* Local internet address */
+ struct sockaddr_storage remote; /* Remote internet address */
+ int addrLen; /* Length of remote address */
enum enum_vio_type type; /* Type of connection */
char desc[30]; /* String description */
char *read_buffer; /* buffer for vio_read_buff */
@@ -197,8 +203,8 @@ struct st_vio
my_bool (*is_blocking)(Vio*);
int (*viokeepalive)(Vio*, my_bool);
int (*fastsend)(Vio*);
- my_bool (*peer_addr)(Vio*, char *, uint16*);
- void (*in_addr)(Vio*, struct in_addr*);
+ my_bool (*peer_addr)(Vio*, char *, uint16*, size_t);
+ void (*in_addr)(Vio*, struct sockaddr_storage*);
my_bool (*should_retry)(Vio*);
my_bool (*was_interrupted)(Vio*);
int (*vioclose)(Vio*);
diff --git a/mysql-test/t/skip_name_resolve.test b/mysql-test/t/skip_name_resolve.test
index df010d15fa0..cd7ffe1690d 100644
--- a/mysql-test/t/skip_name_resolve.test
+++ b/mysql-test/t/skip_name_resolve.test
@@ -15,7 +15,7 @@ DROP USER mysqltest_1@'127.0.0.1/255.255.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
diff --git a/scripts/mysql_system_tables_data.sql b/scripts/mysql_system_tables_data.sql
index 03136fe9361..a7988b5198e 100644
--- a/scripts/mysql_system_tables_data.sql
+++ b/scripts/mysql_system_tables_data.sql
@@ -24,6 +24,7 @@ set @current_hostname= @@hostname;
INSERT INTO tmp_user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0 FROM dual WHERE LOWER( @current_hostname) != 'localhost';
REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
+REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
INSERT INTO tmp_user (host,user) VALUES ('localhost','');
INSERT INTO tmp_user (host,user) SELECT @current_hostname,'' FROM dual WHERE LOWER(@current_hostname ) != 'localhost';
INSERT INTO user SELECT * FROM tmp_user WHERE @had_user_table=0;
diff --git a/sql-common/client.c b/sql-common/client.c
index 5114b645818..1f9c00f93be 100644
--- a/sql-common/client.c
+++ b/sql-common/client.c
@@ -38,6 +38,10 @@
#include "mysql.h"
+#ifndef __WIN__
+#include <netdb.h>
+#endif
+
/* Remove client convenience wrappers */
#undef max_allowed_packet
#undef net_buffer_length
@@ -2227,9 +2231,6 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
{
char buff[NAME_LEN+USERNAME_LENGTH+100];
char *end,*host_info= NULL;
- my_socket sock;
- in_addr_t ip_addr;
- struct sockaddr_in sock_addr;
ulong pkt_length;
NET *net= &mysql->net;
#ifdef MYSQL_SERVER
@@ -2335,7 +2336,6 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
else
{
mysql->options.protocol=MYSQL_PROTOCOL_MEMORY;
- sock=0;
unix_socket = 0;
host=mysql->options.shared_memory_base_name;
my_snprintf(host_info=buff, sizeof(buff)-1,
@@ -2350,12 +2350,9 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
(unix_socket || mysql_unix_port) &&
(!host || !strcmp(host,LOCAL_HOST)))
{
- host=LOCAL_HOST;
- if (!unix_socket)
- unix_socket=mysql_unix_port;
- host_info=(char*) ER(CR_LOCALHOST_CONNECTION);
- DBUG_PRINT("info",("Using UNIX sock '%s'",unix_socket));
- if ((sock = socket(AF_UNIX,SOCK_STREAM,0)) == SOCKET_ERROR)
+ DBUG_PRINT("info", ("Using socket"));
+ my_socket sock= socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock == SOCKET_ERROR)
{
set_mysql_extended_error(mysql, CR_SOCKET_CREATE_ERROR,
unknown_sqlstate,
@@ -2363,12 +2360,28 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
socket_errno);
goto error;
}
+
net->vio= vio_new(sock, VIO_TYPE_SOCKET,
VIO_LOCALHOST | VIO_BUFFERED_READ);
- bzero((char*) &UNIXaddr,sizeof(UNIXaddr));
- UNIXaddr.sun_family = AF_UNIX;
+ if (!net->vio)
+ {
+ DBUG_PRINT("error",("Unknow protocol %d ", mysql->options.protocol));
+ set_mysql_error(mysql, CR_CONN_UNKNOW_PROTOCOL, unknown_sqlstate);
+ closesocket(sock);
+ goto error;
+ }
+
+ host= LOCAL_HOST;
+ if (!unix_socket)
+ unix_socket= mysql_unix_port;
+ host_info= (char*) ER(CR_LOCALHOST_CONNECTION);
+ DBUG_PRINT("info", ("Using UNIX sock '%s'", unix_socket));
+
+ bzero((char*) &UNIXaddr, sizeof(UNIXaddr));
+ UNIXaddr.sun_family= AF_UNIX;
strmake(UNIXaddr.sun_path, unix_socket, sizeof(UNIXaddr.sun_path)-1);
- if (my_connect(sock,(struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr),
+
+ if (my_connect(sock, (struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr),
mysql->options.connect_timeout))
{
DBUG_PRINT("error",("Got error %d on connect to local server",
@@ -2377,6 +2390,8 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
unknown_sqlstate,
ER(CR_CONNECTION_ERROR),
unix_socket, socket_errno);
+ vio_delete(net->vio);
+ net->vio= 0;
goto error;
}
mysql->options.protocol=MYSQL_PROTOCOL_SOCKET;
@@ -2387,7 +2402,6 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
(host && !strcmp(host,LOCAL_HOST_NAMEDPIPE)) ||
(! have_tcpip && (unix_socket || !host && is_NT()))))
{
- sock=0;
if ((hPipe= create_named_pipe(mysql, mysql->options.connect_timeout,
(char**) &host, (char**) &unix_socket)) ==
INVALID_HANDLE_VALUE)
@@ -2417,94 +2431,124 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
(!mysql->options.protocol ||
mysql->options.protocol == MYSQL_PROTOCOL_TCP))
{
- int status= -1;
+ struct addrinfo *res_lst, hints, *t_res;
+ int gai_errno;
+ char port_buf[NI_MAXSERV];
+ my_socket sock= SOCKET_ERROR;
+ int UNINIT_VAR(saved_error), status= -1;
+
unix_socket=0; /* This is not used */
+
if (!port)
- port=mysql_port;
+ port= mysql_port;
+
if (!host)
- host=LOCAL_HOST;
- my_snprintf(host_info=buff,sizeof(buff)-1,ER(CR_TCP_CONNECTION),host);
- DBUG_PRINT("info",("Server name: '%s'. TCP sock: %d", host,port));
+ host= LOCAL_HOST;
+
+ my_snprintf(host_info=buff, sizeof(buff)-1, ER(CR_TCP_CONNECTION), host);
+ DBUG_PRINT("info",("Server name: '%s'. TCP sock: %d", host, port));
#ifdef MYSQL_SERVER
thr_alarm_init(&alarmed);
thr_alarm(&alarmed, mysql->options.connect_timeout, &alarm_buff);
#endif
- /* _WIN64 ; Assume that the (int) range is enough for socket() */
- sock = (my_socket) socket(AF_INET,SOCK_STREAM,0);
+
+ DBUG_PRINT("info",("IP '%s'", "client"));
+
#ifdef MYSQL_SERVER
thr_end_alarm(&alarmed);
#endif
- if (sock == SOCKET_ERROR)
- {
- set_mysql_extended_error(mysql, CR_IPSOCK_ERROR, unknown_sqlstate,
- ER(CR_IPSOCK_ERROR), socket_errno);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype= SOCK_STREAM;
+ hints.ai_protocol= IPPROTO_TCP;
+ hints.ai_family= AF_UNSPEC;
+
+ DBUG_PRINT("info",("IPV6 getaddrinfo %s", host));
+ my_snprintf(port_buf, NI_MAXSERV, "%d", port);
+ gai_errno= getaddrinfo(host, port_buf, &hints, &res_lst);
+
+ if (gai_errno != 0)
+ {
+ /*
+ For DBUG we are keeping the right message but for client we default to
+ historical error message.
+ */
+ DBUG_PRINT("info",("IPV6 getaddrinfo error %d", gai_errno));
+ set_mysql_extended_error(mysql, CR_UNKNOWN_HOST, unknown_sqlstate,
+ ER(CR_UNKNOWN_HOST), host, errno);
+
goto error;
}
- net->vio= vio_new(sock, VIO_TYPE_TCPIP, VIO_BUFFERED_READ);
- bzero((char*) &sock_addr,sizeof(sock_addr));
- sock_addr.sin_family = AF_INET;
- sock_addr.sin_port = (ushort) htons((ushort) port);
/*
- The server name may be a host name or IP address
+ A hostname might map to multiple IP addresses (IPv4/IPv6). Go over the
+ list of IP addresses until a successful connection can be established.
*/
-
- if ((int) (ip_addr = inet_addr(host)) != (int) INADDR_NONE)
- {
- memcpy_fixed(&sock_addr.sin_addr,&ip_addr,sizeof(ip_addr));
- status= my_connect(sock, (struct sockaddr *) &sock_addr,
- sizeof(sock_addr), mysql->options.connect_timeout);
- }
- else
+ DBUG_PRINT("info", ("Try connect on all addresses for host."));
+ for (t_res= res_lst; t_res; t_res= t_res->ai_next)
{
- int i, tmp_errno;
- struct hostent tmp_hostent,*hp;
- char buff2[GETHOSTBYNAME_BUFF_SIZE];
- hp = my_gethostbyname_r(host,&tmp_hostent,buff2,sizeof(buff2),
- &tmp_errno);
+ DBUG_PRINT("info", ("Create socket, family: %d type: %d proto: %d",
+ t_res->ai_family, t_res->ai_socktype,
+ t_res->ai_protocol));
+ sock= socket(t_res->ai_family, t_res->ai_socktype, t_res->ai_protocol);
+ if (sock == SOCKET_ERROR)
+ {
+ saved_error= socket_errno;
+ continue;
+ }
+ DBUG_PRINT("info", ("Connect socket"));
+ status= my_connect(sock, t_res->ai_addr, t_res->ai_addrlen,
+ mysql->options.connect_timeout);
/*
- Don't attempt to connect to non IPv4 addresses as the client could
- end up sending information to a unknown server. For example, a IPv6
- address might be returned from gethostbyname depending on options
- set via the RES_OPTIONS environment variable.
+ Here we rely on my_connect() to return success only if the
+ connect attempt was really successful. Otherwise we would stop
+ trying another address, believing we were successful.
*/
- if (!hp || (hp->h_addrtype != AF_INET))
- {
- my_gethostbyname_r_free();
- set_mysql_extended_error(mysql, CR_UNKNOWN_HOST, unknown_sqlstate,
- ER(CR_UNKNOWN_HOST), host, tmp_errno);
- goto error;
- }
+ if (!status)
+ break;
- for (i= 0; status && hp->h_addr_list[i]; i++)
- {
- char ipaddr[18] __attribute__((unused));
- memcpy(&sock_addr.sin_addr, hp->h_addr_list[i],
- min(sizeof(sock_addr.sin_addr), (size_t) hp->h_length));
- DBUG_PRINT("info",("Trying %s...",
- (my_inet_ntoa(sock_addr.sin_addr, ipaddr), ipaddr)));
- /*
- Here we rely on my_connect() to return success only if the
- connect attempt was really successful. Otherwise we would stop
- trying another address, believing we were successful.
- */
- status= my_connect(sock, (struct sockaddr *) &sock_addr,
- sizeof(sock_addr), mysql->options.connect_timeout);
- }
+ /*
+ Save value as socket errno might be overwritten due to
+ calling a socket function below.
+ */
+ saved_error= socket_errno;
- my_gethostbyname_r_free();
+ DBUG_PRINT("info", ("No success, close socket, try next address."));
+ closesocket(sock);
+ }
+ DBUG_PRINT("info",
+ ("End of connect attempts, sock: %d status: %d error: %d",
+ sock, status, saved_error));
+
+ freeaddrinfo(res_lst);
+
+ if (sock == SOCKET_ERROR)
+ {
+ set_mysql_extended_error(mysql, CR_IPSOCK_ERROR, unknown_sqlstate,
+ ER(CR_IPSOCK_ERROR), saved_error);
+ goto error;
}
if (status)
{
- DBUG_PRINT("error",("Got error %d on connect to '%s'",socket_errno,
- host));
+ DBUG_PRINT("error",("Got error %d on connect to '%s'", saved_error, host));
set_mysql_extended_error(mysql, CR_CONN_HOST_ERROR, unknown_sqlstate,
- ER(CR_CONN_HOST_ERROR), host, socket_errno);
+ ER(CR_CONN_HOST_ERROR), host, saved_error);
+ goto error;
+ }
+
+ net->vio= vio_new(sock, VIO_TYPE_TCPIP, VIO_BUFFERED_READ);
+ if (! net->vio )
+ {
+ DBUG_PRINT("error",("Unknow protocol %d ", mysql->options.protocol));
+ set_mysql_error(mysql, CR_CONN_UNKNOW_PROTOCOL, unknown_sqlstate);
+ closesocket(sock);
goto error;
}
}
+
+ DBUG_PRINT("info", ("net->vio: %p", net->vio));
if (!net->vio)
{
DBUG_PRINT("error",("Unknow protocol %d ",mysql->options.protocol));
@@ -2640,14 +2684,14 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
if (client_flag & CLIENT_MULTI_STATEMENTS)
client_flag|= CLIENT_MULTI_RESULTS;
-#ifdef HAVE_OPENSSL
+#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
if (mysql->options.ssl_key || mysql->options.ssl_cert ||
mysql->options.ssl_ca || mysql->options.ssl_capath ||
mysql->options.ssl_cipher)
mysql->options.use_ssl= 1;
if (mysql->options.use_ssl)
client_flag|=CLIENT_SSL;
-#endif /* HAVE_OPENSSL */
+#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY*/
if (db)
client_flag|=CLIENT_CONNECT_WITH_DB;
@@ -2676,7 +2720,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
}
mysql->client_flag=client_flag;
-#ifdef HAVE_OPENSSL
+#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
if (client_flag & CLIENT_SSL)
{
/* Do the SSL layering. */
@@ -2727,7 +2771,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
}
}
-#endif /* HAVE_OPENSSL */
+#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
DBUG_PRINT("info",("Server version = '%s' capabilites: %lu status: %u client_flag: %lu",
mysql->server_version,mysql->server_capabilities,
diff --git a/sql/client_settings.h b/sql/client_settings.h
index fd50bfdbb88..7d103d5904d 100644
--- a/sql/client_settings.h
+++ b/sql/client_settings.h
@@ -30,10 +30,6 @@
#define set_sigpipe(mysql)
#define reset_sigpipe(mysql)
#define read_user_name(A) {}
-#define mysql_rpl_query_type(A,B) MYSQL_RPL_ADMIN
-#define mysql_master_send_query(A, B, C) 1
-#define mysql_slave_send_query(A, B, C) 1
-#define mysql_rpl_probe(mysql) 0
#undef HAVE_SMEM
#undef _CUSTOMCONFIG_
diff --git a/sql/hostname.cc b/sql/hostname.cc
index b498c548e61..cf35e60061f 100644
--- a/sql/hostname.cc
+++ b/sql/hostname.cc
@@ -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"
@@ -34,24 +34,54 @@ extern "C" { // Because of SCO 3.2V4.2
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
-#include <netdb.h>
#include <sys/utsname.h>
#endif // __WIN__
#ifdef __cplusplus
}
#endif
+/*
+ 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.
-class host_entry :public hash_filo_element
+ 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 in_addr *) 0)->s_addr)];
- 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_key[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;
-static pthread_mutex_t LOCK_hostname;
void hostname_cache_refresh()
{
@@ -60,219 +90,468 @@ 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 in_addr),NULL,
- (my_hash_free_key) free,
- &my_charset_bin)))
+ Host_entry 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,
+ NULL, (my_hash_free_key) free,
+ &my_charset_bin)))
return 1;
+
hostname_cache->clear();
- (void) pthread_mutex_init(&LOCK_hostname,MY_MUTEX_INIT_SLOW);
+
return 0;
}
void hostname_cache_free()
{
- if (hostname_cache)
- {
- (void) pthread_mutex_destroy(&LOCK_hostname);
- delete hostname_cache;
- hostname_cache= 0;
- }
+ delete hostname_cache;
+ hostname_cache= NULL;
}
+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);
-static void add_hostname(struct in_addr *in,const char *name)
+ 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)
{
- if (!(specialflag & SPECIAL_NO_HOST_CACHE))
+ return (Host_entry *) hostname_cache->search((uchar *) ip_key, 0);
+}
+
+static bool add_hostname_impl(const char *ip_key, const char *hostname)
+{
+ if (hostname_cache_search(ip_key))
+ 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_key, ip_key, 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->s_addr,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->s_addr, sizeof(in->s_addr));
- 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_key,
+ (const char *) hostname_copy));
}
-}
+ else
+ {
+ hostname_copy= NULL;
+
+ DBUG_PRINT("info", ("Adding '%s' -> NULL to the hostname cache...'",
+ (const char *) ip_key));
+ }
+
+ entry->hostname= hostname_copy;
+ entry->connect_errors= 0;
+ return hostname_cache->add(entry);
+}
-inline void add_wrong_ip(struct in_addr *in)
+static bool add_hostname(const char *ip_key, 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_key, hostname);
+
+ pthread_mutex_unlock(&hostname_cache->lock);
+
+ return err_status;
}
-void inc_host_errors(struct in_addr *in)
+void inc_host_errors(const char *ip_string)
{
+ if (!ip_string)
+ return;
+
+ char ip_key[HOST_ENTRY_KEY_SIZE];
+ prepare_hostname_cache_key(ip_string, ip_key);
+
pthread_mutex_lock(&hostname_cache->lock);
- host_entry *entry;
- if ((entry=(host_entry*) hostname_cache->search((uchar*) &in->s_addr,0)))
- entry->errors++;
+
+ Host_entry *entry= hostname_cache_search(ip_key);
+
+ if (entry)
+ entry->connect_errors++;
+
pthread_mutex_unlock(&hostname_cache->lock);
}
-void reset_host_errors(struct in_addr *in)
+
+void reset_host_errors(const char *ip_string)
{
+ if (!ip_string)
+ return;
+
+ char ip_key[HOST_ENTRY_KEY_SIZE];
+ prepare_hostname_cache_key(ip_string, ip_key);
+
pthread_mutex_lock(&hostname_cache->lock);
- host_entry *entry;
- if ((entry=(host_entry*) hostname_cache->search((uchar*) &in->s_addr,0)))
- entry->errors=0;
+
+ Host_entry *entry= hostname_cache_search(ip_key);
+
+ if (entry)
+ entry->connect_errors= 0;
+
pthread_mutex_unlock(&hostname_cache->lock);
}
-/* Deal with systems that don't defined INADDR_LOOPBACK */
-#ifndef INADDR_LOOPBACK
-#define INADDR_LOOPBACK 0x7f000001UL
-#endif
-char * ip_to_hostname(struct in_addr *in, 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 ntohl(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)
{
- uint i;
- host_entry *entry;
+ /*
+ 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;
+
+ 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;
+ bool err_status;
+
DBUG_ENTER("ip_to_hostname");
- *errors=0;
+ DBUG_PRINT("info", ("IP address: '%s'; family: %d.",
+ (const char *) ip_string,
+ (int) ip->sa_family));
- /* We always treat the loopback address as "localhost". */
- if (in->s_addr == htonl(INADDR_LOOPBACK)) // is expanded inline by gcc
- DBUG_RETURN((char *)my_localhost);
+ /* Check if we have loopback address (127.0.0.1 or ::1). */
+
+ if (is_ip_loopback(ip))
+ {
+ DBUG_PRINT("info", ("Loopback address detected."));
+
+ *connect_errors= 0; /* Do not count connect errors from localhost. */
+ *hostname= (char *) my_localhost;
+
+ DBUG_RETURN(FALSE);
+ }
+
+ /* Prepare host name cache key. */
+
+ 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. */
- /* Check first if we have name in cache */
if (!(specialflag & SPECIAL_NO_HOST_CACHE))
{
pthread_mutex_lock(&hostname_cache->lock);
- if ((entry=(host_entry*) hostname_cache->search((uchar*) &in->s_addr,0)))
+
+ Host_entry *entry= hostname_cache_search(ip_key);
+
+ if (entry)
{
- char *name;
- if (!entry->hostname)
- name=0; // Don't allow connection
- else
- name=my_strdup(entry->hostname,MYF(0));
- *errors= entry->errors;
+ *connect_errors= entry->connect_errors;
+ *hostname= NULL;
+
+ if (entry->hostname)
+ *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_key,
+ (const char *) (*hostname? *hostname : "null"),
+ (int) *connect_errors));
+
pthread_mutex_unlock(&hostname_cache->lock);
- DBUG_RETURN(name);
+
+ DBUG_RETURN(FALSE);
}
+
pthread_mutex_unlock(&hostname_cache->lock);
}
- struct hostent *hp, *check;
- char *name;
- LINT_INIT(check);
-#if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST)
- char buff[GETHOSTBYADDR_BUFF_SIZE],buff2[GETHOSTBYNAME_BUFF_SIZE];
- int tmp_errno;
- struct hostent tmp_hostent, tmp_hostent2;
-#ifdef HAVE_purify
- bzero(buff,sizeof(buff)); // Bug in purify
-#endif
- if (!(hp=gethostbyaddr_r((char*) in,sizeof(*in),
- AF_INET,
- &tmp_hostent,buff,sizeof(buff),&tmp_errno)))
- {
- DBUG_PRINT("error",("gethostbyaddr_r returned %d",tmp_errno));
- return 0;
- }
- if (!(check=my_gethostbyname_r(hp->h_name,&tmp_hostent2,buff2,sizeof(buff2),
- &tmp_errno)))
+ /*
+ 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_key));
+
+ err_code= vio_getnameinfo(ip, hostname_buffer, NI_MAXHOST, NULL, 0,
+ NI_NAMEREQD);
+
+ if (err_code == EAI_NONAME)
{
- DBUG_PRINT("error",("gethostbyname_r returned %d",tmp_errno));
/*
- Don't cache responses when the DSN 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.
+ There is no reverse address mapping for the IP address. A host name
+ can not be resolved.
*/
- if (tmp_errno == HOST_NOT_FOUND || tmp_errno == NO_DATA)
- add_wrong_ip(in);
- my_gethostbyname_r_free();
- DBUG_RETURN(0);
- }
- if (!hp->h_name[0])
- {
- DBUG_PRINT("error",("Got an empty hostname"));
- add_wrong_ip(in);
- my_gethostbyname_r_free();
- DBUG_RETURN(0); // Don't allow empty hostnames
- }
- if (!(name=my_strdup(hp->h_name,MYF(0))))
- {
- my_gethostbyname_r_free();
- DBUG_RETURN(0); // out of memory
+
+ DBUG_PRINT("error", ("IP address '%s' could not be resolved: "
+ "no reverse address mapping.",
+ (const char *) ip_key));
+
+ sql_print_warning("IP address '%s' could not be resolved: "
+ "no reverse address mapping.",
+ (const char *) ip_key);
+
+ err_status= add_hostname(ip_key, NULL);
+
+ *hostname= NULL;
+ *connect_errors= 0; /* New IP added to the cache. */
+
+ DBUG_RETURN(err_status);
}
- my_gethostbyname_r_free();
-#else
- pthread_mutex_lock(&LOCK_hostname);
- if (!(hp=gethostbyaddr((char*) in,sizeof(*in), AF_INET)))
+ else if (err_code)
{
- pthread_mutex_unlock(&LOCK_hostname);
- DBUG_PRINT("error",("gethostbyaddr returned %d",errno));
+ DBUG_PRINT("error", ("IP address '%s' could not be resolved: "
+ "getnameinfo() returned %d.",
+ (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_key,
+ (int) err_code);
- if (errno == HOST_NOT_FOUND || errno == NO_DATA)
- goto add_wrong_ip_and_return;
- /* Failure, don't cache responce */
- DBUG_RETURN(0);
+ DBUG_RETURN(TRUE);
}
- if (!hp->h_name[0]) // Don't allow empty hostnames
+
+ DBUG_PRINT("info", ("IP '%s' resolved to '%s'.",
+ (const char *) ip_key,
+ (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))
{
- pthread_mutex_unlock(&LOCK_hostname);
- DBUG_PRINT("error",("Got an empty hostname"));
- 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_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_key,
+ (const char *) hostname_buffer);
+
+ err_status= add_hostname(ip_key, NULL);
+
+ *hostname= NULL;
+ *connect_errors= 0; /* New IP added to the cache. */
+
+ DBUG_RETURN(err_status);
}
- if (!(name=my_strdup(hp->h_name,MYF(0))))
+
+ /* 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_family= AF_UNSPEC;
+
+ 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)
{
- pthread_mutex_unlock(&LOCK_hostname);
- DBUG_RETURN(0); // out of memory
+ /*
+ 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.
+ */
+
+ err_status= add_hostname(ip_key, NULL);
+
+ *hostname= NULL;
+ *connect_errors= 0; /* New IP added to the cache. */
+
+ DBUG_RETURN(err_status);
}
- check=gethostbyname(name);
- pthread_mutex_unlock(&LOCK_hostname);
- if (!check)
+ else if (err_code)
{
- DBUG_PRINT("error",("gethostbyname returned %d",errno));
- my_free(name,MYF(0));
- DBUG_RETURN(0);
+ DBUG_PRINT("error", ("getaddrinfo() failed with error code %d.", err_code));
+ DBUG_RETURN(TRUE);
}
-#endif
- /* Don't accept hostnames that starts with digits because they may be
- false ip:s */
- if (my_isdigit(&my_charset_latin1,name[0]))
+ /* 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 *pos;
- for (pos= name+1 ; my_isdigit(&my_charset_latin1,*pos); pos++) ;
- if (*pos == '.')
+ char ip_buffer[HOST_ENTRY_KEY_SIZE];
+
+ {
+ 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_key, ip_buffer) == 0)
{
- DBUG_PRINT("error",("mysqld doesn't accept hostnames that starts with a number followed by a '.'"));
- my_free(name,MYF(0));
- goto add_wrong_ip_and_return;
+ /* 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 'gethostbyname' returned the used ip */
- for (i=0; check->h_addr_list[i]; i++)
+ /* Log resolved IP-addresses if no match was found. */
+
+ if (!*hostname)
{
- if (*(uint32*)(check->h_addr_list)[i] == in->s_addr)
+ sql_print_information("Hostname '%s' does not resolve to '%s'.",
+ (const char *) hostname_buffer,
+ (const char *) ip_key);
+ 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);
- DBUG_RETURN(name);
+ char ip_buffer[HOST_ENTRY_KEY_SIZE];
+
+ 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);
}
}
- DBUG_PRINT("error",("Couldn't verify hostname with gethostbyname"));
- my_free(name,MYF(0));
-add_wrong_ip_and_return:
- 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. */
+
+ if (*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_key, NULL);
+ *hostname= NULL;
+ *connect_errors= 0;
+ }
+
+ DBUG_RETURN(err_status);
}
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index c6454679e28..552b834b379 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -53,6 +53,9 @@
#include "sql_array.h"
#include "sql_plugin.h"
#include "scheduler.h"
+#ifndef __WIN__
+#include <netdb.h>
+#endif
class Parser_state;
@@ -2235,10 +2238,11 @@ uint build_table_shadow_filename(char *buff, size_t bufflen,
#define FRM_ONLY (1 << 3)
/* from hostname.cc */
-struct in_addr;
-char * ip_to_hostname(struct in_addr *in,uint *errors);
-void inc_host_errors(struct in_addr *in);
-void reset_host_errors(struct in_addr *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);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 1cab245b317..5f00fa0ec64 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -106,7 +106,6 @@ extern "C" { // Because of SCO 3.2V4.2
#ifdef HAVE_SYS_UN_H
# include <sys/un.h>
#endif
-#include <netdb.h>
#ifdef HAVE_SELECT_H
# include <select.h>
#endif
@@ -176,7 +175,7 @@ static void registerwithneb();
static void getvolumename();
static void getvolumeID(BYTE *volumeName);
#endif /* __NETWARE__ */
-
+
#ifdef _AIX41
int initgroups(const char *,unsigned int);
@@ -390,7 +389,6 @@ static my_bool opt_short_log_format= 0;
static uint kill_cached_threads, wake_thread;
static ulong killed_threads, thread_created;
static ulong max_used_connections;
-static ulong my_bind_addr; /**< the address we bind to */
static volatile ulong cached_thread_count= 0;
static const char *sql_mode_str= "OFF";
/* Text representation for OPTIMIZER_SWITCH_DEFAULT */
@@ -1635,17 +1633,18 @@ static void set_root(const char *path)
#endif
}
+
static void network_init(void)
{
- struct sockaddr_in IPaddr;
#ifdef HAVE_SYS_UN_H
struct sockaddr_un UNIXaddr;
#endif
- int arg=1;
+ int arg;
int ret;
uint waited;
uint this_wait;
uint retry;
+ char port_buf[NI_MAXSERV];
DBUG_ENTER("network_init");
LINT_INIT(ret);
@@ -1656,26 +1655,65 @@ static void network_init(void)
if (mysqld_port != 0 && !opt_disable_networking && !opt_bootstrap)
{
+ struct addrinfo *ai, *a;
+ struct addrinfo hints;
+ int error;
DBUG_PRINT("general",("IP Socket is %d",mysqld_port));
- ip_sock = socket(AF_INET, SOCK_STREAM, 0);
+
+ bzero(&hints, sizeof (hints));
+ hints.ai_flags= AI_PASSIVE;
+ hints.ai_socktype= SOCK_STREAM;
+ hints.ai_family= AF_UNSPEC;
+
+ my_snprintf(port_buf, NI_MAXSERV, "%d", mysqld_port);
+ error= getaddrinfo(my_bind_addr_str, port_buf, &hints, &ai);
+ if (error != 0)
+ {
+ DBUG_PRINT("error",("Got error: %d from getaddrinfo()", error));
+ sql_perror(ER(ER_IPSOCK_ERROR)); /* purecov: tested */
+ unireg_abort(1); /* purecov: tested */
+ }
+
+ for (a= ai; a != NULL; a= a->ai_next)
+ {
+ ip_sock= socket(a->ai_family, a->ai_socktype, a->ai_protocol);
+ if (ip_sock != INVALID_SOCKET)
+ break;
+ }
+
if (ip_sock == INVALID_SOCKET)
{
DBUG_PRINT("error",("Got error: %d from socket()",socket_errno));
sql_perror(ER(ER_IPSOCK_ERROR)); /* purecov: tested */
unireg_abort(1); /* purecov: tested */
}
- bzero((char*) &IPaddr, sizeof(IPaddr));
- IPaddr.sin_family = AF_INET;
- IPaddr.sin_addr.s_addr = my_bind_addr;
- IPaddr.sin_port = (unsigned short) htons((unsigned short) mysqld_port);
#ifndef __WIN__
/*
We should not use SO_REUSEADDR on windows as this would enable a
user to open two mysqld servers with the same TCP/IP port.
*/
+ arg= 1;
(void) setsockopt(ip_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&arg,sizeof(arg));
#endif /* __WIN__ */
+
+#ifdef IPV6_V6ONLY
+ /*
+ For interoperability with older clients, IPv6 socket should
+ listen on both IPv6 and IPv4 wildcard addresses.
+ Turn off IPV6_V6ONLY option.
+
+ NOTE: this will work starting from Windows Vista only.
+ On Windows XP dual stack is not available, so it will not
+ listen on the corresponding IPv4-address.
+ */
+ if (a->ai_family == AF_INET6)
+ {
+ arg= 0;
+ (void) setsockopt(ip_sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg,
+ sizeof(arg));
+ }
+#endif
/*
Sometimes the port is not released fast enough when stopping and
restarting the server. This happens quite often with the test suite
@@ -1686,8 +1724,7 @@ static void network_init(void)
*/
for (waited= 0, retry= 1; ; retry++, waited+= this_wait)
{
- if (((ret= bind(ip_sock, my_reinterpret_cast(struct sockaddr *) (&IPaddr),
- sizeof(IPaddr))) >= 0) ||
+ if (((ret= bind(ip_sock, a->ai_addr, a->ai_addrlen)) >= 0 ) ||
(socket_errno != SOCKET_EADDRINUSE) ||
(waited >= mysqld_port_timeout))
break;
@@ -1695,6 +1732,7 @@ static void network_init(void)
this_wait= retry * retry / 3 + 1;
sleep(this_wait);
}
+ freeaddrinfo(ai);
if (ret < 0)
{
DBUG_PRINT("error",("Got error: %d from bind",socket_errno));
@@ -1716,7 +1754,6 @@ static void network_init(void)
if (Service.IsNT() && mysqld_unix_port[0] && !opt_bootstrap &&
opt_enable_named_pipe)
{
-
strxnmov(pipe_name, sizeof(pipe_name)-1, "\\\\.\\pipe\\",
mysqld_unix_port, NullS);
bzero((char*) &saPipeSecurity, sizeof(saPipeSecurity));
@@ -1782,6 +1819,7 @@ static void network_init(void)
UNIXaddr.sun_family = AF_UNIX;
strmov(UNIXaddr.sun_path, mysqld_unix_port);
(void) unlink(mysqld_unix_port);
+ arg= 1;
(void) setsockopt(unix_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&arg,
sizeof(arg));
umask(0);
@@ -4850,7 +4888,7 @@ static bool read_init_file(char *file_name)
When we enter this function, LOCK_thread_count is hold!
*/
-
+
void handle_connection_in_main_thread(THD *thd)
{
safe_mutex_assert_owner(&LOCK_thread_count);
@@ -4933,7 +4971,6 @@ void create_thread_to_handle_connection(THD *thd)
static void create_new_thread(THD *thd)
{
- NET *net=&thd->net;
DBUG_ENTER("create_new_thread");
/*
@@ -5003,6 +5040,7 @@ inline void kill_broken_server()
/* Handle new connections and spawn new process to handle them */
#ifndef EMBEDDED_LIBRARY
+
void handle_connections_sockets()
{
my_socket sock,new_sock;
@@ -5010,7 +5048,7 @@ void handle_connections_sockets()
uint max_used_connection= (uint) (max(ip_sock,unix_sock)+1);
fd_set readFDs,clientFDs;
THD *thd;
- struct sockaddr_in cAddr;
+ struct sockaddr_storage cAddr;
int ip_flags=0,socket_flags=0,flags;
st_vio *vio_tmp;
DBUG_ENTER("handle_connections_sockets");
@@ -5084,9 +5122,9 @@ void handle_connections_sockets()
#endif /* NO_FCNTL_NONBLOCK */
for (uint retry=0; retry < MAX_ACCEPT_RETRY; retry++)
{
- size_socket length=sizeof(struct sockaddr_in);
- new_sock = accept(sock, my_reinterpret_cast(struct sockaddr *) (&cAddr),
- &length);
+ size_socket length= sizeof(struct sockaddr_storage);
+ new_sock= accept(sock, (struct sockaddr *)(&cAddr),
+ &length);
#ifdef __NETWARE__
// TODO: temporary fix, waiting for TCP/IP fix - DEFECT000303149
if ((new_sock == INVALID_SOCKET) && (socket_errno == EINVAL))
@@ -5157,9 +5195,10 @@ void handle_connections_sockets()
{
size_socket dummyLen;
- struct sockaddr dummy;
- dummyLen = sizeof(struct sockaddr);
- if (getsockname(new_sock,&dummy, &dummyLen) < 0)
+ struct sockaddr_storage dummy;
+ dummyLen = sizeof(dummy);
+ if ( getsockname(new_sock,(struct sockaddr *)&dummy,
+ (SOCKET_SIZE_TYPE *)&dummyLen) < 0 )
{
sql_perror("Error on new connection socket");
(void) shutdown(new_sock, SHUT_RDWR);
@@ -5505,7 +5544,7 @@ errorconn:
NullS);
sql_perror(buff);
}
- if (handle_client_file_map)
+ if (handle_client_file_map)
CloseHandle(handle_client_file_map);
if (handle_client_map)
UnmapViewOfFile(handle_client_map);
@@ -7786,7 +7825,6 @@ static int mysql_init_variables(void)
strmov(server_version, MYSQL_SERVER_VERSION);
myisam_recover_options_str= sql_mode_str= "OFF";
myisam_stats_method_str= "nulls_unequal";
- my_bind_addr = htonl(INADDR_ANY);
threads.empty();
thread_cache.empty();
key_caches.empty();
@@ -8226,27 +8264,25 @@ mysqld_get_one_option(int optid,
my_use_symdir=0;
break;
case (int) OPT_BIND_ADDRESS:
- if ((my_bind_addr= (ulong) inet_addr(argument)) == INADDR_NONE)
{
- struct hostent *ent;
- if (argument[0])
- ent=gethostbyname(argument);
- else
+ struct addrinfo *res_lst, hints;
+
+ bzero(&hints, sizeof(struct addrinfo));
+ hints.ai_socktype= SOCK_STREAM;
+ hints.ai_protocol= IPPROTO_TCP;
+
+ if (getaddrinfo(argument, NULL, &hints, &res_lst) != 0)
{
- char myhostname[255];
- if (gethostname(myhostname,sizeof(myhostname)) < 0)
- {
- sql_perror("Can't start server: cannot get my own hostname!");
- return 1;
- }
- ent=gethostbyname(myhostname);
+ sql_print_error("Can't start server: cannot resolve hostname!");
+ return 1;
}
- if (!ent)
+
+ if (res_lst->ai_next)
{
- sql_perror("Can't start server: cannot resolve hostname!");
+ sql_print_error("Can't start server: bind-address refers to multiple interfaces!");
return 1;
}
- my_bind_addr = (ulong) ((in_addr*)ent->h_addr_list[0])->s_addr;
+ freeaddrinfo(res_lst);
}
break;
case (int) OPT_PID_FILE:
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 641423605aa..155e7298e90 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -1785,24 +1785,83 @@ static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
(ip && !wild_compare(ip, host->hostname, 0)));
}
+/**
+ Check if the given host name needs to be resolved or not.
+ Host name has to be resolved if it actually contains *name*.
+
+ For example:
+ 192.168.1.1 --> FALSE
+ 192.168.1.0/255.255.255.0 --> FALSE
+ % --> FALSE
+ 192.168.1.% --> FALSE
+ AB% --> FALSE
+
+ AAAAFFFF --> TRUE (Hostname)
+ AAAA:FFFF:1234:5678 --> FALSE
+ ::1 --> FALSE
+
+ This function does not check if the given string is a valid host name or
+ not. It assumes that the argument is a valid host name.
+
+ @param hostname the string to check.
+
+ @return a flag telling if the argument needs to be resolved or not.
+ @retval TRUE the argument is a host name and needs to be resolved.
+ @retval FALSE the argument is either an IP address, or a patter and
+ should not be resolved.
+*/
+
bool hostname_requires_resolving(const char *hostname)
{
- char cur;
if (!hostname)
return FALSE;
- size_t namelen= strlen(hostname);
- size_t lhlen= strlen(my_localhost);
- if ((namelen == lhlen) &&
- !my_strnncoll(system_charset_info, (const uchar *)hostname, namelen,
- (const uchar *)my_localhost, strlen(my_localhost)))
+
+ /* Check if hostname is the localhost. */
+
+ size_t hostname_len= strlen(hostname);
+ size_t localhost_len= strlen(my_localhost);
+
+ if (hostname == my_localhost ||
+ (hostname_len == localhost_len &&
+ !my_strnncoll(system_charset_info,
+ (const uchar *) hostname, hostname_len,
+ (const uchar *) my_localhost, strlen(my_localhost))))
+ {
return FALSE;
- for (; (cur=*hostname); hostname++)
+ }
+
+ /*
+ If the string contains any of {':', '%', '_', '/'}, it is definitely
+ not a host name:
+ - ':' means that the string is an IPv6 address;
+ - '%' or '_' means that the string is a pattern;
+ - '/' means that the string is an IPv4 network address;
+ */
+
+ for (const char *p= hostname; *p; ++p)
{
- if ((cur != '%') && (cur != '_') && (cur != '.') && (cur != '/') &&
- ((cur < '0') || (cur > '9')))
- return TRUE;
+ switch (*p) {
+ case ':':
+ case '%':
+ case '_':
+ case '/':
+ return FALSE;
+ }
}
- return FALSE;
+
+ /*
+ Now we have to tell a host name (ab.cd, 12.ab) from an IPv4 address
+ (12.34.56.78). The assumption is that if the string contains only
+ digits and dots, it is an IPv4 address. Otherwise -- a host name.
+ */
+
+ for (const char *p= hostname; *p; ++p)
+ {
+ if (*p != '.' && !my_isdigit(&my_charset_latin1, *p))
+ return TRUE; /* a "letter" has been found. */
+ }
+
+ return FALSE; /* all characters are either dots or digits. */
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 03a92c1a685..551a2372632 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1238,7 +1238,6 @@ public:
HASH user_vars; // hash for user variables
String packet; // dynamic buffer for network I/O
String convert_buffer; // buffer for charset conversions
- struct sockaddr_in remote; // client socket address
struct rand_struct rand; // used for authentication
struct system_variables variables; // Changeable local variables
struct system_status_var status_var; // Per thread statistic vars
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index f5f962a02a3..8ecafce6f93 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -377,7 +377,8 @@ check_user(THD *thd, enum enum_server_command command,
if (send_old_password_request(thd) ||
my_net_read(net) != SCRAMBLE_LENGTH_323 + 1)
{
- inc_host_errors(&thd->remote.sin_addr);
+ 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);
}
@@ -498,9 +499,9 @@ check_user(THD *thd, enum enum_server_command command,
thd->main_security_ctx.host_or_ip,
passwd_len ? ER(ER_YES) : ER(ER_NO));
/*
- log access denied messages to the error log when log-warnings = 2
+ Log access denied messages to the error log when log-warnings = 2
so that the overhead of the general query log is not required to track
- failed connections
+ failed connections.
*/
if (global_system_variables.log_warnings > 1)
{
@@ -666,9 +667,9 @@ static int check_connection(THD *thd)
if (!thd->main_security_ctx.host) // If TCP/IP connection
{
- char ip[30];
+ char ip[NI_MAXHOST];
- if (vio_peer_addr(net->vio, ip, &thd->peer_port))
+ if (vio_peer_addr(net->vio, ip, &thd->peer_port, NI_MAXHOST))
{
my_error(ER_BAD_HOST_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
return 1;
@@ -676,12 +677,15 @@ static int check_connection(THD *thd)
if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(MY_WME))))
return 1; /* The error is set by my_strdup(). */
thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip;
- vio_in_addr(net->vio,&thd->remote.sin_addr);
if (!(specialflag & SPECIAL_NO_RESOLVE))
{
- vio_in_addr(net->vio,&thd->remote.sin_addr);
- thd->main_security_ctx.host=
- ip_to_hostname(&thd->remote.sin_addr, &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)
{
@@ -714,7 +718,7 @@ static int check_connection(THD *thd)
thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
thd->main_security_ctx.ip= 0;
/* Reset sin_addr */
- bzero((char*) &thd->remote, sizeof(thd->remote));
+ bzero((char*) &net->vio->remote, sizeof(net->vio->remote));
}
vio_keepalive(net->vio, TRUE);
@@ -769,7 +773,8 @@ static int check_connection(THD *thd)
(pkt_len= my_net_read(net)) == packet_error ||
pkt_len < MIN_HANDSHAKE_SIZE)
{
- inc_host_errors(&thd->remote.sin_addr);
+ inc_host_errors(thd->main_security_ctx.ip);
+
my_error(ER_HANDSHAKE_ERROR, MYF(0),
thd->main_security_ctx.host_or_ip);
return 1;
@@ -779,7 +784,7 @@ static int check_connection(THD *thd)
#include "_cust_sql_parse.h"
#endif
if (connect_errors)
- reset_host_errors(&thd->remote.sin_addr);
+ 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(&thd->remote.sin_addr);
+ 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,8 @@ 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(&thd->remote.sin_addr);
+ inc_host_errors(thd->main_security_ctx.ip);
+
my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
return 1;
}
@@ -831,7 +837,8 @@ static int check_connection(THD *thd)
{
DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
pkt_len));
- inc_host_errors(&thd->remote.sin_addr);
+ inc_host_errors(thd->main_security_ctx.ip);
+
my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
return 1;
}
@@ -840,7 +847,8 @@ static int check_connection(THD *thd)
if (end >= (char*) net->read_pos+ pkt_len +2)
{
- inc_host_errors(&thd->remote.sin_addr);
+ inc_host_errors(thd->main_security_ctx.ip);
+
my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
return 1;
}
@@ -878,7 +886,8 @@ static int check_connection(THD *thd)
if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len)
{
- inc_host_errors(&thd->remote.sin_addr);
+ inc_host_errors(thd->main_security_ctx.ip);
+
my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
return 1;
}
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);
+}