MXS-2546 Add DNS-resolving to topology detection
When matching hostnames between MaxScale server configuration and the SHOW SLAVE STATUS-output, use DNS-resolution if a simple string comparison doesn't find an answer. Results of the resolution are saved to avoid repeating the operation for the same address.
This commit is contained in:
@ -109,6 +109,16 @@ inline bool operator!=(const Host& l, const Host& r)
|
|||||||
return !(l == 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.
|
* 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.
|
* @param output Where to write the output. If operation fails, original IP is written.
|
||||||
* @return True on success
|
* @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);
|
||||||
}
|
}
|
||||||
|
@ -11,13 +11,15 @@
|
|||||||
* Public License.
|
* Public License.
|
||||||
*/
|
*/
|
||||||
#include <maxbase/host.hh>
|
#include <maxbase/host.hh>
|
||||||
#include <maxbase/string.hh>
|
|
||||||
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
|
#include <maxbase/assert.h>
|
||||||
|
#include <maxbase/format.hh>
|
||||||
|
#include <maxbase/string.hh>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@ -225,7 +227,54 @@ std::istream& operator>>(std::istream& is, Host& host)
|
|||||||
return is;
|
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;
|
sockaddr_storage socket_address;
|
||||||
memset(&socket_address, 0, sizeof(socket_address));
|
memset(&socket_address, 0, sizeof(socket_address));
|
||||||
|
@ -766,7 +766,7 @@ json_t* session_json_data(const Session* session, const char* host, bool rdns)
|
|||||||
auto remote = session->client_dcb->remote;
|
auto remote = session->client_dcb->remote;
|
||||||
if (rdns)
|
if (rdns)
|
||||||
{
|
{
|
||||||
maxbase::reverse_dns(remote, &result_address);
|
maxbase::reverse_name_lookup(remote, &result_address);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -12,11 +12,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "mariadbmon.hh"
|
#include "mariadbmon.hh"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <maxbase/format.hh>
|
#include <maxbase/format.hh>
|
||||||
|
#include <maxbase/host.hh>
|
||||||
#include <maxscale/modutil.hh>
|
#include <maxscale/modutil.hh>
|
||||||
#include <maxscale/mysql_utils.hh>
|
#include <maxscale/mysql_utils.hh>
|
||||||
|
|
||||||
@ -1002,3 +1004,35 @@ bool MariaDBMonitor::is_candidate_valid(MariaDBServer* cand, RequireRunning req_
|
|||||||
}
|
}
|
||||||
return is_valid;
|
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;
|
||||||
|
}
|
||||||
|
@ -94,6 +94,8 @@ void MariaDBMonitor::reset_server_info()
|
|||||||
{
|
{
|
||||||
m_servers.push_back(new MariaDBServer(mon_server, m_servers.size(), m_settings.shared));
|
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()
|
void MariaDBMonitor::reset_node_index_info()
|
||||||
@ -106,10 +108,9 @@ void MariaDBMonitor::reset_node_index_info()
|
|||||||
|
|
||||||
MariaDBServer* MariaDBMonitor::get_server(const EndPoint& search_ep)
|
MariaDBServer* MariaDBMonitor::get_server(const EndPoint& search_ep)
|
||||||
{
|
{
|
||||||
// TODO: Do this with a map lookup
|
|
||||||
// TODO: Add DNS check here.
|
|
||||||
MariaDBServer* found = NULL;
|
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);
|
EndPoint srv(server->m_server_base->server);
|
||||||
if (srv == search_ep)
|
if (srv == search_ep)
|
||||||
@ -118,6 +119,28 @@ MariaDBServer* MariaDBMonitor::get_server(const EndPoint& search_ep)
|
|||||||
break;
|
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;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,6 +169,21 @@ private:
|
|||||||
bool result_waiting = false; /* Guard variable for has_result */
|
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<std::string, MapElement> m_mapping; // hostname -> address cache
|
||||||
|
};
|
||||||
|
|
||||||
ManualCommand m_manual_cmd; /* Communicates manual commands and results */
|
ManualCommand m_manual_cmd; /* Communicates manual commands and results */
|
||||||
|
|
||||||
// Server containers, mostly constant.
|
// Server containers, mostly constant.
|
||||||
@ -185,6 +200,8 @@ private:
|
|||||||
bool m_cluster_modified = false; /* Has a cluster operation been performed this loop? Prevents
|
bool m_cluster_modified = false; /* Has a cluster operation been performed this loop? Prevents
|
||||||
* other operations during this tick. */
|
* other operations during this tick. */
|
||||||
|
|
||||||
|
DNSResolver m_resolver; /* DNS-resolver with cache */
|
||||||
|
|
||||||
/* Counter for temporary automatic cluster operation disabling. */
|
/* Counter for temporary automatic cluster operation disabling. */
|
||||||
int cluster_operation_disable_timer = 0;
|
int cluster_operation_disable_timer = 0;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user