Commit 543e8672 authored by Davi Arnaut's avatar Davi Arnaut

Bug#45017: Failure to connect if hostname maps to multiple addresses

The problem is that the C API function mysql_real_connect
only attempts to connect to the first IP address returned
for a hostname. This can be a problem if a hostname maps
to multiple IP address and the server is not bound to the
first one that is returned.

The solution is to augment mysql_real_connect so that it
attempts to connect to all IPv4 addresses that a domain
name maps to. The function goes over the list of address
until a successful connection is established.

No test case is provided as its not possible to test this
automatically with the current testing infrastructure.

sql-common/client.c:
  The client will try to connect to each IPv4 address from
  the list of addresses that hostname maps to until a successful
  connection is established or there are no more address.
parent ad2b5063
...@@ -2027,6 +2027,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, ...@@ -2027,6 +2027,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
(!mysql->options.protocol || (!mysql->options.protocol ||
mysql->options.protocol == MYSQL_PROTOCOL_TCP)) mysql->options.protocol == MYSQL_PROTOCOL_TCP))
{ {
int status= -1;
unix_socket=0; /* This is not used */ unix_socket=0; /* This is not used */
if (!port) if (!port)
port=mysql_port; port=mysql_port;
...@@ -2052,6 +2053,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, ...@@ -2052,6 +2053,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
net->vio= vio_new(sock, VIO_TYPE_TCPIP, VIO_BUFFERED_READ); net->vio= vio_new(sock, VIO_TYPE_TCPIP, VIO_BUFFERED_READ);
bzero((char*) &sock_addr,sizeof(sock_addr)); bzero((char*) &sock_addr,sizeof(sock_addr));
sock_addr.sin_family = AF_INET; 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 The server name may be a host name or IP address
...@@ -2060,28 +2062,46 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, ...@@ -2060,28 +2062,46 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
if ((int) (ip_addr = inet_addr(host)) != (int) INADDR_NONE) if ((int) (ip_addr = inet_addr(host)) != (int) INADDR_NONE)
{ {
memcpy_fixed(&sock_addr.sin_addr,&ip_addr,sizeof(ip_addr)); 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 else
{ {
int tmp_errno; int i, tmp_errno;
struct hostent tmp_hostent,*hp; struct hostent tmp_hostent,*hp;
char buff2[GETHOSTBYNAME_BUFF_SIZE]; char buff2[GETHOSTBYNAME_BUFF_SIZE];
hp = my_gethostbyname_r(host,&tmp_hostent,buff2,sizeof(buff2), hp = my_gethostbyname_r(host,&tmp_hostent,buff2,sizeof(buff2),
&tmp_errno); &tmp_errno);
if (!hp)
/*
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.
*/
if (!hp || (hp->h_addrtype != AF_INET))
{ {
my_gethostbyname_r_free(); my_gethostbyname_r_free();
set_mysql_extended_error(mysql, CR_UNKNOWN_HOST, unknown_sqlstate, set_mysql_extended_error(mysql, CR_UNKNOWN_HOST, unknown_sqlstate,
ER(CR_UNKNOWN_HOST), host, tmp_errno); ER(CR_UNKNOWN_HOST), host, tmp_errno);
goto error; goto error;
} }
memcpy(&sock_addr.sin_addr, hp->h_addr,
min(sizeof(sock_addr.sin_addr), (size_t) hp->h_length)); for (i= 0; status && hp->h_addr_list[i]; i++)
{
IF_DBUG(char ipaddr[18];)
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)));
status= my_connect(sock, (struct sockaddr *) &sock_addr,
sizeof(sock_addr), mysql->options.connect_timeout);
}
my_gethostbyname_r_free(); my_gethostbyname_r_free();
} }
sock_addr.sin_port = (ushort) htons((ushort) port);
if (my_connect(sock,(struct sockaddr *) &sock_addr, sizeof(sock_addr), if (status)
mysql->options.connect_timeout))
{ {
DBUG_PRINT("error",("Got error %d on connect to '%s'",socket_errno, DBUG_PRINT("error",("Got error %d on connect to '%s'",socket_errno,
host)); host));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment