diff --git a/maxutils/maxbase/include/maxbase/host.hh b/maxutils/maxbase/include/maxbase/host.hh index 9d789b378..a290c7f55 100644 --- a/maxutils/maxbase/include/maxbase/host.hh +++ b/maxutils/maxbase/include/maxbase/host.hh @@ -109,6 +109,16 @@ inline bool operator!=(const Host& l, const Host& r) return !(l == r); } +/** + * Perform DNS resolution on a hostname or text-form IP address. + * + * @param host Hostname to convert. + * @param addr_out Output buffer. The output is in IPv6-form as returned by "inet_ntop(AF_INET6, ...)". + * @param error_out Error output + * @return True if successful + */ +bool name_lookup(const std::string& host, std::string* addr_out, std::string* error_out = nullptr); + /** * Perform reverse DNS on an IP address. This may involve network communication so can be slow. * @@ -116,5 +126,5 @@ inline bool operator!=(const Host& l, const Host& r) * @param output Where to write the output. If operation fails, original IP is written. * @return True on success */ -bool reverse_dns(const std::string& ip, std::string* output); +bool reverse_name_lookup(const std::string& ip, std::string* output); } diff --git a/maxutils/maxbase/src/host.cc b/maxutils/maxbase/src/host.cc index b44553217..33a439601 100644 --- a/maxutils/maxbase/src/host.cc +++ b/maxutils/maxbase/src/host.cc @@ -11,13 +11,15 @@ * Public License. */ #include -#include #include #include #include #include #include +#include +#include +#include namespace { @@ -225,7 +227,54 @@ std::istream& operator>>(std::istream& is, Host& host) return is; } -bool reverse_dns(const std::string& ip, std::string* output) +bool name_lookup(const std::string& host, std::string* addr_out, std::string* error_out) +{ + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; /* Only return IPv6-addresses, possibly mapped from IPv4 */ + hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ + hints.ai_flags = AI_V4MAPPED; /* Mapped IPv4 */ + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + addrinfo* results = nullptr; + bool success = false; + std::string error_msg; + int rv_addrinfo = getaddrinfo(host.c_str(), nullptr, &hints, &results); + if (rv_addrinfo == 0) + { + mxb_assert(results); + if (results) + { + // getaddrinfo may return multiple result addresses. Only consider the first. + char buf[INET6_ADDRSTRLEN]; + if (inet_ntop(AF_INET6, results->ai_addr, buf, sizeof(buf))) + { + *addr_out = buf; + success = true; + } + else + { + error_msg = mxb::string_printf("inet_ntop() failed: '%s'.", strerror(errno)); + } + freeaddrinfo(results); + } + } + else + { + error_msg = mxb::string_printf("getaddrinfo() failed: '%s'.", gai_strerror(rv_addrinfo)); + } + + if (error_out) + { + *error_out = error_msg; + } + return success; +} + +bool reverse_name_lookup(const std::string& ip, std::string* output) { sockaddr_storage socket_address; memset(&socket_address, 0, sizeof(socket_address)); diff --git a/server/core/session.cc b/server/core/session.cc index 01424abfe..8c735b178 100644 --- a/server/core/session.cc +++ b/server/core/session.cc @@ -766,7 +766,7 @@ json_t* session_json_data(const Session* session, const char* host, bool rdns) auto remote = session->client_dcb->remote; if (rdns) { - maxbase::reverse_dns(remote, &result_address); + maxbase::reverse_name_lookup(remote, &result_address); } else { diff --git a/server/modules/monitor/mariadbmon/cluster_discovery.cc b/server/modules/monitor/mariadbmon/cluster_discovery.cc index 6d2e23951..9d5ff30b6 100644 --- a/server/modules/monitor/mariadbmon/cluster_discovery.cc +++ b/server/modules/monitor/mariadbmon/cluster_discovery.cc @@ -12,11 +12,13 @@ */ #include "mariadbmon.hh" + #include #include #include #include #include +#include #include #include @@ -1002,3 +1004,35 @@ bool MariaDBMonitor::is_candidate_valid(MariaDBServer* cand, RequireRunning req_ } return is_valid; }; + +string MariaDBMonitor::DNSResolver::resolve_server(const string& host) +{ + auto now = mxb::Clock::now(); + const auto MAX_AGE = mxb::Duration((double)5*60); // Refresh interval for cache entries. + auto recent_time = now - MAX_AGE; + + string rval; + auto iter = m_mapping.find(host); + + if (iter == m_mapping.end() || iter->second.timestamp < recent_time) + { + // Map did not have a record, or it was too old. In either case, generate a new one. + string addr; + string error_msg; + bool dns_success = mxb::name_lookup(host, &addr, &error_msg); + if (!dns_success) + { + MXB_ERROR("Could not resolve host '%s'. %s", host.c_str(), error_msg.c_str()); + } + // If dns failed, addr will be empty. Add the element anyway to prevent repeated lookups. + MapElement newelem = {addr, now}; + m_mapping[host] = newelem; + rval = addr; + } + else + { + // Return recent value. + rval = iter->second.address; + } + return rval; +} diff --git a/server/modules/monitor/mariadbmon/mariadbmon.cc b/server/modules/monitor/mariadbmon/mariadbmon.cc index 50675a848..51d24b8e8 100644 --- a/server/modules/monitor/mariadbmon/mariadbmon.cc +++ b/server/modules/monitor/mariadbmon/mariadbmon.cc @@ -94,6 +94,8 @@ void MariaDBMonitor::reset_server_info() { m_servers.push_back(new MariaDBServer(mon_server, m_servers.size(), m_settings.shared)); } + + m_resolver = DNSResolver(); // Erases result cache. } void MariaDBMonitor::reset_node_index_info() @@ -106,10 +108,9 @@ void MariaDBMonitor::reset_node_index_info() MariaDBServer* MariaDBMonitor::get_server(const EndPoint& search_ep) { - // TODO: Do this with a map lookup - // TODO: Add DNS check here. MariaDBServer* found = NULL; - for (MariaDBServer* server : m_servers) + // Phase 1: Direct string compare + for (auto server : m_servers) { EndPoint srv(server->m_server_base->server); if (srv == search_ep) @@ -118,6 +119,28 @@ MariaDBServer* MariaDBMonitor::get_server(const EndPoint& search_ep) break; } } + + if (!found) + { + // Phase 2: Was not found with simple string compare. Try DNS resolving for endpoints with + // matching ports. + string target_addr = m_resolver.resolve_server(search_ep.host()); + if (!target_addr.empty()) + { + for (auto server : m_servers) + { + if (server->m_server_base->server->port == search_ep.port()) + { + string server_addr = m_resolver.resolve_server(server->m_server_base->server->address); + if (server_addr == target_addr) + { + found = server; + break; + } + } + } + } + } return found; } diff --git a/server/modules/monitor/mariadbmon/mariadbmon.hh b/server/modules/monitor/mariadbmon/mariadbmon.hh index 882247db4..6303296ee 100644 --- a/server/modules/monitor/mariadbmon/mariadbmon.hh +++ b/server/modules/monitor/mariadbmon/mariadbmon.hh @@ -169,6 +169,21 @@ private: bool result_waiting = false; /* Guard variable for has_result */ }; + class DNSResolver + { + public: + std::string resolve_server(const std::string& host); + + private: + struct MapElement + { + std::string address; + mxb::TimePoint timestamp; + }; + + std::unordered_map m_mapping; // hostname -> address cache + }; + ManualCommand m_manual_cmd; /* Communicates manual commands and results */ // Server containers, mostly constant. @@ -185,6 +200,8 @@ private: bool m_cluster_modified = false; /* Has a cluster operation been performed this loop? Prevents * other operations during this tick. */ + DNSResolver m_resolver; /* DNS-resolver with cache */ + /* Counter for temporary automatic cluster operation disabling. */ int cluster_operation_disable_timer = 0;