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
This commit is contained in:
Alexander Nozdrin 2009-11-25 13:53:23 +03:00
commit 7eb84da890
18 changed files with 1034 additions and 377 deletions

View file

@ -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
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;
}
/*
A hostname might map to multiple IP addresses (IPv4/IPv6). Go over the
list of IP addresses until a successful connection can be established.
*/
DBUG_PRINT("info", ("Try connect on all addresses for host."));
for (t_res= res_lst; t_res; t_res= t_res->ai_next)
{
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);
/*
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 (!status)
break;
/*
Save value as socket errno might be overwritten due to
calling a socket function below.
*/
saved_error= socket_errno;
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), socket_errno);
ER(CR_IPSOCK_ERROR), saved_error);
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
*/
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 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);
/*
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;
}
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);
}
my_gethostbyname_r_free();
}
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,