diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rwxr-xr-x | configure | 2 | ||||
-rw-r--r-- | docs/conf/helpop.conf.example | 2 | ||||
-rw-r--r-- | docs/conf/modules.conf.example | 66 | ||||
-rw-r--r-- | include/modules/geolocation.h | 80 | ||||
-rw-r--r-- | src/modules/extra/m_geo_maxmind.cpp | 202 | ||||
-rw-r--r-- | src/modules/extra/m_geoip.cpp | 220 | ||||
-rw-r--r-- | src/modules/m_geoban.cpp | 78 | ||||
-rw-r--r-- | src/modules/m_geoclass.cpp | 109 | ||||
-rwxr-xr-x | tools/travis-ci.sh | 4 |
10 files changed, 516 insertions, 249 deletions
diff --git a/.gitignore b/.gitignore index 7b23912d7..0bf719c7c 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,7 @@ /include/config.h -/src/modules/m_geoip.cpp +/src/modules/m_geo_maxmind.cpp /src/modules/m_ldap.cpp /src/modules/m_mysql.cpp /src/modules/m_pgsql.cpp @@ -326,7 +326,7 @@ if (prompt_bool $interactive, $question, 0) { # system './modulemanager', 'enable', '--auto'; my %modules = ( # Missing: m_ldap, m_regex_stdlib, m_ssl_mbedtls - 'm_geoip.cpp' => 'pkg-config --exists geoip', + 'm_geo_maxmind.cpp' => 'pkg-config --exists libmaxminddb', 'm_mysql.cpp' => 'mysql_config --version', 'm_pgsql.cpp' => 'pg_config --version', 'm_regex_pcre.cpp' => 'pcre-config --version', diff --git a/docs/conf/helpop.conf.example b/docs/conf/helpop.conf.example index 8c8ab12d9..059de40b7 100644 --- a/docs/conf/helpop.conf.example +++ b/docs/conf/helpop.conf.example @@ -1044,7 +1044,7 @@ Y Show connection classes O Show opertypes and the allowed user and channel modes it can set E Show socket engine events S Show currently held registered nicknames -G Show how many local users are connected from each country according to GeoIP +G Show how many local users are connected from each country Note that all /STATS use is broadcast to online IRC operators."> diff --git a/docs/conf/modules.conf.example b/docs/conf/modules.conf.example index e5b8af9a5..fe189e7c4 100644 --- a/docs/conf/modules.conf.example +++ b/docs/conf/modules.conf.example @@ -885,30 +885,48 @@ #<module name="gecosban"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# -# GeoIP module: Allows the server admin to match users by country code. -# This module is in extras. Re-run configure with: -# ./configure --enable-extras=m_geoip.cpp -# and run make install, then uncomment this module to enable it. -# This module requires GeoIP to be installed on your system, -# use your package manager to find the appropriate packages -# or check the InspIRCd wiki page for this module. -#<module name="geoip"> -# -# The actual allow/ban actions are done by connect classes, not by the -# GeoIP module. An example connect class to ban people from russia or -# turkey: -# -# <connect deny="*" geoip="TR,RU"> -# -# If enabled you can also ban people from channnels by country code -# using the G: extban (e.g. /MODE #channel +b G:US). -# <geoip extban="yes"> -# -# The country code must be in capitals and should be an ISO country -# code such as TR, GB, or US. Unknown IPs (localhost, LAN IPs, etc) -# will be assigned the country code "UNK". Since connect classes are -# matched from top down, your deny classes must be above your allow -# classes for them to match. +# Geolocation ban module: Adds support for extban 'G' which matches # +# against the ISO 3166-1 alpha-2 codes for the countries that users # +# are connecting from. Users connecting from unknown origins such as # +# internal networks can be matched against using the XX alpha-2 code. # +# A full list of ISO 3166-1 alpha-2 codes can be found at # +# https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 # +#<module name="geoban"> + +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# +# Geolocation connect class module: Adds support for limiting connect # +# classes to users from specific countries. With this module you can # +# specify a space-delimited list of two character the ISO 3166-1 # +# alpha-2 codes in the "country" field of a connect class. e.g. to # +# deny connections from users in Russia or Turkey: # +# # +# <connect deny="*" country="TR RU"> # +# # +# Users connecting from unknown origins such as internal networks can # +# be matched against using the XX alpha-2 code. A full list of ISO # +# 3166-1 alpha-2 codes can be found at # +# https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 # +#<module name="geoclass"> + +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# +# MaxMindDB geolocation module: Provides geolocation information for # +# other modules that need it using the libMaxMindDB library. # +# # +# This module is in extras. Re-run configure with: # +# ./configure --enable-extras=m_geo_maxmind.cpp +# and run make install, then uncomment this module to enable it. # +# # +# This module requires libMaxMindDB to be installed on your system. # +# Use your package manager to find the appropriate packages or check # +# the InspIRCd documentation page for this module. # +#<module name="geo_maxmind"> +# # +# If you use the geo_maxmind module you MUST provide a database file # +# to look up geolocation information in. You can either purchase this # +# from MaxMind at https://www.maxmind.com/en/geoip2-country-database # +# or use the free CC-BY-SA licensed GeoLite2 Country database which # +# can be downloaded at https://dev.maxmind.com/geoip/geoip2/geolite2/ # +#<maxmind file="GeoLite2-Country.mmdb"> #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-# # Globops module: Provides the /GLOBOPS command and snomask +g. diff --git a/include/modules/geolocation.h b/include/modules/geolocation.h new file mode 100644 index 000000000..911a9634d --- /dev/null +++ b/include/modules/geolocation.h @@ -0,0 +1,80 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2019 Peter Powell <petpow@saberuk.com> + * + * This file is part of InspIRCd. InspIRCd is free software: 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 <http://www.gnu.org/licenses/>. + */ + + +#pragma once + +namespace Geolocation +{ + class APIBase; + class API; + class Location; +} + +class Geolocation::APIBase : public DataProvider +{ + public: + APIBase(Module* parent) + : DataProvider(parent, "geolocationapi") + { + } + + /** Looks up the location of the specified user. + * @param user The user to look up the location of. + * @return Either an instance of the Location class or NULL if no location could be found. + */ + virtual Location* GetLocation(User* user) = 0; + + /** Looks up the location of the specified IP address. + * @param sa The IP address to look up the location of. + * @return Either an instance of the Location class or NULL if no location could be found. + */ + virtual Location* GetLocation(irc::sockets::sockaddrs& sa) = 0; +}; + +class Geolocation::API : public dynamic_reference<Geolocation::APIBase> +{ + public: + API(Module* parent) + : dynamic_reference<Geolocation::APIBase>(parent, "geolocationapi") + { + } +}; + +class Geolocation::Location : public usecountbase +{ +private: + /** The two character country code for this location. */ + std::string code; + + /** The country name for this location. */ + std::string name; + + public: + Location(const std::string& Code, const std::string& Name) + : code(Code) + , name(Name) + { + } + + /** Retrieves the two character country code for this location. */ + std::string GetCode() const { return code; } + + /** Retrieves the country name for this location. */ + std::string GetName() const { return name; } +}; diff --git a/src/modules/extra/m_geo_maxmind.cpp b/src/modules/extra/m_geo_maxmind.cpp new file mode 100644 index 000000000..b7639c287 --- /dev/null +++ b/src/modules/extra/m_geo_maxmind.cpp @@ -0,0 +1,202 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2019 Peter Powell <petpow@saberuk.com> + * + * This file is part of InspIRCd. InspIRCd is free software: 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 <http://www.gnu.org/licenses/>. + */ + +/// $CompilerFlags: find_compiler_flags("libmaxminddb") +/// $LinkerFlags: find_linker_flags("libmaxminddb") + +/// $PackageInfo: require_system("darwin") libmaxminddb pkg-config +/// $PackageInfo: require_system("debian" "9.0") libmaxminddb-dev pkg-config +/// $PackageInfo: require_system("ubuntu" "16.04") libmaxminddb-dev pkg-config + +#ifdef _WIN32 +# pragma comment(lib, "libmaxminddb.lib") +#endif + +#include <maxminddb.h> +#include "inspircd.h" +#include "modules/geolocation.h" + +class GeolocationExtItem : public LocalExtItem +{ + public: + GeolocationExtItem(Module* parent) + : LocalExtItem("geolocation", ExtensionItem::EXT_USER, parent) + { + } + + void free(Extensible* container, void* item) CXX11_OVERRIDE + { + Geolocation::Location* old = static_cast<Geolocation::Location*>(item); + if (old) + old->refcount_dec(); + } + + Geolocation::Location* get(const Extensible* item) const + { + return static_cast<Geolocation::Location*>(get_raw(item)); + } + + void set(Extensible* item, Geolocation::Location* value) + { + value->refcount_inc(); + free(item, set_raw(item, value)); + } + + void unset(Extensible* container) + { + free(container, unset_raw(container)); + } +}; + +typedef insp::flat_map<std::string, Geolocation::Location*> LocationMap; + +class GeolocationAPIImpl : public Geolocation::APIBase +{ + public: + GeolocationExtItem ext; + LocationMap locations; + MMDB_s mmdb; + + GeolocationAPIImpl(Module* parent) + : Geolocation::APIBase(parent) + , ext(parent) + { + } + + Geolocation::Location* GetLocation(User* user) CXX11_OVERRIDE + { + // If we have the location cached then use that instead. + Geolocation::Location* location = ext.get(user); + if (location) + return location; + + // Attempt to locate this user. + location = GetLocation(user->client_sa); + if (!location) + return NULL; + + // We found the user. Cache their location for future use. + ext.set(user, location); + return location; + } + + Geolocation::Location* GetLocation(irc::sockets::sockaddrs& sa) CXX11_OVERRIDE + { + // Attempt to look up the socket address. + int result; + MMDB_lookup_result_s lookup = MMDB_lookup_sockaddr(&mmdb, &sa.sa, &result); + if (result != MMDB_SUCCESS || !lookup.found_entry) + return NULL; + + // Attempt to retrieve the country code. + MMDB_entry_data_s country_code; + result = MMDB_get_value(&lookup.entry, &country_code, "country", "iso_code", NULL); + if (result != MMDB_SUCCESS || !country_code.has_data || country_code.type != MMDB_DATA_TYPE_UTF8_STRING || country_code.data_size != 2) + return NULL; + + // If the country has been seen before then use our cached Location object. + const std::string code(country_code.utf8_string, country_code.data_size); + LocationMap::iterator liter = locations.find(code); + if (liter != locations.end()) + return liter->second; + + // Attempt to retrieve the country name. + MMDB_entry_data_s country_name; + result = MMDB_get_value(&lookup.entry, &country_name, "country", "names", "en", NULL); + if (result != MMDB_SUCCESS || !country_name.has_data || country_name.type != MMDB_DATA_TYPE_UTF8_STRING) + return NULL; + + // Create a Location object and cache it. + const std::string cname(country_name.utf8_string, country_name.data_size); + Geolocation::Location* location = new Geolocation::Location(code, cname); + locations[code] = location; + return location; + } +}; + +class ModuleGeoMaxMind : public Module +{ + private: + GeolocationAPIImpl geoapi; + + public: + ModuleGeoMaxMind() + : geoapi(this) + { + memset(&geoapi.mmdb, 0, sizeof(geoapi.mmdb)); + } + + ~ModuleGeoMaxMind() + { + MMDB_close(&geoapi.mmdb); + } + + Version GetVersion() CXX11_OVERRIDE + { + return Version("Provides Geolocation lookups using the libMaxMindDB library", VF_VENDOR); + } + + void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE + { + ConfigTag* tag = ServerInstance->Config->ConfValue("maxmind"); + const std::string file = ServerInstance->Config->Paths.PrependConfig(tag->getString("file", "GeoLite2-Country.mmdb")); + + // Try to read the new database. + MMDB_s mmdb; + int result = MMDB_open(file.c_str(), MMDB_MODE_MMAP, &mmdb); + if (result != MMDB_SUCCESS) + throw ModuleException(InspIRCd::Format("Unable to load the MaxMind database (%s): %s", + file.c_str(), MMDB_strerror(result))); + + // Swap the new database with the old database. + std::swap(mmdb, geoapi.mmdb); + + // Free the old database. + MMDB_close(&mmdb); + } + + void OnGarbageCollect() CXX11_OVERRIDE + { + for (LocationMap::iterator iter = geoapi.locations.begin(); iter != geoapi.locations.end(); ) + { + Geolocation::Location* location = iter->second; + if (location->GetUseCount()) + { + ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Preserving geolocation data for %s (%s) with use count %u... ", + location->GetName().c_str(), location->GetCode().c_str(), location->GetUseCount()); + iter++; + } + else + { + ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Deleting unused geolocation data for %s (%s)", + location->GetName().c_str(), location->GetCode().c_str()); + delete location; + iter = geoapi.locations.erase(iter); + } + } + } + + void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE + { + // Unset the extension so that the location of this user is looked + // up again next time it is requested. + geoapi.ext.unset(user); + } +}; + +MODULE_INIT(ModuleGeoMaxMind) diff --git a/src/modules/extra/m_geoip.cpp b/src/modules/extra/m_geoip.cpp deleted file mode 100644 index e4299a1c2..000000000 --- a/src/modules/extra/m_geoip.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org> - * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc> - * - * This file is part of InspIRCd. InspIRCd is free software: 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 <http://www.gnu.org/licenses/>. - */ - -/// $CompilerFlags: find_compiler_flags("geoip" "") -/// $LinkerFlags: find_linker_flags("geoip" "-lGeoIP") - -/// $PackageInfo: require_system("centos" "7.0") GeoIP-devel pkgconfig -/// $PackageInfo: require_system("darwin") geoip pkg-config -/// $PackageInfo: require_system("debian") libgeoip-dev pkg-config -/// $PackageInfo: require_system("ubuntu") libgeoip-dev pkg-config - -#include "inspircd.h" -#include "xline.h" -#include "modules/stats.h" -#include "modules/whois.h" - -// Fix warnings about the use of commas at end of enumerator lists on C++03. -#if defined __clang__ -# pragma clang diagnostic ignored "-Wc++11-extensions" -#elif defined __GNUC__ -# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) -# pragma GCC diagnostic ignored "-Wpedantic" -# else -# pragma GCC diagnostic ignored "-pedantic" -# endif -#endif - -#include <GeoIP.h> - -#ifdef _WIN32 -# pragma comment(lib, "GeoIP.lib") -#endif - -enum -{ - // InspIRCd-specific. - RPL_WHOISCOUNTRY = 344 -}; - -class ModuleGeoIP : public Module, public Stats::EventListener, public Whois::EventListener -{ - StringExtItem ext; - bool extban; - GeoIP* ipv4db; - GeoIP* ipv6db; - - std::string* SetExt(User* user) - { - const char* code = NULL; - switch (user->client_sa.family()) - { - case AF_INET: - code = GeoIP_country_code_by_addr(ipv4db, user->GetIPString().c_str()); - break; - - case AF_INET6: - code = GeoIP_country_code_by_addr_v6(ipv6db, user->GetIPString().c_str()); - break; - } - - ext.set(user, code ? code : "UNK"); - return ext.get(user); - } - - public: - ModuleGeoIP() - : Stats::EventListener(this) - , Whois::EventListener(this) - , ext("geoip_cc", ExtensionItem::EXT_USER, this) - , extban(true) - , ipv4db(NULL) - , ipv6db(NULL) - { - } - - void init() CXX11_OVERRIDE - { - ipv4db = GeoIP_open_type(GEOIP_COUNTRY_EDITION, GEOIP_STANDARD); - if (!ipv4db) - throw ModuleException("Unable to load the IPv4 GeoIP database. Are you missing GeoIP.dat?"); - - ipv6db = GeoIP_open_type(GEOIP_COUNTRY_EDITION_V6, GEOIP_STANDARD); - if (!ipv6db) - throw ModuleException("Unable to load the IPv6 GeoIP database. Are you missing GeoIPv6.dat?"); - - const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers(); - for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i) - { - LocalUser* user = *i; - if ((user->registered == REG_ALL) && (!ext.get(user))) - { - SetExt(user); - } - } - } - - ~ModuleGeoIP() - { - if (ipv4db) - GeoIP_delete(ipv4db); - - if (ipv6db) - GeoIP_delete(ipv6db); - } - - void ReadConfig(ConfigStatus&) CXX11_OVERRIDE - { - ConfigTag* tag = ServerInstance->Config->ConfValue("geoip"); - extban = tag->getBool("extban"); - } - - Version GetVersion() CXX11_OVERRIDE - { - return Version("Provides a way to assign users to connect classes by country using GeoIP lookup", VF_OPTCOMMON|VF_VENDOR); - } - - void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE - { - if (extban) - tokens["EXTBAN"].push_back('G'); - } - - ModResult OnCheckBan(User* user, Channel*, const std::string& mask) CXX11_OVERRIDE - { - if (extban && (mask.length() > 2) && (mask[0] == 'G') && (mask[1] == ':')) - { - std::string* cc = ext.get(user); - if (!cc) - cc = SetExt(user); - - if (InspIRCd::Match(*cc, mask.substr(2))) - return MOD_RES_DENY; - } - return MOD_RES_PASSTHRU; - } - - ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE - { - std::string* cc = ext.get(user); - if (!cc) - cc = SetExt(user); - - std::string geoip = myclass->config->getString("geoip"); - if (geoip.empty()) - return MOD_RES_PASSTHRU; - irc::commasepstream list(geoip); - std::string country; - while (list.GetToken(country)) - if (country == *cc) - return MOD_RES_PASSTHRU; - return MOD_RES_DENY; - } - - void OnSetUserIP(LocalUser* user) CXX11_OVERRIDE - { - // If user has sent NICK/USER, re-set the ExtItem as this is likely CGI:IRC changing the IP - if (user->registered == REG_NICKUSER) - SetExt(user); - } - - void OnWhois(Whois::Context& whois) CXX11_OVERRIDE - { - // If the extban is disabled we don't expose users location. - if (!extban) - return; - - std::string* cc = ext.get(whois.GetTarget()); - if (!cc) - cc = SetExt(whois.GetTarget()); - - whois.SendLine(RPL_WHOISCOUNTRY, *cc, "is located in this country"); - } - - ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE - { - if (stats.GetSymbol() != 'G') - return MOD_RES_PASSTHRU; - - unsigned int unknown = 0; - std::map<std::string, unsigned int> results; - - const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers(); - for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i) - { - std::string* cc = ext.get(*i); - if (cc) - results[*cc]++; - else - unknown++; - } - - for (std::map<std::string, unsigned int>::const_iterator i = results.begin(); i != results.end(); ++i) - { - stats.AddRow(801, "GeoIPSTATS " + i->first + " " + ConvToStr(i->second)); - } - - if (unknown) - stats.AddRow(801, "GeoIPSTATS Unknown " + ConvToStr(unknown)); - - return MOD_RES_DENY; - } -}; - -MODULE_INIT(ModuleGeoIP) diff --git a/src/modules/m_geoban.cpp b/src/modules/m_geoban.cpp new file mode 100644 index 000000000..221d6f800 --- /dev/null +++ b/src/modules/m_geoban.cpp @@ -0,0 +1,78 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2019 Peter Powell <petpow@saberuk.com> + * + * This file is part of InspIRCd. InspIRCd is free software: 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 <http://www.gnu.org/licenses/>. + */ + + +#include "inspircd.h" +#include "modules/geolocation.h" +#include "modules/whois.h" + +enum +{ + // InspIRCd-specific. + RPL_WHOISCOUNTRY = 344 +}; + +class ModuleGeoBan + : public Module + , public Whois::EventListener +{ + private: + Geolocation::API geoapi; + + public: + ModuleGeoBan() + : Whois::EventListener(this) + , geoapi(this) + { + } + + Version GetVersion() CXX11_OVERRIDE + { + return Version("Provides a way to ban users by country", VF_OPTCOMMON|VF_VENDOR); + } + + void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE + { + tokens["EXTBAN"].push_back('G'); + } + + ModResult OnCheckBan(User* user, Channel*, const std::string& mask) CXX11_OVERRIDE + { + if ((mask.length() > 2) && (mask[0] == 'G') && (mask[1] == ':')) + { + Geolocation::Location* location = geoapi ? geoapi->GetLocation(user) : NULL; + const std::string code = location ? location->GetCode() : "XX"; + + // Does this user match against the ban? + if (InspIRCd::Match(code, mask.substr(2))) + return MOD_RES_DENY; + } + return MOD_RES_PASSTHRU; + } + + void OnWhois(Whois::Context& whois) CXX11_OVERRIDE + { + Geolocation::Location* location = geoapi ? geoapi->GetLocation(whois.GetTarget()) : NULL; + if (location) + whois.SendLine(RPL_WHOISCOUNTRY, location->GetCode(), "is connecting from " + location->GetName()); + else + whois.SendLine(RPL_WHOISCOUNTRY, "*", "is connecting from an unknown country"); + } +}; + +MODULE_INIT(ModuleGeoBan) diff --git a/src/modules/m_geoclass.cpp b/src/modules/m_geoclass.cpp new file mode 100644 index 000000000..6ec03c71f --- /dev/null +++ b/src/modules/m_geoclass.cpp @@ -0,0 +1,109 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2019 Peter Powell <petpow@saberuk.com> + * + * This file is part of InspIRCd. InspIRCd is free software: 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 <http://www.gnu.org/licenses/>. + */ + + +#include "inspircd.h" +#include "modules/geolocation.h" +#include "modules/stats.h" + +enum +{ + // InspIRCd-specific. + RPL_STATSCOUNTRY = 801 +}; + +class ModuleGeoClass + : public Module + , public Stats::EventListener +{ + private: + Geolocation::API geoapi; + + public: + ModuleGeoClass() + : Stats::EventListener(this) + , geoapi(this) + { + } + + Version GetVersion() CXX11_OVERRIDE + { + return Version("Provides a way to assign users to connect classes by country", VF_VENDOR); + } + + ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE + { + const std::string country = myclass->config->getString("country"); + if (country.empty()) + return MOD_RES_PASSTHRU; + + // If we can't find the location of this user then we can't assign + // them to a location-specific connect class. + Geolocation::Location* location = geoapi ? geoapi->GetLocation(user) : NULL; + const std::string code = location ? location->GetCode() : "XX"; + + irc::spacesepstream codes(country); + for (std::string token; codes.GetToken(token); ) + { + // If the user matches this country code then they can use this + // connect class. + if (stdalgo::string::equalsci(token, code)) + return MOD_RES_PASSTHRU; + } + + // A list of country codes were specified but the user didn't match + // any of them. + return MOD_RES_DENY; + } + + ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE + { + if (stats.GetSymbol() != 'G') + return MOD_RES_PASSTHRU; + + // Counter for the number of users in each country. + typedef std::map<Geolocation::Location*, size_t> CountryCounts; + CountryCounts counts; + + // Counter for the number of users in an unknown country. + size_t unknown = 0; + + const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers(); + for (UserManager::LocalList::const_iterator iter = list.begin(); iter != list.end(); ++iter) + { + Geolocation::Location* location = geoapi ? geoapi->GetLocation(*iter) : NULL; + if (location) + counts[location]++; + else + unknown++; + } + + for (CountryCounts::const_iterator iter = counts.begin(); iter != counts.end(); ++iter) + { + Geolocation::Location* location = iter->first; + stats.AddRow(RPL_STATSCOUNTRY, iter->second, location->GetCode(), location->GetName()); + } + + if (unknown) + stats.AddRow(RPL_STATSCOUNTRY, unknown, "*", "Unknown Country"); + + return MOD_RES_DENY; + } +}; + +MODULE_INIT(ModuleGeoClass) diff --git a/tools/travis-ci.sh b/tools/travis-ci.sh index bb32e19a1..8069717f0 100755 --- a/tools/travis-ci.sh +++ b/tools/travis-ci.sh @@ -3,10 +3,10 @@ set -ev if [ "$TRAVIS_OS_NAME" = "linux" ] then sudo apt-get update --assume-yes - sudo apt-get install --assume-yes libgeoip-dev libgnutls-dev libldap2-dev libmysqlclient-dev libpcre3-dev libpq-dev libsqlite3-dev libssl-dev libtre-dev + sudo apt-get install --assume-yes libgnutls-dev libldap2-dev libmaxminddb-dev libmysqlclient-dev libpcre3-dev libpq-dev libsqlite3-dev libssl-dev libtre-dev else >&2 echo "'$TRAVIS_OS_NAME' is an unknown Travis CI environment!" exit 1 fi -export TEST_BUILD_MODULES="m_geoip.cpp,m_ldap.cpp,m_mysql.cpp,m_pgsql.cpp,m_regex_pcre.cpp,m_regex_posix.cpp,m_regex_tre.cpp,m_sqlite3.cpp,m_ssl_gnutls.cpp,m_ssl_openssl.cpp" +export TEST_BUILD_MODULES="m_geo_maxmind.cpp,m_ldap.cpp,m_mysql.cpp,m_pgsql.cpp,m_regex_pcre.cpp,m_regex_posix.cpp,m_regex_tre.cpp,m_sqlite3.cpp,m_ssl_gnutls.cpp,m_ssl_openssl.cpp" ./tools/test-build $CXX |