#!/usr/bin/perl -w
###
# Project: pflogstats
# Module: pflogstats-extensions-addressmapping.pm
# Description: Address mapping module
# Copyright: Dr. Peter Bieringer <pbieringer at aerasec dot de>
# AERAsec GmbH <http://www.aerasec.de/>
# License: GNU GPL v2
# CVS: $Id: pflogstats-extensions-addressmapping.pm,v 1.13 2005/04/26 15:58:14 peter Exp $
###
## ChangeLog
# 0.01:
# - initial
# 0.02:
# - add support of regexp
# 0.03:
# - don't modify special bounce addresses
# 0.04
# - make Perl 5.0 compatible
# 0.05
# - display table contents only in verbose mode
# 0.06
# - add caching and statistics
##
use strict;
## Local constants
my $module_type = "extensions";
my $module_name = $module_type . "-addressmapping";
my $module_version = "0.06";
## Export module info
$main::moduleinfo{$module_name}->{'version'} = $module_version;
$main::moduleinfo{$module_name}->{'type'} = $module_type;
$main::moduleinfo{$module_name}->{'name'} = $module_name;
## Global prototyping
sub beforemainloopstarts();
sub modifyaddress($);
sub help();
## Local prototyping
sub read_address_mapping($);
## Register options
$main::options{'addressmapping_file=s'} = \$main::opts{'addressmapping_file'};
## Register calling hooks
$main::hooks{'beforemainloopstarts'}->{$module_name} = \&beforemainloopstarts;
$main::hooks{'modifyaddress'}->{$module_name} = \&modifyaddress;
$main::hooks{'help'}->{$module_name} = \&help;
$main::hooks{'print_additional_statistics'}->{$module_name} = \&printstatistics;
## Local variables
my %address_mapping;
my %address_mapping_evalpattern;
my %cache;
my %cache_statistics;
$cache_statistics{'new'} = 0;
$cache_statistics{'hit'} = 0;
## Global callable functions
# Help
sub help() {
my $helpstring = "
Extension: addressmapping
[--addressmapping_file <file>] maps addresses into another addresses
format: 2 colums
regular expressions are also supported
";
return $helpstring;
};
# Initialization
sub beforemainloopstarts() {
# print "Called: " . $module_name . "/" . "beforemainloopstarts" . "\n";
# Read file contents
if (defined $main::opts{'addressmapping_file'} ) {
&read_address_mapping($main::opts{'addressmapping_file'});
};
};
# Modify an address depeding on mapping
sub modifyaddress($) {
my $address_new;
my $address = shift || die "ERROR: arg1 (address) missing";
if (defined $cache{$address}) {
$cache_statistics{'hit'}++;
return $cache{$address};
} else {
$cache_statistics{'new'}++;
};
# Do not modify special bounce addresses
if ($address eq "from=<>" || $address eq "from=<#@[]>") {
$cache{$address} = $address;
return $address;
};
# Simple replacement
if (defined $address_mapping{$address} ) {
# Match, so replace
$address_new = $address_mapping{$address};
$cache{$address} = $address_new;
return $address_new;
};
# regex support
print STDERR "Check against pattern: " . $address . "\n" if ($main::opts{'debug'} & 0x1000);
for my $pattern ( keys %address_mapping ) {
print STDERR "Pattern: " . $pattern . "\n" if ($main::opts{'debug'} & 0x1000);
if ( $address =~ /^$pattern$/ ) {
#print STDERR "Patternmatch: " . $address . "\n" if ($main::opts{'debug'} & 0x1000);
$address_new = eval $address_mapping_evalpattern{$pattern};
if (! defined $address_new) { $address_new = "EMPTY_ADDRESS"; };
print STDERR "Replaced: " . $address_new . "\n" if ($main::opts{'debug'} & 0x1000);
$cache{$address} = $address_new;
return $address_new;
};
};
# Nothing to do
$cache{$address} = $address;
return $address;
};
## Local functions
# Read address mapping file
sub read_address_mapping($) {
my $filename = shift || die "ERROR: arg1 (filename) missing";
my ($address_found, $address_replacement);
# open file
open(ADDRESSMAPPING,"<" . $filename) || die "ERROR: cannot open: " . $filename;
# read file contents
while(<ADDRESSMAPPING>) {
#printf STDERR $_ if ($opts{'debug'} & 0x0040);
$_ =~ s/([^#]*)#.*/$1/; # Remove comments
$_ =~ s/[[:space:][:cntrl:]]+$//; # Remove trailing spaces and control chars
$_ =~ s/^[[:space:][:cntrl:]]+//; # Remove leading spaces and control chars
if ( length($_) == 0 ) { next; }; # Skip empty lines
# Split
($address_found, $address_replacement) = split " ", $_, 2;
if ( (defined $address_found) && (defined $address_replacement) ) {
if ( (length($address_found) > 0) && (length($address_replacement) > 0) ) {
if (! defined $address_mapping{$address_found} ) {
$address_mapping{$address_found} = $address_replacement;
# Generate eval pattern
$address_replacement =~ s/@/\\@/;
$address_replacement = "return \"" . $address_replacement . "\"";
$address_mapping_evalpattern{$address_found} = $address_replacement;
} else {
warn "WARNING: 1st value already found and defined, therefore skipped in " . $filename;
};
} else {
warn "WARNING: line isn't valid and therefore skipped in " . $filename;
};
} else {
warn "WARNING: line isn't valid and therefore skipped in " . $filename;
};
};
close(ADDRESSMAPPING);
# Print contents (debug)
if ( scalar (keys %address_mapping) > 0 ) {
if (defined $main::opts{'verbose'}) {
print '='x79 . "\n";
print "Address mapping table\n";
print '-'x79 . "\n";
for $address_found (keys %address_mapping) {
print $address_found . " -> " . $address_mapping{$address_found} . "\n";
};
print '='x79 . "\n";
};
};
};
# Print some statistics
sub printstatistics() {
# About cache usage
my $requests = $cache_statistics{'new'} + $cache_statistics{'hit'};
if ($requests == 0) { return; };
my $ratehit_percent = ($cache_statistics{'hit'} / $requests) * 100;
print "# module '" . $module_name . "' internal cache usage:\n";
print "# requests=" . $requests . " hits=" . $cache_statistics{'hit'};
printf " (%2.1f%%)\n", $ratehit_percent;
};
## End of module
return 1;
syntax highlighted by Code2HTML, v. 0.9.1