Commit ba16202e authored by Paragoumba's avatar Paragoumba Committed by GitHub

MDEV-24377: Accept comma separated addresses as --bind-address value (#2009)

* MDEV-24377: Accept comma separated addresses as --bind-address value

When bind address form the basis of wsrep based variables, the first
address in the comma separated list is used.

The test uses the IP address 192.168.0.1 as we need to include
multiple address. This will include failures without the following
commit.

The tests for bind_multiple_address_resolution could return
addresses that we cannot bind too. Windows and FreeBSD, and
probably other OSs will terminate the service if addresses are
unavailable.

We use the WSAEADDRNOTAVAIL / POSIX EADDRNOTAVAIL codes to
continue to bind to other interfaces. If at the end of the
bind list, if no binds are successful, the we terminate
but still leaving the error messages in the log.
Co-authored-by: default avatarDaniel Black <daniel@mariadb.org>
parent fd0dcad6
call mtr.add_suppression("Can't start server: Bind on TCP/IP port.");
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
connection default;
DROP TABLE t;
disconnect con1;
disconnect con2;
--source include/check_ipv6.inc
--source include/not_embedded.inc
call mtr.add_suppression("Can't start server: Bind on TCP/IP port.");
# The server is started with --bind-address=localhost,192.168.0.1, and should
# listen on all addresses 'localhost' resolves to and 192.168.0.1. 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;
# Temporary disable.
# Some cloud-init services don't resolve localhost->::1
# bb - kvm-asan, kvm-deb-focal-amd64, kvm-deb-groovy-amd64
#--connect(con3,::1,root,,test)
#SELECT * FROM t;
--connection default
DROP TABLE t;
--disconnect con1
--disconnect con2
#--disconnect con3
......@@ -42,7 +42,8 @@ The following specify which files/extra groups are read (specified before remain
by saving all temporary sets to disk, avoiding 'table
full' errors. No longer needed, as the server now handles
this automatically.
--bind-address=name IP address to bind to.
--bind-address=name IP address to bind to. Several addresses may be
specified, separated by a comma (,).
--binlog-alter-two-phase
When set, split ALTER at binary logging into 2
statements: START ALTER and COMMIT/ROLLBACK ALTER
......
......@@ -325,7 +325,7 @@ COMMAND_LINE_ARGUMENT OPTIONAL
VARIABLE_NAME BIND_ADDRESS
VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE VARCHAR
VARIABLE_COMMENT IP address to bind to.
VARIABLE_COMMENT IP address to bind to. Several addresses may be specified, separated by a comma (,).
NUMERIC_MIN_VALUE NULL
NUMERIC_MAX_VALUE NULL
NUMERIC_BLOCK_SIZE NULL
......
......@@ -325,7 +325,7 @@ COMMAND_LINE_ARGUMENT OPTIONAL
VARIABLE_NAME BIND_ADDRESS
VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE VARCHAR
VARIABLE_COMMENT IP address to bind to.
VARIABLE_COMMENT IP address to bind to. Several addresses may be specified, separated by a comma (,).
NUMERIC_MIN_VALUE NULL
NUMERIC_MAX_VALUE NULL
NUMERIC_BLOCK_SIZE NULL
......
......@@ -135,7 +135,6 @@
#define mysqld_charset &my_charset_latin1
extern "C" { // Because of SCO 3.2V4.2
#include <sys/stat.h>
#ifndef __GNU_LIBRARY__
......@@ -2291,7 +2290,7 @@ static void activate_tcp_port(uint port,
Dynamic_array<MYSQL_SOCKET> *sockets,
bool is_extra_port= false)
{
struct addrinfo *ai, *a;
struct addrinfo *ai, *a = NULL, *head = NULL;
struct addrinfo hints;
int error;
int arg;
......@@ -2312,16 +2311,52 @@ static void activate_tcp_port(uint port,
real_bind_addr_str= my_bind_addr_str;
my_snprintf(port_buf, NI_MAXSERV, "%d", port);
error= getaddrinfo(real_bind_addr_str, port_buf, &hints, &ai);
if (unlikely(error != 0))
if (real_bind_addr_str && *real_bind_addr_str)
{
DBUG_PRINT("error",("Got error: %d from getaddrinfo()", error));
sql_print_error("%s: %s", ER_DEFAULT(ER_IPSOCK_ERROR), gai_strerror(error));
unireg_abort(1); /* purecov: tested */
char *end;
char address[FN_REFLEN];
do
{
end= strcend(real_bind_addr_str, ',');
strmake(address, real_bind_addr_str, (uint) (end - real_bind_addr_str));
error= getaddrinfo(address, port_buf, &hints, &ai);
if (unlikely(error != 0))
{
DBUG_PRINT("error", ("Got error: %d from getaddrinfo()", error));
sql_print_error("%s: %s", ER_DEFAULT(ER_IPSOCK_ERROR),
gai_strerror(error));
unireg_abort(1); /* purecov: tested */
}
if (!head)
{
head= ai;
}
if (a)
{
a->ai_next= ai;
}
a= ai;
while (a->ai_next)
{
a= a->ai_next;
}
real_bind_addr_str= end + 1;
} while (*end);
}
else
{
error= getaddrinfo(real_bind_addr_str, port_buf, &hints, &ai);
head= ai;
}
for (a= ai; a != NULL; a= a->ai_next)
for (a= head; a != NULL; a= a->ai_next)
{
ip_sock= mysql_socket_socket(key_socket_tcpip, a->ai_family,
a->ai_socktype, a->ai_protocol);
......@@ -2410,9 +2445,31 @@ static void activate_tcp_port(uint port,
if (ret < 0)
{
char buff[100];
int s_errno= socket_errno;
sprintf(buff, "Can't start server: Bind on TCP/IP port. Got error: %d",
(int) socket_errno);
(int) s_errno);
sql_perror(buff);
/*
Linux will quite happily bind to addresses not present. The
mtr test main.bind_multiple_addresses_resolution relies on this.
For Windows, this is fatal and generates the error:
WSAEADDRNOTAVAIL: The requested address is not valid in its context
In this case, where multiple addresses where specified, maybe
we can live with an error in the log and hope the other addresses
are successful. We catch if no successful bindings occur at the
end of this function.
FreeBSD returns EADDRNOTAVAIL, and EADDRNOTAVAIL is even in Linux
manual pages. So may was well apply uniform behaviour.
*/
#ifdef _WIN32
if (s_errno == WSAEADDRNOTAVAIL)
continue;
#endif
#ifdef EADDRNOTAVAIL
if (s_errno == EADDRNOTAVAIL)
continue;
#endif
sql_print_error("Do you already have another server running on "
"port: %u ?", port);
unireg_abort(1);
......@@ -2433,7 +2490,12 @@ static void activate_tcp_port(uint port,
}
}
freeaddrinfo(ai);
freeaddrinfo(head);
if (head && sockets->size() == 0)
{
sql_print_error("No TCP address could be bound to");
unireg_abort(1);
}
DBUG_VOID_RETURN;
}
......
......@@ -511,7 +511,8 @@ static Sys_var_charptr_fscs Sys_basedir(
DEFAULT(0));
static Sys_var_charptr_fscs Sys_my_bind_addr(
"bind_address", "IP address to bind to.",
"bind_address", "IP address to bind to. Several addresses may be "
"specified, separated by a comma (,).",
READ_ONLY GLOBAL_VAR(my_bind_addr_str), CMD_LINE(REQUIRED_ARG),
DEFAULT(0));
......
......@@ -457,7 +457,13 @@ unsigned int wsrep_check_ip (const char* const addr, bool *is_ipv6)
*is_ipv6= false;
int gai_ret= getaddrinfo(addr, NULL, &hints, &res);
char *end;
char address[INET6_ADDRSTRLEN];
end= strcend(addr, ',');
strmake(address, addr, (uint) (end - addr));
int gai_ret= getaddrinfo(address, NULL, &hints, &res);
if (0 == gai_ret)
{
if (AF_INET == res->ai_family) /* IPv4 */
......
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