-
-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
54 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,8 +17,8 @@ | |
*/ | ||
|
||
/// $ModAuthor: reverse <[email protected]> | ||
/// $ModDesc: Adds city and country information to WHOIS using local MaxMind database and with it's own usermode +y. | ||
/// $ModConfig: <geolite dbpath="path/geodata/GeoLite2-City.mmdb"> | ||
/// $ModDesc: Adds city and country information to WHOIS using a local MaxMind GeoLite2 database with user mode +y. | ||
/// $ModConfig: <geomaxlite dbpath="path/geodata/GeoLite2-City.mmdb"> | ||
/// $ModDepends: core 4 | ||
|
||
/// $LinkerFlags: -lmaxminddb | ||
|
@@ -34,40 +34,45 @@ | |
#include "extension.h" | ||
#include <maxminddb.h> | ||
|
||
class GeoLiteMode final : public SimpleUserMode | ||
{ | ||
public: | ||
GeoLiteMode(Module* mod) | ||
: SimpleUserMode(mod, "geolite", 'y', false) | ||
{ | ||
} | ||
}; | ||
|
||
class ModuleWhoisGeoLite final : public Module, public Whois::EventListener | ||
class ModuleGeoMaxLite final | ||
: public Module | ||
, public Whois::EventListener | ||
{ | ||
private: | ||
MMDB_s mmdb; | ||
std::string dbpath; | ||
// MaxMind DB handle | ||
MMDB_s mmdb; | ||
|
||
// Path to the MaxMind database file | ||
std::string dbpath; | ||
|
||
// Extension item to store a user's city/country info | ||
StringExtItem country_item; | ||
GeoLiteMode geolite_mode; | ||
|
||
// +y user mode for enabling GeoMaxLite lookups | ||
SimpleUserMode geomaxlite_mode; | ||
|
||
public: | ||
ModuleWhoisGeoLite() | ||
: Module(VF_OPTCOMMON, "Adds city and country information to WHOIS using the MaxMind database.") | ||
ModuleGeoMaxLite() | ||
: Module(VF_OPTCOMMON, "Adds city and country information to WHOIS using a local MaxMind GeoLite2 database.") | ||
, Whois::EventListener(this) | ||
, country_item(this, "geo-lite-country", ExtensionType::USER, true) // Sync across servers | ||
, geolite_mode(this) | ||
, country_item(this, "geomaxlite-country", ExtensionType::USER, true) | ||
, geomaxlite_mode(this, "geomaxlite", 'y', false) | ||
{ | ||
} | ||
|
||
void ReadConfig(ConfigStatus& status) override | ||
{ | ||
auto& tag = ServerInstance->Config->ConfValue("geolite"); | ||
dbpath = ServerInstance->Config->Paths.PrependConfig(tag->getString("dbpath", "data/GeoLite2-City.mmdb")); | ||
// Read the <geomaxlite> block from the config | ||
auto& tag = ServerInstance->Config->ConfValue("geomaxlite"); | ||
dbpath = ServerInstance->Config->Paths.PrependConfig( | ||
tag->getString("dbpath", "data/GeoLite2-City.mmdb")); | ||
|
||
int status_open = MMDB_open(dbpath.c_str(), MMDB_MODE_MMAP, &mmdb); | ||
if (status_open != MMDB_SUCCESS) { | ||
std::string error_msg = "GeoLite2: Failed to open GeoLite2 database: " + std::string(MMDB_strerror(status_open)); | ||
if (status_open != MMDB_SUCCESS) | ||
{ | ||
// If the database can't be opened, throw a module exception | ||
std::string error_msg = "GeoMaxLite: Failed to open GeoLite2 database: " | ||
+ std::string(MMDB_strerror(status_open)); | ||
throw ModuleException(this, error_msg.c_str()); | ||
} | ||
} | ||
|
@@ -76,21 +81,26 @@ class ModuleWhoisGeoLite final : public Module, public Whois::EventListener | |
{ | ||
User* target = whois.GetTarget(); | ||
|
||
// Check if the target user has +y mode set | ||
if (!target->IsModeSet(geolite_mode)) | ||
// Only display city/country data if the target user has +y (geomaxlite) set | ||
if (!target->IsModeSet(geomaxlite_mode)) | ||
return; | ||
|
||
const std::string* info = country_item.Get(target); | ||
if (info && !info->empty()) { | ||
if (info && !info->empty()) | ||
{ | ||
whois.SendLine(RPL_WHOISSPECIAL, "is connecting from " + *info); | ||
} else { | ||
} | ||
else | ||
{ | ||
whois.SendLine(RPL_WHOISSPECIAL, "City: Unknown, Country: Unknown"); | ||
} | ||
} | ||
|
||
void OnChangeRemoteAddress(LocalUser* user) override | ||
{ | ||
if (!user->client_sa.is_ip()) { | ||
// If the address is not an IP, unset any stored geo info | ||
if (!user->client_sa.is_ip()) | ||
{ | ||
country_item.Unset(user); | ||
return; | ||
} | ||
|
@@ -99,32 +109,37 @@ class ModuleWhoisGeoLite final : public Module, public Whois::EventListener | |
const struct sockaddr* addr = &user->client_sa.sa; | ||
MMDB_lookup_result_s result = MMDB_lookup_sockaddr(&mmdb, addr, &gai_error); | ||
|
||
if (gai_error != 0 || !result.found_entry) { | ||
// Unset if we got a lookup error or didn't find anything | ||
if (gai_error != 0 || !result.found_entry) | ||
{ | ||
country_item.Unset(user); | ||
return; | ||
} | ||
|
||
// Fetch city and country strings (in English) | ||
MMDB_entry_data_s city_data = {}; | ||
MMDB_entry_data_s country_data = {}; | ||
|
||
int status_city = MMDB_get_value(&result.entry, &city_data, "city", "names", "en", nullptr); | ||
int status_country = MMDB_get_value(&result.entry, &country_data, "country", "names", "en", nullptr); | ||
|
||
std::string city = (status_city == MMDB_SUCCESS && city_data.has_data) ? std::string(city_data.utf8_string, city_data.data_size) : "Unknown"; | ||
std::string country = (status_country == MMDB_SUCCESS && country_data.has_data) ? std::string(country_data.utf8_string, country_data.data_size) : "Unknown"; | ||
std::string city = (status_city == MMDB_SUCCESS && city_data.has_data) | ||
? std::string(city_data.utf8_string, city_data.data_size) | ||
: "Unknown"; | ||
|
||
country_item.Set(user, "City: " + city + ", Country: " + country); | ||
} | ||
std::string country = (status_country == MMDB_SUCCESS && country_data.has_data) | ||
? std::string(country_data.utf8_string, country_data.data_size) | ||
: "Unknown"; | ||
|
||
void OnUserQuit(User* user, const std::string& message, const std::string& opermessage) override | ||
{ | ||
if (user->IsModeSet(geolite_mode)) | ||
country_item.Unset(user); | ||
// Store | ||
country_item.Set(user, "City: " + city + ", Country: " + country); | ||
} | ||
|
||
~ModuleWhoisGeoLite() override | ||
~ModuleGeoMaxLite() override | ||
{ | ||
// Close the MaxMind DB on unload | ||
MMDB_close(&mmdb); | ||
} | ||
}; | ||
|
||
MODULE_INIT(ModuleWhoisGeoLite) | ||
MODULE_INIT(ModuleGeoMaxLite) |