MXS-1973 Support reverse DNS for client hostnames in MaxCtrl
May slow maxscale down when used. Only supported for "list sessions", "show sessions" and "show session <id>".
This commit is contained in:
parent
0e0342e657
commit
e3b5ba9620
@ -15,6 +15,10 @@ GET /v1/sessions/:id
|
||||
Get a single session. _:id_ must be a valid session ID. The session ID is the
|
||||
same that is exposed to the client as the connection ID.
|
||||
|
||||
This endpoint also supports the `rdns=true` parameter, which instructs MaxScale to
|
||||
perform reverse DNS on the client IP address. As this requires communicating with
|
||||
an external server, the operation may be expensive.
|
||||
|
||||
#### Response
|
||||
|
||||
`Status: 200 OK`
|
||||
|
@ -438,19 +438,19 @@ void session_put_ref(MXS_SESSION* session);
|
||||
*
|
||||
* @param session Session to convert
|
||||
* @param host Hostname of this server
|
||||
*
|
||||
* @param rdns Attempt reverse DNS on client ip address
|
||||
* @return New JSON object or NULL on error
|
||||
*/
|
||||
json_t* session_to_json(const MXS_SESSION* session, const char* host);
|
||||
json_t* session_to_json(const MXS_SESSION* session, const char* host, bool rdns);
|
||||
|
||||
/**
|
||||
* @brief Convert all sessions to JSON
|
||||
*
|
||||
* @param host Hostname of this server
|
||||
*
|
||||
* @param rdns Attempt reverse DNS on client ip addresses
|
||||
* @return A JSON array with all sessions
|
||||
*/
|
||||
json_t* session_list_to_json(const char* host);
|
||||
json_t* session_list_to_json(const char* host, bool rdns);
|
||||
|
||||
/**
|
||||
* Qualify the session for connection pooling
|
||||
|
@ -409,6 +409,16 @@ module.exports = function() {
|
||||
this.error = function(err) {
|
||||
return Promise.reject(colors.red('Error: ') + err)
|
||||
}
|
||||
|
||||
this.rDnsOption = {
|
||||
shortname: 'rdns',
|
||||
optionOn: 'rdns=true',
|
||||
definition : {
|
||||
describe: 'Reverse DNS on client IP. May slow MaxScale down.',
|
||||
type: 'bool',
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -115,9 +115,15 @@ exports.builder = function(yargs) {
|
||||
.command('sessions', 'List sessions', function(yargs) {
|
||||
return yargs.epilog('List all client sessions.')
|
||||
.usage('Usage: list sessions')
|
||||
.group([rDnsOption.shortname], 'Options:')
|
||||
.option(rDnsOption.shortname, rDnsOption.definition)
|
||||
}, function(argv) {
|
||||
maxctrl(argv, function(host) {
|
||||
return getCollection(host, 'sessions',[
|
||||
var resource = 'sessions'
|
||||
if (argv[this.rDnsOption.shortname]) {
|
||||
resource += '?' + this.rDnsOption.optionOn
|
||||
}
|
||||
return getCollection(host, resource,[
|
||||
{'Id': 'id'},
|
||||
{'User': 'attributes.user'},
|
||||
{'Host': 'attributes.remote'},
|
||||
|
@ -174,18 +174,30 @@ exports.builder = function(yargs) {
|
||||
'the session is connected and the `Connection IDs` ' +
|
||||
'field lists the IDs for those connections.')
|
||||
.usage('Usage: show session <session>')
|
||||
.group([rDnsOption.shortname], 'Options:')
|
||||
.option(rDnsOption.shortname, rDnsOption.definition)
|
||||
}, function(argv) {
|
||||
maxctrl(argv, function(host) {
|
||||
return getResource(host, 'sessions/' + argv.session, session_fields)
|
||||
var resource = 'sessions/' + argv.session
|
||||
if (argv[this.rDnsOption.shortname]) {
|
||||
resource += '?' + this.rDnsOption.optionOn
|
||||
}
|
||||
return getResource(host, resource, session_fields)
|
||||
})
|
||||
})
|
||||
.command('sessions', 'Show all sessions', function(yargs) {
|
||||
return yargs.epilog('Show detailed information about all sessions. ' +
|
||||
'See `help show session` for more details.')
|
||||
.usage('Usage: show sessions')
|
||||
.group([rDnsOption.shortname], 'Options:')
|
||||
.option(rDnsOption.shortname, rDnsOption.definition)
|
||||
}, function(argv) {
|
||||
maxctrl(argv, function(host) {
|
||||
return getCollectionAsResource(host, 'sessions/', session_fields)
|
||||
var resource = 'sessions/'
|
||||
if (argv[this.rDnsOption.shortname]) {
|
||||
resource += '?' + this.rDnsOption.optionOn
|
||||
}
|
||||
return getCollectionAsResource(host, resource, session_fields)
|
||||
})
|
||||
})
|
||||
.command('filter <filter>', 'Show filter', function(yargs) {
|
||||
|
@ -108,4 +108,14 @@ inline bool operator!=(const Host& l, const Host& r)
|
||||
{
|
||||
return !(l == r);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform reverse DNS on an IP address. This may involve network communication so can be slow.
|
||||
*
|
||||
* @param ip IP to convert to hostname
|
||||
* @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);
|
||||
|
||||
}
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include <ostream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -222,4 +224,52 @@ std::istream& operator>>(std::istream& is, Host& host)
|
||||
host = Host(input);
|
||||
return is;
|
||||
}
|
||||
|
||||
bool reverse_dns(const std::string& ip, std::string* output)
|
||||
{
|
||||
sockaddr_storage socket_address;
|
||||
memset(&socket_address, 0, sizeof(socket_address));
|
||||
socklen_t slen = 0;
|
||||
|
||||
if (is_valid_ipv4(ip))
|
||||
{
|
||||
// Casts between the different sockaddr-types should work.
|
||||
int family = AF_INET;
|
||||
auto sa_in = reinterpret_cast<sockaddr_in*>(&socket_address);
|
||||
if (inet_pton(family, ip.c_str(), &sa_in->sin_addr) == 1)
|
||||
{
|
||||
sa_in->sin_family = family;
|
||||
slen = sizeof(sockaddr_in);
|
||||
}
|
||||
}
|
||||
else if (is_valid_ipv6(ip))
|
||||
{
|
||||
int family = AF_INET6;
|
||||
auto sa_in6 = reinterpret_cast<sockaddr_in6*>(&socket_address);
|
||||
if (inet_pton(family, ip.c_str(), &sa_in6->sin6_addr) == 1)
|
||||
{
|
||||
sa_in6->sin6_family = family;
|
||||
slen = sizeof(sockaddr_in6);
|
||||
}
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
if (slen > 0)
|
||||
{
|
||||
char host[NI_MAXHOST];
|
||||
auto sa = reinterpret_cast<sockaddr*>(&socket_address);
|
||||
if (getnameinfo(sa, slen, host, sizeof(host), nullptr, 0, NI_NAMEREQD) == 0)
|
||||
{
|
||||
*output = host;
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
*output = ip;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -178,6 +178,12 @@ bool Resource::requires_body() const
|
||||
namespace
|
||||
{
|
||||
|
||||
bool option_rdns_is_on(const HttpRequest& request)
|
||||
{
|
||||
return request.get_option("rdns") == "true";
|
||||
}
|
||||
|
||||
|
||||
static bool drop_path_part(std::string& path)
|
||||
{
|
||||
size_t pos = path.find_last_of('/');
|
||||
@ -622,7 +628,8 @@ HttpResponse cb_get_monitor(const HttpRequest& request)
|
||||
|
||||
HttpResponse cb_all_sessions(const HttpRequest& request)
|
||||
{
|
||||
return HttpResponse(MHD_HTTP_OK, session_list_to_json(request.host()));
|
||||
bool rdns = option_rdns_is_on(request);
|
||||
return HttpResponse(MHD_HTTP_OK, session_list_to_json(request.host(), rdns));
|
||||
}
|
||||
|
||||
HttpResponse cb_get_session(const HttpRequest& request)
|
||||
@ -632,7 +639,8 @@ HttpResponse cb_get_session(const HttpRequest& request)
|
||||
|
||||
if (session)
|
||||
{
|
||||
json_t* json = session_to_json(session, request.host());
|
||||
bool rdns = option_rdns_is_on(request);
|
||||
json_t* json = session_to_json(session, request.host(), rdns);
|
||||
session_put_ref(session);
|
||||
return HttpResponse(MHD_HTTP_OK, json);
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <sstream>
|
||||
|
||||
#include <maxbase/atomic.hh>
|
||||
#include <maxbase/host.hh>
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/clock.h>
|
||||
#include <maxscale/dcb.hh>
|
||||
@ -714,7 +715,7 @@ uint64_t session_get_next_id()
|
||||
return mxb::atomic::add(&this_unit.next_session_id, 1, mxb::atomic::RELAXED);
|
||||
}
|
||||
|
||||
json_t* session_json_data(const Session* session, const char* host)
|
||||
json_t* session_json_data(const Session* session, const char* host, bool rdns)
|
||||
{
|
||||
json_t* data = json_object();
|
||||
|
||||
@ -761,7 +762,17 @@ json_t* session_json_data(const Session* session, const char* host)
|
||||
|
||||
if (session->client_dcb->remote)
|
||||
{
|
||||
json_object_set_new(attr, "remote", json_string(session->client_dcb->remote));
|
||||
string result_address;
|
||||
auto remote = session->client_dcb->remote;
|
||||
if (rdns)
|
||||
{
|
||||
maxbase::reverse_dns(remote, &result_address);
|
||||
}
|
||||
else
|
||||
{
|
||||
result_address = remote;
|
||||
}
|
||||
json_object_set_new(attr, "remote", json_string(result_address.c_str()));
|
||||
}
|
||||
|
||||
struct tm result;
|
||||
@ -798,18 +809,26 @@ json_t* session_json_data(const Session* session, const char* host)
|
||||
return data;
|
||||
}
|
||||
|
||||
json_t* session_to_json(const MXS_SESSION* session, const char* host)
|
||||
json_t* session_to_json(const MXS_SESSION* session, const char* host, bool rdns)
|
||||
{
|
||||
stringstream ss;
|
||||
ss << MXS_JSON_API_SESSIONS << session->ses_id;
|
||||
const Session* s = static_cast<const Session*>(session);
|
||||
return mxs_json_resource(host, ss.str().c_str(), session_json_data(s, host));
|
||||
return mxs_json_resource(host, ss.str().c_str(), session_json_data(s, host, rdns));
|
||||
}
|
||||
|
||||
struct SessionListData
|
||||
{
|
||||
json_t* json;
|
||||
const char* host;
|
||||
SessionListData(const char* host, bool rdns)
|
||||
: json(json_array())
|
||||
, host(host)
|
||||
, rdns(rdns)
|
||||
{
|
||||
}
|
||||
|
||||
json_t* json {nullptr};
|
||||
const char* host {nullptr};
|
||||
bool rdns {false};
|
||||
};
|
||||
|
||||
bool seslist_cb(DCB* dcb, void* data)
|
||||
@ -818,15 +837,15 @@ bool seslist_cb(DCB* dcb, void* data)
|
||||
{
|
||||
SessionListData* d = (SessionListData*)data;
|
||||
Session* session = static_cast<Session*>(dcb->session);
|
||||
json_array_append_new(d->json, session_json_data(session, d->host));
|
||||
json_array_append_new(d->json, session_json_data(session, d->host, d->rdns));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
json_t* session_list_to_json(const char* host)
|
||||
json_t* session_list_to_json(const char* host, bool rdns)
|
||||
{
|
||||
SessionListData data = {json_array(), host};
|
||||
SessionListData data(host, rdns);
|
||||
dcb_foreach(seslist_cb, &data);
|
||||
return mxs_json_resource(host, MXS_JSON_API_SESSIONS, data.json);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user