]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_nickflood.cpp
m_spanningtree Move all server-to-server command handlers into handler classes
[user/henk/code/inspircd.git] / src / modules / m_nickflood.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2007, 2009 Robin Burchell <robin+git@viroteck.net>
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
21 #include "inspircd.h"
22
23 /** Holds settings and state associated with channel mode +F
24  */
25 class nickfloodsettings
26 {
27  public:
28         unsigned int secs;
29         unsigned int nicks;
30         time_t reset;
31         time_t unlocktime;
32         unsigned int counter;
33
34         nickfloodsettings(unsigned int b, unsigned int c)
35                 : secs(b), nicks(c), unlocktime(0), counter(0)
36         {
37                 reset = ServerInstance->Time() + secs;
38         }
39
40         void addnick()
41         {
42                 if (ServerInstance->Time() > reset)
43                 {
44                         counter = 1;
45                         reset = ServerInstance->Time() + secs;
46                 }
47                 else
48                         counter++;
49         }
50
51         bool shouldlock()
52         {
53                 /* XXX HACK: using counter + 1 here now to allow the counter to only be incremented
54                  * on successful nick changes; this will be checked before the counter is
55                  * incremented.
56                  */
57                 return ((ServerInstance->Time() <= reset) && (counter == this->nicks));
58         }
59
60         void clear()
61         {
62                 counter = 0;
63         }
64
65         bool islocked()
66         {
67                 if (ServerInstance->Time() > unlocktime)
68                         unlocktime = 0;
69
70                 return (unlocktime != 0);
71         }
72
73         void lock()
74         {
75                 unlocktime = ServerInstance->Time() + 60;
76         }
77 };
78
79 /** Handles channel mode +F
80  */
81 class NickFlood : public ModeHandler
82 {
83  public:
84         SimpleExtItem<nickfloodsettings> ext;
85         NickFlood(Module* Creator) : ModeHandler(Creator, "nickflood", 'F', PARAM_SETONLY, MODETYPE_CHANNEL),
86                 ext("nickflood", Creator) { }
87
88         ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
89         {
90                 if (adding)
91                 {
92                         std::string::size_type colon = parameter.find(':');
93                         if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos))
94                         {
95                                 source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
96                                 return MODEACTION_DENY;
97                         }
98
99                         /* Set up the flood parameters for this channel */
100                         unsigned int nnicks = ConvToInt(parameter.substr(0, colon));
101                         unsigned int nsecs = ConvToInt(parameter.substr(colon+1));
102
103                         if ((nnicks<1) || (nsecs<1))
104                         {
105                                 source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
106                                 return MODEACTION_DENY;
107                         }
108
109                         nickfloodsettings* f = ext.get(channel);
110                         if ((f) && (nnicks == f->nicks) && (nsecs == f->secs))
111                                 // mode params match
112                                 return MODEACTION_DENY;
113
114                         ext.set(channel, new nickfloodsettings(nsecs, nnicks));
115                         parameter = ConvToStr(nnicks) + ":" + ConvToStr(nsecs);
116                         return MODEACTION_ALLOW;
117                 }
118                 else
119                 {
120                         if (!channel->IsModeSet(this))
121                                 return MODEACTION_DENY;
122
123                         ext.unset(channel);
124                         return MODEACTION_ALLOW;
125                 }
126         }
127 };
128
129 class ModuleNickFlood : public Module
130 {
131         NickFlood nf;
132
133  public:
134         ModuleNickFlood()
135                 : nf(this)
136         {
137         }
138
139         void init() CXX11_OVERRIDE
140         {
141                 ServerInstance->Modules->AddService(nf);
142                 ServerInstance->Modules->AddService(nf.ext);
143         }
144
145         ModResult OnUserPreNick(User* user, const std::string &newnick) CXX11_OVERRIDE
146         {
147                 for (UCListIter i = user->chans.begin(); i != user->chans.end(); i++)
148                 {
149                         Channel *channel = *i;
150                         ModResult res;
151
152                         nickfloodsettings *f = nf.ext.get(channel);
153                         if (f)
154                         {
155                                 res = ServerInstance->OnCheckExemption(user,channel,"nickflood");
156                                 if (res == MOD_RES_ALLOW)
157                                         continue;
158
159                                 if (f->islocked())
160                                 {
161                                         user->WriteNumeric(447, "%s :%s has been locked for nickchanges for 60 seconds because there have been more than %u nick changes in %u seconds", user->nick.c_str(), channel->name.c_str(), f->nicks, f->secs);
162                                         return MOD_RES_DENY;
163                                 }
164
165                                 if (f->shouldlock())
166                                 {
167                                         f->clear();
168                                         f->lock();
169                                         channel->WriteChannelWithServ((char*)ServerInstance->Config->ServerName.c_str(), "NOTICE %s :No nick changes are allowed for 60 seconds because there have been more than %u nick changes in %u seconds.", channel->name.c_str(), f->nicks, f->secs);
170                                         return MOD_RES_DENY;
171                                 }
172                         }
173                 }
174
175                 return MOD_RES_PASSTHRU;
176         }
177
178         /*
179          * XXX: HACK: We do the increment on the *POST* event here (instead of all together) because we have no way of knowing whether other modules would block a nickchange.
180          */
181         void OnUserPostNick(User* user, const std::string &oldnick) CXX11_OVERRIDE
182         {
183                 if (isdigit(user->nick[0])) /* allow switches to UID */
184                         return;
185
186                 for (UCListIter i = user->chans.begin(); i != user->chans.end(); ++i)
187                 {
188                         Channel *channel = *i;
189                         ModResult res;
190
191                         nickfloodsettings *f = nf.ext.get(channel);
192                         if (f)
193                         {
194                                 res = ServerInstance->OnCheckExemption(user,channel,"nickflood");
195                                 if (res == MOD_RES_ALLOW)
196                                         return;
197
198                                 /* moved this here to avoid incrementing the counter for nick
199                                  * changes that are denied for some other reason (bans, +N, etc.)
200                                  * per bug #874.
201                                  */
202                                 f->addnick();
203                         }
204                 }
205         }
206
207         Version GetVersion() CXX11_OVERRIDE
208         {
209                 return Version("Channel mode F - nick flood protection", VF_VENDOR);
210         }
211 };
212
213 MODULE_INIT(ModuleNickFlood)