diff --git a/4/m_geomaxlite.cpp b/4/m_geomaxlite.cpp new file mode 100644 index 00000000..5ca13483 --- /dev/null +++ b/4/m_geomaxlite.cpp @@ -0,0 +1,146 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2024 reverse + * + * This file contains a third-party module for InspIRCd. You can + * redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/// $ModAuthor: reverse +/// $ModDesc: Adds city and country information to WHOIS using a local MaxMind GeoLite2 database with user mode +y. +/// $ModConfig: +/// $ModDepends: core 4 + +/// $CompilerFlags: find_compiler_flags("libmaxminddb") +/// $LinkerFlags: find_linker_flags("libmaxminddb") + +/// $PackageInfo: require_system("alpine") libmaxminddb-dev pkgconf +/// $PackageInfo: require_system("arch") pkgconf libmaxminddb +/// $PackageInfo: require_system("darwin") libmaxminddb pkg-config +/// $PackageInfo: require_system("debian~") libmaxminddb-dev pkg-config +/// $PackageInfo: require_system("rhel~") pkg-config libmaxminddb-devel + +#include "inspircd.h" +#include "modules/whois.h" +#include "extension.h" +#include + +class ModuleGeoMaxLite final + : public Module + , public Whois::EventListener +{ +private: + // 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; + + // +y user mode for enabling GeoMaxLite lookups + SimpleUserMode geomaxlite_mode; + +public: + ModuleGeoMaxLite() + : Module(VF_OPTCOMMON, "Adds city and country information to WHOIS using a local MaxMind GeoLite2 database.") + , Whois::EventListener(this) + , country_item(this, "geomaxlite-country", ExtensionType::USER, true) + , geomaxlite_mode(this, "geomaxlite", 'y', false) + { + } + + void ReadConfig(ConfigStatus& status) override + { + // Read the 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) + { + // 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()); + } + } + + void OnWhois(Whois::Context& whois) override + { + User* target = whois.GetTarget(); + + // 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()) + { + whois.SendLine(RPL_WHOISSPECIAL, "is connecting from " + *info); + } + else + { + whois.SendLine(RPL_WHOISSPECIAL, "City: Unknown, Country: Unknown"); + } + } + + void OnChangeRemoteAddress(LocalUser* user) override + { + // If the address is not an IP, unset any stored geo info + if (!user->client_sa.is_ip()) + { + country_item.Unset(user); + return; + } + + int gai_error = 0; + const struct sockaddr* addr = &user->client_sa.sa; + MMDB_lookup_result_s result = MMDB_lookup_sockaddr(&mmdb, addr, &gai_error); + + // 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"; + + // Store + country_item.Set(user, "City: " + city + ", Country: " + country); + } + + ~ModuleGeoMaxLite() override + { + // Close the MaxMind DB on unload + MMDB_close(&mmdb); + } +}; + +MODULE_INIT(ModuleGeoMaxLite) diff --git a/4/m_whoisgeolite.cpp b/4/m_whoisgeolite.cpp deleted file mode 100644 index 789bb61a..00000000 --- a/4/m_whoisgeolite.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2024 reverse - * - * This file contains a third-party module for InspIRCd. You can - * redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, version 2. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/// $ModAuthor: reverse -/// $ModDesc: Adds city and country information to WHOIS using the MaxMind database. -/// $ModConfig: -/// $ModDepends: core 4 - - -/// $CompilerFlags: find_compiler_flags("libmaxminddb") -/// $LinkerFlags: find_linker_flags("libmaxminddb") - -/// $PackageInfo: require_system("alpine") libmaxminddb-dev pkgconf -/// $PackageInfo: require_system("arch") pkgconf libmaxminddb -/// $PackageInfo: require_system("darwin") libmaxminddb pkg-config -/// $PackageInfo: require_system("debian~") libmaxminddb-dev pkg-config -/// $PackageInfo: require_system("rhel~") pkg-config libmaxminddb-devel - -#include "inspircd.h" -#include "modules/whois.h" -#include "extension.h" -#include - -class ModuleWhoisGeoLite final : public Module, public Whois::EventListener -{ -private: - MMDB_s mmdb; // MaxMind database object - std::string dbpath; // Path to the GeoLite2 database - StringExtItem country_item; // Extension item for storing city and country info - -public: - ModuleWhoisGeoLite() - : Module(VF_OPTCOMMON, "Adds city and country information to WHOIS using the MaxMind database.") - , Whois::EventListener(this) - , country_item(this, "geo-lite-country", ExtensionType::USER, true) // Sync Servers - { - } - - void ReadConfig(ConfigStatus& status) override - { - auto& tag = ServerInstance->Config->ConfValue("geolite"); - 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)); - throw ModuleException(this, error_msg.c_str()); - } - } - - void OnWhois(Whois::Context& whois) override - { - User* source = whois.GetSource(); - User* target = whois.GetTarget(); - - if (!source->IsOper()) - return; - - const std::string* info = country_item.Get(target); - if (info && !info->empty()) { - whois.SendLine(RPL_WHOISSPECIAL, "is connecting from " + *info); - } else { - whois.SendLine(RPL_WHOISSPECIAL, "City: Unknown, Country: Unknown"); - } - } - - void OnChangeRemoteAddress(LocalUser* user) override - { - if (!user->client_sa.is_ip()) { - country_item.Unset(user); - return; - } - - int gai_error = 0; - 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) { - country_item.Unset(user); - return; - } - - 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"; - - country_item.Set(user, "City: " + city + ", Country: " + country); - } - - ~ModuleWhoisGeoLite() override - { - MMDB_close(&mmdb); - } -}; - -MODULE_INIT(ModuleWhoisGeoLite) -