diff options
author | Davi Arnaut <Davi.Arnaut@Sun.COM> | 2009-07-30 21:28:43 -0300 |
---|---|---|
committer | Davi Arnaut <Davi.Arnaut@Sun.COM> | 2009-07-30 21:28:43 -0300 |
commit | 543e86728f02da79bb9fec4de35aae3d8759c88a (patch) | |
tree | 7104e8ab7b3d8c70adacdd42fca824303fda4d10 /sql-common | |
parent | ad2b50638461ba30162f17b62dbe477502135745 (diff) | |
download | mariadb-git-543e86728f02da79bb9fec4de35aae3d8759c88a.tar.gz |
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.
Diffstat (limited to 'sql-common')
-rw-r--r-- | sql-common/client.c | 34 |
1 files changed, 27 insertions, 7 deletions
diff --git a/sql-common/client.c b/sql-common/client.c index 2cbc15ad746..06ab3e0b632 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -2027,6 +2027,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, (!mysql->options.protocol || mysql->options.protocol == MYSQL_PROTOCOL_TCP)) { + int status= -1; unix_socket=0; /* This is not used */ if (!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); 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 @@ -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) { 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 { - int tmp_errno; + 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); - 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(); set_mysql_extended_error(mysql, CR_UNKNOWN_HOST, unknown_sqlstate, ER(CR_UNKNOWN_HOST), host, tmp_errno); 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(); } - sock_addr.sin_port = (ushort) htons((ushort) port); - if (my_connect(sock,(struct sockaddr *) &sock_addr, sizeof(sock_addr), - mysql->options.connect_timeout)) + + if (status) { DBUG_PRINT("error",("Got error %d on connect to '%s'",socket_errno, host)); |