Introduce SortAddressHash function.
[people/stevee/guardian.git] / modules / Base.pm
1 package Guardian::Base;
2 use strict;
3 use warnings;
4
5 use Exporter qw(import);
6
7 our @EXPORT_OK = qw(GenerateMonitoredFiles GetFileposition DetectIPProtocolVersion FilePositions);
8
9 use Net::IP;
10
11 #
12 ## Function to generate a hash of monitored files and their file positions.
13 #
14 ## This function is responsible for creating the hash of which files should be
15 ## monitored by guardian. In order to do this, all options from the given hash of
16 ## main settings will be parsed and all files to monitor and their configured parsers
17 ## get extracted, validated and stored into a temporary hash.
18 #
19 ## Next step will be to cleanup files which have been monitored in the past but have been
20 ## requested for beeing unmonitored for now. To do this, a check if the the file name is
21 ## part of the existing hash of monitored files and if true to transfer the data into the
22 ## new temporary hash which get returned by the function.
23 #
24 sub GenerateMonitoredFiles (\%\%) {
25         # Dereference the given hash-refs and store
26         # them into a new temporary hashes.
27         my %mainsettings = %{ $_[0] };
28         my %current_monitored_files = %{ $_[1] };
29
30         # Private hash for storing the new monitored files.
31         my %new_monitored_files = ();
32
33         # Loop through the temporary hash which contains the main settings.
34         # Search for files which should be monitored and extract the requested
35         # parser. Compare if the file already was a part of the hash which contains
36         # the monitored files and add them to the private new hash of monitored
37         # files which will be returned.
38         foreach my $config_option (keys %mainsettings) {
39                 # Skip option if it does not look like "Monitor_XYZ".
40                 next unless($config_option =~ m/^Monitor_/);
41
42                 # Splitt monitor instruction into 2 parts, to grab the
43                 # requested parser module.
44                 my ($start, $parser) = split (/_/, $config_option);
45
46                 # Convert parser name into lower case format.
47                 # Internally the parser module name is completely handled
48                 # in this way. This also prevents from any problems related
49                 # how the parser name has been spelled in the config file.
50                 $parser = lc($parser);
51
52                 # Check if the configured parser is available and valid.
53                 next unless(&Guardian::Parser::IsSupportedParser($parser));
54
55                 # Get the configured file for this option.
56                 my $file = $mainsettings{$config_option};
57
58                 # Skip the file, if it does not exist or is not read-able.
59                 next unless(-r "$file");
60
61                 # Check if the file not yet has been added to the hash
62                 # of monitored files.
63                 unless(exists($current_monitored_files{$file})) {
64                         # Add the file, init and store the fileposition.
65                         $new_monitored_files{$file} = $parser;
66                 } else {
67                         # Copy file and parser information to the new hash.
68                         $new_monitored_files{$file} = $current_monitored_files{$file};
69                 }
70         }
71
72         # Return the new_monitored_files hash.
73         return %new_monitored_files;
74 }
75
76 #
77 ## The FilePositions function.
78 #
79 ## This function is responsible for creating and/or updating the hash which
80 ## stores the current cursor position of the end of file (EOF) of all
81 ## monitored files.
82 #
83 ## The function requires the hash of currently monitored files and the old hash
84 ## of the current file positions in order to work properly.
85 #
86 sub FilePositions (\%\%) {
87         # Dereference the given hash-refs and store
88         # them into a new temporary hashes.
89         my %monitored_files = %{ $_[0] };
90         my %current_file_positions = %{ $_[1] };
91
92         # Private hash for storing the new monitored files.
93         my %new_file_positions = ();
94
95         # Loop through the hash of monitored files.
96         # Compare if the file allready has been a part of the hash
97         # which contains the file positions and transfer the stored
98         # cursor position into the temporary hash which will be returned.
99         #
100         # Otherwise, call the responsible function to obtain the current
101         # end of file (EOF) and store it.
102         foreach my $file (keys %monitored_files) {
103                 # Check if the filename is allready part of the hash
104                 # of file positions.
105                 if (exists($current_file_positions{$file})) {
106                         # Copy file position into temporary hash.
107                         $new_file_positions{$file} = $current_file_positions{$file};
108                 } else {
109                         # Call function to obtain the file position.
110                         my $position = &GetFileposition($file);
111
112                         # Add filename and position to the temporary hash.
113                         $new_file_positions{$file} = $position;
114                 }
115         }
116
117         # Return the new_file_positions hash.
118         return %new_file_positions;
119 }
120
121 #
122 ## Address/Network to binary format caluculator function.
123 #
124 ## This function is used to convert a given single IP address
125 ## or network into a binary format.
126 #
127 ## The used Net::IP module is not able to directly detect
128 ## single addresses or network ranges. Only an element which may be
129 ## a single address or a whole network can be assigned, for which a
130 ## lot of different values can be calculated. In case the input has
131 ## been a single address, the module will calculate the same binary
132 ## address (intip) and last address for the network range (last_int)
133 ## because internally it uses a /32 bit prefix for IPv4 and a /128 prefix
134 ## on IPv6 addresses.
135 #
136 ## So a single address can be detected by just comparing both calculated
137 ## addresses if they are equal.
138 #
139 sub IPOrNet2Int($) {
140         my $address = shift;
141
142         # Assign and validate the given address, or directly return
143         # nothing (False) and exit the function.
144         my $ip = new Net::IP ($address) || return;
145
146         # Convert the given address into integer format.
147         my $first .= $ip->intip();
148
149         # Calculate last address for the given network.
150         my $last .= $ip->last_int();
151
152         # Check whether the first address equals the last address.
153         # If this is true, a single IP address has been passed.
154         if ($first eq $last) {
155                 # Return the binary converted single address.
156                 return $first;
157         }
158
159         # If both addresses are not equal a network has been passed.
160         #
161         # Check if the converted first address is less than the calculated last
162         # address of the network.
163         elsif ($first < $last) {
164                 # Return the binary converted first and last address of
165                 # the given network.
166                 return $first, $last;
167         }
168
169         # If we got here, something strange happend, return nothing (False).
170         else {
171                 return;
172         }
173 }
174
175 #
176 ## DetectIPProtocolVersion function.
177 #
178 ## Wrapper function for determining the used protocol version (4/6)
179 ## for a given IP address.
180 #
181 sub DetectIPProtocolVersion ($) {
182         my $address = shift;
183
184         # Call external perl module to detect the used IP protocol version.
185         my $version = &Net::IP::ip_get_version($address);
186
187         # Return the detected version.
188         return $version;
189 }
190
191 #
192 ## Function to get the current (EOF) cursor postion.
193 #
194 ## This function is used to get the cursor position of the end of file (EOF) of
195 ## a specified file.
196 #
197 ## In order to prevent from permanently read and keep files opened, or dealing
198 ## with huge logfiles, at initialization time of the worker processes, the file will
199 ## be opened once and the cursor position of the end of file (EOF) get stored.
200 #
201 sub GetFileposition ($) {
202         my $file = $_[0];
203
204         # Open the file.
205         open(FILE, $file) or die "Could not open $file. $!";
206
207         # Just seek to the end of the file (EOF).
208         seek(FILE, 0, 2);
209
210         # Get and store the position.
211         my $position = tell(FILE),
212
213         # Close the file again.
214         close(FILE);
215
216         # Return the position.
217         return $position;
218 }
219
220 #
221 ## The SortAddressHash function.
222 #
223 # This function requires a reference to an hash containing
224 # IP-addresses, will sort them into a nice looking order
225 # and return the soreted result as an array.
226 #
227 sub SortAddressHash (\%) {
228         # Dereference the given hash reference and store it
229         # in a new temporary hash.
230         my %addresshash = %{ $_[0] };
231
232         # Loop through the entire hash keys.
233         foreach my $address (keys(%addresshash)) {
234                 # Convert the address or subnet into binary format.
235                 my @bin_address = &IPOrNet2Int($address);
236
237                 # Only store the first result if there are multiple
238                 # one in case of a given subnet.
239                 $addresshash{$address} = $bin_address[0];
240         }
241
242         # Sort the addresshash by the binary addresses
243         # of the stored addresses and save them is an array.
244         my @sorted_addresses = sort { $addresshash{$a} <=> $addresshash{$b} } keys %addresshash;
245
246         # Return the sorted address array.
247         return @sorted_addresses;
248 }
249
250 1;