Commit bb529054 authored by Nirbhay Choubey's avatar Nirbhay Choubey

MDEV-8034 : wsrep_node_address can't be IPV6

Updated address parsing logic to include IPv6 format.
parent 31cf362c
......@@ -618,6 +618,7 @@ int wsrep_init()
wsrep->provider_vendor, sizeof(provider_vendor) - 1);
}
/* Initialize node address */
char node_addr[512]= { 0, };
size_t const node_addr_max= sizeof(node_addr) - 1;
if (!wsrep_node_address || !strcmp(wsrep_node_address, ""))
......@@ -635,86 +636,81 @@ int wsrep_init()
strncpy(node_addr, wsrep_node_address, node_addr_max);
}
/* Initialize node's incoming address */
char inc_addr[512]= { 0, };
size_t const inc_addr_max= sizeof (inc_addr);
/*
In case wsrep_node_incoming_address is either not set or set to AUTO,
we need to use mysqld's my_bind_addr_str:mysqld_port, lastly fallback
to wsrep_node_address' value if mysqld's bind-address is not set either.
*/
if ((!wsrep_node_incoming_address ||
!strcmp (wsrep_node_incoming_address, WSREP_NODE_INCOMING_AUTO)))
{
bool is_ipv6= false;
unsigned int my_bind_ip= INADDR_ANY; // default if not set
if (my_bind_addr_str && strlen(my_bind_addr_str))
{
my_bind_ip= wsrep_check_ip(my_bind_addr_str);
my_bind_ip= wsrep_check_ip(my_bind_addr_str, &is_ipv6);
}
if (INADDR_ANY != my_bind_ip)
{
/*
If its a not a valid address, leave inc_addr as empty string. mysqld
is not listening for client connections on network interfaces.
*/
if (INADDR_NONE != my_bind_ip && INADDR_LOOPBACK != my_bind_ip)
{
snprintf(inc_addr, inc_addr_max, "%s:%u",
my_bind_addr_str, (int)mysqld_port);
} // else leave inc_addr an empty string - mysqld is not listening for
// client connections on network interfaces.
const char *fmt= (is_ipv6) ? "[%s]:%u" : "%s:%u";
snprintf(inc_addr, inc_addr_max, fmt, my_bind_addr_str, mysqld_port);
}
else // mysqld binds to 0.0.0.0, take IP from wsrep_node_address if possible
}
else /* mysqld binds to 0.0.0.0, try taking IP from wsrep_node_address. */
{
size_t const node_addr_len= strlen(node_addr);
if (node_addr_len > 0)
{
const char* const colon= strrchr(node_addr, ':');
if (strchr(node_addr, ':') == colon) // 1 or 0 ':'
{
size_t const ip_len= colon ? colon - node_addr : node_addr_len;
if (ip_len + 7 /* :55555\0 */ < inc_addr_max)
{
memcpy (inc_addr, node_addr, ip_len);
snprintf(inc_addr + ip_len, inc_addr_max - ip_len, ":%u",
(int)mysqld_port);
}
else
{
WSREP_WARN("Guessing address for incoming client connections: "
"address too long.");
inc_addr[0]= '\0';
}
}
else
{
WSREP_WARN("Guessing address for incoming client connections: "
"too many colons :) .");
inc_addr[0]= '\0';
}
}
wsp::Address addr(node_addr);
if (!strlen(inc_addr))
if (!addr.is_valid())
{
WSREP_DEBUG("Could not parse node address : %s", node_addr);
WSREP_WARN("Guessing address for incoming client connections failed. "
"Try setting wsrep_node_incoming_address explicitly.");
goto done;
}
const char *fmt= (addr.is_ipv6()) ? "[%s]:%u" : "%s:%u";
snprintf(inc_addr, inc_addr_max, fmt, addr.get_address(),
(int) mysqld_port);
}
}
else if (!strchr(wsrep_node_incoming_address, ':')) // no port included
{
if ((int)inc_addr_max <=
snprintf(inc_addr, inc_addr_max, "%s:%u",
wsrep_node_incoming_address,(int)mysqld_port))
{
WSREP_WARN("Guessing address for incoming client connections: "
"address too long.");
inc_addr[0]= '\0';
}
}
else
{
size_t const need = strlen (wsrep_node_incoming_address);
if (need >= inc_addr_max) {
WSREP_WARN("wsrep_node_incoming_address too long: %zu", need);
inc_addr[0]= '\0';
}
else {
memcpy (inc_addr, wsrep_node_incoming_address, need);
wsp::Address addr(wsrep_node_incoming_address);
if (!addr.is_valid())
{
WSREP_WARN("Could not parse wsrep_node_incoming_address : %s",
wsrep_node_incoming_address);
goto done;
}
/*
In case port is not specified in wsrep_node_incoming_address, we use
mysqld_port.
*/
int port= (addr.get_port() > 0) ? addr.get_port() : (int) mysqld_port;
const char *fmt= (addr.is_ipv6()) ? "[%s]:%u" : "%s:%u";
snprintf(inc_addr, inc_addr_max, fmt, addr.get_address(), port);
}
done:
struct wsrep_init_args wsrep_args;
struct wsrep_gtid const state_id = { local_uuid, local_seqno };
......
......@@ -629,8 +629,6 @@ static bool SE_initialized = false;
ssize_t wsrep_sst_prepare (void** msg)
{
const ssize_t ip_max= 256;
char ip_buf[ip_max];
const char* addr_in= NULL;
const char* addr_out= NULL;
......@@ -646,27 +644,34 @@ ssize_t wsrep_sst_prepare (void** msg)
return ret;
}
// Figure out SST address. Common for all SST methods
/*
Figure out SST receive address. Common for all SST methods.
*/
char ip_buf[256];
const ssize_t ip_max= sizeof(ip_buf);
// Attempt 1: wsrep_sst_receive_address
if (wsrep_sst_receive_address &&
strcmp (wsrep_sst_receive_address, WSREP_SST_ADDRESS_AUTO))
{
addr_in= wsrep_sst_receive_address;
}
//Attempt 2: wsrep_node_address
else if (wsrep_node_address && strlen(wsrep_node_address))
{
const char* const colon= strchr (wsrep_node_address, ':');
if (colon)
{
ptrdiff_t const len= colon - wsrep_node_address;
strncpy (ip_buf, wsrep_node_address, len);
ip_buf[len]= '\0';
addr_in= ip_buf;
}
else
wsp::Address addr(wsrep_node_address);
if (!addr.is_valid())
{
addr_in= wsrep_node_address;
WSREP_ERROR("Could not parse wsrep_node_address : %s",
wsrep_node_address);
unireg_abort(1);
}
memcpy(ip_buf, addr.get_address(), addr.get_address_len());
addr_in= ip_buf;
}
// Attempt 3: Try to get the IP from the list of available interfaces.
else
{
ssize_t ret= wsrep_guess_ip (ip_buf, ip_max);
......@@ -677,8 +682,7 @@ ssize_t wsrep_sst_prepare (void** msg)
}
else
{
WSREP_ERROR("Could not prepare state transfer request: "
"failed to guess address to accept state transfer at. "
WSREP_ERROR("Failed to guess address to accept state transfer. "
"wsrep_sst_receive_address must be set manually.");
unireg_abort(1);
}
......@@ -778,8 +782,10 @@ static void sst_reject_queries(my_bool close_conn)
if (TRUE == close_conn) wsrep_close_client_connections(FALSE);
}
static int sst_mysqldump_check_addr (const char* user, const char* pswd,
const char* host, const char* port)
static int sst_mysqldump_check_addr (const char* user,
const char* pswd,
const char* host,
int port)
{
return 0;
}
......@@ -790,25 +796,17 @@ static int sst_donate_mysqldump (const char* addr,
wsrep_seqno_t seqno,
bool bypass)
{
size_t host_len;
const char* port = strchr (addr, ':');
char host[256];
wsp::Address address(addr);
if (port)
{
port += 1;
host_len = port - addr;
}
else
if (!address.is_valid())
{
port = "";
host_len = strlen (addr) + 1;
WSREP_ERROR("Could not parse SST address : %s", addr);
return 0;
}
char *host= (char *) alloca(host_len);
strncpy (host, addr, host_len - 1);
host[host_len - 1] = '\0';
memcpy(host, address.get_address(), address.get_address_len());
int port= address.get_port();
const char* auth = sst_auth_real;
const char* pswd = (auth) ? strchr (auth, ':') : NULL;
size_t user_len;
......@@ -843,7 +841,7 @@ static int sst_donate_mysqldump (const char* addr,
WSREP_SST_OPT_USER" '%s' "
WSREP_SST_OPT_PSWD" '%s' "
WSREP_SST_OPT_HOST" '%s' "
WSREP_SST_OPT_PORT" '%s' "
WSREP_SST_OPT_PORT" '%d' "
WSREP_SST_OPT_LPORT" '%u' "
WSREP_SST_OPT_SOCKET" '%s' "
" %s "
......
......@@ -352,7 +352,7 @@ thd::~thd ()
} // namespace wsp
/* Returns INADDR_NONE, INADDR_ANY, INADDR_LOOPBACK or something else */
unsigned int wsrep_check_ip (const char* const addr)
unsigned int wsrep_check_ip (const char* const addr, bool *is_ipv6)
{
unsigned int ret = INADDR_NONE;
struct addrinfo *res, hints;
......@@ -362,6 +362,8 @@ unsigned int wsrep_check_ip (const char* const addr)
hints.ai_socktype= SOCK_STREAM;
hints.ai_family= AF_UNSPEC;
*is_ipv6= false;
int gai_ret = getaddrinfo(addr, NULL, &hints, &res);
if (0 == gai_ret)
{
......@@ -379,6 +381,8 @@ unsigned int wsrep_check_ip (const char* const addr)
ret= INADDR_LOOPBACK;
else
ret= 0xdeadbeef;
*is_ipv6= true;
}
freeaddrinfo (res);
}
......@@ -387,10 +391,6 @@ unsigned int wsrep_check_ip (const char* const addr)
addr, gai_ret, gai_strerror(gai_ret));
}
// uint8_t* b= (uint8_t*)&ret;
// fprintf (stderr, "########## wsrep_check_ip returning: %hhu.%hhu.%hhu.%hhu\n",
// b[0], b[1], b[2], b[3]);
return ret;
}
......@@ -398,44 +398,47 @@ extern char* my_bind_addr_str;
size_t wsrep_guess_ip (char* buf, size_t buf_len)
{
size_t ip_len = 0;
size_t ret= 0;
// Attempt 1: Try to get the IP from bind-address.
if (my_bind_addr_str && my_bind_addr_str[0] != '\0')
{
unsigned int const ip_type= wsrep_check_ip(my_bind_addr_str);
bool unused;
unsigned int const ip_type= wsrep_check_ip(my_bind_addr_str, &unused);
if (INADDR_NONE == ip_type) {
WSREP_ERROR("Networking not configured, cannot receive state "
"transfer.");
return 0;
}
if (INADDR_ANY != ip_type) {
ret= 0;
} else if (INADDR_ANY != ip_type) {
strncpy (buf, my_bind_addr_str, buf_len);
return strlen(buf);
ret= strlen(buf);
}
goto done;
}
// mysqld binds to all interfaces - try IP from wsrep_node_address
// Attempt 2: mysqld binds to all interfaces, try IP from wsrep_node_address.
if (wsrep_node_address && wsrep_node_address[0] != '\0') {
const char* const colon_ptr = strchr(wsrep_node_address, ':');
if (colon_ptr)
ip_len = colon_ptr - wsrep_node_address;
else
ip_len = strlen(wsrep_node_address);
if (ip_len >= buf_len) {
WSREP_WARN("default_ip(): buffer too short: %zu <= %zd", buf_len, ip_len);
return 0;
wsp::Address addr(wsrep_node_address);
if (!addr.is_valid())
{
WSREP_WARN("Could not parse wsrep_node_address : %s",
wsrep_node_address);
ret= 0;
goto done;
}
memcpy (buf, wsrep_node_address, ip_len);
buf[ip_len] = '\0';
return ip_len;
/* Safety check: Buffer length should be sufficiently large. */
DBUG_ASSERT(buf_len >= addr.get_address_len());
memcpy(buf, addr.get_address(), addr.get_address_len());
ret= addr.get_address_len();
goto done;
}
/*
Attempt 3: Try to get the IP from the list of available interfaces.
getifaddrs() is avaiable at least on Linux since glib 2.3, FreeBSD,
MAC OSX, OpenSolaris, Solaris.
......@@ -444,26 +447,42 @@ size_t wsrep_guess_ip (char* buf, size_t buf_len)
*/
#if HAVE_GETIFADDRS
struct ifaddrs *ifaddr, *ifa;
int family;
if (getifaddrs(&ifaddr) == 0)
{
for (ifa= ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET) // TODO AF_INET6
if (!ifa->ifa_addr)
continue;
family= ifa->ifa_addr->sa_family;
if ((family != AF_INET) && (family != AF_INET6))
continue;
// Skip loopback interfaces (like lo:127.0.0.1)
if (ifa->ifa_flags & IFF_LOOPBACK)
continue;
/*
Get IP address from the socket address. The resulting address may have
zone ID appended for IPv6 addresses (<address>%<zone-id>).
*/
if (vio_getnameinfo(ifa->ifa_addr, buf, buf_len, NULL, 0, NI_NUMERICHOST))
continue;
freeifaddrs(ifaddr);
return strlen(buf);
ret= strlen(buf);
goto done;
}
freeifaddrs(ifaddr);
}
#endif /* HAVE_GETIFADDRS */
return 0;
done:
WSREP_DEBUG("wsrep_guess_ip() : %s", (ret > 0) ? buf : "????");
return ret;
}
......@@ -19,11 +19,153 @@
#include "wsrep_priv.h"
#include "wsrep_mysqld.h"
unsigned int wsrep_check_ip (const char* addr);
unsigned int wsrep_check_ip (const char* addr, bool *is_ipv6);
size_t wsrep_guess_ip (char* buf, size_t buf_len);
namespace wsp {
class Address {
public:
Address()
: m_address_len(0), m_family(UNSPEC), m_port(0), m_valid(false)
{
memset(m_address, 0, sizeof(m_address));
}
Address(const char *addr_in)
: m_address_len(0), m_family(UNSPEC), m_port(0), m_valid(false)
{
memset(m_address, 0, sizeof(m_address));
parse_addr(addr_in);
}
bool is_valid() { return m_valid; }
bool is_ipv6() { return (m_family == INET6); }
const char* get_address() { return m_address; }
size_t get_address_len() { return m_address_len; }
int get_port() { return m_port; }
private:
enum family {
UNSPEC= 0,
INET, /* IPv4 */
INET6, /* IPv6 */
};
char m_address[256];
size_t m_address_len;
family m_family;
int m_port;
bool m_valid;
void parse_addr(const char *addr_in) {
const char *start;
const char *end;
const char *port;
const char* open_bracket= strchr(const_cast<char *>(addr_in), '[');
const char* close_bracket= strchr(const_cast<char *>(addr_in), ']');
const char* colon= strchr(const_cast<char *>(addr_in), ':');
const char* dot= strchr(const_cast<char *>(addr_in), '.');
int cc= colon_count(addr_in);
if (open_bracket != NULL ||
dot == NULL ||
(colon != NULL && (dot == NULL || colon < dot)))
{
// This could be an IPv6 address or a hostname
if (open_bracket != NULL) {
/* Sanity check: Address with '[' must include ']' */
if (close_bracket == NULL &&
open_bracket < close_bracket) /* Error: malformed address */
{
m_valid= false;
return;
}
start= open_bracket + 1;
end= close_bracket;
/* Check for port */
port= strchr(close_bracket, ':');
if ((port != NULL) && parse_port(port + 1))
{
return; /* Error: invalid port */
}
m_family= INET6;
}
else
{
switch (cc) {
case 0:
/* Hostname with no port */
start= addr_in;
end= addr_in + strlen(addr_in);
break;
case 1:
/* Hostname with port (host:port) */
start= addr_in;
end= colon;
parse_port(colon + 1);
break;
default:
/* IPv6 address */
start= addr_in;
end= addr_in + strlen(addr_in);
m_family= INET6;
break;
}
}
} else { /* IPv4 address or hostname */
start= addr_in;
if (colon != NULL) { /* Port */
end= colon;
if (parse_port(colon + 1))
return; /* Error: invalid port */
} else {
end= addr_in + strlen(addr_in);
}
}
size_t len= end - start;
/* Safety */
if (len >= sizeof(m_address))
{
// The supplied address is too large to fit into the internal buffer.
m_valid= false;
return;
}
memcpy(m_address, start, len);
m_address[len]= '\0';
m_address_len= ++ len;
m_valid= true;
return;
}
int colon_count(const char *addr) {
int count= 0, i= 0;
while(addr[i] != '\0')
{
if (addr[i] == ':') ++count;
++ i;
}
return count;
}
bool parse_port(const char *port) {
m_port= strtol(port, NULL, 10);
if (errno == EINVAL || errno == ERANGE)
{
m_port= 0; /* Error: invalid port */
m_valid= false;
return true;
}
return false;
}
};
class Config_state
{
public:
......
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