=== modified file 'sql-common/client.c' --- sql-common/client.c 2009-10-01 07:16:52 +0000 +++ sql-common/client.c 2009-10-01 15:57:05 +0000 @@ -2048,9 +2048,11 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,cons (!mysql->options.protocol || mysql->options.protocol == MYSQL_PROTOCOL_TCP)) { - struct addrinfo *res_lst= NULL, hints; + struct addrinfo *res_lst= NULL, *client_bind_ai_lst= NULL, *t_res, hints; int gai_errno; char port_buf[NI_MAXSERV]; + my_socket sock= my_socket_create_invalid(); + int saved_error, status= -1, bindResult= 0; unix_socket=0; /* This is not used */ if (!port) @@ -2095,112 +2097,137 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,cons goto error; } - /* We only look at the first item (something to think about changing in the future) */ + /* Get address info for client bind name if it is provided */ + if (mysql->options.bind_name) { + int bind_gai_errno= 0; + + DBUG_PRINT("info",("Resolving addresses for client bind name : %s", + mysql->options.bind_name)); + /* Lookup address info for name */ + bind_gai_errno= getaddrinfo(mysql->options.bind_name, 0, + &hints, &client_bind_ai_lst); + if (bind_gai_errno) + { + DBUG_PRINT("info",("client bind getaddrinfo error %d", bind_gai_errno)); + set_mysql_extended_error(mysql, CR_UNKNOWN_HOST, unknown_sqlstate, + ER(CR_UNKNOWN_HOST), mysql->options.bind_name, errno); + + if (client_bind_ai_lst) + freeaddrinfo(client_bind_ai_lst); + + freeaddrinfo(res_lst); + goto error; + } + DBUG_PRINT("info", (" got address info for client bind name")); + } + + /* + A hostname might map to multiple IP addresses (IPv4/IPv6). Go over the + list of IP addresses until a successful connection can be established. + */ + for (t_res= res_lst; t_res; t_res= t_res->ai_next) { DBUG_PRINT("info",("Creating socket : Family %d, Type %d, Protocol %d", - res_lst->ai_family, res_lst->ai_socktype, res_lst->ai_protocol)); - sock= my_socket_create(res_lst->ai_family, res_lst->ai_socktype, res_lst->ai_protocol); + t_res->ai_family, t_res->ai_socktype, t_res->ai_protocol)); + sock= my_socket_create(t_res->ai_family, t_res->ai_socktype, t_res->ai_protocol); if (!my_socket_valid(sock)) { DBUG_PRINT("info",("Socket created was invalid")); - set_mysql_extended_error(mysql, CR_IPSOCK_ERROR, unknown_sqlstate, - ER(CR_IPSOCK_ERROR), socket_errno); - - freeaddrinfo(res_lst); - goto error; + /* Try next address if there is one */ + saved_error= socket_errno; + continue; } - if (mysql->options.bind_name) { + if (client_bind_ai_lst) { /* TODOs for client bind: - check error codes - don't use socket for localhost if this option is given */ - struct addrinfo* clientBindAddrInfo= NULL; - struct addrinfo* currAddr= NULL; - int gai_errno= 0; - int bindResult= -1; - - DBUG_PRINT("info",("Attempting client bind to address : %s", - mysql->options.bind_name)); - /* Lookup address info for name */ - gai_errno= getaddrinfo(mysql->options.bind_name, 0, - &hints, &clientBindAddrInfo); - if (gai_errno) - { - DBUG_PRINT("info",("getaddrinfo error %d", gai_errno)); - set_mysql_extended_error(mysql, CR_UNKNOWN_HOST, unknown_sqlstate, - ER(CR_UNKNOWN_HOST), mysql->options.bind_name, errno); - - if (clientBindAddrInfo) - freeaddrinfo(clientBindAddrInfo); - - freeaddrinfo(res_lst); - goto error; - } - - DBUG_PRINT("info",("Got address info for address, attempting bind")); - + struct addrinfo* curr_bind_ai= NULL; + DBUG_PRINT("info", ("Attempting to bind socket to bind address(es)")); + /* We'll attempt to bind to each of the addresses returned, until * we find one that works */ - currAddr= clientBindAddrInfo; + curr_bind_ai= client_bind_ai_lst; - while((currAddr != NULL) && (bindResult != 0)) + while (curr_bind_ai != NULL) { /* Bind the socket to the given address */ bindResult= my_bind(sock, - currAddr->ai_addr, - currAddr->ai_addrlen); + curr_bind_ai->ai_addr, + curr_bind_ai->ai_addrlen); - if (bindResult) - { - DBUG_PRINT("info",("bind failed, attempting another bind address")); - /* Problem with the bind, move to next address if present */ - currAddr= currAddr->ai_next; - } - } + if (!bindResult) + break; /* Success */ - freeaddrinfo(clientBindAddrInfo); + DBUG_PRINT("info", ("bind failed, attempting another bind address")); + /* Problem with the bind, move to next address if present */ + curr_bind_ai= curr_bind_ai->ai_next; + } if (bindResult) { - set_mysql_extended_error(mysql, CR_IPSOCK_ERROR, unknown_sqlstate, - ER(CR_IPSOCK_ERROR), mysql->options.bind_name, errno); - freeaddrinfo(res_lst); - goto error; + DBUG_PRINT("info", ("All bind attempts with this address failed")); + saved_error= errno; + /* Try next address if available */ + continue; + } DBUG_PRINT("info", ("Successfully bound client side of socket")); } - 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); - my_socket_close(sock); - my_socket_invalidate(&sock); - freeaddrinfo(res_lst); - goto error; - } + status= my_connect(MY_SOCKET_FORMAT_VALUE(sock), t_res->ai_addr, t_res->ai_addrlen, + mysql->options.connect_timeout); + + if (!status) + break; /* Success */ - if (my_connect(MY_SOCKET_FORMAT_VALUE(sock), res_lst->ai_addr, res_lst->ai_addrlen, - mysql->options.connect_timeout)) - { - DBUG_PRINT("error",("Got error %d on connect to '%s'",socket_errno, - host)); - set_mysql_extended_error(mysql, CR_CONN_HOST_ERROR, unknown_sqlstate, - ER(CR_CONN_HOST_ERROR), host, socket_errno); - vio_delete(net->vio); - net->vio = 0; - my_socket_close(sock); - my_socket_invalidate(&sock); - freeaddrinfo(res_lst); + /* + Save value as socket errno might be overwritten due to + calling a socket function below. + */ + saved_error= socket_errno; - goto error; - } + my_socket_close(sock); + my_socket_invalidate(&sock); } freeaddrinfo(res_lst); + freeaddrinfo(client_bind_ai_lst); + + if (!my_socket_valid(sock)) + { + set_mysql_extended_error(mysql, CR_IPSOCK_ERROR, unknown_sqlstate, + ER(CR_IPSOCK_ERROR), saved_error); + goto error; + } + + if (bindResult) + { + set_mysql_extended_error(mysql, CR_IPSOCK_ERROR, unknown_sqlstate, + ER(CR_IPSOCK_ERROR), mysql->options.bind_name, saved_error); + goto error; + } + + if (status) + { + 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, saved_error); + goto error; + } + + /* Managed to connect if we get here, now setup VIO */ + 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); + my_socket_close(sock); + my_socket_invalidate(&sock); + goto error; + } } if (!net->vio)