Commit b3abcf80 authored by Rinat Ibragimov's avatar Rinat Ibragimov Committed by Daniel Black

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
parent f691d986
...@@ -75,6 +75,15 @@ struct st_mysql_socket ...@@ -75,6 +75,15 @@ struct st_mysql_socket
/** The real socket descriptor. */ /** The real socket descriptor. */
my_socket fd; 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. The instrumentation hook.
Note that this hook is not conditionally defined, Note that this hook is not conditionally defined,
...@@ -105,7 +114,7 @@ typedef struct st_mysql_socket MYSQL_SOCKET; ...@@ -105,7 +114,7 @@ typedef struct st_mysql_socket MYSQL_SOCKET;
static inline MYSQL_SOCKET static inline MYSQL_SOCKET
mysql_socket_invalid() mysql_socket_invalid()
{ {
MYSQL_SOCKET mysql_socket= {INVALID_SOCKET, NULL}; MYSQL_SOCKET mysql_socket= {INVALID_SOCKET, 0, 0, 0, NULL};
return mysql_socket; return mysql_socket;
} }
......
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;
--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
...@@ -80,15 +80,16 @@ Expect 1 ...@@ -80,15 +80,16 @@ Expect 1
1 1
# Characteristics of 'server_tcpip_socket' entry # Characteristics of 'server_tcpip_socket' entry
# Server listening socket, TCP/IP # Server listening socket, TCP/IP
# There is only one entry with 'wait/io/socket/sql/server_tcpip_socket'. # There are two entries with 'wait/io/socket/sql/server_tcpip_socket',
# It shares the same thread id as 'wait/io/socket/sql/server_unix_socket'. # for [::] and for 0.0.0.0.
SELECT COUNT(*) = 1 AS 'Expect 1' # 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 FROM performance_schema.socket_instances
WHERE EVENT_NAME = 'wait/io/socket/sql/server_tcpip_socket'; WHERE EVENT_NAME = 'wait/io/socket/sql/server_tcpip_socket';
Expect 1 Expect 1
1 1
# Get the 'server_tcpip_socket' thread id # 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 FROM performance_schema.socket_instances
WHERE EVENT_NAME = 'wait/io/socket/sql/server_tcpip_socket'; WHERE EVENT_NAME = 'wait/io/socket/sql/server_tcpip_socket';
# Check the content. # Check the content.
...@@ -100,6 +101,7 @@ FROM performance_schema.socket_instances ...@@ -100,6 +101,7 @@ FROM performance_schema.socket_instances
WHERE EVENT_NAME = 'wait/io/socket/sql/server_tcpip_socket'; WHERE EVENT_NAME = 'wait/io/socket/sql/server_tcpip_socket';
Expect 1 Expect 1
1 1
1
# Characteristics of 'server_unix_socket' entry # Characteristics of 'server_unix_socket' entry
# Server listening socket, unix domain (socket file) # Server listening socket, unix domain (socket file)
# There is only one entry with 'wait/io/socket/sql/server_unix_socket'. # 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'; ...@@ -123,12 +125,12 @@ WHERE EVENT_NAME = 'wait/io/socket/sql/server_unix_socket';
Expect 1 Expect 1
1 1
# Server listening sockets (TCP and Unix) are handled on the same thread # 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 FROM performance_schema.socket_instances
WHERE THREAD_ID = @thread_id; WHERE THREAD_ID = @thread_id;
Expect 1 Expect 1
1 1
SELECT COUNT(*) = 2 AS 'Expect 1' SELECT COUNT(*) = 3 AS 'Expect 1'
FROM performance_schema.socket_instances FROM performance_schema.socket_instances
WHERE THREAD_ID = @thread_id; WHERE THREAD_ID = @thread_id;
Expect 1 Expect 1
......
...@@ -38,6 +38,10 @@ if($my_socket_debug) ...@@ -38,6 +38,10 @@ if($my_socket_debug)
--echo IPV6=$check_ipv6_supported, IPV4_MAPPED=$check_ipv4_mapped_supported, LOCALHOST=$my_localhost --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 # Preserve the current state of SOCKET_INSTANCES
# #
...@@ -222,17 +226,18 @@ AND PORT = 0 AND THREAD_ID = @thread_id; ...@@ -222,17 +226,18 @@ AND PORT = 0 AND THREAD_ID = @thread_id;
--echo # Characteristics of 'server_tcpip_socket' entry --echo # Characteristics of 'server_tcpip_socket' entry
--echo # Server listening socket, TCP/IP --echo # Server listening socket, TCP/IP
--echo # There is only one entry with 'wait/io/socket/sql/server_tcpip_socket'. --echo # There are two entries with 'wait/io/socket/sql/server_tcpip_socket',
--echo # It shares the same thread id as 'wait/io/socket/sql/server_unix_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 FROM performance_schema.socket_instances
WHERE EVENT_NAME = 'wait/io/socket/sql/server_tcpip_socket'; WHERE EVENT_NAME = 'wait/io/socket/sql/server_tcpip_socket';
# Store the thread id of server_tcpip_socket # Store the thread id of server_tcpip_socket
--echo # Get the 'server_tcpip_socket' thread id --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 FROM performance_schema.socket_instances
WHERE EVENT_NAME = 'wait/io/socket/sql/server_tcpip_socket'; WHERE EVENT_NAME = 'wait/io/socket/sql/server_tcpip_socket';
...@@ -288,14 +293,14 @@ WHERE EVENT_NAME = 'wait/io/socket/sql/server_unix_socket'; ...@@ -288,14 +293,14 @@ WHERE EVENT_NAME = 'wait/io/socket/sql/server_unix_socket';
--disable_query_log ONCE --disable_query_log ONCE
eval SET @thread_id = $server_tcpip_thread_id; 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 FROM performance_schema.socket_instances
WHERE THREAD_ID = @thread_id; WHERE THREAD_ID = @thread_id;
--disable_query_log ONCE --disable_query_log ONCE
eval SET @thread_id = $server_unix_thread_id; 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 FROM performance_schema.socket_instances
WHERE THREAD_ID = @thread_id; WHERE THREAD_ID = @thread_id;
......
...@@ -12,7 +12,7 @@ log-bin= master-bin ...@@ -12,7 +12,7 @@ log-bin= master-bin
loose-innodb loose-innodb
skip-name-resolve skip-name-resolve
bind-address= :: bind-address= *
[mysqld.2] [mysqld.2]
...@@ -38,7 +38,7 @@ report-user= root ...@@ -38,7 +38,7 @@ report-user= root
skip-slave-start skip-slave-start
skip-name-resolve skip-name-resolve
bind-address= :: bind-address= *
# Directory where slaves find the dumps generated by "load data" # Directory where slaves find the dumps generated by "load data"
# on the server. The path need to have constant length otherwise # on the server. The path need to have constant length otherwise
......
...@@ -22,11 +22,13 @@ ...@@ -22,11 +22,13 @@
#include <mswsock.h> #include <mswsock.h>
#include <mysql/psi/mysql_socket.h> #include <mysql/psi/mysql_socket.h>
#include <sddl.h> #include <sddl.h>
#include <vector>
#include <handle_connections_win.h> #include <handle_connections_win.h>
/* From mysqld.cc */ /* 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 #ifdef HAVE_POOL_OF_THREADS
extern PTP_CALLBACK_ENVIRON get_threadpool_win_callback_environ(); extern PTP_CALLBACK_ENVIRON get_threadpool_win_callback_environ();
extern void tp_win_callback_prolog(); extern void tp_win_callback_prolog();
...@@ -128,6 +130,9 @@ struct Socket_Listener: public Listener ...@@ -128,6 +130,9 @@ struct Socket_Listener: public Listener
/** Client socket passed to AcceptEx() call.*/ /** Client socket passed to AcceptEx() call.*/
SOCKET m_client_socket; SOCKET m_client_socket;
/** Listening socket. */
MYSQL_SOCKET m_listen_socket;
/** Buffer for sockaddrs passed to AcceptEx()/GetAcceptExSockaddrs() */ /** Buffer for sockaddrs passed to AcceptEx()/GetAcceptExSockaddrs() */
char m_buffer[2 * sizeof(sockaddr_storage) + 32]; char m_buffer[2 * sizeof(sockaddr_storage) + 32];
...@@ -162,7 +167,8 @@ struct Socket_Listener: public Listener ...@@ -162,7 +167,8 @@ struct Socket_Listener: public Listener
*/ */
Socket_Listener(MYSQL_SOCKET listen_socket, PTP_CALLBACK_ENVIRON callback_environ) : Socket_Listener(MYSQL_SOCKET listen_socket, PTP_CALLBACK_ENVIRON callback_environ) :
Listener((HANDLE)listen_socket.fd,0), Listener((HANDLE)listen_socket.fd,0),
m_client_socket(INVALID_SOCKET) m_client_socket(INVALID_SOCKET),
m_listen_socket(listen_socket)
{ {
if (callback_environ) if (callback_environ)
{ {
...@@ -184,7 +190,8 @@ struct Socket_Listener: public Listener ...@@ -184,7 +190,8 @@ struct Socket_Listener: public Listener
void begin_accept() void begin_accept()
{ {
retry : 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) if (m_client_socket == INVALID_SOCKET)
{ {
sql_perror("socket() call failed."); sql_perror("socket() call failed.");
...@@ -233,7 +240,6 @@ retry : ...@@ -233,7 +240,6 @@ retry :
} }
MYSQL_SOCKET s_client{m_client_socket}; MYSQL_SOCKET s_client{m_client_socket};
MYSQL_SOCKET s_listen{(SOCKET)m_handle};
#ifdef HAVE_PSI_SOCKET_INTERFACE #ifdef HAVE_PSI_SOCKET_INTERFACE
/* Parse socket addresses buffer filled by AcceptEx(), /* Parse socket addresses buffer filled by AcceptEx(),
...@@ -246,7 +252,8 @@ retry : ...@@ -246,7 +252,8 @@ retry :
&local_addr, &local_addr_len, &remote_addr, &remote_addr_len); &local_addr, &local_addr_len, &remote_addr, &remote_addr_len);
s_client.m_psi= PSI_SOCKET_CALL(init_socket) 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 #endif
/* Start accepting new connection. After this point, do not use /* Start accepting new connection. After this point, do not use
...@@ -255,7 +262,7 @@ retry : ...@@ -255,7 +262,7 @@ retry :
/* Some chores post-AcceptEx() that we need to create a normal socket.*/ /* Some chores post-AcceptEx() that we need to create a normal socket.*/
if (setsockopt(s_client.fd, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, 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) if (!abort_loop)
{ {
...@@ -265,7 +272,7 @@ retry : ...@@ -265,7 +272,7 @@ retry :
} }
/* Create a new connection.*/ /* Create a new connection.*/
handle_accepted_socket(s_client, s_listen); handle_accepted_socket(s_client, m_listen_socket);
} }
~Socket_Listener() ~Socket_Listener()
...@@ -280,14 +287,12 @@ retry : ...@@ -280,14 +287,12 @@ retry :
*/ */
static void init_winsock_extensions() static void init_winsock_extensions()
{ {
SOCKET s= mysql_socket_getfd(base_ip_sock); if (listen_sockets.size() == 0) {
if (s == INVALID_SOCKET)
s= mysql_socket_getfd(extra_ip_sock);
if (s == INVALID_SOCKET)
{
/* --skip-networking was used*/ /* --skip-networking was used*/
return; return;
} }
SOCKET s= mysql_socket_getfd(listen_sockets[0]);
GUID guid_AcceptEx= WSAID_ACCEPTEX; GUID guid_AcceptEx= WSAID_ACCEPTEX;
GUID guid_GetAcceptExSockaddrs= WSAID_GETACCEPTEXSOCKADDRS; GUID guid_GetAcceptExSockaddrs= WSAID_GETACCEPTEXSOCKADDRS;
...@@ -540,22 +545,24 @@ static void create_shutdown_event() ...@@ -540,22 +545,24 @@ static void create_shutdown_event()
*/ */
#define MAX_WAIT_HANDLES 32
#define NUM_PIPE_LISTENERS 24 #define NUM_PIPE_LISTENERS 24
#define SHUTDOWN_IDX 0 #define SHUTDOWN_IDX 0
#define LISTENER_START_IDX 1 #define LISTENER_START_IDX 1
static Listener *all_listeners[MAX_WAIT_HANDLES]; static std::vector<Listener *> all_listeners;
static HANDLE wait_events[MAX_WAIT_HANDLES]; static std::vector<HANDLE> wait_events;
static int n_listeners;
void network_init_win() void network_init_win()
{ {
Socket_Listener::init_winsock_extensions(); Socket_Listener::init_winsock_extensions();
/* Listen for TCP connections on "extra-port" (no threadpool).*/ /* Listen for TCP connections on "extra-port" (no threadpool).*/
if (extra_ip_sock.fd != INVALID_SOCKET) for (std::vector<MYSQL_SOCKET>::iterator it= listen_sockets.begin();
all_listeners[n_listeners++]= new Socket_Listener(extra_ip_sock, 0); it != listen_sockets.end(); ++it)
{
if (it->is_extra_port)
all_listeners.push_back(new Socket_Listener(*it, 0));
}
/* Listen for named pipe connections */ /* Listen for named pipe connections */
if (mysqld_unix_port[0] && !opt_bootstrap && opt_enable_named_pipe) if (mysqld_unix_port[0] && !opt_bootstrap && opt_enable_named_pipe)
...@@ -564,17 +571,22 @@ void network_init_win() ...@@ -564,17 +571,22 @@ void network_init_win()
Use several listeners for pipe, to reduce ERROR_PIPE_BUSY on client side. Use several listeners for pipe, to reduce ERROR_PIPE_BUSY on client side.
*/ */
for (int i= 0; i < NUM_PIPE_LISTENERS; i++) 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.*/ if (it->is_extra_port)
SetFileCompletionNotificationModes((HANDLE)base_ip_sock.fd, FILE_SKIP_SET_EVENT_ON_HANDLE); continue;
all_listeners[n_listeners++]= new Socket_Listener(base_ip_sock, get_threadpool_win_callback_environ()); /* 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."); sql_print_error("Either TCP connections or named pipe connections must be enabled.");
unireg_abort(1); unireg_abort(1);
...@@ -586,26 +598,41 @@ void handle_connections_win() ...@@ -586,26 +598,41 @@ void handle_connections_win()
int n_waits; int n_waits;
create_shutdown_event(); create_shutdown_event();
wait_events[SHUTDOWN_IDX]= hEventShutdown; wait_events.push_back(hEventShutdown);
n_waits= 1; 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(); HANDLE wait_handle= all_listeners[i]->wait_handle();
if (wait_handle) if (wait_handle)
{ {
DBUG_ASSERT((i == 0) || (all_listeners[i - 1]->wait_handle() != 0)); 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(); all_listeners[i]->begin_accept();
} }
mysqld_win_set_startup_complete(); 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 (;;) for (;;)
{ {
DWORD idx = WaitForMultipleObjects(n_waits ,wait_events, FALSE, INFINITE); DBUG_ASSERT(wait_events.size() <= MAXIMUM_WAIT_OBJECTS);
DBUG_ASSERT((int)idx >= 0 && (int)idx < n_waits); 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) if (idx == SHUTDOWN_IDX)
break; break;
...@@ -616,7 +643,7 @@ void handle_connections_win() ...@@ -616,7 +643,7 @@ void handle_connections_win()
mysqld_win_initiate_shutdown(); mysqld_win_initiate_shutdown();
/* Cleanup */ /* Cleanup */
for (int i= 0; i < n_listeners; i++) for (size_t i= 0; i < all_listeners.size(); i++)
{ {
Listener *listener= all_listeners[i]; Listener *listener= all_listeners[i];
if (listener->wait_handle()) if (listener->wait_handle())
......
This diff is collapsed.
...@@ -147,7 +147,6 @@ extern ulong opt_replicate_events_marked_for_skip; ...@@ -147,7 +147,6 @@ extern ulong opt_replicate_events_marked_for_skip;
extern char *default_tz_name; extern char *default_tz_name;
extern Time_zone *default_tz; extern Time_zone *default_tz;
extern char *my_bind_addr_str; extern char *my_bind_addr_str;
extern int server_socket_ai_family;
extern char *default_storage_engine, *default_tmp_storage_engine; extern char *default_storage_engine, *default_tmp_storage_engine;
extern char *enforced_storage_engine; extern char *enforced_storage_engine;
extern char *gtid_pos_auto_engines; extern char *gtid_pos_auto_engines;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment