--- mariadb-10.0.1/sql/mysqld.cc.orig 2013-04-02 18:27:10.417102860 -0700 +++ mariadb-10.0.1/sql/mysqld.cc 2013-04-02 19:35:12.267110705 -0700 @@ -1281,7 +1281,7 @@ #endif /* !EMBEDDED_LIBRARY */ #endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */ -static MYSQL_SOCKET unix_sock, base_ip_sock, extra_ip_sock; +static MYSQL_SOCKET unix_sock, base_ip_sock, extra_ip_sock, ip_sock; struct my_rnd_struct sql_rand; ///< used by sql_class.cc:THD::THD() #ifndef EMBEDDED_LIBRARY @@ -2071,6 +2071,16 @@ ** Init IP and UNIX socket ****************************************************************************/ +/** + MY_BIND_ALL_ADDRESSES defines a special value for the bind-address option, + which means that the server should listen to all available network addresses, + both IPv6 (if available) and IPv4. + + Basically, this value instructs the server to make an attempt to bind the + server socket to '::' address, and rollback to '0.0.0.0' if the attempt fails. +*/ +const char *MY_BIND_ALL_ADDRESSES= "*"; + #ifndef EMBEDDED_LIBRARY static void set_ports() { @@ -2243,130 +2253,67 @@ #endif } -/** - Activate usage of a tcp port -*/ -static MYSQL_SOCKET activate_tcp_port(uint port) +static MYSQL_SOCKET create_socket(const struct addrinfo *addrinfo_list, + int addr_family, + struct addrinfo **use_addrinfo) { - struct addrinfo *ai, *a; - struct addrinfo hints; - int error; - int arg; - char port_buf[NI_MAXSERV]; - MYSQL_SOCKET ip_sock= MYSQL_INVALID_SOCKET; - DBUG_ENTER("activate_tcp_port"); - DBUG_PRINT("general",("IP Socket is %d",port)); - - bzero(&hints, sizeof (hints)); - hints.ai_flags= AI_PASSIVE; - hints.ai_socktype= SOCK_STREAM; - hints.ai_family= AF_UNSPEC; + MYSQL_SOCKET sock= MYSQL_INVALID_SOCKET; - my_snprintf(port_buf, NI_MAXSERV, "%d", port); - error= getaddrinfo(my_bind_addr_str, port_buf, &hints, &ai); - if (error != 0) + for (const struct addrinfo *cur_ai= addrinfo_list; cur_ai != NULL; + cur_ai= cur_ai->ai_next) { - DBUG_PRINT("error",("Got error: %d from getaddrinfo()", error)); - sql_perror(ER_DEFAULT(ER_IPSOCK_ERROR)); /* purecov: tested */ - unireg_abort(1); /* purecov: tested */ - } + if (cur_ai->ai_family != addr_family) + continue; - for (a= ai; a != NULL; a= a->ai_next) - { - ip_sock= mysql_socket_socket(key_socket_tcpip, a->ai_family, - a->ai_socktype, a->ai_protocol); - if (mysql_socket_getfd(ip_sock) != INVALID_SOCKET) - break; - } + sock= mysql_socket_socket(key_socket_tcpip, cur_ai->ai_family, + cur_ai->ai_socktype, cur_ai->ai_protocol); - if (mysql_socket_getfd(ip_sock) == INVALID_SOCKET) - { - DBUG_PRINT("error",("Got error: %d from socket()",socket_errno)); - sql_perror(ER_DEFAULT(ER_IPSOCK_ERROR)); /* purecov: tested */ - unireg_abort(1); /* purecov: tested */ - } + char ip_addr[INET6_ADDRSTRLEN]; - mysql_socket_set_thread_owner(ip_sock); + if (vio_getnameinfo(cur_ai->ai_addr, ip_addr, sizeof (ip_addr), + NULL, 0, NI_NUMERICHOST)) + { + ip_addr[0]= 0; + } -#ifndef __WIN__ - /* - We should not use SO_REUSEADDR on windows as this would enable a - user to open two mysqld servers with the same TCP/IP port. - */ - arg= 1; - (void) mysql_socket_setsockopt(ip_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&arg, - sizeof(arg)); -#endif /* __WIN__ */ + if (mysql_socket_getfd(sock) == INVALID_SOCKET) + { + sql_print_error("Failed to create a socket for %s '%s': errno: %d.", + (addr_family == AF_INET) ? "IPv4" : "IPv6", + (const char *) ip_addr, + (int) socket_errno); -#ifdef IPV6_V6ONLY - /* - For interoperability with older clients, IPv6 socket should - listen on both IPv6 and IPv4 wildcard addresses. - Turn off IPV6_V6ONLY option. - - NOTE: this will work starting from Windows Vista only. - On Windows XP dual stack is not available, so it will not - listen on the corresponding IPv4-address. - */ - if (a->ai_family == AF_INET6) - { - arg= 0; - (void) mysql_socket_setsockopt(ip_sock, IPPROTO_IPV6, IPV6_V6ONLY, - (char*)&arg, sizeof(arg)); - } -#endif - /* - Sometimes the port is not released fast enough when stopping and - restarting the server. This happens quite often with the test suite - on busy Linux systems. Retry to bind the address at these intervals: - Sleep intervals: 1, 2, 4, 6, 9, 13, 17, 22, ... - Retry at second: 1, 3, 7, 13, 22, 35, 52, 74, ... - Limit the sequence by mysqld_port_timeout (set --port-open-timeout=#). - */ - int ret; - uint waited, retry, this_wait; - for (waited= 0, retry= 1; ; retry++, waited+= this_wait) - { - if (((ret= mysql_socket_bind(ip_sock, a->ai_addr, a->ai_addrlen)) >= 0 ) || - (socket_errno != SOCKET_EADDRINUSE) || - (waited >= mysqld_port_timeout)) - break; - sql_print_information("Retrying bind on TCP/IP port %u", port); - this_wait= retry * retry / 3 + 1; - sleep(this_wait); - } - freeaddrinfo(ai); - if (ret < 0) - { - char buff[100]; - sprintf(buff, "Can't start server: Bind on TCP/IP port. Got error: %d", - (int) socket_errno); - sql_perror(buff); - sql_print_error("Do you already have another mysqld server running on " - "port: %u ?", port); - unireg_abort(1); - } - if (mysql_socket_listen(ip_sock,(int) back_log) < 0) - { - sql_perror("Can't start server: listen() on TCP/IP port"); - sql_print_error("listen() on TCP/IP failed with error %d", - socket_errno); - unireg_abort(1); + continue; + } + + sql_print_information("Server socket created on IP: '%s'.", + (const char *) ip_addr); + + *use_addrinfo= (struct addrinfo *)cur_ai; + return sock; } - DBUG_RETURN(ip_sock); + + return MYSQL_INVALID_SOCKET; } + static void network_init(void) { #ifdef HAVE_SYS_UN_H - struct sockaddr_un UNIXaddr; - int arg; + struct sockaddr_un UNIXaddr; #endif + int arg; + int ret; + uint waited; + uint this_wait; + uint retry; + char port_buf[NI_MAXSERV]; DBUG_ENTER("network_init"); + LINT_INIT(ret); if (MYSQL_CALLBACK_ELSE(thread_scheduler, init, (), 0)) - unireg_abort(1); /* purecov: inspected */ + unireg_abort(1); /* purecov: inspected */ set_ports(); @@ -2374,16 +2321,200 @@ { report_port= mysqld_port; } + #ifndef DBUG_OFF if (!opt_disable_networking) DBUG_ASSERT(report_port != 0); #endif - if (!opt_disable_networking && !opt_bootstrap) + + if (mysqld_port != 0 && !opt_disable_networking && !opt_bootstrap) { - if (mysqld_port) - base_ip_sock= activate_tcp_port(mysqld_port); - if (mysqld_extra_port) - extra_ip_sock= activate_tcp_port(mysqld_extra_port); + struct addrinfo *ai; + struct addrinfo hints; + + const char *bind_address_str= NULL; + const char *ipv6_all_addresses= "::"; + const char *ipv4_all_addresses= "0.0.0.0"; + + sql_print_information("Server hostname (bind-address): '%s'; port: %d", + my_bind_addr_str, mysqld_port); + + // Get list of IP-addresses associated with the bind-address. + + memset(&hints, 0, sizeof (hints)); + hints.ai_flags= AI_PASSIVE; + hints.ai_socktype= SOCK_STREAM; + hints.ai_family= AF_UNSPEC; + + my_snprintf(port_buf, NI_MAXSERV, "%d", mysqld_port); + + if (strcasecmp(my_bind_addr_str, MY_BIND_ALL_ADDRESSES) == 0) + { + /* + That's the case when bind-address is set to a special value ('*'), + meaning "bind to all available IP addresses". If the box supports + the IPv6 stack, that means binding to '::'. If only IPv4 is available, + bind to '0.0.0.0'. + */ + + bool ipv6_available= false; + + if (!getaddrinfo(ipv6_all_addresses, port_buf, &hints, &ai)) + { + /* + IPv6 might be available (the system might be able to resolve an IPv6 + address, but not be able to create an IPv6-socket). Try to create a + dummy IPv6-socket. Do not instrument that socket by P_S. + */ + + MYSQL_SOCKET s= mysql_socket_socket(0, AF_INET6, SOCK_STREAM, 0); + + ipv6_available= mysql_socket_getfd(s) != INVALID_SOCKET; + + mysql_socket_close(s); + } + + if (ipv6_available) + { + sql_print_information("IPv6 is available."); + + // Address info (ai) for IPv6 address is already set. + + bind_address_str= ipv6_all_addresses; + } + else + { + sql_print_information("IPv6 is not available."); + + // Retrieve address info (ai) for IPv4 address. + + if (getaddrinfo(ipv4_all_addresses, port_buf, &hints, &ai)) + { + sql_perror(ER_DEFAULT(ER_IPSOCK_ERROR)); + sql_print_error("Can't start server: cannot resolve hostname!"); + unireg_abort(1); + } + + bind_address_str= ipv4_all_addresses; + } + } + else + { + if (getaddrinfo(my_bind_addr_str, port_buf, &hints, &ai)) + { + sql_perror(ER_DEFAULT(ER_IPSOCK_ERROR)); /* purecov: tested */ + sql_print_error("Can't start server: cannot resolve hostname!"); + unireg_abort(1); /* purecov: tested */ + } + + bind_address_str= my_bind_addr_str; + } + + // Log all the IP-addresses. + for (struct addrinfo *cur_ai= ai; cur_ai != NULL; cur_ai= cur_ai->ai_next) + { + char ip_addr[INET6_ADDRSTRLEN]; + + if (vio_getnameinfo(cur_ai->ai_addr, ip_addr, sizeof (ip_addr), + NULL, 0, NI_NUMERICHOST)) + { + sql_print_error("Fails to print out IP-address."); + continue; + } + + sql_print_information(" - '%s' resolves to '%s';", + bind_address_str, ip_addr); + } + + /* + If the 'bind-address' option specifies the hostname, which resolves to + multiple IP-address, use the following rule: + - if there are IPv6-addresses, use the first IPv6-address + returned by getaddrinfo(); + - if there are IPv4-addresses, use the first IPv4-address + returned by getaddrinfo(); + */ + + struct addrinfo *a; + ip_sock= create_socket(ai, AF_INET6, &a); + + if (mysql_socket_getfd(ip_sock) == INVALID_SOCKET) + ip_sock= create_socket(ai, AF_INET, &a); + + // Report user-error if we failed to create a socket. + if (mysql_socket_getfd(ip_sock) == INVALID_SOCKET) + { + sql_perror(ER_DEFAULT(ER_IPSOCK_ERROR)); /* purecov: tested */ + unireg_abort(1); /* purecov: tested */ + } + + mysql_socket_set_thread_owner(ip_sock); + +#ifndef __WIN__ + /* + We should not use SO_REUSEADDR on windows as this would enable a + user to open two mysqld servers with the same TCP/IP port. + */ + arg= 1; + (void) mysql_socket_setsockopt(ip_sock, SOL_SOCKET, SO_REUSEADDR, (char*)&arg,sizeof(arg)); +#endif /* __WIN__ */ + +#ifdef IPV6_V6ONLY + /* + For interoperability with older clients, IPv6 socket should + listen on both IPv6 and IPv4 wildcard addresses. + Turn off IPV6_V6ONLY option. + + NOTE: this will work starting from Windows Vista only. + On Windows XP dual stack is not available, so it will not + listen on the corresponding IPv4-address. + */ + if (a->ai_family == AF_INET6) + { + arg= 0; + + if (mysql_socket_setsockopt(ip_sock, IPPROTO_IPV6, IPV6_V6ONLY, + (char *) &arg, sizeof (arg))) + { + sql_print_warning("Failed to reset IPV6_V6ONLY flag (error: %d). " + "The server will listen to IPv6 addresses only.", + (int) socket_errno); + } + } +#endif + /* + Sometimes the port is not released fast enough when stopping and + restarting the server. This happens quite often with the test suite + on busy Linux systems. Retry to bind the address at these intervals: + Sleep intervals: 1, 2, 4, 6, 9, 13, 17, 22, ... + Retry at second: 1, 3, 7, 13, 22, 35, 52, 74, ... + Limit the sequence by mysqld_port_timeout (set --port-open-timeout=#). + */ + for (waited= 0, retry= 1; ; retry++, waited+= this_wait) + { + if (((ret= mysql_socket_bind(ip_sock, a->ai_addr, a->ai_addrlen)) >= 0 ) || + (socket_errno != SOCKET_EADDRINUSE) || + (waited >= mysqld_port_timeout)) + break; + sql_print_information("Retrying bind on TCP/IP port %u", mysqld_port); + this_wait= retry * retry / 3 + 1; + sleep(this_wait); + } + freeaddrinfo(ai); + if (ret < 0) + { + DBUG_PRINT("error",("Got error: %d from bind",socket_errno)); + sql_perror("Can't start server: Bind on TCP/IP port"); + sql_print_error("Do you already have another mysqld server running on port: %d ?",mysqld_port); + unireg_abort(1); + } + if (mysql_socket_listen(ip_sock, (int)back_log) < 0) + { + sql_perror("Can't start server: listen() on TCP/IP port"); + sql_print_error("listen() on TCP/IP failed with error %d", + socket_errno); + unireg_abort(1); + } } #ifdef _WIN32 @@ -8082,28 +8213,6 @@ case (int) OPT_WANT_CORE: test_flags |= TEST_CORE_ON_SIGNAL; break; - case (int) OPT_BIND_ADDRESS: - { - struct addrinfo *res_lst, hints; - - bzero(&hints, sizeof(struct addrinfo)); - hints.ai_socktype= SOCK_STREAM; - hints.ai_protocol= IPPROTO_TCP; - - if (getaddrinfo(argument, NULL, &hints, &res_lst) != 0) - { - sql_print_error("Can't start server: cannot resolve hostname!"); - return 1; - } - - if (res_lst->ai_next) - { - sql_print_error("Can't start server: bind-address refers to multiple interfaces!"); - return 1; - } - freeaddrinfo(res_lst); - } - break; case OPT_CONSOLE: if (opt_console) opt_error_log= 0; // Force logs to stdout