File Coverage

blib/lib/Net/CIDR/Compare.pm
Criterion Covered Total %
statement 36 214 16.8
branch 0 88 0.0
condition 0 41 0.0
subroutine 12 22 54.5
pod 5 8 62.5
total 53 373 14.2


line stmt bran cond sub pod time code
1             package Net::CIDR::Compare;
2              
3 1     1   31041 use 5.005000;
  1         4  
  1         36  
4 1     1   7 use strict;
  1         2  
  1         34  
5 1     1   4 use warnings;
  1         6  
  1         37  
6 1     1   4 use Carp;
  1         1  
  1         113  
7 1     1   1125 use Net::CIDR;
  1         6579  
  1         79  
8 1     1   1063 use Net::Netmask;
  1         29926  
  1         151  
9              
10             $|++;
11              
12             require Exporter;
13 1     1   2691 use AutoLoader;
  1         2181  
  1         9  
14              
15             our @ISA = qw(Exporter);
16              
17             # Items to export into callers namespace by default. Note: do not export
18             # names by default without a very good reason. Use EXPORT_OK instead.
19             # Do not simply export all your public functions/methods/constants.
20              
21             # This allows declaration use Net::CIDR::Compare ':all';
22             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
23             # will save memory.
24             our %EXPORT_TAGS = ( 'all' => [ qw(
25            
26             ) ] );
27              
28             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
29              
30             our @EXPORT = qw(
31            
32             );
33              
34             our $VERSION = '0.03';
35              
36             sub AUTOLOAD {
37             # This AUTOLOAD is used to 'autoload' constants from the constant()
38             # XS function.
39              
40 0     0     my $constname;
41 0           our $AUTOLOAD;
42 0           ($constname = $AUTOLOAD) =~ s/.*:://;
43 0 0         croak "&Net::CIDR::Compare::constant not defined" if $constname eq 'constant';
44 0           my ($error, $val) = constant($constname);
45 0 0         if ($error) { croak $error; }
  0            
46             {
47 1     1   370 no strict 'refs';
  1         3  
  1         120  
  0            
48             # Fixed between 5.005_53 and 5.005_61
49             #XXX if ($] >= 5.00561) {
50             #XXX *$AUTOLOAD = sub () { $val };
51             #XXX }
52             #XXX else {
53 0     0     *$AUTOLOAD = sub { $val };
  0            
54             #XXX }
55             }
56 0           goto &$AUTOLOAD;
57             }
58              
59             require XSLoader;
60             XSLoader::load('Net::CIDR::Compare', $VERSION);
61              
62             # Preloaded methods go here.
63              
64 1     1   1011 use IO::File;
  1         16309  
  1         154  
65 1     1   1440 use File::Temp qw(tempfile tempdir);
  1         38671  
  1         94  
66 1     1   901 use IO::Socket;
  1         30171  
  1         7  
67 1     1   7931 use Data::Dumper;
  1         7684  
  1         2308  
