]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_geoip.cpp
35efc8d70a4187ad5c80537587aeca7fbe7bd9af
[user/henk/code/inspircd.git] / src / modules / extra / m_geoip.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
6  *
7  * This file is part of InspIRCd.  InspIRCd is free software: you can
8  * redistribute it and/or modify it under the terms of the GNU General Public
9  * License as published by the Free Software Foundation, version 2.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
14  * details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /// $CompilerFlags: find_compiler_flags("geoip" "")
21 /// $LinkerFlags: find_linker_flags("geoip" "-lGeoIP")
22
23 /// $PackageInfo: require_system("centos" "7.0") GeoIP-devel pkgconfig
24 /// $PackageInfo: require_system("darwin") geoip pkg-config
25 /// $PackageInfo: require_system("debian") libgeoip-dev pkg-config
26 /// $PackageInfo: require_system("ubuntu") libgeoip-dev pkg-config
27
28 #include "inspircd.h"
29 #include "xline.h"
30
31 // Fix warnings about the use of commas at end of enumerator lists on C++03.
32 #if defined __clang__
33 # pragma clang diagnostic ignored "-Wc++11-extensions"
34 #elif defined __GNUC__
35 # pragma GCC diagnostic ignored "-pedantic"
36 #endif
37
38 #include <GeoIP.h>
39
40 #ifdef _WIN32
41 # pragma comment(lib, "GeoIP.lib")
42 #endif
43
44 enum
45 {
46         // InspIRCd-specific.
47         RPL_WHOISCOUNTRY = 344
48 };
49
50 class ModuleGeoIP : public Module, public Whois::EventListener
51 {
52         StringExtItem ext;
53         bool extban;
54         GeoIP* gi;
55
56         std::string* SetExt(User* user)
57         {
58                 const char* c = GeoIP_country_code_by_addr(gi, user->GetIPString().c_str());
59                 if (!c)
60                         c = "UNK";
61
62                 ext.set(user, c);
63                 return ext.get(user);
64         }
65
66  public:
67         ModuleGeoIP()
68                 : Whois::EventListener(this)
69                 , ext("geoip_cc", ExtensionItem::EXT_USER, this)
70                 , extban(true)
71                 , gi(NULL)
72         {
73         }
74
75         void init() CXX11_OVERRIDE
76         {
77                 gi = GeoIP_new(GEOIP_STANDARD);
78                 if (gi == NULL)
79                                 throw ModuleException("Unable to initialize geoip, are you missing GeoIP.dat?");
80
81                 const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
82                 for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
83                 {
84                         LocalUser* user = *i;
85                         if ((user->registered == REG_ALL) && (!ext.get(user)))
86                         {
87                                 SetExt(user);
88                         }
89                 }
90         }
91
92         ~ModuleGeoIP()
93         {
94                 if (gi)
95                         GeoIP_delete(gi);
96         }
97
98         void ReadConfig(ConfigStatus&) CXX11_OVERRIDE
99         {
100                 ConfigTag* tag = ServerInstance->Config->ConfValue("geoip");
101                 extban = tag->getBool("extban");
102         }
103
104         Version GetVersion() CXX11_OVERRIDE
105         {
106                 return Version("Provides a way to assign users to connect classes by country using GeoIP lookup", VF_OPTCOMMON|VF_VENDOR);
107         }
108
109         void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
110         {
111                 if (extban)
112                         tokens["EXTBAN"].push_back('G');
113         }
114
115         ModResult OnCheckBan(User* user, Channel*, const std::string& mask) CXX11_OVERRIDE
116         {
117                 if (extban && (mask.length() > 2) && (mask[0] == 'G') && (mask[1] == ':'))
118                 {
119                         std::string* cc = ext.get(user);
120                         if (!cc)
121                                 cc = SetExt(user);
122
123                         if (InspIRCd::Match(*cc, mask.substr(2)))
124                                 return MOD_RES_DENY;
125                 }
126                 return MOD_RES_PASSTHRU;
127         }
128
129         ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
130         {
131                 std::string* cc = ext.get(user);
132                 if (!cc)
133                         cc = SetExt(user);
134
135                 std::string geoip = myclass->config->getString("geoip");
136                 if (geoip.empty())
137                         return MOD_RES_PASSTHRU;
138                 irc::commasepstream list(geoip);
139                 std::string country;
140                 while (list.GetToken(country))
141                         if (country == *cc)
142                                 return MOD_RES_PASSTHRU;
143                 return MOD_RES_DENY;
144         }
145
146         void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
147         {
148                 // If the extban is disabled we don't expose users location.
149                 if (!extban)
150                         return;
151
152                 std::string* cc = ext.get(whois.GetTarget());
153                 if (!cc)
154                         cc = SetExt(whois.GetTarget());
155
156                 whois.SendLine(RPL_WHOISCOUNTRY, *cc, "is located in this country");
157         }
158
159         ModResult OnStats(Stats::Context& stats) CXX11_OVERRIDE
160         {
161                 if (stats.GetSymbol() != 'G')
162                         return MOD_RES_PASSTHRU;
163
164                 unsigned int unknown = 0;
165                 std::map<std::string, unsigned int> results;
166
167                 const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
168                 for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
169                 {
170                         std::string* cc = ext.get(*i);
171                         if (cc)
172                                 results[*cc]++;
173                         else
174                                 unknown++;
175                 }
176
177                 for (std::map<std::string, unsigned int>::const_iterator i = results.begin(); i != results.end(); ++i)
178                 {
179                         stats.AddRow(801, "GeoIPSTATS " + i->first + " " + ConvToStr(i->second));
180                 }
181
182                 if (unknown)
183                         stats.AddRow(801, "GeoIPSTATS Unknown " + ConvToStr(unknown));
184
185                 return MOD_RES_DENY;
186         }
187 };
188
189 MODULE_INIT(ModuleGeoIP)