1 # ho_hammer.pl
2 #
3 # $Id: ho_hammer.pl,v 1.5 2004/09/11 12:21:49 jvunder REL_0_3 $
4 #
5 # Part of the Hybrid Oper Script Collection.
6 #
7 # Looks for hammering clients and acts upon them.
8 #
9 # TODO: code HOSC::Kliner and use it.
10
11 use strict;
12 use vars qw($VERSION %IRSSI $SCRIPT_NAME);
13
14 use Irssi;
15 use Irssi::Irc;
16 use HOSC::again;
17 use HOSC::again 'HOSC::Base';
18 use HOSC::again 'HOSC::Tools';
19 use HOSC::again 'HOSC::Kliner';
20 import HOSC::Tools qw{is_server_notice};
21
22 # ---------------------------------------------------------------------
23
24 ($VERSION) = '$Revision: 1.5 $' =~ / (\d+\.\d+) /;
25 %IRSSI = (
26 authors => 'Garion',
27 contact => 'garion@efnet.nl',
28 name => 'ho_hammer',
29 description => 'Looks for hammering clients and acts upon them.',
30 license => 'Public Domain',
31 url => 'http://www.garion.org/irssi/hosc.php',
32 changed => '19 January 2003 13:07:07',
33 );
34 $SCRIPT_NAME = 'Hammer';
35
36 # Hashtable with connection times per host
37 # Key is the host
38 # Value is an array of connection times (unix timestamp)
39 my %conntimes;
40
41 # The last time the connection hash has been cleaned (unix timestamp)
42 my $conntimes_last_cleaned = 0;
43
44 my $kliner = HOSC::Kliner->new();
45
46 # ---------------------------------------------------------------------
47 # A Server Event has occurred. Check if it is a server NOTICE;
48 # if so, process it.
49
50 sub event_serverevent {
51 my ($server, $msg, $nick, $hostmask) = @_;
52
53 return unless is_server_notice(@_);
54
55 process_event($server, $msg);
56 }
57
58
59 # ---------------------------------------------------------------------
60 # This function takes a server notice and matches it with a few regular
61 # expressions to see if any special action needs to be taken.
62
63 sub process_event {
64 my ($server, $msg) = @_;
65
66 # HYBRID 7 - need a setting to determine which server type we have!
67 # Client connect: nick, user, host, ip, class, realname
68 if ($msg =~ /Client connecting: (.*) \((.*)@(.*)\) \[(.*)\] {(.*)} \[(.*)\]/) {
69 process_connect($server, $1, $2, $3, $4, $5, $6);
70 return;
71 }
72
73 # HYBRID 6
74 # Client connect: nick, user, host, ip, class, realname
75 if ($msg =~ /Client connecting: (.*) \((.*)@(.*)\) \[(.*)\] {(.*)}/) {
76 process_connect($server, $1, $2, $3, $4, $5, undef);
77 return;
78 }
79 }
80
81 # ---------------------------------------------------------------------
82 # This function processes a client connect.
83 # It shows warning in case of:
84 # - many connects in a short timespan from a single host
85
86 sub process_connect {
87 my ($server, $nick, $user, $host, $ip, $class, $realname) = @_;
88
89 return unless Irssi::settings_get_bool('ho_hammer_enable');
90
91 return if $ip eq '255.255.255.255' &&
92 Irssi::settings_get_bool('ho_hammer_ignore_spoofs');
93
94 my $tag = $server->{tag};
95
96 # Check whether the server notice is on one of the networks we are
97 # monitoring.
98 my $watch_this_network = 0;
99 foreach my $network (split /\s+/,
100 lc(Irssi::settings_get_str('ho_hammer_network_tags'))
101 ) {
102 if ($network eq lc($server->{tag})) {
103 $watch_this_network = 1;
104 last;
105 }
106 }
107 return unless $watch_this_network;
108
109 my $now = time();
110 push @{ $conntimes{$tag}->{$host} }, $now;
111
112 # Check whether this host has connected more than
113 # ho_hammer_warning_count times in the past
114 # ho_hammer_warning_time seconds.
115 if (@{ $conntimes{$tag}->{$host} } ==
116 Irssi::settings_get_int('ho_hammer_warning_count')
117 ) {
118 # Get the time of the first connect
119 my $firsttime = ${ $conntimes{$tag}->{$host} }[0];
120
121 # Get the time of the last connect
122 my $lasttime = ${ $conntimes{$tag}->{$host} }[@{ $conntimes{$tag}->{$host} } - 1];
123
124 my $timediff = $lasttime - $firsttime;
125
126 if ($timediff < Irssi::settings_get_int('ho_hammer_warning_time')) {
127 ho_print_warning("Hammer: " . @{ $conntimes{$tag}->{$host} } . "/".
128 "$timediff: $nick ($user\@$host).");
129 }
130 }
131
132 # Check whether this host has connected more than
133 # ho_hammer_violation_count times in the past
134 # ho_hammer_violation_time seconds.
135 if (@{ $conntimes{$tag}->{$host} } >=
136 Irssi::settings_get_int('ho_hammer_violation_count')) {
137 # Get the time of the first connect
138 my $firsttime = ${ $conntimes{$tag}->{$host} }[0];
139
140 # Get the time of the last connect
141 my $lasttime = ${ $conntimes{$tag}->{$host} }[@{ $conntimes{$tag}->{$host} } - 1];
142
143 my $timediff = $lasttime - $firsttime;
144
145 if ($timediff < Irssi::settings_get_int('ho_hammer_violation_time')) {
146 my $time = Irssi::settings_get_int('ho_hammer_kline_time');
147 my $reason = Irssi::settings_get_str('ho_hammer_kline_reason');
148
149 # If number of connections is equal to max number of connections
150 # allowed, kline user@host. If it is higher, that means the user
151 # has been k-lined once and has changed ident; therefore, kline
152 # *@host.
153 if (@{ $conntimes{$tag}->{$host} } ==
154 Irssi::settings_get_int('ho_hammer_violation_count')
155 ) {
156 $kliner->kline(
157 server => $server,
158 user => $user,
159 host => $host,
160 reason => $reason,
161 );
162 ho_print("K-lined $user\@$host for hammering.");
163 } else {
164 $kliner->kline(
165 server => $server,
166 user => '*',
167 host => $host,
168 reason => $reason,
169 );
170 ho_print("K-lined *\@$host for hammering.");
171 }
172 }
173 }
174
175 # Clean up the connection times hash to make sure it doesn't grow
176 # to infinity :)
177 # Do this every 60 seconds.
178 if ($now > $conntimes_last_cleaned + 60) {
179 $conntimes_last_cleaned = $now;
180 cleanup_conntimes_hash(300);
181 }
182 }
183
184
185
186 # ---------------------------------------------------------------------
187 # Cleans up the connection times hash.
188 # The only argument is the number of seconds to keep the hostnames for.
189 # This means that if the last connection from a hostname was longer ago
190 # than that number of seconds, the hostname is dropped from the hash.
191
192 sub cleanup_conntimes_hash {
193 my ($keeptime) = @_;
194 my $now = time();
195
196 # If the last time this host has connected is over $keeptime secs ago,
197 # delete it.
198 for my $tag (keys %conntimes) {
199 for my $host (keys %{ $conntimes{$tag} }) {
200 my $lasttime = ${ $conntimes{$tag}->{$host} }[@{ $conntimes{$tag}->{$host} } - 1];
201
202 # Discard this host if no connections have been made from it during
203 # the last $keeptime seconds.
204 if ($now > $lasttime + $keeptime) {
205 delete $conntimes{$tag}->{$host};
206 }
207 }
208 }
209 }
210
211 # ---------------------------------------------------------------------
212 # The /hammer command.
213
214 sub cmd_hammer {
215 my ($data, $server, $item) = @_;
216 if ($data =~ m/^[(help)]/i ) {
217 Irssi::command_runsub ('hammer', $data, $server, $item);
218 } else {
219 ho_print("Use /HAMMER HELP for help.")
220 }
221 }
222
223 # ---------------------------------------------------------------------
224 # The /hammer help command.
225
226 sub cmd_hammer_help {
227 print_help();
228 }
229
230 # ---------------------------------------------------------------------
231
232 ho_print_init_begin();
233
234 Irssi::signal_add_first('server event', 'event_serverevent');
235
236 Irssi::command_bind('hammer', 'cmd_hammer');
237 Irssi::command_bind('hammer help', 'cmd_hammer_help');
238
239 Irssi::settings_add_bool('ho', 'ho_hammer_enable', 0);
240 Irssi::settings_add_bool('ho', 'ho_hammer_ignore_spoofs', 1);
241 Irssi::settings_add_int('ho', 'ho_hammer_warning_count', 8);
242 Irssi::settings_add_int('ho', 'ho_hammer_warning_time', 100);
243 Irssi::settings_add_int('ho', 'ho_hammer_violation_count', 10);
244 Irssi::settings_add_int('ho', 'ho_hammer_violation_time', 120);
245 Irssi::settings_add_int('ho', 'ho_hammer_kline_time', 1440);
246 Irssi::settings_add_str('ho', 'ho_hammer_network_tags', '');
247 Irssi::settings_add_str('ho', 'ho_hammer_kline_reason',
248 '[Automated K-line] Reconnecting too fast. Please try again later.');
249
250 if (length Irssi::settings_get_str('ho_hammer_network_tags') > 0) {
251 if (Irssi::settings_get_bool('ho_hammer_enable')) {
252 ho_print("Script enabled for the following tags: " .
253 Irssi::settings_get_str('ho_hammer_network_tags'));
254 } else {
255 ho_print("Script disabled. The following tags have been set: " .
256 Irssi::settings_get_str('ho_hammer_network_tags') .
257 ". Use /SET ho_hammer_enable ON to enable the script.");
258 }
259 } else {
260 ho_print_warning("No network tags set. Please use ".
261 "/SET ho_hammer_network_tags tag1 tag2 tag3 .. ".
262 "to choose the tags the script will work on.");
263 }
264
265 ho_print_init_end();
266 ho_print("Use /HAMMER HELP for help.");
267
268 # ---------------------------------------------------------------------
269
270 sub print_help {
271 ho_print_help('head', $SCRIPT_NAME);
272
273 ho_print_help('section', 'Description');
274 ho_print_help("This script tracks reconnecting clients and can take action on ".
275 "them, being either printing a warning or banning them from the server ".
276 "automatically. Clients that reconnect rapidly are called 'hammering ".
277 "clients', which explains the name of this script.\n");
278
279 ho_print_help('section', 'Settings');
280 ho_print_help('setting', 'ho_hammer_enable',
281 'Master setting to enable/disable this script.');
282 ho_print_help('setting', 'ho_hammer_network_tags',
283 'Tags of the networks hammering clients must be tracked.');
284 ho_print_help('setting', 'ho_hammer_ignore_spoofs',
285 'Whether spoofs should be ignored.');
286 ho_print_help('setting', 'ho_hammer_kline_reason',
287 'The reason of the ban placed on the hammering client.');
288
289 ho_print_help('setting', 'ho_hammer_warning_count', 'and');
290 ho_print_help('setting', 'ho_hammer_warning_time',
291 'If clients from a host connect more than ho_hammer_warning_count '.
292 'times in ho_hammer_warning_time seconds, a warning is printed.');
293
294 ho_print_help('setting', 'ho_hammer_violation_count', 'and');
295 ho_print_help('setting', 'ho_hammer_violation_time',
296 'If clients from a host connect more than ho_hammer_violation_count '.
297 'times in ho_violation_warning_time seconds, the host is banned.');
298 }
syntax highlighted by Code2HTML, v. 0.9.1