diff options
author | Nirbhay Choubey <nirbhay@mariadb.com> | 2015-09-15 18:58:08 -0400 |
---|---|---|
committer | Nirbhay Choubey <nirbhay@mariadb.com> | 2015-09-15 18:58:08 -0400 |
commit | bb52905432779d1648241baa5945c61617f2d58f (patch) | |
tree | 369b6c90366e3c8add210d0367e2d0cc05fcff7e | |
parent | 31cf362c21b24ebda9d3997a401750ec94a19d5c (diff) | |
download | mariadb-git-bb52905432779d1648241baa5945c61617f2d58f.tar.gz |
MDEV-8034 : wsrep_node_address can't be IPV6
Updated address parsing logic to include IPv6 format.
-rw-r--r-- | sql/wsrep_mysqld.cc | 96 | ||||
-rw-r--r-- | sql/wsrep_sst.cc | 64 | ||||
-rw-r--r-- | sql/wsrep_utils.cc | 77 | ||||
-rw-r--r-- | sql/wsrep_utils.h | 144 |
4 files changed, 268 insertions, 113 deletions
diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index a785b8764cf..d72d9fdf151 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -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 }; diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc index 7e6bc2123dc..b31d433de08 100644 --- a/sql/wsrep_sst.cc +++ b/sql/wsrep_sst.cc @@ -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 " diff --git a/sql/wsrep_utils.cc b/sql/wsrep_utils.cc index 7a87d38d430..80f8bd4c7d4 100644 --- a/sql/wsrep_utils.cc +++ b/sql/wsrep_utils.cc @@ -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; } + diff --git a/sql/wsrep_utils.h b/sql/wsrep_utils.h index 1cc65578202..32829c605fb 100644 --- a/sql/wsrep_utils.h +++ b/sql/wsrep_utils.h @@ -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: |