Last Updated or created 2023-01-04
See also 2nd Firewall 2013 and Led Firewall
https://www.henriaanstoot.nl/2013/08/03/ohm-2013-hackerevent/
While partying @ HAL2001, a hackers event, Venom and I made a Soundfirewall.
We had a little DMZ, with our servers.
This was protected by a iptables firewall.
Our idea was to get a sound notification on every (interesting) network packet the firewall dropped.
So we made this: (At the bottom are the sound definitions.)
Example of the sound we heared whole day.
#use strict;
# @(#) First Edit: Bas
# @(#) Last Edit: Fash
use POSIX ":sys_wait_h";
use vars qw(%Msg_Rec);
$SIG{'TERM'} = $SIG{'HUP'} = 'goodbye';
$SIG{'CHLD'} = 'IGNORE';
## Constants
my $BELL = "";
my $MAILER = "/usr/sbin/sendmail";
my $WRITE = "/usr/bin/write";
$/ = "
";
autoflush STDOUT;
sub goodbye {
$| = 0;
close_pipe_if_open();
exit(0);
}
#
# in_range($range, $number)
# returns 1 if $number is inside $range, 0 if not
#
sub in_range {
my $range = shift;
my $num = shift;
foreach my $f (split(/,/, $range)) {
if ($f =~ /-/) {
my ($low,$high) = split(/-/, $f);
return 1 if ($low <= $num and $num <= $high);
} elsif ($f == $num) {
return 1;
}
}
return 0;
}
#
# inside_time_window($days,$hours)
# returns 1 if inside window, 0 if outside window
#
sub inside_time_window {
my $range = shift;
my($days, $hours) = split(/:/, $range);
my ($hr, $wday) = (localtime(time))[2,6];
if (($days eq '*' or in_range($days, $wday))
and ($hours eq '*' or in_range($hours, $hr))) {
return 1;
} else {
return 0;
}
}
print "\n*** swatch-3.0.1 (pid:6826) started at " . `/bin/date` . "\n";
use Date::Calc qw(:all);
sub parse_dot {
my $message = shift;
my $dot_loc = shift;
my @dot = ();
my @ranges = split(/:/, $dot_loc);
foreach my $range (0..$#ranges) {
if ($ranges[$range] != -1) {
my ($begin, $end) = split(/-/, $ranges[$range]);
$dot[$range] = substr($message, $begin, ($end - $begin + 1));
}
}
return @dot;
}
my ($date_loc, $time_loc) = ("-1:0-2:4-5", "7-8:10-11:13-14");
my %months = (
Jan => 1,
Feb => 2,
Mar => 3,
Apr => 4,
May => 5,
Jun => 6,
Jul => 7,
Aug => 8,
Sep => 9,
Oct => 10,
Nov => 11,
Dec => 12
);
# Returns an array of year, month, day, hours, minutes, and seconds.
#
sub YMDHMS {
my $string = shift;
my $year_today = (Today())[0];
my ($y, $m, $d) = parse_dot($string, $date_loc);
my ($hrs, $mins, $secs) = parse_dot($string, $time_loc);
if (length($y) eq 0) { $y = (Today())[0] };
return ($y, $months{$m}, $d, $hrs, $mins, $secs);
}
sub new_msg {
my $use = shift;
my $msg = shift;
my $count = shift;
my @delta = @_;
my $delta;
if ($delta[0] == 0) {
$delta = sprintf("%d:%.2d:%.2d", $delta[1], $delta[2], $delta[3]);
} else {
$delta = sprintf("$delta[0] day%s %d:%.2d:%.2d", $delta[0] > 1 ? 's' : '',
$delta[1], $delta[2], $delta[3]);
}
if ($use eq 'regex') {
return "$count $msg regular expressions in $delta";
} else {
return "$count in $delta: $msg";
}
}
#
# Stores message information in
# $Msg_Rec = (
# {<truncated message>|<pattern>} => {
# dhms => [ array ], # days,hours,minutes,seconds
# count => integer,
sub throttle {
my %opts = (
KEY => $_,
CUT_MARKS => [ "0:16" ], # not used yet
USE => 'message',
@_
);
my $msg = $opts{'KEY'};
my $use = $opts{'USE'};
my @ymdhms = YMDHMS($msg);
my $key;
my @min_dhms_delta = split(/(\s+|:)/, $opts{'MIN_DELTA'});
foreach my $i (0..$#min_dhms_delta) {
# strip out unwanted element
splice (@min_dhms_delta, $i, 1) if ($min_dhms_delta[$i] eq ":");
}
if ($use eq 'regex') {
$key = $opts{'REGEX'};
} else {
$key = substr($msg, 16);
$key =~ s/\[\d+\]/[PID]/;
}
while ($#min_dhms_delta < 3) {
unshift(@min_dhms_delta, 0); # make sure that the dhms array is full
}
if (exists $Msg_Rec{$key} and defined $Msg_Rec{$key}->{ymdhms}) {
my $passed = 1;
$Msg_Rec{$key}->{count}++;
if ($ymdhms[1] > $Msg_Rec{$key}->{ymdhms}[1]) { $ymdhms[0]--; }
my @delta_dhms = Delta_DHMS(@{$Msg_Rec{$key}->{ymdhms}}, @ymdhms);
foreach my $i (0..$#min_dhms_delta) {
$passed = 0 if ($delta_dhms[$i] < $min_dhms_delta[$i]);
last unless ($delta_dhms[$i] == $min_dhms_delta[$i]);
}
if ($passed) {
my $new = '';
$new = new_msg($use, $key, $Msg_Rec{$key}->{count}, @delta_dhms);
$Msg_Rec{$key}->{ymdhms} = [ @ymdhms ];
$Msg_Rec{$key}->{count} = 1;
return $new;
} else {
return '';
}
} else {
my $rec;
$rec->{ymdhms} = [ @ymdhms ];
$Msg_Rec{$key} = $rec;
return $msg;
}
}
##
## ACTION SUBROUTINES
##
my %text_modes = (
"black" => "\033[30;1m",
"red" => "\033[31;1m",
"green" => "\033[32;1m",
"yellow" => "\033[33;1m",
"blue" => "\033[34;1m",
"magenta" => "\033[35;1m",
"cyan" => "\033[36;1m",
"white" => "\033[37;1m",
"black_h" => "\033[40;1m",
"red_h" => "\033[41;1m",
"green_h" => "\033[42;1m",
"yellow_h" => "\033[43;1m",
"blue_h" => "\033[44;1m",
"magenta_h" => "\033[45;1m",
"cyan_h" => "\033[46;1m",
"white_h" => "\033[47;1m",
"bold" => "\033[1m",
"blink" => "\033[5m",
"inverse" => "\033[7m",
"normal" => "\033[0m",
"underscore" => "\033[4m",
);
sub echo {
my %args = (
'MODES' => [ 'normal' ],
@_
);
return if (exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'}));
if ($args{'MODES'}[0] eq 'random') {
my @mode_names = keys %text_modes;
print $text_modes{$mode_names[rand $#mode_names]};
} else {
foreach my $mode (@{$args{'MODES'}}) {
print $text_modes{$mode};
}
}
print $args{'MESSAGE'};
print $text_modes{'normal'};
print "\n";
}
#
# ring_bell(args) -- send x number of control-G characters to the output.
#
sub ring_bell {
my %args = (
'RINGS' => 1,
@_
);
my $sun_terminal = (`uname -s` eq 'SunOS\n');
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
my $bells = $args{'RINGS'};
for ( ; $bells > 0 ; $bells-- ) {
print $BELL;
sleep 1 if $sun_terminal; # SunOS needed this. Not sure about Solaris though
}
}
#
# exec_command(args) -- fork and execute a command
#
sub exec_command {
my %args = (@_);
my $exec_pid;
my $command;
if (exists $args{'COMMAND'}) {
$command = $args{'COMMAND'};
} else {
warn "$0: No command was specified in exec action.\n";
return;
}
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
EXECFORK: {
if ($exec_pid = fork) {
waitpid(-1, WNOHANG);
return;
} elsif (defined $exec_pid) {
exec($command);
} elsif ($! =~ /No more processes/) {
# EAGAIN, supposedly recoverable fork error
sleep 5;
redo EXECFORK;
} else {
warn "$0: Can't fork to exec $command: $!\n";
}
}
return;
}
{
my $pipe_is_open;
my $current_command_name;
#
# send_message_to_pipe -- send text to a pipe.
#
# usage: &send_message_to_pipe($program_to_pipe_to_including_the_vertical_bar_symbol,
# $message_to_send_to_the_pipe);
#
sub send_message_to_pipe {
my %args = (@_);
my $command;
if (exists $args{'COMMAND'}) {
$command = $args{'COMMAND'};
} else {
warn "$0: No command was specified in pipe action.\n";
return;
}
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
# open a new pipe if necessary
if ( !$pipe_is_open or $current_command_name ne $command ) {
# first close an open pipe
close(PIPE) if $pipe_is_open;
$pipe_is_open = 0;
open(PIPE, "| $command")
or warn "$0: cannot open pipe to $command: $!\n" && return;
PIPE->autoflush(1);
$pipe_is_open = 1;
$current_command_name = $command;
}
# send the text
print PIPE "$args{'MESSAGE'}";
if (not exists $args{'KEEP_OPEN'}) {
close(PIPE) if $pipe_is_open;
$pipe_is_open = 0;
}
}
#
# close_pipe_if_open -- used at the end of a script to close a pipe
# opened by &pipe_it().
#
# usage: &close_pipe_if_open();
#
sub close_pipe_if_open {
if ($pipe_is_open) {
close(PIPE);
}
}
}
#
# send_email -- send some mail using $MAILER.
#
# usage: &send_email($addresses_to_mail_to);
#
sub send_email {
my $login = (getpwuid($<))[0];
my %args = (
'ADDRESSES' => $login,
'SUBJECT' => 'Message from Swatch',
@_
);
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
my $addresses = $args{'ADDRESSES'};
$addresses =~ s/:/,/g;
if ($MAILER eq '') {
warn "ERROR: $0 cannot find a mail delivery program\n";
return;
}
open(MAIL, "| $MAILER $addresses")
or warn "$0: cannot open pipe to $MAILER: $!\n" && return;
print MAIL "To: $addresses\n";
print MAIL "Subject: $args{SUBJECT}\n\n";
print MAIL "$args{'MESSAGE'}\n";
close(MAIL);
}
#
# write_message -- use $WRITE to send a message logged on users.
#
sub write_message {
my %args = (@_);
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
if ($WRITE eq '') {
warn "ERROR: $0 cannot find the write(1) program\n";
return;
}
if (exists($args{'USERS'})) {
foreach my $user (split(/:/, $args{'USERS'})) {
send_message_to_pipe(COMMAND => "$WRITE $user 2>/dev/null",
MESSAGE => "$args{'MESSAGE'}\n");
}
}
}
use File::Tail;
my $Filename = '/var/log/ulog/syslogemu.log';
my $File = File::Tail->new(name=>$Filename, maxinterval => 1, interval => 1);
if (not defined $File) {
die "/usr/local/bin/swatch: cannot read input \"$Filename\": $!\n";
}
LOOP: while (defined($_=$File->read)) {
chomp;
my $sanitized_ = $_;
@_ = split;
# quote all special shell chars
$sanitized_ =~ s/([;&\(\)\|\^><\$`'\\])/\\$1/g;
my @sanitized_ = split(/\s+/, $sanitized_);
if (/INVALID|REPEATED|INCOMPLETE:LOGIN/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
ring_bell('RINGS' => "3", );
next;
}
if (/(panic|halt)/) {
echo('MESSAGE' => "$_", );
ring_bell();
next;
}
if (/Regents/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
ring_bell();
next;
}
if (/ipfw:.*Deny ICMP:8.0/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 -q /root/wavs/drip.wav &", );
next;
}
if (/ipfw:.*Deny TCP .*:6000/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 -q /root/wavs/camera.wav &", );
next;
}
if (/ipfw:.*Deny UDP .*:513/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 -q /root/wavs/flush.wav &", );
next;
}
if (/ipfw:.*Deny TCP .*:21/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 -q /root/wavs/vault.wav &", );
next;
}
if (/PROTO=TCP .*DPT=80/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 /root/wavs/tcp80.mp3 &", );
next;
}
if (/PROTO=TCP .*DPT=23/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 /root/wavs/tcp23.mp3 &", );
next;
}
if (/UDP .*=1300007/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 /root/wavs/udp137.mp3 &", );
next;
}
if (/PROTO=ICMP/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 /root/wavs/ping.mp3 &", );
next;
}
if (/.*/) {
echo('MESSAGE' => "$_", );
next;
}
}
#use strict;
# @(#) First Edit: Bas
# @(#) Last Edit: Fash
use POSIX ":sys_wait_h";
use vars qw(%Msg_Rec);
$SIG{'TERM'} = $SIG{'HUP'} = 'goodbye';
$SIG{'CHLD'} = 'IGNORE';
## Constants
my $BELL = "";
my $MAILER = "/usr/sbin/sendmail";
my $WRITE = "/usr/bin/write";
$/ = "
";
autoflush STDOUT;
sub goodbye {
$| = 0;
close_pipe_if_open();
exit(0);
}
#
# in_range($range, $number)
# returns 1 if $number is inside $range, 0 if not
#
sub in_range {
my $range = shift;
my $num = shift;
foreach my $f (split(/,/, $range)) {
if ($f =~ /-/) {
my ($low,$high) = split(/-/, $f);
return 1 if ($low <= $num and $num <= $high);
} elsif ($f == $num) {
return 1;
}
}
return 0;
}
#
# inside_time_window($days,$hours)
# returns 1 if inside window, 0 if outside window
#
sub inside_time_window {
my $range = shift;
my($days, $hours) = split(/:/, $range);
my ($hr, $wday) = (localtime(time))[2,6];
if (($days eq '*' or in_range($days, $wday))
and ($hours eq '*' or in_range($hours, $hr))) {
return 1;
} else {
return 0;
}
}
print "\n*** swatch-3.0.1 (pid:6826) started at " . `/bin/date` . "\n";
use Date::Calc qw(:all);
sub parse_dot {
my $message = shift;
my $dot_loc = shift;
my @dot = ();
my @ranges = split(/:/, $dot_loc);
foreach my $range (0..$#ranges) {
if ($ranges[$range] != -1) {
my ($begin, $end) = split(/-/, $ranges[$range]);
$dot[$range] = substr($message, $begin, ($end - $begin + 1));
}
}
return @dot;
}
my ($date_loc, $time_loc) = ("-1:0-2:4-5", "7-8:10-11:13-14");
my %months = (
Jan => 1,
Feb => 2,
Mar => 3,
Apr => 4,
May => 5,
Jun => 6,
Jul => 7,
Aug => 8,
Sep => 9,
Oct => 10,
Nov => 11,
Dec => 12
);
# Returns an array of year, month, day, hours, minutes, and seconds.
#
sub YMDHMS {
my $string = shift;
my $year_today = (Today())[0];
my ($y, $m, $d) = parse_dot($string, $date_loc);
my ($hrs, $mins, $secs) = parse_dot($string, $time_loc);
if (length($y) eq 0) { $y = (Today())[0] };
return ($y, $months{$m}, $d, $hrs, $mins, $secs);
}
sub new_msg {
my $use = shift;
my $msg = shift;
my $count = shift;
my @delta = @_;
my $delta;
if ($delta[0] == 0) {
$delta = sprintf("%d:%.2d:%.2d", $delta[1], $delta[2], $delta[3]);
} else {
$delta = sprintf("$delta[0] day%s %d:%.2d:%.2d", $delta[0] > 1 ? 's' : '',
$delta[1], $delta[2], $delta[3]);
}
if ($use eq 'regex') {
return "$count $msg regular expressions in $delta";
} else {
return "$count in $delta: $msg";
}
}
#
# Stores message information in
# $Msg_Rec = (
# {<truncated message>|<pattern>} => {
# dhms => [ array ], # days,hours,minutes,seconds
# count => integer,
sub throttle {
my %opts = (
KEY => $_,
CUT_MARKS => [ "0:16" ], # not used yet
USE => 'message',
@_
);
my $msg = $opts{'KEY'};
my $use = $opts{'USE'};
my @ymdhms = YMDHMS($msg);
my $key;
my @min_dhms_delta = split(/(\s+|:)/, $opts{'MIN_DELTA'});
foreach my $i (0..$#min_dhms_delta) {
# strip out unwanted element
splice (@min_dhms_delta, $i, 1) if ($min_dhms_delta[$i] eq ":");
}
if ($use eq 'regex') {
$key = $opts{'REGEX'};
} else {
$key = substr($msg, 16);
$key =~ s/\[\d+\]/[PID]/;
}
while ($#min_dhms_delta < 3) {
unshift(@min_dhms_delta, 0); # make sure that the dhms array is full
}
if (exists $Msg_Rec{$key} and defined $Msg_Rec{$key}->{ymdhms}) {
my $passed = 1;
$Msg_Rec{$key}->{count}++;
if ($ymdhms[1] > $Msg_Rec{$key}->{ymdhms}[1]) { $ymdhms[0]--; }
my @delta_dhms = Delta_DHMS(@{$Msg_Rec{$key}->{ymdhms}}, @ymdhms);
foreach my $i (0..$#min_dhms_delta) {
$passed = 0 if ($delta_dhms[$i] < $min_dhms_delta[$i]);
last unless ($delta_dhms[$i] == $min_dhms_delta[$i]);
}
if ($passed) {
my $new = '';
$new = new_msg($use, $key, $Msg_Rec{$key}->{count}, @delta_dhms);
$Msg_Rec{$key}->{ymdhms} = [ @ymdhms ];
$Msg_Rec{$key}->{count} = 1;
return $new;
} else {
return '';
}
} else {
my $rec;
$rec->{ymdhms} = [ @ymdhms ];
$Msg_Rec{$key} = $rec;
return $msg;
}
}
##
## ACTION SUBROUTINES
##
my %text_modes = (
"black" => "\033[30;1m",
"red" => "\033[31;1m",
"green" => "\033[32;1m",
"yellow" => "\033[33;1m",
"blue" => "\033[34;1m",
"magenta" => "\033[35;1m",
"cyan" => "\033[36;1m",
"white" => "\033[37;1m",
"black_h" => "\033[40;1m",
"red_h" => "\033[41;1m",
"green_h" => "\033[42;1m",
"yellow_h" => "\033[43;1m",
"blue_h" => "\033[44;1m",
"magenta_h" => "\033[45;1m",
"cyan_h" => "\033[46;1m",
"white_h" => "\033[47;1m",
"bold" => "\033[1m",
"blink" => "\033[5m",
"inverse" => "\033[7m",
"normal" => "\033[0m",
"underscore" => "\033[4m",
);
sub echo {
my %args = (
'MODES' => [ 'normal' ],
@_
);
return if (exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'}));
if ($args{'MODES'}[0] eq 'random') {
my @mode_names = keys %text_modes;
print $text_modes{$mode_names[rand $#mode_names]};
} else {
foreach my $mode (@{$args{'MODES'}}) {
print $text_modes{$mode};
}
}
print $args{'MESSAGE'};
print $text_modes{'normal'};
print "\n";
}
#
# ring_bell(args) -- send x number of control-G characters to the output.
#
sub ring_bell {
my %args = (
'RINGS' => 1,
@_
);
my $sun_terminal = (`uname -s` eq 'SunOS\n');
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
my $bells = $args{'RINGS'};
for ( ; $bells > 0 ; $bells-- ) {
print $BELL;
sleep 1 if $sun_terminal; # SunOS needed this. Not sure about Solaris though
}
}
#
# exec_command(args) -- fork and execute a command
#
sub exec_command {
my %args = (@_);
my $exec_pid;
my $command;
if (exists $args{'COMMAND'}) {
$command = $args{'COMMAND'};
} else {
warn "$0: No command was specified in exec action.\n";
return;
}
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
EXECFORK: {
if ($exec_pid = fork) {
waitpid(-1, WNOHANG);
return;
} elsif (defined $exec_pid) {
exec($command);
} elsif ($! =~ /No more processes/) {
# EAGAIN, supposedly recoverable fork error
sleep 5;
redo EXECFORK;
} else {
warn "$0: Can't fork to exec $command: $!\n";
}
}
return;
}
{
my $pipe_is_open;
my $current_command_name;
#
# send_message_to_pipe -- send text to a pipe.
#
# usage: &send_message_to_pipe($program_to_pipe_to_including_the_vertical_bar_symbol,
# $message_to_send_to_the_pipe);
#
sub send_message_to_pipe {
my %args = (@_);
my $command;
if (exists $args{'COMMAND'}) {
$command = $args{'COMMAND'};
} else {
warn "$0: No command was specified in pipe action.\n";
return;
}
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
# open a new pipe if necessary
if ( !$pipe_is_open or $current_command_name ne $command ) {
# first close an open pipe
close(PIPE) if $pipe_is_open;
$pipe_is_open = 0;
open(PIPE, "| $command")
or warn "$0: cannot open pipe to $command: $!\n" && return;
PIPE->autoflush(1);
$pipe_is_open = 1;
$current_command_name = $command;
}
# send the text
print PIPE "$args{'MESSAGE'}";
if (not exists $args{'KEEP_OPEN'}) {
close(PIPE) if $pipe_is_open;
$pipe_is_open = 0;
}
}
#
# close_pipe_if_open -- used at the end of a script to close a pipe
# opened by &pipe_it().
#
# usage: &close_pipe_if_open();
#
sub close_pipe_if_open {
if ($pipe_is_open) {
close(PIPE);
}
}
}
#
# send_email -- send some mail using $MAILER.
#
# usage: &send_email($addresses_to_mail_to);
#
sub send_email {
my $login = (getpwuid($<))[0];
my %args = (
'ADDRESSES' => $login,
'SUBJECT' => 'Message from Swatch',
@_
);
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
my $addresses = $args{'ADDRESSES'};
$addresses =~ s/:/,/g;
if ($MAILER eq '') {
warn "ERROR: $0 cannot find a mail delivery program\n";
return;
}
open(MAIL, "| $MAILER $addresses")
or warn "$0: cannot open pipe to $MAILER: $!\n" && return;
print MAIL "To: $addresses\n";
print MAIL "Subject: $args{SUBJECT}\n\n";
print MAIL "$args{'MESSAGE'}\n";
close(MAIL);
}
#
# write_message -- use $WRITE to send a message logged on users.
#
sub write_message {
my %args = (@_);
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
if ($WRITE eq '') {
warn "ERROR: $0 cannot find the write(1) program\n";
return;
}
if (exists($args{'USERS'})) {
foreach my $user (split(/:/, $args{'USERS'})) {
send_message_to_pipe(COMMAND => "$WRITE $user 2>/dev/null",
MESSAGE => "$args{'MESSAGE'}\n");
}
}
}
use File::Tail;
my $Filename = '/var/log/ulog/syslogemu.log';
my $File = File::Tail->new(name=>$Filename, maxinterval => 1, interval => 1);
if (not defined $File) {
die "/usr/local/bin/swatch: cannot read input \"$Filename\": $!\n";
}
LOOP: while (defined($_=$File->read)) {
chomp;
my $sanitized_ = $_;
@_ = split;
# quote all special shell chars
$sanitized_ =~ s/([;&\(\)\|\^><\$`'\\])/\\$1/g;
my @sanitized_ = split(/\s+/, $sanitized_);
if (/INVALID|REPEATED|INCOMPLETE:LOGIN/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
ring_bell('RINGS' => "3", );
next;
}
if (/(panic|halt)/) {
echo('MESSAGE' => "$_", );
ring_bell();
next;
}
if (/Regents/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
ring_bell();
next;
}
if (/ipfw:.*Deny ICMP:8.0/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 -q /root/wavs/drip.wav &", );
next;
}
if (/ipfw:.*Deny TCP .*:6000/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 -q /root/wavs/camera.wav &", );
next;
}
if (/ipfw:.*Deny UDP .*:513/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 -q /root/wavs/flush.wav &", );
next;
}
if (/ipfw:.*Deny TCP .*:21/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 -q /root/wavs/vault.wav &", );
next;
}
if (/PROTO=TCP .*DPT=80/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 /root/wavs/tcp80.mp3 &", );
next;
}
if (/PROTO=TCP .*DPT=23/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 /root/wavs/tcp23.mp3 &", );
next;
}
if (/UDP .*=1300007/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 /root/wavs/udp137.mp3 &", );
next;
}
if (/PROTO=ICMP/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 /root/wavs/ping.mp3 &", );
next;
}
if (/.*/) {
echo('MESSAGE' => "$_", );
next;
}
}
#use strict; # @(#) First Edit: Bas # @(#) Last Edit: Fash use POSIX ":sys_wait_h"; use vars qw(%Msg_Rec); $SIG{'TERM'} = $SIG{'HUP'} = 'goodbye'; $SIG{'CHLD'} = 'IGNORE'; ## Constants my $BELL = ""; my $MAILER = "/usr/sbin/sendmail"; my $WRITE = "/usr/bin/write"; $/ = " "; autoflush STDOUT; sub goodbye { $| = 0; close_pipe_if_open(); exit(0); } # # in_range($range, $number) # returns 1 if $number is inside $range, 0 if not # sub in_range { my $range = shift; my $num = shift; foreach my $f (split(/,/, $range)) { if ($f =~ /-/) { my ($low,$high) = split(/-/, $f); return 1 if ($low <= $num and $num <= $high); } elsif ($f == $num) { return 1; } } return 0; } # # inside_time_window($days,$hours) # returns 1 if inside window, 0 if outside window # sub inside_time_window { my $range = shift; my($days, $hours) = split(/:/, $range); my ($hr, $wday) = (localtime(time))[2,6]; if (($days eq '*' or in_range($days, $wday)) and ($hours eq '*' or in_range($hours, $hr))) { return 1; } else { return 0; } } print "\n*** swatch-3.0.1 (pid:6826) started at " . `/bin/date` . "\n"; use Date::Calc qw(:all); sub parse_dot { my $message = shift; my $dot_loc = shift; my @dot = (); my @ranges = split(/:/, $dot_loc); foreach my $range (0..$#ranges) { if ($ranges[$range] != -1) { my ($begin, $end) = split(/-/, $ranges[$range]); $dot[$range] = substr($message, $begin, ($end - $begin + 1)); } } return @dot; } my ($date_loc, $time_loc) = ("-1:0-2:4-5", "7-8:10-11:13-14"); my %months = ( Jan => 1, Feb => 2, Mar => 3, Apr => 4, May => 5, Jun => 6, Jul => 7, Aug => 8, Sep => 9, Oct => 10, Nov => 11, Dec => 12 ); # Returns an array of year, month, day, hours, minutes, and seconds. # sub YMDHMS { my $string = shift; my $year_today = (Today())[0]; my ($y, $m, $d) = parse_dot($string, $date_loc); my ($hrs, $mins, $secs) = parse_dot($string, $time_loc); if (length($y) eq 0) { $y = (Today())[0] }; return ($y, $months{$m}, $d, $hrs, $mins, $secs); } sub new_msg { my $use = shift; my $msg = shift; my $count = shift; my @delta = @_; my $delta; if ($delta[0] == 0) { $delta = sprintf("%d:%.2d:%.2d", $delta[1], $delta[2], $delta[3]); } else { $delta = sprintf("$delta[0] day%s %d:%.2d:%.2d", $delta[0] > 1 ? 's' : '', $delta[1], $delta[2], $delta[3]); } if ($use eq 'regex') { return "$count $msg regular expressions in $delta"; } else { return "$count in $delta: $msg"; } } # # Stores message information in # $Msg_Rec = ( # {<truncated message>|<pattern>} => { # dhms => [ array ], # days,hours,minutes,seconds # count => integer, sub throttle { my %opts = ( KEY => $_, CUT_MARKS => [ "0:16" ], # not used yet USE => 'message', @_ ); my $msg = $opts{'KEY'}; my $use = $opts{'USE'}; my @ymdhms = YMDHMS($msg); my $key; my @min_dhms_delta = split(/(\s+|:)/, $opts{'MIN_DELTA'}); foreach my $i (0..$#min_dhms_delta) { # strip out unwanted element splice (@min_dhms_delta, $i, 1) if ($min_dhms_delta[$i] eq ":"); } if ($use eq 'regex') { $key = $opts{'REGEX'}; } else { $key = substr($msg, 16); $key =~ s/\[\d+\]/[PID]/; } while ($#min_dhms_delta < 3) { unshift(@min_dhms_delta, 0); # make sure that the dhms array is full } if (exists $Msg_Rec{$key} and defined $Msg_Rec{$key}->{ymdhms}) { my $passed = 1; $Msg_Rec{$key}->{count}++; if ($ymdhms[1] > $Msg_Rec{$key}->{ymdhms}[1]) { $ymdhms[0]--; } my @delta_dhms = Delta_DHMS(@{$Msg_Rec{$key}->{ymdhms}}, @ymdhms); foreach my $i (0..$#min_dhms_delta) { $passed = 0 if ($delta_dhms[$i] < $min_dhms_delta[$i]); last unless ($delta_dhms[$i] == $min_dhms_delta[$i]); } if ($passed) { my $new = ''; $new = new_msg($use, $key, $Msg_Rec{$key}->{count}, @delta_dhms); $Msg_Rec{$key}->{ymdhms} = [ @ymdhms ]; $Msg_Rec{$key}->{count} = 1; return $new; } else { return ''; } } else { my $rec; $rec->{ymdhms} = [ @ymdhms ]; $Msg_Rec{$key} = $rec; return $msg; } } ## ## ACTION SUBROUTINES ## my %text_modes = ( "black" => "\033[30;1m", "red" => "\033[31;1m", "green" => "\033[32;1m", "yellow" => "\033[33;1m", "blue" => "\033[34;1m", "magenta" => "\033[35;1m", "cyan" => "\033[36;1m", "white" => "\033[37;1m", "black_h" => "\033[40;1m", "red_h" => "\033[41;1m", "green_h" => "\033[42;1m", "yellow_h" => "\033[43;1m", "blue_h" => "\033[44;1m", "magenta_h" => "\033[45;1m", "cyan_h" => "\033[46;1m", "white_h" => "\033[47;1m", "bold" => "\033[1m", "blink" => "\033[5m", "inverse" => "\033[7m", "normal" => "\033[0m", "underscore" => "\033[4m", ); sub echo { my %args = ( 'MODES' => [ 'normal' ], @_ ); return if (exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'})); if ($args{'MODES'}[0] eq 'random') { my @mode_names = keys %text_modes; print $text_modes{$mode_names[rand $#mode_names]}; } else { foreach my $mode (@{$args{'MODES'}}) { print $text_modes{$mode}; } } print $args{'MESSAGE'}; print $text_modes{'normal'}; print "\n"; } # # ring_bell(args) -- send x number of control-G characters to the output. # sub ring_bell { my %args = ( 'RINGS' => 1, @_ ); my $sun_terminal = (`uname -s` eq 'SunOS\n'); return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'}); my $bells = $args{'RINGS'}; for ( ; $bells > 0 ; $bells-- ) { print $BELL; sleep 1 if $sun_terminal; # SunOS needed this. Not sure about Solaris though } } # # exec_command(args) -- fork and execute a command # sub exec_command { my %args = (@_); my $exec_pid; my $command; if (exists $args{'COMMAND'}) { $command = $args{'COMMAND'}; } else { warn "$0: No command was specified in exec action.\n"; return; } return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'}); EXECFORK: { if ($exec_pid = fork) { waitpid(-1, WNOHANG); return; } elsif (defined $exec_pid) { exec($command); } elsif ($! =~ /No more processes/) { # EAGAIN, supposedly recoverable fork error sleep 5; redo EXECFORK; } else { warn "$0: Can't fork to exec $command: $!\n"; } } return; } { my $pipe_is_open; my $current_command_name; # # send_message_to_pipe -- send text to a pipe. # # usage: &send_message_to_pipe($program_to_pipe_to_including_the_vertical_bar_symbol, # $message_to_send_to_the_pipe); # sub send_message_to_pipe { my %args = (@_); my $command; if (exists $args{'COMMAND'}) { $command = $args{'COMMAND'}; } else { warn "$0: No command was specified in pipe action.\n"; return; } return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'}); # open a new pipe if necessary if ( !$pipe_is_open or $current_command_name ne $command ) { # first close an open pipe close(PIPE) if $pipe_is_open; $pipe_is_open = 0; open(PIPE, "| $command") or warn "$0: cannot open pipe to $command: $!\n" && return; PIPE->autoflush(1); $pipe_is_open = 1; $current_command_name = $command; } # send the text print PIPE "$args{'MESSAGE'}"; if (not exists $args{'KEEP_OPEN'}) { close(PIPE) if $pipe_is_open; $pipe_is_open = 0; } } # # close_pipe_if_open -- used at the end of a script to close a pipe # opened by &pipe_it(). # # usage: &close_pipe_if_open(); # sub close_pipe_if_open { if ($pipe_is_open) { close(PIPE); } } } # # send_email -- send some mail using $MAILER. # # usage: &send_email($addresses_to_mail_to); # sub send_email { my $login = (getpwuid($<))[0]; my %args = ( 'ADDRESSES' => $login, 'SUBJECT' => 'Message from Swatch', @_ ); return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'}); my $addresses = $args{'ADDRESSES'}; $addresses =~ s/:/,/g; if ($MAILER eq '') { warn "ERROR: $0 cannot find a mail delivery program\n"; return; } open(MAIL, "| $MAILER $addresses") or warn "$0: cannot open pipe to $MAILER: $!\n" && return; print MAIL "To: $addresses\n"; print MAIL "Subject: $args{SUBJECT}\n\n"; print MAIL "$args{'MESSAGE'}\n"; close(MAIL); } # # write_message -- use $WRITE to send a message logged on users. # sub write_message { my %args = (@_); return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'}); if ($WRITE eq '') { warn "ERROR: $0 cannot find the write(1) program\n"; return; } if (exists($args{'USERS'})) { foreach my $user (split(/:/, $args{'USERS'})) { send_message_to_pipe(COMMAND => "$WRITE $user 2>/dev/null", MESSAGE => "$args{'MESSAGE'}\n"); } } } use File::Tail; my $Filename = '/var/log/ulog/syslogemu.log'; my $File = File::Tail->new(name=>$Filename, maxinterval => 1, interval => 1); if (not defined $File) { die "/usr/local/bin/swatch: cannot read input \"$Filename\": $!\n"; } LOOP: while (defined($_=$File->read)) { chomp; my $sanitized_ = $_; @_ = split; # quote all special shell chars $sanitized_ =~ s/([;&\(\)\|\^><\$`'\\])/\\$1/g; my @sanitized_ = split(/\s+/, $sanitized_); if (/INVALID|REPEATED|INCOMPLETE:LOGIN/) { echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", ); ring_bell('RINGS' => "3", ); next; } if (/(panic|halt)/) { echo('MESSAGE' => "$_", ); ring_bell(); next; } if (/Regents/) { echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", ); ring_bell(); next; } if (/ipfw:.*Deny ICMP:8.0/) { echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", ); exec_command('COMMAND' => "mpg123 -q /root/wavs/drip.wav &", ); next; } if (/ipfw:.*Deny TCP .*:6000/) { echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", ); exec_command('COMMAND' => "mpg123 -q /root/wavs/camera.wav &", ); next; } if (/ipfw:.*Deny UDP .*:513/) { echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", ); exec_command('COMMAND' => "mpg123 -q /root/wavs/flush.wav &", ); next; } if (/ipfw:.*Deny TCP .*:21/) { echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", ); exec_command('COMMAND' => "mpg123 -q /root/wavs/vault.wav &", ); next; } if (/PROTO=TCP .*DPT=80/) { echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", ); exec_command('COMMAND' => "mpg123 /root/wavs/tcp80.mp3 &", ); next; } if (/PROTO=TCP .*DPT=23/) { echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", ); exec_command('COMMAND' => "mpg123 /root/wavs/tcp23.mp3 &", ); next; } if (/UDP .*=1300007/) { echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", ); exec_command('COMMAND' => "mpg123 /root/wavs/udp137.mp3 &", ); next; } if (/PROTO=ICMP/) { echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", ); exec_command('COMMAND' => "mpg123 /root/wavs/ping.mp3 &", ); next; } if (/.*/) { echo('MESSAGE' => "$_", ); next; } }