列表 2 使用 strict; 使用 File::Tail; 使用 Crypt::CBC; 使用 Schedule::At; 使用 Math::VecStat qw(sum); 使用 POSIX qw(strftime); 使用 Getopt::Std; 使用常量 PORTMIN => 745; 使用常量 KNOCKLENGTH => 8; 使用常量 KEY => "knock"; 使用常量 CIPHER => "Blowfish"; 使用 vars qw($opt_f); getopts("f:"); my $file = File::Tail->new(name=>"$opt_f",maxinterval=>2); my %QUEUE; while(defined(my $line=$file->read)) { # 只关注 DENY 数据包 next unless $line =~ /DENY/; if($line =~ /PROTO=6 ([\d.]+):\d+ [\d.]+:(\d+)/) { my ($ip,$port) = ($1,$2); # 只关注用于敲门的端口 next if ($port < PORTMIN || $port > PORTMIN+255); print "来自 $ip 到端口 $port 的敲门\n"; # 将此端口推入每个队列项 $QUEUE{$ip} = [] if ! $QUEUE{$ip}; push(@{$QUEUE{$ip}},$port); print "当前队列 ",join(" ",@{$QUEUE{$ip}}),"\n"; # 如果队列的长度达到预期,则处理它 if(@{$QUEUE{$ip}} == KNOCKLENGTH) { ProcessQueue($QUEUE{$ip}); print "当前队列 @{$QUEUE{$ip}}\n"; } } } sub ProcessQueue { my $queue = shift; # 尝试解密队列内容 my $cipher = Crypt::CBC->new({key => KEY, iv =>"01234567", prepend_iv => 0, cipher => CIPHER}); my @data = unpack("C*", $cipher->decrypt(pack("C*", map {$_ - PORTMIN } @$queue))); # 解密后的列表必须有 7 个元素,否则移除最旧的元素 # 并继续监听 if(@data != 7) { print "错误:无法正确解密\n"; shift(@$queue); return; } print "获得数据 ",join(" ",@data),"\n"; # 检查敲门序列的 crc if ((sum(@data[0..@data-2]) % 255) == $data[-1]) { # 从解密列表中提取信息 my ($remoteip,$localport,$time) = (join(".",@data[0..3]),$data[4],$data[5]); print "$remoteip $localport $time\n"; # 为远程 IP 打开端口 system("/sbin/ipchains -I input -p tcp -s $remoteip/32 -d 0/0 $localport -j ACCEPT") if $time != 255; # 如果 time = 255,则关闭端口 if($time == 255) { system("/sbin/ipchains -D input -p tcp -s $remoteip/32 -d 0/0 $localport -j ACCEPT "); } elsif ($time) { # 计划在 $time 分钟后关闭端口 my $time = strftime "%Y%m%d%H%M", localtime(time+60*$time); my $command = "/sbin/ipchains -D input -p tcp -s $remoteip/32 -d 0/0 $localport -j ACCEPT"; print "计划任务 $time $command\n"; Schedule::At::add(TIME=>$time,COMMAND=>$command); } # 清空队列 @$queue = (); } else { print "错误:错误的 crc\n"; shift(@$queue); } } ### 列表 2 结束
© . All rights reserved.