从给定范围(端口范围)中排除数字范围

时间:2015-09-12 09:11:46

标签: perl

我试图让端口范围1-65535允许用户从中排除某些范围的端口

输入将是逗号分隔的单个或范围的端口号,例如: 1-2,3-4,4-5,5-6,7-8,9-10,1-100

输出应为: 101-65535

我编写的代码涵盖了很多情况,但由于某些原因,我目前所拥有的代码并没有处理上一次排除1-100,因为9是当前的最小端口号

这是我的代码:

 my @ranges;
 push @ranges, '1-65535';

 my $bFound = 0;
 do {
  $bFound = 0;
  foreach my $ptrExclusion (@exclusions) {
   my %exclusion = %{$ptrExclusion};
   print STDERR "handling exclusion @ 7964: ".Dumper(\%exclusion);
   my $currentPos = 0;
   foreach my $range (@ranges) {
    $currentPos++;

    if ($range =~ /([0-9]+)-([0-9]+)/) {
     my $firstPortInRange = $1;
     my $secondPortInRange = $2;

     if ($secondPortInRange == $exclusion{first} and
         $exclusion{second} == $exclusion{first}) {
      $bFound = 1;
      my @newranges;
      if ($exclusion{first} - 1 > 0) {
       push @newranges, "$firstPortInRange-".(sprintf("%d", $exclusion{first} - 1));
      } else { # Handle port "1"
       ### Don't put anything, we are excluded from adding this
      }

      if ($currentPos > 1) {
       unshift @newranges, $ranges[1..($currentPos-1)];
      }

      if ($currentPos + 1 < scalar(@ranges)) {
       push @newranges, $ranges[($currentPos+1) .. scalar(@ranges)];
      }

      print STDERR "newranges @ 7985: ".Dumper(\@newranges);
      @ranges = @newranges;
      last;
     }

     if ($firstPortInRange == $exclusion{first} and
         $exclusion{second} == $exclusion{first}) {
      $bFound = 1;
      my @newranges;

      if ($exclusion{second} + 1 <= 65535) {
       push @newranges, (sprintf("%d", $exclusion{second} + 1))."-$secondPortInRange";
      } else { # Handle port 65535
       #### Don't put anything, we are excluded from adding this
      }

      if ($currentPos > 1) {
       unshift @newranges, $ranges[1..($currentPos-1)];
      }

      if ($currentPos + 1 < scalar(@ranges)) {
       push @newranges, $ranges[($currentPos+1) .. scalar(@ranges)];
      }

      print STDERR "newranges @ 8005: ".Dumper(\@newranges);
      @ranges = @newranges;
      last;
     }

     if ($firstPortInRange < $exclusion{first} and
         $secondPortInRange > $exclusion{second} # An exclusion is between the ranges we currently have, this doesn't include "hits" on the exact port number, i.e. excluding port 1 and port 65535
        ) {
      print STDERR "exclusion matched @ 8022\n";
      $bFound = 1;
      #printf (STDERR "currentPos @ 7973: %d\n", $currentPos);
      my @newranges;
      push @newranges, "$firstPortInRange-".(sprintf("%d", $exclusion{first} - 1));
      push @newranges, (sprintf("%d", $exclusion{second} + 1))."-$secondPortInRange";
      if ($currentPos > 1) {
       unshift @newranges, $ranges[1..($currentPos-1)];
      }

      if ($currentPos + 1 < scalar(@ranges)) {
       push @newranges, $ranges[($currentPos+1) .. scalar(@ranges)];
      }

      print STDERR "newranges @ 8026: ".Dumper(\@newranges);
      @ranges = @newranges;
      last;
     }

     if ($firstPortInRange >= $exclusion{first} and
         $firstPortInRange < $exclusion{second} and
         $secondPortInRange <= $exclusion{second} and
         $secondPortInRange > $exclusion{first} # An exclusion is holding our range inside it
        ) {
      print STDERR "exclusion matched @ 8045\n";
      $bFound = 1;

      splice(@ranges, $currentPos-1, 1); # -1 as our index starts from 1, while @ranges index starts at 0
      print STDERR "ranges @ 8051: ".Dumper(\@ranges);

      last;
     }
    }
   }
   if ($bFound) {
    last;
   }
  }
 } while ($bFound);

 print STDERR "ranges @ 7980: ".join(", ", @ranges). "\n";

