2024-01-31 17:34:53 +00:00

195 lines
6.3 KiB
C++

#include "geo_location.h"
#include <maxminddb.h>
#include "config.h"
#include "debug.h"
#include "enum_range.h"
using namespace std;
USE_DEBUG_FLAG(D_GEO_DB);
class GeoLocation::Impl : Singleton::Provide<I_GeoLocation>::From<GeoLocation>
{
public:
void
preload()
{
registerExpectedConfigFile("agentGeoDb", Config::ConfigFileType::RawData);
}
void
init()
{
registerConfigLoadCb([this]() { initGeoDbObj(); });
initGeoDbObj();
}
void
fini()
{
if (mind_db_obj_status != MMDB_SUCCESS) return;
MMDB_close(&max_mind_db_obj);
dbgTrace(D_GEO_DB) << "Closed geo location DB";
}
Maybe<EnumArray<I_GeoLocation::GeoLocationField, string>>
lookupLocation(const string &ip) override
{
dbgFlow(D_GEO_DB) << "Geo location lookup by string";
if (mind_db_obj_status != MMDB_SUCCESS) {
dbgDebug(D_GEO_DB) << "Maxmind db is uninitialized";
return genError("Maxmind db is uninitialized");
}
Maybe<IPAddr> maybe_ip_addr = IPAddr::createIPAddr(ip);
if (!maybe_ip_addr.ok()) {
dbgWarning(D_GEO_DB)
<< "Error in creating IPAddr from string: "
<< ip
<< ", error: "
<< maybe_ip_addr.getErr();
return genError(
"Error in creating IPAddr from string: " +
ip +
", error: " +
maybe_ip_addr.getErr()
);
}
return lookupLocation(maybe_ip_addr.unpack());
}
Maybe<EnumArray<I_GeoLocation::GeoLocationField, string>>
lookupLocation(const IPAddr &ip) override
{
dbgFlow(D_GEO_DB) << "Geo location lookup by IPAddr";
if (mind_db_obj_status != MMDB_SUCCESS) {
dbgDebug(D_GEO_DB) << "Maxmind db is uninitialized";
return genError("Maxmind db is uninitialized");
}
int maxminddb_error;
struct sockaddr sockaddr_to_search = convertIPAddrtoSockaddr(ip);
MMDB_lookup_result_s result = MMDB_lookup_sockaddr(&max_mind_db_obj, &sockaddr_to_search, &maxminddb_error);
if (maxminddb_error != MMDB_SUCCESS) {
dbgWarning(D_GEO_DB) << "maxMindDB error: " << MMDB_strerror(maxminddb_error);
return genError("maxMindDB error: " + string(MMDB_strerror(maxminddb_error)));
}
if (result.found_entry) {
return getGeoLocationDetails(result);
}
return genError("No results were found by lookup geo location");
}
private:
void
initGeoDbObj()
{
if (mind_db_obj_status == MMDB_SUCCESS) {
dbgTrace(D_GEO_DB) << "Closing an open geo location DB file";
MMDB_close(&max_mind_db_obj);
mind_db_obj_status = -1;
}
string geo_location_db_file = getPolicyConfigPath("agentGeoDb", Config::ConfigFileType::RawData);
dbgDebug(D_GEO_DB) << "Path to GeoDb file" << geo_location_db_file;
if (geo_location_db_file == "") {
dbgWarning(D_GEO_DB) << "No geo location db file specified";
return;
}
mind_db_obj_status = MMDB_open(geo_location_db_file.c_str(), MMDB_MODE_MMAP, &max_mind_db_obj);
if (mind_db_obj_status != MMDB_SUCCESS) {
dbgWarning(D_GEO_DB) << "maxMindDB error: " << MMDB_strerror(mind_db_obj_status);
if (mind_db_obj_status == MMDB_IO_ERROR) {
dbgWarning(D_GEO_DB) << "maxMindDB IO error: " << strerror(mind_db_obj_status);
}
return;
}
dbgDebug(D_GEO_DB) << "Successfully Opened geo location DB";
}
EnumArray<I_GeoLocation::GeoLocationField, string>
getGeoLocationDetails(MMDB_lookup_result_s &result)
{
EnumArray<I_GeoLocation::GeoLocationField, string> geo_location_details;
for (I_GeoLocation::GeoLocationField geo_field : makeRange<I_GeoLocation::GeoLocationField>()) {
geo_location_details[geo_field] = getGeoLocationValueResults(result, geo_field);
}
return geo_location_details;
}
string
getGeoLocationValueResults(MMDB_lookup_result_s &result, I_GeoLocation::GeoLocationField field_type)
{
MMDB_entry_data_s entry_data;
int status = -1;
switch (field_type) {
case I_GeoLocation::GeoLocationField::COUNTRY_NAME: {
status = MMDB_get_value(&result.entry, &entry_data, "country", "names", "en", NULL);
break;
}
case I_GeoLocation::GeoLocationField::COUNTRY_CODE: {
status = MMDB_get_value(&result.entry, &entry_data, "country", "iso_code", NULL);
break;
}
case I_GeoLocation::GeoLocationField::CONTINENT_NAME: {
status = MMDB_get_value(&result.entry, &entry_data, "continent", "names", "en", NULL);
break;
}
case I_GeoLocation::GeoLocationField::CONTINENT_CODE: {
status = MMDB_get_value(&result.entry, &entry_data, "continent", "code", NULL);
break;
}
default: {
dbgError(D_GEO_DB) << "Invalid geo location field type";
break;
}
}
if (status != MMDB_SUCCESS) {
dbgWarning(D_GEO_DB) << "maxMindDB error: " << MMDB_strerror(status);
} else if (!entry_data.has_data) {
dbgWarning(D_GEO_DB) << "maxMindDB Entry has no data";
} else {
string search_result(entry_data.utf8_string, entry_data.data_size);
return search_result;
}
return "";
}
struct sockaddr
convertIPAddrtoSockaddr(const IPAddr &address)
{
if (address.getType() == IPType::V6) {
struct sockaddr_in6 sa6;
sa6.sin6_family = AF_INET6;
sa6.sin6_addr = address.getIPv6();
return *(struct sockaddr *)&sa6;
}
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_addr = address.getIPv4();
return *(struct sockaddr *)&sa;
}
MMDB_s max_mind_db_obj;
int mind_db_obj_status = -1;
};
GeoLocation::GeoLocation() : Component("GeoLocation"), pimpl(make_unique<Impl>()) {}
GeoLocation::~GeoLocation() {}
void GeoLocation::preload() { pimpl->preload(); }
void GeoLocation::init() { pimpl->init(); }
void GeoLocation::fini() { pimpl->fini(); }