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.
This commit is contained in:
parent
5f63862f0f
commit
35e5b9b8c9
@ -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,
|
|||||||
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,
|
|||||||
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,
|
|
||||||
|
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));
|
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));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user