Validate IP addresses before passing to the firewall module.
[people/stevee/guardian.git] / modules / Events.pm
CommitLineData
df6ec59f
SS
1package Guardian::Events;
2use strict;
3use warnings;
4
5use Exporter qw(import);
6
7our @EXPORT_OK = qw(Init CheckAction Update);
8
9# Hash which stores all supported commands from the queue.
10my %commands = (
11 'count' => \&Counter,
12 'block' => \&CallBlock,
13 'unblock' => \&CallUnblock,
14 'flush' => \&CallFlush,
15);
16
17# Hash to store addresses and their current count.
18my %counthash = ();
19
20# Hash to store all currentyl blocked addresses and a timestamp
21# when the block for this address can be released.
22my %blockhash = ();
23
24# This object will contain the reference to the logger object after calling Init.
25my $logger;
26
27#
28## The "Init" (Block) function.
29#
30## This function is responsible to initialize Block as a class based object.
31## It has to be called once before any blocking event can be processed.
32#
33## The following arguments must be passed, during initialization:
34## "BlockCount" and "BlockTime" which both have to be natural numbers.
35#
36sub Init (%) {
37 my ( $class, %args ) = @_;
38 my $self = \%args;
39
40 # Fail, if some critical arguments are missing.
41 unless ((exists($self->{BlockCount})) && (exists($self->{BlockTime}))) {
42 die "Could not initialize Block: Too less arguments are given.\n";
43 }
44
45 # Use bless to make "$self" to an object of class "$class".
46 bless($self, $class);
47
48 # Assign logger object.
49 $logger = $self->{Logger};
50
51 # Log used firewall engine.
52 $logger->Log("debug", "Using firewall engine: $self->{FirewallEngine}");
53
54 # Try to load the specified firewall engine or die.
55 my $module_name = "Guardian::" . $self->{FirewallEngine};
56 eval "use $module_name; 1" or die "Could not load a module for firewall engine: $self->{FirewallEngine}!";
57
58 # Return the class object.
59 return $self;
60}
61
62#
63## The main "CheckAction" function.
64#
65## This function is used to handle each recived event from the main event queue of guardian.
66#
67## It will check if the given command is valid and will pass it to the responsible function.
68#
69sub CheckAction ($$) {
70 my $self = shift;
71 my @event = split(/ /, $_[0], 4);
72 my ($command, $address, $module, $message) = @event;
73
74 # Check if we got an invalid command.
75 unless(exists($commands{$command})) {
76 $logger->Log("err", "The CheckAction function has been called with an unsupported command ($command)!");
77 return;
78 }
79
df6ec59f 80 # Check if the given address is valid.
bc97b7c0
SS
81 unless(&Guardian::Base::IsValidAddressOrNetwork($address)) {
82 # Log error message.
83 $logger->Log("err", "Invalid IP address: $address");
84 return;
85 }
df6ec59f
SS
86
87 # Call required handler.
88 my $error = $commands{$command}->($self, $address, $module, $message);
89
90 # If we got any return code, something went wrong and should be logged.
91 if ($error) {
92 $logger->Log("err", "Got error: $error");
93 return;
94 }
95}
96
97#
98## The main "Counter" function.
99#
100## This function is used to handle each count message + address, which has been sent by the main event
101## loop of guardian.
102#
103## It stores the address and the current count into the counthash and increase the count each time when
104## the same address should be counted again. When the current count reaches the configured BlockCount,
105## the responsible function will be called to block the address.
106#
107sub Counter ($@) {
108 my $self = shift;
109 my ($address, $module, $message) = @_;
110
111 # Log event.
112 $logger->Log("debug", "$module reported $message for address: $address");
113
114 # Increase existing count or start counting for new source addresses.
115 if (exists($counthash{$address})) {
116 # Skip already blocked addresses.
117 if (exists($blockhash{$address})) {
118 return undef;
119 }
120
121 # Increase count of the existing entry.
122 $counthash{$address} = $counthash{$address} + 1;
123
124 # Log altered count of the address.
125 $logger->Log("debug", "Source $address now has count $counthash{$address}/$self->{BlockCount}...");
126 } else {
127 # Log that counting for the address has been started.
128 $logger->Log("debug", "Start counting for $address...");
129
130 # Set count to "1".
131 $counthash{$address} = 1;
132 }
133
134 # Check if the address has reached the configured count limit.
135 if ($counthash{$address} >= $self->{BlockCount}) {
136 # Write out log message.
137 $logger->Log("info", "Blocking $address for $self->{BlockTime} seconds...");
138
139 # Call block subroutine to block the address.
140 my $error = &CallBlock($self, $address, $module, $message);
141
142 # Return the message if an error occurs.
143 return $error;
144 }
145
146 # Everything worked well, return nothing.
147 return undef;
148}
149
150#
151## The RemoveBlocks function.
152#
153## This function periodly will be called and is responsible for releasing the block if the Blocktime
154## on an address has expired.
155#
156## To do this, the code will loop through all entries of the blockhash and check
157## if the estimiated BlockTime of each address has reached and so the block can be released.
158#
159sub RemoveBlocks () {
160 my $self = shift;
161
162 # Get the current time.
163 my $time = time();
164
165 # Loop through the blockhash.
166 foreach my $address (keys %blockhash) {
167 # Check if the blocktime for the address has expired.
168 if ($blockhash{$address} <= $time) {
169 # Call unblock subroutine.
170 my $error = &CallUnblock($self, $address, "BlockTime", "has expired for $address");
171
172 # Log error messages if returned.
173 if ($error) {
174 $logger->Log("err", "$error");
175 }
176 }
177 }
178
179 # Everything okay, return nothing.
180 return undef;
181}
182
183#
184## The CallBlock function.
185#
186## This function is called, if the BlockCount for an address is reached or a direct
187## request for blocking an address has been recieved.
188#
189sub CallBlock ($@) {
190 my $self = shift;
191 my ($address, $module, $message) = @_;
192
193 # Log the call for blocking an address.
194 $logger->Log("info", "$module - $message");
195
196 # Check if an entry for this address exists
197 # in the blockhash. If not, the address has
198 # not been blocked yet, call the responisible
199 # function to do this now.
200 unless (exists($blockhash{$address})) {
e9c558fe
SS
201 # Obtain the configured FirewallAction.
202 my $action = $self->{FirewallAction};
df6ec59f
SS
203
204 # Block the given address.
205 my $error = &DoBlock($address, $action);
206
207 # If we got back an error message something went wrong.
208 if ($error) {
f842727a
SS
209 # Exit function and return the used FirewallEngine and the error message.
210 return "$self->{FirewallEngine} - $error";
df6ec59f
SS
211 } else {
212 # Address has been successfully blocked, print a log message.
213 $logger->Log("debug", "Address $address successfully has been blocked...");
214 }
215 }
216
217 # Generate time when the block will expire.
218 my $expire = time() + $self->{BlockTime};
219
220 # Store the blocked address and the expire time
221 # in the blockhash.
222 $blockhash{$address} = $expire;
223
224 # Return nothing "undef" if everything is okay.
225 return undef;
226}
227
228#
229## CallUnblock function.
230#
231## This function is responsible for unblocking and deleting a given
232## address from the blockhash.
233#
234sub CallUnblock ($) {
235 my $self = shift;
236 my ($address, $module, $message) = @_;
237
238 # Log the call for unblocking an address.
239 $logger->Log("info", "$module - $message");
240
241 # Return an error if no entry for the given address
242 # is present in the blockhash.
243 unless (exists($blockhash{$address})) {
244 return "Address $address was not blocked!";
245 }
246
247 # Unblock the address.
248 my $error = &DoUnblock($address);
249
250 # If an error message is returned, something went wrong.
251 if ($error) {
252 # Exit function and return the error message.
253 return $error;
254 } else {
255 # Address successfully has been unblocked.
256 $logger->Log("debug", "Address $address successfully has been unblocked...");
257 }
258
259 # Drop address from blockhash.
260 delete ($blockhash{$address});
261
262 # Everything worked well, return nothing.
263 return undef;
264}
265
2661;