summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rwxr-xr-xconfigure2
-rw-r--r--docs/conf/helpop.conf.example2
-rw-r--r--docs/conf/modules.conf.example66
-rw-r--r--include/modules/geolocation.h80
-rw-r--r--src/modules/extra/m_geo_maxmind.cpp202
-rw-r--r--src/modules/extra/m_geoip.cpp220
-rw-r--r--src/modules/m_geoban.cpp78
-rw-r--r--src/modules/m_geoclass.cpp109
-rwxr-xr-xtools/travis-ci.sh4
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
diff --git a/configure b/configure
index 048975f41..2d91101b5 100755
--- a/configure
+++ b/configure
@@ -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