b60a572b6a0431868d5ef3ff0676a05eab804427
[people/stevee/guardian.git] / modules / Events.pm
1 package Guardian::Events;
2 use strict;
3 use warnings;
4
5 use Exporter qw(import);
6
7 our @EXPORT_OK = qw(Init CheckAction Update);
8
9 # Hash which stores all supported commands from the queue.
10 my %commands = (
11         'count' => \&Counter,
12         'block' => \&CallBlock,
13         'unblock' => \&CallUnblock,
14         'flush' => \&CallFlush,
15 );
16
17 # Hash to store addresses and their current count.
18 my %counthash = ();
19
20 # Hash to store all currentyl blocked addresses and a timestamp
21 # when the block for this address can be released.
22 my %blockhash = ();
23
24 # This object will contain the reference to the logger object after calling Init.
25 my $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 #
36 sub 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 #
69 sub 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
80         # Check if the given address is valid.
81         unless(&Guardian::Base::IsValidAddressOrNetwork($address)) {
82                 # Log error message.
83                 $logger->Log("err", "Invalid IP address: $address");
84                 return;
85         }
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 #
107 sub 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 #
159 sub 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 #
189 sub 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})) {
201                 # Obtain the configured FirewallAction.
202                 my $action = $self->{FirewallAction};
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) {
209                         # Exit function and return the used FirewallEngine and the error message.
210                         return "$self->{FirewallEngine} - $error";
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 #
234 sub 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
266 1;