Код:
#!/usr/bin/perl
use POSIX;
use IO::Socket;
use IO::Select;
use Socket;
use Fcntl;
use Tie::RefHash;
$debug = 1;
$port = 4567;
$iptables = "/sbin/iptables";
$rule_insert_index = 1;
$| = 1;
print "starting manad..\n";
# База данных правил клиентов
%CLRULE = ();
%NRULE = ();
%RUL_NUMBERS = ();
%DEL_RULES = ();
%CHECK_RULES = ();
%CONTRACT_INDEX = ();
# Начать с пустыми буферами
%inbuffer = ();
%outbuffer = ();
%ready = ();
tie %ready, 'Tie::RefHash';
# Прослушивать порт
$server = IO::Socket::INET->new( LocalPort => $port, Listen => 10 )
or die "Can`t make server socket: $@\n";
nonblock( $server );
$SIG{INT} = sub { $server->close(); exit( 0 ); };
$select = IO::Select->new( $server );
$pid = getpid();
open(FILE, ">/var/run/manad.pid");
print FILE $pid;
close(FILE);
# Устанавливаем новый root каталог для процесса
# chroot( $homedir ) or die "Couldn`t chroot to $homedir: $!\n";
# Главный цикл: проверка чтения/принятия, проверка записи,
# проверка готовности к работе
while( 1 )
{
my $client;
my $rv;
my $data;
# Проверить наличие новой информации на имеющихся подключениях
# Есть ли что-нибудь для чтения или подтверждения?
foreach $client ( $select->can_read( 1 ) )
{
if ( $client == $server )
{
# Принять новое подключение
$client = $server->accept();
$select->add( $client );
nonblock( $client );
}
else
{
# Прочитать данные
$data = '';
$rv = $client->recv( $data, POSIX::BUFSIZ, 0 );
unless( defined( $rv ) && length $data )
{
# Это должен быть конец файла, поэтому закрываем клиента
delete $inbuffer{$client};
delete $outbuffer{$client};
delete $ready{$client};
$select->remove( $client );
close $client;
next;
}
$inbuffer{$client} .= $data;
# Проверить, говорят ли данные в буфере или только что прочитанные
# данные о наличии полного запроса, ожидающего выполнения. Если да -
# заполнить $ready{$client} запросами, ожидающими обработки.
while( $inbuffer{$client} =~ s/(.*\n)// ) { push( @{$ready{$client}}, $1 ) }
}
}
# Есть ли полные запросы для обработки?
foreach $client ( keys %ready ) { handle( $client ); }
# Сбрасываем буферы?
foreach $client ( $select->can_write( 1 ) )
{
# Пропустить этого клиента, если нам нечего сказать
next unless $outbuffer{$client};
$rv = $client->send( $outbuffer{$client}, 0 );
unless( defined $rv )
{
# Пожаловаться, но следовать дальше
warn "I was told I could write? but I can`t.\n";
next;
}
if ( $rv == length $outbuffer{$client} || $! == POSIX::EWOULDBLOCK )
{
substr( $outbuffer{$client}, 0, $rv ) = '';
delete $outbuffer{$client} unless length $outbuffer{$client};
}
else
{
# Не удалось записать все данные и не из-за блокировки.
# Очистить буферы и следовать дальше.
delete $inbuffer{$client};
delete $outbuffer{$client};
delete $ready{$client};
$select->remove($client);
close($client);
next;
}
}
}
# handle( $socket ) обрабатывает все необработанные запросы
# для клиента $client
sub handle
{
# Запрос находится в $ready{$client}
# Отправить вывод в $outbuffer{$client}
my $client = shift;
my $request;
foreach $request ( @{$ready{$client}} )
{
print "\nrequest =".$request."\n" if ( $debug == 1 );
if ( $request =~ /^test/ )
{
my $open_client = "";
foreach my $kod ( keys %CLRULE )
{ $open_client .= $open_client eq "" ? $kod : " ".$kod; }
$outbuffer{$client} .= $open_client."\n";
}
elsif ( $request =~ /^add\t([0-9]+)\t(.*)/ )
{
my ($kod, $rule) = ($1, $2);
&delete_rule( $kod, $rule ) if ( exists $CLRULE{$kod} );
&add_rule( $kod, $rule ) if ( !exists $CLRULE{$kod} );
}
elsif ( $request =~ /^remove\t([0-9]+)(\t(.*))?/ )
{
my $kod = $1;
&delete_rule( $kod ) if ( exists $CLRULE{$1} );
}
elsif ( $request =~ /^check\t([0-9]+)\t(.*)/ )
{
my ($kod, $rule) = ($1, $2);
&check_rule( $kod, $rule ) if ( exists $CLRULE{$1} );
}
}
delete $ready{$client};
}
# nonblock( $socket ) переводит сокет в неблокирующий режим
sub nonblock
{
my $socket = shift;
my $flags;
$flags = fcntl( $socket, F_GETFL, 0 )
or die "Can`t get flags for socket: $!\n";
fcntl( $socket, F_SETFL, $flags | O_NONBLOCK )
or die "Can`t make socket nonblocking: $!\n";
}
sub add_rule
{
my $kod = $_[0];
my $rule = $_[1];
#заменяем все контсрукции типа {N0}
while ( $rule =~ /\[N([0-9]+)\]/ )
{
my $n = $1;
my $i = 20;
#находим первый свободный номер
while( 1 )
{
$i++;
last if ( !exists $RUL_NUMBERS{$i} );
}
$RUL_NUMBERS{$i} = "1";
$rule =~ s/\[N$n\]/$i/g;
$CONTRACT_INDEX{$kod}{$i}=$i;
$NRULE{$kod}{$n} = $i;
}
$CLRULE{$kod} = $rule;
#добавляем правило
my $add_rule = $rule;
$add_rule =~ s/\[OPEN\]((.|\n)+)\[\/OPEN\]((.|\n)*)/${1}/;
print "commands:\n" if ( $debug == 1 );
my @rules = split( /\|/, $add_rule );
foreach $line ( @rules )
{
print "$line\n" if ( $debug == 1 );
$err = `$line`;
}
#запоминаем правило на удаление в кэш
my $del_rule = $rule;
$del_rule =~ s/(?:(.|\n?:)*)\[CLOSE\]((.|\n)+)\[\/CLOSE\]((.|\n)*)/${2}/;
print "\ndelete =".$del_rule."\n" if ( $debug == 1 );
$DEL_RULES{$kod}=$del_rule;
my $check_rule = $rule;
$check_rule =~ s/(?:(.|\n?:)*)\[CHECK\]((.|\n)+)\[\/CHECK\]((.|\n)*)/${2}/;
print "\ncheck =".$check_rule."\n" if ( $debug == 1 );
$CHECK_RULES{$kod}=$check_rule;
}
sub check_rule
{
my $kod = $_[0];
my $rule = $_[1];
my $check_rule = $rule;
$check_rule =~ s/(?:(.|\n?:)*)\[CHECK\]((.|\n)+)\[\/CHECK\]((.|\n)*)/${2}/;
while ( $check_rule =~ /\[N([0-9]+)\]/ )
{
my $n = $1;
$check_rule =~ s/\[N$n\]/$NRULE{$kod}{$n}/g;
}
$CHECK_RULES{$kod}=$check_rule;
print "rules\n";
print "$CHECK_RULES{$kod}";
my $rule = $CHECK_RULES{$kod};
my @rules = split( /\|/, $rule );
print "commands:\n" if ( $debug == 1 );
foreach $line ( @rules )
{
print "$line\n" if ( $debug == 1 );
$err = `$line`;
}
}
sub delete_rule
{
my $kod = $_[0];
my $rule = $DEL_RULES{$kod};
my @rules = split( /\|/, $rule );
print "commands:\n" if ( $debug == 1 );
foreach $line ( @rules )
{
print "$line\n" if ( $debug == 1 );
$err = `$line`;
}
delete $DEL_RULES{$kod};
delete $CHECK_RULES{$kod};
delete $NRULE{$kod};
delete $CLRULE{$kod};
foreach my $idx ( keys %{$CONTRACT_INDEX{$kod}} )
{
print "\n deleting rules with idx=".$CONTRACT_INDEX{$kod}{$idx}."\n";
delete $RUL_NUMBERS{$CONTRACT_INDEX{$kod}{$idx}};
}
delete $CONTRACT_INDEX{$kod};
}
Теперь в теги [CHECK]пишем все что угодно[/CHECK]