68              
69             sub new {
70 0     0 0   my $invocant = shift;
71 0           my %params = @_;
72 0   0       my $class = ref($invocant) || $invocant;
73 0           my $cidr_ptr = start_new();
74 0           my $self = { cidr_ptr => $cidr_ptr };
75 0 0         $self->{print_errors} = 1 if $params{print_errors};
76 0           return bless $self, $class;
77             }
78              
79             sub new_list {
80 0     0 1   my $self = shift;
81 0           my $list_ptr = setup_new_list($self->{cidr_ptr});
82 0           return $list_ptr;
83             }
84              
85             sub remove_list {
86 0     0 1   my $self = shift;
87 0           my $list_ptr = shift;
88 0           delete_list($self->{cidr_ptr}, $list_ptr);
89             }
90              
91             sub add_range {
92 0     0 1   my $self = shift;
93 0           my $list = shift;
94 0           my $ip_range = shift;
95 0           my $skip_check = shift;
96 0           my $array_ref = ();
97 0 0         if ($skip_check) {
98 0           push @$array_ref, $ip_range;
99             }
100             else {
101 0   0       $array_ref = $self->process_ip_range($ip_range) || return 0;
102             }
103 0           foreach my $cidr_range (@$array_ref) {
104 0           my ($network, $cidr) = split(/\//, $cidr_range);
105 0 0         if (!defined($cidr)) {
106 0           $self->{error} = "IP range is malformed [$ip_range].";
107 0 0         print STDERR $self->{error} . "\n" if $self->{print_errors};
108 0           return 0;
109             }
110 0           my $network_decimal = unpack 'N', inet_aton($network);
111 0           save_cidr($list, $network_decimal, $cidr);
112             }
113 0           return 1;
114             }
115              
116             sub process_intersection {
117 0     0 1   my $self = shift;
118 0           while ($self->get_next_intersection_range()) {
119             # do nothing. this frees C pointers.
120             }
121 0           delete $self->{leftover_cidr_processed};
122 0           delete $self->{leftover_cidr_unprocessed};
123 0           delete $self->{expand_cidr};
124 0           my %params = @_;
125 0           $self->{expand_cidr} = $params{expand_cidr};
126 0           my $cidr_ptr = $self->{cidr_ptr};
127 0           dump_intersection_output($cidr_ptr);
128             }
129              
130             sub get_next_intersection_range {
131 0     0 1   my $self = shift;
132 0           my $cidr_ptr = $self->{cidr_ptr};
133 0 0 0       if ($self->{leftover_cidr_processed} && @{$self->{leftover_cidr_processed}}) {
  0            
134 0           return shift @{$self->{leftover_cidr_processed}};
  0            
135             }
136 0 0 0       if ($self->{leftover_cidr_unprocessed} && @{$self->{leftover_cidr_unprocessed}}) {
  0            
137 0           my $range = shift @{$self->{leftover_cidr_unprocessed}};
  0            
138 0           my $cidr_aref = expand_cidr($range, $self->{expand_cidr});
139 0           my $first_expand_range = shift @$cidr_aref;
140 0 0         if (@$cidr_aref) {
141 0           unshift @{$self->{leftover_cidr_processed}}, @$cidr_aref;
  0            
142             }
143 0           return $first_expand_range;
144             }
145 0           my $range = dump_next_intersection_output($cidr_ptr);
146 0 0         return unless $range;
147 0 0         if (defined($self->{expand_cidr})) {
148 0           my ($network, $cidr) = split("/", $range);
149 0 0         if ($cidr >= $self->{expand_cidr}) {
150 0           return $range;
151             }
152             else {
153 0 0         if (($self->{expand_cidr} - $cidr) > 16) {
154 0           my $cidr_aref = expand_cidr($range, 16);
155 0           my $first_slash16 = shift @$cidr_aref;
156 0           my $cidr_aref_first_slash16 = expand_cidr($first_slash16, $self->{expand_cidr});
157 0           my $first_expand_range = shift @$cidr_aref_first_slash16;
158 0           push @{$self->{leftover_cidr_processed}}, @$cidr_aref_first_slash16;
  0            
159 0           push @{$self->{leftover_cidr_unprocessed}}, @$cidr_aref;
  0            
160 0           return $first_expand_range;
161             }
162 0           my $cidr_aref = expand_cidr($range, $self->{expand_cidr});
163 0           my $first_expand_range = shift @$cidr_aref;
164 0           push @{$self->{leftover_cidr_processed}}, @$cidr_aref;
  0            
165 0           return $first_expand_range;
166             }
167             }
168 0           return $range;
169             }
170              
171             sub process_ip_range {
172 0     0 0   my $self = shift;
173 0           my $ip_range = shift;
174 0           my @octets;
175             my $cidr;
176 0           $ip_range =~ s/(\s|\n|\r)+//g;
177 0 0         if ($ip_range =~ /^(\d+\.\d+\.\d+\.\d+)-(\d+\.\d+\.\d+\.\d+)$/) {
    0          
    0          
178 0           my $ip_start = $1;
179 0           my $ip_end = $2;
180 0           my $ip_start_decimal = unpack 'N', inet_aton($ip_start);
181 0           my $ip_end_decimal = unpack 'N', inet_aton($ip_end);
182 0 0         $self->process_ip_range($ip_start) || return 0; # Do this to run further sanity checks
183 0 0         $self->process_ip_range($ip_end) || return 0; #
184 0 0         if ($ip_end_decimal < $ip_start_decimal) {
185 0           $self->{error} = "IP range is malformed [$ip_range]. Range problem.";
186 0 0         print STDERR $self->{error} . "\n" if $self->{print_errors};
187 0           return 0;
188             }
189 0           my @cidr_array = Net::CIDR::range2cidr("$ip_start-$ip_end");
190 0           return \@cidr_array;
191             }
192             elsif ($ip_range =~ /^(.+)\.(.+)\.(.+)\.([\d\-\[\]\*]+)$/) {
193 0           @octets = ($1, $2, $3, $4);
194             }
195             elsif ($ip_range =~ /^(.+)\.(.+)\.(.+)\.(.+)\/(\d+)$/) {
196 0           @octets = ($1, $2, $3, $4);
197 0 0         $cidr = $5 if defined $5;
198             }
199             else {
200 0           $self->{error} = "IP range is malformed [$ip_range]";
201 0 0         print STDERR $self->{error} . "\n" if $self->{print_errors};
202 0           return 0;
203             }
204 0           my $range_flag = 0;
205 0           for (my $x = 0; $x <= $#octets; $x++) {
206 0 0         if ($octets[$x] eq "[0-255]") {
207 0           $octets[$x] = "*";
208             }
209 0 0 0       if ($octets[$x] =~ /^\[(\d+)-(\d+)\]$/ && !defined($cidr)) {
    0 0        
    0          
210 0           my $begin_range = $1;
211 0           my $end_range = $2;
212 0 0 0       if ($begin_range < 0 || $begin_range > 255 || $end_range < 0 || $end_range > 255 || $begin_range > $end_range) {
      0        
      0        
      0        
213 0           $self->{error} = "IP range is malformed [$ip_range]. Range problem.";
214 0 0         print STDERR $self->{error} . "\n" if $self->{print_errors};
215 0           return 0;
216             }
217 0 0         if ($range_flag) {
218 0           $self->{error} = "IP range is malformed [$ip_range]. Range values can only be used for one octet.";
219 0 0         print STDERR $self->{error} . "\n" if $self->{print_errors};
220 0           return 0;
221             }
222 0           $range_flag = 1;
223             }
224             elsif ($octets[$x] =~ /^\d+$/) {
225 0 0         if ($range_flag) {
226 0           $self->{error} = "IP range is malformed [$ip_range]. Only asterisks can be used after a bracketed range. Example: 10.10.[1-2].*";
227 0 0         print STDERR $self->{error} . "\n" if $self->{print_errors};
228 0           return 0;
229             }
230 0 0 0       if ($octets[$x] < 0 || $octets[$x] > 255) {
231 0           $self->{error} = "IP range is malformed [$ip_range]. Range problem.";
232 0 0         print STDERR $self->{error} . "\n" if $self->{print_errors};
233 0           return 0;
234             }
235             }
236             elsif ($octets[$x] =~ /^\*$/ && !defined($cidr)) {
237             # Do nothing
238             }
239             else {
240 0           $self->{error} = "IP range is malformed [$ip_range]";
241 0 0         print STDERR $self->{error} . "\n" if $self->{print_errors};
242 0           return 0;
243             }
244             }
245 0 0 0       if (defined($cidr) && ($cidr > 32 || $cidr < 0)) {
      0        
246 0           $self->{error} = "IP range is malformed [$ip_range]. Incorrect CIDR notation.";
247 0 0         print STDERR $self->{error} . "\n" if $self->{print_errors};
248 0           return 0;
249             }
250             # Passed initial checks
251              
252 0           my %hash;
253 0 0         if (defined($cidr)) {
254 0           my @range = Net::CIDR::cidr2range($ip_range);
255 0           ($hash{ip_start}, $hash{ip_end}) = split(/-/, $range[0]);
256 0           $hash{ip_start_decimal} = unpack 'N', inet_aton($hash{ip_start});
257 0           $hash{ip_end_decimal} = unpack 'N', inet_aton($hash{ip_end});
258             }
259             else {
260 0           for (my $x = 0; $x < 4; $x++) {
261 0 0         if ($octets[$x] eq '*') {
    0          
    0          
262 0           $hash{ip_start} .= "0.";
263 0           $hash{ip_end} .= "255.";
264             }
265             elsif ($octets[$x] =~ /\[(\d+)-(\d+)\]/) {
266 0           $hash{ip_start} .= $1 . ".";
267 0           $hash{ip_end} .= $2 . ".";
268             }
269             elsif ($octets[$x] =~ /(\d+)/) {
270 0           $hash{ip_start} .= $1 . ".";
271 0           $hash{ip_end} .= $1 . ".";
272             }
273             else {
274 0           $self->{error} = "Got unexpected IP value [$ip_range]";
275 0 0         print STDERR $self->{error} . "\n" if $self->{print_errors};
276 0           return 0;
277             }
278             }
279 0           $hash{ip_start} =~ s/^(.+)\.$/$1/;
280 0           $hash{ip_end} =~ s/^(.+)\.$/$1/;
281             }
282 0           my @cidr_array = range2cidrlist($hash{ip_start}, $hash{ip_end});
283 0           return \@cidr_array;
284             }
285              
286             sub expand_cidr {
287 0     0 0   my $cidr_range = shift;
288 0           my $level = shift; # Should be 0 thru 32
289 0 0 0       die "Invalid CIDR notation [$level]" if ($level < 0 || $level > 32);
290            
291 0           my ($network, $cidr) = split("/", $cidr_range);
292              
293 0           my $network_decimal = unpack 'N', inet_aton($network);
294 0           my @result = ();
295 0 0         if ($cidr >= $level) {
296 0           push @result, $cidr_range;
297 0           return \@result;
298             }
299 0           my $num_slices = 2 ** ($level - $cidr);
300 0           for (my $x = 0; $x < $num_slices; $x++) {
301 0           my $add = $x * (2 ** (32 - $level));
302 0           my $smaller_network = inet_ntoa(pack 'N', ($network_decimal + $add));
303 0           push @result, ($smaller_network . "/" . $level);
304             }
305 0           return \@result;
306             }
307              
308             # Autoload methods go after =cut, and are processed by the autosplit program.
309              
310             1;
311             __END__