@exclusions下面有哈希元素,其中包含firstsecond值,指定端口A和端口B(较低范围和最高范围),如果排除它们的值可以匹配是1端口。

4 个答案:

答案 0 :(得分:3)

有许多模块可以使用套装,让您的生活更轻松。我建议Set::IntSpan::Fast

use strict;
use warnings;
use 5.010;

use Set::IntSpan::Fast;

my $ports = Set::IntSpan::Fast->new('1-65535');
my $exclude = Set::IntSpan::Fast->new('1-2,3-4,4-5,5-6,7-8,9-10,1-100');

say $ports->diff($exclude)->as_string;

输出

101-65535

答案 1 :(得分:3)

Range::Object模块集非常全面,Range::Object::Serial完全符合您的要求

这个简短的程序演示了

use strict;
use warnings;
use v5.10;

use Range::Object::Serial;

my $range = Range::Object::Serial->new('1-65535');
say scalar $range->collapsed;

$range->remove('1-2,3-4,4-5,5-6,7-8,9-10,1-100');
say scalar $range->collapsed;

输出

1-65535
101-65535

答案 2 :(得分:0)

这是一次尝试,不使用库,而只是使用数组(因为它不是很大)。

#!/usr/bin/env perl

use warnings;
use strict;

my @port_range = ( 1, 65535 );
my @ports = map { 1 } ( 0 .. $port_range[1] );
my $exclusions = '1-2,3-4,4-5,5-6,7-8,9-10,1-100';

for my $exclusion ( split /,/, $exclusions ) {
    if ($exclusion =~ m|\-|) {
        # Range
        my ($start, $stop) = split /-/, $exclusion;
        $ports[$_] = 0 for ($start..$stop);
    } else {
        # Single port
        $ports[$exclusion] = 0;
    }
}

my @good_ports = grep { $ports[$_] > 0 } ( $port_range[0] .. $port_range[1] );
my $last_good = 0;
for my $i ( 1 .. $#good_ports ) {
    if ($good_ports[$i] - $good_ports[$i-1] > 1) {
        # gap
        print join '-', $good_ports[$last_good], $good_ports[$i-1] . "\n";
        $last_good = $i;
    }    
}
print join '-', $good_ports[$last_good], $good_ports[$#good_ports] . "\n";

<强>输出

101-65535

答案 3 :(得分:0)

在范围尽可能小的情况下,您可以保留每个端口的列表并设置打开的标记。关闭,并打印出结果。

#!/usr/bin/perl
use warnings;
use strict;

# print a list of open ports, given a string of closed ports

my @ports = map { 1 } 1..65535; # start with all ports flagged as open

while (<DATA>) {     # get the string of closed port numbers & ranges
    chomp;
    /\d/ or next;    # ensure we have at least one number to work with

    my @exclusions = split /,/;

    for (@exclusions) {

        # each exclusion is a number, optionally followed
        # by a dash and another number
        /^(\d+)(?:-(\d+))?$/ or next;

        # set the flag to 0 for a single port or a range of ports
        if ($1 and ! $2) {
            $ports[$1-1] = 0;  # single port
        }
        elsif ($1 and $2) {
            @ports[$1-1..$2-1] = map {0} $1..$2; # range of ports
        }
    }
}

# get a list of all ports which are open
my @open_ports = map {$_ + 1} grep {$ports[$_] == 1} 0..$#ports;

# the final list of open port ranges, to be displayed
my @ranges     = ();

# build up the list of open ranges
for (@open_ports) {
    my $one_less = $_ - 1;

    # either add this open port to the previous range,
    #   or start a new range with this port
    #
    (@ranges and $ranges[-1] =~ s/-$one_less$/-$_/) 
       or push @ranges, "$_-$_";
}

# fix single-number ranges for display
for (@ranges) {
    s/^(\d+)-\1$/$1/;
}

# display the result
print join ',', @ranges;

__DATA__
1-2,3-4,4-5,5-6,7-8,9-10,1-100

输出:

101-65535