diff options
author | Rinat Ibragimov <ri@tempesta-tech.com> | 2020-09-10 03:39:20 +0300 |
---|---|---|
committer | Daniel Black <daniel@mariadb.org> | 2021-03-04 11:19:59 +1100 |
commit | 816b6ea849057dbcafc7660a152cf6f7d70b85ac (patch) | |
tree | 5348cdbbfb67bfc97f0ec35e65c421b81f00a6bc | |
parent | 80ac9ec1cc10d56b048272642653d5d301394779 (diff) | |
download | mariadb-git-816b6ea849057dbcafc7660a152cf6f7d70b85ac.tar.gz |
MDEV-6536: make --bind=hostname to listen on both IPv6 and IPv4 addresses
Binding to a hostname now makes MariaDB server to listen on all addresses
that hostname resolves to.
Rebased to 10.6 by Daniel Black
Closes: #1668
-rw-r--r-- | include/mysql/psi/mysql_socket.h | 11 | ||||
-rw-r--r-- | mysql-test/main/bind_address_resolution.opt | 1 | ||||
-rw-r--r-- | mysql-test/main/bind_address_resolution.result | 15 | ||||
-rw-r--r-- | mysql-test/main/bind_address_resolution.test | 19 | ||||
-rw-r--r-- | mysql-test/suite/perfschema/r/socket_instances_func.result | 14 | ||||
-rw-r--r-- | mysql-test/suite/perfschema/t/socket_instances_func.test | 17 | ||||
-rw-r--r-- | mysql-test/suite/rpl/t/rpl_ipv6.cnf | 4 | ||||
-rw-r--r-- | sql/handle_connections_win.cc | 87 | ||||
-rw-r--r-- | sql/mysqld.cc | 304 | ||||
-rw-r--r-- | sql/mysqld.h | 1 |
10 files changed, 262 insertions, 211 deletions
diff --git a/include/mysql/psi/mysql_socket.h b/include/mysql/psi/mysql_socket.h index aa85daaaab1..59489fabe25 100644 --- a/include/mysql/psi/mysql_socket.h +++ b/include/mysql/psi/mysql_socket.h @@ -75,6 +75,15 @@ struct st_mysql_socket /** The real socket descriptor. */ my_socket fd; + /** Is this a Unix-domain socket? */ + char is_unix_domain_socket; + + /** Is this a socket opened for the extra port? */ + char is_extra_port; + + /** Address family of the socket. (See sa_family from struct sockaddr). */ + unsigned short address_family; + /** The instrumentation hook. Note that this hook is not conditionally defined, @@ -105,7 +114,7 @@ typedef struct st_mysql_socket MYSQL_SOCKET; static inline MYSQL_SOCKET mysql_socket_invalid() { - MYSQL_SOCKET mysql_socket= {INVALID_SOCKET, NULL}; + MYSQL_SOCKET mysql_socket= {INVALID_SOCKET, 0, 0, 0, NULL}; return mysql_socket; } diff --git a/mysql-test/main/bind_address_resolution.opt b/mysql-test/main/bind_address_resolution.opt new file mode 100644 index 00000000000..b0b906a7091 --- /dev/null +++ b/mysql-test/main/bind_address_resolution.opt @@ -0,0 +1 @@ +--bind-address=localhost diff --git a/mysql-test/main/bind_address_resolution.result b/mysql-test/main/bind_address_resolution.result new file mode 100644 index 00000000000..9dc6c7a1f77 --- /dev/null +++ b/mysql-test/main/bind_address_resolution.result @@ -0,0 +1,15 @@ +CREATE TABLE t (a TEXT); +connect con1,localhost,root,,test; +SELECT * FROM t; +a +connect con2,127.0.0.1,root,,test; +SELECT * FROM t; +a +connect con3,::1,root,,test; +SELECT * FROM t; +a +connection default; +DROP TABLE t; +disconnect con1; +disconnect con2; +disconnect con3; diff --git a/mysql-test/main/bind_address_resolution.test b/mysql-test/main/bind_address_resolution.test new file mode 100644 index 00000000000..9bad9313cf7 --- /dev/null +++ b/mysql-test/main/bind_address_resolution.test @@ -0,0 +1,19 @@ +--source include/check_ipv6.inc +--source include/not_embedded.inc + +# The server is started with --bind-address=localhost, and should +# listen on all addresses 'localhost' resolves to. With at least +# 127.0.0.1 and ::1 amongst them. + +CREATE TABLE t (a TEXT); +--connect(con1,localhost,root,,test) +SELECT * FROM t; +--connect(con2,127.0.0.1,root,,test) +SELECT * FROM t; +--connect(con3,::1,root,,test) +SELECT * FROM t; +--connection default +DROP TABLE t; +--disconnect con1 +--disconnect con2 +--disconnect con3 diff --git a/mysql-test/suite/perfschema/r/socket_instances_func.result b/mysql-test/suite/perfschema/r/socket_instances_func.result index 8792730fa29..28643ca07e8 100644 --- a/mysql-test/suite/perfschema/r/socket_instances_func.result +++ b/mysql-test/suite/perfschema/r/socket_instances_func.result @@ -80,15 +80,16 @@ Expect 1 1 # Characteristics of 'server_tcpip_socket' entry # Server listening socket, TCP/IP -# There is only one entry with 'wait/io/socket/sql/server_tcpip_socket'. -# It shares the same thread id as 'wait/io/socket/sql/server_unix_socket'. -SELECT COUNT(*) = 1 AS 'Expect 1' +# There are two entries with 'wait/io/socket/sql/server_tcpip_socket', +# for [::] and for 0.0.0.0. +# They share the same thread id with 'wait/io/socket/sql/server_unix_socket'. +SELECT COUNT(*) = 2 AS 'Expect 1' FROM performance_schema.socket_instances WHERE EVENT_NAME = 'wait/io/socket/sql/server_tcpip_socket'; Expect 1 1 # Get the 'server_tcpip_socket' thread id -SELECT THREAD_ID INTO @thread_id +SELECT DISTINCT THREAD_ID INTO @thread_id FROM performance_schema.socket_instances WHERE EVENT_NAME = 'wait/io/socket/sql/server_tcpip_socket'; # Check the content. @@ -100,6 +101,7 @@ FROM performance_schema.socket_instances WHERE EVENT_NAME = 'wait/io/socket/sql/server_tcpip_socket'; Expect 1 1 +1 # Characteristics of 'server_unix_socket' entry # Server listening socket, unix domain (socket file) # There is only one entry with 'wait/io/socket/sql/server_unix_socket'. @@ -123,12 +125,12 @@ WHERE EVENT_NAME = 'wait/io/socket/sql/server_unix_socket'; Expect 1 1 # Server listening sockets (TCP and Unix) are handled on the same thread -SELECT COUNT(*) = 2 AS 'Expect 1' +SELECT COUNT(*) = 3 AS 'Expect 1' FROM performance_schema.socket_instances WHERE THREAD_ID = @thread_id; Expect 1 1 -SELECT COUNT(*) = 2 AS 'Expect 1' +SELECT COUNT(*) = 3 AS 'Expect 1' FROM performance_schema.socket_instances WHERE THREAD_ID = @thread_id; Expect 1 diff --git a/mysql-test/suite/perfschema/t/socket_instances_func.test b/mysql-test/suite/perfschema/t/socket_instances_func.test index 4cf58d42185..27f0b3983c0 100644 --- a/mysql-test/suite/perfschema/t/socket_instances_func.test +++ b/mysql-test/suite/perfschema/t/socket_instances_func.test @@ -38,6 +38,10 @@ if($my_socket_debug) --echo IPV6=$check_ipv6_supported, IPV4_MAPPED=$check_ipv4_mapped_supported, LOCALHOST=$my_localhost } +# This test only runs when IPv6 is supported (see include/check_ipv6.inc), so +# the server will listen on both IPv4 and IPv6 wildcard addresses. That's why +# the expected number of TCP/IP listeners is always 2. + # # Preserve the current state of SOCKET_INSTANCES # @@ -222,17 +226,18 @@ AND PORT = 0 AND THREAD_ID = @thread_id; --echo # Characteristics of 'server_tcpip_socket' entry --echo # Server listening socket, TCP/IP ---echo # There is only one entry with 'wait/io/socket/sql/server_tcpip_socket'. ---echo # It shares the same thread id as 'wait/io/socket/sql/server_unix_socket'. +--echo # There are two entries with 'wait/io/socket/sql/server_tcpip_socket', +--echo # for [::] and for 0.0.0.0. +--echo # They share the same thread id with 'wait/io/socket/sql/server_unix_socket'. -SELECT COUNT(*) = 1 AS 'Expect 1' +SELECT COUNT(*) = 2 AS 'Expect 1' FROM performance_schema.socket_instances WHERE EVENT_NAME = 'wait/io/socket/sql/server_tcpip_socket'; # Store the thread id of server_tcpip_socket --echo # Get the 'server_tcpip_socket' thread id -SELECT THREAD_ID INTO @thread_id +SELECT DISTINCT THREAD_ID INTO @thread_id FROM performance_schema.socket_instances WHERE EVENT_NAME = 'wait/io/socket/sql/server_tcpip_socket'; @@ -288,14 +293,14 @@ WHERE EVENT_NAME = 'wait/io/socket/sql/server_unix_socket'; --disable_query_log ONCE eval SET @thread_id = $server_tcpip_thread_id; -eval SELECT COUNT(*) = 2 AS 'Expect 1' +eval SELECT COUNT(*) = 3 AS 'Expect 1' FROM performance_schema.socket_instances WHERE THREAD_ID = @thread_id; --disable_query_log ONCE eval SET @thread_id = $server_unix_thread_id; -eval SELECT COUNT(*) = 2 AS 'Expect 1' +eval SELECT COUNT(*) = 3 AS 'Expect 1' FROM performance_schema.socket_instances WHERE THREAD_ID = @thread_id; diff --git a/mysql-test/suite/rpl/t/rpl_ipv6.cnf b/mysql-test/suite/rpl/t/rpl_ipv6.cnf index c657e7c5115..6bf97a94a4e 100644 --- a/mysql-test/suite/rpl/t/rpl_ipv6.cnf +++ b/mysql-test/suite/rpl/t/rpl_ipv6.cnf @@ -12,7 +12,7 @@ log-bin= master-bin loose-innodb skip-name-resolve -bind-address= :: +bind-address= * [mysqld.2] @@ -38,7 +38,7 @@ report-user= root skip-slave-start skip-name-resolve -bind-address= :: +bind-address= * # Directory where slaves find the dumps generated by "load data" # on the server. The path need to have constant length otherwise diff --git a/sql/handle_connections_win.cc b/sql/handle_connections_win.cc index debbce998fa..07d50590869 100644 --- a/sql/handle_connections_win.cc +++ b/sql/handle_connections_win.cc @@ -22,11 +22,13 @@ #include <mswsock.h> #include <mysql/psi/mysql_socket.h> #include <sddl.h> +#include <vector> #include <handle_connections_win.h> /* From mysqld.cc */ -extern MYSQL_SOCKET base_ip_sock, extra_ip_sock; +extern HANDLE hEventShutdown; +extern std::vector<MYSQL_SOCKET> listen_sockets; #ifdef HAVE_POOL_OF_THREADS extern PTP_CALLBACK_ENVIRON get_threadpool_win_callback_environ(); extern void tp_win_callback_prolog(); @@ -128,6 +130,9 @@ struct Socket_Listener: public Listener /** Client socket passed to AcceptEx() call.*/ SOCKET m_client_socket; + /** Listening socket. */ + MYSQL_SOCKET m_listen_socket; + /** Buffer for sockaddrs passed to AcceptEx()/GetAcceptExSockaddrs() */ char m_buffer[2 * sizeof(sockaddr_storage) + 32]; @@ -162,7 +167,8 @@ struct Socket_Listener: public Listener */ Socket_Listener(MYSQL_SOCKET listen_socket, PTP_CALLBACK_ENVIRON callback_environ) : Listener((HANDLE)listen_socket.fd,0), - m_client_socket(INVALID_SOCKET) + m_client_socket(INVALID_SOCKET), + m_listen_socket(listen_socket) { if (callback_environ) { @@ -184,7 +190,8 @@ struct Socket_Listener: public Listener void begin_accept() { retry : - m_client_socket= socket(server_socket_ai_family, SOCK_STREAM, IPPROTO_TCP); + m_client_socket= socket(m_listen_socket.address_family, SOCK_STREAM, + IPPROTO_TCP); if (m_client_socket == INVALID_SOCKET) { sql_perror("socket() call failed."); @@ -233,7 +240,6 @@ retry : } MYSQL_SOCKET s_client{m_client_socket}; - MYSQL_SOCKET s_listen{(SOCKET)m_handle}; #ifdef HAVE_PSI_SOCKET_INTERFACE /* Parse socket addresses buffer filled by AcceptEx(), @@ -246,7 +252,8 @@ retry : &local_addr, &local_addr_len, &remote_addr, &remote_addr_len); s_client.m_psi= PSI_SOCKET_CALL(init_socket) - (key_socket_client_connection, (const my_socket*)&s_listen.fd, remote_addr, remote_addr_len); + (key_socket_client_connection, (const my_socket*)&m_listen_socket.fd, + remote_addr, remote_addr_len); #endif /* Start accepting new connection. After this point, do not use @@ -255,7 +262,7 @@ retry : /* Some chores post-AcceptEx() that we need to create a normal socket.*/ if (setsockopt(s_client.fd, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, - (char *)&s_listen.fd, sizeof(s_listen.fd))) + (char *)&m_listen_socket.fd, sizeof(m_listen_socket.fd))) { if (!abort_loop) { @@ -265,7 +272,7 @@ retry : } /* Create a new connection.*/ - handle_accepted_socket(s_client, s_listen); + handle_accepted_socket(s_client, m_listen_socket); } ~Socket_Listener() @@ -280,14 +287,12 @@ retry : */ static void init_winsock_extensions() { - SOCKET s= mysql_socket_getfd(base_ip_sock); - if (s == INVALID_SOCKET) - s= mysql_socket_getfd(extra_ip_sock); - if (s == INVALID_SOCKET) - { + if (listen_sockets.size() == 0) { /* --skip-networking was used*/ return; } + + SOCKET s= mysql_socket_getfd(listen_sockets[0]); GUID guid_AcceptEx= WSAID_ACCEPTEX; GUID guid_GetAcceptExSockaddrs= WSAID_GETACCEPTEXSOCKADDRS; @@ -540,22 +545,24 @@ static void create_shutdown_event() */ -#define MAX_WAIT_HANDLES 32 #define NUM_PIPE_LISTENERS 24 #define SHUTDOWN_IDX 0 #define LISTENER_START_IDX 1 -static Listener *all_listeners[MAX_WAIT_HANDLES]; -static HANDLE wait_events[MAX_WAIT_HANDLES]; -static int n_listeners; +static std::vector<Listener *> all_listeners; +static std::vector<HANDLE> wait_events; void network_init_win() { Socket_Listener::init_winsock_extensions(); /* Listen for TCP connections on "extra-port" (no threadpool).*/ - if (extra_ip_sock.fd != INVALID_SOCKET) - all_listeners[n_listeners++]= new Socket_Listener(extra_ip_sock, 0); + for (std::vector<MYSQL_SOCKET>::iterator it= listen_sockets.begin(); + it != listen_sockets.end(); ++it) + { + if (it->is_extra_port) + all_listeners.push_back(new Socket_Listener(*it, 0)); + } /* Listen for named pipe connections */ if (mysqld_unix_port[0] && !opt_bootstrap && opt_enable_named_pipe) @@ -564,17 +571,22 @@ void network_init_win() Use several listeners for pipe, to reduce ERROR_PIPE_BUSY on client side. */ for (int i= 0; i < NUM_PIPE_LISTENERS; i++) - all_listeners[n_listeners++]= new Pipe_Listener(); + all_listeners.push_back(new Pipe_Listener()); } - if (base_ip_sock.fd != INVALID_SOCKET) + for (std::vector<MYSQL_SOCKET>::iterator it= listen_sockets.begin(); + it != listen_sockets.end(); ++it) { - /* Wait for TCP connections.*/ - SetFileCompletionNotificationModes((HANDLE)base_ip_sock.fd, FILE_SKIP_SET_EVENT_ON_HANDLE); - all_listeners[n_listeners++]= new Socket_Listener(base_ip_sock, get_threadpool_win_callback_environ()); + if (it->is_extra_port) + continue; + /* Wait for TCP connections.*/ + SetFileCompletionNotificationModes((HANDLE)it->fd, + FILE_SKIP_SET_EVENT_ON_HANDLE); + all_listeners.push_back( + new Socket_Listener(*it, get_threadpool_win_callback_environ())); } - if (!n_listeners && !opt_bootstrap) + if (all_listeners.size() == 0 && !opt_bootstrap) { sql_print_error("Either TCP connections or named pipe connections must be enabled."); unireg_abort(1); @@ -586,26 +598,41 @@ void handle_connections_win() int n_waits; create_shutdown_event(); - wait_events[SHUTDOWN_IDX]= hEventShutdown; + wait_events.push_back(hEventShutdown); n_waits= 1; - for (int i= 0; i < n_listeners; i++) + for (int i= 0; i < all_listeners.size(); i++) { HANDLE wait_handle= all_listeners[i]->wait_handle(); if (wait_handle) { DBUG_ASSERT((i == 0) || (all_listeners[i - 1]->wait_handle() != 0)); - wait_events[n_waits++]= wait_handle; + wait_events.push_back(wait_handle); } all_listeners[i]->begin_accept(); } mysqld_win_set_startup_complete(); + // WaitForMultipleObjects can't wait on more than MAXIMUM_WAIT_OBJECTS + // handles simultaneously. Since MAXIMUM_WAIT_OBJECTS is only 64, there is + // a theoretical possiblity of exceeding that limit on installations where + // host name resolves to a lot of addresses. + if (wait_events.size() > MAXIMUM_WAIT_OBJECTS) + { + sql_print_warning( + "Too many wait events (%lu). Some connection listeners won't be handled. " + "Try to switch \"thread-handling\" to \"pool-of-threads\" and/or disable " + "\"extra-port\".", static_cast<ulong>(wait_events.size())); + wait_events.resize(MAXIMUM_WAIT_OBJECTS); + } + for (;;) { - DWORD idx = WaitForMultipleObjects(n_waits ,wait_events, FALSE, INFINITE); - DBUG_ASSERT((int)idx >= 0 && (int)idx < n_waits); + DBUG_ASSERT(wait_events.size() <= MAXIMUM_WAIT_OBJECTS); + DWORD idx = WaitForMultipleObjects((DWORD)wait_events.size(), + wait_events.data(), FALSE, INFINITE); + DBUG_ASSERT((int)idx >= 0 && (int)idx < (int)wait_events.size()); if (idx == SHUTDOWN_IDX) break; @@ -616,7 +643,7 @@ void handle_connections_win() mysqld_win_initiate_shutdown(); /* Cleanup */ - for (int i= 0; i < n_listeners; i++) + for (size_t i= 0; i < all_listeners.size(); i++) { Listener *listener= all_listeners[i]; if (listener->wait_handle()) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 02f69e1d512..39c764d71b9 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -338,7 +338,6 @@ static char *character_set_filesystem_name; static char *lc_messages; static char *lc_time_names_name; char *my_bind_addr_str; -int server_socket_ai_family; static char *default_collation_name; char *default_storage_engine, *default_tmp_storage_engine; char *enforced_storage_engine=NULL; @@ -1349,7 +1348,8 @@ static Buffered_logs buffered_logs; struct my_rnd_struct sql_rand; ///< used by sql_class.cc:THD::THD() #ifndef EMBEDDED_LIBRARY -MYSQL_SOCKET unix_sock, base_ip_sock, extra_ip_sock; +std::vector<MYSQL_SOCKET> listen_sockets; +bool unix_sock_is_online= false; /** Error reporter that buffer log messages. @param level log message level @@ -1690,28 +1690,15 @@ static void close_connections(void) /* Abort listening to new connections */ DBUG_PRINT("quit",("Closing sockets")); - if (!opt_disable_networking ) + for (std::vector<MYSQL_SOCKET>::iterator sock = listen_sockets.begin(); + sock != listen_sockets.end(); ++sock) { - if (mysql_socket_getfd(base_ip_sock) != INVALID_SOCKET) - { - (void) mysql_socket_close(base_ip_sock); - base_ip_sock= MYSQL_INVALID_SOCKET; - } - if (mysql_socket_getfd(extra_ip_sock) != INVALID_SOCKET) - { - (void) mysql_socket_close(extra_ip_sock); - extra_ip_sock= MYSQL_INVALID_SOCKET; - } + (void) mysql_socket_close(*sock); + if (sock->is_unix_domain_socket) + (void) unlink(mysqld_unix_port); } + listen_sockets.clear(); -#ifdef HAVE_SYS_UN_H - if (mysql_socket_getfd(unix_sock) != INVALID_SOCKET) - { - (void) mysql_socket_close(unix_sock); - (void) unlink(mysqld_unix_port); - unix_sock= MYSQL_INVALID_SOCKET; - } -#endif end_thr_alarm(0); // Abort old alarms. /* @@ -1800,13 +1787,20 @@ static void close_server_sock() #ifdef HAVE_CLOSE_SERVER_SOCK DBUG_ENTER("close_server_sock"); - close_socket(base_ip_sock, "TCP/IP"); - close_socket(extra_ip_sock, "TCP/IP"); - close_socket(unix_sock, "unix/IP"); - - if (mysql_socket_getfd(unix_sock) != INVALID_SOCKET) - (void) unlink(mysqld_unix_port); - base_ip_sock= extra_ip_sock= unix_sock= MYSQL_INVALID_SOCKET; + for (std::vector<MYSQL_SOCKET>::iterator sock= listen_sockets.begin(); + sock != listen_sockets.end(); ++sock) + { + if (sock->is_unix_domain_socket) + { + close_socket(*sock, "unix/IP"); + (void) unlink(mysqld_unix_port); + } + else + { + close_socket(*sock, "TCP/IP"); + } + } + listen_sockets.clear(); DBUG_VOID_RETURN; #endif @@ -2241,7 +2235,9 @@ static void set_root(const char *path) Activate usage of a tcp port */ -static MYSQL_SOCKET activate_tcp_port(uint port) +static void activate_tcp_port(uint port, + std::vector<MYSQL_SOCKET> *listen_sockets, + bool is_extra_port= false) { struct addrinfo *ai, *a; struct addrinfo hints; @@ -2273,20 +2269,6 @@ static MYSQL_SOCKET activate_tcp_port(uint port) unireg_abort(1); /* purecov: tested */ } - /* - special case: for wildcard addresses prefer ipv6 over ipv4, - because we later switch off IPV6_V6ONLY, so ipv6 wildcard - addresses will work for ipv4 too - */ - if (!real_bind_addr_str && ai->ai_family == AF_INET && ai->ai_next - && ai->ai_next->ai_family == AF_INET6) - { - a= ai; - ai= ai->ai_next; - a->ai_next= ai->ai_next; - ai->ai_next= a; - } - for (a= ai; a != NULL; a= a->ai_next) { ip_sock= mysql_socket_socket(key_socket_tcpip, a->ai_family, @@ -2309,99 +2291,98 @@ static MYSQL_SOCKET activate_tcp_port(uint port) } else { - server_socket_ai_family= a->ai_family; + ip_sock.address_family= a->ai_family; sql_print_information("Server socket created on IP: '%s'.", (const char *) ip_addr); - break; - } - } - 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 */ - } + 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 */ + } - mysql_socket_set_thread_owner(ip_sock); + 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)); + /* + 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; - (void) mysql_socket_setsockopt(ip_sock, IPPROTO_IPV6, IPV6_V6ONLY, - (char*)&arg, sizeof(arg)); - } + /* + If an address name resolves to both IPv4 and IPv6 addresses, the server + will listen on them both. With IPV6_V6ONLY unset, listening on an IPv6 + wildcard address may cause listening on an IPv4 wildcard address + to fail. That's why IPV6_V6ONLY needs to be forcefully turned on. + */ + if (a->ai_family == AF_INET6) + { + arg= 1; + (void) mysql_socket_setsockopt(ip_sock, IPPROTO_IPV6, IPV6_V6ONLY, + (char*)&arg, sizeof(arg)); + } #endif #ifdef IP_FREEBIND - arg= 1; - (void) mysql_socket_setsockopt(ip_sock, IPPROTO_IP, IP_FREEBIND, (char*) &arg, - sizeof(arg)); + arg= 1; + (void) mysql_socket_setsockopt(ip_sock, IPPROTO_IP, IP_FREEBIND, + (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); - } + /* + 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); + } + + 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); + } #ifdef FD_CLOEXEC - (void) fcntl(mysql_socket_getfd(ip_sock), F_SETFD, FD_CLOEXEC); + (void) fcntl(mysql_socket_getfd(ip_sock), F_SETFD, FD_CLOEXEC); #endif + ip_sock.is_extra_port= is_extra_port; + listen_sockets->push_back(ip_sock); + } + } - DBUG_RETURN(ip_sock); + freeaddrinfo(ai); + DBUG_VOID_RETURN; } static void network_init(void) @@ -2431,9 +2412,11 @@ static void network_init(void) if (!opt_disable_networking && !opt_bootstrap) { if (mysqld_port) - base_ip_sock= activate_tcp_port(mysqld_port); + activate_tcp_port(mysqld_port, &listen_sockets, + /* is_extra_port= */ false); if (mysqld_extra_port) - extra_ip_sock= activate_tcp_port(mysqld_extra_port); + activate_tcp_port(mysqld_extra_port, &listen_sockets, + /* is_extra_port= */ true); } #if defined(HAVE_SYS_UN_H) @@ -2442,6 +2425,7 @@ static void network_init(void) */ if (mysqld_unix_port[0] && !opt_bootstrap) { + MYSQL_SOCKET unix_sock= MYSQL_INVALID_SOCKET; size_t port_len; DBUG_PRINT("general",("UNIX Socket is %s",mysqld_unix_port)); @@ -2458,6 +2442,9 @@ static void network_init(void) unireg_abort(1); /* purecov: inspected */ } + unix_sock.is_unix_domain_socket= true; + listen_sockets.push_back(unix_sock); + unix_sock_is_online= true; mysql_socket_set_thread_owner(unix_sock); bzero((char*) &UNIXaddr, sizeof(UNIXaddr)); @@ -5645,8 +5632,7 @@ int mysqld_main(int argc, char **argv) if (IS_SYSVAR_AUTOSIZE(&server_version_ptr)) sql_print_information(ER_DEFAULT(ER_STARTUP), my_progname, server_version, - ((mysql_socket_getfd(unix_sock) == INVALID_SOCKET) ? - (char*) "" : mysqld_unix_port), + (unix_sock_is_online ? mysqld_unix_port : (char*) ""), mysqld_port, MYSQL_COMPILATION_COMMENT); else { @@ -5658,8 +5644,7 @@ int mysqld_main(int argc, char **argv) sql_print_information(ER_DEFAULT(ER_STARTUP), my_progname, real_server_version, - ((mysql_socket_getfd(unix_sock) == INVALID_SOCKET) ? - (char*) "" : mysqld_unix_port), + (unix_sock_is_online ? mysqld_unix_port : (char*) ""), mysqld_port, MYSQL_COMPILATION_COMMENT); } @@ -5866,8 +5851,7 @@ void handle_accepted_socket(MYSQL_SOCKET new_sock, MYSQL_SOCKET sock) { #ifdef HAVE_LIBWRAP { - if (mysql_socket_getfd(sock) == mysql_socket_getfd(base_ip_sock) || - mysql_socket_getfd(sock) == mysql_socket_getfd(extra_ip_sock)) + if (!sock.is_unix_domain_socket) { struct request_info req; signal(SIGCHLD, SIG_DFL); @@ -5910,11 +5894,9 @@ void handle_accepted_socket(MYSQL_SOCKET new_sock, MYSQL_SOCKET sock) DBUG_PRINT("info", ("Creating CONNECT for new connection")); if (auto connect= new CONNECT(new_sock, - mysql_socket_getfd(sock) == - mysql_socket_getfd(unix_sock) ? + sock.is_unix_domain_socket ? VIO_TYPE_SOCKET : VIO_TYPE_TCPIP, - mysql_socket_getfd(sock) == - mysql_socket_getfd(extra_ip_sock) ? + sock.is_extra_port ? extra_thread_scheduler : thread_scheduler)) create_new_thread(connect); else @@ -5950,36 +5932,30 @@ void handle_connections_sockets() struct sockaddr_storage cAddr; int retval; #ifdef HAVE_POLL - int socket_count= 0; - struct pollfd fds[3]; // for ip_sock, unix_sock and extra_ip_sock - MYSQL_SOCKET pfs_fds[3]; // for performance schema -#define setup_fds(X) \ - mysql_socket_set_thread_owner(X); \ - pfs_fds[socket_count]= (X); \ - fds[socket_count].fd= mysql_socket_getfd(X); \ - fds[socket_count].events= POLLIN; \ - socket_count++ + std::vector<struct pollfd> fds; // for ip_sock, unix_sock and extra_ip_sock #else -#define setup_fds(X) FD_SET(mysql_socket_getfd(X),&clientFDs) fd_set readFDs,clientFDs; - FD_ZERO(&clientFDs); #endif DBUG_ENTER("handle_connections_sockets"); - if (mysql_socket_getfd(base_ip_sock) != INVALID_SOCKET) +#ifdef HAVE_POLL + fds.resize(listen_sockets.size()); + for (size_t i= 0; i < listen_sockets.size(); i++) { - setup_fds(base_ip_sock); - set_non_blocking_if_supported(base_ip_sock); + mysql_socket_set_thread_owner(listen_sockets[i]); + fds[i].fd= mysql_socket_getfd(listen_sockets[i]); + fds[i].events= POLLIN; + set_non_blocking_if_supported(listen_sockets[i]); } - if (mysql_socket_getfd(extra_ip_sock) != INVALID_SOCKET) +#else + FD_ZERO(&clientFDs); + for (size_t i= 0; i < listen_sockets.size(); i++) { - setup_fds(extra_ip_sock); - set_non_blocking_if_supported(extra_ip_sock); + int fd= mysql_socket_getfd(listen_sockets[i]); + FD_SET(fd, &clientFDs); + set_non_blocking_if_supported(listen_sockets[i]); } -#ifdef HAVE_SYS_UN_H - setup_fds(unix_sock); - set_non_blocking_if_supported(unix_sock); #endif sd_notify(0, "READY=1\n" @@ -5989,10 +5965,10 @@ void handle_connections_sockets() while (!abort_loop) { #ifdef HAVE_POLL - retval= poll(fds, socket_count, -1); + retval= poll(fds.data(), fds.size(), -1); #else readFDs=clientFDs; - retval= select((int) 0,&readFDs,0,0,0); + retval= select(FD_SETSIZE, &readFDs, NULL, NULL, NULL); #endif if (retval < 0) @@ -6016,22 +5992,23 @@ void handle_connections_sockets() /* Is this a new connection request ? */ #ifdef HAVE_POLL - for (int i= 0; i < socket_count; ++i) + for (size_t i= 0; i < fds.size(); ++i) { if (fds[i].revents & POLLIN) { - sock= pfs_fds[i]; + sock= listen_sockets[i]; break; } } #else // HAVE_POLL - if (FD_ISSET(mysql_socket_getfd(base_ip_sock),&readFDs)) - sock= base_ip_sock; - else - if (FD_ISSET(mysql_socket_getfd(extra_ip_sock),&readFDs)) - sock= extra_ip_sock; - else - sock = unix_sock; + for (size_t i=0; i < listen_sockets.size(); i++) + { + if (FD_ISSET(mysql_socket_getfd(listen_sockets[i]), &readFDs)) + { + sock= listen_sockets[i]; + break; + } + } #endif // HAVE_POLL for (uint retry=0; retry < MAX_ACCEPT_RETRY; retry++) @@ -7514,9 +7491,6 @@ static int mysql_init_variables(void) character_set_filesystem= &my_charset_bin; opt_specialflag= SPECIAL_ENGLISH; -#ifndef EMBEDDED_LIBRARY - unix_sock= base_ip_sock= extra_ip_sock= MYSQL_INVALID_SOCKET; -#endif mysql_home_ptr= mysql_home; log_error_file_ptr= log_error_file; protocol_version= PROTOCOL_VERSION; diff --git a/sql/mysqld.h b/sql/mysqld.h index 2a73e518ded..64cf1c5ebb0 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -147,7 +147,6 @@ extern ulong opt_replicate_events_marked_for_skip; extern char *default_tz_name; extern Time_zone *default_tz; extern char *my_bind_addr_str; -extern int server_socket_ai_family; extern char *default_storage_engine, *default_tmp_storage_engine; extern char *enforced_storage_engine; extern char *gtid_pos_auto_engines; |