mirror of
https://github.com/zeek/zeek.git
synced 2025-10-03 15:18:20 +00:00
Initial import of svn+ssh:://svn.icir.org/bro/trunk/bro as of r7088
This commit is contained in:
commit
61757ac78b
1383 changed files with 380824 additions and 0 deletions
120
scripts/perl/lib/Bro/Config.pm
Normal file
120
scripts/perl/lib/Bro/Config.pm
Normal file
|
@ -0,0 +1,120 @@
|
|||
package Bro::Config;
|
||||
|
||||
use strict;
|
||||
use Config::General;
|
||||
require Exporter;
|
||||
|
||||
use vars qw( $VERSION
|
||||
$DEBUG
|
||||
@ISA
|
||||
@EXPORT_OK
|
||||
%DEFAULTS
|
||||
$DEFAULT_CONFIG_FILE
|
||||
$BRO_CONFIG );
|
||||
|
||||
# $Id: Config.pm 987 2005-01-08 01:04:43Z rwinslow $
|
||||
$VERSION = 1.20;
|
||||
$DEBUG = 0;
|
||||
|
||||
@ISA = ( 'Exporter' );
|
||||
@EXPORT_OK = qw( $BRO_CONFIG );
|
||||
%DEFAULTS = ( BROHOME => '/usr/local/bro',
|
||||
BRO_POLICY_SUFFIX => '.bro',
|
||||
BRO_SIG_SUFFIX => '.sig',
|
||||
META_DATA_PREFIX => '.',
|
||||
);
|
||||
|
||||
$DEFAULTS{CONFIG_FILE} = $DEFAULTS{BROHOME} . '/etc/bro.cfg';
|
||||
|
||||
sub parse
|
||||
{
|
||||
my $sub_name = 'parse';
|
||||
|
||||
my %args = @_;
|
||||
my $config_file;
|
||||
my $brohome;
|
||||
my $conf;
|
||||
my $ret_hash;
|
||||
|
||||
# Check for a config-path that may override the default
|
||||
if( exists( $args{'File'} ) )
|
||||
{
|
||||
$config_file = $args{'File'};
|
||||
}
|
||||
else
|
||||
{
|
||||
$config_file = $DEFAULT_CONFIG_FILE;
|
||||
}
|
||||
|
||||
# Check for the existance and readability of the config file
|
||||
if( !( -f $config_file and -r $config_file ) )
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, The Bro config file at $config_file is not readable\n" );
|
||||
return( undef );
|
||||
}
|
||||
|
||||
$conf = Config::General->new( -ConfigFile => $config_file,
|
||||
-MergeDuplicateOptions => 1,
|
||||
-AutoTrue => 1,
|
||||
);
|
||||
%{$ret_hash} = $conf->getall;
|
||||
|
||||
return( $ret_hash );
|
||||
}
|
||||
|
||||
sub Configure
|
||||
{
|
||||
my $sub_name = 'Configure';
|
||||
|
||||
my %args = @_;
|
||||
|
||||
if( exists( $args{File} ) )
|
||||
{
|
||||
if( $args{File} !~ m/[\;\|\?\*\&\{\}]/ and $args{File} =~ m/^([[:print:]]+)$/ )
|
||||
{
|
||||
my $clean_name = $1;
|
||||
if( -f $clean_name and -r $clean_name )
|
||||
{
|
||||
$DEFAULT_CONFIG_FILE = $clean_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Unable to read config file at $clean_name\n" );
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Filename contains invalid characters\n" );
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
$BRO_CONFIG = parse();
|
||||
|
||||
# Set other defaults that have been omitted or don't exist in the config file
|
||||
setdefaults();
|
||||
|
||||
return( 1 );
|
||||
}
|
||||
|
||||
sub setdefaults
|
||||
{
|
||||
my $sub_name = 'setdefaults';
|
||||
|
||||
my $override = $_[0] || 0;
|
||||
my @variables_changed;
|
||||
|
||||
foreach my $key( keys( %DEFAULTS ) )
|
||||
{
|
||||
if( $override or !( exists( $BRO_CONFIG->{$key} ) ) )
|
||||
{
|
||||
$BRO_CONFIG->{$key} = $DEFAULTS{$key};
|
||||
push( @variables_changed, $key )
|
||||
}
|
||||
}
|
||||
|
||||
return( @variables_changed );
|
||||
}
|
||||
|
||||
1;
|
295
scripts/perl/lib/Bro/Log.pm
Normal file
295
scripts/perl/lib/Bro/Log.pm
Normal file
|
@ -0,0 +1,295 @@
|
|||
package Bro::Log;
|
||||
|
||||
require 5.006_001;
|
||||
use strict;
|
||||
use Bro::Config( '$BRO_CONFIG' );
|
||||
use Time::Local;
|
||||
|
||||
use vars qw( $VERSION
|
||||
$BROLOGS );
|
||||
|
||||
# $Id: Log.pm 2865 2006-04-27 19:09:18Z tierney $
|
||||
$VERSION = 1.20;
|
||||
|
||||
|
||||
|
||||
# This is the bare minimum format in which the filename must conform
|
||||
my $FILENAME_REGEX = qr/^[[:alnum:]]\.(?:log|[[:print:]]\.[[:print:]])/;
|
||||
|
||||
# filename produced by Bro running from a trace file
|
||||
my $name_trace = qr/^([[:alnum:]]+)\.log$/;
|
||||
|
||||
# filename produced from a Bro running on live traffic and currently open
|
||||
# or logs that are not rotated or post processed
|
||||
my $name_running = qr/^([[:alnum:]]+) # log name
|
||||
\. # seperator
|
||||
([^-][[:alnum:]-]*(?:\.[^-][[:alnum:]-])*) # hostname
|
||||
\. # seperator
|
||||
([[:digit:]]{2}-[[:digit:]]{2}-[[:digit:]]{2} # date
|
||||
_ # time seperator
|
||||
[[:digit:]]{2}\.[[:digit:]]{2}\.[[:digit:]]{2}) # time
|
||||
$/x;
|
||||
|
||||
# filename produced after post processing for things like the GUI. The
|
||||
# filename contains the log name, hostname, begin epoch time, and end
|
||||
# epoch time.
|
||||
my $name_epoch_range = qr/^([[:alnum:]]+) # log name
|
||||
\. # seperator
|
||||
([^-][[:alnum:]-]*(?:\.[^-][[:alnum:]-])*) # hostname
|
||||
\. # seperator
|
||||
([[:digit:]]{10}) # beginning epoch time
|
||||
- # seperator
|
||||
([[:digit:]]{10}) # ending epoch time
|
||||
$/x;
|
||||
|
||||
my $name_rotate_log = qr/^([[:alnum:]]+) # log name
|
||||
\. # seperator
|
||||
([^-][[:alnum:]-]*(?:\.[^-][[:alnum:]-])*) # hostname
|
||||
\. # seperator
|
||||
([[:digit:]]{2}-[[:digit:]]{2}-[[:digit:]]{2} # date
|
||||
_ # time seperator
|
||||
[[:digit:]]{2}\.[[:digit:]]{2}\.[[:digit:]]{2}) # time
|
||||
- # second time seperator
|
||||
([[:digit:]]{2}-[[:digit:]]{2}-[[:digit:]]{2} # date
|
||||
_ # time seperator
|
||||
[[:digit:]]{2}\.[[:digit:]]{2}\.[[:digit:]]{2}) # time
|
||||
(\.log)?$/x;
|
||||
|
||||
sub activelog
|
||||
{
|
||||
my $sub_name = 'activelog';
|
||||
|
||||
my $log_dir = $BRO_CONFIG->{BROLOGS};
|
||||
my $ret_str;
|
||||
|
||||
if( !( defined( $log_dir ) ) )
|
||||
{
|
||||
warn( "no log directory defined\n" );
|
||||
return( undef );
|
||||
}
|
||||
|
||||
if( -f "$log_dir/active_log" )
|
||||
{
|
||||
if( open( I_FILE, "$log_dir/active_log" ) )
|
||||
{
|
||||
if( defined( $ret_str = <I_FILE> ) )
|
||||
{
|
||||
# remove any trailing newlines
|
||||
if( $ret_str !~ m/[[:space]]+$/ )
|
||||
{
|
||||
chomp( $ret_str );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
warn( "Failed to read the active log file at $log_dir/active_log\n" );
|
||||
}
|
||||
|
||||
close( I_FILE );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
return( $ret_str );
|
||||
}
|
||||
|
||||
sub loglist
|
||||
{
|
||||
my $sub_name = 'log_list';
|
||||
|
||||
my $__log_type = $_[0] || return( undef );
|
||||
my $brologs_dir = $BRO_CONFIG->{BROLOGS};
|
||||
my @ret_list;
|
||||
|
||||
if( opendir( DIR, $brologs_dir ) )
|
||||
{
|
||||
while( defined( my $file_name = readdir( DIR ) ) )
|
||||
{
|
||||
if( my $log_type = ( filenametoepochtime( $file_name ) )[0] )
|
||||
{
|
||||
if( $log_type eq $__log_type )
|
||||
{
|
||||
push( @ret_list, "$brologs_dir/$file_name" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Unable to open the BROLOGS directory\n" );
|
||||
return( undef );
|
||||
}
|
||||
|
||||
closedir( DIR );
|
||||
|
||||
if( wantarray )
|
||||
{
|
||||
return( @ret_list );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( \@ret_list );
|
||||
}
|
||||
}
|
||||
|
||||
sub filenametoepochtime
|
||||
{
|
||||
my $sub_name = 'filenametoepochtime';
|
||||
|
||||
# returns the log name, hostname, start time, and end time
|
||||
# log name will always return.
|
||||
# If any of the other three are not available then return value
|
||||
# will be undef.
|
||||
|
||||
my $filename = $_[0] || return( undef );
|
||||
my $log_name;
|
||||
my $host_name;
|
||||
my $start_time;
|
||||
my $end_time;
|
||||
|
||||
if( ! $filename =~ $FILENAME_REGEX )
|
||||
{
|
||||
print "$filename is bad!!\n";
|
||||
return( undef );
|
||||
}
|
||||
|
||||
# There are several ways in which the filename is formatted. This
|
||||
# if tree attempts to parse each of those
|
||||
|
||||
# Log name but no hostname or times. This can occur when running Bro
|
||||
# from a trace file.
|
||||
if( $filename =~ $name_trace )
|
||||
{
|
||||
$log_name = $1;
|
||||
}
|
||||
# filename contains the log name, hostname, and start time. This usually
|
||||
# occurs on filenames which are currently being written to or are not
|
||||
# rotated.
|
||||
elsif( my @file_parts = $filename =~ $name_running )
|
||||
{
|
||||
my $start_time_string;
|
||||
( $log_name, $host_name, $start_time_string ) = ( @file_parts );
|
||||
|
||||
# split up the string so it can be passed to timetoepoch
|
||||
my @parts = $start_time_string =~ m/^([[:digit:]]{2}) # year
|
||||
- # seperator
|
||||
([[:digit:]]{2}) # month
|
||||
- # seperator
|
||||
([[:digit:]]{2}) # day
|
||||
_ # time seperator
|
||||
([[:digit:]]{2}) # hour
|
||||
\. # seperator
|
||||
([[:digit:]]{2}) # minute
|
||||
\. # seperator
|
||||
([[:digit:]]{2}) # second
|
||||
$/x;
|
||||
|
||||
if( @parts == 6 )
|
||||
{
|
||||
$start_time = timetoepoch( @parts );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
# filename contains the log name, hostname, epoch start time, epoch end time
|
||||
elsif( my @file_parts = $filename =~ $name_epoch_range )
|
||||
{
|
||||
( $log_name, $host_name, $start_time, $end_time ) = @file_parts;
|
||||
}
|
||||
# filename contains the log name, hostname, start time and end time as
|
||||
# strings as put out by rotate logs.
|
||||
# i.e weird.lite3.06-04-27_10.40.53-06-04-27_10.41.12
|
||||
elsif( my @file_parts = $filename =~ $name_rotate_log )
|
||||
{
|
||||
my $start_time_string;
|
||||
my $end_time_string;
|
||||
|
||||
( $log_name, $host_name, $start_time_string, $end_time_string ) = @file_parts;
|
||||
|
||||
#print "***** $filename: st: $start_time_string, et: $end_time_string\n";
|
||||
|
||||
# look at the start date
|
||||
my @parts = $start_time_string =~ m/^([[:digit:]]{2}) # year
|
||||
- # seperator
|
||||
([[:digit:]]{2}) # month
|
||||
- # seperator
|
||||
([[:digit:]]{2}) # day
|
||||
_ # time seperator
|
||||
([[:digit:]]{2}) # hour
|
||||
\. # seperator
|
||||
([[:digit:]]{2}) # minute
|
||||
\. # seperator
|
||||
([[:digit:]]{2}) # second
|
||||
$/x;
|
||||
$start_time = timetoepoch( @parts );
|
||||
|
||||
# look at the start date
|
||||
@parts = $end_time_string =~ m/^([[:digit:]]{2}) # year
|
||||
- # seperator
|
||||
([[:digit:]]{2}) # month
|
||||
- # seperator
|
||||
([[:digit:]]{2}) # day
|
||||
_ # time seperator
|
||||
([[:digit:]]{2}) # hour
|
||||
\. # seperator
|
||||
([[:digit:]]{2}) # minute
|
||||
\. # seperator
|
||||
([[:digit:]]{2}) # second
|
||||
$/x;
|
||||
|
||||
$end_time = timetoepoch( @parts );
|
||||
|
||||
#print "***** st: $start_time, et: $end_time\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
|
||||
return( $log_name, $host_name, $start_time, $end_time );
|
||||
}
|
||||
|
||||
sub timetoepoch
|
||||
{
|
||||
my $sub_name = 'timetoepoch';
|
||||
|
||||
# arguments are in the order
|
||||
# year
|
||||
# month
|
||||
# day
|
||||
# hour
|
||||
# minutes
|
||||
# seconds
|
||||
|
||||
my $epoch_time;
|
||||
my( $year, $mon, $day, $hour, $min, $sec ) = @_;
|
||||
# The month fed into timelocal is 0 based index
|
||||
if( $mon > 0 )
|
||||
{
|
||||
--$mon;
|
||||
}
|
||||
|
||||
if( $epoch_time = timelocal($sec,$min,$hour,$day,$mon,$year) )
|
||||
{
|
||||
return( $epoch_time );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
694
scripts/perl/lib/Bro/Log/Alarm.pm
Normal file
694
scripts/perl/lib/Bro/Log/Alarm.pm
Normal file
|
@ -0,0 +1,694 @@
|
|||
package Bro::Log::Alarm;
|
||||
|
||||
use strict;
|
||||
require 5.006_001;
|
||||
use strict;
|
||||
|
||||
use vars qw( $VERSION
|
||||
%DATA_MAP );
|
||||
|
||||
# $Id: Alarm.pm 987 2005-01-08 01:04:43Z rwinslow $
|
||||
$VERSION = 1.20;
|
||||
|
||||
# Map data descriptions to subroutine names
|
||||
%DATA_MAP = ( t => \×tamp,
|
||||
timestamp => \×tamp,
|
||||
notice => \¬ice_type,
|
||||
notice_type => \¬ice_type,
|
||||
notice_act => \¬ice_action,
|
||||
notice_action => \¬ice_action,
|
||||
event_src => \&event_source,
|
||||
event_source => \&event_source,
|
||||
source_addr => \&source_addr,
|
||||
src_addr => \&source_addr,
|
||||
srcip => \&source_addr,
|
||||
source_ip => \&source_addr,
|
||||
src_port => \&source_port,
|
||||
source_port => \&source_port,
|
||||
destination_addr => \&destination_addr,
|
||||
dst_addr => \&destination_addr,
|
||||
dstip => \&destination_addr,
|
||||
destination_ip => \&destination_addr,
|
||||
dst_port => \&destination_port,
|
||||
destination_port => \&destination_port,
|
||||
user => \&user,
|
||||
filename => \&filename,
|
||||
sigid => \&sigid,
|
||||
method => \&method,
|
||||
URL => \&url,
|
||||
n => \&misc_integer,
|
||||
count => \&misc_integer,
|
||||
return_code => \&misc_integer,
|
||||
msg => \&message,
|
||||
message => \&message,
|
||||
sub_msg => \&sub_message,
|
||||
sub_message => \&sub_message,
|
||||
);
|
||||
|
||||
sub new
|
||||
{
|
||||
my $sub_name = 'new';
|
||||
|
||||
# This is the parser for tag based alarm and notice files.
|
||||
my $_log_line;
|
||||
my @_args = @_;
|
||||
my %alarm_parts;
|
||||
|
||||
if( @_args == 1 )
|
||||
{
|
||||
$_log_line = $_args[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
|
||||
# Order of data in array
|
||||
# t = timestamp
|
||||
# no = notice_type
|
||||
# na = notice_action
|
||||
# es = event_src, event_source
|
||||
# sa = source_ip (source address)
|
||||
# sp = source_port
|
||||
# da = destination_ip (destination address)
|
||||
# dp = destination_port
|
||||
# user = user
|
||||
# file = filename or sigid
|
||||
# method = method
|
||||
# url = URL
|
||||
# num = count or number or return_code
|
||||
# msg = message
|
||||
# sub = sub_message
|
||||
# tag = tag
|
||||
|
||||
# Is this a tag based log line delimited by spaces?
|
||||
if( $_log_line =~ m/^t\=/ )
|
||||
{
|
||||
my $i = 0;
|
||||
my $i2 = 0;
|
||||
my $len = length( $_log_line );
|
||||
my $p_idx = 0;
|
||||
my $buff_pos = 0;
|
||||
my $subtr_len = 0;
|
||||
my @log_parts;
|
||||
|
||||
for( $i2 = 0; $i2 < $len; ++$i2 )
|
||||
{
|
||||
if( substr( $_log_line, $i2, 1 ) eq ' ' and
|
||||
substr( $_log_line, $p_idx, 1 ) ne "\\" )
|
||||
{
|
||||
if( $subtr_len < 1 )
|
||||
{
|
||||
# Skip over this entry, probably just leading space.
|
||||
# Regardless of what happened there is no useful data.
|
||||
}
|
||||
else
|
||||
{
|
||||
my $tag;
|
||||
my $tag_data;
|
||||
|
||||
( $tag, $tag_data ) = extracttag( substr( $_log_line, $buff_pos, $subtr_len ) );
|
||||
if( exists( $alarm_parts{$tag} ) )
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Found duplicate tag '$tag', in data. It will be ignored\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
$alarm_parts{$tag} = $tag_data;
|
||||
}
|
||||
}
|
||||
$subtr_len = 0;
|
||||
$p_idx = $i2 + 1;
|
||||
$buff_pos = $i2 + 1;
|
||||
++$i;
|
||||
}
|
||||
else
|
||||
{
|
||||
++$subtr_len;
|
||||
$p_idx = $i2;
|
||||
}
|
||||
}
|
||||
|
||||
# Get the last piece of data
|
||||
my $tag;
|
||||
my $tag_data;
|
||||
( $tag, $tag_data ) = extracttag( substr( $_log_line, $buff_pos, $subtr_len ) );
|
||||
|
||||
# Make sure this is not a duplicate tag.
|
||||
if( exists( $alarm_parts{$tag} ) )
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Found duplicate tag '$tag', in data. It will be ignored\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
# Remove any trailing newlines
|
||||
chomp( $tag_data );
|
||||
$alarm_parts{$tag} = $tag_data;
|
||||
}
|
||||
}
|
||||
# Is this a colon delimited log line?
|
||||
elsif( $_log_line =~ m/^[[:digit:]]{10}\.[[:digit:]]{6}/ and $_log_line =~ m/\:/ )
|
||||
{
|
||||
my $i = 0;
|
||||
my $i2 = 0;
|
||||
my $len = length( $_log_line );
|
||||
my $p_idx = 0;
|
||||
my $buff_pos = 0;
|
||||
my $subtr_len = 0;
|
||||
my @log_parts;
|
||||
|
||||
for( $i2 = 0; $i2 < $len; ++$i2 )
|
||||
{
|
||||
if( substr( $_log_line, $i2, 1 ) eq ':' and
|
||||
substr( $_log_line, $p_idx, 1 ) ne "\\" )
|
||||
{
|
||||
if( $subtr_len < 1 )
|
||||
{
|
||||
$log_parts[$i] = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$log_parts[$i] = substr( $_log_line, $buff_pos, $subtr_len );
|
||||
$log_parts[$i] = unescape_colons( $log_parts[$i] );
|
||||
}
|
||||
$subtr_len = 0;
|
||||
$p_idx = $i2 + 1;
|
||||
$buff_pos = $i2 + 1;
|
||||
++$i;
|
||||
}
|
||||
else
|
||||
{
|
||||
++$subtr_len;
|
||||
$p_idx = $i2;
|
||||
}
|
||||
}
|
||||
|
||||
# Get the last piece of data
|
||||
$log_parts[$i] = unescape_colons( substr( $_log_line, $buff_pos, $subtr_len ) );
|
||||
|
||||
# Remove any trailing newline that may have been left on
|
||||
chomp( $log_parts[$i] );
|
||||
|
||||
$alarm_parts{t} = $log_parts[0];
|
||||
$alarm_parts{no} = $log_parts[1];
|
||||
$alarm_parts{na} = $log_parts[2];
|
||||
$alarm_parts{es} = $log_parts[3];
|
||||
$alarm_parts{sa} = $log_parts[4];
|
||||
$alarm_parts{sp} = $log_parts[5];
|
||||
$alarm_parts{da} = $log_parts[6];
|
||||
$alarm_parts{dp} = $log_parts[7];
|
||||
$alarm_parts{user} = $log_parts[8];
|
||||
$alarm_parts{file} = $log_parts[9];
|
||||
$alarm_parts{method} = $log_parts[10];
|
||||
$alarm_parts{url} = $log_parts[11];
|
||||
$alarm_parts{num} = $log_parts[12];
|
||||
$alarm_parts{msg} = $log_parts[13];
|
||||
$alarm_parts{sub} = $log_parts[14];
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
|
||||
# Make sure that certain fields have values otherwise the data is invalid
|
||||
if( exists( $alarm_parts{t} ) )
|
||||
{
|
||||
return( \%alarm_parts );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sub unescape
|
||||
{
|
||||
my $sub_name = 'unescape';
|
||||
|
||||
&unescape_spaces;
|
||||
}
|
||||
|
||||
sub unescape_spaces
|
||||
{
|
||||
my $sub_name = 'unescape_spaces';
|
||||
|
||||
my $data = $_[0];
|
||||
|
||||
if( ! defined( $data ) )
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
else
|
||||
{
|
||||
$data =~ s/\\ / /g;
|
||||
$data =~ s/\\\\/\\/g;
|
||||
}
|
||||
|
||||
return( $data );
|
||||
}
|
||||
|
||||
sub unescape_colons
|
||||
{
|
||||
my $sub_name = 'unescape_colons';
|
||||
|
||||
my $data = $_[0];
|
||||
|
||||
if( ! defined( $data ) )
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
else
|
||||
{
|
||||
$data =~ s/\\:/:/g;
|
||||
$data =~ s/\\\\/\\/g;
|
||||
}
|
||||
|
||||
return( $data );
|
||||
}
|
||||
|
||||
sub extracttag
|
||||
{
|
||||
my $sub_name = 'extracttag';
|
||||
|
||||
# Seperate the tag from it's data and return them. If there is a problem
|
||||
# this sub will return undef. If a tag has no data then a zero length
|
||||
# string will be returned.
|
||||
|
||||
my $__data = $_[0];
|
||||
my $ret_tag;
|
||||
my $ret_data;
|
||||
|
||||
# Seperate out the tag from the data
|
||||
( $ret_tag, $ret_data ) = split( /\=/, $__data, 2 );
|
||||
|
||||
if( length( $ret_tag ) > 0 )
|
||||
{
|
||||
if( defined( $ret_data ) )
|
||||
{
|
||||
$ret_data = unescape_spaces( $ret_data );
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret_data = '';
|
||||
}
|
||||
|
||||
return( $ret_tag, $ret_data );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
sub timestamp
|
||||
{
|
||||
my $sub_name = 'timestamp';
|
||||
|
||||
my $data = $_[0];
|
||||
my $format = $_[1]; # Maybe for future expansion. Just thinking out loud.
|
||||
|
||||
return( $data->{t} );
|
||||
}
|
||||
|
||||
sub notice_type
|
||||
{
|
||||
my $sub_name = 'notice_type';
|
||||
|
||||
my $data = $_[0] || return( undef );
|
||||
|
||||
return( $data->{no} );
|
||||
}
|
||||
|
||||
sub notice_action
|
||||
{
|
||||
my $sub_name = 'notice_action';
|
||||
|
||||
my $data = $_[0] || return( undef );
|
||||
|
||||
return( $data->{na} );
|
||||
}
|
||||
|
||||
sub event_source
|
||||
{
|
||||
my $sub_name = 'event_source';
|
||||
|
||||
my $data = $_[0] || return( undef );
|
||||
|
||||
if( exists( $data->{es} ) )
|
||||
{
|
||||
return( $data->{es} );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
sub source_addr
|
||||
{
|
||||
my $sub_name = 'source_addr';
|
||||
|
||||
my $data = $_[0] || return( undef );
|
||||
|
||||
if( exists( $data->{sa} ) )
|
||||
{
|
||||
return( $data->{sa} );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
sub source_ip
|
||||
{
|
||||
# This is for backwards compatibility and will be removed in the future
|
||||
&source_addr;
|
||||
}
|
||||
|
||||
sub source_port
|
||||
{
|
||||
my $sub_name = 'source_port';
|
||||
|
||||
my $data = $_[0] || return( undef );
|
||||
|
||||
if( exists( $data->{sp} ) )
|
||||
{
|
||||
return( $data->{sp} );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
sub destination_addr
|
||||
{
|
||||
my $sub_name = 'destination_addr';
|
||||
|
||||
my $data = $_[0] || return( undef );
|
||||
|
||||
return( $data->{da} );
|
||||
}
|
||||
|
||||
sub destination_ip
|
||||
{
|
||||
# This is for backwards compatibility and will be removed in the future
|
||||
&destination_addr;
|
||||
}
|
||||
|
||||
sub destination_port
|
||||
{
|
||||
my $sub_name = 'destination_port';
|
||||
|
||||
my $data = $_[0] || return( undef );
|
||||
|
||||
if( exists( $data->{dp} ) )
|
||||
{
|
||||
return( $data->{dp} );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
sub user
|
||||
{
|
||||
my $sub_name = 'user';
|
||||
|
||||
my $data = $_[0] || return( undef );
|
||||
|
||||
if( exists( $data->{user} ) )
|
||||
{
|
||||
return( $data->{user} );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
sub filename
|
||||
{
|
||||
my $sub_name = 'filename';
|
||||
|
||||
my $data = $_[0] || return( undef );
|
||||
|
||||
if( exists( $data->{file} ) )
|
||||
{
|
||||
return( $data->{file} );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
sub sigid
|
||||
{
|
||||
my $sub_name = 'sigid';
|
||||
|
||||
&filename;
|
||||
}
|
||||
|
||||
sub method
|
||||
{
|
||||
my $sub_name = 'method';
|
||||
|
||||
my $data = $_[0] || return( undef );
|
||||
|
||||
if( exists( $data->{method} ) )
|
||||
{
|
||||
return( $data->{method} );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
sub url
|
||||
{
|
||||
my $sub_name = 'url';
|
||||
|
||||
my $data = $_[0] || return( undef );
|
||||
|
||||
if( exists( $data->{url} ) )
|
||||
{
|
||||
return( $data->{url} );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
sub misc_integer
|
||||
{
|
||||
my $sub_name = 'misc_integer';
|
||||
|
||||
my $data = $_[0] || return( undef );
|
||||
|
||||
if( exists( $data->{num} ) )
|
||||
{
|
||||
return( $data->{num} );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
sub count
|
||||
{
|
||||
&misc_integer;
|
||||
}
|
||||
|
||||
sub return_code
|
||||
{
|
||||
&misc_integer;
|
||||
}
|
||||
|
||||
sub message
|
||||
{
|
||||
my $sub_name = 'message';
|
||||
|
||||
my $data = $_[0] || return( undef );
|
||||
|
||||
if( exists( $data->{msg} ) )
|
||||
{
|
||||
return( $data->{msg} );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
sub sub_message
|
||||
{
|
||||
my $sub_name = 'sub_message';
|
||||
|
||||
my $data = $_[0] || return( undef );
|
||||
|
||||
if( exists( $data->{sub} ) )
|
||||
{
|
||||
return( $data->{sub} );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
sub tag
|
||||
{
|
||||
my $sub_name = 'tag';
|
||||
|
||||
my $data = $_[0] || return( undef );
|
||||
|
||||
if( exists( $data->{tag} ) )
|
||||
{
|
||||
return( $data->{tag} );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
sub timerange
|
||||
{
|
||||
my $sub_name = 'timerange';
|
||||
# Find the most likely beginning and ending times covered by a given
|
||||
# alarm file.
|
||||
|
||||
my $filename = $_[0];
|
||||
my $start_time = 9999999999;
|
||||
my $end_time = -1;
|
||||
my $f_size = ( stat( $filename ) )[7];
|
||||
|
||||
if( open( INFILE, $filename ) )
|
||||
{
|
||||
my $s_idx = 0;
|
||||
my $s_no_change = 0;
|
||||
|
||||
# Find the smallest timestamp in the first 1000 lines.
|
||||
while( defined( my $ln = <INFILE> ) and
|
||||
( $s_idx < 1000 ) and
|
||||
( $s_no_change < 20 ) )
|
||||
{
|
||||
if( my $alarm_line = new( $ln ) )
|
||||
{
|
||||
my $w_timestamp = timestamp( $alarm_line );
|
||||
if( $w_timestamp < $start_time )
|
||||
{
|
||||
$start_time = $w_timestamp;
|
||||
$s_no_change = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
++$s_no_change;
|
||||
}
|
||||
}
|
||||
|
||||
++$s_idx;
|
||||
}
|
||||
|
||||
close( INFILE );
|
||||
|
||||
# Find the largest timestamp in the last 1000 lines
|
||||
# Each connection with a status of "SF" will be counted as one line
|
||||
# Every line will be examined but the "SF" lines are the only ones
|
||||
# that give a good picture as to the time state of the file.
|
||||
if( sysopen( INFILE, $filename, 0 ) )
|
||||
{
|
||||
sysseek( INFILE, $f_size, 0 );
|
||||
my $cur_pos = sysseek( INFILE, 0, 1 );
|
||||
my $nl_pos = $cur_pos;
|
||||
my $line_count = 0;
|
||||
my $e_no_change = 0;
|
||||
|
||||
# Get last 1000 lines
|
||||
while( $line_count < 1000 and $e_no_change < 20 )
|
||||
{
|
||||
my $new_line_found = 0;
|
||||
my $buf;
|
||||
sysread( INFILE, $buf, 1 );
|
||||
|
||||
if( $cur_pos > -1 )
|
||||
{
|
||||
if( $buf eq $/ )
|
||||
{
|
||||
$new_line_found = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
# Must have hit the beginning of the file
|
||||
if( $nl_pos > 20 )
|
||||
{
|
||||
$cur_pos = 0;
|
||||
sysseek( INFILE, 0, 0 );
|
||||
$new_line_found = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
if( $new_line_found )
|
||||
{
|
||||
my $cur_line = '';
|
||||
sysread( INFILE, $cur_line, $nl_pos - $cur_pos );
|
||||
if( my $alarm_line = new( $cur_line ) )
|
||||
{
|
||||
my $w_timestamp = timestamp( $alarm_line );
|
||||
if( $w_timestamp > $end_time )
|
||||
{
|
||||
$end_time = $w_timestamp;
|
||||
}
|
||||
else
|
||||
{
|
||||
++$e_no_change;
|
||||
}
|
||||
}
|
||||
$nl_pos = $cur_pos;
|
||||
++$line_count;
|
||||
}
|
||||
--$cur_pos;
|
||||
if( $cur_pos < 0 )
|
||||
{
|
||||
last;
|
||||
}
|
||||
sysseek( INFILE, $cur_pos, 0 );
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Unable to open file '$filename' with sysread.\n" );
|
||||
return( undef );
|
||||
}
|
||||
|
||||
close( INFILE );
|
||||
}
|
||||
else
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Unable to open file '$filename'.\n" );
|
||||
return( undef );
|
||||
}
|
||||
|
||||
# Make sure that sane values were found for the start and end times
|
||||
if( $start_time == 9999999999 or $end_time == -1 )
|
||||
{
|
||||
# warn( __PACKAGE__ . "::$sub_name, There was an error determining the start and end ranges.\n" );
|
||||
# warn( "No valid values could be found.\n" );
|
||||
return( undef );
|
||||
}
|
||||
|
||||
return( $start_time, $end_time );
|
||||
}
|
773
scripts/perl/lib/Bro/Log/Conn.pm
Normal file
773
scripts/perl/lib/Bro/Log/Conn.pm
Normal file
|
@ -0,0 +1,773 @@
|
|||
package Bro::Log::Conn;
|
||||
|
||||
require 5.006_001;
|
||||
use strict;
|
||||
|
||||
use vars qw( $VERSION
|
||||
$NULL_VALUE
|
||||
$DEBUG );
|
||||
|
||||
# $Id: Conn.pm 1426 2005-09-30 00:19:18Z rwinslow $
|
||||
$VERSION = 1.20;
|
||||
$NULL_VALUE = -1;
|
||||
$DEBUG = 0;
|
||||
|
||||
my $CONN_SPLIT_PATT = ' ';
|
||||
# my $CONN_SPLIT_PATT = qr/ /o;
|
||||
|
||||
# Map data descriptions to subroutine names
|
||||
my %DATA_MAP = ( timestamp => \×tamp,
|
||||
duration => \&duration,
|
||||
source_ip => \&srcip,
|
||||
srcip => \&srcip,
|
||||
destination_ip => \&dstip,
|
||||
dstip => \&dstip,
|
||||
service => \&service,
|
||||
source_port => \&srcport,
|
||||
srcport => \&srcport,
|
||||
destination_port => \&dstport,
|
||||
dstport => \&dstport,
|
||||
protocol => \&protocol,
|
||||
source_bytes => \&srcbytes,
|
||||
srcbytes => \&srcbytes,
|
||||
destination_bytes => \&srcbytes,
|
||||
dstbytes => \&dstbytes,
|
||||
connection_status => \&connstat,
|
||||
connstat => \&connstat,
|
||||
source_network => \&srcnetwork,
|
||||
srcnetwork => \&srcnetwork,
|
||||
other => \&other,
|
||||
);
|
||||
|
||||
sub new
|
||||
{
|
||||
my $_log_line = $_[0] || return( undef ); # string ref
|
||||
|
||||
# Order of data in array
|
||||
# 0 = timestamp
|
||||
# 1 = duration
|
||||
# 2 = source ip
|
||||
# 3 = destination ip
|
||||
# 4 = service
|
||||
# 5 = source port
|
||||
# 6 = destination port
|
||||
# 7 = protocol
|
||||
# 8 = source bytes
|
||||
# 9 = destination bytes
|
||||
# 10 = connection status
|
||||
# 11 = source network
|
||||
# 12 = other
|
||||
|
||||
my @log_parts = split( $CONN_SPLIT_PATT, $$_log_line, 13 );
|
||||
if( defined( $log_parts[11] ) )
|
||||
{
|
||||
return( \@log_parts );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
sub output
|
||||
{
|
||||
my $sub_name = 'output';
|
||||
|
||||
my $data = $_[0] || return undef;
|
||||
my $format = $_[1] || '';
|
||||
my @ret_data;
|
||||
|
||||
if( ref( $format ) ne 'ARRAY' )
|
||||
{
|
||||
$format = [ 'timestamp',
|
||||
'duration',
|
||||
'srcip',
|
||||
'dstip',
|
||||
'service',
|
||||
'srcport',
|
||||
'dstport',
|
||||
'protocol',
|
||||
'srcbytes',
|
||||
'dstbytes',
|
||||
'connstat',
|
||||
'srcnetwork',
|
||||
'other',
|
||||
];
|
||||
}
|
||||
|
||||
my $i = 0;
|
||||
foreach my $key( @{$format} )
|
||||
{
|
||||
if( exists( $DATA_MAP{$key} ) )
|
||||
{
|
||||
$ret_data[$i] = &{$DATA_MAP{$key}}( $data );
|
||||
++$i;
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
if( wantarray )
|
||||
{
|
||||
return( @ret_data );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( join( ' ', @ret_data ) );
|
||||
}
|
||||
}
|
||||
|
||||
sub timestamp
|
||||
{
|
||||
my $sub_name = 'timestamp';
|
||||
|
||||
my $data = $_[0] || return( undef );
|
||||
|
||||
return( $data->[0] );
|
||||
}
|
||||
|
||||
sub duration
|
||||
{
|
||||
my $sub_name = 'duration';
|
||||
|
||||
my $data = $_[0] || return undef;
|
||||
my $arg1 = $_[1] || 0;
|
||||
|
||||
if( $arg1 eq 'raw' )
|
||||
{
|
||||
return( $data->[1] );
|
||||
}
|
||||
elsif( $data->[1] eq '?' and defined( $NULL_VALUE ) )
|
||||
{
|
||||
return( $NULL_VALUE );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( $data->[1] );
|
||||
}
|
||||
}
|
||||
|
||||
sub source_ip
|
||||
{
|
||||
&srcip;
|
||||
}
|
||||
|
||||
sub srcip
|
||||
{
|
||||
my $sub_name = 'srcip';
|
||||
|
||||
return( $_[0]->[2] );
|
||||
}
|
||||
|
||||
sub destination_ip
|
||||
{
|
||||
&dstip;
|
||||
}
|
||||
|
||||
sub dstip
|
||||
{
|
||||
my $sub_name = 'dstip';
|
||||
|
||||
return( $_[0]->[3] );
|
||||
}
|
||||
|
||||
sub service
|
||||
{
|
||||
my $sub_name = 'service';
|
||||
|
||||
return( $_[0]->[4] );
|
||||
}
|
||||
|
||||
sub source_port
|
||||
{
|
||||
&srcport;
|
||||
}
|
||||
|
||||
sub srcport
|
||||
{
|
||||
my $sub_name = 'srcport';
|
||||
|
||||
return( $_[0]->[5] );
|
||||
}
|
||||
|
||||
sub destination_port
|
||||
{
|
||||
&dstport
|
||||
}
|
||||
|
||||
sub dstport
|
||||
{
|
||||
my $sub_name = 'dstport';
|
||||
|
||||
return( $_[0]->[6] );
|
||||
}
|
||||
|
||||
sub protocol
|
||||
{
|
||||
my $sub_name = 'protocol';
|
||||
|
||||
return( $_[0]->[7] );
|
||||
}
|
||||
|
||||
sub source_bytes
|
||||
{
|
||||
&srcbytes;
|
||||
}
|
||||
|
||||
sub srcbytes
|
||||
{
|
||||
my $sub_name = 'srcbytes';
|
||||
|
||||
my $data = $_[0] || return undef;
|
||||
my $arg1 = $_[1] || 0;
|
||||
|
||||
if( $arg1 eq 'raw' )
|
||||
{
|
||||
return( $data->[8] );
|
||||
}
|
||||
elsif( $data->[8] eq '?' and defined( $NULL_VALUE ) )
|
||||
{
|
||||
return( $NULL_VALUE );
|
||||
}
|
||||
elsif( $data->[10] eq 'SF')
|
||||
{
|
||||
# safest to only count sessions with normal termination
|
||||
return( $data->[8] );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( $NULL_VALUE );
|
||||
}
|
||||
}
|
||||
|
||||
sub destination_bytes
|
||||
{
|
||||
&dstbytes;
|
||||
}
|
||||
|
||||
sub dstbytes
|
||||
{
|
||||
my $sub_name = 'dstbytes';
|
||||
|
||||
my $data = $_[0] || return undef;
|
||||
my $arg1 = $_[1] || 0;
|
||||
|
||||
if( $arg1 eq 'raw' )
|
||||
{
|
||||
return( $data->[9] );
|
||||
}
|
||||
elsif( $data->[9] eq '?' and defined( $NULL_VALUE ) )
|
||||
{
|
||||
return( $NULL_VALUE );
|
||||
}
|
||||
elsif( $data->[10] eq 'SF' )
|
||||
{
|
||||
# safest to only count sessions with normal termination
|
||||
return( $data->[9] );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( $NULL_VALUE );
|
||||
}
|
||||
}
|
||||
|
||||
sub connstat
|
||||
{
|
||||
my $sub_name = 'connstat';
|
||||
|
||||
my $data = $_[0] || return undef;
|
||||
|
||||
return( $data->[10] );
|
||||
}
|
||||
|
||||
sub source_network
|
||||
{
|
||||
&srcnetwork;
|
||||
}
|
||||
|
||||
sub srcnetwork
|
||||
{
|
||||
my $sub_name = 'srcnetwork';
|
||||
|
||||
my $data = $_[0] || return undef;
|
||||
chomp( $data->[11] );
|
||||
|
||||
return( $data->[11] );
|
||||
}
|
||||
|
||||
sub tag
|
||||
{
|
||||
my $sub_name = 'tag';
|
||||
|
||||
my $data = $_[0] || return( undef );
|
||||
my $other_field = $data->[12];
|
||||
my @ret_tag_ids;
|
||||
|
||||
while( $other_field =~ s/(\@[[:digit:]]+)// )
|
||||
{
|
||||
push( @ret_tag_ids, $1 );
|
||||
}
|
||||
|
||||
if( @ret_tag_ids > 0 )
|
||||
{
|
||||
if( wantarray )
|
||||
{
|
||||
return( @ret_tag_ids );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( \@ret_tag_ids );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
sub other
|
||||
{
|
||||
my $sub_name = 'other';
|
||||
|
||||
my $data = $_[0] || return undef;
|
||||
|
||||
# Remove any newline character at the end
|
||||
chomp( $data->[12] );
|
||||
|
||||
return( $data->[12] );
|
||||
}
|
||||
|
||||
sub timerange
|
||||
{
|
||||
my $sub_name = 'timerange';
|
||||
# Find the most likely beginning and ending times covered by a given
|
||||
# conn file.
|
||||
|
||||
my $filename = $_[0];
|
||||
my $find_start_time = $_[1];
|
||||
my $find_end_time = $_[2];
|
||||
my $start_time = 9999999999;
|
||||
my $end_time = -1;
|
||||
my $max_start_lines = 10000;
|
||||
my $max_end_lines = 10000;
|
||||
my $max_line_length = 5000;
|
||||
my $f_size = ( stat( $filename ) )[7] || 0;
|
||||
my $default_start;
|
||||
my $default_end;
|
||||
|
||||
if( $DEBUG > 2 )
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Filename: $filename\n" );
|
||||
}
|
||||
|
||||
# If the file is zero size then don't even both continuing
|
||||
if( $f_size < 1 )
|
||||
{
|
||||
if( $DEBUG > 2 )
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, File is zero size, skipping\n" );
|
||||
}
|
||||
return( undef );
|
||||
}
|
||||
|
||||
# If $find_start_time and $find_end_time are defined then the the first
|
||||
# line that is greater than or equal to the timestamp in $find_start_time
|
||||
# will be read by seek and then set into $start_pos.
|
||||
# The last line that contains a timestamp less than or equal to
|
||||
# $find_end_time will be read by seek and then set in $end_pos.
|
||||
eval {
|
||||
local $SIG{ALRM} = sub { die( "Alarm Timeout\n" ) };
|
||||
alarm 90;
|
||||
if( open( INFILE, $filename ) )
|
||||
{
|
||||
my $s_idx = 0; # start line counter
|
||||
my $s_no_change = 0; # start no change counter
|
||||
|
||||
# Set the very first connection timestamp to $default_start
|
||||
while( ! $default_start and defined( my $line = <INFILE> ) )
|
||||
{
|
||||
if( my $conn_line = new( \$line ) )
|
||||
{
|
||||
$default_start = timestamp( $conn_line );
|
||||
}
|
||||
}
|
||||
|
||||
# Find the smallest timestamp in the first 1000 lines where the
|
||||
# connection is complete (SF) or (REJ) and the duration is less
|
||||
# than .1 seconds
|
||||
while( ( $s_idx < $max_start_lines ) and
|
||||
( $s_no_change < 20 ) and
|
||||
defined( my $ln = <INFILE> ) )
|
||||
{
|
||||
if( my $conn_line = new( \$ln ) )
|
||||
{
|
||||
if( connstat( $conn_line ) =~ m/^(?:SF)|(?:REJ)$/ )
|
||||
{
|
||||
if( duration( $conn_line ) < 0.1 )
|
||||
{
|
||||
my $w_timestamp = timestamp( $conn_line );
|
||||
if( $w_timestamp < $start_time )
|
||||
{
|
||||
$start_time = $w_timestamp;
|
||||
$s_no_change = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
++$s_no_change;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++$s_idx;
|
||||
}
|
||||
|
||||
close( INFILE );
|
||||
|
||||
# Find the largest timestamp in the last 20 lines
|
||||
# Each connection with a status of "SF" or "REJ" will be counted as
|
||||
# one line. Every line will be examined but the "SF" or "REJ"
|
||||
# lines are the only ones that give a good picture as to the time
|
||||
# state of the file.
|
||||
if( sysopen( INFILE, $filename, 0 ) )
|
||||
{
|
||||
sysseek( INFILE, $f_size, 0 );
|
||||
my $cur_pos = sysseek( INFILE, 0, 1 );
|
||||
my $nl_pos = $cur_pos;
|
||||
my $matched_count = 0;
|
||||
my $line_count = 0;
|
||||
|
||||
# Get last 20 lines
|
||||
while( $matched_count < 20 and
|
||||
$line_count < $max_end_lines )
|
||||
{
|
||||
my $new_line_found = 0;
|
||||
my $buf;
|
||||
sysread( INFILE, $buf, 1 );
|
||||
|
||||
if( $cur_pos > -1 )
|
||||
{
|
||||
if( $buf eq $/ )
|
||||
{
|
||||
$new_line_found = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
# Must have hit the beginning of the file
|
||||
if( $nl_pos > 20 ) # supress things like blank lines
|
||||
{
|
||||
sysseek( INFILE, 0, 0 );
|
||||
$new_line_found = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
if( $new_line_found )
|
||||
{
|
||||
my $cur_line = '';
|
||||
++$line_count;
|
||||
# Make sure that the line is not too large
|
||||
# Fix for some funky rsync errors that may occur
|
||||
if( $nl_pos - $cur_pos > $max_line_length )
|
||||
{
|
||||
# WAY too big, just mark new position and ignore
|
||||
}
|
||||
else
|
||||
{
|
||||
sysread( INFILE, $cur_line, $nl_pos - $cur_pos );
|
||||
if( my $conn_line = new( \$cur_line ) )
|
||||
{
|
||||
if( ! $default_end )
|
||||
{
|
||||
$default_end = timestamp( $conn_line );
|
||||
}
|
||||
|
||||
if( duration( $conn_line ) < 0.1 and duration( $conn_line ) >= 0 )
|
||||
{
|
||||
my $w_timestamp = timestamp( $conn_line );
|
||||
if( $w_timestamp > $end_time )
|
||||
{
|
||||
$end_time = $w_timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
if( connstat( $conn_line ) =~ m/^(?:SF)|(?:REJ)$/ )
|
||||
{
|
||||
++$matched_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
$nl_pos = $cur_pos;
|
||||
}
|
||||
--$cur_pos;
|
||||
if( $cur_pos < 0 )
|
||||
{
|
||||
last;
|
||||
}
|
||||
sysseek( INFILE, $cur_pos, 0 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( $DEBUG > 0 )
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Unable to open file '$filename' with sysread.\n" );
|
||||
}
|
||||
return( undef );
|
||||
}
|
||||
|
||||
close( INFILE );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( $DEBUG > 0 )
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Unable to open file '$filename'.\n" );
|
||||
}
|
||||
return( undef );
|
||||
}
|
||||
|
||||
close( INFILE );
|
||||
};
|
||||
|
||||
alarm 0;
|
||||
|
||||
# Make sure that $start_time has something other than the filler value.
|
||||
if( $start_time == 9999999999 )
|
||||
{
|
||||
if( $default_start )
|
||||
{
|
||||
$start_time = $default_start;
|
||||
if( $DEBUG > 1 )
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, No start_time was found, setting to a default of $default_start\n" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( $DEBUG > 1 )
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, No start_time was found and no default_start time was found\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Make sure that $end_time has something other than the filler value.
|
||||
if( $end_time == -1 )
|
||||
{
|
||||
if( $default_end )
|
||||
{
|
||||
$end_time = $default_end;
|
||||
if( $DEBUG > 1 )
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, No end_time was found, setting to a default of $default_start\n" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( $DEBUG > 1 )
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, No end_time was found and no default_end time was found\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( $DEBUG > 2 )
|
||||
{
|
||||
warn( " " . __PACKAGE__ . "::$sub_name, Start time: $start_time\n" );
|
||||
warn( " " . __PACKAGE__ . "::$sub_name, End time: $end_time\n" );
|
||||
}
|
||||
|
||||
if( $@ )
|
||||
{
|
||||
if( $@ =~ m/Alarm Timeout/ )
|
||||
{
|
||||
if( !( $start_time and $end_time ) )
|
||||
{
|
||||
if( $DEBUG > 0 )
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Error occurred in trying to read the file $filename\n" );
|
||||
}
|
||||
return( undef );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( $DEBUG > 0 )
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Timed out during file read. The first and last timestamps have been set as the range of time available\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
warn( $@ );
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
return( $start_time, $end_time );
|
||||
}
|
||||
|
||||
sub containstag
|
||||
{
|
||||
my $sub_name = 'containstag';
|
||||
|
||||
my $data = shift || return( undef );
|
||||
my @tags_to_match = @_;
|
||||
my $conn_tags = tag( $data ) || return( 0 );
|
||||
my $matched_tag = 0;
|
||||
|
||||
OUT_LOOP:
|
||||
{
|
||||
foreach my $tag_to_match( @tags_to_match )
|
||||
{
|
||||
foreach my $tag_id( @{$conn_tags} )
|
||||
{
|
||||
if( $tag_id eq $tag_to_match )
|
||||
{
|
||||
$matched_tag = $tag_id;
|
||||
last OUT_LOOP;
|
||||
}
|
||||
}
|
||||
}
|
||||
} # end OUT_LOOP
|
||||
|
||||
return( $matched_tag );
|
||||
}
|
||||
|
||||
sub startposition
|
||||
{
|
||||
my $sub_name = 'startposition';
|
||||
# Find the first file position where $timestamp is greater than or equal to
|
||||
# a timestamp in the file.
|
||||
my $timestamp = $_[0];
|
||||
}
|
||||
|
||||
sub endposition
|
||||
{
|
||||
my $sub_name = 'endposition';
|
||||
# Find the last file position where $timestamp is less than or equal to
|
||||
# a timestamp in a file.
|
||||
my $timestamp = $_[0];
|
||||
}
|
||||
|
||||
sub connectsucceed
|
||||
{
|
||||
my $sub_name = 'connectsucceed';
|
||||
|
||||
my $data = $_[0] || return( undef );
|
||||
|
||||
my $S_REGEX = qr/^S/o;
|
||||
my $S123_REGEX = qr/^S[123]$/o;
|
||||
my $connstat = connstat( $data );
|
||||
|
||||
if( $connstat =~ $S_REGEX )
|
||||
{
|
||||
if( $connstat eq 'SF' )
|
||||
{
|
||||
return( 1 );
|
||||
}
|
||||
elsif( $connstat =~ $S123_REGEX )
|
||||
{
|
||||
if( srcbytes( $data ) > 0 && dstbytes( $data ) > 0 )
|
||||
{
|
||||
return( 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
# connection failed
|
||||
return( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
sub range
|
||||
{
|
||||
my $sub_name = 'range';
|
||||
|
||||
my $data = $_[0] || return( undef );
|
||||
my $match_time = $_[1];
|
||||
my $error_margin = $_[2];
|
||||
my $start_time;
|
||||
my $end_time;
|
||||
my $duration;
|
||||
|
||||
# Make sure that the error margin is greater than zero
|
||||
if( !( defined( $error_margin ) and $error_margin > 0 ) )
|
||||
{
|
||||
$error_margin = 0;
|
||||
}
|
||||
|
||||
$start_time = timestamp( $data );
|
||||
$duration = duration( $data );
|
||||
|
||||
if( $match_time )
|
||||
{
|
||||
if( $duration < 0 )
|
||||
{
|
||||
$duration = 10;
|
||||
}
|
||||
|
||||
$end_time = $start_time + $duration + $error_margin;
|
||||
$start_time = $start_time - $error_margin;
|
||||
|
||||
if( $match_time >= $start_time and
|
||||
$match_time <= $end_time )
|
||||
{
|
||||
return( 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( $duration > -1 )
|
||||
{
|
||||
$end_time = $start_time + $duration;
|
||||
}
|
||||
|
||||
return( $start_time, $end_time );
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
# The args to Bro::Log::Conn::output are the connection array ref returned by
|
||||
# Bro::Log::Conn::new and an optional array ref of what order and fields
|
||||
# should be printed.
|
||||
|
||||
# EXAMPLE:
|
||||
# $array_ref = Bro::Log::Conn::new( $ln );
|
||||
# @output_parts = Bro::Log::Conn::output( $array_ref, [ 'srcip', 'dstip', 'timestamp' ] )
|
||||
#
|
||||
# The available fields are as follows:
|
||||
# timestamp
|
||||
# duration
|
||||
# srcip
|
||||
# dstip
|
||||
# service
|
||||
# srcport
|
||||
# dstport
|
||||
# protocol
|
||||
# srcbytes
|
||||
# dstbytes
|
||||
# connstat
|
||||
# srcnetwork
|
||||
# other
|
||||
|
||||
# For convenience any data that is represented by a ? will be replaced by a -1
|
||||
# This occurs for duration, srcbytes, and dstbytes
|
||||
# This is adjustable by changing $NULL_VALUE
|
714
scripts/perl/lib/Bro/Report.pm
Normal file
714
scripts/perl/lib/Bro/Report.pm
Normal file
|
@ -0,0 +1,714 @@
|
|||
package Bro::Report;
|
||||
|
||||
use strict;
|
||||
require 5.006_001;
|
||||
require Exporter;
|
||||
|
||||
use Socket;
|
||||
use vars qw( $VERSION
|
||||
$DEBUG
|
||||
@EXPORT_OK
|
||||
@ISA
|
||||
$USE_FLOCK
|
||||
$INCIDENT_COUNT_FILE
|
||||
$TEMP_DIR
|
||||
@TEMP_FILES
|
||||
$IPTONAME_TIMEOUT
|
||||
$USE_IPTONAME_CACHE
|
||||
%IPTONAME_CACHE );
|
||||
|
||||
@ISA = ( 'Exporter' );
|
||||
# $Id: Report.pm 1419 2005-09-29 18:56:06Z rwinslow $
|
||||
$VERSION = 1.20;
|
||||
$DEBUG = 0;
|
||||
@EXPORT_OK = qw( iptoname swrite trimhostname trimbytes time_mdhm time_hms date_md
|
||||
date_ymd getincidentnumber standard_deviation mean_val tempfile
|
||||
trimstring );
|
||||
|
||||
my %STEPS = ( 0 => '',
|
||||
1 => 'K',
|
||||
2 => 'M',
|
||||
3 => 'G',
|
||||
4 => 'T',
|
||||
5 => 'P',
|
||||
K => 1,
|
||||
M => 2,
|
||||
G => 3,
|
||||
T => 4,
|
||||
G => 5, );
|
||||
|
||||
# Check if flock can be used
|
||||
eval {
|
||||
flock( STDIN, 1 )
|
||||
};
|
||||
|
||||
if( $@ )
|
||||
{
|
||||
$USE_FLOCK = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
$USE_FLOCK = 1;
|
||||
}
|
||||
|
||||
# Default temp directorywhich to write to
|
||||
$TEMP_DIR = '/tmp';
|
||||
|
||||
# Default timeout for dns reverse lookups
|
||||
$IPTONAME_TIMEOUT = 3;
|
||||
|
||||
# Should ip to name reverse lookups be cached?
|
||||
$USE_IPTONAME_CACHE = 1;
|
||||
|
||||
sub iptoname
|
||||
{
|
||||
my $sub_name = 'iptoname';
|
||||
|
||||
my $h_ip = $_[0] || return( undef );
|
||||
|
||||
my $resolved_hostname = undef;
|
||||
my $ret_val;
|
||||
|
||||
if( exists( $IPTONAME_CACHE{$h_ip} ) )
|
||||
{
|
||||
return( $IPTONAME_CACHE{$h_ip} );
|
||||
}
|
||||
|
||||
eval
|
||||
{
|
||||
local $SIG{ALRM} = sub { die( "Lookup Timeout\n" ) };
|
||||
alarm( $IPTONAME_TIMEOUT);
|
||||
$resolved_hostname = gethostbyaddr( inet_aton( $h_ip ), 2 );
|
||||
alarm( 0 );
|
||||
};
|
||||
|
||||
if( $resolved_hostname )
|
||||
{
|
||||
$ret_val = $resolved_hostname;
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret_val = $h_ip;
|
||||
}
|
||||
|
||||
if( $USE_IPTONAME_CACHE )
|
||||
{
|
||||
$IPTONAME_CACHE{$h_ip} = $ret_val;
|
||||
}
|
||||
|
||||
return( $ret_val );
|
||||
}
|
||||
|
||||
sub swrite
|
||||
{
|
||||
my $sub_name = 'swrite';
|
||||
|
||||
my $format = shift;
|
||||
my @args = @_;
|
||||
my $ret_val;
|
||||
|
||||
$^A = '';
|
||||
formline( $format, @args );
|
||||
$ret_val = $^A;
|
||||
$^A = '';
|
||||
return( $ret_val );
|
||||
}
|
||||
|
||||
sub trimhostname
|
||||
{
|
||||
my $sub_name = 'trimhostname';
|
||||
|
||||
my $hostname = $_[0];
|
||||
my $max_length = $_[1] || 35;
|
||||
my $direction = $_[2] || '>';
|
||||
my $ret_val = '';
|
||||
|
||||
my $len = length( $hostname );
|
||||
if( $len > $max_length )
|
||||
{
|
||||
my $dif = $len - $max_length + 3;
|
||||
if( $direction eq '>' )
|
||||
{
|
||||
$ret_val = "..." . substr( $hostname, $dif, $len);
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret_val = substr( $hostname, 0, $len - $dif) . "...";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret_val = $hostname;
|
||||
}
|
||||
|
||||
return( $ret_val );
|
||||
}
|
||||
|
||||
sub trimbytes
|
||||
{
|
||||
my $sub_name = 'trimbytes';
|
||||
|
||||
my $arg1 = $_[0];
|
||||
my $max_width = $_[1] || 6;
|
||||
my $quantifiers = 'KMGTP';
|
||||
my $step_count = 0;
|
||||
my $bytes;
|
||||
my $ret_val;
|
||||
|
||||
if( $arg1 =~ m/([[:digit:]]+)[[:space:]]*([$quantifiers])$/ )
|
||||
{
|
||||
$bytes = $1;
|
||||
$step_count = $STEPS{$2};
|
||||
}
|
||||
else
|
||||
{
|
||||
$bytes = $arg1;
|
||||
}
|
||||
|
||||
if( length( $bytes ) > $max_width )
|
||||
{
|
||||
$max_width -= 2;
|
||||
my $ints = int( $bytes );
|
||||
while( exists( $STEPS{$step_count} ) and length( $ints ) > $max_width )
|
||||
{
|
||||
$bytes = $bytes / 1024;
|
||||
$ints = int( $bytes );
|
||||
++$step_count;
|
||||
}
|
||||
my $float_length = $max_width - length( $ints ) - 1;
|
||||
if( $float_length > 0 )
|
||||
{
|
||||
$bytes = sprintf( "%.$float_length" . 'f', $bytes );
|
||||
}
|
||||
else
|
||||
{
|
||||
$bytes = sprintf( "%d", $bytes );
|
||||
}
|
||||
}
|
||||
|
||||
if( $STEPS{$step_count} )
|
||||
{
|
||||
return( $bytes . " $STEPS{$step_count}" );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( $bytes );
|
||||
}
|
||||
}
|
||||
|
||||
sub trimstring
|
||||
{
|
||||
my $sub_name = 'trimstring';
|
||||
|
||||
my $string = $_[0] || return( undef );
|
||||
my $max_length = $_[1] || 73;
|
||||
my $max_lines = $_[2];
|
||||
my @ret_lines;
|
||||
my $trunc_string = 0;
|
||||
|
||||
if( length( $string ) <= $max_length )
|
||||
{
|
||||
return( $string );
|
||||
}
|
||||
|
||||
if( defined( $max_lines )
|
||||
and $max_lines =~ /^[[:digit:]]+$/
|
||||
and $max_lines > 0 )
|
||||
{
|
||||
# OK, looks good
|
||||
}
|
||||
else
|
||||
{
|
||||
$max_lines = 1;
|
||||
}
|
||||
|
||||
while( length( $string ) > $max_length
|
||||
and !( scalar( @ret_lines ) >= $max_lines ) )
|
||||
{
|
||||
my $cur_idx = $max_length - 1;
|
||||
my $found_break_point = 0;
|
||||
while( $cur_idx > 0 )
|
||||
{
|
||||
if( substr( $string, $cur_idx, 1 ) =~ m/[[:space:]]/ )
|
||||
{
|
||||
push( @ret_lines, substr( $string, 0, $cur_idx + 1 ) );
|
||||
$string = substr( $string, $cur_idx );
|
||||
$found_break_point = 1;
|
||||
last;
|
||||
}
|
||||
else
|
||||
{
|
||||
--$cur_idx;
|
||||
}
|
||||
}
|
||||
|
||||
if( ! $found_break_point )
|
||||
{
|
||||
push( @ret_lines, substr( $string, 0, $max_length ) );
|
||||
$string = substr( $string, $max_length );
|
||||
}
|
||||
}
|
||||
|
||||
# Check if anything is left in the string
|
||||
if( length( $string ) > 0 )
|
||||
{
|
||||
$trunc_string = 1;
|
||||
|
||||
if( !( scalar( @ret_lines ) >= $max_lines ) )
|
||||
{
|
||||
push( @ret_lines, $string );
|
||||
$trunc_string = 0;
|
||||
}
|
||||
elsif( length( $ret_lines[$#ret_lines] ) < $max_length )
|
||||
{
|
||||
$ret_lines[$#ret_lines] .= substr( $string, 0, $max_length - length( $ret_lines[$#ret_lines] ) );
|
||||
}
|
||||
|
||||
if( $trunc_string )
|
||||
{
|
||||
$ret_lines[$#ret_lines] =~ s/.{4}$/\.\.\.>/;
|
||||
}
|
||||
}
|
||||
|
||||
return( @ret_lines );
|
||||
}
|
||||
|
||||
sub time_mdhm
|
||||
{
|
||||
my $sub_name = 'time_mdhm';
|
||||
# Convert time from epoch to MONTH/DAY HOUR:MINUTE
|
||||
# 08/13 13:44
|
||||
my $arg1 = $_[0];
|
||||
my $ret_val;
|
||||
|
||||
if( my @tp = localtime( $arg1 ) )
|
||||
{
|
||||
my $mon = sprintf( "%02d", $tp[4] + 1 );
|
||||
my $day = sprintf( "%02d", $tp[3] );
|
||||
my $hour = sprintf( "%02d", $tp[2] );
|
||||
my $min = sprintf( "%02d", $tp[1] );
|
||||
|
||||
$ret_val = "$mon/$day $hour:$min";
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
|
||||
return( $ret_val );
|
||||
}
|
||||
|
||||
sub time_hms
|
||||
{
|
||||
my $sub_name = 'time_hms';
|
||||
# Convert epoch to to HH:MM:SS
|
||||
|
||||
my $arg1 = $_[0];
|
||||
my $ret_val;
|
||||
|
||||
if( my @tp = localtime( $arg1 ) )
|
||||
{
|
||||
my $hour = sprintf( "%02d", $tp[2] );
|
||||
my $min = sprintf( "%02d", $tp[1] );
|
||||
my $sec = sprintf( "%02d", $tp[0] );
|
||||
|
||||
$ret_val = "$hour:$min:$sec";
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
|
||||
return( $ret_val );
|
||||
|
||||
}
|
||||
|
||||
sub date_md
|
||||
{
|
||||
my $sub_name = 'date_md';
|
||||
# Convert time from epoch to MONTH/DAY
|
||||
|
||||
my $arg1 = $_[0];
|
||||
my $ret_val;
|
||||
|
||||
if( my @tp = localtime( $arg1 ) )
|
||||
{
|
||||
my $mon = sprintf( "%02d", $tp[4] + 1 );
|
||||
my $day = sprintf( "%02d", $tp[3] );
|
||||
|
||||
$ret_val = "$mon/$day";
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
|
||||
return( $ret_val );
|
||||
}
|
||||
|
||||
sub date_ymd
|
||||
{
|
||||
my $sub_name = 'date_ymd';
|
||||
# Convert time from epoch to YEAR/MONTH/DAY
|
||||
|
||||
my $arg1 = $_[0];
|
||||
my $ret_val;
|
||||
|
||||
if( my @tp = localtime( $arg1 ) )
|
||||
{
|
||||
my $mon = sprintf( "%02d", $tp[4] + 1 );
|
||||
my $day = sprintf( "%02d", $tp[3] );
|
||||
my $year = $tp[5] + 1900;
|
||||
|
||||
$ret_val = "$year/$mon/$day";
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
|
||||
return( $ret_val );
|
||||
}
|
||||
|
||||
sub getincidentnumber
|
||||
{
|
||||
my $sub_name = 'getincidentnumber';
|
||||
|
||||
my $arg1 = $_[0];
|
||||
my $failed = 0;
|
||||
my $ret_count;
|
||||
|
||||
# Check if the $INCIDENT_COUNT_FILE has been set yet
|
||||
if( ! $INCIDENT_COUNT_FILE )
|
||||
{
|
||||
setincidentcountfile();
|
||||
}
|
||||
|
||||
# Make sure that the files exists
|
||||
if( ! -f $INCIDENT_COUNT_FILE )
|
||||
{
|
||||
if( open( OUTFILE, ">$INCIDENT_COUNT_FILE" ) )
|
||||
{
|
||||
print OUTFILE "0\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
warn( "Failed to create the incident count file at $INCIDENT_COUNT_FILE\n;" );
|
||||
$failed = 1;
|
||||
}
|
||||
close( OUTFILE );
|
||||
|
||||
return( undef ) if $failed;
|
||||
}
|
||||
|
||||
# If anything besides 0 or undef is passed in then this is true
|
||||
# If true then don't get a new incident number but rather return the current.
|
||||
if( open( RW_FILE, $INCIDENT_COUNT_FILE ) )
|
||||
{
|
||||
lock( *RW_FILE );
|
||||
my $cur_count = <RW_FILE>;
|
||||
chomp( $cur_count );
|
||||
if( $arg1 )
|
||||
{
|
||||
$ret_count = $cur_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( open( RW_FILE, ">$INCIDENT_COUNT_FILE" ) )
|
||||
{
|
||||
lock( *RW_FILE ) or print "FAILED TO RE-LOCK\n";
|
||||
$ret_count = $cur_count + 1;;
|
||||
print RW_FILE "$ret_count\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
warn( "Failed to reopen incident count file $INCIDENT_COUNT_FILE for wirtting.\n" );
|
||||
$failed = 1;
|
||||
}
|
||||
}
|
||||
unlock( *RW_FILE );
|
||||
close( RW_FILE );
|
||||
}
|
||||
else
|
||||
{
|
||||
warn( "Failed to open incident count file $INCIDENT_COUNT_FILE for reading.\n" );
|
||||
$failed = 1;
|
||||
}
|
||||
|
||||
return( $ret_count );
|
||||
}
|
||||
|
||||
sub lock
|
||||
{
|
||||
my $sub_name = 'lock';
|
||||
|
||||
my $fh = $_[0];
|
||||
|
||||
if( $USE_FLOCK )
|
||||
{
|
||||
flock( $fh, 2 );
|
||||
}
|
||||
return( 1 );
|
||||
}
|
||||
|
||||
sub unlock
|
||||
{
|
||||
my $sub_name = 'unlock';
|
||||
|
||||
my $fh = $_[0];
|
||||
|
||||
if( $USE_FLOCK )
|
||||
{
|
||||
flock( $fh, 8 );
|
||||
}
|
||||
|
||||
return( 1 );
|
||||
}
|
||||
|
||||
sub standard_deviation
|
||||
{
|
||||
my $sub_name = 'standard_deviation';
|
||||
|
||||
my $arg1 = $_[0]; # ref to array
|
||||
my $mean;
|
||||
my $dev_mean;
|
||||
my $ret_val;
|
||||
my $num_elements;
|
||||
my $sum;
|
||||
|
||||
if( ref( $arg1 ) eq 'ARRAY' )
|
||||
{
|
||||
my $i = 0;
|
||||
my $deviation_sum;
|
||||
$num_elements = scalar( @{$arg1} );
|
||||
$dev_mean = $arg1->[0] ** 2;
|
||||
for( $i = 1; $i > $num_elements; ++$i )
|
||||
{
|
||||
$sum += $arg1->[$i];
|
||||
$deviation_sum += $arg1->[$i] ** 2;
|
||||
}
|
||||
|
||||
$dev_mean = $deviation_sum / $num_elements;
|
||||
}
|
||||
elsif( ref( $arg1 ) eq 'HASH' )
|
||||
{
|
||||
my $deviation_sum;
|
||||
while( my( $num, $quan ) = each( %{$arg1} ) )
|
||||
{
|
||||
$sum += $num * $quan;
|
||||
$num_elements += $quan;
|
||||
$deviation_sum += ( $num ** 2 ) * $quan;
|
||||
}
|
||||
$dev_mean = $deviation_sum / $num_elements;
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
|
||||
# There should be a minimum of 5 (five) values to produce a valid result
|
||||
if( $num_elements < 5 )
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
|
||||
$mean = $sum / $num_elements;
|
||||
$ret_val = sqrt( $dev_mean - ( $mean ** 2 ) );
|
||||
return( $ret_val );
|
||||
}
|
||||
|
||||
sub mean_val
|
||||
{
|
||||
my $sub_name = 'mean_val';
|
||||
|
||||
my $arg1 = $_[0]; #ref to array
|
||||
my $array_count;
|
||||
my $sum = 0;
|
||||
my $ret_val;
|
||||
|
||||
if( ref( $arg1 ) ne 'ARRAY' )
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
|
||||
foreach my $num( @{$arg1} )
|
||||
{
|
||||
$sum += $num;
|
||||
++$array_count;
|
||||
}
|
||||
|
||||
if( $array_count > 0 )
|
||||
{
|
||||
$ret_val = $sum / $ret_val;
|
||||
return( $ret_val );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
sub tempfile
|
||||
{
|
||||
my $sub_name = 'tempfile';
|
||||
|
||||
my $action = shift || return( undef );;
|
||||
my @args = @_;
|
||||
|
||||
if( $action =~ m/^add$/i )
|
||||
{
|
||||
addtempfile( @args );
|
||||
}
|
||||
elsif( $action =~ m/^delete|remove$/i )
|
||||
{
|
||||
removetempfile( @args );
|
||||
}
|
||||
elsif( $action =~ m/^delete all|remove all$/i )
|
||||
{
|
||||
removealltempfiles();
|
||||
}
|
||||
else
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Unknown action of $action passed to function.\n" );
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
sub addtempfile
|
||||
{
|
||||
my $sub_name = 'addtempfile';
|
||||
|
||||
my $prefix = $_[0] || return( undef );
|
||||
my $force = $_[1] || 0;
|
||||
my $ret_file = "$TEMP_DIR/$prefix".$$.".tmp";
|
||||
|
||||
if( -f $ret_file )
|
||||
{
|
||||
if( ! $force )
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Temp file $ret_file already exists\n" );
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
if( open( OUTFILE, ">$ret_file" ) )
|
||||
{
|
||||
if( $DEBUG > 2 )
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Successfully created temp file $ret_file.\n" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Unable to open temp file $ret_file for writting.\n" );
|
||||
}
|
||||
|
||||
close( OUTFILE );
|
||||
|
||||
push( @TEMP_FILES, $ret_file );
|
||||
return( $ret_file );
|
||||
}
|
||||
|
||||
sub removetempfile
|
||||
{
|
||||
my $sub_name = 'removetempfile';
|
||||
|
||||
my @file_names = @_;
|
||||
my $num_removed = 0;
|
||||
my @new_array;
|
||||
|
||||
if( ! defined( $file_names[0] ) )
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
|
||||
foreach my $cur_file( @TEMP_FILES )
|
||||
{
|
||||
foreach my $file_to_remove( @file_names )
|
||||
{
|
||||
my $did_find = 0;
|
||||
if( $cur_file eq $file_to_remove )
|
||||
{
|
||||
if( unlink $file_to_remove )
|
||||
{
|
||||
++$num_removed;
|
||||
if( $DEBUG > 1 )
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Removed temp file $file_to_remove\n" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( $DEBUG > 0 )
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Failed to remove temp file $file_to_remove\n" );
|
||||
}
|
||||
}
|
||||
$did_find = 1;
|
||||
last;
|
||||
}
|
||||
|
||||
if( ! $did_find )
|
||||
{
|
||||
push( @new_array, $cur_file );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TEMP_FILES = @new_array;
|
||||
return( $num_removed );
|
||||
|
||||
}
|
||||
|
||||
sub removealltempfiles
|
||||
{
|
||||
my $sub_name = 'removealltempfiles';
|
||||
my $num_removed = 0;
|
||||
|
||||
foreach my $file_name( @TEMP_FILES )
|
||||
{
|
||||
if( unlink( $file_name ) )
|
||||
{
|
||||
++$num_removed;
|
||||
if( $DEBUG > 1 )
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Successfully deleted temp file $file_name\n" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( $DEBUG > 0 )
|
||||
{
|
||||
warn( __PACKAGE__ . "::$sub_name, Failed to delete temp file $file_name\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TEMP_FILES = ();
|
||||
return( $num_removed );
|
||||
}
|
||||
|
||||
sub setincidentcountfile
|
||||
{
|
||||
my $sub_name = 'setincidentcountfile';
|
||||
|
||||
my $brosite;
|
||||
use Bro::Config( '$BRO_CONFIG' );
|
||||
if($brosite = $BRO_CONFIG->{BROSITE} )
|
||||
{
|
||||
|
||||
|
||||
# Location of the file that holds the incident number counter
|
||||
$INCIDENT_COUNT_FILE = "$brosite/incident_counter";
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
warn( "No value for \$BROHOME has been set in the Bro config file. Nothing much works without it.\n" );
|
||||
return( undef );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
1;
|
2070
scripts/perl/lib/Bro/Report/Alarm.pm
Normal file
2070
scripts/perl/lib/Bro/Report/Alarm.pm
Normal file
File diff suppressed because it is too large
Load diff
770
scripts/perl/lib/Bro/Report/Conn.pm
Normal file
770
scripts/perl/lib/Bro/Report/Conn.pm
Normal file
|
@ -0,0 +1,770 @@
|
|||
package Bro::Report::Conn;
|
||||
|
||||
use strict;
|
||||
require 5.006_001;
|
||||
use Bro::Report qw( trimhostname iptoname swrite trimbytes );
|
||||
use Bro::Log::Conn;
|
||||
|
||||
use vars qw( $VERSION
|
||||
$MAX_LOCAL_SERVICE_USERS );
|
||||
|
||||
# $Id: Conn.pm 1418 2005-09-29 18:25:09Z tierney $
|
||||
$VERSION = 1.20;
|
||||
|
||||
$MAX_LOCAL_SERVICE_USERS = 50;
|
||||
|
||||
my %REPORT_MAP = ( 'top_sources' => { input => __PACKAGE__ . '::sourcecount',
|
||||
output => __PACKAGE__ . '::output_sourcecount' },
|
||||
'top_destinations' => { input => __PACKAGE__ . '::destcount',
|
||||
output => __PACKAGE__ . '::output_destcount' },
|
||||
'top_services' => { input => __PACKAGE__ . '::servicecount',
|
||||
output => __PACKAGE__ . '::output_servicecount', },
|
||||
'top_local_service_users' => { input => __PACKAGE__ . '::localserviceusers',
|
||||
output => __PACKAGE__ . '::output_localserviceusers', },
|
||||
'success_fail_stats' => { input => __PACKAGE__ . '::successfailcount',
|
||||
output => __PACKAGE__ . '::output_successfailcount', },
|
||||
'byte_transfer_pairs' => { input => __PACKAGE__ . '::bytetransferpairs',
|
||||
output => __PACKAGE__ . '::output_bytetransferpairs', },
|
||||
);
|
||||
|
||||
# Memory used in this variable will be deleted by functions which output
|
||||
# the values stored for it's respective counting function.
|
||||
my $RPT_CACHE;
|
||||
|
||||
sub sourcecount
|
||||
{
|
||||
my $sub_name = 'sourcecount';
|
||||
|
||||
# [0] CONN_COUNT
|
||||
# [1] BYTE_COUNT
|
||||
my $_conn_struc = $_[0] || return( undef );
|
||||
my $src_ip = Bro::Log::Conn::source_ip( $_conn_struc ) || return( undef );
|
||||
if( Bro::Log::Conn::connectsucceed( $_conn_struc ) )
|
||||
{
|
||||
my $bytes = Bro::Log::Conn::source_bytes( $_conn_struc );
|
||||
++$RPT_CACHE->{$sub_name}->{$src_ip}->[0];
|
||||
$RPT_CACHE->{$sub_name}->{$src_ip}->[1] += $bytes;
|
||||
return( 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
sub output_sourcecount
|
||||
{
|
||||
my $sub_name = 'output_sourcecount';
|
||||
|
||||
my $_max_output = $_[0] || 20;
|
||||
my $top_format = $_[1];
|
||||
my $format = $_[2];
|
||||
my $conn_sum = 0;
|
||||
my $cnt = 0;
|
||||
my $avg = 0;
|
||||
my $max_hostname_length = 31;
|
||||
my @results;
|
||||
my $ret_string;
|
||||
my @heading_names = ( 'Host', 'IP', 'Bytes', 'Conn. Count' );
|
||||
|
||||
if( ! $top_format )
|
||||
{
|
||||
$top_format = <<'END'
|
||||
@|||||||||||||||||||||||||||||| @|||||||||||||| @||||| @|||||||||||
|
||||
------------------------------- --------------- ------ ------------
|
||||
END
|
||||
}
|
||||
|
||||
if( ! $format )
|
||||
{
|
||||
$format = <<'END'
|
||||
@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> @<<<<<<<<<<<<<< @>>>>> @>>>>>>>>>>>
|
||||
END
|
||||
}
|
||||
|
||||
# Figure out what the average count is
|
||||
foreach my $count_struc( values( %{$RPT_CACHE->{sourcecount}} ) )
|
||||
{
|
||||
$conn_sum += $count_struc->[0];
|
||||
++$cnt
|
||||
}
|
||||
|
||||
# If there are no connection counts then bail
|
||||
if( $cnt < 1 )
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
|
||||
$avg = $conn_sum / $cnt;
|
||||
|
||||
# remove anything which is way too small before sorting
|
||||
my $smallest_count = 2;
|
||||
my $percent_of_avg = .1;
|
||||
my $max_sort_size = $_max_output * 2;
|
||||
while( ( $cnt > $max_sort_size ) and ( $percent_of_avg < .3 ) )
|
||||
{
|
||||
while( my( $ip, $struc ) = each( %{$RPT_CACHE->{sourcecount}} ) and $cnt > $max_sort_size )
|
||||
{
|
||||
if( $struc->[0] < $smallest_count )
|
||||
{
|
||||
delete( $RPT_CACHE->{sourcecount}->{$ip} );
|
||||
--$cnt;
|
||||
}
|
||||
$smallest_count = int( $avg * $percent_of_avg );
|
||||
}
|
||||
$percent_of_avg += .1;
|
||||
}
|
||||
|
||||
# Put the remaining data into a temp hash for sorting
|
||||
my %count_hash;
|
||||
foreach my $ip( keys( %{$RPT_CACHE->{sourcecount}} ) )
|
||||
{
|
||||
# connection count = $RPT_CACHE->{sourcecount}->{$ip}->[0];
|
||||
# byte count = $RPT_CACHE->{sourcecount}->{$ip}->[1];
|
||||
push( @{$count_hash{$RPT_CACHE->{sourcecount}->{$ip}->[0]}},
|
||||
[ $ip, $RPT_CACHE->{sourcecount}->{$ip}->[0], $RPT_CACHE->{sourcecount}->{$ip}->[1] ] );
|
||||
}
|
||||
|
||||
my $output_cnt = 0;
|
||||
foreach my $num_conn( sort { $b <=> $a } keys( %count_hash ) )
|
||||
{
|
||||
foreach my $struc( @{$count_hash{$num_conn}} )
|
||||
{
|
||||
++$output_cnt;
|
||||
if( $output_cnt > $_max_output )
|
||||
{
|
||||
last;
|
||||
}
|
||||
else
|
||||
{
|
||||
push( @results, $struc );
|
||||
}
|
||||
}
|
||||
if( $output_cnt > $_max_output )
|
||||
{
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
# clear out memory space
|
||||
delete( $RPT_CACHE->{sourcecount} );
|
||||
|
||||
# Set the heading
|
||||
$ret_string .= swrite( $top_format, @heading_names );
|
||||
|
||||
# Write the contents
|
||||
foreach my $line( @results )
|
||||
{
|
||||
my $ip = $line->[0];
|
||||
my $num_conn = $line->[1];
|
||||
my $num_bytes = trimbytes( $line->[2], 5 );
|
||||
my $name = trimhostname( iptoname( $ip ), $max_hostname_length, '>' );
|
||||
$ret_string .= swrite( $format, $name, $ip, $num_bytes, $num_conn );
|
||||
}
|
||||
|
||||
return( $ret_string );
|
||||
}
|
||||
|
||||
sub destcount
|
||||
{
|
||||
my $sub_name = 'destcount';
|
||||
|
||||
my $_conn_struc = $_[0] || return( undef );
|
||||
my $dst_ip = Bro::Log::Conn::destination_ip( $_conn_struc ) || return( undef );
|
||||
if( Bro::Log::Conn::connectsucceed( $_conn_struc ) )
|
||||
{
|
||||
my $bytes = Bro::Log::Conn::destination_bytes( $_conn_struc );
|
||||
++$RPT_CACHE->{$sub_name}->{$dst_ip}->[0];
|
||||
$RPT_CACHE->{$sub_name}->{$dst_ip}->[1] += $bytes;
|
||||
return( 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
sub output_destcount
|
||||
{
|
||||
my $sub_name = 'output_destcount';
|
||||
|
||||
my $_max_output = $_[0] || 20;
|
||||
my $top_format = $_[1];
|
||||
my $format = $_[2];
|
||||
my $conn_sum = 0;
|
||||
my $cnt = 0;
|
||||
my $avg = 0;
|
||||
my $max_hostname_length = 31;
|
||||
my @results;
|
||||
my $ret_string;
|
||||
my @heading_names = ( 'Host', 'IP', 'Bytes', 'Conn. Count' );
|
||||
|
||||
if( ! $top_format )
|
||||
{
|
||||
$top_format = <<'END'
|
||||
@|||||||||||||||||||||||||||||| @|||||||||||||| @||||| @|||||||||||
|
||||
------------------------------- --------------- ------ ------------
|
||||
END
|
||||
}
|
||||
|
||||
if( ! $format )
|
||||
{
|
||||
$format = <<'END'
|
||||
@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> @<<<<<<<<<<<<<< @>>>>> @>>>>>>>>>>>
|
||||
END
|
||||
}
|
||||
|
||||
# Figure out what the average count is
|
||||
foreach my $count_struc( values( %{$RPT_CACHE->{destcount}} ) )
|
||||
{
|
||||
$conn_sum += $count_struc->[0];
|
||||
++$cnt
|
||||
}
|
||||
|
||||
# If there are no connection counts then bail
|
||||
if( $cnt < 1 )
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
|
||||
$avg = $conn_sum / $cnt;
|
||||
|
||||
# remove anything which is way too small before sorting
|
||||
my $smallest_count = 2;
|
||||
my $percent_of_avg = .1;
|
||||
my $max_sort_size = $_max_output * 2;
|
||||
while( ( $cnt > $max_sort_size ) and ( $percent_of_avg < .3 ) )
|
||||
{
|
||||
while( my( $ip, $struc ) = each( %{$RPT_CACHE->{destcount}} ) and $cnt > $max_sort_size )
|
||||
{
|
||||
if( $struc->[0] < $smallest_count )
|
||||
{
|
||||
delete( $RPT_CACHE->{destcount}->{$ip} );
|
||||
--$cnt;
|
||||
}
|
||||
$smallest_count = int( $avg * $percent_of_avg );
|
||||
}
|
||||
$percent_of_avg += .1;
|
||||
}
|
||||
|
||||
# Put the remaining data into a temp hash for sorting
|
||||
my %count_hash;
|
||||
foreach my $ip( keys( %{$RPT_CACHE->{destcount}} ) )
|
||||
{
|
||||
# connection count = $RPT_CACHE->{destcount}->{$ip}->{CONN_COUNT};
|
||||
# byte count = $RPT_CACHE->{destcount}->{$ip}->{BYTE_COUNT};
|
||||
push( @{$count_hash{$RPT_CACHE->{destcount}->{$ip}->[0]}},
|
||||
[ $ip, $RPT_CACHE->{destcount}->{$ip}->[0], $RPT_CACHE->{destcount}->{$ip}->[1] ] );
|
||||
}
|
||||
|
||||
my $output_cnt = 0;
|
||||
foreach my $num_conn( sort { $b <=> $a } keys( %count_hash ) )
|
||||
{
|
||||
foreach my $struc( @{$count_hash{$num_conn}} )
|
||||
{
|
||||
++$output_cnt;
|
||||
if( $output_cnt > $_max_output )
|
||||
{
|
||||
last;
|
||||
}
|
||||
else
|
||||
{
|
||||
push( @results, $struc );
|
||||
}
|
||||
}
|
||||
if( $output_cnt > $_max_output )
|
||||
{
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
# clear out memory space
|
||||
delete( $RPT_CACHE->{destcount} );
|
||||
|
||||
# Set the heading
|
||||
$ret_string .= swrite( $top_format, @heading_names );
|
||||
|
||||
# Write the contents
|
||||
foreach my $line( @results )
|
||||
{
|
||||
my $ip = $line->[0];
|
||||
my $num_conn = $line->[1];
|
||||
my $num_bytes = trimbytes( $line->[2], 5 );
|
||||
my $name = trimhostname( iptoname( $ip ), $max_hostname_length, '>' );
|
||||
$ret_string .= swrite( $format, $name, $ip, $num_bytes, $num_conn );
|
||||
}
|
||||
|
||||
return( $ret_string );
|
||||
}
|
||||
|
||||
sub servicecount
|
||||
{
|
||||
my $sub_name = 'servicecount';
|
||||
|
||||
# [0] CONN_COUNT
|
||||
# [1] BYTES_IN
|
||||
# [2] BYTES_OUT
|
||||
|
||||
my $_conn_struc = $_[0] || return( undef );
|
||||
my $service = Bro::Log::Conn::service( $_conn_struc ) || return( undef );
|
||||
if( Bro::Log::Conn::connectsucceed( $_conn_struc ) )
|
||||
{
|
||||
my $src_bytes = Bro::Log::Conn::source_bytes( $_conn_struc );
|
||||
my $dest_bytes = Bro::Log::Conn::destination_bytes( $_conn_struc );
|
||||
++$RPT_CACHE->{$sub_name}->{$service}->[0];
|
||||
if( Bro::Log::Conn::source_network( $_conn_struc ) eq 'L' )
|
||||
{
|
||||
$RPT_CACHE->{$sub_name}->{$service}->[1] += $dest_bytes;
|
||||
$RPT_CACHE->{$sub_name}->{$service}->[2] += $src_bytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
$RPT_CACHE->{$sub_name}->{$service}->[1] += $src_bytes;
|
||||
$RPT_CACHE->{$sub_name}->{$service}->[2] += $dest_bytes;
|
||||
}
|
||||
return( 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
sub output_servicecount
|
||||
{
|
||||
my $sub_name = 'output_servicecount';
|
||||
|
||||
my $_max_output_count = $_[0] || 20;
|
||||
my $top_format;
|
||||
my $format;
|
||||
my @results;
|
||||
my @heading_names = ( 'Service', 'Conn. Count', '% of Total', 'Bytes In', 'Bytes Out' );
|
||||
my $ret_string;
|
||||
|
||||
if( ! $top_format )
|
||||
{
|
||||
$top_format = <<'END'
|
||||
@<<<<<<<<<<< @>>>>>>>>>>> @>>>>>>>>> @>>>>>>>> @>>>>>>>>
|
||||
------------ ------------ ---------- --------- ---------
|
||||
END
|
||||
}
|
||||
|
||||
if( ! $format )
|
||||
{
|
||||
$format = <<'END'
|
||||
@<<<<<<<<<<< @>>>>>>>>>>> @>>>>>>>>> @>>>>>>>> @>>>>>>>>
|
||||
END
|
||||
}
|
||||
|
||||
my %count_hash;
|
||||
my $total_count = 0;
|
||||
while( my( $name, $struc ) = each( %{$RPT_CACHE->{servicecount}} ) )
|
||||
{
|
||||
$total_count += $struc->[0];
|
||||
push( @{$count_hash{$struc->[0]}},
|
||||
[ $name, $struc->[1], $struc->[2] ] );
|
||||
}
|
||||
|
||||
my $ret_count = 0;
|
||||
foreach my $num( sort { $b <=> $a } keys( %count_hash ) )
|
||||
{
|
||||
if( $ret_count < $_max_output_count )
|
||||
{
|
||||
foreach my $struc( @{$count_hash{$num}} )
|
||||
{
|
||||
if( $ret_count < $_max_output_count )
|
||||
{
|
||||
my $avg_of_total = sprintf( "%.2f", $num / $total_count * 100 );
|
||||
my $service = $struc->[0];
|
||||
my $bytes_in = trimbytes( $struc->[1], 5 );
|
||||
my $bytes_out = trimbytes( $struc->[2], 5 );
|
||||
push( @results, [ $service, $num, $avg_of_total, $bytes_in, $bytes_out ] );
|
||||
++$ret_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
# Clean up some memory
|
||||
delete( $RPT_CACHE->{servicecount} );
|
||||
|
||||
# Print the heading
|
||||
$ret_string .= swrite( $top_format, @heading_names );
|
||||
|
||||
foreach my $line( @results )
|
||||
{
|
||||
$ret_string .= swrite( $format, @{$line} );
|
||||
}
|
||||
|
||||
return( $ret_string );
|
||||
}
|
||||
|
||||
sub localserviceusers
|
||||
{
|
||||
my $sub_name = 'localserviceusers';
|
||||
|
||||
my $_conn_struc = $_[0] || return( undef );
|
||||
my $service_name = $_[1] || 'smtp';
|
||||
|
||||
my $service = Bro::Log::Conn::service( $_conn_struc );
|
||||
|
||||
if( $service eq $service_name )
|
||||
{
|
||||
my $src_net = Bro::Log::Conn::source_network( $_conn_struc );
|
||||
|
||||
if( $src_net eq 'L' and Bro::Log::Conn::connectsucceed( $_conn_struc ) )
|
||||
{
|
||||
my $source_ip = Bro::Log::Conn::source_ip( $_conn_struc );
|
||||
++$RPT_CACHE->{$sub_name}->{$service_name}->{$source_ip};
|
||||
}
|
||||
}
|
||||
|
||||
return( 1 );
|
||||
}
|
||||
|
||||
sub output_localserviceusers
|
||||
{
|
||||
my $sub_name = 'output_localserviceusers';
|
||||
|
||||
my $service_name = $_[0] || return( undef );
|
||||
my $max_count = $_[1] || $MAX_LOCAL_SERVICE_USERS;
|
||||
my $top_format;
|
||||
my $format;
|
||||
my @results;
|
||||
my $ret_string;
|
||||
my @heading_names = ( 'Hostname', 'IP', 'Conn. Count' );
|
||||
my $total_count = keys( %{$RPT_CACHE->{localserviceusers}->{$service_name}} );
|
||||
my $max_hostname_length = 39;
|
||||
my $actual_count = 0;
|
||||
|
||||
if( ! $top_format )
|
||||
{
|
||||
$top_format = <<'END'
|
||||
@|||||||||||||||||||||||||||||||||||||| @|||||||||||||| @>>>>>>>>>>>
|
||||
--------------------------------------- --------------- ------------
|
||||
END
|
||||
}
|
||||
|
||||
if( ! $format )
|
||||
{
|
||||
$format = <<'END'
|
||||
@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> @<<<<<<<<<<<<<< @>>>>>>>>>>>
|
||||
END
|
||||
}
|
||||
|
||||
my %count_hash;
|
||||
while( my( $key, $val ) = each( %{$RPT_CACHE->{localserviceusers}->{$service_name}} ) )
|
||||
{
|
||||
push( @{$count_hash{$val}}, $key );
|
||||
}
|
||||
|
||||
foreach my $num( sort { $b <=> $a } keys( %count_hash ) )
|
||||
{
|
||||
foreach my $ip( @{$count_hash{$num}} )
|
||||
{
|
||||
if( $actual_count + 1 > $max_count )
|
||||
{
|
||||
last;
|
||||
}
|
||||
$results[$actual_count] = [ $ip, $num ];
|
||||
++$actual_count;
|
||||
}
|
||||
}
|
||||
|
||||
# Clean up some memory usage
|
||||
delete( $RPT_CACHE->{localserviceusers}->{$service_name} );
|
||||
|
||||
# Set the heading
|
||||
$ret_string .= swrite( $top_format, @heading_names );
|
||||
|
||||
# Write the contents
|
||||
foreach my $line( @results )
|
||||
{
|
||||
# my $ip = $line->[0];
|
||||
# my $num_conn = $line->[1];
|
||||
my $name = trimhostname( iptoname( $line->[0] ), $max_hostname_length, '>' );
|
||||
$ret_string .= swrite( $format, $name, $line->[0], $line->[1] );
|
||||
}
|
||||
|
||||
if( $actual_count > 0 )
|
||||
{
|
||||
if( $total_count > $max_count )
|
||||
{
|
||||
my $not_listed = $total_count - $max_count;
|
||||
$ret_string .= <<"END";
|
||||
|
||||
A maximum of $max_count entries are show.
|
||||
There are another $not_listed that are not displayed.
|
||||
END
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret_string = "\n No data to report for this section\n";
|
||||
}
|
||||
|
||||
return( $ret_string );
|
||||
}
|
||||
|
||||
sub successfailcount
|
||||
{
|
||||
my $sub_name = 'successfailcount';
|
||||
|
||||
my $_conn_struc = $_[0] || return( undef );
|
||||
|
||||
if( Bro::Log::Conn::connectsucceed( $_conn_struc ) )
|
||||
{
|
||||
++$RPT_CACHE->{$sub_name}->{SUCCESS};
|
||||
}
|
||||
else
|
||||
{
|
||||
# connection is failed
|
||||
++$RPT_CACHE->{$sub_name}->{FAIL};
|
||||
}
|
||||
}
|
||||
|
||||
sub output_successfailcount
|
||||
{
|
||||
my $sub_name = 'output_successfailcount';
|
||||
|
||||
my $format = $_[0];
|
||||
my $ret_string;
|
||||
|
||||
if( ! $format )
|
||||
{
|
||||
$format = <<'END'
|
||||
Successful: @<<<<<<<<<<<<<<<
|
||||
Unsuccessful: @<<<<<<<<<<<<<<<
|
||||
Ratio: @<<<<<<
|
||||
END
|
||||
}
|
||||
|
||||
# Success and fail counts must be greater than zero
|
||||
if( $RPT_CACHE->{successfailcount}->{FAIL} < 1 or
|
||||
$RPT_CACHE->{successfailcount}->{SUCCESS} < 1 )
|
||||
{
|
||||
return( 'undef' );
|
||||
}
|
||||
my $ratio = $RPT_CACHE->{successfailcount}->{FAIL} / $RPT_CACHE->{successfailcount}->{SUCCESS};
|
||||
|
||||
$ret_string = swrite( $format,
|
||||
$RPT_CACHE->{successfailcount}->{SUCCESS},
|
||||
$RPT_CACHE->{successfailcount}->{FAIL},
|
||||
"1:$ratio" );
|
||||
|
||||
return( $ret_string );
|
||||
}
|
||||
|
||||
sub bytetransferpairs
|
||||
{
|
||||
my $sub_name = 'bytetransferpairs';
|
||||
|
||||
# This report can be very memory expensive. It can also be very processor
|
||||
# intesive as the hash tables can get very large and take longer and
|
||||
# longer to traverse.
|
||||
|
||||
my $conn_struc = $_[0] || return( undef );
|
||||
|
||||
my $local_host;
|
||||
my $remote_host;
|
||||
my $local_bytes;
|
||||
my $remote_bytes;
|
||||
|
||||
if( Bro::Log::Conn::source_network( $conn_struc ) eq 'L' )
|
||||
{
|
||||
$local_host = Bro::Log::Conn::source_ip( $conn_struc );
|
||||
$remote_host = Bro::Log::Conn::destination_ip( $conn_struc );
|
||||
$local_bytes = Bro::Log::Conn::source_bytes( $conn_struc );
|
||||
$remote_bytes = Bro::Log::Conn::destination_bytes( $conn_struc );
|
||||
}
|
||||
else
|
||||
{
|
||||
$remote_host = Bro::Log::Conn::source_ip( $conn_struc );
|
||||
$local_host = Bro::Log::Conn::destination_ip( $conn_struc );
|
||||
$remote_bytes = Bro::Log::Conn::source_bytes( $conn_struc );
|
||||
$local_bytes = Bro::Log::Conn::destination_bytes( $conn_struc );
|
||||
}
|
||||
|
||||
if( $local_bytes > 0 and $remote_bytes > 0 )
|
||||
{
|
||||
$RPT_CACHE->{bytetransferpairs}->{$local_host}->{$remote_host}->{LOCAL_BYTES} += $local_bytes;
|
||||
$RPT_CACHE->{bytetransferpairs}->{$local_host}->{$remote_host}->{REMOTE_BYTES} += $remote_bytes;
|
||||
++$RPT_CACHE->{bytetransferpairs}->{$local_host}->{$remote_host}->{CONN_COUNT};
|
||||
return( 1 );
|
||||
}
|
||||
elsif( exists( $RPT_CACHE->{bytetransferpairs}->{$local_host} ) and
|
||||
exists( $RPT_CACHE->{bytetransferpairs}->{$local_host}->{$remote_host} ) )
|
||||
{
|
||||
$RPT_CACHE->{bytetransferpairs}->{$local_host}->{$remote_host}->{LOCAL_BYTES} += $local_bytes || 0;
|
||||
$RPT_CACHE->{bytetransferpairs}->{$local_host}->{$remote_host}->{REMOTE_BYTES} += $remote_bytes || 0;
|
||||
++$RPT_CACHE->{bytetransferpairs}->{$local_host}->{$remote_host}->{CONN_COUNT};
|
||||
return( 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
sub output_bytetransferpairs
|
||||
{
|
||||
my $sub_name = 'output_bytetransferpairs';
|
||||
my $max_hostname_length = 22;
|
||||
|
||||
my $max_output = $_[0] || 20;
|
||||
|
||||
my $ret_string;
|
||||
my $_base = $RPT_CACHE->{bytetransferpairs};
|
||||
my %reversed_hash;
|
||||
my @ordered_list;
|
||||
my $top_format;
|
||||
my $format;
|
||||
|
||||
$top_format = <<"END";
|
||||
Hot Report - Top $max_output
|
||||
Local Remote Conn.
|
||||
Local Host Remote Host Bytes Bytes Count
|
||||
----------------------- ----------------------- --------- --------- -------
|
||||
END
|
||||
|
||||
$format = <<'END';
|
||||
@<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<< @>>>>>>>> @>>>>>>>> @<<<<<<<<
|
||||
END
|
||||
|
||||
foreach my $l_host( keys( %{$_base} ) )
|
||||
{
|
||||
foreach my $r_host( keys( %{$_base->{$l_host}} ) )
|
||||
{
|
||||
my $big_bytes;
|
||||
if( $_base->{$l_host}->{$r_host}->{LOCAL_BYTES} > $_base->{$l_host}->{$r_host}->{REMOTE_BYTES} )
|
||||
{
|
||||
$big_bytes = $_base->{$l_host}->{$r_host}->{LOCAL_BYTES};
|
||||
}
|
||||
else
|
||||
{
|
||||
$big_bytes = $_base->{$l_host}->{$r_host}->{REMOTE_BYTES};
|
||||
}
|
||||
|
||||
push( @{$reversed_hash{$big_bytes}}, { REF => $_base->{$l_host}->{$r_host},
|
||||
LOCAL_HOST => $l_host,
|
||||
REMOTE_HOST => $r_host, } );
|
||||
}
|
||||
}
|
||||
|
||||
my @ordered_list = sort( { $b<=>$a } keys( %reversed_hash ) );
|
||||
|
||||
my $i = 0;
|
||||
while( defined( my $key = shift( @ordered_list ) ) and $i < $max_output )
|
||||
{
|
||||
foreach my $data( @{$reversed_hash{$key}} )
|
||||
{
|
||||
my $local_bytes = trimbytes( $data->{REF}->{LOCAL_BYTES}, 6 );
|
||||
my $remote_bytes = trimbytes( $data->{REF}->{REMOTE_BYTES}, 6 );
|
||||
my $conn_count = $data->{REF}->{CONN_COUNT};
|
||||
my $local_name = trimhostname( iptoname( $data->{LOCAL_HOST} ), $max_hostname_length, '>' );
|
||||
my $remote_name = trimhostname( iptoname( $data->{REMOTE_HOST} ), $max_hostname_length, '>' );
|
||||
|
||||
$ret_string .= swrite( $format,
|
||||
$local_name,
|
||||
$remote_name,
|
||||
$local_bytes,
|
||||
$remote_bytes,
|
||||
$conn_count );
|
||||
|
||||
++$i;
|
||||
if( !( $i < $max_output ) )
|
||||
{
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Free up some memory
|
||||
$_base = undef;
|
||||
%reversed_hash = ();
|
||||
delete( $RPT_CACHE->{bytetransferpairs} );
|
||||
|
||||
if( length( $ret_string ) < 32 )
|
||||
{
|
||||
$ret_string = $top_format . " No data to report\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret_string = $top_format . $ret_string . "\n";
|
||||
}
|
||||
|
||||
return( $ret_string );
|
||||
}
|
||||
|
||||
sub output_successcount
|
||||
{
|
||||
my $sub_name = 'output_successcount';
|
||||
my $ret_val = $RPT_CACHE->{successfailcount}->{SUCCESS};
|
||||
|
||||
# Clean up some memory
|
||||
delete( $RPT_CACHE->{successfailcount}->{SUCCESS} );
|
||||
|
||||
return( $ret_val );
|
||||
}
|
||||
|
||||
sub output_failcount
|
||||
{
|
||||
my $sub_name = 'output_failcount';
|
||||
my $ret_val = $RPT_CACHE->{successfailcount}->{FAIL};
|
||||
|
||||
# Clean up some memory
|
||||
delete( $RPT_CACHE->{successfailcount}->{FAIL} );
|
||||
|
||||
return( $ret_val );
|
||||
}
|
||||
|
||||
sub availablereports
|
||||
{
|
||||
my $sub_name = 'availablereports';
|
||||
|
||||
my @ret_list = keys( %REPORT_MAP );
|
||||
|
||||
return( @ret_list );
|
||||
}
|
||||
|
||||
sub reportinputfunc
|
||||
{
|
||||
my $sub_name = 'reportinputfunc';
|
||||
|
||||
my $report_name = $_[0] || return( undef );
|
||||
|
||||
if( exists( $REPORT_MAP{$report_name} ) )
|
||||
{
|
||||
return( $REPORT_MAP{$report_name}->{'input'} );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
sub reportoutputfunc
|
||||
{
|
||||
my $sub_name = 'reportoutputfunc';
|
||||
|
||||
my $report_name = $_[0] || return( undef );
|
||||
|
||||
if( exists( $REPORT_MAP{$report_name} ) )
|
||||
{
|
||||
return( $REPORT_MAP{$report_name}->{'output'} );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( undef );
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
1316
scripts/perl/lib/Bro/Signature.pm
Normal file
1316
scripts/perl/lib/Bro/Signature.pm
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue