+package Guardian::Events;
+use strict;
+use warnings;
+
+use Exporter qw(import);
+
+our @EXPORT_OK = qw(Init CheckAction Update);
+
+# Hash which stores all supported commands from the queue.
+my %commands = (
+ 'count' => \&Counter,
+ 'block' => \&CallBlock,
+ 'unblock' => \&CallUnblock,
+ 'flush' => \&CallFlush,
+);
+
+# Hash to store addresses and their current count.
+my %counthash = ();
+
+# Hash to store all currentyl blocked addresses and a timestamp
+# when the block for this address can be released.
+my %blockhash = ();
+
+# This object will contain the reference to the logger object after calling Init.
+my $logger;
+
+#
+## The "Init" (Block) function.
+#
+## This function is responsible to initialize Block as a class based object.
+## It has to be called once before any blocking event can be processed.
+#
+## The following arguments must be passed, during initialization:
+## "BlockCount" and "BlockTime" which both have to be natural numbers.
+#
+sub Init (%) {
+ my ( $class, %args ) = @_;
+ my $self = \%args;
+
+ # Fail, if some critical arguments are missing.
+ unless ((exists($self->{BlockCount})) && (exists($self->{BlockTime}))) {
+ die "Could not initialize Block: Too less arguments are given.\n";
+ }
+
+ # Use bless to make "$self" to an object of class "$class".
+ bless($self, $class);
+
+ # Assign logger object.
+ $logger = $self->{Logger};
+
+ # Log used firewall engine.
+ $logger->Log("debug", "Using firewall engine: $self->{FirewallEngine}");
+
+ # Try to load the specified firewall engine or die.
+ my $module_name = "Guardian::" . $self->{FirewallEngine};
+ eval "use $module_name; 1" or die "Could not load a module for firewall engine: $self->{FirewallEngine}!";
+
+ # Return the class object.
+ return $self;
+}
+
+#
+## The main "CheckAction" function.
+#
+## This function is used to handle each recived event from the main event queue of guardian.
+#
+## It will check if the given command is valid and will pass it to the responsible function.
+#
+sub CheckAction ($$) {
+ my $self = shift;
+ my @event = split(/ /, $_[0], 4);
+ my ($command, $address, $module, $message) = @event;
+
+ # Check if we got an invalid command.
+ unless(exists($commands{$command})) {
+ $logger->Log("err", "The CheckAction function has been called with an unsupported command ($command)!");
+ return;
+ }
+
+ # XXX
+ # Check if the given address is valid.
+
+ # Call required handler.
+ my $error = $commands{$command}->($self, $address, $module, $message);
+
+ # If we got any return code, something went wrong and should be logged.
+ if ($error) {
+ $logger->Log("err", "Got error: $error");
+ return;
+ }
+}
+
+#
+## The main "Counter" function.
+#
+## This function is used to handle each count message + address, which has been sent by the main event
+## loop of guardian.
+#
+## It stores the address and the current count into the counthash and increase the count each time when
+## the same address should be counted again. When the current count reaches the configured BlockCount,
+## the responsible function will be called to block the address.
+#
+sub Counter ($@) {
+ my $self = shift;
+ my ($address, $module, $message) = @_;
+
+ # Log event.
+ $logger->Log("debug", "$module reported $message for address: $address");
+
+ # Increase existing count or start counting for new source addresses.
+ if (exists($counthash{$address})) {
+ # Skip already blocked addresses.
+ if (exists($blockhash{$address})) {
+ return undef;
+ }
+
+ # Increase count of the existing entry.
+ $counthash{$address} = $counthash{$address} + 1;
+
+ # Log altered count of the address.
+ $logger->Log("debug", "Source $address now has count $counthash{$address}/$self->{BlockCount}...");
+ } else {
+ # Log that counting for the address has been started.
+ $logger->Log("debug", "Start counting for $address...");
+
+ # Set count to "1".
+ $counthash{$address} = 1;
+ }
+
+ # Check if the address has reached the configured count limit.
+ if ($counthash{$address} >= $self->{BlockCount}) {
+ # Write out log message.
+ $logger->Log("info", "Blocking $address for $self->{BlockTime} seconds...");
+
+ # Call block subroutine to block the address.
+ my $error = &CallBlock($self, $address, $module, $message);
+
+ # Return the message if an error occurs.
+ return $error;
+ }
+
+ # Everything worked well, return nothing.
+ return undef;
+}
+
+#
+## The RemoveBlocks function.
+#
+## This function periodly will be called and is responsible for releasing the block if the Blocktime
+## on an address has expired.
+#
+## To do this, the code will loop through all entries of the blockhash and check
+## if the estimiated BlockTime of each address has reached and so the block can be released.
+#
+sub RemoveBlocks () {
+ my $self = shift;
+
+ # Get the current time.
+ my $time = time();
+
+ # Loop through the blockhash.
+ foreach my $address (keys %blockhash) {
+ # Check if the blocktime for the address has expired.
+ if ($blockhash{$address} <= $time) {
+ # Call unblock subroutine.
+ my $error = &CallUnblock($self, $address, "BlockTime", "has expired for $address");
+
+ # Log error messages if returned.
+ if ($error) {
+ $logger->Log("err", "$error");
+ }
+ }
+ }
+
+ # Everything okay, return nothing.
+ return undef;
+}
+
+#
+## The CallBlock function.
+#
+## This function is called, if the BlockCount for an address is reached or a direct
+## request for blocking an address has been recieved.
+#
+sub CallBlock ($@) {
+ my $self = shift;
+ my ($address, $module, $message) = @_;
+
+ # Log the call for blocking an address.
+ $logger->Log("info", "$module - $message");
+
+ # Check if an entry for this address exists
+ # in the blockhash. If not, the address has
+ # not been blocked yet, call the responisible
+ # function to do this now.
+ unless (exists($blockhash{$address})) {
+ # XXX
+ # Add posibility to use a configure-able action.
+ my $action;
+
+ # Block the given address.
+ my $error = &DoBlock($address, $action);
+
+ # If we got back an error message something went wrong.
+ if ($error) {
+ # Exit function and return the error message.
+ return $error;
+ } else {
+ # Address has been successfully blocked, print a log message.
+ $logger->Log("debug", "Address $address successfully has been blocked...");
+ }
+ }
+
+ # Generate time when the block will expire.
+ my $expire = time() + $self->{BlockTime};
+
+ # Store the blocked address and the expire time
+ # in the blockhash.
+ $blockhash{$address} = $expire;
+
+ # Return nothing "undef" if everything is okay.
+ return undef;
+}
+
+#
+## CallUnblock function.
+#
+## This function is responsible for unblocking and deleting a given
+## address from the blockhash.
+#
+sub CallUnblock ($) {
+ my $self = shift;
+ my ($address, $module, $message) = @_;
+
+ # Log the call for unblocking an address.
+ $logger->Log("info", "$module - $message");
+
+ # Return an error if no entry for the given address
+ # is present in the blockhash.
+ unless (exists($blockhash{$address})) {
+ return "Address $address was not blocked!";
+ }
+
+ # Unblock the address.
+ my $error = &DoUnblock($address);
+
+ # If an error message is returned, something went wrong.
+ if ($error) {
+ # Exit function and return the error message.
+ return $error;
+ } else {
+ # Address successfully has been unblocked.
+ $logger->Log("debug", "Address $address successfully has been unblocked...");
+ }
+
+ # Drop address from blockhash.
+ delete ($blockhash{$address});
+
+ # Everything worked well, return nothing.
+ return undef;
+}
+
+1;